aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/ab4500-core.c
diff options
context:
space:
mode:
authorSrinidhi Kasagar <srinidhi.kasagar@stericsson.com>2009-10-12 11:11:52 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2009-12-13 13:20:38 -0500
commit0c41839e98272a317d4af4dfcb54b599b2c3dcba (patch)
tree7317c67b92ba9ff5930d78074c93e148c321af6d /drivers/mfd/ab4500-core.c
parent4107da2a2853c070fb3effa58a83f94dc067fc44 (diff)
mfd: add AB4500 driver
This adds core driver support for AB4500 mixed signal multimedia & power management chip. This connects to U8500 on the SSP (pl022) and exports read/write functions for the device to get access to this chip. This also registers the client devices and sets the parent. Signed-off-by: srinidhi kasagar <srinidhi.kasagar@stericsson.com> Acked-by: Andrea Gallo <andrea.gallo@stericsson.com> Reviewed-by: Mark Brown <broonie@sirena.org.uk> Reviewed-by: Jean-Christophe <plagnioj@jcrosoft.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/ab4500-core.c')
-rw-r--r--drivers/mfd/ab4500-core.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/mfd/ab4500-core.c b/drivers/mfd/ab4500-core.c
new file mode 100644
index 000000000000..3ad91f7ee22b
--- /dev/null
+++ b/drivers/mfd/ab4500-core.c
@@ -0,0 +1,207 @@
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/init.h>
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/spi/spi.h>
22#include <linux/mfd/ab4500.h>
23
24/* just required if probe fails, we need to
25 * unregister the device
26 */
27static struct spi_driver ab4500_driver;
28
29/*
30 * This funtion writes to any AB4500 registers using
31 * SPI protocol & before it writes it packs the data
32 * in the below 24 bit frame format
33 *
34 * *|------------------------------------|
35 * *| 23|22...18|17.......10|9|8|7......0|
36 * *| r/w bank adr data |
37 * * ------------------------------------
38 *
39 * This function shouldn't be called from interrupt
40 * context
41 */
42int ab4500_write(struct ab4500 *ab4500, unsigned char block,
43 unsigned long addr, unsigned char data)
44{
45 struct spi_transfer xfer;
46 struct spi_message msg;
47 int err;
48 unsigned long spi_data =
49 block << 18 | addr << 10 | data;
50
51 mutex_lock(&ab4500->lock);
52 ab4500->tx_buf[0] = spi_data;
53 ab4500->rx_buf[0] = 0;
54
55 xfer.tx_buf = ab4500->tx_buf;
56 xfer.rx_buf = NULL;
57 xfer.len = sizeof(unsigned long);
58
59 spi_message_init(&msg);
60 spi_message_add_tail(&xfer, &msg);
61
62 err = spi_sync(ab4500->spi, &msg);
63 mutex_unlock(&ab4500->lock);
64
65 return err;
66}
67EXPORT_SYMBOL(ab4500_write);
68
69int ab4500_read(struct ab4500 *ab4500, unsigned char block,
70 unsigned long addr)
71{
72 struct spi_transfer xfer;
73 struct spi_message msg;
74 unsigned long spi_data =
75 1 << 23 | block << 18 | addr << 10;
76
77 mutex_lock(&ab4500->lock);
78 ab4500->tx_buf[0] = spi_data;
79 ab4500->rx_buf[0] = 0;
80
81 xfer.tx_buf = ab4500->tx_buf;
82 xfer.rx_buf = ab4500->rx_buf;
83 xfer.len = sizeof(unsigned long);
84
85 spi_message_init(&msg);
86 spi_message_add_tail(&xfer, &msg);
87
88 spi_sync(ab4500->spi, &msg);
89 mutex_unlock(&ab4500->lock);
90
91 return ab4500->rx_buf[0];
92}
93EXPORT_SYMBOL(ab4500_read);
94
95/* ref: ab3100 core */
96#define AB4500_DEVICE(devname, devid) \
97static struct platform_device ab4500_##devname##_device = { \
98 .name = devid, \
99 .id = -1, \
100}
101
102/* list of childern devices of ab4500 - all are
103 * not populated here - TODO
104 */
105AB4500_DEVICE(charger, "ab4500-charger");
106AB4500_DEVICE(audio, "ab4500-audio");
107AB4500_DEVICE(usb, "ab4500-usb");
108AB4500_DEVICE(tvout, "ab4500-tvout");
109AB4500_DEVICE(sim, "ab4500-sim");
110AB4500_DEVICE(gpadc, "ab4500-gpadc");
111AB4500_DEVICE(clkmgt, "ab4500-clkmgt");
112AB4500_DEVICE(misc, "ab4500-misc");
113
114static struct platform_device *ab4500_platform_devs[] = {
115 &ab4500_charger_device,
116 &ab4500_audio_device,
117 &ab4500_usb_device,
118 &ab4500_tvout_device,
119 &ab4500_sim_device,
120 &ab4500_gpadc_device,
121 &ab4500_clkmgt_device,
122 &ab4500_misc_device,
123};
124
125static int __init ab4500_probe(struct spi_device *spi)
126{
127 struct ab4500 *ab4500;
128 unsigned char revision;
129 int err = 0;
130 int i;
131
132 ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL);
133 if (!ab4500) {
134 dev_err(&spi->dev, "could not allocate AB4500\n");
135 err = -ENOMEM;
136 goto not_detect;
137 }
138
139 ab4500->spi = spi;
140 spi_set_drvdata(spi, ab4500);
141
142 mutex_init(&ab4500->lock);
143
144 /* read the revision register */
145 revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG);
146
147 /* revision id 0x0 is for early drop, 0x10 is for cut1.0 */
148 if (revision == 0x0 || revision == 0x10)
149 dev_info(&spi->dev, "Detected chip: %s, revision = %x\n",
150 ab4500_driver.driver.name, revision);
151 else {
152 dev_err(&spi->dev, "unknown chip: 0x%x\n", revision);
153 goto not_detect;
154 }
155
156 for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++) {
157 ab4500_platform_devs[i]->dev.parent =
158 &spi->dev;
159 platform_set_drvdata(ab4500_platform_devs[i], ab4500);
160 }
161
162 /* register the ab4500 platform devices */
163 platform_add_devices(ab4500_platform_devs,
164 ARRAY_SIZE(ab4500_platform_devs));
165
166 return err;
167
168 not_detect:
169 spi_unregister_driver(&ab4500_driver);
170 kfree(ab4500);
171 return err;
172}
173
174static int __devexit ab4500_remove(struct spi_device *spi)
175{
176 struct ab4500 *ab4500 =
177 spi_get_drvdata(spi);
178
179 kfree(ab4500);
180
181 return 0;
182}
183
184static struct spi_driver ab4500_driver = {
185 .driver = {
186 .name = "ab4500",
187 .owner = THIS_MODULE,
188 },
189 .probe = ab4500_probe,
190 .remove = __devexit_p(ab4500_remove)
191};
192
193static int __devinit ab4500_init(void)
194{
195 return spi_register_driver(&ab4500_driver);
196}
197
198static void __exit ab4500_exit(void)
199{
200 spi_unregister_driver(&ab4500_driver);
201}
202
203subsys_initcall_sync(ab4500_init);
204
205MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
206MODULE_DESCRIPTION("AB4500 core driver");
207MODULE_LICENSE("GPL");