Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/mfd/dm355evm_msp.c
15109 views
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_INPUT_DM355EVM) || defined(CONFIG_INPUT_DM355EVM_MODULE)
36
#define msp_has_keyboard() true
37
#else
38
#define msp_has_keyboard() false
39
#endif
40
41
#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
42
#define msp_has_leds() true
43
#else
44
#define msp_has_leds() false
45
#endif
46
47
#if defined(CONFIG_RTC_DRV_DM355EVM) || defined(CONFIG_RTC_DRV_DM355EVM_MODULE)
48
#define msp_has_rtc() true
49
#else
50
#define msp_has_rtc() false
51
#endif
52
53
#if defined(CONFIG_VIDEO_TVP514X) || defined(CONFIG_VIDEO_TVP514X_MODULE)
54
#define msp_has_tvp() true
55
#else
56
#define msp_has_tvp() false
57
#endif
58
59
60
/*----------------------------------------------------------------------*/
61
62
/* REVISIT for paranoia's sake, retry reads/writes on error */
63
64
static struct i2c_client *msp430;
65
66
/**
67
* dm355evm_msp_write - Writes a register in dm355evm_msp
68
* @value: the value to be written
69
* @reg: register address
70
*
71
* Returns result of operation - 0 is success, else negative errno
72
*/
73
int dm355evm_msp_write(u8 value, u8 reg)
74
{
75
return i2c_smbus_write_byte_data(msp430, reg, value);
76
}
77
EXPORT_SYMBOL(dm355evm_msp_write);
78
79
/**
80
* dm355evm_msp_read - Reads a register from dm355evm_msp
81
* @reg: register address
82
*
83
* Returns result of operation - value, or negative errno
84
*/
85
int dm355evm_msp_read(u8 reg)
86
{
87
return i2c_smbus_read_byte_data(msp430, reg);
88
}
89
EXPORT_SYMBOL(dm355evm_msp_read);
90
91
/*----------------------------------------------------------------------*/
92
93
/*
94
* Many of the msp430 pins are just used as fixed-direction GPIOs.
95
* We could export a few more of them this way, if we wanted.
96
*/
97
#define MSP_GPIO(bit,reg) ((DM355EVM_MSP_ ## reg) << 3 | (bit))
98
99
static const u8 msp_gpios[] = {
100
/* eight leds */
101
MSP_GPIO(0, LED), MSP_GPIO(1, LED),
102
MSP_GPIO(2, LED), MSP_GPIO(3, LED),
103
MSP_GPIO(4, LED), MSP_GPIO(5, LED),
104
MSP_GPIO(6, LED), MSP_GPIO(7, LED),
105
/* SW6 and the NTSC/nPAL jumper */
106
MSP_GPIO(0, SWITCH1), MSP_GPIO(1, SWITCH1),
107
MSP_GPIO(2, SWITCH1), MSP_GPIO(3, SWITCH1),
108
MSP_GPIO(4, SWITCH1),
109
/* switches on MMC/SD sockets */
110
/*
111
* Note: EVMDM355_ECP_VA4.pdf suggests that Bit 2 and 4 should be
112
* checked for card detection. However on the EVM bit 1 and 3 gives
113
* this status, for 0 and 1 instance respectively. The pdf also
114
* suggests that Bit 1 and 3 should be checked for write protection.
115
* However on the EVM bit 2 and 4 gives this status,for 0 and 1
116
* instance respectively.
117
*/
118
MSP_GPIO(2, SDMMC), MSP_GPIO(1, SDMMC), /* mmc0 WP, nCD */
119
MSP_GPIO(4, SDMMC), MSP_GPIO(3, SDMMC), /* mmc1 WP, nCD */
120
};
121
122
#define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3)
123
#define MSP_GPIO_MASK(offset) BIT(msp_gpios[(offset)] & 0x07)
124
125
static int msp_gpio_in(struct gpio_chip *chip, unsigned offset)
126
{
127
switch (MSP_GPIO_REG(offset)) {
128
case DM355EVM_MSP_SWITCH1:
129
case DM355EVM_MSP_SWITCH2:
130
case DM355EVM_MSP_SDMMC:
131
return 0;
132
default:
133
return -EINVAL;
134
}
135
}
136
137
static u8 msp_led_cache;
138
139
static int msp_gpio_get(struct gpio_chip *chip, unsigned offset)
140
{
141
int reg, status;
142
143
reg = MSP_GPIO_REG(offset);
144
status = dm355evm_msp_read(reg);
145
if (status < 0)
146
return status;
147
if (reg == DM355EVM_MSP_LED)
148
msp_led_cache = status;
149
return status & MSP_GPIO_MASK(offset);
150
}
151
152
static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value)
153
{
154
int mask, bits;
155
156
/* NOTE: there are some other signals that could be
157
* packaged as output GPIOs, but they aren't as useful
158
* as the LEDs ... so for now we don't.
159
*/
160
if (MSP_GPIO_REG(offset) != DM355EVM_MSP_LED)
161
return -EINVAL;
162
163
mask = MSP_GPIO_MASK(offset);
164
bits = msp_led_cache;
165
166
bits &= ~mask;
167
if (value)
168
bits |= mask;
169
msp_led_cache = bits;
170
171
return dm355evm_msp_write(bits, DM355EVM_MSP_LED);
172
}
173
174
static void msp_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
175
{
176
msp_gpio_out(chip, offset, value);
177
}
178
179
static struct gpio_chip dm355evm_msp_gpio = {
180
.label = "dm355evm_msp",
181
.owner = THIS_MODULE,
182
.direction_input = msp_gpio_in,
183
.get = msp_gpio_get,
184
.direction_output = msp_gpio_out,
185
.set = msp_gpio_set,
186
.base = -EINVAL, /* dynamic assignment */
187
.ngpio = ARRAY_SIZE(msp_gpios),
188
.can_sleep = true,
189
};
190
191
/*----------------------------------------------------------------------*/
192
193
static struct device *add_child(struct i2c_client *client, const char *name,
194
void *pdata, unsigned pdata_len,
195
bool can_wakeup, int irq)
196
{
197
struct platform_device *pdev;
198
int status;
199
200
pdev = platform_device_alloc(name, -1);
201
if (!pdev) {
202
dev_dbg(&client->dev, "can't alloc dev\n");
203
status = -ENOMEM;
204
goto err;
205
}
206
207
device_init_wakeup(&pdev->dev, can_wakeup);
208
pdev->dev.parent = &client->dev;
209
210
if (pdata) {
211
status = platform_device_add_data(pdev, pdata, pdata_len);
212
if (status < 0) {
213
dev_dbg(&pdev->dev, "can't add platform_data\n");
214
goto err;
215
}
216
}
217
218
if (irq) {
219
struct resource r = {
220
.start = irq,
221
.flags = IORESOURCE_IRQ,
222
};
223
224
status = platform_device_add_resources(pdev, &r, 1);
225
if (status < 0) {
226
dev_dbg(&pdev->dev, "can't add irq\n");
227
goto err;
228
}
229
}
230
231
status = platform_device_add(pdev);
232
233
err:
234
if (status < 0) {
235
platform_device_put(pdev);
236
dev_err(&client->dev, "can't add %s dev\n", name);
237
return ERR_PTR(status);
238
}
239
return &pdev->dev;
240
}
241
242
static int add_children(struct i2c_client *client)
243
{
244
static const struct {
245
int offset;
246
char *label;
247
} config_inputs[] = {
248
/* 8 == right after the LEDs */
249
{ 8 + 0, "sw6_1", },
250
{ 8 + 1, "sw6_2", },
251
{ 8 + 2, "sw6_3", },
252
{ 8 + 3, "sw6_4", },
253
{ 8 + 4, "NTSC/nPAL", },
254
};
255
256
struct device *child;
257
int status;
258
int i;
259
260
/* GPIO-ish stuff */
261
dm355evm_msp_gpio.dev = &client->dev;
262
status = gpiochip_add(&dm355evm_msp_gpio);
263
if (status < 0)
264
return status;
265
266
/* LED output */
267
if (msp_has_leds()) {
268
#define GPIO_LED(l) .name = l, .active_low = true
269
static struct gpio_led evm_leds[] = {
270
{ GPIO_LED("dm355evm::ds14"),
271
.default_trigger = "heartbeat", },
272
{ GPIO_LED("dm355evm::ds15"),
273
.default_trigger = "mmc0", },
274
{ GPIO_LED("dm355evm::ds16"),
275
/* could also be a CE-ATA drive */
276
.default_trigger = "mmc1", },
277
{ GPIO_LED("dm355evm::ds17"),
278
.default_trigger = "nand-disk", },
279
{ GPIO_LED("dm355evm::ds18"), },
280
{ GPIO_LED("dm355evm::ds19"), },
281
{ GPIO_LED("dm355evm::ds20"), },
282
{ GPIO_LED("dm355evm::ds21"), },
283
};
284
#undef GPIO_LED
285
286
struct gpio_led_platform_data evm_led_data = {
287
.num_leds = ARRAY_SIZE(evm_leds),
288
.leds = evm_leds,
289
};
290
291
for (i = 0; i < ARRAY_SIZE(evm_leds); i++)
292
evm_leds[i].gpio = i + dm355evm_msp_gpio.base;
293
294
/* NOTE: these are the only fully programmable LEDs
295
* on the board, since GPIO-61/ds22 (and many signals
296
* going to DC7) must be used for AEMIF address lines
297
* unless the top 1 GB of NAND is unused...
298
*/
299
child = add_child(client, "leds-gpio",
300
&evm_led_data, sizeof(evm_led_data),
301
false, 0);
302
if (IS_ERR(child))
303
return PTR_ERR(child);
304
}
305
306
/* configuration inputs */
307
for (i = 0; i < ARRAY_SIZE(config_inputs); i++) {
308
int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset;
309
310
gpio_request(gpio, config_inputs[i].label);
311
gpio_direction_input(gpio);
312
313
/* make it easy for userspace to see these */
314
gpio_export(gpio, false);
315
}
316
317
/* MMC/SD inputs -- right after the last config input */
318
if (client->dev.platform_data) {
319
void (*mmcsd_setup)(unsigned) = client->dev.platform_data;
320
321
mmcsd_setup(dm355evm_msp_gpio.base + 8 + 5);
322
}
323
324
/* RTC is a 32 bit counter, no alarm */
325
if (msp_has_rtc()) {
326
child = add_child(client, "rtc-dm355evm",
327
NULL, 0, false, 0);
328
if (IS_ERR(child))
329
return PTR_ERR(child);
330
}
331
332
/* input from buttons and IR remote (uses the IRQ) */
333
if (msp_has_keyboard()) {
334
child = add_child(client, "dm355evm_keys",
335
NULL, 0, true, client->irq);
336
if (IS_ERR(child))
337
return PTR_ERR(child);
338
}
339
340
return 0;
341
}
342
343
/*----------------------------------------------------------------------*/
344
345
static void dm355evm_command(unsigned command)
346
{
347
int status;
348
349
status = dm355evm_msp_write(command, DM355EVM_MSP_COMMAND);
350
if (status < 0)
351
dev_err(&msp430->dev, "command %d failure %d\n",
352
command, status);
353
}
354
355
static void dm355evm_power_off(void)
356
{
357
dm355evm_command(MSP_COMMAND_POWEROFF);
358
}
359
360
static int dm355evm_msp_remove(struct i2c_client *client)
361
{
362
pm_power_off = NULL;
363
msp430 = NULL;
364
return 0;
365
}
366
367
static int
368
dm355evm_msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
369
{
370
int status;
371
const char *video = msp_has_tvp() ? "TVP5146" : "imager";
372
373
if (msp430)
374
return -EBUSY;
375
msp430 = client;
376
377
/* display revision status; doubles as sanity check */
378
status = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
379
if (status < 0)
380
goto fail;
381
dev_info(&client->dev, "firmware v.%02X, %s as video-in\n",
382
status, video);
383
384
/* mux video input: either tvp5146 or some external imager */
385
status = dm355evm_msp_write(msp_has_tvp() ? 0 : MSP_VIDEO_IMAGER,
386
DM355EVM_MSP_VIDEO_IN);
387
if (status < 0)
388
dev_warn(&client->dev, "error %d muxing %s as video-in\n",
389
status, video);
390
391
/* init LED cache, and turn off the LEDs */
392
msp_led_cache = 0xff;
393
dm355evm_msp_write(msp_led_cache, DM355EVM_MSP_LED);
394
395
/* export capabilities we support */
396
status = add_children(client);
397
if (status < 0)
398
goto fail;
399
400
/* PM hookup */
401
pm_power_off = dm355evm_power_off;
402
403
return 0;
404
405
fail:
406
/* FIXME remove children ... */
407
dm355evm_msp_remove(client);
408
return status;
409
}
410
411
static const struct i2c_device_id dm355evm_msp_ids[] = {
412
{ "dm355evm_msp", 0 },
413
{ /* end of list */ },
414
};
415
MODULE_DEVICE_TABLE(i2c, dm355evm_msp_ids);
416
417
static struct i2c_driver dm355evm_msp_driver = {
418
.driver.name = "dm355evm_msp",
419
.id_table = dm355evm_msp_ids,
420
.probe = dm355evm_msp_probe,
421
.remove = dm355evm_msp_remove,
422
};
423
424
static int __init dm355evm_msp_init(void)
425
{
426
return i2c_add_driver(&dm355evm_msp_driver);
427
}
428
subsys_initcall(dm355evm_msp_init);
429
430
static void __exit dm355evm_msp_exit(void)
431
{
432
i2c_del_driver(&dm355evm_msp_driver);
433
}
434
module_exit(dm355evm_msp_exit);
435
436
MODULE_DESCRIPTION("Interface to MSP430 firmware on DM355EVM");
437
MODULE_LICENSE("GPL");
438
439