1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ca632f55SGrant Likely /*
3ca632f55SGrant Likely * Support Infineon TLE62x0 driver chips
4ca632f55SGrant Likely *
5ca632f55SGrant Likely * Copyright (c) 2007 Simtec Electronics
6ca632f55SGrant Likely * Ben Dooks, <ben@simtec.co.uk>
7ca632f55SGrant Likely */
8ca632f55SGrant Likely
9ca632f55SGrant Likely #include <linux/device.h>
10ca632f55SGrant Likely #include <linux/kernel.h>
11d7614de4SPaul Gortmaker #include <linux/module.h>
12ca632f55SGrant Likely #include <linux/slab.h>
13ca632f55SGrant Likely
14ca632f55SGrant Likely #include <linux/spi/spi.h>
15ca632f55SGrant Likely #include <linux/spi/tle62x0.h>
16ca632f55SGrant Likely
17ca632f55SGrant Likely
18ca632f55SGrant Likely #define CMD_READ 0x00
19ca632f55SGrant Likely #define CMD_SET 0xff
20ca632f55SGrant Likely
21ca632f55SGrant Likely #define DIAG_NORMAL 0x03
22ca632f55SGrant Likely #define DIAG_OVERLOAD 0x02
23ca632f55SGrant Likely #define DIAG_OPEN 0x01
24ca632f55SGrant Likely #define DIAG_SHORTGND 0x00
25ca632f55SGrant Likely
26ca632f55SGrant Likely struct tle62x0_state {
27ca632f55SGrant Likely struct spi_device *us;
28ca632f55SGrant Likely struct mutex lock;
29ca632f55SGrant Likely unsigned int nr_gpio;
30ca632f55SGrant Likely unsigned int gpio_state;
31ca632f55SGrant Likely
32ca632f55SGrant Likely unsigned char tx_buff[4];
33ca632f55SGrant Likely unsigned char rx_buff[4];
34ca632f55SGrant Likely };
35ca632f55SGrant Likely
36ca632f55SGrant Likely static int to_gpio_num(struct device_attribute *attr);
37ca632f55SGrant Likely
tle62x0_write(struct tle62x0_state * st)38ca632f55SGrant Likely static inline int tle62x0_write(struct tle62x0_state *st)
39ca632f55SGrant Likely {
40ca632f55SGrant Likely unsigned char *buff = st->tx_buff;
41ca632f55SGrant Likely unsigned int gpio_state = st->gpio_state;
42ca632f55SGrant Likely
43ca632f55SGrant Likely buff[0] = CMD_SET;
44ca632f55SGrant Likely
45ca632f55SGrant Likely if (st->nr_gpio == 16) {
46ca632f55SGrant Likely buff[1] = gpio_state >> 8;
47ca632f55SGrant Likely buff[2] = gpio_state;
48ca632f55SGrant Likely } else {
49ca632f55SGrant Likely buff[1] = gpio_state;
50ca632f55SGrant Likely }
51ca632f55SGrant Likely
52cd4c4244SAndy Shevchenko dev_dbg(&st->us->dev, "buff %3ph\n", buff);
53ca632f55SGrant Likely
54ca632f55SGrant Likely return spi_write(st->us, buff, (st->nr_gpio == 16) ? 3 : 2);
55ca632f55SGrant Likely }
56ca632f55SGrant Likely
tle62x0_read(struct tle62x0_state * st)57ca632f55SGrant Likely static inline int tle62x0_read(struct tle62x0_state *st)
58ca632f55SGrant Likely {
59ca632f55SGrant Likely unsigned char *txbuff = st->tx_buff;
60ca632f55SGrant Likely struct spi_transfer xfer = {
61ca632f55SGrant Likely .tx_buf = txbuff,
62ca632f55SGrant Likely .rx_buf = st->rx_buff,
63ca632f55SGrant Likely .len = (st->nr_gpio * 2) / 8,
64ca632f55SGrant Likely };
65ca632f55SGrant Likely struct spi_message msg;
66ca632f55SGrant Likely
67ca632f55SGrant Likely txbuff[0] = CMD_READ;
68ca632f55SGrant Likely txbuff[1] = 0x00;
69ca632f55SGrant Likely txbuff[2] = 0x00;
70ca632f55SGrant Likely txbuff[3] = 0x00;
71ca632f55SGrant Likely
72ca632f55SGrant Likely spi_message_init(&msg);
73ca632f55SGrant Likely spi_message_add_tail(&xfer, &msg);
74ca632f55SGrant Likely
75ca632f55SGrant Likely return spi_sync(st->us, &msg);
76ca632f55SGrant Likely }
77ca632f55SGrant Likely
decode_fault(unsigned int fault_code)78ca632f55SGrant Likely static unsigned char *decode_fault(unsigned int fault_code)
79ca632f55SGrant Likely {
80ca632f55SGrant Likely fault_code &= 3;
81ca632f55SGrant Likely
82ca632f55SGrant Likely switch (fault_code) {
83ca632f55SGrant Likely case DIAG_NORMAL:
84ca632f55SGrant Likely return "N";
85ca632f55SGrant Likely case DIAG_OVERLOAD:
86ca632f55SGrant Likely return "V";
87ca632f55SGrant Likely case DIAG_OPEN:
88ca632f55SGrant Likely return "O";
89ca632f55SGrant Likely case DIAG_SHORTGND:
90ca632f55SGrant Likely return "G";
91ca632f55SGrant Likely }
92ca632f55SGrant Likely
93ca632f55SGrant Likely return "?";
94ca632f55SGrant Likely }
95ca632f55SGrant Likely
tle62x0_status_show(struct device * dev,struct device_attribute * attr,char * buf)96ca632f55SGrant Likely static ssize_t tle62x0_status_show(struct device *dev,
97ca632f55SGrant Likely struct device_attribute *attr, char *buf)
98ca632f55SGrant Likely {
99ca632f55SGrant Likely struct tle62x0_state *st = dev_get_drvdata(dev);
100ca632f55SGrant Likely char *bp = buf;
101ca632f55SGrant Likely unsigned char *buff = st->rx_buff;
102ca632f55SGrant Likely unsigned long fault = 0;
103ca632f55SGrant Likely int ptr;
104ca632f55SGrant Likely int ret;
105ca632f55SGrant Likely
106ca632f55SGrant Likely mutex_lock(&st->lock);
107ca632f55SGrant Likely ret = tle62x0_read(st);
108ca632f55SGrant Likely dev_dbg(dev, "tle62x0_read() returned %d\n", ret);
109ca632f55SGrant Likely if (ret < 0) {
110ca632f55SGrant Likely mutex_unlock(&st->lock);
111ca632f55SGrant Likely return ret;
112ca632f55SGrant Likely }
113ca632f55SGrant Likely
114ca632f55SGrant Likely for (ptr = 0; ptr < (st->nr_gpio * 2)/8; ptr += 1) {
115ca632f55SGrant Likely fault <<= 8;
116ca632f55SGrant Likely fault |= ((unsigned long)buff[ptr]);
117ca632f55SGrant Likely
118ca632f55SGrant Likely dev_dbg(dev, "byte %d is %02x\n", ptr, buff[ptr]);
119ca632f55SGrant Likely }
120ca632f55SGrant Likely
121ca632f55SGrant Likely for (ptr = 0; ptr < st->nr_gpio; ptr++) {
122ca632f55SGrant Likely bp += sprintf(bp, "%s ", decode_fault(fault >> (ptr * 2)));
123ca632f55SGrant Likely }
124ca632f55SGrant Likely
125ca632f55SGrant Likely *bp++ = '\n';
126ca632f55SGrant Likely
127ca632f55SGrant Likely mutex_unlock(&st->lock);
128ca632f55SGrant Likely return bp - buf;
129ca632f55SGrant Likely }
130ca632f55SGrant Likely
131ca632f55SGrant Likely static DEVICE_ATTR(status_show, S_IRUGO, tle62x0_status_show, NULL);
132ca632f55SGrant Likely
tle62x0_gpio_show(struct device * dev,struct device_attribute * attr,char * buf)133ca632f55SGrant Likely static ssize_t tle62x0_gpio_show(struct device *dev,
134ca632f55SGrant Likely struct device_attribute *attr, char *buf)
135ca632f55SGrant Likely {
136ca632f55SGrant Likely struct tle62x0_state *st = dev_get_drvdata(dev);
137ca632f55SGrant Likely int gpio_num = to_gpio_num(attr);
138ca632f55SGrant Likely int value;
139ca632f55SGrant Likely
140ca632f55SGrant Likely mutex_lock(&st->lock);
141ca632f55SGrant Likely value = (st->gpio_state >> gpio_num) & 1;
142ca632f55SGrant Likely mutex_unlock(&st->lock);
143ca632f55SGrant Likely
14408411e34SQing Wang return sysfs_emit(buf, "%d", value);
145ca632f55SGrant Likely }
146ca632f55SGrant Likely
tle62x0_gpio_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)147ca632f55SGrant Likely static ssize_t tle62x0_gpio_store(struct device *dev,
148ca632f55SGrant Likely struct device_attribute *attr,
149ca632f55SGrant Likely const char *buf, size_t len)
150ca632f55SGrant Likely {
151ca632f55SGrant Likely struct tle62x0_state *st = dev_get_drvdata(dev);
152ca632f55SGrant Likely int gpio_num = to_gpio_num(attr);
153ca632f55SGrant Likely unsigned long val;
154ca632f55SGrant Likely char *endp;
155ca632f55SGrant Likely
156ca632f55SGrant Likely val = simple_strtoul(buf, &endp, 0);
157ca632f55SGrant Likely if (buf == endp)
158ca632f55SGrant Likely return -EINVAL;
159ca632f55SGrant Likely
160ca632f55SGrant Likely dev_dbg(dev, "setting gpio %d to %ld\n", gpio_num, val);
161ca632f55SGrant Likely
162ca632f55SGrant Likely mutex_lock(&st->lock);
163ca632f55SGrant Likely
164ca632f55SGrant Likely if (val)
165ca632f55SGrant Likely st->gpio_state |= 1 << gpio_num;
166ca632f55SGrant Likely else
167ca632f55SGrant Likely st->gpio_state &= ~(1 << gpio_num);
168ca632f55SGrant Likely
169ca632f55SGrant Likely tle62x0_write(st);
170ca632f55SGrant Likely mutex_unlock(&st->lock);
171ca632f55SGrant Likely
172ca632f55SGrant Likely return len;
173ca632f55SGrant Likely }
174ca632f55SGrant Likely
175ca632f55SGrant Likely static DEVICE_ATTR(gpio1, S_IWUSR|S_IRUGO,
176ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
177ca632f55SGrant Likely static DEVICE_ATTR(gpio2, S_IWUSR|S_IRUGO,
178ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
179ca632f55SGrant Likely static DEVICE_ATTR(gpio3, S_IWUSR|S_IRUGO,
180ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
181ca632f55SGrant Likely static DEVICE_ATTR(gpio4, S_IWUSR|S_IRUGO,
182ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
183ca632f55SGrant Likely static DEVICE_ATTR(gpio5, S_IWUSR|S_IRUGO,
184ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
185ca632f55SGrant Likely static DEVICE_ATTR(gpio6, S_IWUSR|S_IRUGO,
186ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
187ca632f55SGrant Likely static DEVICE_ATTR(gpio7, S_IWUSR|S_IRUGO,
188ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
189ca632f55SGrant Likely static DEVICE_ATTR(gpio8, S_IWUSR|S_IRUGO,
190ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
191ca632f55SGrant Likely static DEVICE_ATTR(gpio9, S_IWUSR|S_IRUGO,
192ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
193ca632f55SGrant Likely static DEVICE_ATTR(gpio10, S_IWUSR|S_IRUGO,
194ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
195ca632f55SGrant Likely static DEVICE_ATTR(gpio11, S_IWUSR|S_IRUGO,
196ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
197ca632f55SGrant Likely static DEVICE_ATTR(gpio12, S_IWUSR|S_IRUGO,
198ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
199ca632f55SGrant Likely static DEVICE_ATTR(gpio13, S_IWUSR|S_IRUGO,
200ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
201ca632f55SGrant Likely static DEVICE_ATTR(gpio14, S_IWUSR|S_IRUGO,
202ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
203ca632f55SGrant Likely static DEVICE_ATTR(gpio15, S_IWUSR|S_IRUGO,
204ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
205ca632f55SGrant Likely static DEVICE_ATTR(gpio16, S_IWUSR|S_IRUGO,
206ca632f55SGrant Likely tle62x0_gpio_show, tle62x0_gpio_store);
207ca632f55SGrant Likely
208ca632f55SGrant Likely static struct device_attribute *gpio_attrs[] = {
209ca632f55SGrant Likely [0] = &dev_attr_gpio1,
210ca632f55SGrant Likely [1] = &dev_attr_gpio2,
211ca632f55SGrant Likely [2] = &dev_attr_gpio3,
212ca632f55SGrant Likely [3] = &dev_attr_gpio4,
213ca632f55SGrant Likely [4] = &dev_attr_gpio5,
214ca632f55SGrant Likely [5] = &dev_attr_gpio6,
215ca632f55SGrant Likely [6] = &dev_attr_gpio7,
216ca632f55SGrant Likely [7] = &dev_attr_gpio8,
217ca632f55SGrant Likely [8] = &dev_attr_gpio9,
218ca632f55SGrant Likely [9] = &dev_attr_gpio10,
219ca632f55SGrant Likely [10] = &dev_attr_gpio11,
220ca632f55SGrant Likely [11] = &dev_attr_gpio12,
221ca632f55SGrant Likely [12] = &dev_attr_gpio13,
222ca632f55SGrant Likely [13] = &dev_attr_gpio14,
223ca632f55SGrant Likely [14] = &dev_attr_gpio15,
224ca632f55SGrant Likely [15] = &dev_attr_gpio16
225ca632f55SGrant Likely };
226ca632f55SGrant Likely
to_gpio_num(struct device_attribute * attr)227ca632f55SGrant Likely static int to_gpio_num(struct device_attribute *attr)
228ca632f55SGrant Likely {
229ca632f55SGrant Likely int ptr;
230ca632f55SGrant Likely
231ca632f55SGrant Likely for (ptr = 0; ptr < ARRAY_SIZE(gpio_attrs); ptr++) {
232ca632f55SGrant Likely if (gpio_attrs[ptr] == attr)
233ca632f55SGrant Likely return ptr;
234ca632f55SGrant Likely }
235ca632f55SGrant Likely
236ca632f55SGrant Likely return -1;
237ca632f55SGrant Likely }
238ca632f55SGrant Likely
tle62x0_probe(struct spi_device * spi)239fd4a319bSGrant Likely static int tle62x0_probe(struct spi_device *spi)
240ca632f55SGrant Likely {
241ca632f55SGrant Likely struct tle62x0_state *st;
242ca632f55SGrant Likely struct tle62x0_pdata *pdata;
243ca632f55SGrant Likely int ptr;
244ca632f55SGrant Likely int ret;
245ca632f55SGrant Likely
2468074cf06SJingoo Han pdata = dev_get_platdata(&spi->dev);
247ca632f55SGrant Likely if (pdata == NULL) {
248ca632f55SGrant Likely dev_err(&spi->dev, "no device data specified\n");
249ca632f55SGrant Likely return -EINVAL;
250ca632f55SGrant Likely }
251ca632f55SGrant Likely
252ca632f55SGrant Likely st = kzalloc(sizeof(struct tle62x0_state), GFP_KERNEL);
2539f48e54bSJingoo Han if (st == NULL)
254ca632f55SGrant Likely return -ENOMEM;
255ca632f55SGrant Likely
256ca632f55SGrant Likely st->us = spi;
257ca632f55SGrant Likely st->nr_gpio = pdata->gpio_count;
258ca632f55SGrant Likely st->gpio_state = pdata->init_state;
259ca632f55SGrant Likely
260ca632f55SGrant Likely mutex_init(&st->lock);
261ca632f55SGrant Likely
262ca632f55SGrant Likely ret = device_create_file(&spi->dev, &dev_attr_status_show);
263ca632f55SGrant Likely if (ret) {
264ca632f55SGrant Likely dev_err(&spi->dev, "cannot create status attribute\n");
265ca632f55SGrant Likely goto err_status;
266ca632f55SGrant Likely }
267ca632f55SGrant Likely
268ca632f55SGrant Likely for (ptr = 0; ptr < pdata->gpio_count; ptr++) {
269ca632f55SGrant Likely ret = device_create_file(&spi->dev, gpio_attrs[ptr]);
270ca632f55SGrant Likely if (ret) {
271ca632f55SGrant Likely dev_err(&spi->dev, "cannot create gpio attribute\n");
272ca632f55SGrant Likely goto err_gpios;
273ca632f55SGrant Likely }
274ca632f55SGrant Likely }
275ca632f55SGrant Likely
276ca632f55SGrant Likely /* tle62x0_write(st); */
277ca632f55SGrant Likely spi_set_drvdata(spi, st);
278ca632f55SGrant Likely return 0;
279ca632f55SGrant Likely
280ca632f55SGrant Likely err_gpios:
281ca632f55SGrant Likely while (--ptr >= 0)
282ca632f55SGrant Likely device_remove_file(&spi->dev, gpio_attrs[ptr]);
283ca632f55SGrant Likely
284ca632f55SGrant Likely device_remove_file(&spi->dev, &dev_attr_status_show);
285ca632f55SGrant Likely
286ca632f55SGrant Likely err_status:
287ca632f55SGrant Likely kfree(st);
288ca632f55SGrant Likely return ret;
289ca632f55SGrant Likely }
290ca632f55SGrant Likely
tle62x0_remove(struct spi_device * spi)291*a0386bbaSUwe Kleine-König static void tle62x0_remove(struct spi_device *spi)
292ca632f55SGrant Likely {
293ca632f55SGrant Likely struct tle62x0_state *st = spi_get_drvdata(spi);
294ca632f55SGrant Likely int ptr;
295ca632f55SGrant Likely
296ca632f55SGrant Likely for (ptr = 0; ptr < st->nr_gpio; ptr++)
297ca632f55SGrant Likely device_remove_file(&spi->dev, gpio_attrs[ptr]);
298ca632f55SGrant Likely
299ca632f55SGrant Likely device_remove_file(&spi->dev, &dev_attr_status_show);
300ca632f55SGrant Likely kfree(st);
301ca632f55SGrant Likely }
302ca632f55SGrant Likely
303ca632f55SGrant Likely static struct spi_driver tle62x0_driver = {
304ca632f55SGrant Likely .driver = {
305ca632f55SGrant Likely .name = "tle62x0",
306ca632f55SGrant Likely },
307ca632f55SGrant Likely .probe = tle62x0_probe,
308fd4a319bSGrant Likely .remove = tle62x0_remove,
309ca632f55SGrant Likely };
310ca632f55SGrant Likely
31138e271cdSSachin Kamat module_spi_driver(tle62x0_driver);
312ca632f55SGrant Likely
313ca632f55SGrant Likely MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
314ca632f55SGrant Likely MODULE_DESCRIPTION("TLE62x0 SPI driver");
315ca632f55SGrant Likely MODULE_LICENSE("GPL v2");
316ca632f55SGrant Likely MODULE_ALIAS("spi:tle62x0");
317