aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/mfd/Kconfig15
-rw-r--r--drivers/mfd/Makefile5
-rwxr-xr-xdrivers/mfd/ab3550-core.c1820
-rw-r--r--drivers/mfd/abx500-core.c157
-rwxr-xr-xinclude/linux/mfd/abx.h107
-rw-r--r--include/linux/mfd/abx500.h364
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 = &reg;
-
- 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, &reg, 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, &reg, 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,
- &regvalue);
-
- 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,
- &regvalue);
+ err = get_register_interruptible(ab, (u8)ab->debug_bank,
+ (u8)ab->debug_address, &regvalue);
+ 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, &regvalue);
+ 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