diff options
-rw-r--r-- | drivers/mfd/Kconfig | 15 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 5 | ||||
-rwxr-xr-x | drivers/mfd/ab3550-core.c | 1820 | ||||
-rw-r--r-- | drivers/mfd/abx500-core.c | 157 | ||||
-rwxr-xr-x | include/linux/mfd/abx.h | 107 | ||||
-rw-r--r-- | include/linux/mfd/abx500.h | 364 |
6 files changed, 1545 insertions, 923 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 28ea3489129..bb8105794b6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -212,9 +212,20 @@ config MFD_TC6393XB help Support for Toshiba Mobile IO Controller TC6393XB -config MFD_AB3550_CORE +config ABX500_CORE + bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" + default y if ARCH_U300 + help + Say yes here if you have the ABX500 Mixed Signal IC family + chips. This core driver expose register access functions. + Functionality specific drivers using these functions can + remain unchanged when IC changes. Binding of the functions to + actual register access is done by the IC core driver. + +config AB3550_CORE bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions" - depends on I2C && MACH_U8500_MOP + select MFD_CORE + depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE help Select this to enable the AB3550 Mixed Signal IC core functionality. This connects to a AB3550 on the I2C bus diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7fed01d398e..ab270030e32 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -2,7 +2,8 @@ # Makefile for multifunction miscellaneous devices # -obj-$(CONFIG_MFD_AB3550_CORE) += ab3550-core.o +obj-$(CONFIG_ABX500_CORE) += abx500-core.o +obj-$(CONFIG_AB3550_CORE) += ab3550-core.o 88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o obj-$(CONFIG_MFD_SM501) += sm501.o @@ -64,4 +65,4 @@ obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB4500_CORE) += ab4500-core.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o -obj-$(CONFIG_LPC_SCH) += lpc_sch.o
\ No newline at end of file +obj-$(CONFIG_LPC_SCH) += lpc_sch.o diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index c80156b11f2..94e67abd321 100755 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -1,116 +1,111 @@ /* - * Copyright (C) 2007-2009 ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> + * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> + * Author: Mattias Wallin <mattias.wallin@stericsson.com> + * Author: Rickard Andersson <rickard.andersson@stericsson.com> + * * Low-level core for exclusive access to the AB3550 IC on the I2C bus * and some basic chip-configuration. - * Author: Bengt Jönsson <bengt.g.jonsson@stericsson.com> - * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> */ #include <linux/i2c.h> #include <linux/mutex.h> -#include <linux/notifier.h> #include <linux/err.h> #include <linux/platform_device.h> -#include <linux/device.h> #include <linux/slab.h> +#include <linux/device.h> +#include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/random.h> #include <linux/workqueue.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/uaccess.h> -#include <linux/mfd/abx.h> - -/* */ -#define ABX_NAME_STRING "ab3550" -#define ABX_ID_FORMAT_STRING "AB3550 %s" +#include <linux/mfd/abx500.h> +#include <linux/list.h> +#include <linux/bitops.h> +#include <linux/spinlock.h> +#include <linux/mfd/core.h> +#include <linux/version.h> + +#define AB3550_NAME_STRING "ab3550" +#define AB3550_ID_FORMAT_STRING "AB3550 %s" +#define AB3550_NUM_BANKS 2 +#define AB3550_NUM_EVENT_REG 5 /* These are the only registers inside AB3550 used in this main file */ /* Chip ID register */ -#define ABX_CID_BANK 0 -#define ABX_CID_REG 0x20 +#define AB3550_CID_REG 0x20 /* Interrupt event registers */ -#define ABX_EVENT_BANK 0 -#define ABX_EVENT_REG 0x22 - -/* Interrupt mask registers */ -#define AB3550_IMR3 0x2b +#define AB3550_EVENT_BANK 0 +#define AB3550_EVENT_REG 0x22 /* Read/write operation values. */ -#define ABX_PERM_RD (0x01) -#define ABX_PERM_WR (0x02) +#define AB3550_PERM_RD (0x01) +#define AB3550_PERM_WR (0x02) /* Read/write permissions. */ -#define ABX_PERM_RO (ABX_PERM_RD) -#define ABX_PERM_RW (ABX_PERM_RD | ABX_PERM_WR) +#define AB3550_PERM_RO (AB3550_PERM_RD) +#define AB3550_PERM_RW (AB3550_PERM_RD | AB3550_PERM_WR) /** - * struct abx - * @access_mutex: lock out concurrent accesses to the ABX registers - * @dev: pointer to the containing device + * struct ab3550 + * @access_mutex: lock out concurrent accesses to the AB registers * @i2c_client: I2C client for this chip - * @testreg_client: secondary client for test registers * @chip_name: name of this chip variant * @chip_id: 8 bit chip ID for this chip variant - * @work: an event handling worker - * @event_subscribers: event subscribers are listed here - * @events: current events, owned by the interrupt handler and worker + * @mask_work: a worker for writing to mask registers + * @event_lock: a lock to protect the event_mask + * @event_mask: a local copy of the mask event registers * @startup_events: a copy of the first reading of the event registers * @startup_events_read: whether the first events have been read - * @devlist: a list of handles for the subdevices - * - * This struct is PRIVATE and devices using it should NOT - * access ANY fields. */ -struct abx { +struct ab3550 { struct mutex access_mutex; - struct device *dev; - struct i2c_client *i2c_client[ABX_NUM_BANKS]; + struct i2c_client *i2c_client[AB3550_NUM_BANKS]; char chip_name[32]; u8 chip_id; - struct work_struct work; - struct blocking_notifier_head event_subscribers; - u8 events[ABX_NUM_EVENT_REG]; - u8 startup_events[ABX_NUM_EVENT_REG]; + struct work_struct mask_work; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + struct work_struct irq_work; +#endif + spinlock_t event_lock; + u8 event_mask[AB3550_NUM_EVENT_REG]; + u8 startup_events[AB3550_NUM_EVENT_REG]; bool startup_events_read; - struct abx_dev *devlist; +#ifdef CONFIG_DEBUG_FS + unsigned int debug_bank; + unsigned int debug_address; +#endif }; /** - * struct abx_reg_range + * struct ab3550_reg_range * @first: the first address of the range * @last: the last address of the range * @perm: access permissions for the range */ -struct abx_reg_range { +struct ab3550_reg_range { u8 first; u8 last; u8 perm; }; /** - * struct abx_reg_ranges + * struct ab3550_reg_ranges * @count: the number of ranges in the list * @range: the list of register ranges */ -struct abx_reg_ranges { +struct ab3550_reg_ranges { u8 count; - const struct abx_reg_range *range; + const struct ab3550_reg_range *range; }; -/** - * struct abx_devinfo - * @pdev: a platform device structure for the subdevice - * @reg_ranges: a list of register access permissions for the subdevice - */ -struct abx_devinfo { - struct platform_device pdev; - const struct abx_reg_ranges reg_ranges[ABX_NUM_BANKS]; -}; - - /* * Permissible register ranges for reading and writing per device and bank. * @@ -118,452 +113,422 @@ struct abx_devinfo { * allowed. It is assumed that write permission implies read permission * (i.e. only RO and RW permissions should be used). Ranges with write * permission must not be split up. - * - * TODO:These lists will have to be updated when more is known about Petronella. */ #define NO_RANGE {.count = 0, .range = NULL,} -static struct abx_devinfo ab3550_devs[] = { - { - .pdev = { - .name = "ab3550-dac", - .id = -1, - }, - .reg_ranges = { - { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - } - - }, - { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0xb0, - .last = 0xc3, - .perm = ABX_PERM_RW, - }, - } - +static struct +ab3550_reg_ranges ab3550_reg_ranges[AB3550_NUM_DEVICES][AB3550_NUM_BANKS] = { + [AB3550_DEVID_DAC] = { + NO_RANGE, + { + .count = 2, + .range = (struct ab3550_reg_range[]) { + { + .first = 0xb0, + .last = 0xba, + .perm = AB3550_PERM_RW, + }, + { + .first = 0xbc, + .last = 0xc3, + .perm = AB3550_PERM_RW, + }, }, }, }, - { - .pdev = { - .name = "ab3550-leds", - .id = -1, + [AB3550_DEVID_LEDS] = { + NO_RANGE, + { + .count = 2, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x5a, + .last = 0x88, + .perm = AB3550_PERM_RW, + }, + { + .first = 0x8a, + .last = 0xad, + .perm = AB3550_PERM_RW, + }, + } }, - .reg_ranges = { - { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - } - - }, - { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x5a, - .last = 0xad, - .perm = ABX_PERM_RW, - }, - } - }, + }, + [AB3550_DEVID_POWER] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x21, + .last = 0x21, + .perm = AB3550_PERM_RO, + }, + } }, + NO_RANGE, }, - { - .pdev = { - .name = "ab3550-power", - .id = -1, + [AB3550_DEVID_REGULATORS] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x69, + .last = 0xa3, + .perm = AB3550_PERM_RW, + }, + } }, - .reg_ranges = { - { - .count = 2, - .range = (struct abx_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = ABX_PERM_RO, - }, - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - - } - - }, - NO_RANGE, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x14, + .last = 0x16, + .perm = AB3550_PERM_RW, + }, + } }, }, - { - .pdev = { - .name = "ab3550-regulators", - .id = -1, + [AB3550_DEVID_SIM] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x21, + .last = 0x21, + .perm = AB3550_PERM_RO, + }, + } }, - .reg_ranges = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x14, + .last = 0x17, + .perm = AB3550_PERM_RW, + }, + } - { - .count = 2, - .range = (struct abx_reg_range[]) { - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - { - .first = 0x69, - .last = 0xa4, - .perm = ABX_PERM_RW, - }, - } - }, - NO_RANGE, }, }, - { - .pdev = { - .name = "ab3550-sim", - .id = -1, + [AB3550_DEVID_UART] = { + NO_RANGE, + NO_RANGE, + }, + [AB3550_DEVID_RTC] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x00, + .last = 0x0c, + .perm = AB3550_PERM_RW, + }, + } }, - .reg_ranges = { - { - .count = 2, - .range = (struct abx_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = ABX_PERM_RO, - }, - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - } - }, - { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x14, - .last = 0x18, - .perm = ABX_PERM_RW, - }, - } + NO_RANGE, + }, + [AB3550_DEVID_CHARGER] = { + { + .count = 2, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x10, + .last = 0x1a, + .perm = AB3550_PERM_RW, + }, + { + .first = 0x21, + .last = 0x21, + .perm = AB3550_PERM_RO, + }, + } + }, + NO_RANGE, + }, + [AB3550_DEVID_ADC] = { + NO_RANGE, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x20, + .last = 0x56, + .perm = AB3550_PERM_RW, + }, - }, + } }, }, - { - .pdev = { - .name = "ab3550-uart", - .id = -1, + [AB3550_DEVID_FUELGAUGE] = { + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x21, + .last = 0x21, + .perm = AB3550_PERM_RO, + }, + } }, - .reg_ranges = { - NO_RANGE, - NO_RANGE, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x00, + .last = 0x0e, + .perm = AB3550_PERM_RW, + }, + } }, }, - { - .pdev = { - .name = "ab3550-rtc", - .id = -1, - }, - .reg_ranges = { - { - .count = 2, - .range = (struct abx_reg_range[]) { - { - .first = 0x00, - .last = 0x0c, - .perm = ABX_PERM_RW, - }, - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - } - }, - NO_RANGE, + [AB3550_DEVID_VIBRATOR] = { + NO_RANGE, + { + .count = 1, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB3550_PERM_RW, + }, + + } }, }, - { - .pdev = { - .name = "ab3550-charger", - .id = -1, + [AB3550_DEVID_CODEC] = { + { + .count = 2, + .range = (struct ab3550_reg_range[]) { + { + .first = 0x31, + .last = 0x63, + .perm = AB3550_PERM_RW, + }, + { + .first = 0x65, + .last = 0x68, + .perm = AB3550_PERM_RW, + }, + } }, - .reg_ranges = { + NO_RANGE, + }, +}; + +static struct mfd_cell ab3550_devs[AB3550_NUM_DEVICES] = { + [AB3550_DEVID_DAC] = { + .name = "ab3550-dac", + .id = AB3550_DEVID_DAC, + .num_resources = 0, + }, + [AB3550_DEVID_LEDS] = { + .name = "ab3550-leds", + .id = AB3550_DEVID_LEDS, + }, + [AB3550_DEVID_POWER] = { + .name = "ab3550-power", + .id = AB3550_DEVID_POWER, + }, + [AB3550_DEVID_REGULATORS] = { + .name = "ab3550-regulators", + .id = AB3550_DEVID_REGULATORS, + }, + [AB3550_DEVID_SIM] = { + .name = "ab3550-sim", + .id = AB3550_DEVID_SIM, + }, + [AB3550_DEVID_UART] = { + .name = "ab3550-uart", + .id = AB3550_DEVID_UART, + }, + [AB3550_DEVID_RTC] = { + .name = "ab3550-rtc", + .id = AB3550_DEVID_RTC, + }, + [AB3550_DEVID_CHARGER] = { + .name = "ab3550-charger", + .id = AB3550_DEVID_CHARGER, + }, + [AB3550_DEVID_ADC] = { + .name = "ab3550-adc", + .id = AB3550_DEVID_ADC, + .num_resources = 10, + .resources = (struct resource[]) { { - .count = 3, - .range = (struct abx_reg_range[]) { - { - .first = 0x10, - .last = 0x1c, - .perm = ABX_PERM_RW, - }, - { - .first = 0x21, - .last = 0x21, - .perm = ABX_PERM_RO, - }, - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - } + .name = "TRIGGER-0", + .flags = IORESOURCE_IRQ, + .start = 16, + .end = 16, }, - NO_RANGE, - }, - }, - { - .pdev = { - .name = "ab3550-adc", - .id = -1, - }, - .reg_ranges = { { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - - } + .name = "TRIGGER-1", + .flags = IORESOURCE_IRQ, + .start = 17, + .end = 17, }, { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x20, - .last = 0x57, - .perm = ABX_PERM_RW, - }, - - } + .name = "TRIGGER-2", + .flags = IORESOURCE_IRQ, + .start = 18, + .end = 18, }, - }, - }, - { - .pdev = { - .name = "ab3550-fuelgauge", - .id = -1, - }, - .reg_ranges = { { - .count = 2, - .range = (struct abx_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = ABX_PERM_RO, - }, - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - } + .name = "TRIGGER-3", + .flags = IORESOURCE_IRQ, + .start = 19, + .end = 19, }, { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x00, - .last = 0x0f, - .perm = ABX_PERM_RW, - }, - } + .name = "TRIGGER-4", + .flags = IORESOURCE_IRQ, + .start = 20, + .end = 20, }, - }, - }, - { - .pdev = { - .name = "ab3550-vibrator", - .id = -1, - }, - .reg_ranges = { { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - } + .name = "TRIGGER-5", + .flags = IORESOURCE_IRQ, + .start = 21, + .end = 21, }, { - .count = 1, - .range = (struct abx_reg_range[]) { - { - .first = 0x10, - .last = 0x13, - .perm = ABX_PERM_RW, - }, - - } + .name = "TRIGGER-6", + .flags = IORESOURCE_IRQ, + .start = 22, + .end = 22, + }, + { + .name = "TRIGGER-7", + .flags = IORESOURCE_IRQ, + .start = 23, + .end = 23, + }, + { + .name = "TRIGGER-VBAT-TXON", + .flags = IORESOURCE_IRQ, + .start = 13, + .end = 13, }, - }, - }, - { - /* The codec entry must be the last one as long as the function - * get_u300_codec_device (defined below) exists. - */ - .pdev = { - .name = "ab3550-codec", - .id = -1, - }, - .reg_ranges = { { - .count = 2, - .range = (struct abx_reg_range[]) { - { - .first = 0x29, - .last = 0x2d, - .perm = ABX_PERM_RW, - }, - { - .first = 0x31, - .last = 0x68, - .perm = ABX_PERM_RW, - }, - } + .name = "TRIGGER-VBAT", + .flags = IORESOURCE_IRQ, + .start = 12, + .end = 12, }, - NO_RANGE, }, }, + [AB3550_DEVID_FUELGAUGE] = { + .name = "ab3550-fuelgauge", + .id = AB3550_DEVID_FUELGAUGE, + }, + [AB3550_DEVID_VIBRATOR] = { + .name = "ab3550-vibrator", + .id = AB3550_DEVID_VIBRATOR, + }, + [AB3550_DEVID_CODEC] = { + .name = "ab3550-codec", + .id = AB3550_DEVID_CODEC, + }, }; -u8 abx_get_chip_type(struct abx_dev *abx_dev) -{ - u8 chip = ABUNKNOWN; - - switch (abx_dev->abx->chip_id & 0xf0) { - case 0xa0: - chip = AB3000; - break; - case 0xc0: - chip = AB3100; - break; - case 0x10: - chip = AB3550; - break; - } - return chip; -} -EXPORT_SYMBOL(abx_get_chip_type); - /* * I2C transactions with error messages. */ -static int abx_i2c_master_send(struct abx *abx, u8 bank, u8 *data, u8 count) +static int ab3550_i2c_master_send(struct ab3550 *ab, u8 bank, u8 *data, + u8 count) { int err; - err = i2c_master_send(abx->i2c_client[bank], data, count); - if (err < 0) - dev_err(abx->dev, "send error: %d\n", err); - else - err = 0; - return err; + err = i2c_master_send(ab->i2c_client[bank], data, count); + if (err < 0) { + dev_err(&ab->i2c_client[0]->dev, "send error: %d\n", err); + return err; + } + return 0; } -static int abx_i2c_master_recv(struct abx *abx, u8 bank, u8 reg, u8 *data) +static int ab3550_i2c_master_recv(struct ab3550 *ab, u8 bank, u8 *data, + u8 count) { int err; - struct i2c_msg msg[2]; - - msg[0].addr = abx->i2c_client[bank]->addr; - msg[0].flags = 0x0; - msg[0].len = 1; - msg[0].buf = ® - - msg[1].addr = abx->i2c_client[bank]->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 1; - msg[1].buf = data; - - err = i2c_transfer(abx->i2c_client[bank]->adapter, msg, 2); - if (err < 0) - dev_err(abx->dev, "receive error: %d\n", err); - else - err = 0; - return err; + + err = i2c_master_recv(ab->i2c_client[bank], data, count); + if (err < 0) { + dev_err(&ab->i2c_client[0]->dev, "receive error: %d\n", err); + return err; + } + return 0; } /* - * Functionality for getting/setting mixed signal registers + * Functionality for getting/setting register values. */ -static int get_register_interruptible(struct abx *abx, u8 bank, u8 reg, - u8 *value) +static int get_register_interruptible(struct ab3550 *ab, u8 bank, u8 reg, + u8 *value) { int err; - err = mutex_lock_interruptible(&abx->access_mutex); + err = mutex_lock_interruptible(&ab->access_mutex); if (err) return err; - err = abx_i2c_master_recv(abx, bank, reg, value); - mutex_unlock(&abx->access_mutex); + + err = ab3550_i2c_master_send(ab, bank, ®, 1); + if (!err) + err = ab3550_i2c_master_recv(ab, bank, value, 1); + + mutex_unlock(&ab->access_mutex); return err; } -static int get_register_page_interruptible(struct abx *abx, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) +static int get_register_page_interruptible(struct ab3550 *ab, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) { - BUG(); - return -EINVAL; + int err; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + err = ab3550_i2c_master_send(ab, bank, &first_reg, 1); + if (!err) + err = ab3550_i2c_master_recv(ab, bank, regvals, numregs); + + mutex_unlock(&ab->access_mutex); + return err; } -static int mask_and_set_register_interruptible(struct abx *abx, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues) +static int mask_and_set_register_interruptible(struct ab3550 *ab, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) { int err = 0; - if (likely(bitmask)) { + if (bitmask) { u8 reg_bits[2] = {reg, 0}; - err = mutex_lock_interruptible(&abx->access_mutex); + err = mutex_lock_interruptible(&ab->access_mutex); if (err) return err; - if (bitmask == 0xFF) /* No need to read in this case */ + if (bitmask == 0xFF) /* No need to read in this case. */ reg_bits[1] = bitvalues; - else { + else { /* Read and modify the register value. */ u8 bits; - /* First read out the target register. */ - err = abx_i2c_master_recv(abx, bank, reg, &bits); + err = ab3550_i2c_master_send(ab, bank, ®, 1); + if (err) + goto unlock_and_return; + err = ab3550_i2c_master_recv(ab, bank, &bits, 1); if (err) goto unlock_and_return; - - /* Modify the bits. */ reg_bits[1] = ((~bitmask & bits) | - (bitmask & bitvalues)); + (bitmask & bitvalues)); } - /* Write the register */ - err = abx_i2c_master_send(abx, bank, reg_bits, 2); - + /* Write the new value. */ + err = ab3550_i2c_master_send(ab, bank, reg_bits, 2); unlock_and_return: - mutex_unlock(&abx->access_mutex); + mutex_unlock(&ab->access_mutex); } return err; } @@ -571,30 +536,31 @@ unlock_and_return: /* * Read/write permission checking functions. */ -static bool page_write_allowed(const struct abx_reg_ranges *ranges, +static bool page_write_allowed(const struct ab3550_reg_ranges *ranges, u8 first_reg, u8 last_reg) { u8 i; if (last_reg < first_reg) return false; + for (i = 0; i < ranges->count; i++) { if (first_reg < ranges->range[i].first) break; if ((last_reg <= ranges->range[i].last) && - (ranges->range[i].perm & ABX_PERM_WR)) + (ranges->range[i].perm & AB3550_PERM_WR)) return true; } return false; } -static bool reg_write_allowed(const struct abx_reg_ranges *ranges, u8 reg) +static bool reg_write_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) { return page_write_allowed(ranges, reg, reg); } -static bool page_read_allowed(const struct abx_reg_ranges *ranges, u8 first_reg, - u8 last_reg) +static bool page_read_allowed(const struct ab3550_reg_ranges *ranges, + u8 first_reg, u8 last_reg) { u8 i; @@ -610,19 +576,20 @@ static bool page_read_allowed(const struct abx_reg_ranges *ranges, u8 first_reg, /* Make sure that the entire range up to and including last_reg is * readable. This may span several of the ranges in the list. */ - while ((i < ranges->count) && (ranges->range[i].perm & ABX_PERM_RD)) { + while ((i < ranges->count) && + (ranges->range[i].perm & AB3550_PERM_RD)) { if (last_reg <= ranges->range[i].last) return true; if ((++i >= ranges->count) || (ranges->range[i].first != - (ranges->range[i - 1].last + 1))) { + (ranges->range[i - 1].last + 1))) { break; } } return false; } -static bool reg_read_allowed(const struct abx_reg_ranges *ranges, u8 reg) +static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) { return page_read_allowed(ranges, reg, reg); } @@ -630,125 +597,252 @@ static bool reg_read_allowed(const struct abx_reg_ranges *ranges, u8 reg) /* * The exported register access functionality. */ -int abx_set_register_interruptible(struct abx_dev *abx_dev, u8 bank, u8 reg, - u8 value) +int ab3550_get_chip_id(struct device *dev) { - return abx_mask_and_set_register_interruptible(abx_dev, bank, reg, - 0xFF, value); + struct ab3550 *ab = dev_get_drvdata(dev->parent); + return (int)ab->chip_id; } -EXPORT_SYMBOL(abx_set_register_interruptible); -int abx_get_register_interruptible(struct abx_dev *abx_dev, u8 bank, u8 reg, - u8 *value) +int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) { - if ((ABX_NUM_BANKS <= bank) || - !reg_read_allowed(&abx_dev->devinfo->reg_ranges[bank], reg)) + struct ab3550 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB3550_NUM_BANKS <= bank) || + !reg_write_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) return -EINVAL; - return get_register_interruptible(abx_dev->abx, bank, reg, value); + ab = dev_get_drvdata(dev->parent); + return mask_and_set_register_interruptible(ab, bank, reg, + bitmask, bitvalues); } -EXPORT_SYMBOL(abx_get_register_interruptible); -int abx_get_register_page_interruptible(struct abx_dev *abx_dev, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) +int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 value) { - if ((ABX_NUM_BANKS <= bank) || - !page_read_allowed(&abx_dev->devinfo->reg_ranges[bank], - first_reg, (first_reg + numregs - 1))) + return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, + value); +} + +int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value) +{ + struct ab3550 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB3550_NUM_BANKS <= bank) || + !reg_read_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) return -EINVAL; - return get_register_page_interruptible(abx_dev->abx, bank, first_reg, - regvals, numregs); + ab = dev_get_drvdata(dev->parent); + return get_register_interruptible(ab, bank, reg, value); } -EXPORT_SYMBOL(abx_get_register_page_interruptible); -int abx_mask_and_set_register_interruptible(struct abx_dev *abx_dev, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues) +int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) { - if ((ABX_NUM_BANKS <= bank) || - !reg_write_allowed(&abx_dev->devinfo->reg_ranges[bank], reg)) + struct ab3550 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB3550_NUM_BANKS <= bank) || + !page_read_allowed(&ab3550_reg_ranges[pdev->id][bank], + first_reg, (first_reg + numregs - 1))) return -EINVAL; - return mask_and_set_register_interruptible(abx_dev->abx, bank, reg, - bitmask, bitvalues); + ab = dev_get_drvdata(dev->parent); + return get_register_page_interruptible(ab, bank, first_reg, regvals, + numregs); } -EXPORT_SYMBOL(abx_mask_and_set_register_interruptible); -/* - * Register a simple callback for handling any AB3550 events. - */ -int abx_event_register(struct abx_dev *abx_dev, struct notifier_block *nb) +int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event) { - return blocking_notifier_chain_register( - &abx_dev->abx->event_subscribers, nb); + struct ab3550 *ab; + + ab = dev_get_drvdata(dev->parent); + if (!ab->startup_events_read) + return -EAGAIN; /* Try again later */ + + memcpy(event, ab->startup_events, AB3550_NUM_EVENT_REG); + return 0; } -EXPORT_SYMBOL(abx_event_register); -/* - * Remove a previously registered callback. - */ -int abx_event_unregister(struct abx_dev *abx_dev, struct notifier_block *nb) +int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) { - return blocking_notifier_chain_unregister( - &abx_dev->abx->event_subscribers, nb); + struct ab3550 *ab; + struct ab3550_platform_data *plf_data; + bool val; + + ab = get_irq_chip_data(irq); + plf_data = ab->i2c_client[0]->dev.platform_data; + irq -= plf_data->irq.base; + val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0); + + return val; } -EXPORT_SYMBOL(abx_event_unregister); -int abx_event_registers_startup_state_get(struct abx_dev *abx_dev, u8 *event) +static struct abx500_ops ab3550_ops = { + .get_chip_id = ab3550_get_chip_id, + .get_register = ab3550_get_register_interruptible, + .set_register = ab3550_set_register_interruptible, + .get_register_page = ab3550_get_register_page_interruptible, + .set_register_page = NULL, + .mask_and_set_register = ab3550_mask_and_set_register_interruptible, + .event_registers_startup_state_get = + ab3550_event_registers_startup_state_get, + .startup_irq_enabled = ab3550_startup_irq_enabled, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) +static irqreturn_t ab3550_irq_handler(int irq, void *data) { - if (!abx_dev->abx->startup_events_read) - return -EAGAIN; /* Try again later */ - memcpy(event, abx_dev->abx->startup_events, ABX_NUM_EVENT_REG); - return 0; + struct ab3550 *ab = data; + /* + * Disable the IRQ and dispatch a worker to handle the + * event. Since the chip resides on I2C this is slow + * stuff and we will re-enable the interrupts once the + * worker has finished. + */ + disable_irq_nosync(irq); + schedule_work(&ab->irq_work); + return IRQ_HANDLED; } -EXPORT_SYMBOL(abx_event_registers_startup_state_get); - -/* Interrupt handling worker */ -static void abx_work(struct work_struct *work) +static void ab3550_irq_work(struct work_struct *work) { - struct abx *abx = container_of(work, struct abx, work); + struct ab3550 *ab = container_of(work, struct ab3550, irq_work); int err; - int i; - - err = get_register_page_interruptible(abx, ABX_EVENT_BANK, - ABX_EVENT_REG, abx->events, ABX_NUM_EVENT_REG); + unsigned int i; + u8 e[AB3550_NUM_EVENT_REG]; + u8 *events; + unsigned long flags; + + events = (ab->startup_events_read ? e : ab->startup_events); + err = get_register_page_interruptible(ab, AB3550_EVENT_BANK, + AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG); if (err) goto err_event_wq; - - if (!abx->startup_events_read) { - memcpy(abx->startup_events, abx->events, ABX_NUM_EVENT_REG); - abx->startup_events_read = true; + if (!ab->startup_events_read) { + dev_info(&ab->i2c_client[0]->dev, + "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n", + ab->startup_events[0], ab->startup_events[1], + ab->startup_events[2], ab->startup_events[3], + ab->startup_events[4]); + ab->startup_events_read = true; + goto out; } - - /* - * The notified parties will have to mask out the events - * they're interested in and react to them. They will be - * notified on all events, then they use the event array - * to determine if they're interested. + events[4] &= 0x3f; /* The two highest bits in event[4] are not used. */ + spin_lock_irqsave(&ab->event_lock, flags); + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) + events[i] &= ~ab->event_mask[i]; + spin_unlock_irqrestore(&ab->event_lock, flags); + + /* TODO: Can this disabling be "merged" with the + * spin_lock_irqsave/spin_unlock_irqrestore above? */ - blocking_notifier_call_chain(&abx->event_subscribers, ABX_NUM_EVENT_REG, - abx->events); - - for (i = 0; i < ABX_NUM_EVENT_REG; i++) - dev_dbg(abx->dev, "IRQ Event[%d]: 0x%2x\n", i, abx->events[i]); - + local_irq_disable(); + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { + u8 bit; + u8 event_reg; + dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n", + i, events[i]); + event_reg = events[i]; + for (bit = 0; event_reg; bit++, event_reg /= 2) { + if (event_reg % 2) { + unsigned int irq; + struct irq_desc *desc; + struct ab3550_platform_data *plf_data; + + plf_data = ab->i2c_client[0]->dev.platform_data; + irq = plf_data->irq.base + (i * 8) + bit; + desc = irq_to_desc(irq); + if (desc->status & IRQ_DISABLED) + note_interrupt(irq, desc, IRQ_NONE); + else + desc->handle_irq(irq, desc); + } + } + } + local_irq_enable(); +out: /* By now the IRQ should be acked and deasserted so enable it again */ - enable_irq(abx->i2c_client[0]->irq); + enable_irq(ab->i2c_client[0]->irq); return; err_event_wq: - dev_dbg(abx->dev, "error in event workqueue\n"); + dev_dbg(&ab->i2c_client[0]->dev, "error in event workqueue\n"); /* Enable the IRQ anyway, what choice do we have? */ - enable_irq(abx->i2c_client[0]->irq); + enable_irq(ab->i2c_client[0]->irq); return; } -#ifdef CONFIG_DEBUG_FS +#else +static irqreturn_t ab3550_irq_handler(int irq, void *data) +{ + struct ab3550 *ab = data; + int err; + unsigned int i; + u8 e[AB3550_NUM_EVENT_REG]; + u8 *events; + unsigned long flags; + + events = (ab->startup_events_read ? e : ab->startup_events); + + err = get_register_page_interruptible(ab, AB3550_EVENT_BANK, + AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG); + if (err) + goto err_event_rd; + + if (!ab->startup_events_read) { + dev_info(&ab->i2c_client[0]->dev, + "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n", + ab->startup_events[0], ab->startup_events[1], + ab->startup_events[2], ab->startup_events[3], + ab->startup_events[4]); + ab->startup_events_read = true; + goto out; + } + + /* The two highest bits in event[4] are not used. */ + events[4] &= 0x3f; + + spin_lock_irqsave(&ab->event_lock, flags); + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) + events[i] &= ~ab->event_mask[i]; + spin_unlock_irqrestore(&ab->event_lock, flags); -static struct abx_reg_ranges debug_ranges[ABX_NUM_BANKS] = { + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { + u8 bit; + u8 event_reg; + + dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n", + i, events[i]); + + event_reg = events[i]; + for (bit = 0; event_reg; bit++, event_reg /= 2) { + if (event_reg % 2) { + unsigned int irq; + struct ab3550_platform_data *plf_data; + + plf_data = ab->i2c_client[0]->dev.platform_data; + irq = plf_data->irq.base + (i * 8) + bit; + handle_nested_irq(irq); + } + } + } +out: + return IRQ_HANDLED; + +err_event_rd: + dev_dbg(&ab->i2c_client[0]->dev, "error reading event registers\n"); + return IRQ_HANDLED; +} +#endif + +#ifdef CONFIG_DEBUG_FS +static struct ab3550_reg_ranges debug_ranges[AB3550_NUM_BANKS] = { { .count = 6, - .range = (struct abx_reg_range[]) { + .range = (struct ab3550_reg_range[]) { { .first = 0x00, .last = 0x0e, @@ -771,13 +865,13 @@ static struct abx_reg_ranges debug_ranges[ABX_NUM_BANKS] = { }, { .first = 0xa5, - .last = 0xa9, + .last = 0xa8, }, } }, { - .count = 5, - .range = (struct abx_reg_range[]) { + .count = 8, + .range = (struct ab3550_reg_range[]) { { .first = 0x00, .last = 0x0e, @@ -788,28 +882,40 @@ static struct abx_reg_ranges debug_ranges[ABX_NUM_BANKS] = { }, { .first = 0x1a, + .last = 0x1c, + }, + { + .first = 0x20, .last = 0x56, }, { .first = 0x5a, - .last = 0xac, + .last = 0x88, + }, + { + .first = 0x8a, + .last = 0xad, }, { .first = 0xb0, - .last = 0xc2, + .last = 0xba, + }, + { + .first = 0xbc, + .last = 0xc3, }, } }, }; -static int abx_registers_print(struct seq_file *s, void *p) +static int ab3550_registers_print(struct seq_file *s, void *p) { - struct abx *abx = s->private; + struct ab3550 *ab = s->private; int bank; - seq_printf(s, ABX_NAME_STRING " register values:\n"); + seq_printf(s, AB3550_NAME_STRING " register values:\n"); - for (bank = 0; bank < ABX_NUM_BANKS; bank++) { + for (bank = 0; bank < AB3550_NUM_BANKS; bank++) { unsigned int i; seq_printf(s, " bank %d:\n", bank); @@ -821,253 +927,264 @@ static int abx_registers_print(struct seq_file *s, void *p) reg++) { u8 value; - get_register_interruptible(abx, bank, reg, + get_register_interruptible(ab, bank, reg, &value); seq_printf(s, " [%d/0x%02X]: 0x%02X\n", bank, - reg, value); + reg, value); } } } return 0; } -static int abx_registers_open(struct inode *inode, struct file *file) +static int ab3550_registers_open(struct inode *inode, struct file *file) { - return single_open(file, abx_registers_print, inode->i_private); + return single_open(file, ab3550_registers_print, inode->i_private); } -static const struct file_operations abx_registers_fops = { - .open = abx_registers_open, +static const struct file_operations ab3550_registers_fops = { + .open = ab3550_registers_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .owner = THIS_MODULE, }; -struct abx_get_set_reg_priv { - struct abx *abx; - bool mode; -}; - -static int abx_get_set_reg_open_file(struct inode *inode, struct file *file) +static int ab3550_bank_print(struct seq_file *s, void *p) { - file->private_data = inode->i_private; + struct ab3550 *ab = s->private; + + seq_printf(s, "%d\n", ab->debug_bank); return 0; } -static ssize_t abx_get_set_reg(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) +static int ab3550_bank_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab3550_bank_print, inode->i_private); +} + +static ssize_t ab3550_bank_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { - struct abx_get_set_reg_priv *priv = file->private_data; - struct abx *abx = priv->abx; + struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; char buf[32]; int buf_size; - int regp; - int bankp; - unsigned long user_reg; unsigned long user_bank; int err; - int i = 0; - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf)-1)); + buf_size = min(count, (sizeof(buf) - 1)); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; buf[buf_size] = 0; - /* - * The idea is here to parse a string which is either - * "b 0xnn" for reading a register with adress 0xnn in - * bank b, or "b 0xaa 0xbb" for writing 0xbb to the - * register 0xaa in bank b. First move past - * whitespace and then begin to parse the bank. - */ - while ((i < buf_size) && (buf[i] == ' ')) - i++; - if (i >= (buf_size - 1)) - goto wrong_input; - bankp = i; - - /* - * Advance pointer to end of string then terminate - * the bank string. This is needed to satisfy - * the strict_strtoul() function. - */ - while ((i < buf_size) && (buf[i] != ' ')) - i++; - if (i >= buf_size) - goto wrong_input; - buf[i++] = '\0'; - - err = strict_strtoul(&buf[bankp], 0, &user_bank); + err = strict_strtoul(buf, 0, &user_bank); if (err) - goto wrong_input; - if (user_bank >= ABX_NUM_BANKS) { - dev_err(abx->dev, "debug input error: invalid bank number"); + return -EINVAL; + + if (user_bank >= AB3550_NUM_BANKS) { + dev_err(&ab->i2c_client[0]->dev, + "debugfs error input > number of banks\n"); return -EINVAL; } - while ((i < buf_size) && (buf[i] == ' ')) - i++; - if (i >= (buf_size - 1)) - goto wrong_input; - regp = i; + ab->debug_bank = user_bank; - /* - * Advance pointer to end of string then terminate - * the register string. This is needed to satisfy - * the strict_strtoul() function. - */ - while ((i < buf_size) && (buf[i] != ' ')) - i++; - if ((i >= buf_size) && priv->mode) - goto wrong_input; - buf[i] = '\0'; + return buf_size; +} + +static int ab3550_address_print(struct seq_file *s, void *p) +{ + struct ab3550 *ab = s->private; + + seq_printf(s, "0x%02X\n", ab->debug_address); + return 0; +} + +static int ab3550_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab3550_address_print, inode->i_private); +} - err = strict_strtoul(&buf[regp], 0, &user_reg); +static ssize_t ab3550_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_address; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_address); if (err) - goto wrong_input; - if (user_reg > 0xff) { - dev_err(abx->dev, - "debug input error: invalid register address"); + return -EINVAL; + if (user_address > 0xff) { + dev_err(&ab->i2c_client[0]->dev, + "debugfs error input > 0xff\n"); return -EINVAL; } + ab->debug_address = user_address; + return buf_size; +} - /* Either we read or we write a register here */ - if (!priv->mode) { - /* Reading */ - - u8 regvalue; - - get_register_interruptible(abx, (u8)user_bank, (u8)user_reg, - ®value); - - dev_info(abx->dev, - "debug read " ABX_NAME_STRING " [%d/0x%02X]: 0x%02X\n", - (u8)user_bank, (u8)user_reg, regvalue); - } else { - /* Writing */ - - int valp; - unsigned long user_value; - u8 regvalue; - - /* - * We need some value to write to - * the register so keep parsing the string - * from userspace. - */ - i++; - while ((i < buf_size) && (buf[i] == ' ')) - i++; - if (i >= (buf_size - 1)) - goto wrong_input; - valp = i; - while ((i < buf_size) && (buf[i] != ' ')) - i++; - buf[i] = '\0'; - - err = strict_strtoul(&buf[valp], 0, &user_value); - if (err) - goto wrong_input; - if (user_reg > 0xff) { - dev_err(abx->dev, - "debug input error: invalid register value"); - return -EINVAL; - } +static int ab3550_val_print(struct seq_file *s, void *p) +{ + struct ab3550 *ab = s->private; + int err; + u8 regvalue; - mask_and_set_register_interruptible(abx, (u8)user_bank, - (u8)user_reg, 0xFF, (u8)user_value); - get_register_interruptible(abx, (u8)user_bank, (u8)user_reg, - ®value); + err = get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + seq_printf(s, "0x%02X\n", regvalue); - dev_info(abx->dev, - "debug write [%d/0x%02X] with 0x%02X, " - "after readback: 0x%02X\n", - (u8)user_bank, (u8)user_reg, (u8)user_value, regvalue); + return 0; +} + +static int ab3550_val_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab3550_val_print, inode->i_private); +} + +static ssize_t ab3550_val_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_val; + int err; + u8 regvalue; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_val); + if (err) + return -EINVAL; + if (user_val > 0xff) { + dev_err(&ab->i2c_client[0]->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; } + err = mask_and_set_register_interruptible( + ab, (u8)ab->debug_bank, + (u8)ab->debug_address, 0xFF, (u8)user_val); + if (err) + return -EINVAL; + + get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + return buf_size; -wrong_input: - dev_err(abx->dev, "debug input error: should be \"B 0xRR%s\" " - "(B: bank, RR: register%s)\n", - (priv->mode ? " 0xVV" : ""), - (priv->mode ? ", VV: value" : "")); - return -EINVAL; } -static const struct file_operations abx_get_set_reg_fops = { - .open = abx_get_set_reg_open_file, - .write = abx_get_set_reg, +static const struct file_operations ab3550_bank_fops = { + .open = ab3550_bank_open, + .write = ab3550_bank_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, }; -static struct dentry *abx_dir; -static struct dentry *abx_reg_file; -static struct abx_get_set_reg_priv abx_get_priv; -static struct dentry *abx_get_reg_file; -static struct abx_get_set_reg_priv abx_set_priv; -static struct dentry *abx_set_reg_file; +static const struct file_operations ab3550_address_fops = { + .open = ab3550_address_open, + .write = ab3550_address_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; -static inline void abx_setup_debugfs(struct abx *abx) +static const struct file_operations ab3550_val_fops = { + .open = ab3550_val_open, + .write = ab3550_val_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct dentry *ab3550_dir; +static struct dentry *ab3550_reg_file; +static struct dentry *ab3550_bank_file; +static struct dentry *ab3550_address_file; +static struct dentry *ab3550_val_file; + +static inline void ab3550_setup_debugfs(struct ab3550 *ab) { - int err; + ab->debug_bank = 0; + ab->debug_address = 0x00; - abx_dir = debugfs_create_dir(ABX_NAME_STRING, NULL); - if (!abx_dir) + ab3550_dir = debugfs_create_dir(AB3550_NAME_STRING, NULL); + if (!ab3550_dir) goto exit_no_debugfs; - abx_reg_file = debugfs_create_file("registers", - S_IRUGO, abx_dir, abx, - &abx_registers_fops); - if (!abx_reg_file) { - err = -ENOMEM; + ab3550_reg_file = debugfs_create_file("all-registers", + S_IRUGO, ab3550_dir, ab, &ab3550_registers_fops); + if (!ab3550_reg_file) goto exit_destroy_dir; - } - abx_get_priv.abx = abx; - abx_get_priv.mode = false; - abx_get_reg_file = debugfs_create_file("get_reg", - S_IWUGO, abx_dir, &abx_get_priv, - &abx_get_set_reg_fops); - if (!abx_get_reg_file) { - err = -ENOMEM; + ab3550_bank_file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_bank_fops); + if (!ab3550_bank_file) goto exit_destroy_reg; - } - abx_set_priv.abx = abx; - abx_set_priv.mode = true; - abx_set_reg_file = debugfs_create_file("set_reg", - S_IWUGO, abx_dir, &abx_set_priv, - &abx_get_set_reg_fops); - if (!abx_set_reg_file) { - err = -ENOMEM; - goto exit_destroy_get_reg; - } - return; + ab3550_address_file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_address_fops); + if (!ab3550_address_file) + goto exit_destroy_bank; + + ab3550_val_file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_val_fops); + if (!ab3550_val_file) + goto exit_destroy_address; - exit_destroy_get_reg: - debugfs_remove(abx_get_reg_file); - exit_destroy_reg: - debugfs_remove(abx_reg_file); - exit_destroy_dir: - debugfs_remove(abx_dir); - exit_no_debugfs: return; +exit_destroy_address: + debugfs_remove(ab3550_address_file); +exit_destroy_bank: + debugfs_remove(ab3550_bank_file); +exit_destroy_reg: + debugfs_remove(ab3550_reg_file); +exit_destroy_dir: + debugfs_remove(ab3550_dir); +exit_no_debugfs: + dev_err(&ab->i2c_client[0]->dev, "failed to create debugfs entries.\n"); + return; } -static inline void abx_remove_debugfs(void) + +static inline void ab3550_remove_debugfs(void) { - debugfs_remove(abx_set_reg_file); - debugfs_remove(abx_get_reg_file); - debugfs_remove(abx_reg_file); - debugfs_remove(abx_dir); + debugfs_remove(ab3550_val_file); + debugfs_remove(ab3550_address_file); + debugfs_remove(ab3550_bank_file); + debugfs_remove(ab3550_reg_file); + debugfs_remove(ab3550_dir); } -#else -static inline void abx_setup_debugfs(struct abx *abx) + +#else /* !CONFIG_DEBUG_FS */ +static inline void ab3550_setup_debugfs(struct ab3550 *ab) { } -static inline void abx_remove_debugfs(void) +static inline void ab3550_remove_debugfs(void) { } #endif @@ -1076,55 +1193,110 @@ static inline void abx_remove_debugfs(void) * Basic set-up, datastructure creation/destruction and I2C interface. * This sets up a default config in the AB3550 chip so that it * will work as expected. - * - * TODO: This should be moved to the machine specific platform data structure. */ - -struct abx_init_setting { - u8 bank; - u8 reg; - u8 setting; -}; - -static const struct abx_init_setting __initdata -ab3550_init_settings[] = { - /* TODO: Fill this with more Petronella init values. */ - { - .bank = 0, - .reg = AB3550_IMR3, - .setting = 0x00 - }, -}; - -static int __init abx_setup(struct abx *abx) +static int __init ab3550_setup(struct ab3550 *ab) { int err = 0; int i; + struct ab3550_platform_data *plf_data; + struct abx500_init_settings *settings; - for (i = 0; i < ARRAY_SIZE(ab3550_init_settings); i++) { - err = mask_and_set_register_interruptible(abx, - ab3550_init_settings[i].bank, - ab3550_init_settings[i].reg, - 0xFF, ab3550_init_settings[i].setting); + plf_data = ab->i2c_client[0]->dev.platform_data; + settings = plf_data->init_settings; + + for (i = 0; i < plf_data->init_settings_sz; i++) { + err = mask_and_set_register_interruptible(ab, + settings[i].bank, + settings[i].reg, + 0xFF, settings[i].setting); if (err) goto exit_no_setup; - } + /* If event mask register update the event mask in ab3550 */ + if ((settings[i].bank == 0) && + (AB3550_IMR1 <= settings[i].reg) && + (settings[i].reg <= AB3550_IMR5)) { + ab->event_mask[settings[i].reg - AB3550_IMR1] = + settings[i].setting; + } + } exit_no_setup: return err; } -/* This function should be removed when possible. - * See arch/arm/mach-u300/i2c.c for details. - */ -struct device *get_u300_codec_device(void) +static void ab3550_mask_work(struct work_struct *work) +{ + struct ab3550 *ab = container_of(work, struct ab3550, mask_work); + int i; + unsigned long flags; + u8 mask[AB3550_NUM_EVENT_REG]; + + spin_lock_irqsave(&ab->event_lock, flags); + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) + mask[i] = ab->event_mask[i]; + spin_unlock_irqrestore(&ab->event_lock, flags); + + for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { + int err; + + err = mask_and_set_register_interruptible(ab, 0, + (AB3550_IMR1 + i), ~0, mask[i]); + if (err) + dev_err(&ab->i2c_client[0]->dev, + "ab3550_mask_work failed 0x%x,0x%x\n", + (AB3550_IMR1 + i), mask[i]); + } +} + +static void ab3550_mask(unsigned int irq) +{ + unsigned long flags; + struct ab3550 *ab; + struct ab3550_platform_data *plf_data; + + ab = get_irq_chip_data(irq); + plf_data = ab->i2c_client[0]->dev.platform_data; + irq -= plf_data->irq.base; + + spin_lock_irqsave(&ab->event_lock, flags); + ab->event_mask[irq / 8] |= BIT(irq % 8); + spin_unlock_irqrestore(&ab->event_lock, flags); + + schedule_work(&ab->mask_work); +} + +static void ab3550_unmask(unsigned int irq) +{ + unsigned long flags; + struct ab3550 *ab; + struct ab3550_platform_data *plf_data; + + ab = get_irq_chip_data(irq); + plf_data = ab->i2c_client[0]->dev.platform_data; + irq -= plf_data->irq.base; + + spin_lock_irqsave(&ab->event_lock, flags); + ab->event_mask[irq / 8] &= ~BIT(irq % 8); + spin_unlock_irqrestore(&ab->event_lock, flags); + + schedule_work(&ab->mask_work); +} + +static void noop(unsigned int irq) { - /* For now, we simply assume that the codec driver is the last entry in - * ab3550_devs. - */ - return &ab3550_devs[ARRAY_SIZE(ab3550_devs) - 1].pdev.dev; } -EXPORT_SYMBOL(get_u300_codec_device); + +static struct irq_chip ab3550_irq_chip = { + .name = "ab3550-core", /* Keep the same name as the request */ + .startup = NULL, /* defaults to enable */ + .shutdown = NULL, /* defaults to disable */ + .enable = NULL, /* defaults to unmask */ + .disable = ab3550_mask, /* No default to mask in chip.c */ + .ack = noop, + .mask = ab3550_mask, + .unmask = ab3550_unmask, + .end = NULL, +}; struct ab_family_id { u8 id; @@ -1134,7 +1306,7 @@ struct ab_family_id { static const struct ab_family_id ids[] __initdata = { /* AB3550 */ { - .id = 0x10, + .id = AB3550_P1A, .name = "P1A" }, /* Terminator */ @@ -1143,43 +1315,32 @@ static const struct ab_family_id ids[] __initdata = { } }; -static int __init abx_probe(struct i2c_client *client, +static int __init ab3550_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct abx *abx; - struct abx_platform_data *abx_plf_data = client->dev.platform_data; + struct ab3550 *ab; + struct ab3550_platform_data *ab3550_plf_data = + client->dev.platform_data; int err; int i; int num_i2c_clients = 0; - abx = kzalloc(sizeof(struct abx), GFP_KERNEL); - if (!abx) { + ab = kzalloc(sizeof(struct ab3550), GFP_KERNEL); + if (!ab) { dev_err(&client->dev, - "could not allocate " ABX_NAME_STRING " device\n"); + "could not allocate " AB3550_NAME_STRING " device\n"); return -ENOMEM; } - abx->devlist = kcalloc(ARRAY_SIZE(ab3550_devs), - sizeof(struct abx_dev), GFP_KERNEL); - if (!abx->devlist) { - dev_err(&client->dev, - "could not allocate " ABX_NAME_STRING " subdevices\n"); - err = -ENOMEM; - goto free_abx_and_return; - } /* Initialize data structure */ - mutex_init(&abx->access_mutex); - BLOCKING_INIT_NOTIFIER_HEAD(&abx->event_subscribers); + mutex_init(&ab->access_mutex); + spin_lock_init(&ab->event_lock); + ab->i2c_client[0] = client; - abx->i2c_client[num_i2c_clients] = client; - num_i2c_clients++; - abx->dev = &client->dev; - - i2c_set_clientdata(client, abx); + i2c_set_clientdata(client, ab); /* Read chip ID register */ - err = get_register_interruptible(abx, ABX_CID_BANK, ABX_CID_REG, - &abx->chip_id); + err = get_register_interruptible(ab, 0, AB3550_CID_REG, &ab->chip_id); if (err) { dev_err(&client->dev, "could not communicate with the analog " "baseband chip\n"); @@ -1187,130 +1348,165 @@ static int __init abx_probe(struct i2c_client *client, } for (i = 0; ids[i].id != 0x0; i++) { - if (ids[i].id == abx->chip_id) { - snprintf(&abx->chip_name[0], sizeof(abx->chip_name) - 1, - ABX_ID_FORMAT_STRING, ids[i].name); + if (ids[i].id == ab->chip_id) { + snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1, + AB3550_ID_FORMAT_STRING, ids[i].name); break; } } if (ids[i].id == 0x0) { dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n", - abx->chip_id); + ab->chip_id); dev_err(&client->dev, "driver not started!\n"); goto exit_no_detect; } - dev_info(&client->dev, "detected chip: %s\n", &abx->chip_name[0]); + dev_info(&client->dev, "detected AB chip: %s\n", &ab->chip_name[0]); /* Attach other dummy I2C clients. */ - while (num_i2c_clients < ABX_NUM_BANKS) { - abx->i2c_client[num_i2c_clients] = + while (++num_i2c_clients < AB3550_NUM_BANKS) { + ab->i2c_client[num_i2c_clients] = i2c_new_dummy(client->adapter, (client->addr + num_i2c_clients)); - if (!abx->i2c_client[num_i2c_clients]) { + if (!ab->i2c_client[num_i2c_clients]) { err = -ENOMEM; goto exit_no_dummy_client; } - strlcpy(abx->i2c_client[num_i2c_clients]->name, id->name, - sizeof(abx->i2c_client[num_i2c_clients]->name)); - num_i2c_clients++; + strlcpy(ab->i2c_client[num_i2c_clients]->name, id->name, + sizeof(ab->i2c_client[num_i2c_clients]->name)); } - err = abx_setup(abx); + err = ab3550_setup(ab); if (err) goto exit_no_setup; - INIT_WORK(&abx->work, abx_work); - - /* Set parent and a pointer back to the container in device data. */ - for (i = 0; i < ARRAY_SIZE(ab3550_devs); i++) { - int x; - ab3550_devs[i].pdev.dev.parent = &client->dev; - ab3550_devs[i].pdev.dev.platform_data = abx_plf_data; - abx->devlist[i].abx = abx; - abx->devlist[i].devinfo = &ab3550_devs[i]; - platform_set_drvdata(&ab3550_devs[i].pdev, - &abx->devlist[i]); - x = platform_device_register(&ab3550_devs[i].pdev); - printk(KERN_DEBUG "platform device %s registered %s\n", - ab3550_devs[i].pdev.name, - x ? "unsuccessfully" : "successfully"); + INIT_WORK(&ab->irq_work, ab3550_irq_work); + INIT_WORK(&ab->mask_work, ab3550_mask_work); + + for (i = 0; i < ab3550_plf_data->irq.count; i++) { + unsigned int irq; + + irq = ab3550_plf_data->irq.base + i; + set_irq_chip_data(irq, ab); + set_irq_chip_and_handler(irq, &ab3550_irq_chip, + handle_simple_irq); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + set_irq_nested_thread(irq, 1); +#endif +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + set_irq_noprobe(irq); +#endif + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + if (client->irq > 0) { + /* This really unpredictable IRQ is of course sampled for entropy. */ + err = request_irq(client->irq, ab3550_irq_handler, + (IRQF_DISABLED | IRQF_SAMPLE_RANDOM), + "ab3550-core", ab); + if (err) + goto exit_no_irq; + + /* We probably already got an irq here, but if not, + * we force a first time and save the startup events here.*/ + disable_irq_nosync(client->irq); + schedule_work(&ab->irq_work); + } +#else + if (client->irq > 0) { + err = request_threaded_irq(client->irq, NULL, + ab3550_irq_handler, + IRQF_ONESHOT, "ab3550-core", ab); + /* This real unpredictable IRQ is of course sampled for entropy */ + rand_initialize_irq(client->irq); + + if (err) + goto exit_no_irq; } +#endif + + err = abx500_register_ops(&client->dev, &ab3550_ops); + if (err) + goto exit_no_ops; + + /* Set up and register the platform devices. */ + for (i = 0; i < AB3550_NUM_DEVICES; i++) { + ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i]; + ab3550_devs[i].data_size = ab3550_plf_data->dev_data_sz[i]; + } + + err = mfd_add_devices(&client->dev, 0, ab3550_devs, + ARRAY_SIZE(ab3550_devs), NULL, + ab3550_plf_data->irq.base); - abx_setup_debugfs(abx); + ab3550_setup_debugfs(ab); return 0; +exit_no_ops: +exit_no_irq: exit_no_setup: exit_no_dummy_client: /* Unregister the dummy i2c clients. */ while (--num_i2c_clients) - i2c_unregister_device(abx->i2c_client[num_i2c_clients]); - + i2c_unregister_device(ab->i2c_client[num_i2c_clients]); exit_no_detect: - kfree(abx->devlist); -free_abx_and_return: - kfree(abx); + kfree(ab); return err; } -static int __exit abx_remove(struct i2c_client *client) +static int __exit ab3550_remove(struct i2c_client *client) { - struct abx *abx = i2c_get_clientdata(client); - int i; - int num_i2c_clients = ABX_NUM_BANKS; - - /* Unregister subdevices */ - for (i = 0; i < ARRAY_SIZE(ab3550_devs); i++) - platform_device_unregister(&ab3550_devs[i].pdev); + struct ab3550 *ab = i2c_get_clientdata(client); + int num_i2c_clients = AB3550_NUM_BANKS; - abx_remove_debugfs(); + mfd_remove_devices(&client->dev); + ab3550_remove_debugfs(); - while (num_i2c_clients > 1) { - num_i2c_clients--; - i2c_unregister_device(abx->i2c_client[num_i2c_clients]); - } + while (--num_i2c_clients) + i2c_unregister_device(ab->i2c_client[num_i2c_clients]); /* * At this point, all subscribers should have unregistered * their notifiers so deactivate IRQ */ - free_irq(client->irq, abx); - kfree(abx->devlist); - kfree(abx); + free_irq(client->irq, ab); + i2c_set_clientdata(client, NULL); + kfree(ab); return 0; } -static const struct i2c_device_id abx_id[] = { - { ABX_NAME_STRING, 0 }, - { } +static const struct i2c_device_id ab3550_id[] = { + {AB3550_NAME_STRING, 0}, + {} }; -MODULE_DEVICE_TABLE(i2c, abx_id); +MODULE_DEVICE_TABLE(i2c, ab3550_id); -static struct i2c_driver abx_driver = { +static struct i2c_driver ab3550_driver = { .driver = { - .name = ABX_NAME_STRING, + .name = AB3550_NAME_STRING, .owner = THIS_MODULE, }, - .id_table = abx_id, - .probe = abx_probe, - .remove = __exit_p(abx_remove), + .id_table = ab3550_id, + .probe = ab3550_probe, + .remove = __exit_p(ab3550_remove), }; -static int __init abx_i2c_init(void) +static int __init ab3550_i2c_init(void) { - return i2c_add_driver(&abx_driver); + return i2c_add_driver(&ab3550_driver); } -static void __exit abx_i2c_exit(void) +static void __exit ab3550_i2c_exit(void) { - i2c_del_driver(&abx_driver); + i2c_del_driver(&ab3550_driver); } -subsys_initcall(abx_i2c_init); -module_exit(abx_i2c_exit); +subsys_initcall(ab3550_i2c_init); +module_exit(ab3550_i2c_exit); -MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>"); +MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>"); MODULE_DESCRIPTION("AB3550 core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c new file mode 100644 index 00000000000..22d02fe4e45 --- /dev/null +++ b/drivers/mfd/abx500-core.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2007-2010 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Register access functions for the ABX500 Mixed Signal IC family. + * Author: Mattias Wallin <mattias.wallin@stericsson.com> + */ + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/mfd/abx500.h> + +static LIST_HEAD(abx500_list); + +struct abx500_device_entry { + struct list_head list; + struct abx500_ops ops; + struct device *dev; +}; + +static void lookup_ops(struct device *dev, struct abx500_ops **ops) +{ + struct abx500_device_entry *dev_entry; + + *ops = NULL; + list_for_each_entry(dev_entry, &abx500_list, list) { + if (dev_entry->dev == dev) { + *ops = &dev_entry->ops; + return; + } + } +} + +int abx500_register_ops(struct device *dev, struct abx500_ops *ops) +{ + struct abx500_device_entry *dev_entry; + + dev_entry = kmalloc(sizeof(struct abx500_device_entry), GFP_KERNEL); + if (dev_entry) { + dev_err(dev, "register_ops kzalloc failed"); + return -ENOMEM; + } + dev_entry->dev = dev; + memcpy(&dev_entry->ops, ops, sizeof(struct abx500_ops)); + + list_add_tail(&dev_entry->list, &abx500_list); + return 0; +} +EXPORT_SYMBOL(abx500_register_ops); + +void abx500_remove_ops(struct device *dev) +{ + struct abx500_device_entry *dev_entry, *tmp; + + list_for_each_entry_safe(dev_entry, tmp, &abx500_list, list) + { + if (dev_entry->dev == dev) { + list_del(&dev_entry->list); + kfree(dev_entry); + } + } +} +EXPORT_SYMBOL(abx500_remove_ops); + +int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 value) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->set_register != NULL)) + return ops->set_register(dev, bank, reg, value); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_set_register_interruptible); + +int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_register != NULL)) + return ops->get_register(dev, bank, reg, value); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_register_interruptible); + +int abx500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_register_page != NULL)) + return ops->get_register_page(dev, bank, + first_reg, regvals, numregs); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_register_page_interruptible); + +int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->mask_and_set_register != NULL)) + return ops->mask_and_set_register(dev, bank, + reg, bitmask, bitvalues); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_mask_and_set_register_interruptible); + +int abx500_get_chip_id(struct device *dev) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->get_chip_id != NULL)) + return ops->get_chip_id(dev); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_get_chip_id); + +int abx500_event_registers_startup_state_get(struct device *dev, u8 *event) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->event_registers_startup_state_get != NULL)) + return ops->event_registers_startup_state_get(dev, event); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_event_registers_startup_state_get); + +int abx500_startup_irq_enabled(struct device *dev, unsigned int irq) +{ + struct abx500_ops *ops; + + lookup_ops(dev->parent, &ops); + if ((ops != NULL) && (ops->startup_irq_enabled != NULL)) + return ops->startup_irq_enabled(dev, irq); + else + return -ENOTSUPP; +} +EXPORT_SYMBOL(abx500_startup_irq_enabled); + +MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>"); +MODULE_DESCRIPTION("ABX500 core driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/abx.h b/include/linux/mfd/abx.h deleted file mode 100755 index 8a7685aae14..00000000000 --- a/include/linux/mfd/abx.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2007-2009 ST-Ericsson AB - * License terms: GNU General Public License (GPL) version 2 - * ABX core access functions - * Author: Linus Walleij <linus.walleij@stericsson.com> - */ - -#include <linux/device.h> -#include <linux/regulator/machine.h> - -#ifndef MFD_ABX_H -#define MFD_ABX_H - -#define ABUNKNOWN (0) -#define AB3000 (1) -#define AB3100 (2) -#define AB3550 (3) - -/* AB3100, STR register flags */ -#define AB3100_STR_ONSWA (0x01) -#define AB3100_STR_ONSWB (0x02) -#define AB3100_STR_ONSWC (0x04) -#define AB3100_STR_DCIO (0x08) -#define AB3100_STR_BOOT_MODE (0x10) -#define AB3100_STR_SIM_OFF (0x20) -#define AB3100_STR_BATT_REMOVAL (0x40) -#define AB3100_STR_VBUS (0x80) - -/* - * AB3100 contains 8 regulators, one external regulator controller - * and a buck converter, further the LDO E and buck converter can - * have separate settings if they are in sleep mode, this is - * modeled as a separate regulator. - */ -#define ABX_NUM_REGULATORS (10) - -/* - * Number of banks in the chip. These are mapped to I2C clients. - */ -#define ABX_NUM_BANKS (2) - -/* - * Number of event registers. - */ -#if defined(CONFIG_AB3100_CORE) - #define ABX_NUM_EVENT_REG (3) -#elif defined(CONFIG_MFD_AB3550_CORE) - #define ABX_NUM_EVENT_REG (5) -#endif - -struct abx; -struct abx_devinfo; - -/** - * struct abx_dev - * @abx: the underlying core structure - * @devinfo: information specific to the subdevice - * - * This structure is PRIVATE and devices using it should NOT - * access ANY fields. It is used as a token for calling the - * ABX functions. - */ -struct abx_dev { - struct abx *abx; - struct abx_devinfo *devinfo; -}; - -/** - * struct ab3100_platform_data - * Data supplied to initialize board connections to the AB3100 - * @reg_constraints: regulator constraints for target board - * the order of these constraints are: LDO A, C, D, E, - * F, G, H, K, EXT and BUCK. - * @reg_initvals: initial values for the regulator registers - * plus two sleep settings for LDO E and the BUCK converter. - * exactly ABX_NUM_REGULATORS+2 values must be sent in. - * Order: LDO A, C, E, E sleep, F, G, H, K, EXT, BUCK, - * BUCK sleep, LDO D. (LDO D need to be initialized last.) - * @external_voltage: voltage level of the external regulator. - */ -struct ab3100_platform_data { - struct regulation_constraints reg_constraints[ABX_NUM_REGULATORS]; - u8 reg_initvals[ABX_NUM_REGULATORS+2]; - int external_voltage; -}; - -int abx_set_register_interruptible(struct abx_dev *abx_dev, u8 bank, u8 reg, - u8 value); -int abx_get_register_interruptible(struct abx_dev *abx_dev, u8 bank, u8 reg, - u8 *value); -int abx_get_register_page_interruptible(struct abx_dev *abx_dev, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs); -/** - * This function modifies selected bits of a target register. - * - * @bitmask: Set bits to modify to 1 - * @bitvalues: The bits selected in bitmask will be written to the value - * contained in bitvalues - */ -int abx_mask_and_set_register_interruptible(struct abx_dev *abx_dev, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues); -u8 abx_get_chip_type(struct abx_dev *abx_dev); -int abx_event_register(struct abx_dev *abx_dev, struct notifier_block *nb); -int abx_event_unregister(struct abx_dev *abx_dev, struct notifier_block *nb); -int abx_event_registers_startup_state_get(struct abx_dev *abx_dev, u8 *event); - -#endif diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h new file mode 100644 index 00000000000..36628452725 --- /dev/null +++ b/include/linux/mfd/abx500.h @@ -0,0 +1,364 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * License terms: GNU General Public License v2 + * AB3100 core access functions + * Author: Linus Walleij <linus.walleij@stericsson.com> + * + * ABX500 core access functions. + * The abx500 interface is used for the Analog Baseband chip + * ab3100, ab3550, ab5500 and possibly comming. It is not used for + * ab4500 and ab8500 since they are another family of chip. + * + * Author: Mattias Wallin <mattias.wallin@stericsson.com> + * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> + * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> + * Author: Rickard Andersson <rickard.andersson@stericsson.com> + */ + +#include <linux/device.h> +#include <linux/regulator/machine.h> + +#ifndef MFD_ABX500_H +#define MFD_ABX500_H + +#define AB3100_P1A 0xc0 +#define AB3100_P1B 0xc1 +#define AB3100_P1C 0xc2 +#define AB3100_P1D 0xc3 +#define AB3100_P1E 0xc4 +#define AB3100_P1F 0xc5 +#define AB3100_P1G 0xc6 +#define AB3100_R2A 0xc7 +#define AB3100_R2B 0xc8 +#define AB3550_P1A 0x10 +#define AB5500_1_0 0x20 +#define AB5500_2_0 0x21 +#define AB5500_2_1 0x22 + +/* + * AB3100, EVENTA1, A2 and A3 event register flags + * these are catenated into a single 32-bit flag in the code + * for event notification broadcasts. + */ +#define AB3100_EVENTA1_ONSWA (0x01<<16) +#define AB3100_EVENTA1_ONSWB (0x02<<16) +#define AB3100_EVENTA1_ONSWC (0x04<<16) +#define AB3100_EVENTA1_DCIO (0x08<<16) +#define AB3100_EVENTA1_OVER_TEMP (0x10<<16) +#define AB3100_EVENTA1_SIM_OFF (0x20<<16) +#define AB3100_EVENTA1_VBUS (0x40<<16) +#define AB3100_EVENTA1_VSET_USB (0x80<<16) + +#define AB3100_EVENTA2_READY_TX (0x01<<8) +#define AB3100_EVENTA2_READY_RX (0x02<<8) +#define AB3100_EVENTA2_OVERRUN_ERROR (0x04<<8) +#define AB3100_EVENTA2_FRAMING_ERROR (0x08<<8) +#define AB3100_EVENTA2_CHARG_OVERCURRENT (0x10<<8) +#define AB3100_EVENTA2_MIDR (0x20<<8) +#define AB3100_EVENTA2_BATTERY_REM (0x40<<8) +#define AB3100_EVENTA2_ALARM (0x80<<8) + +#define AB3100_EVENTA3_ADC_TRIG5 (0x01) +#define AB3100_EVENTA3_ADC_TRIG4 (0x02) +#define AB3100_EVENTA3_ADC_TRIG3 (0x04) +#define AB3100_EVENTA3_ADC_TRIG2 (0x08) +#define AB3100_EVENTA3_ADC_TRIGVBAT (0x10) +#define AB3100_EVENTA3_ADC_TRIGVTX (0x20) +#define AB3100_EVENTA3_ADC_TRIG1 (0x40) +#define AB3100_EVENTA3_ADC_TRIG0 (0x80) + +/* AB3100, STR register flags */ +#define AB3100_STR_ONSWA (0x01) +#define AB3100_STR_ONSWB (0x02) +#define AB3100_STR_ONSWC (0x04) +#define AB3100_STR_DCIO (0x08) +#define AB3100_STR_BOOT_MODE (0x10) +#define AB3100_STR_SIM_OFF (0x20) +#define AB3100_STR_BATT_REMOVAL (0x40) +#define AB3100_STR_VBUS (0x80) + +/* + * AB3100 contains 8 regulators, one external regulator controller + * and a buck converter, further the LDO E and buck converter can + * have separate settings if they are in sleep mode, this is + * modeled as a separate regulator. + */ +#define AB3100_NUM_REGULATORS 10 + +/** + * struct ab3100 + * @access_mutex: lock out concurrent accesses to the AB3100 registers + * @dev: pointer to the containing device + * @i2c_client: I2C client for this chip + * @testreg_client: secondary client for test registers + * @chip_name: name of this chip variant + * @chip_id: 8 bit chip ID for this chip variant + * @event_subscribers: event subscribers are listed here + * @startup_events: a copy of the first reading of the event registers + * @startup_events_read: whether the first events have been read + * + * This struct is PRIVATE and devices using it should NOT + * access ANY fields. It is used as a token for calling the + * AB3100 functions. + */ +struct ab3100 { + struct mutex access_mutex; + struct device *dev; + struct i2c_client *i2c_client; + struct i2c_client *testreg_client; + char chip_name[32]; + u8 chip_id; + struct blocking_notifier_head event_subscribers; + u8 startup_events[3]; + bool startup_events_read; +}; + +/** + * struct ab3100_platform_data + * Data supplied to initialize board connections to the AB3100 + * @reg_constraints: regulator constraints for target board + * the order of these constraints are: LDO A, C, D, E, + * F, G, H, K, EXT and BUCK. + * @reg_initvals: initial values for the regulator registers + * plus two sleep settings for LDO E and the BUCK converter. + * exactly AB3100_NUM_REGULATORS+2 values must be sent in. + * Order: LDO A, C, E, E sleep, F, G, H, K, EXT, BUCK, + * BUCK sleep, LDO D. (LDO D need to be initialized last.) + * @external_voltage: voltage level of the external regulator. + */ +struct ab3100_platform_data { + struct regulator_init_data reg_constraints[AB3100_NUM_REGULATORS]; + u8 reg_initvals[AB3100_NUM_REGULATORS+2]; + int external_voltage; +}; + +/** + * ab3100_event_register() - Register a notifier to an event + * @ab3100: The ab3100 subdevice + * @nb: The notifier block to be used + */ +int ab3100_event_register(struct ab3100 *ab3100, + struct notifier_block *nb); + +/** + * ab3100_event_unregister() - Unregister a notifier to an event + * @ab3100: The ab3100 subdevice + * @nb: The notifier block to be used + */ +int ab3100_event_unregister(struct ab3100 *ab3100, + struct notifier_block *nb); + +/* AB3550, STR register flags */ +#define AB3550_STR_ONSWA (0x01) +#define AB3550_STR_ONSWB (0x02) +#define AB3550_STR_ONSWC (0x04) +#define AB3550_STR_DCIO (0x08) +#define AB3550_STR_BOOT_MODE (0x10) +#define AB3550_STR_SIM_OFF (0x20) +#define AB3550_STR_BATT_REMOVAL (0x40) +#define AB3550_STR_VBUS (0x80) + +/* Interrupt mask registers */ +#define AB3550_IMR1 0x29 +#define AB3550_IMR2 0x2a +#define AB3550_IMR3 0x2b +#define AB3550_IMR4 0x2c +#define AB3550_IMR5 0x2d + +/** + * enum ab3550_devid + * @AB3550_DEVID_ADC: Analog Digital Converter subdevice id + * @AB3550_DEVID_DAC: Digital Analog Converter subdevice id + * @AB3550_DEVID_LEDS: Leds subdevice id + * @AB3550_DEVID_POWER: Power subdevice id + * @AB3550_DEVID_REGULATORS: Regulator subdevice id + * @AB3550_DEVID_SIM: SIM subdevice id + * @AB3550_DEVID_UART: UART subdevice id + * @AB3550_DEVID_RTC: RTC subdevice id + * @AB3550_DEVID_CHARGER: Charger subdevice id + * @AB3550_DEVID_FUELGAUGE: Fuelgauge subdevice id + * @AB3550_DEVID_VIBRATOR: Vibrator subdevice id + * @AB3550_DEVID_CODEC: ALSA Codec subdevice id + * @AB3550_NUM_DEVICES: Number of subdevices + */ + +enum ab3550_devid { + AB3550_DEVID_ADC, + AB3550_DEVID_DAC, + AB3550_DEVID_LEDS, + AB3550_DEVID_POWER, + AB3550_DEVID_REGULATORS, + AB3550_DEVID_SIM, + AB3550_DEVID_UART, + AB3550_DEVID_RTC, + AB3550_DEVID_CHARGER, + AB3550_DEVID_FUELGAUGE, + AB3550_DEVID_VIBRATOR, + AB3550_DEVID_CODEC, + AB3550_NUM_DEVICES, +}; + +/** + * struct abx500_init_setting + * @bank: I2C bank to set + * @reg: I2C register address + * @setting: I2C value to set on above register and bank + * + * Initial value of the registers for driver to use during setup. + */ +struct abx500_init_settings { + u8 bank; + u8 reg; + u8 setting; +}; + +/** + * struct ab3550_platform_data + * @irq: Interrupt base and count used for events to the subdrivers + * @dev_data: Device specific data + * @dev_data_sz: Size of dev_data in bytes + * @init_settings: Initial I2C register settings + * @init_settings_sz: Size of init_settings in bytes + * + * Data supplied to initialize board connections to the AB3550 + */ +struct ab3550_platform_data { + struct {unsigned int base; unsigned int count; } irq; + void *dev_data[AB3550_NUM_DEVICES]; + size_t dev_data_sz[AB3550_NUM_DEVICES]; + struct abx500_init_settings *init_settings; + unsigned int init_settings_sz; +}; + +/** + * abx500_set_register_interruptible() - Set one target register + * @dev: The AB subdevice + * @bank: The I2C bank + * @reg: The I2C register address + * @value: The I2C value to written on above bank and reg + * + * Write to one I2C register. + */ +int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 value); + +/** + * abx500_get_register_interruptible() - Get one target register + * @dev: The AB subdevice + * @bank: The I2C bank + * @reg: The I2C register address + * @value: The I2C value read from above bank and reg + * + * Read one I2C register. + */ +int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, + u8 *value); + +/** + * abx500_get_register_page_interruptible() - Get several target registers + * @dev: The AB subdevice + * @bank: The I2C bank + * @first_reg: The first I2C register address + * @regvals: The I2C values read from above bank and registers + * @numregs: The number of registers to read + * + * Read I2C registers. + */ +int abx500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs); + +/** + * abx500_set_register_page_interruptible() - Set several target registers + * @dev: The AB subdevice + * @bank: The I2C bank + * @first_reg: The first I2C register address + * @regvals: The I2C values to be written to above bank and registers + * @numregs: The number of registers to read + * + * Read I2C registers. + */ +int abx500_set_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs); + +/** + * abx500_mask_and_set_register_inerruptible() - Modifies selected bits of a + * target register + * + * @dev: The AB subdevice + * @bank: The I2c bank number + * @reg: The I2C register address + * @bitmask: The bit mask to use + * @bitvalues: The new bit values + * + * Updates the value of an I2C register: + * value -> ((value & ~bitmask) | (bitvalues & bitmask)) + */ +int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues); + +/** + * abx500_get_chip_id() - Retreive the chip id + * @dev: The AB subdevice + * + * Read the chip id from the chip + */ +int abx500_get_chip_id(struct device *dev); + +/** + * abx500_event_registers_startup_state_get() - Get the startup state + * @dev: The AB subdevice + * @event: The event bitmask of the startup events + * + * Get an event bitmask over which events was enabled at startup. + * This function is depricated. Use abx500_startup_irq_enabled istead. + */ +int abx500_event_registers_startup_state_get(struct device *dev, u8 *event); + +/** + * abx500_startup_irq_enabled() - Check if an event was enabled at startup + * @dev: The AB subdevice + * @irq: The interrupt (or event) + * + * This functions returns 1 if an interrupt was generated at startup and + * 0 otherwise. Since subdevices are not started during startup the state + * is saved and subdevices can later ask through this functions what + * startupevents caused the startup. + */ +int abx500_startup_irq_enabled(struct device *dev, unsigned int irq); + +/** + * struct abx500_ops - I2C access funtions + * @get_chip_id: Get chip id + * @get_register: Get one register + * @set_register: Set one register + * @get_register_page: Get several registers + * @set_register_page: Set several registers + * @mask_and_set_register: Mask and set register + * @event_registers_startup_state_get: Get the startupstate + * @startup_irq_enabled: Check if a an event was enabled at startup + * + * These operations should be used by a subdevice to register a set + * of I2C register access funcions. + */ +struct abx500_ops { + int (*get_chip_id) (struct device *); + int (*get_register) (struct device *, u8, u8, u8 *); + int (*set_register) (struct device *, u8, u8, u8); + int (*get_register_page) (struct device *, u8, u8, u8 *, u8); + int (*set_register_page) (struct device *, u8, u8, u8 *, u8); + int (*mask_and_set_register) (struct device *, u8, u8, u8, u8); + int (*event_registers_startup_state_get) (struct device *, u8 *); + int (*startup_irq_enabled) (struct device *, unsigned int); +}; + +/** + * abx500_regiser_ops() - Register abx500 I2C access funcions + * @dev: The mixed signal core device + * @ops: The set of functions to register + * + * Register a set of I2C access functions to the abx500 framework. + * This is done to keep a separate namespace for several mixed signal + * chip drivers to live side by side. + */ +int abx500_register_ops(struct device *dev, struct abx500_ops *ops); +#endif |