Commit 2aa13b9e8096ab7f12c67f3a5b9a38b194a30ce9
Committed by
Samuel Ortiz
1 parent
e5b486841d
mfd: Add WM831x SPI support
Implement support for controlling WM831x and WM832x devices using SPI. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Showing 3 changed files with 244 additions and 0 deletions Side-by-side Diff
drivers/mfd/Kconfig
... | ... | @@ -329,6 +329,17 @@ |
329 | 329 | for accessing the device, additional drivers must be enabled in |
330 | 330 | order to use the functionality of the device. |
331 | 331 | |
332 | +config MFD_WM831X_SPI | |
333 | + bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI" | |
334 | + select MFD_CORE | |
335 | + select MFD_WM831X | |
336 | + depends on SPI_MASTER && GENERIC_HARDIRQS | |
337 | + help | |
338 | + Support for the Wolfson Microelecronics WM831x and WM832x PMICs | |
339 | + when controlled using SPI. This driver provides common support | |
340 | + for accessing the device, additional drivers must be enabled in | |
341 | + order to use the functionality of the device. | |
342 | + | |
332 | 343 | config MFD_WM8350 |
333 | 344 | bool |
334 | 345 | depends on GENERIC_HARDIRQS |
drivers/mfd/Makefile
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o |
26 | 26 | obj-$(CONFIG_MFD_WM831X) += wm831x.o |
27 | 27 | obj-$(CONFIG_MFD_WM831X_I2C) += wm831x-i2c.o |
28 | +obj-$(CONFIG_MFD_WM831X_SPI) += wm831x-spi.o | |
28 | 29 | wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o |
29 | 30 | wm8350-objs += wm8350-irq.o |
30 | 31 | obj-$(CONFIG_MFD_WM8350) += wm8350.o |
drivers/mfd/wm831x-spi.c
1 | +/* | |
2 | + * wm831x-spi.c -- SPI access for Wolfson WM831x PMICs | |
3 | + * | |
4 | + * Copyright 2009,2010 Wolfson Microelectronics PLC. | |
5 | + * | |
6 | + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify it | |
9 | + * under the terms of the GNU General Public License as published by the | |
10 | + * Free Software Foundation; either version 2 of the License, or (at your | |
11 | + * option) any later version. | |
12 | + * | |
13 | + */ | |
14 | + | |
15 | +#include <linux/kernel.h> | |
16 | +#include <linux/module.h> | |
17 | +#include <linux/spi/spi.h> | |
18 | + | |
19 | +#include <linux/mfd/wm831x/core.h> | |
20 | + | |
21 | +static int wm831x_spi_read_device(struct wm831x *wm831x, unsigned short reg, | |
22 | + int bytes, void *dest) | |
23 | +{ | |
24 | + u16 tx_val; | |
25 | + u16 *d = dest; | |
26 | + int r, ret; | |
27 | + | |
28 | + /* Go register at a time */ | |
29 | + for (r = reg; r < reg + (bytes / 2); r++) { | |
30 | + tx_val = r | 0x8000; | |
31 | + | |
32 | + ret = spi_write_then_read(wm831x->control_data, | |
33 | + (u8 *)&tx_val, 2, (u8 *)d, 2); | |
34 | + if (ret != 0) | |
35 | + return ret; | |
36 | + | |
37 | + *d = be16_to_cpu(*d); | |
38 | + | |
39 | + d++; | |
40 | + } | |
41 | + | |
42 | + return 0; | |
43 | +} | |
44 | + | |
45 | +static int wm831x_spi_write_device(struct wm831x *wm831x, unsigned short reg, | |
46 | + int bytes, void *src) | |
47 | +{ | |
48 | + struct spi_device *spi = wm831x->control_data; | |
49 | + u16 *s = src; | |
50 | + u16 data[2]; | |
51 | + int ret, r; | |
52 | + | |
53 | + /* Go register at a time */ | |
54 | + for (r = reg; r < reg + (bytes / 2); r++) { | |
55 | + data[0] = r; | |
56 | + data[1] = *s++; | |
57 | + | |
58 | + ret = spi_write(spi, (char *)&data, sizeof(data)); | |
59 | + if (ret != 0) | |
60 | + return ret; | |
61 | + } | |
62 | + | |
63 | + return 0; | |
64 | +} | |
65 | + | |
66 | +static int __devinit wm831x_spi_probe(struct spi_device *spi) | |
67 | +{ | |
68 | + struct wm831x *wm831x; | |
69 | + enum wm831x_parent type; | |
70 | + | |
71 | + /* Currently SPI support for ID tables is unmerged, we're faking it */ | |
72 | + if (strcmp(spi->modalias, "wm8310") == 0) | |
73 | + type = WM8310; | |
74 | + else if (strcmp(spi->modalias, "wm8311") == 0) | |
75 | + type = WM8311; | |
76 | + else if (strcmp(spi->modalias, "wm8312") == 0) | |
77 | + type = WM8312; | |
78 | + else if (strcmp(spi->modalias, "wm8320") == 0) | |
79 | + type = WM8320; | |
80 | + else if (strcmp(spi->modalias, "wm8321") == 0) | |
81 | + type = WM8321; | |
82 | + else if (strcmp(spi->modalias, "wm8325") == 0) | |
83 | + type = WM8325; | |
84 | + else { | |
85 | + dev_err(&spi->dev, "Unknown device type\n"); | |
86 | + return -EINVAL; | |
87 | + } | |
88 | + | |
89 | + wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL); | |
90 | + if (wm831x == NULL) | |
91 | + return -ENOMEM; | |
92 | + | |
93 | + spi->bits_per_word = 16; | |
94 | + spi->mode = SPI_MODE_0; | |
95 | + | |
96 | + dev_set_drvdata(&spi->dev, wm831x); | |
97 | + wm831x->dev = &spi->dev; | |
98 | + wm831x->control_data = spi; | |
99 | + wm831x->read_dev = wm831x_spi_read_device; | |
100 | + wm831x->write_dev = wm831x_spi_write_device; | |
101 | + | |
102 | + return wm831x_device_init(wm831x, type, spi->irq); | |
103 | +} | |
104 | + | |
105 | +static int __devexit wm831x_spi_remove(struct spi_device *spi) | |
106 | +{ | |
107 | + struct wm831x *wm831x = dev_get_drvdata(&spi->dev); | |
108 | + | |
109 | + wm831x_device_exit(wm831x); | |
110 | + | |
111 | + return 0; | |
112 | +} | |
113 | + | |
114 | +static int wm831x_spi_suspend(struct spi_device *spi, pm_message_t m) | |
115 | +{ | |
116 | + struct wm831x *wm831x = dev_get_drvdata(&spi->dev); | |
117 | + | |
118 | + return wm831x_device_suspend(wm831x); | |
119 | +} | |
120 | + | |
121 | +static struct spi_driver wm8310_spi_driver = { | |
122 | + .driver = { | |
123 | + .name = "wm8310", | |
124 | + .bus = &spi_bus_type, | |
125 | + .owner = THIS_MODULE, | |
126 | + }, | |
127 | + .probe = wm831x_spi_probe, | |
128 | + .remove = __devexit_p(wm831x_spi_remove), | |
129 | + .suspend = wm831x_spi_suspend, | |
130 | +}; | |
131 | + | |
132 | +static struct spi_driver wm8311_spi_driver = { | |
133 | + .driver = { | |
134 | + .name = "wm8311", | |
135 | + .bus = &spi_bus_type, | |
136 | + .owner = THIS_MODULE, | |
137 | + }, | |
138 | + .probe = wm831x_spi_probe, | |
139 | + .remove = __devexit_p(wm831x_spi_remove), | |
140 | + .suspend = wm831x_spi_suspend, | |
141 | +}; | |
142 | + | |
143 | +static struct spi_driver wm8312_spi_driver = { | |
144 | + .driver = { | |
145 | + .name = "wm8312", | |
146 | + .bus = &spi_bus_type, | |
147 | + .owner = THIS_MODULE, | |
148 | + }, | |
149 | + .probe = wm831x_spi_probe, | |
150 | + .remove = __devexit_p(wm831x_spi_remove), | |
151 | + .suspend = wm831x_spi_suspend, | |
152 | +}; | |
153 | + | |
154 | +static struct spi_driver wm8320_spi_driver = { | |
155 | + .driver = { | |
156 | + .name = "wm8320", | |
157 | + .bus = &spi_bus_type, | |
158 | + .owner = THIS_MODULE, | |
159 | + }, | |
160 | + .probe = wm831x_spi_probe, | |
161 | + .remove = __devexit_p(wm831x_spi_remove), | |
162 | + .suspend = wm831x_spi_suspend, | |
163 | +}; | |
164 | + | |
165 | +static struct spi_driver wm8321_spi_driver = { | |
166 | + .driver = { | |
167 | + .name = "wm8321", | |
168 | + .bus = &spi_bus_type, | |
169 | + .owner = THIS_MODULE, | |
170 | + }, | |
171 | + .probe = wm831x_spi_probe, | |
172 | + .remove = __devexit_p(wm831x_spi_remove), | |
173 | + .suspend = wm831x_spi_suspend, | |
174 | +}; | |
175 | + | |
176 | +static struct spi_driver wm8325_spi_driver = { | |
177 | + .driver = { | |
178 | + .name = "wm8325", | |
179 | + .bus = &spi_bus_type, | |
180 | + .owner = THIS_MODULE, | |
181 | + }, | |
182 | + .probe = wm831x_spi_probe, | |
183 | + .remove = __devexit_p(wm831x_spi_remove), | |
184 | + .suspend = wm831x_spi_suspend, | |
185 | +}; | |
186 | + | |
187 | +static int __init wm831x_spi_init(void) | |
188 | +{ | |
189 | + int ret; | |
190 | + | |
191 | + ret = spi_register_driver(&wm8310_spi_driver); | |
192 | + if (ret != 0) | |
193 | + pr_err("Failed to register WM8310 SPI driver: %d\n", ret); | |
194 | + | |
195 | + ret = spi_register_driver(&wm8311_spi_driver); | |
196 | + if (ret != 0) | |
197 | + pr_err("Failed to register WM8311 SPI driver: %d\n", ret); | |
198 | + | |
199 | + ret = spi_register_driver(&wm8312_spi_driver); | |
200 | + if (ret != 0) | |
201 | + pr_err("Failed to register WM8312 SPI driver: %d\n", ret); | |
202 | + | |
203 | + ret = spi_register_driver(&wm8320_spi_driver); | |
204 | + if (ret != 0) | |
205 | + pr_err("Failed to register WM8320 SPI driver: %d\n", ret); | |
206 | + | |
207 | + ret = spi_register_driver(&wm8321_spi_driver); | |
208 | + if (ret != 0) | |
209 | + pr_err("Failed to register WM8321 SPI driver: %d\n", ret); | |
210 | + | |
211 | + ret = spi_register_driver(&wm8325_spi_driver); | |
212 | + if (ret != 0) | |
213 | + pr_err("Failed to register WM8325 SPI driver: %d\n", ret); | |
214 | + | |
215 | + return 0; | |
216 | +} | |
217 | +subsys_initcall(wm831x_spi_init); | |
218 | + | |
219 | +static void __exit wm831x_spi_exit(void) | |
220 | +{ | |
221 | + spi_unregister_driver(&wm8325_spi_driver); | |
222 | + spi_unregister_driver(&wm8321_spi_driver); | |
223 | + spi_unregister_driver(&wm8320_spi_driver); | |
224 | + spi_unregister_driver(&wm8312_spi_driver); | |
225 | + spi_unregister_driver(&wm8311_spi_driver); | |
226 | + spi_unregister_driver(&wm8310_spi_driver); | |
227 | +} | |
228 | +module_exit(wm831x_spi_exit); | |
229 | + | |
230 | +MODULE_DESCRIPTION("SPI support for WM831x/2x AudioPlus PMICs"); | |
231 | +MODULE_LICENSE("GPL"); | |
232 | +MODULE_AUTHOR("Mark Brown"); |