diff options
-rw-r--r-- | Documentation/devicetree/bindings/mfd/twl-familly.txt | 47 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 2 | ||||
-rw-r--r-- | drivers/mfd/twl-core.c | 51 |
3 files changed, 98 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/mfd/twl-familly.txt b/Documentation/devicetree/bindings/mfd/twl-familly.txt new file mode 100644 index 00000000000..a66fcf94675 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/twl-familly.txt | |||
@@ -0,0 +1,47 @@ | |||
1 | Texas Instruments TWL family | ||
2 | |||
3 | The TWLs are Integrated Power Management Chips. | ||
4 | Some version might contain much more analog function like | ||
5 | USB transceiver or Audio amplifier. | ||
6 | These chips are connected to an i2c bus. | ||
7 | |||
8 | |||
9 | Required properties: | ||
10 | - compatible : Must be "ti,twl4030"; | ||
11 | For Integrated power-management/audio CODEC device used in OMAP3 | ||
12 | based boards | ||
13 | - compatible : Must be "ti,twl6030"; | ||
14 | For Integrated power-management used in OMAP4 based boards | ||
15 | - interrupts : This i2c device has an IRQ line connected to the main SoC | ||
16 | - interrupt-controller : Since the twl support several interrupts internally, | ||
17 | it is considered as an interrupt controller cascaded to the SoC one. | ||
18 | - #interrupt-cells = <1>; | ||
19 | - interrupt-parent : The parent interrupt controller. | ||
20 | |||
21 | Optional node: | ||
22 | - Child nodes contain in the twl. The twl family is made of several variants | ||
23 | that support a different number of features. | ||
24 | The children nodes will thus depend of the capability of the variant. | ||
25 | |||
26 | |||
27 | Example: | ||
28 | /* | ||
29 | * Integrated Power Management Chip | ||
30 | * http://www.ti.com/lit/ds/symlink/twl6030.pdf | ||
31 | */ | ||
32 | twl@48 { | ||
33 | compatible = "ti,twl6030"; | ||
34 | reg = <0x48>; | ||
35 | interrupts = <39>; /* IRQ_SYS_1N cascaded to gic */ | ||
36 | interrupt-controller; | ||
37 | #interrupt-cells = <1>; | ||
38 | interrupt-parent = <&gic>; | ||
39 | #address-cells = <1>; | ||
40 | #size-cells = <0>; | ||
41 | |||
42 | twl_rtc { | ||
43 | compatible = "ti,twl_rtc"; | ||
44 | interrupts = <11>; | ||
45 | reg = <0>; | ||
46 | }; | ||
47 | }; | ||
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0f6db32240f..08a3e087bce 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -200,7 +200,7 @@ config MENELAUS | |||
200 | 200 | ||
201 | config TWL4030_CORE | 201 | config TWL4030_CORE |
202 | bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support" | 202 | bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support" |
203 | depends on I2C=y && GENERIC_HARDIRQS | 203 | depends on I2C=y && GENERIC_HARDIRQS && IRQ_DOMAIN |
204 | help | 204 | help |
205 | Say yes here if you have TWL4030 / TWL6030 family chip on your board. | 205 | Say yes here if you have TWL4030 / TWL6030 family chip on your board. |
206 | This core driver provides register access and IRQ handling | 206 | This core driver provides register access and IRQ handling |
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 61e70cfaa77..e04e04ddc15 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c | |||
@@ -34,6 +34,11 @@ | |||
34 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <linux/clk.h> | 35 | #include <linux/clk.h> |
36 | #include <linux/err.h> | 36 | #include <linux/err.h> |
37 | #include <linux/device.h> | ||
38 | #include <linux/of.h> | ||
39 | #include <linux/of_irq.h> | ||
40 | #include <linux/of_platform.h> | ||
41 | #include <linux/irqdomain.h> | ||
37 | 42 | ||
38 | #include <linux/regulator/machine.h> | 43 | #include <linux/regulator/machine.h> |
39 | 44 | ||
@@ -144,6 +149,9 @@ | |||
144 | 149 | ||
145 | #define TWL_MODULE_LAST TWL4030_MODULE_LAST | 150 | #define TWL_MODULE_LAST TWL4030_MODULE_LAST |
146 | 151 | ||
152 | #define TWL4030_NR_IRQS 8 | ||
153 | #define TWL6030_NR_IRQS 20 | ||
154 | |||
147 | /* Base Address defns for twl4030_map[] */ | 155 | /* Base Address defns for twl4030_map[] */ |
148 | 156 | ||
149 | /* subchip/slave 0 - USB ID */ | 157 | /* subchip/slave 0 - USB ID */ |
@@ -255,6 +263,7 @@ struct twl_client { | |||
255 | 263 | ||
256 | static struct twl_client twl_modules[TWL_NUM_SLAVES]; | 264 | static struct twl_client twl_modules[TWL_NUM_SLAVES]; |
257 | 265 | ||
266 | static struct irq_domain domain; | ||
258 | 267 | ||
259 | /* mapping the module id to slave id and base address */ | 268 | /* mapping the module id to slave id and base address */ |
260 | struct twl_mapping { | 269 | struct twl_mapping { |
@@ -1183,14 +1192,48 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
1183 | int status; | 1192 | int status; |
1184 | unsigned i; | 1193 | unsigned i; |
1185 | struct twl4030_platform_data *pdata = client->dev.platform_data; | 1194 | struct twl4030_platform_data *pdata = client->dev.platform_data; |
1195 | struct device_node *node = client->dev.of_node; | ||
1186 | u8 temp; | 1196 | u8 temp; |
1187 | int ret = 0; | 1197 | int ret = 0; |
1198 | int nr_irqs = TWL4030_NR_IRQS; | ||
1199 | |||
1200 | if ((id->driver_data) & TWL6030_CLASS) | ||
1201 | nr_irqs = TWL6030_NR_IRQS; | ||
1202 | |||
1203 | if (node && !pdata) { | ||
1204 | /* | ||
1205 | * XXX: Temporary pdata until the information is correctly | ||
1206 | * retrieved by every TWL modules from DT. | ||
1207 | */ | ||
1208 | pdata = devm_kzalloc(&client->dev, | ||
1209 | sizeof(struct twl4030_platform_data), | ||
1210 | GFP_KERNEL); | ||
1211 | if (!pdata) | ||
1212 | return -ENOMEM; | ||
1213 | } | ||
1188 | 1214 | ||
1189 | if (!pdata) { | 1215 | if (!pdata) { |
1190 | dev_dbg(&client->dev, "no platform data?\n"); | 1216 | dev_dbg(&client->dev, "no platform data?\n"); |
1191 | return -EINVAL; | 1217 | return -EINVAL; |
1192 | } | 1218 | } |
1193 | 1219 | ||
1220 | status = irq_alloc_descs(-1, pdata->irq_base, nr_irqs, 0); | ||
1221 | if (IS_ERR_VALUE(status)) { | ||
1222 | dev_err(&client->dev, "Fail to allocate IRQ descs\n"); | ||
1223 | return status; | ||
1224 | } | ||
1225 | |||
1226 | pdata->irq_base = status; | ||
1227 | pdata->irq_end = pdata->irq_base + nr_irqs; | ||
1228 | |||
1229 | domain.irq_base = pdata->irq_base; | ||
1230 | domain.nr_irq = nr_irqs; | ||
1231 | #ifdef CONFIG_OF_IRQ | ||
1232 | domain.of_node = of_node_get(node); | ||
1233 | domain.ops = &irq_domain_simple_ops; | ||
1234 | #endif | ||
1235 | irq_domain_add(&domain); | ||
1236 | |||
1194 | if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { | 1237 | if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { |
1195 | dev_dbg(&client->dev, "can't talk I2C?\n"); | 1238 | dev_dbg(&client->dev, "can't talk I2C?\n"); |
1196 | return -EIO; | 1239 | return -EIO; |
@@ -1270,7 +1313,13 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
1270 | twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1); | 1313 | twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1); |
1271 | } | 1314 | } |
1272 | 1315 | ||
1273 | status = add_children(pdata, id->driver_data); | 1316 | #ifdef CONFIG_OF_DEVICE |
1317 | if (node) | ||
1318 | status = of_platform_populate(node, NULL, NULL, &client->dev); | ||
1319 | else | ||
1320 | #endif | ||
1321 | status = add_children(pdata, id->driver_data); | ||
1322 | |||
1274 | fail: | 1323 | fail: |
1275 | if (status < 0) | 1324 | if (status < 0) |
1276 | twl_remove(client); | 1325 | twl_remove(client); |