aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2008-12-22 06:05:27 -0500
committerSamuel Ortiz <samuel@sortiz.org>2009-01-04 06:17:43 -0500
commit0931a4c6dbfab03f2bfd22a9170130f7b155d53a (patch)
tree3b943723615a0212bf5e5b74b1fbc0689ee6cbae /drivers/mfd
parent4331bb32339a55fd88fbfb0581ed5132207bf9a2 (diff)
mfd: dm355evm msp430 driver
Basic MFD framework for the MSP430 microcontroller firmware used on the dm355evm board: - Provides an interface for other drivers: register read/write utilities, and register declarations. - Directly exports: * Many signals through the GPIO framework + LEDs + SW6 through gpio sysfs + NTSC/nPAL jumper through gpio sysfs + ... more could be added later, e.g. MMC signals * Child devices: + LEDs, via leds-gpio child (and default triggers) + RTC, via rtc-dm355evm child device + Buttons and IR control, via dm355evm_keys - Supports power-off system call. Use the reset button to power the board back up; the power supply LED will be on, but the MSP430 waits to re-activate the regulators. - On probe() this: * Announces firmware revision * Turns off the banked LEDs * Exports the resources noted above * Hooks the power-off support * Muxes tvp5146 -or- imager for video input Unless the new tvp514x driver (tracked for mainline) is configured, this assumes that some custom imager driver handles video-in. This completely ignores the registers reporting the output voltages on the various power supplies. Someone could add a hwmon interface if that seems useful. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Samuel Ortiz <sameo@openedhand.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/dm355evm_msp.c420
3 files changed, 430 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 781a27955ff..02e9146ca44 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -34,6 +34,14 @@ config MFD_ASIC3
34 This driver supports the ASIC3 multifunction chip found on many 34 This driver supports the ASIC3 multifunction chip found on many
35 PDAs (mainly iPAQ and HTC based ones) 35 PDAs (mainly iPAQ and HTC based ones)
36 36
37config MFD_DM355EVM_MSP
38 bool "DaVinci DM355 EVM microcontroller"
39 depends on I2C && MACH_DAVINCI_DM355_EVM
40 help
41 This driver supports the MSP430 microcontroller used on these
42 boards. MSP430 firmware manages resets and power sequencing,
43 inputs from buttons and the IR remote, LEDs, an RTC, and more.
44
37config HTC_EGPIO 45config HTC_EGPIO
38 bool "HTC EGPIO support" 46 bool "HTC EGPIO support"
39 depends on GENERIC_HARDIRQS && GPIOLIB && ARM 47 depends on GENERIC_HARDIRQS && GPIOLIB && ARM
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9a5ad8af911..8f6cd5c4893 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -8,6 +8,8 @@ obj-$(CONFIG_MFD_ASIC3) += asic3.o
8obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o 8obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o
9obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o 9obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o
10 10
11obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
12
11obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o 13obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o
12obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o 14obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o
13obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o 15obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c
new file mode 100644
index 00000000000..4214b3f7242
--- /dev/null
+++ b/drivers/mfd/dm355evm_msp.c
@@ -0,0 +1,420 @@
1/*
2 * dm355evm_msp.c - driver for MSP430 firmware on DM355EVM board
3 *
4 * Copyright (C) 2008 David Brownell
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <linux/init.h>
13#include <linux/mutex.h>
14#include <linux/platform_device.h>
15#include <linux/clk.h>
16#include <linux/err.h>
17#include <linux/gpio.h>
18#include <linux/leds.h>
19#include <linux/i2c.h>
20#include <linux/i2c/dm355evm_msp.h>
21
22
23/*
24 * The DM355 is a DaVinci chip with video support but no C64+ DSP. Its
25 * EVM board has an MSP430 programmed with firmware for various board
26 * support functions. This driver exposes some of them directly, and
27 * supports other drivers (e.g. RTC, input) for more complex access.
28 *
29 * Because this firmware is entirely board-specific, this file embeds
30 * knowledge that would be passed as platform_data in a generic driver.
31 *
32 * This driver was tested with firmware revision A4.
33 */
34
35#if defined(CONFIG_KEYBOARD_DM355EVM) \
36 || defined(CONFIG_KEYBOARD_DM355EVM_MODULE)
37#define msp_has_keyboard() true
38#else
39#define msp_has_keyboard() false
40#endif
41
42#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
43#define msp_has_leds() true
44#else
45#define msp_has_leds() false
46#endif
47
48#if defined(CONFIG_RTC_DRV_DM355EVM) || defined(CONFIG_RTC_DRV_DM355EVM_MODULE)
49#define msp_has_rtc() true
50#else
51#define msp_has_rtc() false
52#endif
53
54#if defined(CONFIG_VIDEO_TVP514X) || defined(CONFIG_VIDEO_TVP514X_MODULE)
55#define msp_has_tvp() true
56#else
57#define msp_has_tvp() false
58#endif
59
60
61/*----------------------------------------------------------------------*/
62
63/* REVISIT for paranoia's sake, retry reads/writes on error */
64
65static struct i2c_client *msp430;
66
67/**
68 * dm355evm_msp_write - Writes a register in dm355evm_msp
69 * @value: the value to be written
70 * @reg: register address
71 *
72 * Returns result of operation - 0 is success, else negative errno
73 */
74int dm355evm_msp_write(u8 value, u8 reg)
75{
76 return i2c_smbus_write_byte_data(msp430, reg, value);
77}
78EXPORT_SYMBOL(dm355evm_msp_write);
79
80/**
81 * dm355evm_msp_read - Reads a register from dm355evm_msp
82 * @reg: register address
83 *
84 * Returns result of operation - value, or negative errno
85 */
86int dm355evm_msp_read(u8 reg)
87{
88 return i2c_smbus_read_byte_data(msp430, reg);
89}
90EXPORT_SYMBOL(dm355evm_msp_read);
91
92/*----------------------------------------------------------------------*/
93
94/*
95 * Many of the msp430 pins are just used as fixed-direction GPIOs.
96 * We could export a few more of them this way, if we wanted.
97 */
98#define MSP_GPIO(bit,reg) ((DM355EVM_MSP_ ## reg) << 3 | (bit))
99
100static const u8 msp_gpios[] = {
101 /* eight leds */
102 MSP_GPIO(0, LED), MSP_GPIO(1, LED),
103 MSP_GPIO(2, LED), MSP_GPIO(3, LED),
104 MSP_GPIO(4, LED), MSP_GPIO(5, LED),
105 MSP_GPIO(6, LED), MSP_GPIO(7, LED),
106 /* SW6 and the NTSC/nPAL jumper */
107 MSP_GPIO(0, SWITCH1), MSP_GPIO(1, SWITCH1),
108 MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1),
109 MSP_GPIO(4, SWITCH1),
110};
111
112#define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3)
113#define MSP_GPIO_MASK(offset) BIT(msp_gpios[(offset)] & 0x07)
114
115static int msp_gpio_in(struct gpio_chip *chip, unsigned offset)
116{
117 switch (MSP_GPIO_REG(offset)) {
118 case DM355EVM_MSP_SWITCH1:
119 case DM355EVM_MSP_SWITCH2:
120 case DM355EVM_MSP_SDMMC:
121 return 0;
122 default:
123 return -EINVAL;
124 }
125}
126
127static u8 msp_led_cache;
128
129static int msp_gpio_get(struct gpio_chip *chip, unsigned offset)
130{
131 int reg, status;
132
133 reg = MSP_GPIO_REG(offset);
134 status = dm355evm_msp_read(reg);
135 if (status < 0)
136 return status;
137 if (reg == DM355EVM_MSP_LED)
138 msp_led_cache = status;
139 return status & MSP_GPIO_MASK(offset);
140}
141
142static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value)
143{
144 int mask, bits;
145
146 /* NOTE: there are some other signals that could be
147 * packaged as output GPIOs, but they aren't as useful
148 * as the LEDs ... so for now we don't.
149 */
150 if (MSP_GPIO_REG(offset) != DM355EVM_MSP_LED)
151 return -EINVAL;
152
153 mask = MSP_GPIO_MASK(offset);
154 bits = msp_led_cache;
155
156 bits &= ~mask;
157 if (value)
158 bits |= mask;
159 msp_led_cache = bits;
160
161 return dm355evm_msp_write(bits, DM355EVM_MSP_LED);
162}
163
164static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
165{
166 msp_gpio_out(chip, offset, value);
167}
168
169static struct gpio_chip dm355evm_msp_gpio = {
170 .label = "dm355evm_msp",
171 .owner = THIS_MODULE,
172 .direction_input = msp_gpio_in,
173 .get = msp_gpio_get,
174 .direction_output = msp_gpio_out,
175 .set = msp_gpio_set,
176 .base = -EINVAL, /* dynamic assignment */
177 .ngpio = ARRAY_SIZE(msp_gpios),
178 .can_sleep = true,
179};
180
181/*----------------------------------------------------------------------*/
182
183static struct device *add_child(struct i2c_client *client, const char *name,
184 void *pdata, unsigned pdata_len,
185 bool can_wakeup, int irq)
186{
187 struct platform_device *pdev;
188 int status;
189
190 pdev = platform_device_alloc(name, -1);
191 if (!pdev) {
192 dev_dbg(&client->dev, "can't alloc dev\n");
193 status = -ENOMEM;
194 goto err;
195 }
196
197 device_init_wakeup(&pdev->dev, can_wakeup);
198 pdev->dev.parent = &client->dev;
199
200 if (pdata) {
201 status = platform_device_add_data(pdev, pdata, pdata_len);
202 if (status < 0) {
203 dev_dbg(&pdev->dev, "can't add platform_data\n");
204 goto err;
205 }
206 }
207
208 if (irq) {
209 struct resource r = {
210 .start = irq,
211 .flags = IORESOURCE_IRQ,
212 };
213
214 status = platform_device_add_resources(pdev, &r, 1);
215 if (status < 0) {
216 dev_dbg(&pdev->dev, "can't add irq\n");
217 goto err;
218 }
219 }
220
221 status = platform_device_add(pdev);
222
223err:
224 if (status < 0) {
225 platform_device_put(pdev);
226 dev_err(&client->dev, "can't add %s dev\n", name);
227 return ERR_PTR(status);
228 }
229 return &pdev->dev;
230}
231
232static int add_children(struct i2c_client *client)
233{
234 static const struct {
235 int offset;
236 char *label;
237 } config_inputs[] = {
238 /* 8 == right after the LEDs */
239 { 8 + 0, "sw6_1", },
240 { 8 + 1, "sw6_2", },
241 { 8 + 2, "sw6_3", },
242 { 8 + 3, "sw6_4", },
243 { 8 + 4, "NTSC/nPAL", },
244 };
245
246 struct device *child;
247 int status;
248 int i;
249
250 /* GPIO-ish stuff */
251 dm355evm_msp_gpio.dev = &client->dev;
252 status = gpiochip_add(&dm355evm_msp_gpio);
253 if (status < 0)
254 return status;
255
256 /* LED output */
257 if (msp_has_leds()) {
258#define GPIO_LED(l) .name = l, .active_low = true
259 static struct gpio_led evm_leds[] = {
260 { GPIO_LED("dm355evm::ds14"),
261 .default_trigger = "heartbeat", },
262 { GPIO_LED("dm355evm::ds15"),
263 .default_trigger = "mmc0", },
264 { GPIO_LED("dm355evm::ds16"),
265 /* could also be a CE-ATA drive */
266 .default_trigger = "mmc1", },
267 { GPIO_LED("dm355evm::ds17"),
268 .default_trigger = "nand-disk", },
269 { GPIO_LED("dm355evm::ds18"), },
270 { GPIO_LED("dm355evm::ds19"), },
271 { GPIO_LED("dm355evm::ds20"), },
272 { GPIO_LED("dm355evm::ds21"), },
273 };
274#undef GPIO_LED
275
276 struct gpio_led_platform_data evm_led_data = {
277 .num_leds = ARRAY_SIZE(evm_leds),
278 .leds = evm_leds,
279 };
280
281 for (i = 0; i < ARRAY_SIZE(evm_leds); i++)
282 evm_leds[i].gpio = i + dm355evm_msp_gpio.base;
283
284 /* NOTE: these are the only fully programmable LEDs
285 * on the board, since GPIO-61/ds22 (and many signals
286 * going to DC7) must be used for AEMIF address lines
287 * unless the top 1 GB of NAND is unused...
288 */
289 child = add_child(client, "leds-gpio",
290 &evm_led_data, sizeof(evm_led_data),
291 false, 0);
292 if (IS_ERR(child))
293 return PTR_ERR(child);
294 }
295
296 /* configuration inputs */
297 for (i = 0; i < ARRAY_SIZE(config_inputs); i++) {
298 int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset;
299
300 gpio_request(gpio, config_inputs[i].label);
301 gpio_direction_input(gpio);
302
303 /* make it easy for userspace to see these */
304 gpio_export(gpio, false);
305 }
306
307 /* RTC is a 32 bit counter, no alarm */
308 if (msp_has_rtc()) {
309 child = add_child(client, "rtc-dm355evm",
310 NULL, 0, false, 0);
311 if (IS_ERR(child))
312 return PTR_ERR(child);
313 }
314
315 /* input from buttons and IR remote (uses the IRQ) */
316 if (msp_has_keyboard()) {
317 child = add_child(client, "dm355evm_keys",
318 NULL, 0, true, client->irq);
319 if (IS_ERR(child))
320 return PTR_ERR(child);
321 }
322
323 return 0;
324}
325
326/*----------------------------------------------------------------------*/
327
328static void dm355evm_command(unsigned command)
329{
330 int status;
331
332 status = dm355evm_msp_write(command, DM355EVM_MSP_COMMAND);
333 if (status < 0)
334 dev_err(&msp430->dev, "command %d failure %d\n",
335 command, status);
336}
337
338static void dm355evm_power_off(void)
339{
340 dm355evm_command(MSP_COMMAND_POWEROFF);
341}
342
343static int dm355evm_msp_remove(struct i2c_client *client)
344{
345 pm_power_off = NULL;
346 msp430 = NULL;
347 return 0;
348}
349
350static int
351dm355evm_msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
352{
353 int status;
354 const char *video = msp_has_tvp() ? "TVP5146" : "imager";
355
356 if (msp430)
357 return -EBUSY;
358 msp430 = client;
359
360 /* display revision status; doubles as sanity check */
361 status = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
362 if (status < 0)
363 goto fail;
364 dev_info(&client->dev, "firmware v.%02X, %s as video-in\n",
365 status, video);
366
367 /* mux video input: either tvp5146 or some external imager */
368 status = dm355evm_msp_write(msp_has_tvp() ? 0 : MSP_VIDEO_IMAGER,
369 DM355EVM_MSP_VIDEO_IN);
370 if (status < 0)
371 dev_warn(&client->dev, "error %d muxing %s as video-in\n",
372 status, video);
373
374 /* init LED cache, and turn off the LEDs */
375 msp_led_cache = 0xff;
376 dm355evm_msp_write(msp_led_cache, DM355EVM_MSP_LED);
377
378 /* export capabilities we support */
379 status = add_children(client);
380 if (status < 0)
381 goto fail;
382
383 /* PM hookup */
384 pm_power_off = dm355evm_power_off;
385
386 return 0;
387
388fail:
389 /* FIXME remove children ... */
390 dm355evm_msp_remove(client);
391 return status;
392}
393
394static const struct i2c_device_id dm355evm_msp_ids[] = {
395 { "dm355evm_msp", 0 },
396 { /* end of list */ },
397};
398MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids);
399
400static struct i2c_driver dm355evm_msp_driver = {
401 .driver.name = "dm355evm_msp",
402 .id_table = dm355evm_msp_ids,
403 .probe = dm355evm_msp_probe,
404 .remove = dm355evm_msp_remove,
405};
406
407static int __init dm355evm_msp_init(void)
408{
409 return i2c_add_driver(&dm355evm_msp_driver);
410}
411subsys_initcall(dm355evm_msp_init);
412
413static void __exit dm355evm_msp_exit(void)
414{
415 i2c_del_driver(&dm355evm_msp_driver);
416}
417module_exit(dm355evm_msp_exit);
418
419MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM");
420MODULE_LICENSE("GPL");