summaryrefslogtreecommitdiff
path: root/drivers/mfd/twl-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/twl-core.c')
-rw-r--r--drivers/mfd/twl-core.c209
1 files changed, 137 insertions, 72 deletions
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 61e70cfaa774..7c2267e71f8b 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -34,15 +34,19 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/regulator/machine.h>
#include <linux/i2c.h>
#include <linux/i2c/twl.h>
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
-#include <plat/cpu.h>
-#endif
+#include "twl-core.h"
/*
* The TWL4030 "Triton 2" is one of a family of a multi-function "Power
@@ -110,8 +114,8 @@
#define twl_has_watchdog() false
#endif
-#if defined(CONFIG_MFD_TWL4030_AUDIO) || defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) ||\
- defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
+#if defined(CONFIG_MFD_TWL4030_AUDIO) || \
+ defined(CONFIG_MFD_TWL4030_AUDIO_MODULE)
#define twl_has_codec() true
#else
#define twl_has_codec() false
@@ -141,6 +145,7 @@
#define SUB_CHIP_ID1 1
#define SUB_CHIP_ID2 2
#define SUB_CHIP_ID3 3
+#define SUB_CHIP_ID_INVAL 0xff
#define TWL_MODULE_LAST TWL4030_MODULE_LAST
@@ -255,7 +260,6 @@ struct twl_client {
static struct twl_client twl_modules[TWL_NUM_SLAVES];
-
/* mapping the module id to slave id and base address */
struct twl_mapping {
unsigned char sid; /* Slave ID */
@@ -306,7 +310,7 @@ static struct twl_mapping twl6030_map[] = {
* so they continue to match the order in this table.
*/
{ SUB_CHIP_ID1, TWL6030_BASEADD_USB },
- { SUB_CHIP_ID3, TWL6030_BASEADD_AUDIO },
+ { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO },
{ SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },
{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },
{ SUB_CHIP_ID1, TWL6030_BASEADD_PIH },
@@ -368,6 +372,11 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
return -EPERM;
}
sid = twl_map[mod_no].sid;
+ if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
+ pr_err("%s: module %d is not part of the pmic\n",
+ DRIVER_NAME, mod_no);
+ return -EINVAL;
+ }
twl = &twl_modules[sid];
mutex_lock(&twl->xfer_lock);
@@ -425,6 +434,11 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)
return -EPERM;
}
sid = twl_map[mod_no].sid;
+ if (unlikely(sid == SUB_CHIP_ID_INVAL)) {
+ pr_err("%s: module %d is not part of the pmic\n",
+ DRIVER_NAME, mod_no);
+ return -EINVAL;
+ }
twl = &twl_modules[sid];
mutex_lock(&twl->xfer_lock);
@@ -610,6 +624,8 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
unsigned num_consumers, unsigned long features)
{
unsigned sub_chip_id;
+ struct twl_regulator_driver_data drv_data;
+
/* regulator framework demands init_data ... */
if (!pdata)
return NULL;
@@ -619,7 +635,19 @@ add_regulator_linked(int num, struct regulator_init_data *pdata,
pdata->num_consumer_supplies = num_consumers;
}
- pdata->driver_data = (void *)features;
+ if (pdata->driver_data) {
+ /* If we have existing drv_data, just add the flags */
+ struct twl_regulator_driver_data *tmp;
+ tmp = pdata->driver_data;
+ tmp->features |= features;
+ } else {
+ /* add new driver data struct, used only during init */
+ drv_data.features = features;
+ drv_data.set_voltage = NULL;
+ drv_data.get_voltage = NULL;
+ drv_data.data = NULL;
+ pdata->driver_data = &drv_data;
+ }
/* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */
sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid;
@@ -641,7 +669,8 @@ add_regulator(int num, struct regulator_init_data *pdata,
*/
static int
-add_children(struct twl4030_platform_data *pdata, unsigned long features)
+add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
+ unsigned long features)
{
struct device *child;
unsigned sub_chip_id;
@@ -649,7 +678,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_gpio() && pdata->gpio) {
child = add_child(SUB_CHIP_ID1, "twl4030_gpio",
pdata->gpio, sizeof(*pdata->gpio),
- false, pdata->irq_base + GPIO_INTR_OFFSET, 0);
+ false, irq_base + GPIO_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -657,7 +686,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_keypad() && pdata->keypad) {
child = add_child(SUB_CHIP_ID2, "twl4030_keypad",
pdata->keypad, sizeof(*pdata->keypad),
- true, pdata->irq_base + KEYPAD_INTR_OFFSET, 0);
+ true, irq_base + KEYPAD_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -665,7 +694,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_madc() && pdata->madc) {
child = add_child(2, "twl4030_madc",
pdata->madc, sizeof(*pdata->madc),
- true, pdata->irq_base + MADC_INTR_OFFSET, 0);
+ true, irq_base + MADC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -681,7 +710,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
sub_chip_id = twl_map[TWL_MODULE_RTC].sid;
child = add_child(sub_chip_id, "twl_rtc",
NULL, 0,
- true, pdata->irq_base + RTC_INTR_OFFSET, 0);
+ true, irq_base + RTC_INTR_OFFSET, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -734,17 +763,17 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
pdata->usb, sizeof(*pdata->usb),
true,
/* irq0 = USB_PRES, irq1 = USB */
- pdata->irq_base + USB_PRES_INTR_OFFSET,
- pdata->irq_base + USB_INTR_OFFSET);
+ irq_base + USB_PRES_INTR_OFFSET,
+ irq_base + USB_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
/* we need to connect regulators to this transceiver */
if (twl_has_regulator() && child) {
- usb1v5.dev = child;
- usb1v8.dev = child;
- usb3v1.dev = child;
+ usb1v5.dev_name = dev_name(child);
+ usb1v8.dev_name = dev_name(child);
+ usb3v1.dev_name = dev_name(child);
}
}
if (twl_has_usb() && pdata->usb && twl_class_is_6030()) {
@@ -783,14 +812,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
pdata->usb, sizeof(*pdata->usb),
true,
/* irq1 = VBUS_PRES, irq0 = USB ID */
- pdata->irq_base + USBOTG_INTR_OFFSET,
- pdata->irq_base + USB_PRES_INTR_OFFSET);
+ irq_base + USBOTG_INTR_OFFSET,
+ irq_base + USB_PRES_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
/* we need to connect regulators to this transceiver */
if (twl_has_regulator() && child)
- usb3v3.dev = child;
+ usb3v3.dev_name = dev_name(child);
} else if (twl_has_regulator() && twl_class_is_6030()) {
if (features & TWL6025_SUBCLASS)
child = add_regulator(TWL6025_REG_LDOUSB,
@@ -811,7 +840,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
if (twl_has_pwrbutton() && twl_class_is_4030()) {
child = add_child(1, "twl4030_pwrbutton",
- NULL, 0, true, pdata->irq_base + 8 + 0, 0);
+ NULL, 0, true, irq_base + 8 + 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -825,15 +854,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
return PTR_ERR(child);
}
- if (twl_has_codec() && pdata->audio && twl_class_is_6030()) {
- sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
- child = add_child(sub_chip_id, "twl6040",
- pdata->audio, sizeof(*pdata->audio),
- false, 0, 0);
- if (IS_ERR(child))
- return PTR_ERR(child);
- }
-
/* twl4030 regulators */
if (twl_has_regulator() && twl_class_is_4030()) {
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1,
@@ -926,6 +946,31 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
/* twl6030 regulators */
if (twl_has_regulator() && twl_class_is_6030() &&
!(features & TWL6025_SUBCLASS)) {
+ child = add_regulator(TWL6030_REG_VDD1, pdata->vdd1,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VDD2, pdata->vdd2,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_VDD3, pdata->vdd3,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_V1V8, pdata->v1v8,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
+ child = add_regulator(TWL6030_REG_V2V1, pdata->v2v1,
+ features);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+
child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc,
features);
if (IS_ERR(child))
@@ -1045,8 +1090,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
child = add_child(3, "twl4030_bci",
pdata->bci, sizeof(*pdata->bci), false,
/* irq0 = CHG_PRES, irq1 = BCI */
- pdata->irq_base + BCI_PRES_INTR_OFFSET,
- pdata->irq_base + BCI_INTR_OFFSET);
+ irq_base + BCI_PRES_INTR_OFFSET,
+ irq_base + BCI_INTR_OFFSET);
if (IS_ERR(child))
return PTR_ERR(child);
}
@@ -1146,26 +1191,24 @@ static void clocks_init(struct device *dev,
/*----------------------------------------------------------------------*/
-int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
-int twl4030_exit_irq(void);
-int twl4030_init_chip_irq(const char *chip);
-int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end);
-int twl6030_exit_irq(void);
static int twl_remove(struct i2c_client *client)
{
- unsigned i;
+ unsigned i, num_slaves;
int status;
- if (twl_class_is_4030())
+ if (twl_class_is_4030()) {
status = twl4030_exit_irq();
- else
+ num_slaves = TWL_NUM_SLAVES;
+ } else {
status = twl6030_exit_irq();
+ num_slaves = TWL_NUM_SLAVES - 1;
+ }
if (status < 0)
return status;
- for (i = 0; i < TWL_NUM_SLAVES; i++) {
+ for (i = 0; i < num_slaves; i++) {
struct twl_client *twl = &twl_modules[i];
if (twl->client && twl->client != client)
@@ -1176,15 +1219,27 @@ static int twl_remove(struct i2c_client *client)
return 0;
}
-/* NOTE: this driver only handles a single twl4030/tps659x0 chip */
+/* NOTE: This driver only handles a single twl4030/tps659x0 chip */
static int __devinit
twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
- int status;
- unsigned i;
struct twl4030_platform_data *pdata = client->dev.platform_data;
- u8 temp;
- int ret = 0;
+ struct device_node *node = client->dev.of_node;
+ int irq_base = 0;
+ int status;
+ unsigned i, num_slaves;
+
+ if (node && !pdata) {
+ /*
+ * XXX: Temporary pdata until the information is correctly
+ * retrieved by every TWL modules from DT.
+ */
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct twl4030_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ }
if (!pdata) {
dev_dbg(&client->dev, "no platform data?\n");
@@ -1201,13 +1256,23 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
return -EBUSY;
}
- for (i = 0; i < TWL_NUM_SLAVES; i++) {
- struct twl_client *twl = &twl_modules[i];
+ if ((id->driver_data) & TWL6030_CLASS) {
+ twl_id = TWL6030_CLASS_ID;
+ twl_map = &twl6030_map[0];
+ num_slaves = TWL_NUM_SLAVES - 1;
+ } else {
+ twl_id = TWL4030_CLASS_ID;
+ twl_map = &twl4030_map[0];
+ num_slaves = TWL_NUM_SLAVES;
+ }
+
+ for (i = 0; i < num_slaves; i++) {
+ struct twl_client *twl = &twl_modules[i];
twl->address = client->addr + i;
- if (i == 0)
+ if (i == 0) {
twl->client = client;
- else {
+ } else {
twl->client = i2c_new_dummy(client->adapter,
twl->address);
if (!twl->client) {
@@ -1219,22 +1284,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
mutex_init(&twl->xfer_lock);
}
+
inuse = true;
- if ((id->driver_data) & TWL6030_CLASS) {
- twl_id = TWL6030_CLASS_ID;
- twl_map = &twl6030_map[0];
- } else {
- twl_id = TWL4030_CLASS_ID;
- twl_map = &twl4030_map[0];
- }
/* setup clock framework */
clocks_init(&client->dev, pdata->clock);
/* read TWL IDCODE Register */
if (twl_id == TWL4030_CLASS_ID) {
- ret = twl_read_idcode_register();
- WARN(ret < 0, "Error: reading twl_idcode register value\n");
+ status = twl_read_idcode_register();
+ WARN(status < 0, "Error: reading twl_idcode register value\n");
}
/* load power event scripts */
@@ -1242,38 +1301,44 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
twl4030_power_init(pdata->power);
/* Maybe init the T2 Interrupt subsystem */
- if (client->irq
- && pdata->irq_base
- && pdata->irq_end > pdata->irq_base) {
+ if (client->irq) {
if (twl_class_is_4030()) {
twl4030_init_chip_irq(id->name);
- status = twl4030_init_irq(client->irq, pdata->irq_base,
- pdata->irq_end);
+ irq_base = twl4030_init_irq(&client->dev, client->irq);
} else {
- status = twl6030_init_irq(client->irq, pdata->irq_base,
- pdata->irq_end);
+ irq_base = twl6030_init_irq(&client->dev, client->irq);
}
- if (status < 0)
+ if (irq_base < 0) {
+ status = irq_base;
goto fail;
+ }
}
- /* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
+ /*
+ * Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
* Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,
* SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.
*/
-
if (twl_class_is_4030()) {
+ u8 temp;
+
twl_i2c_read_u8(TWL4030_MODULE_INTBR, &temp, REG_GPPUPDCTR1);
temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \
- I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
+ I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
}
- status = add_children(pdata, id->driver_data);
+ status = -ENODEV;
+ if (node)
+ status = of_platform_populate(node, NULL, NULL, &client->dev);
+ if (status)
+ status = add_children(pdata, irq_base, id->driver_data);
+
fail:
if (status < 0)
twl_remove(client);
+
return status;
}