diff options
author | Aaro Koskinen <aaro.koskinen@iki.fi> | 2013-04-09 15:51:25 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2013-04-18 18:45:20 -0400 |
commit | 95e50f6a2fe9ece6503e355400c171e0f5de61be (patch) | |
tree | d3eb87597fbe75b035e6c73887190a99edddc33f | |
parent | 360e64d8bbe7c78784d769a60d152804f5079577 (diff) |
mfd: retu: Add Tahvo support
Tahvo is a multi-function device on Nokia 770, implementing USB
transceiver and charge/battery control.
It's so close to Retu that a single driver can support both.
Signed-off-by: Aaro Koskinen <aaro.koskinen@iki.fi>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r-- | drivers/mfd/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mfd/retu-mfd.c | 85 | ||||
-rw-r--r-- | include/linux/mfd/retu.h | 8 |
3 files changed, 85 insertions, 14 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2f3ce1836181..9de05020c660 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -371,13 +371,13 @@ config MFD_VIPERBOARD | |||
371 | The drivers do not support all features the board exposes. | 371 | The drivers do not support all features the board exposes. |
372 | 372 | ||
373 | config MFD_RETU | 373 | config MFD_RETU |
374 | tristate "Nokia Retu multi-function device" | 374 | tristate "Nokia Retu and Tahvo multi-function device" |
375 | select MFD_CORE | 375 | select MFD_CORE |
376 | depends on I2C && GENERIC_HARDIRQS | 376 | depends on I2C && GENERIC_HARDIRQS |
377 | select REGMAP_IRQ | 377 | select REGMAP_IRQ |
378 | help | 378 | help |
379 | Retu is a multi-function device found on Nokia Internet Tablets | 379 | Retu and Tahvo are a multi-function devices found on Nokia |
380 | (770, N800 and N810). | 380 | Internet Tablets (770, N800 and N810). |
381 | 381 | ||
382 | config MFD_PCF50633 | 382 | config MFD_PCF50633 |
383 | tristate "NXP PCF50633" | 383 | tristate "NXP PCF50633" |
diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c index 3ba048655bf3..a1830986eeb7 100644 --- a/drivers/mfd/retu-mfd.c +++ b/drivers/mfd/retu-mfd.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Retu MFD driver | 2 | * Retu/Tahvo MFD driver |
3 | * | 3 | * |
4 | * Copyright (C) 2004, 2005 Nokia Corporation | 4 | * Copyright (C) 2004, 2005 Nokia Corporation |
5 | * | 5 | * |
@@ -33,7 +33,8 @@ | |||
33 | #define RETU_REG_ASICR 0x00 /* ASIC ID and revision */ | 33 | #define RETU_REG_ASICR 0x00 /* ASIC ID and revision */ |
34 | #define RETU_REG_ASICR_VILMA (1 << 7) /* Bit indicating Vilma */ | 34 | #define RETU_REG_ASICR_VILMA (1 << 7) /* Bit indicating Vilma */ |
35 | #define RETU_REG_IDR 0x01 /* Interrupt ID */ | 35 | #define RETU_REG_IDR 0x01 /* Interrupt ID */ |
36 | #define RETU_REG_IMR 0x02 /* Interrupt mask */ | 36 | #define RETU_REG_IMR 0x02 /* Interrupt mask (Retu) */ |
37 | #define TAHVO_REG_IMR 0x03 /* Interrupt mask (Tahvo) */ | ||
37 | 38 | ||
38 | /* Interrupt sources */ | 39 | /* Interrupt sources */ |
39 | #define RETU_INT_PWR 0 /* Power button */ | 40 | #define RETU_INT_PWR 0 /* Power button */ |
@@ -84,6 +85,62 @@ static struct regmap_irq_chip retu_irq_chip = { | |||
84 | /* Retu device registered for the power off. */ | 85 | /* Retu device registered for the power off. */ |
85 | static struct retu_dev *retu_pm_power_off; | 86 | static struct retu_dev *retu_pm_power_off; |
86 | 87 | ||
88 | static struct resource tahvo_usb_res[] = { | ||
89 | { | ||
90 | .name = "tahvo-usb", | ||
91 | .start = TAHVO_INT_VBUS, | ||
92 | .end = TAHVO_INT_VBUS, | ||
93 | .flags = IORESOURCE_IRQ, | ||
94 | }, | ||
95 | }; | ||
96 | |||
97 | static struct mfd_cell tahvo_devs[] = { | ||
98 | { | ||
99 | .name = "tahvo-usb", | ||
100 | .resources = tahvo_usb_res, | ||
101 | .num_resources = ARRAY_SIZE(tahvo_usb_res), | ||
102 | }, | ||
103 | }; | ||
104 | |||
105 | static struct regmap_irq tahvo_irqs[] = { | ||
106 | [TAHVO_INT_VBUS] = { | ||
107 | .mask = 1 << TAHVO_INT_VBUS, | ||
108 | } | ||
109 | }; | ||
110 | |||
111 | static struct regmap_irq_chip tahvo_irq_chip = { | ||
112 | .name = "TAHVO", | ||
113 | .irqs = tahvo_irqs, | ||
114 | .num_irqs = ARRAY_SIZE(tahvo_irqs), | ||
115 | .num_regs = 1, | ||
116 | .status_base = RETU_REG_IDR, | ||
117 | .mask_base = TAHVO_REG_IMR, | ||
118 | .ack_base = RETU_REG_IDR, | ||
119 | }; | ||
120 | |||
121 | static const struct retu_data { | ||
122 | char *chip_name; | ||
123 | char *companion_name; | ||
124 | struct regmap_irq_chip *irq_chip; | ||
125 | struct mfd_cell *children; | ||
126 | int nchildren; | ||
127 | } retu_data[] = { | ||
128 | [0] = { | ||
129 | .chip_name = "Retu", | ||
130 | .companion_name = "Vilma", | ||
131 | .irq_chip = &retu_irq_chip, | ||
132 | .children = retu_devs, | ||
133 | .nchildren = ARRAY_SIZE(retu_devs), | ||
134 | }, | ||
135 | [1] = { | ||
136 | .chip_name = "Tahvo", | ||
137 | .companion_name = "Betty", | ||
138 | .irq_chip = &tahvo_irq_chip, | ||
139 | .children = tahvo_devs, | ||
140 | .nchildren = ARRAY_SIZE(tahvo_devs), | ||
141 | } | ||
142 | }; | ||
143 | |||
87 | int retu_read(struct retu_dev *rdev, u8 reg) | 144 | int retu_read(struct retu_dev *rdev, u8 reg) |
88 | { | 145 | { |
89 | int ret; | 146 | int ret; |
@@ -173,9 +230,14 @@ static struct regmap_config retu_config = { | |||
173 | 230 | ||
174 | static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) | 231 | static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) |
175 | { | 232 | { |
233 | struct retu_data const *rdat; | ||
176 | struct retu_dev *rdev; | 234 | struct retu_dev *rdev; |
177 | int ret; | 235 | int ret; |
178 | 236 | ||
237 | if (i2c->addr > ARRAY_SIZE(retu_data)) | ||
238 | return -ENODEV; | ||
239 | rdat = &retu_data[i2c->addr - 1]; | ||
240 | |||
179 | rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL); | 241 | rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL); |
180 | if (rdev == NULL) | 242 | if (rdev == NULL) |
181 | return -ENOMEM; | 243 | return -ENOMEM; |
@@ -190,25 +252,27 @@ static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) | |||
190 | 252 | ||
191 | ret = retu_read(rdev, RETU_REG_ASICR); | 253 | ret = retu_read(rdev, RETU_REG_ASICR); |
192 | if (ret < 0) { | 254 | if (ret < 0) { |
193 | dev_err(rdev->dev, "could not read Retu revision: %d\n", ret); | 255 | dev_err(rdev->dev, "could not read %s revision: %d\n", |
256 | rdat->chip_name, ret); | ||
194 | return ret; | 257 | return ret; |
195 | } | 258 | } |
196 | 259 | ||
197 | dev_info(rdev->dev, "Retu%s v%d.%d found\n", | 260 | dev_info(rdev->dev, "%s%s%s v%d.%d found\n", rdat->chip_name, |
198 | (ret & RETU_REG_ASICR_VILMA) ? " & Vilma" : "", | 261 | (ret & RETU_REG_ASICR_VILMA) ? " & " : "", |
262 | (ret & RETU_REG_ASICR_VILMA) ? rdat->companion_name : "", | ||
199 | (ret >> 4) & 0x7, ret & 0xf); | 263 | (ret >> 4) & 0x7, ret & 0xf); |
200 | 264 | ||
201 | /* Mask all RETU interrupts. */ | 265 | /* Mask all interrupts. */ |
202 | ret = retu_write(rdev, RETU_REG_IMR, 0xffff); | 266 | ret = retu_write(rdev, rdat->irq_chip->mask_base, 0xffff); |
203 | if (ret < 0) | 267 | if (ret < 0) |
204 | return ret; | 268 | return ret; |
205 | 269 | ||
206 | ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1, | 270 | ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1, |
207 | &retu_irq_chip, &rdev->irq_data); | 271 | rdat->irq_chip, &rdev->irq_data); |
208 | if (ret < 0) | 272 | if (ret < 0) |
209 | return ret; | 273 | return ret; |
210 | 274 | ||
211 | ret = mfd_add_devices(rdev->dev, -1, retu_devs, ARRAY_SIZE(retu_devs), | 275 | ret = mfd_add_devices(rdev->dev, -1, rdat->children, rdat->nchildren, |
212 | NULL, regmap_irq_chip_get_base(rdev->irq_data), | 276 | NULL, regmap_irq_chip_get_base(rdev->irq_data), |
213 | NULL); | 277 | NULL); |
214 | if (ret < 0) { | 278 | if (ret < 0) { |
@@ -216,7 +280,7 @@ static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) | |||
216 | return ret; | 280 | return ret; |
217 | } | 281 | } |
218 | 282 | ||
219 | if (!pm_power_off) { | 283 | if (i2c->addr == 1 && !pm_power_off) { |
220 | retu_pm_power_off = rdev; | 284 | retu_pm_power_off = rdev; |
221 | pm_power_off = retu_power_off; | 285 | pm_power_off = retu_power_off; |
222 | } | 286 | } |
@@ -240,6 +304,7 @@ static int retu_remove(struct i2c_client *i2c) | |||
240 | 304 | ||
241 | static const struct i2c_device_id retu_id[] = { | 305 | static const struct i2c_device_id retu_id[] = { |
242 | { "retu-mfd", 0 }, | 306 | { "retu-mfd", 0 }, |
307 | { "tahvo-mfd", 0 }, | ||
243 | { } | 308 | { } |
244 | }; | 309 | }; |
245 | MODULE_DEVICE_TABLE(i2c, retu_id); | 310 | MODULE_DEVICE_TABLE(i2c, retu_id); |
diff --git a/include/linux/mfd/retu.h b/include/linux/mfd/retu.h index 1e2715d5b836..65471c4a3926 100644 --- a/include/linux/mfd/retu.h +++ b/include/linux/mfd/retu.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Retu MFD driver interface | 2 | * Retu/Tahvo MFD driver interface |
3 | * | 3 | * |
4 | * This file is subject to the terms and conditions of the GNU General | 4 | * This file is subject to the terms and conditions of the GNU General |
5 | * Public License. See the file "COPYING" in the main directory of this | 5 | * Public License. See the file "COPYING" in the main directory of this |
@@ -19,4 +19,10 @@ int retu_write(struct retu_dev *, u8, u16); | |||
19 | #define RETU_REG_CC1 0x0d /* Common control register 1 */ | 19 | #define RETU_REG_CC1 0x0d /* Common control register 1 */ |
20 | #define RETU_REG_STATUS 0x16 /* Status register */ | 20 | #define RETU_REG_STATUS 0x16 /* Status register */ |
21 | 21 | ||
22 | /* Interrupt sources */ | ||
23 | #define TAHVO_INT_VBUS 0 /* VBUS state */ | ||
24 | |||
25 | /* Interrupt status */ | ||
26 | #define TAHVO_STAT_VBUS (1 << TAHVO_INT_VBUS) | ||
27 | |||
22 | #endif /* __LINUX_MFD_RETU_H */ | 28 | #endif /* __LINUX_MFD_RETU_H */ |