aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBen Dooks <ben@trinity.fluff.org>2007-07-17 07:04:10 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-17 13:23:05 -0400
commit447aef1a19135a69bfd725c33f7e753740cb8447 (patch)
treebef8836aaf1acef2a86e71ff8898b9675fae6416 /drivers
parentda0abc275f029e0f52c5165de524bcf64e717703 (diff)
SPI: tle620x power switch driver
Add support for the Infineon TLE62x0 series of low-side driver chips, such as the TLE6220 or TLE6230. These can be viewed as output GPIOs specialized for power switching applications. The driver provides a userspace interface to those GPIOs, and to the switch status they provide. Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/Kconfig9
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/tle62x0.c328
3 files changed, 338 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8ce16a0b8a50..a49270eae660 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -198,6 +198,15 @@ config SPI_SPIDEV
198 Note that this application programming interface is EXPERIMENTAL 198 Note that this application programming interface is EXPERIMENTAL
199 and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes. 199 and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes.
200 200
201config SPI_TLE62X0
202 tristate "Infineon TLE62X0 (for power switching)"
203 depends on SPI_MASTER && SYSFS
204 help
205 SPI driver for Infineon TLE62X0 series line driver chips,
206 such as the TLE6220, TLE6230 and TLE6240. This provides a
207 sysfs interface, with each line presented as a kind of GPIO
208 exposing both switch control and diagnostic feedback.
209
201# 210#
202# Add new SPI protocol masters in alphabetical order above this line 211# Add new SPI protocol masters in alphabetical order above this line
203# 212#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 89293338f20c..d9bd7f605d6a 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
29# SPI protocol drivers (device/link on bus) 29# SPI protocol drivers (device/link on bus)
30obj-$(CONFIG_SPI_AT25) += at25.o 30obj-$(CONFIG_SPI_AT25) += at25.o
31obj-$(CONFIG_SPI_SPIDEV) += spidev.o 31obj-$(CONFIG_SPI_SPIDEV) += spidev.o
32obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o
32# ... add above this line ... 33# ... add above this line ...
33 34
34# SPI slave controller drivers (upstream link) 35# SPI slave controller drivers (upstream link)
diff --git a/drivers/spi/tle62x0.c b/drivers/spi/tle62x0.c
new file mode 100644
index 000000000000..6da58ca48b33
--- /dev/null
+++ b/drivers/spi/tle62x0.c
@@ -0,0 +1,328 @@
1/*
2 * tle62x0.c -- support Infineon TLE62x0 driver chips
3 *
4 * Copyright (c) 2007 Simtec Electronics
5 * Ben Dooks, <ben@simtec.co.uk>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/device.h>
13#include <linux/kernel.h>
14
15#include <linux/spi/spi.h>
16#include <linux/spi/tle62x0.h>
17
18
19#define CMD_READ 0x00
20#define CMD_SET 0xff
21
22#define DIAG_NORMAL 0x03
23#define DIAG_OVERLOAD 0x02
24#define DIAG_OPEN 0x01
25#define DIAG_SHORTGND 0x00
26
27struct tle62x0_state {
28 struct spi_device *us;
29 struct mutex lock;
30 unsigned int nr_gpio;
31 unsigned int gpio_state;
32
33 unsigned char tx_buff[4];
34 unsigned char rx_buff[4];
35};
36
37static int to_gpio_num(struct device_attribute *attr);
38
39static inline int tle62x0_write(struct tle62x0_state *st)
40{
41 unsigned char *buff = st->tx_buff;
42 unsigned int gpio_state = st->gpio_state;
43
44 buff[0] = CMD_SET;
45
46 if (st->nr_gpio == 16) {
47 buff[1] = gpio_state >> 8;
48 buff[2] = gpio_state;
49 } else {
50 buff[1] = gpio_state;
51 }
52
53 dev_dbg(&st->us->dev, "buff %02x,%02x,%02x\n",
54 buff[0], buff[1], buff[2]);
55
56 return spi_write(st->us, buff, (st->nr_gpio == 16) ? 3 : 2);
57}
58
59static inline int tle62x0_read(struct tle62x0_state *st)
60{
61 unsigned char *txbuff = st->tx_buff;
62 struct spi_transfer xfer = {
63 .tx_buf = txbuff,
64 .rx_buf = st->rx_buff,
65 .len = (st->nr_gpio * 2) / 8,
66 };
67 struct spi_message msg;
68
69 txbuff[0] = CMD_READ;
70 txbuff[1] = 0x00;
71 txbuff[2] = 0x00;
72 txbuff[3] = 0x00;
73
74 spi_message_init(&msg);
75 spi_message_add_tail(&xfer, &msg);
76
77 return spi_sync(st->us, &msg);
78}
79
80static unsigned char *decode_fault(unsigned int fault_code)
81{
82 fault_code &= 3;
83
84 switch (fault_code) {
85 case DIAG_NORMAL:
86 return "N";
87 case DIAG_OVERLOAD:
88 return "V";
89 case DIAG_OPEN:
90 return "O";
91 case DIAG_SHORTGND:
92 return "G";
93 }
94
95 return "?";
96}
97
98static ssize_t tle62x0_status_show(struct device *dev,
99 struct device_attribute *attr, char *buf)
100{
101 struct tle62x0_state *st = dev_get_drvdata(dev);
102 char *bp = buf;
103 unsigned char *buff = st->rx_buff;
104 unsigned long fault = 0;
105 int ptr;
106 int ret;
107
108 mutex_lock(&st->lock);
109 ret = tle62x0_read(st);
110
111 dev_dbg(dev, "tle62x0_read() returned %d\n", ret);
112
113 for (ptr = 0; ptr < (st->nr_gpio * 2)/8; ptr += 1) {
114 fault <<= 8;
115 fault |= ((unsigned long)buff[ptr]);
116
117 dev_dbg(dev, "byte %d is %02x\n", ptr, buff[ptr]);
118 }
119
120 for (ptr = 0; ptr < st->nr_gpio; ptr++) {
121 bp += sprintf(bp, "%s ", decode_fault(fault >> (ptr * 2)));
122 }
123
124 *bp++ = '\n';
125
126 mutex_unlock(&st->lock);
127 return bp - buf;
128}
129
130static DEVICE_ATTR(status_show, S_IRUGO, tle62x0_status_show, NULL);
131
132static ssize_t tle62x0_gpio_show(struct device *dev,
133 struct device_attribute *attr, char *buf)
134{
135 struct tle62x0_state *st = dev_get_drvdata(dev);
136 int gpio_num = to_gpio_num(attr);
137 int value;
138
139 mutex_lock(&st->lock);
140 value = (st->gpio_state >> gpio_num) & 1;
141 mutex_unlock(&st->lock);
142
143 return snprintf(buf, PAGE_SIZE, "%d", value);
144}
145
146static ssize_t tle62x0_gpio_store(struct device *dev,
147 struct device_attribute *attr,
148 const char *buf, size_t len)
149{
150 struct tle62x0_state *st = dev_get_drvdata(dev);
151 int gpio_num = to_gpio_num(attr);
152 unsigned long val;
153 char *endp;
154
155 val = simple_strtoul(buf, &endp, 0);
156 if (buf == endp)
157 return -EINVAL;
158
159 dev_dbg(dev, "setting gpio %d to %ld\n", gpio_num, val);
160
161 mutex_lock(&st->lock);
162
163 if (val)
164 st->gpio_state |= 1 << gpio_num;
165 else
166 st->gpio_state &= ~(1 << gpio_num);
167
168 tle62x0_write(st);
169 mutex_unlock(&st->lock);
170
171 return len;
172}
173
174static DEVICE_ATTR(gpio1, S_IWUSR|S_IRUGO,
175 tle62x0_gpio_show, tle62x0_gpio_store);
176static DEVICE_ATTR(gpio2, S_IWUSR|S_IRUGO,
177 tle62x0_gpio_show, tle62x0_gpio_store);
178static DEVICE_ATTR(gpio3, S_IWUSR|S_IRUGO,
179 tle62x0_gpio_show, tle62x0_gpio_store);
180static DEVICE_ATTR(gpio4, S_IWUSR|S_IRUGO,
181 tle62x0_gpio_show, tle62x0_gpio_store);
182static DEVICE_ATTR(gpio5, S_IWUSR|S_IRUGO,
183 tle62x0_gpio_show, tle62x0_gpio_store);
184static DEVICE_ATTR(gpio6, S_IWUSR|S_IRUGO,
185 tle62x0_gpio_show, tle62x0_gpio_store);
186static DEVICE_ATTR(gpio7, S_IWUSR|S_IRUGO,
187 tle62x0_gpio_show, tle62x0_gpio_store);
188static DEVICE_ATTR(gpio8, S_IWUSR|S_IRUGO,
189 tle62x0_gpio_show, tle62x0_gpio_store);
190static DEVICE_ATTR(gpio9, S_IWUSR|S_IRUGO,
191 tle62x0_gpio_show, tle62x0_gpio_store);
192static DEVICE_ATTR(gpio10, S_IWUSR|S_IRUGO,
193 tle62x0_gpio_show, tle62x0_gpio_store);
194static DEVICE_ATTR(gpio11, S_IWUSR|S_IRUGO,
195 tle62x0_gpio_show, tle62x0_gpio_store);
196static DEVICE_ATTR(gpio12, S_IWUSR|S_IRUGO,
197 tle62x0_gpio_show, tle62x0_gpio_store);
198static DEVICE_ATTR(gpio13, S_IWUSR|S_IRUGO,
199 tle62x0_gpio_show, tle62x0_gpio_store);
200static DEVICE_ATTR(gpio14, S_IWUSR|S_IRUGO,
201 tle62x0_gpio_show, tle62x0_gpio_store);
202static DEVICE_ATTR(gpio15, S_IWUSR|S_IRUGO,
203 tle62x0_gpio_show, tle62x0_gpio_store);
204static DEVICE_ATTR(gpio16, S_IWUSR|S_IRUGO,
205 tle62x0_gpio_show, tle62x0_gpio_store);
206
207static struct device_attribute *gpio_attrs[] = {
208 [0] = &dev_attr_gpio1,
209 [1] = &dev_attr_gpio2,
210 [2] = &dev_attr_gpio3,
211 [3] = &dev_attr_gpio4,
212 [4] = &dev_attr_gpio5,
213 [5] = &dev_attr_gpio6,
214 [6] = &dev_attr_gpio7,
215 [7] = &dev_attr_gpio8,
216 [8] = &dev_attr_gpio9,
217 [9] = &dev_attr_gpio10,
218 [10] = &dev_attr_gpio11,
219 [11] = &dev_attr_gpio12,
220 [12] = &dev_attr_gpio13,
221 [13] = &dev_attr_gpio14,
222 [14] = &dev_attr_gpio15,
223 [15] = &dev_attr_gpio16
224};
225
226static int to_gpio_num(struct device_attribute *attr)
227{
228 int ptr;
229
230 for (ptr = 0; ptr < ARRAY_SIZE(gpio_attrs); ptr++) {
231 if (gpio_attrs[ptr] == attr)
232 return ptr;
233 }
234
235 return -1;
236}
237
238static int __devinit tle62x0_probe(struct spi_device *spi)
239{
240 struct tle62x0_state *st;
241 struct tle62x0_pdata *pdata;
242 int ptr;
243 int ret;
244
245 pdata = spi->dev.platform_data;
246 if (pdata == NULL) {
247 dev_err(&spi->dev, "no device data specified\n");
248 return -EINVAL;
249 }
250
251 st = kzalloc(sizeof(struct tle62x0_state), GFP_KERNEL);
252 if (st == NULL) {
253 dev_err(&spi->dev, "no memory for device state\n");
254 return -ENOMEM;
255 }
256
257 st->us = spi;
258 st->nr_gpio = pdata->gpio_count;
259 st->gpio_state = pdata->init_state;
260
261 mutex_init(&st->lock);
262
263 ret = device_create_file(&spi->dev, &dev_attr_status_show);
264 if (ret) {
265 dev_err(&spi->dev, "cannot create status attribute\n");
266 goto err_status;
267 }
268
269 for (ptr = 0; ptr < pdata->gpio_count; ptr++) {
270 ret = device_create_file(&spi->dev, gpio_attrs[ptr]);
271 if (ret) {
272 dev_err(&spi->dev, "cannot create gpio attribute\n");
273 goto err_gpios;
274 }
275 }
276
277 /* tle62x0_write(st); */
278 spi_set_drvdata(spi, st);
279 return 0;
280
281 err_gpios:
282 for (; ptr > 0; ptr--)
283 device_remove_file(&spi->dev, gpio_attrs[ptr]);
284
285 device_remove_file(&spi->dev, &dev_attr_status_show);
286
287 err_status:
288 kfree(st);
289 return ret;
290}
291
292static int __devexit tle62x0_remove(struct spi_device *spi)
293{
294 struct tle62x0_state *st = spi_get_drvdata(spi);
295 int ptr;
296
297 for (ptr = 0; ptr < st->nr_gpio; ptr++)
298 device_remove_file(&spi->dev, gpio_attrs[ptr]);
299
300 kfree(st);
301 return 0;
302}
303
304static struct spi_driver tle62x0_driver = {
305 .driver = {
306 .name = "tle62x0",
307 .owner = THIS_MODULE,
308 },
309 .probe = tle62x0_probe,
310 .remove = __devexit_p(tle62x0_remove),
311};
312
313static __init int tle62x0_init(void)
314{
315 return spi_register_driver(&tle62x0_driver);
316}
317
318static __exit void tle62x0_exit(void)
319{
320 spi_unregister_driver(&tle62x0_driver);
321}
322
323module_init(tle62x0_init);
324module_exit(tle62x0_exit);
325
326MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
327MODULE_DESCRIPTION("TLE62x0 SPI driver");
328MODULE_LICENSE("GPL v2");