aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRabin Vincent <rabin.vincent@stericsson.com>2010-05-19 05:39:02 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2010-05-27 19:38:00 -0400
commit62579266cf9caca5b999560be2b5ceee42fc4d4d (patch)
treeeda6066624c734ef3057a9dc568ebaf978499c1a /drivers
parent75907a1153b42100b7a5e960bfe47d208d726309 (diff)
mfd: New AB8500 driver
Add a new driver to support the AB8500 Power Management chip, replacing the current AB4500. The new driver replaces the old one, instead of an incremental modification, because this is a substantial overhaul including: - Split of the driver into -core and -spi portions, to allow another interface layer to be added - Addition of interrupt support - Switch to MFD core API for handling subdevices - Simplification of the APIs to remove a redundant block parameter - Rename of the APIs and macros from ab4500_* to ab8500_* - Rename of the files from ab4500* to ab8500* - Change of the driver name from ab4500 to ab8500 Acked-by: Linus Walleij <linus.walleij@stericsson.com> Acked-by: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/Kconfig9
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/ab4500-core.c209
-rw-r--r--drivers/mfd/ab8500-core.c444
-rw-r--r--drivers/mfd/ab8500-spi.c133
5 files changed, 583 insertions, 214 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b84b7078e909..9da0e504bbe9 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -420,11 +420,12 @@ config EZX_PCAP
420 This enables the PCAP ASIC present on EZX Phones. This is 420 This enables the PCAP ASIC present on EZX Phones. This is
421 needed for MMC, TouchScreen, Sound, USB, etc.. 421 needed for MMC, TouchScreen, Sound, USB, etc..
422 422
423config AB4500_CORE 423config AB8500_CORE
424 tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip" 424 bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
425 depends on SPI 425 depends on SPI=y && GENERIC_HARDIRQS
426 select MFD_CORE
426 help 427 help
427 Select this option to enable access to AB4500 power management 428 Select this option to enable access to AB8500 power management
428 chip. This connects to U8500 on the SSP/SPI bus and exports 429 chip. This connects to U8500 on the SSP/SPI bus and exports
429 read/write functions for the devices to get access to this chip. 430 read/write functions for the devices to get access to this chip.
430 This chip embeds various other multimedia funtionalities as well. 431 This chip embeds various other multimedia funtionalities as well.
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index ca1517e948af..fb503e77dc60 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -64,8 +64,8 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
64obj-$(CONFIG_ABX500_CORE) += abx500-core.o 64obj-$(CONFIG_ABX500_CORE) += abx500-core.o
65obj-$(CONFIG_AB3100_CORE) += ab3100-core.o 65obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
66obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o 66obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
67obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
68obj-$(CONFIG_AB3550_CORE) += ab3550-core.o 67obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
68obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o
69obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o 69obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
70obj-$(CONFIG_PMIC_ADP5520) += adp5520.o 70obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
71obj-$(CONFIG_LPC_SCH) += lpc_sch.o 71obj-$(CONFIG_LPC_SCH) += lpc_sch.o
diff --git a/drivers/mfd/ab4500-core.c b/drivers/mfd/ab4500-core.c
deleted file mode 100644
index c275daa3ab1a..000000000000
--- a/drivers/mfd/ab4500-core.c
+++ /dev/null
@@ -1,209 +0,0 @@
1/*
2 * Copyright (C) 2009 ST-Ericsson
3 *
4 * Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
5 *
6 * This program is free software; you can redistribute it
7 * and/or modify it under the terms of the GNU General Public
8 * License version 2, as published by the Free Software Foundation.
9 *
10 * AB4500 is a companion power management chip used with U8500.
11 * On this platform, this is interfaced with SSP0 controller
12 * which is a ARM primecell pl022.
13 *
14 * At the moment the module just exports read/write features.
15 * Interrupt management to be added - TODO.
16 */
17#include <linux/kernel.h>
18#include <linux/slab.h>
19#include <linux/init.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/spi/spi.h>
23#include <linux/mfd/ab4500.h>
24
25/* just required if probe fails, we need to
26 * unregister the device
27 */
28static struct spi_driver ab4500_driver;
29
30/*
31 * This funtion writes to any AB4500 registers using
32 * SPI protocol & before it writes it packs the data
33 * in the below 24 bit frame format
34 *
35 * *|------------------------------------|
36 * *| 23|22...18|17.......10|9|8|7......0|
37 * *| r/w bank adr data |
38 * * ------------------------------------
39 *
40 * This function shouldn't be called from interrupt
41 * context
42 */
43int ab4500_write(struct ab4500 *ab4500, unsigned char block,
44 unsigned long addr, unsigned char data)
45{
46 struct spi_transfer xfer;
47 struct spi_message msg;
48 int err;
49 unsigned long spi_data =
50 block << 18 | addr << 10 | data;
51
52 mutex_lock(&ab4500->lock);
53 ab4500->tx_buf[0] = spi_data;
54 ab4500->rx_buf[0] = 0;
55
56 xfer.tx_buf = ab4500->tx_buf;
57 xfer.rx_buf = NULL;
58 xfer.len = sizeof(unsigned long);
59
60 spi_message_init(&msg);
61 spi_message_add_tail(&xfer, &msg);
62
63 err = spi_sync(ab4500->spi, &msg);
64 mutex_unlock(&ab4500->lock);
65
66 return err;
67}
68EXPORT_SYMBOL(ab4500_write);
69
70int ab4500_read(struct ab4500 *ab4500, unsigned char block,
71 unsigned long addr)
72{
73 struct spi_transfer xfer;
74 struct spi_message msg;
75 unsigned long spi_data =
76 1 << 23 | block << 18 | addr << 10;
77
78 mutex_lock(&ab4500->lock);
79 ab4500->tx_buf[0] = spi_data;
80 ab4500->rx_buf[0] = 0;
81
82 xfer.tx_buf = ab4500->tx_buf;
83 xfer.rx_buf = ab4500->rx_buf;
84 xfer.len = sizeof(unsigned long);
85
86 spi_message_init(&msg);
87 spi_message_add_tail(&xfer, &msg);
88
89 spi_sync(ab4500->spi, &msg);
90 mutex_unlock(&ab4500->lock);
91
92 return ab4500->rx_buf[0];
93}
94EXPORT_SYMBOL(ab4500_read);
95
96/* ref: ab3100 core */
97#define AB4500_DEVICE(devname, devid) \
98static struct platform_device ab4500_##devname##_device = { \
99 .name = devid, \
100 .id = -1, \
101}
102
103/* list of childern devices of ab4500 - all are
104 * not populated here - TODO
105 */
106AB4500_DEVICE(charger, "ab4500-charger");
107AB4500_DEVICE(audio, "ab4500-audio");
108AB4500_DEVICE(usb, "ab4500-usb");
109AB4500_DEVICE(tvout, "ab4500-tvout");
110AB4500_DEVICE(sim, "ab4500-sim");
111AB4500_DEVICE(gpadc, "ab4500-gpadc");
112AB4500_DEVICE(clkmgt, "ab4500-clkmgt");
113AB4500_DEVICE(misc, "ab4500-misc");
114
115static struct platform_device *ab4500_platform_devs[] = {
116 &ab4500_charger_device,
117 &ab4500_audio_device,
118 &ab4500_usb_device,
119 &ab4500_tvout_device,
120 &ab4500_sim_device,
121 &ab4500_gpadc_device,
122 &ab4500_clkmgt_device,
123 &ab4500_misc_device,
124};
125
126static int __init ab4500_probe(struct spi_device *spi)
127{
128 struct ab4500 *ab4500;
129 unsigned char revision;
130 int err = 0;
131 int i;
132
133 ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL);
134 if (!ab4500) {
135 dev_err(&spi->dev, "could not allocate AB4500\n");
136 err = -ENOMEM;
137 goto not_detect;
138 }
139
140 ab4500->spi = spi;
141 spi_set_drvdata(spi, ab4500);
142
143 mutex_init(&ab4500->lock);
144
145 /* read the revision register */
146 revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG);
147
148 /* revision id 0x0 is for early drop, 0x10 is for cut1.0 */
149 if (revision == 0x0 || revision == 0x10)
150 dev_info(&spi->dev, "Detected chip: %s, revision = %x\n",
151 ab4500_driver.driver.name, revision);
152 else {
153 dev_err(&spi->dev, "unknown chip: 0x%x\n", revision);
154 goto not_detect;
155 }
156
157 for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++) {
158 ab4500_platform_devs[i]->dev.parent =
159 &spi->dev;
160 platform_set_drvdata(ab4500_platform_devs[i], ab4500);
161 }
162
163 /* register the ab4500 platform devices */
164 platform_add_devices(ab4500_platform_devs,
165 ARRAY_SIZE(ab4500_platform_devs));
166
167 return err;
168
169 not_detect:
170 spi_unregister_driver(&ab4500_driver);
171 kfree(ab4500);
172 return err;
173}
174
175static int __devexit ab4500_remove(struct spi_device *spi)
176{
177 struct ab4500 *ab4500 =
178 spi_get_drvdata(spi);
179
180 kfree(ab4500);
181
182 return 0;
183}
184
185static struct spi_driver ab4500_driver = {
186 .driver = {
187 .name = "ab4500",
188 .owner = THIS_MODULE,
189 },
190 .probe = ab4500_probe,
191 .remove = __devexit_p(ab4500_remove)
192};
193
194static int __devinit ab4500_init(void)
195{
196 return spi_register_driver(&ab4500_driver);
197}
198
199static void __exit ab4500_exit(void)
200{
201 spi_unregister_driver(&ab4500_driver);
202}
203
204subsys_initcall(ab4500_init);
205module_exit(ab4500_exit);
206
207MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
208MODULE_DESCRIPTION("AB4500 core driver");
209MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
new file mode 100644
index 000000000000..f3d26fa9c34d
--- /dev/null
+++ b/drivers/mfd/ab8500-core.c
@@ -0,0 +1,444 @@
1/*
2 * Copyright (C) ST-Ericsson SA 2010
3 *
4 * License Terms: GNU General Public License v2
5 * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
6 * Author: Rabin Vincent <rabin.vincent@stericsson.com>
7 */
8
9#include <linux/kernel.h>
10#include <linux/slab.h>
11#include <linux/init.h>
12#include <linux/irq.h>
13#include <linux/delay.h>
14#include <linux/interrupt.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/mfd/core.h>
18#include <linux/mfd/ab8500.h>
19
20/*
21 * Interrupt register offsets
22 * Bank : 0x0E
23 */
24#define AB8500_IT_SOURCE1_REG 0x0E00
25#define AB8500_IT_SOURCE2_REG 0x0E01
26#define AB8500_IT_SOURCE3_REG 0x0E02
27#define AB8500_IT_SOURCE4_REG 0x0E03
28#define AB8500_IT_SOURCE5_REG 0x0E04
29#define AB8500_IT_SOURCE6_REG 0x0E05
30#define AB8500_IT_SOURCE7_REG 0x0E06
31#define AB8500_IT_SOURCE8_REG 0x0E07
32#define AB8500_IT_SOURCE19_REG 0x0E12
33#define AB8500_IT_SOURCE20_REG 0x0E13
34#define AB8500_IT_SOURCE21_REG 0x0E14
35#define AB8500_IT_SOURCE22_REG 0x0E15
36#define AB8500_IT_SOURCE23_REG 0x0E16
37#define AB8500_IT_SOURCE24_REG 0x0E17
38
39/*
40 * latch registers
41 */
42#define AB8500_IT_LATCH1_REG 0x0E20
43#define AB8500_IT_LATCH2_REG 0x0E21
44#define AB8500_IT_LATCH3_REG 0x0E22
45#define AB8500_IT_LATCH4_REG 0x0E23
46#define AB8500_IT_LATCH5_REG 0x0E24
47#define AB8500_IT_LATCH6_REG 0x0E25
48#define AB8500_IT_LATCH7_REG 0x0E26
49#define AB8500_IT_LATCH8_REG 0x0E27
50#define AB8500_IT_LATCH9_REG 0x0E28
51#define AB8500_IT_LATCH10_REG 0x0E29
52#define AB8500_IT_LATCH19_REG 0x0E32
53#define AB8500_IT_LATCH20_REG 0x0E33
54#define AB8500_IT_LATCH21_REG 0x0E34
55#define AB8500_IT_LATCH22_REG 0x0E35
56#define AB8500_IT_LATCH23_REG 0x0E36
57#define AB8500_IT_LATCH24_REG 0x0E37
58
59/*
60 * mask registers
61 */
62
63#define AB8500_IT_MASK1_REG 0x0E40
64#define AB8500_IT_MASK2_REG 0x0E41
65#define AB8500_IT_MASK3_REG 0x0E42
66#define AB8500_IT_MASK4_REG 0x0E43
67#define AB8500_IT_MASK5_REG 0x0E44
68#define AB8500_IT_MASK6_REG 0x0E45
69#define AB8500_IT_MASK7_REG 0x0E46
70#define AB8500_IT_MASK8_REG 0x0E47
71#define AB8500_IT_MASK9_REG 0x0E48
72#define AB8500_IT_MASK10_REG 0x0E49
73#define AB8500_IT_MASK11_REG 0x0E4A
74#define AB8500_IT_MASK12_REG 0x0E4B
75#define AB8500_IT_MASK13_REG 0x0E4C
76#define AB8500_IT_MASK14_REG 0x0E4D
77#define AB8500_IT_MASK15_REG 0x0E4E
78#define AB8500_IT_MASK16_REG 0x0E4F
79#define AB8500_IT_MASK17_REG 0x0E50
80#define AB8500_IT_MASK18_REG 0x0E51
81#define AB8500_IT_MASK19_REG 0x0E52
82#define AB8500_IT_MASK20_REG 0x0E53
83#define AB8500_IT_MASK21_REG 0x0E54
84#define AB8500_IT_MASK22_REG 0x0E55
85#define AB8500_IT_MASK23_REG 0x0E56
86#define AB8500_IT_MASK24_REG 0x0E57
87
88#define AB8500_REV_REG 0x1080
89
90/*
91 * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
92 * numbers are indexed into this array with (num / 8).
93 *
94 * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at
95 * offset 0.
96 */
97static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
98 0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21,
99};
100
101static int __ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
102{
103 int ret;
104
105 dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
106
107 ret = ab8500->write(ab8500, addr, data);
108 if (ret < 0)
109 dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
110 addr, ret);
111
112 return ret;
113}
114
115/**
116 * ab8500_write() - write an AB8500 register
117 * @ab8500: device to write to
118 * @addr: address of the register
119 * @data: value to write
120 */
121int ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
122{
123 int ret;
124
125 mutex_lock(&ab8500->lock);
126 ret = __ab8500_write(ab8500, addr, data);
127 mutex_unlock(&ab8500->lock);
128
129 return ret;
130}
131EXPORT_SYMBOL_GPL(ab8500_write);
132
133static int __ab8500_read(struct ab8500 *ab8500, u16 addr)
134{
135 int ret;
136
137 ret = ab8500->read(ab8500, addr);
138 if (ret < 0)
139 dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
140 addr, ret);
141
142 dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret);
143
144 return ret;
145}
146
147/**
148 * ab8500_read() - read an AB8500 register
149 * @ab8500: device to read from
150 * @addr: address of the register
151 */
152int ab8500_read(struct ab8500 *ab8500, u16 addr)
153{
154 int ret;
155
156 mutex_lock(&ab8500->lock);
157 ret = __ab8500_read(ab8500, addr);
158 mutex_unlock(&ab8500->lock);
159
160 return ret;
161}
162EXPORT_SYMBOL_GPL(ab8500_read);
163
164/**
165 * ab8500_set_bits() - set a bitfield in an AB8500 register
166 * @ab8500: device to read from
167 * @addr: address of the register
168 * @mask: mask of the bitfield to modify
169 * @data: value to set to the bitfield
170 */
171int ab8500_set_bits(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data)
172{
173 int ret;
174
175 mutex_lock(&ab8500->lock);
176
177 ret = __ab8500_read(ab8500, addr);
178 if (ret < 0)
179 goto out;
180
181 ret &= ~mask;
182 ret |= data;
183
184 ret = __ab8500_write(ab8500, addr, ret);
185
186out:
187 mutex_unlock(&ab8500->lock);
188 return ret;
189}
190EXPORT_SYMBOL_GPL(ab8500_set_bits);
191
192static void ab8500_irq_lock(unsigned int irq)
193{
194 struct ab8500 *ab8500 = get_irq_chip_data(irq);
195
196 mutex_lock(&ab8500->irq_lock);
197}
198
199static void ab8500_irq_sync_unlock(unsigned int irq)
200{
201 struct ab8500 *ab8500 = get_irq_chip_data(irq);
202 int i;
203
204 for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
205 u8 old = ab8500->oldmask[i];
206 u8 new = ab8500->mask[i];
207 int reg;
208
209 if (new == old)
210 continue;
211
212 ab8500->oldmask[i] = new;
213
214 reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i];
215 ab8500_write(ab8500, reg, new);
216 }
217
218 mutex_unlock(&ab8500->irq_lock);
219}
220
221static void ab8500_irq_mask(unsigned int irq)
222{
223 struct ab8500 *ab8500 = get_irq_chip_data(irq);
224 int offset = irq - ab8500->irq_base;
225 int index = offset / 8;
226 int mask = 1 << (offset % 8);
227
228 ab8500->mask[index] |= mask;
229}
230
231static void ab8500_irq_unmask(unsigned int irq)
232{
233 struct ab8500 *ab8500 = get_irq_chip_data(irq);
234 int offset = irq - ab8500->irq_base;
235 int index = offset / 8;
236 int mask = 1 << (offset % 8);
237
238 ab8500->mask[index] &= ~mask;
239}
240
241static struct irq_chip ab8500_irq_chip = {
242 .name = "ab8500",
243 .bus_lock = ab8500_irq_lock,
244 .bus_sync_unlock = ab8500_irq_sync_unlock,
245 .mask = ab8500_irq_mask,
246 .unmask = ab8500_irq_unmask,
247};
248
249static irqreturn_t ab8500_irq(int irq, void *dev)
250{
251 struct ab8500 *ab8500 = dev;
252 int i;
253
254 dev_vdbg(ab8500->dev, "interrupt\n");
255
256 for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
257 int regoffset = ab8500_irq_regoffset[i];
258 int status;
259
260 status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset);
261 if (status <= 0)
262 continue;
263
264 do {
265 int bit = __ffs(status);
266 int line = i * 8 + bit;
267
268 handle_nested_irq(ab8500->irq_base + line);
269 status &= ~(1 << bit);
270 } while (status);
271 }
272
273 return IRQ_HANDLED;
274}
275
276static int ab8500_irq_init(struct ab8500 *ab8500)
277{
278 int base = ab8500->irq_base;
279 int irq;
280
281 for (irq = base; irq < base + AB8500_NR_IRQS; irq++) {
282 set_irq_chip_data(irq, ab8500);
283 set_irq_chip_and_handler(irq, &ab8500_irq_chip,
284 handle_simple_irq);
285 set_irq_nested_thread(irq, 1);
286#ifdef CONFIG_ARM
287 set_irq_flags(irq, IRQF_VALID);
288#else
289 set_irq_noprobe(irq);
290#endif
291 }
292
293 return 0;
294}
295
296static void ab8500_irq_remove(struct ab8500 *ab8500)
297{
298 int base = ab8500->irq_base;
299 int irq;
300
301 for (irq = base; irq < base + AB8500_NR_IRQS; irq++) {
302#ifdef CONFIG_ARM
303 set_irq_flags(irq, 0);
304#endif
305 set_irq_chip_and_handler(irq, NULL, NULL);
306 set_irq_chip_data(irq, NULL);
307 }
308}
309
310static struct resource ab8500_gpadc_resources[] = {
311 {
312 .name = "HW_CONV_END",
313 .start = AB8500_INT_GP_HW_ADC_CONV_END,
314 .end = AB8500_INT_GP_HW_ADC_CONV_END,
315 .flags = IORESOURCE_IRQ,
316 },
317 {
318 .name = "SW_CONV_END",
319 .start = AB8500_INT_GP_SW_ADC_CONV_END,
320 .end = AB8500_INT_GP_SW_ADC_CONV_END,
321 .flags = IORESOURCE_IRQ,
322 },
323};
324
325static struct resource ab8500_rtc_resources[] = {
326 {
327 .name = "60S",
328 .start = AB8500_INT_RTC_60S,
329 .end = AB8500_INT_RTC_60S,
330 .flags = IORESOURCE_IRQ,
331 },
332 {
333 .name = "ALARM",
334 .start = AB8500_INT_RTC_ALARM,
335 .end = AB8500_INT_RTC_ALARM,
336 .flags = IORESOURCE_IRQ,
337 },
338};
339
340static struct mfd_cell ab8500_devs[] = {
341 {
342 .name = "ab8500-gpadc",
343 .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
344 .resources = ab8500_gpadc_resources,
345 },
346 {
347 .name = "ab8500-rtc",
348 .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
349 .resources = ab8500_rtc_resources,
350 },
351 { .name = "ab8500-charger", },
352 { .name = "ab8500-audio", },
353 { .name = "ab8500-usb", },
354 { .name = "ab8500-pwm", },
355};
356
357int __devinit ab8500_init(struct ab8500 *ab8500)
358{
359 struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
360 int ret;
361 int i;
362
363 if (plat)
364 ab8500->irq_base = plat->irq_base;
365
366 mutex_init(&ab8500->lock);
367 mutex_init(&ab8500->irq_lock);
368
369 ret = ab8500_read(ab8500, AB8500_REV_REG);
370 if (ret < 0)
371 return ret;
372
373 /*
374 * 0x0 - Early Drop
375 * 0x10 - Cut 1.0
376 * 0x11 - Cut 1.1
377 */
378 if (ret == 0x0 || ret == 0x10 || ret == 0x11) {
379 ab8500->revision = ret;
380 dev_info(ab8500->dev, "detected chip, revision: %#x\n", ret);
381 } else {
382 dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret);
383 return -EINVAL;
384 }
385
386 if (plat && plat->init)
387 plat->init(ab8500);
388
389 /* Clear and mask all interrupts */
390 for (i = 0; i < 10; i++) {
391 ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
392 ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
393 }
394
395 for (i = 18; i < 24; i++) {
396 ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
397 ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
398 }
399
400 for (i = 0; i < AB8500_NUM_IRQ_REGS; i++)
401 ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
402
403 if (ab8500->irq_base) {
404 ret = ab8500_irq_init(ab8500);
405 if (ret)
406 return ret;
407
408 ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,
409 IRQF_ONESHOT, "ab8500", ab8500);
410 if (ret)
411 goto out_removeirq;
412 }
413
414 ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs,
415 ARRAY_SIZE(ab8500_devs), NULL,
416 ab8500->irq_base);
417 if (ret)
418 goto out_freeirq;
419
420 return ret;
421
422out_freeirq:
423 if (ab8500->irq_base) {
424 free_irq(ab8500->irq, ab8500);
425out_removeirq:
426 ab8500_irq_remove(ab8500);
427 }
428 return ret;
429}
430
431int __devexit ab8500_exit(struct ab8500 *ab8500)
432{
433 mfd_remove_devices(ab8500->dev);
434 if (ab8500->irq_base) {
435 free_irq(ab8500->irq, ab8500);
436 ab8500_irq_remove(ab8500);
437 }
438
439 return 0;
440}
441
442MODULE_AUTHOR("Srinidhi Kasagar, Rabin Vincent");
443MODULE_DESCRIPTION("AB8500 MFD core");
444MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c
new file mode 100644
index 000000000000..b81d4f768ef6
--- /dev/null
+++ b/drivers/mfd/ab8500-spi.c
@@ -0,0 +1,133 @@
1/*
2 * Copyright (C) ST-Ericsson SA 2010
3 *
4 * License Terms: GNU General Public License v2
5 * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
6 */
7
8#include <linux/kernel.h>
9#include <linux/slab.h>
10#include <linux/init.h>
11#include <linux/module.h>
12#include <linux/platform_device.h>
13#include <linux/spi/spi.h>
14#include <linux/mfd/ab8500.h>
15
16/*
17 * This funtion writes to any AB8500 registers using
18 * SPI protocol & before it writes it packs the data
19 * in the below 24 bit frame format
20 *
21 * *|------------------------------------|
22 * *| 23|22...18|17.......10|9|8|7......0|
23 * *| r/w bank adr data |
24 * * ------------------------------------
25 *
26 * This function shouldn't be called from interrupt
27 * context
28 */
29static int ab8500_spi_write(struct ab8500 *ab8500, u16 addr, u8 data)
30{
31 struct spi_device *spi = container_of(ab8500->dev, struct spi_device,
32 dev);
33 unsigned long spi_data = addr << 10 | data;
34 struct spi_transfer xfer;
35 struct spi_message msg;
36
37 ab8500->tx_buf[0] = spi_data;
38 ab8500->rx_buf[0] = 0;
39
40 xfer.tx_buf = ab8500->tx_buf;
41 xfer.rx_buf = NULL;
42 xfer.len = sizeof(unsigned long);
43
44 spi_message_init(&msg);
45 spi_message_add_tail(&xfer, &msg);
46
47 return spi_sync(spi, &msg);
48}
49
50static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr)
51{
52 struct spi_device *spi = container_of(ab8500->dev, struct spi_device,
53 dev);
54 unsigned long spi_data = 1 << 23 | addr << 10;
55 struct spi_transfer xfer;
56 struct spi_message msg;
57 int ret;
58
59 ab8500->tx_buf[0] = spi_data;
60 ab8500->rx_buf[0] = 0;
61
62 xfer.tx_buf = ab8500->tx_buf;
63 xfer.rx_buf = ab8500->rx_buf;
64 xfer.len = sizeof(unsigned long);
65
66 spi_message_init(&msg);
67 spi_message_add_tail(&xfer, &msg);
68
69 ret = spi_sync(spi, &msg);
70 if (!ret)
71 ret = ab8500->rx_buf[0];
72
73 return ret;
74}
75
76static int __devinit ab8500_spi_probe(struct spi_device *spi)
77{
78 struct ab8500 *ab8500;
79 int ret;
80
81 ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
82 if (!ab8500)
83 return -ENOMEM;
84
85 ab8500->dev = &spi->dev;
86 ab8500->irq = spi->irq;
87
88 ab8500->read = ab8500_spi_read;
89 ab8500->write = ab8500_spi_write;
90
91 spi_set_drvdata(spi, ab8500);
92
93 ret = ab8500_init(ab8500);
94 if (ret)
95 kfree(ab8500);
96
97 return ret;
98}
99
100static int __devexit ab8500_spi_remove(struct spi_device *spi)
101{
102 struct ab8500 *ab8500 = spi_get_drvdata(spi);
103
104 ab8500_exit(ab8500);
105 kfree(ab8500);
106
107 return 0;
108}
109
110static struct spi_driver ab8500_spi_driver = {
111 .driver = {
112 .name = "ab8500",
113 .owner = THIS_MODULE,
114 },
115 .probe = ab8500_spi_probe,
116 .remove = __devexit_p(ab8500_spi_remove)
117};
118
119static int __init ab8500_spi_init(void)
120{
121 return spi_register_driver(&ab8500_spi_driver);
122}
123subsys_initcall(ab8500_spi_init);
124
125static void __exit ab8500_spi_exit(void)
126{
127 spi_unregister_driver(&ab8500_spi_driver);
128}
129module_exit(ab8500_spi_exit);
130
131MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
132MODULE_DESCRIPTION("AB8500 SPI");
133MODULE_LICENSE("GPL v2");