Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Ben Dooks and committed by
Linus Torvalds
447aef1a da0abc27

+362
+9
drivers/spi/Kconfig
··· 198 198 Note that this application programming interface is EXPERIMENTAL 199 199 and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes. 200 200 201 + config 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 211 # Add new SPI protocol masters in alphabetical order above this line 203 212 #
+1
drivers/spi/Makefile
··· 29 29 # SPI protocol drivers (device/link on bus) 30 30 obj-$(CONFIG_SPI_AT25) += at25.o 31 31 obj-$(CONFIG_SPI_SPIDEV) += spidev.o 32 + obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o 32 33 # ... add above this line ... 33 34 34 35 # SPI slave controller drivers (upstream link)
+328
drivers/spi/tle62x0.c
··· 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 + 27 + struct 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 + 37 + static int to_gpio_num(struct device_attribute *attr); 38 + 39 + static 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 + 59 + static 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 + 80 + static 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 + 98 + static 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 + 130 + static DEVICE_ATTR(status_show, S_IRUGO, tle62x0_status_show, NULL); 131 + 132 + static 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 + 146 + static 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 + 174 + static DEVICE_ATTR(gpio1, S_IWUSR|S_IRUGO, 175 + tle62x0_gpio_show, tle62x0_gpio_store); 176 + static DEVICE_ATTR(gpio2, S_IWUSR|S_IRUGO, 177 + tle62x0_gpio_show, tle62x0_gpio_store); 178 + static DEVICE_ATTR(gpio3, S_IWUSR|S_IRUGO, 179 + tle62x0_gpio_show, tle62x0_gpio_store); 180 + static DEVICE_ATTR(gpio4, S_IWUSR|S_IRUGO, 181 + tle62x0_gpio_show, tle62x0_gpio_store); 182 + static DEVICE_ATTR(gpio5, S_IWUSR|S_IRUGO, 183 + tle62x0_gpio_show, tle62x0_gpio_store); 184 + static DEVICE_ATTR(gpio6, S_IWUSR|S_IRUGO, 185 + tle62x0_gpio_show, tle62x0_gpio_store); 186 + static DEVICE_ATTR(gpio7, S_IWUSR|S_IRUGO, 187 + tle62x0_gpio_show, tle62x0_gpio_store); 188 + static DEVICE_ATTR(gpio8, S_IWUSR|S_IRUGO, 189 + tle62x0_gpio_show, tle62x0_gpio_store); 190 + static DEVICE_ATTR(gpio9, S_IWUSR|S_IRUGO, 191 + tle62x0_gpio_show, tle62x0_gpio_store); 192 + static DEVICE_ATTR(gpio10, S_IWUSR|S_IRUGO, 193 + tle62x0_gpio_show, tle62x0_gpio_store); 194 + static DEVICE_ATTR(gpio11, S_IWUSR|S_IRUGO, 195 + tle62x0_gpio_show, tle62x0_gpio_store); 196 + static DEVICE_ATTR(gpio12, S_IWUSR|S_IRUGO, 197 + tle62x0_gpio_show, tle62x0_gpio_store); 198 + static DEVICE_ATTR(gpio13, S_IWUSR|S_IRUGO, 199 + tle62x0_gpio_show, tle62x0_gpio_store); 200 + static DEVICE_ATTR(gpio14, S_IWUSR|S_IRUGO, 201 + tle62x0_gpio_show, tle62x0_gpio_store); 202 + static DEVICE_ATTR(gpio15, S_IWUSR|S_IRUGO, 203 + tle62x0_gpio_show, tle62x0_gpio_store); 204 + static DEVICE_ATTR(gpio16, S_IWUSR|S_IRUGO, 205 + tle62x0_gpio_show, tle62x0_gpio_store); 206 + 207 + static 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 + 226 + static 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 + 238 + static 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 + 292 + static 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 + 304 + static 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 + 313 + static __init int tle62x0_init(void) 314 + { 315 + return spi_register_driver(&tle62x0_driver); 316 + } 317 + 318 + static __exit void tle62x0_exit(void) 319 + { 320 + spi_unregister_driver(&tle62x0_driver); 321 + } 322 + 323 + module_init(tle62x0_init); 324 + module_exit(tle62x0_exit); 325 + 326 + MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 327 + MODULE_DESCRIPTION("TLE62x0 SPI driver"); 328 + MODULE_LICENSE("GPL v2");
+24
include/linux/spi/tle62x0.h
··· 1 + /* 2 + * tle62x0.h - platform glue to Infineon TLE62x0 driver chips 3 + * 4 + * Copyright 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 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program; if not, write to the Free Software 18 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 + */ 20 + 21 + struct tle62x0_pdata { 22 + unsigned int init_state; 23 + unsigned int gpio_count; 24 + };