summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorMattias Wallin <mattias.wallin@stericsson.com>2010-05-01 18:26:20 +0200
committerSamuel Ortiz <sameo@linux.intel.com>2010-05-28 01:37:45 +0200
commitfa661258a27aa74aaf741882053d195291cefb75 (patch)
tree73b6e0c7156088f660de465555ee13226ac78242 /drivers/mfd
parent812f9e9d424dde9ccb35975c0281edb6f8543735 (diff)
downloadlinux-3.10-fa661258a27aa74aaf741882053d195291cefb75.tar.gz
linux-3.10-fa661258a27aa74aaf741882053d195291cefb75.tar.bz2
linux-3.10-fa661258a27aa74aaf741882053d195291cefb75.zip
mfd: AB3100 register access change to abx500 API
The interface for the AB3100 is changed to make way for the ABX500 family of chips: AB3550, AB5500 and future ST-Ericsson Analog Baseband chips. The register access functions are moved out to a separate struct abx500_ops. In this way the interface is moved from the implementation and the sub functionality drivers can keep their interface intact when chip infrastructure and communication mechanisms changes. We also define the AB3550 device IDs and the AB3550 platform data struct and convert the catenated 32bit event to an array of 3 x 8bits. Signed-off-by: Mattias Wallin <mattias.wallin@stericsson.com> Signed-off-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig12
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/ab3100-core.c95
-rw-r--r--drivers/mfd/ab3100-otp.c11
-rw-r--r--drivers/mfd/abx500-core.c157
5 files changed, 239 insertions, 37 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 614fa7b8a05..1c3d737ec40 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -374,9 +374,19 @@ config PCF50633_GPIO
Say yes here if you want to include support GPIO for pins on
the PCF50633 chip.
+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 AB3100_CORE
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
- depends on I2C=y
+ depends on I2C=y && ABX500_CORE
default y if ARCH_U300
help
Select this to enable the AB3100 Mixed Signal IC core
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5eb79794d5a..4b8ad04ff8e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
+obj-$(CONFIG_ABX500_CORE) += abx500-core.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index f8c4a333ff1..53ebfee548f 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -59,24 +59,15 @@
* The AB3100 is usually assigned address 0x48 (7-bit)
* The chip is defined in the platform i2c_board_data section.
*/
-
-u8 ab3100_get_chip_type(struct ab3100 *ab3100)
+static int ab3100_get_chip_id(struct device *dev)
{
- u8 chip = ABUNKNOWN;
-
- switch (ab3100->chip_id & 0xf0) {
- case 0xa0:
- chip = AB3000;
- break;
- case 0xc0:
- chip = AB3100;
- break;
- }
- return chip;
+ struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+ return (int)ab3100->chip_id;
}
-EXPORT_SYMBOL(ab3100_get_chip_type);
-int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval)
+static int ab3100_set_register_interruptible(struct ab3100 *ab3100,
+ u8 reg, u8 regval)
{
u8 regandval[2] = {reg, regval};
int err;
@@ -108,8 +99,14 @@ int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval)
mutex_unlock(&ab3100->access_mutex);
return err;
}
-EXPORT_SYMBOL(ab3100_set_register_interruptible);
+static int set_register_interruptible(struct device *dev,
+ u8 bank, u8 reg, u8 value)
+{
+ struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+ return ab3100_set_register_interruptible(ab3100, reg, value);
+}
/*
* The test registers exist at an I2C bus address up one
@@ -148,8 +145,8 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
return err;
}
-
-int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval)
+static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
+ u8 reg, u8 *regval)
{
int err;
@@ -203,10 +200,16 @@ int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval)
mutex_unlock(&ab3100->access_mutex);
return err;
}
-EXPORT_SYMBOL(ab3100_get_register_interruptible);
+static int get_register_interruptible(struct device *dev, u8 bank, u8 reg,
+ u8 *value)
+{
+ struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+ return ab3100_get_register_interruptible(ab3100, reg, value);
+}
-int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
+static int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
u8 first_reg, u8 *regvals, u8 numregs)
{
int err;
@@ -260,10 +263,17 @@ int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
mutex_unlock(&ab3100->access_mutex);
return err;
}
-EXPORT_SYMBOL(ab3100_get_register_page_interruptible);
+static int get_register_page_interruptible(struct device *dev, u8 bank,
+ u8 first_reg, u8 *regvals, u8 numregs)
+{
+ struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+ return ab3100_get_register_page_interruptible(ab3100,
+ first_reg, regvals, numregs);
+}
-int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
+static int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 andmask, u8 ormask)
{
u8 regandval[2] = {reg, 0};
@@ -331,8 +341,15 @@ int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
mutex_unlock(&ab3100->access_mutex);
return err;
}
-EXPORT_SYMBOL(ab3100_mask_and_set_register_interruptible);
+static int mask_and_set_register_interruptible(struct device *dev, u8 bank,
+ u8 reg, u8 bitmask, u8 bitvalues)
+{
+ struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+ return ab3100_mask_and_set_register_interruptible(ab3100,
+ reg, bitmask, (bitmask & bitvalues));
+}
/*
* Register a simple callback for handling any AB3100 events.
@@ -357,15 +374,27 @@ int ab3100_event_unregister(struct ab3100 *ab3100,
EXPORT_SYMBOL(ab3100_event_unregister);
-int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100,
- u32 *fatevent)
+static int ab3100_event_registers_startup_state_get(struct device *dev,
+ u8 *event)
{
+ struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
if (!ab3100->startup_events_read)
return -EAGAIN; /* Try again later */
- *fatevent = ab3100->startup_events;
+ memcpy(event, ab3100->startup_events, 3);
return 0;
}
-EXPORT_SYMBOL(ab3100_event_registers_startup_state_get);
+
+static struct abx500_ops ab3100_ops = {
+ .get_chip_id = ab3100_get_chip_id,
+ .set_register = set_register_interruptible,
+ .get_register = get_register_interruptible,
+ .get_register_page = get_register_page_interruptible,
+ .set_register_page = NULL,
+ .mask_and_set_register = mask_and_set_register_interruptible,
+ .event_registers_startup_state_get =
+ ab3100_event_registers_startup_state_get,
+ .startup_irq_enabled = NULL,
+};
/*
* This is a threaded interrupt handler so we can make some
@@ -390,7 +419,9 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data)
event_regs[2];
if (!ab3100->startup_events_read) {
- ab3100->startup_events = fatevent;
+ ab3100->startup_events[0] = event_regs[0];
+ ab3100->startup_events[1] = event_regs[1];
+ ab3100->startup_events[2] = event_regs[2];
ab3100->startup_events_read = true;
}
/*
@@ -703,7 +734,8 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
dev_warn(ab3100->dev,
"AB3100 P1E variant detected, "
"forcing chip to 32KHz\n");
- err = ab3100_set_test_register_interruptible(ab3100, 0x02, 0x08);
+ err = ab3100_set_test_register_interruptible(ab3100,
+ 0x02, 0x08);
}
exit_no_setup:
@@ -898,6 +930,10 @@ static int __init ab3100_probe(struct i2c_client *client,
if (err)
goto exit_no_irq;
+ err = abx500_register_ops(&client->dev, &ab3100_ops);
+ if (err)
+ goto exit_no_ops;
+
/* Set parent and a pointer back to the container in device data */
for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) {
ab3100_platform_devs[i]->dev.parent =
@@ -915,6 +951,7 @@ static int __init ab3100_probe(struct i2c_client *client,
return 0;
+ exit_no_ops:
exit_no_irq:
exit_no_setup:
i2c_unregister_device(ab3100->testreg_client);
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
index 7093f1aef1e..63d2b727ddb 100644
--- a/drivers/mfd/ab3100-otp.c
+++ b/drivers/mfd/ab3100-otp.c
@@ -30,7 +30,6 @@
/**
* struct ab3100_otp
* @dev containing device
- * @ab3100 a pointer to the parent ab3100 device struct
* @locked whether the OTP is locked, after locking, no more bits
* can be changed but before locking it is still possible
* to change bits from 1->0.
@@ -49,7 +48,6 @@
*/
struct ab3100_otp {
struct device *dev;
- struct ab3100 *ab3100;
bool locked;
u32 freq;
bool paf;
@@ -63,19 +61,19 @@ struct ab3100_otp {
static int __init ab3100_otp_read(struct ab3100_otp *otp)
{
- struct ab3100 *ab = otp->ab3100;
u8 otpval[8];
u8 otpp;
int err;
- err = ab3100_get_register_interruptible(ab, AB3100_OTPP, &otpp);
+ err = abx500_get_register_interruptible(otp->dev, 0,
+ AB3100_OTPP, &otpp);
if (err) {
dev_err(otp->dev, "unable to read OTPP register\n");
return err;
}
- err = ab3100_get_register_page_interruptible(ab, AB3100_OTP0,
- otpval, 8);
+ err = abx500_get_register_page_interruptible(otp->dev, 0,
+ AB3100_OTP0, otpval, 8);
if (err) {
dev_err(otp->dev, "unable to read OTP register page\n");
return err;
@@ -197,7 +195,6 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
otp->dev = &pdev->dev;
/* Replace platform data coming in with a local struct */
- otp->ab3100 = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, otp);
err = ab3100_otp_read(otp);
diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c
new file mode 100644
index 00000000000..3b3b97ec32a
--- /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 = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
+ if (IS_ERR(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");