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>

authored by

David Brownell and committed by
Samuel Ortiz
0931a4c6 4331bb32

+509
+8
drivers/mfd/Kconfig
··· 34 34 This driver supports the ASIC3 multifunction chip found on many 35 35 PDAs (mainly iPAQ and HTC based ones) 36 36 37 + config 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 + 37 45 config HTC_EGPIO 38 46 bool "HTC EGPIO support" 39 47 depends on GENERIC_HARDIRQS && GPIOLIB && ARM
+2
drivers/mfd/Makefile
··· 8 8 obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o 9 9 obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o 10 10 11 + obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o 12 + 11 13 obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o 12 14 obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o 13 15 obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
+420
drivers/mfd/dm355evm_msp.c
··· 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 + 65 + static 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 + */ 74 + int dm355evm_msp_write(u8 value, u8 reg) 75 + { 76 + return i2c_smbus_write_byte_data(msp430, reg, value); 77 + } 78 + EXPORT_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 + */ 86 + int dm355evm_msp_read(u8 reg) 87 + { 88 + return i2c_smbus_read_byte_data(msp430, reg); 89 + } 90 + EXPORT_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 + 100 + static 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 + 115 + static 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 + 127 + static u8 msp_led_cache; 128 + 129 + static 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 + 142 + static 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 + 164 + static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 165 + { 166 + msp_gpio_out(chip, offset, value); 167 + } 168 + 169 + static 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 + 183 + static 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 + 223 + err: 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 + 232 + static 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 + 328 + static 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 + 338 + static void dm355evm_power_off(void) 339 + { 340 + dm355evm_command(MSP_COMMAND_POWEROFF); 341 + } 342 + 343 + static int dm355evm_msp_remove(struct i2c_client *client) 344 + { 345 + pm_power_off = NULL; 346 + msp430 = NULL; 347 + return 0; 348 + } 349 + 350 + static int 351 + dm355evm_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 + 388 + fail: 389 + /* FIXME remove children ... */ 390 + dm355evm_msp_remove(client); 391 + return status; 392 + } 393 + 394 + static const struct i2c_device_id dm355evm_msp_ids[] = { 395 + { "dm355evm_msp", 0 }, 396 + { /* end of list */ }, 397 + }; 398 + MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids); 399 + 400 + static 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 + 407 + static int __init dm355evm_msp_init(void) 408 + { 409 + return i2c_add_driver(&dm355evm_msp_driver); 410 + } 411 + subsys_initcall(dm355evm_msp_init); 412 + 413 + static void __exit dm355evm_msp_exit(void) 414 + { 415 + i2c_del_driver(&dm355evm_msp_driver); 416 + } 417 + module_exit(dm355evm_msp_exit); 418 + 419 + MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM"); 420 + MODULE_LICENSE("GPL");
+79
include/linux/i2c/dm355evm_msp.h
··· 1 + /* 2 + * dm355evm_msp.h - support MSP430 microcontroller on DM355EVM board 3 + */ 4 + #ifndef __LINUX_I2C_DM355EVM_MSP 5 + #define __LINUX_I2C_DM355EVM_MSP 6 + 7 + /* 8 + * Written against Spectrum's writeup for the A4 firmware revision, 9 + * and tweaked to match source and rev D2 schematics by removing CPLD 10 + * and NOR flash hooks (which were last appropriate in rev B boards). 11 + * 12 + * Note that the firmware supports a flavor of write posting ... to be 13 + * sure a write completes, issue another read or write. 14 + */ 15 + 16 + /* utilities to access "registers" emulated by msp430 firmware */ 17 + extern int dm355evm_msp_write(u8 value, u8 reg); 18 + extern int dm355evm_msp_read(u8 reg); 19 + 20 + 21 + /* command/control registers */ 22 + #define DM355EVM_MSP_COMMAND 0x00 23 + # define MSP_COMMAND_NULL 0 24 + # define MSP_COMMAND_RESET_COLD 1 25 + # define MSP_COMMAND_RESET_WARM 2 26 + # define MSP_COMMAND_RESET_WARM_I 3 27 + # define MSP_COMMAND_POWEROFF 4 28 + # define MSP_COMMAND_IR_REINIT 5 29 + #define DM355EVM_MSP_STATUS 0x01 30 + # define MSP_STATUS_BAD_OFFSET BIT(0) 31 + # define MSP_STATUS_BAD_COMMAND BIT(1) 32 + # define MSP_STATUS_POWER_ERROR BIT(2) 33 + # define MSP_STATUS_RXBUF_OVERRUN BIT(3) 34 + #define DM355EVM_MSP_RESET 0x02 /* 0 bits == in reset */ 35 + # define MSP_RESET_DC5 BIT(0) 36 + # define MSP_RESET_TVP5154 BIT(2) 37 + # define MSP_RESET_IMAGER BIT(3) 38 + # define MSP_RESET_ETHERNET BIT(4) 39 + # define MSP_RESET_SYS BIT(5) 40 + # define MSP_RESET_AIC33 BIT(7) 41 + 42 + /* GPIO registers ... bit patterns mostly match the source MSP ports */ 43 + #define DM355EVM_MSP_LED 0x03 /* active low (MSP P4) */ 44 + #define DM355EVM_MSP_SWITCH1 0x04 /* (MSP P5, masked) */ 45 + # define MSP_SWITCH1_SW6_1 BIT(0) 46 + # define MSP_SWITCH1_SW6_2 BIT(1) 47 + # define MSP_SWITCH1_SW6_3 BIT(2) 48 + # define MSP_SWITCH1_SW6_4 BIT(3) 49 + # define MSP_SWITCH1_J1 BIT(4) /* NTSC/PAL */ 50 + # define MSP_SWITCH1_MSP_INT BIT(5) /* active low */ 51 + #define DM355EVM_MSP_SWITCH2 0x05 /* (MSP P6, masked) */ 52 + # define MSP_SWITCH2_SW10 BIT(3) 53 + # define MSP_SWITCH2_SW11 BIT(4) 54 + # define MSP_SWITCH2_SW12 BIT(5) 55 + # define MSP_SWITCH2_SW13 BIT(6) 56 + # define MSP_SWITCH2_SW14 BIT(7) 57 + #define DM355EVM_MSP_SDMMC 0x06 /* (MSP P2, masked) */ 58 + # define MSP_SDMMC_0_WP BIT(1) 59 + # define MSP_SDMMC_0_CD BIT(2) /* active low */ 60 + # define MSP_SDMMC_1_WP BIT(3) 61 + # define MSP_SDMMC_1_CD BIT(4) /* active low */ 62 + #define DM355EVM_MSP_FIRMREV 0x07 /* not a GPIO (out of order) */ 63 + #define DM355EVM_MSP_VIDEO_IN 0x08 /* (MSP P3, masked) */ 64 + # define MSP_VIDEO_IMAGER BIT(7) /* low == tvp5146 */ 65 + 66 + /* power supply registers are currently omitted */ 67 + 68 + /* RTC registers */ 69 + #define DM355EVM_MSP_RTC_0 0x12 /* LSB */ 70 + #define DM355EVM_MSP_RTC_1 0x13 71 + #define DM355EVM_MSP_RTC_2 0x14 72 + #define DM355EVM_MSP_RTC_3 0x15 /* MSB */ 73 + 74 + /* input event queue registers; code == ((HIGH << 8) | LOW) */ 75 + #define DM355EVM_MSP_INPUT_COUNT 0x16 /* decrement by reading LOW */ 76 + #define DM355EVM_MSP_INPUT_HIGH 0x17 77 + #define DM355EVM_MSP_INPUT_LOW 0x18 78 + 79 + #endif /* __LINUX_I2C_DM355EVM_MSP */