1 /* 2 * leds-netxbig.c - Driver for the 2Big and 5Big Network series LEDs 3 * 4 * Copyright (C) 2010 LaCie 5 * 6 * Author: Simon Guinot <sguinot@lacie.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 #include <linux/module.h> 24 #include <linux/irq.h> 25 #include <linux/slab.h> 26 #include <linux/spinlock.h> 27 #include <linux/platform_device.h> 28 #include <linux/gpio.h> 29 #include <linux/leds.h> 30 #include <linux/platform_data/leds-kirkwood-netxbig.h> 31 32 /* 33 * GPIO extension bus. 34 */ 35 36 static DEFINE_SPINLOCK(gpio_ext_lock); 37 38 static void gpio_ext_set_addr(struct netxbig_gpio_ext *gpio_ext, int addr) 39 { 40 int pin; 41 42 for (pin = 0; pin < gpio_ext->num_addr; pin++) 43 gpio_set_value(gpio_ext->addr[pin], (addr >> pin) & 1); 44 } 45 46 static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data) 47 { 48 int pin; 49 50 for (pin = 0; pin < gpio_ext->num_data; pin++) 51 gpio_set_value(gpio_ext->data[pin], (data >> pin) & 1); 52 } 53 54 static void gpio_ext_enable_select(struct netxbig_gpio_ext *gpio_ext) 55 { 56 /* Enable select is done on the raising edge. */ 57 gpio_set_value(gpio_ext->enable, 0); 58 gpio_set_value(gpio_ext->enable, 1); 59 } 60 61 static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext, 62 int addr, int value) 63 { 64 unsigned long flags; 65 66 spin_lock_irqsave(&gpio_ext_lock, flags); 67 gpio_ext_set_addr(gpio_ext, addr); 68 gpio_ext_set_data(gpio_ext, value); 69 gpio_ext_enable_select(gpio_ext); 70 spin_unlock_irqrestore(&gpio_ext_lock, flags); 71 } 72 73 static int gpio_ext_init(struct netxbig_gpio_ext *gpio_ext) 74 { 75 int err; 76 int i; 77 78 if (unlikely(!gpio_ext)) 79 return -EINVAL; 80 81 /* Configure address GPIOs. */ 82 for (i = 0; i < gpio_ext->num_addr; i++) { 83 err = gpio_request_one(gpio_ext->addr[i], GPIOF_OUT_INIT_LOW, 84 "GPIO extension addr"); 85 if (err) 86 goto err_free_addr; 87 } 88 /* Configure data GPIOs. */ 89 for (i = 0; i < gpio_ext->num_data; i++) { 90 err = gpio_request_one(gpio_ext->data[i], GPIOF_OUT_INIT_LOW, 91 "GPIO extension data"); 92 if (err) 93 goto err_free_data; 94 } 95 /* Configure "enable select" GPIO. */ 96 err = gpio_request_one(gpio_ext->enable, GPIOF_OUT_INIT_LOW, 97 "GPIO extension enable"); 98 if (err) 99 goto err_free_data; 100 101 return 0; 102 103 err_free_data: 104 for (i = i - 1; i >= 0; i--) 105 gpio_free(gpio_ext->data[i]); 106 i = gpio_ext->num_addr; 107 err_free_addr: 108 for (i = i - 1; i >= 0; i--) 109 gpio_free(gpio_ext->addr[i]); 110 111 return err; 112 } 113 114 static void gpio_ext_free(struct netxbig_gpio_ext *gpio_ext) 115 { 116 int i; 117 118 gpio_free(gpio_ext->enable); 119 for (i = gpio_ext->num_addr - 1; i >= 0; i--) 120 gpio_free(gpio_ext->addr[i]); 121 for (i = gpio_ext->num_data - 1; i >= 0; i--) 122 gpio_free(gpio_ext->data[i]); 123 } 124 125 /* 126 * Class LED driver. 127 */ 128 129 struct netxbig_led_data { 130 struct netxbig_gpio_ext *gpio_ext; 131 struct led_classdev cdev; 132 int mode_addr; 133 int *mode_val; 134 int bright_addr; 135 int bright_max; 136 struct netxbig_led_timer *timer; 137 int num_timer; 138 enum netxbig_led_mode mode; 139 int sata; 140 spinlock_t lock; 141 }; 142 143 static int netxbig_led_get_timer_mode(enum netxbig_led_mode *mode, 144 unsigned long delay_on, 145 unsigned long delay_off, 146 struct netxbig_led_timer *timer, 147 int num_timer) 148 { 149 int i; 150 151 for (i = 0; i < num_timer; i++) { 152 if (timer[i].delay_on == delay_on && 153 timer[i].delay_off == delay_off) { 154 *mode = timer[i].mode; 155 return 0; 156 } 157 } 158 return -EINVAL; 159 } 160 161 static int netxbig_led_blink_set(struct led_classdev *led_cdev, 162 unsigned long *delay_on, 163 unsigned long *delay_off) 164 { 165 struct netxbig_led_data *led_dat = 166 container_of(led_cdev, struct netxbig_led_data, cdev); 167 enum netxbig_led_mode mode; 168 int mode_val; 169 int ret; 170 171 /* Look for a LED mode with the requested timer frequency. */ 172 ret = netxbig_led_get_timer_mode(&mode, *delay_on, *delay_off, 173 led_dat->timer, led_dat->num_timer); 174 if (ret < 0) 175 return ret; 176 177 mode_val = led_dat->mode_val[mode]; 178 if (mode_val == NETXBIG_LED_INVALID_MODE) 179 return -EINVAL; 180 181 spin_lock_irq(&led_dat->lock); 182 183 gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); 184 led_dat->mode = mode; 185 186 spin_unlock_irq(&led_dat->lock); 187 188 return 0; 189 } 190 191 static void netxbig_led_set(struct led_classdev *led_cdev, 192 enum led_brightness value) 193 { 194 struct netxbig_led_data *led_dat = 195 container_of(led_cdev, struct netxbig_led_data, cdev); 196 enum netxbig_led_mode mode; 197 int mode_val, bright_val; 198 int set_brightness = 1; 199 unsigned long flags; 200 201 spin_lock_irqsave(&led_dat->lock, flags); 202 203 if (value == LED_OFF) { 204 mode = NETXBIG_LED_OFF; 205 set_brightness = 0; 206 } else { 207 if (led_dat->sata) 208 mode = NETXBIG_LED_SATA; 209 else if (led_dat->mode == NETXBIG_LED_OFF) 210 mode = NETXBIG_LED_ON; 211 else /* Keep 'timer' mode. */ 212 mode = led_dat->mode; 213 } 214 mode_val = led_dat->mode_val[mode]; 215 216 gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); 217 led_dat->mode = mode; 218 /* 219 * Note that the brightness register is shared between all the 220 * SATA LEDs. So, change the brightness setting for a single 221 * SATA LED will affect all the others. 222 */ 223 if (set_brightness) { 224 bright_val = DIV_ROUND_UP(value * led_dat->bright_max, 225 LED_FULL); 226 gpio_ext_set_value(led_dat->gpio_ext, 227 led_dat->bright_addr, bright_val); 228 } 229 230 spin_unlock_irqrestore(&led_dat->lock, flags); 231 } 232 233 static ssize_t netxbig_led_sata_store(struct device *dev, 234 struct device_attribute *attr, 235 const char *buff, size_t count) 236 { 237 struct led_classdev *led_cdev = dev_get_drvdata(dev); 238 struct netxbig_led_data *led_dat = 239 container_of(led_cdev, struct netxbig_led_data, cdev); 240 unsigned long enable; 241 enum netxbig_led_mode mode; 242 int mode_val; 243 int ret; 244 245 ret = kstrtoul(buff, 10, &enable); 246 if (ret < 0) 247 return ret; 248 249 enable = !!enable; 250 251 spin_lock_irq(&led_dat->lock); 252 253 if (led_dat->sata == enable) { 254 ret = count; 255 goto exit_unlock; 256 } 257 258 if (led_dat->mode != NETXBIG_LED_ON && 259 led_dat->mode != NETXBIG_LED_SATA) 260 mode = led_dat->mode; /* Keep modes 'off' and 'timer'. */ 261 else if (enable) 262 mode = NETXBIG_LED_SATA; 263 else 264 mode = NETXBIG_LED_ON; 265 266 mode_val = led_dat->mode_val[mode]; 267 if (mode_val == NETXBIG_LED_INVALID_MODE) { 268 ret = -EINVAL; 269 goto exit_unlock; 270 } 271 272 gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); 273 led_dat->mode = mode; 274 led_dat->sata = enable; 275 276 ret = count; 277 278 exit_unlock: 279 spin_unlock_irq(&led_dat->lock); 280 281 return ret; 282 } 283 284 static ssize_t netxbig_led_sata_show(struct device *dev, 285 struct device_attribute *attr, char *buf) 286 { 287 struct led_classdev *led_cdev = dev_get_drvdata(dev); 288 struct netxbig_led_data *led_dat = 289 container_of(led_cdev, struct netxbig_led_data, cdev); 290 291 return sprintf(buf, "%d\n", led_dat->sata); 292 } 293 294 static DEVICE_ATTR(sata, 0644, netxbig_led_sata_show, netxbig_led_sata_store); 295 296 static struct attribute *netxbig_led_attrs[] = { 297 &dev_attr_sata.attr, 298 NULL 299 }; 300 ATTRIBUTE_GROUPS(netxbig_led); 301 302 static void delete_netxbig_led(struct netxbig_led_data *led_dat) 303 { 304 led_classdev_unregister(&led_dat->cdev); 305 } 306 307 static int 308 create_netxbig_led(struct platform_device *pdev, 309 struct netxbig_led_data *led_dat, 310 const struct netxbig_led *template) 311 { 312 struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 313 314 spin_lock_init(&led_dat->lock); 315 led_dat->gpio_ext = pdata->gpio_ext; 316 led_dat->cdev.name = template->name; 317 led_dat->cdev.default_trigger = template->default_trigger; 318 led_dat->cdev.blink_set = netxbig_led_blink_set; 319 led_dat->cdev.brightness_set = netxbig_led_set; 320 /* 321 * Because the GPIO extension bus don't allow to read registers 322 * value, there is no way to probe the LED initial state. 323 * So, the initial sysfs LED value for the "brightness" and "sata" 324 * attributes are inconsistent. 325 * 326 * Note that the initial LED state can't be reconfigured. 327 * The reason is that the LED behaviour must stay uniform during 328 * the whole boot process (bootloader+linux). 329 */ 330 led_dat->sata = 0; 331 led_dat->cdev.brightness = LED_OFF; 332 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 333 /* 334 * If available, expose the SATA activity blink capability through 335 * a "sata" sysfs attribute. 336 */ 337 if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) 338 led_dat->cdev.groups = netxbig_led_groups; 339 led_dat->mode_addr = template->mode_addr; 340 led_dat->mode_val = template->mode_val; 341 led_dat->bright_addr = template->bright_addr; 342 led_dat->bright_max = (1 << pdata->gpio_ext->num_data) - 1; 343 led_dat->timer = pdata->timer; 344 led_dat->num_timer = pdata->num_timer; 345 346 return led_classdev_register(&pdev->dev, &led_dat->cdev); 347 } 348 349 static int netxbig_led_probe(struct platform_device *pdev) 350 { 351 struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 352 struct netxbig_led_data *leds_data; 353 int i; 354 int ret; 355 356 if (!pdata) 357 return -EINVAL; 358 359 leds_data = devm_kzalloc(&pdev->dev, 360 sizeof(struct netxbig_led_data) * pdata->num_leds, GFP_KERNEL); 361 if (!leds_data) 362 return -ENOMEM; 363 364 ret = gpio_ext_init(pdata->gpio_ext); 365 if (ret < 0) 366 return ret; 367 368 for (i = 0; i < pdata->num_leds; i++) { 369 ret = create_netxbig_led(pdev, &leds_data[i], &pdata->leds[i]); 370 if (ret < 0) 371 goto err_free_leds; 372 } 373 374 platform_set_drvdata(pdev, leds_data); 375 376 return 0; 377 378 err_free_leds: 379 for (i = i - 1; i >= 0; i--) 380 delete_netxbig_led(&leds_data[i]); 381 382 gpio_ext_free(pdata->gpio_ext); 383 return ret; 384 } 385 386 static int netxbig_led_remove(struct platform_device *pdev) 387 { 388 struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 389 struct netxbig_led_data *leds_data; 390 int i; 391 392 leds_data = platform_get_drvdata(pdev); 393 394 for (i = 0; i < pdata->num_leds; i++) 395 delete_netxbig_led(&leds_data[i]); 396 397 gpio_ext_free(pdata->gpio_ext); 398 399 return 0; 400 } 401 402 static struct platform_driver netxbig_led_driver = { 403 .probe = netxbig_led_probe, 404 .remove = netxbig_led_remove, 405 .driver = { 406 .name = "leds-netxbig", 407 .owner = THIS_MODULE, 408 }, 409 }; 410 411 module_platform_driver(netxbig_led_driver); 412 413 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 414 MODULE_DESCRIPTION("LED driver for LaCie xBig Network boards"); 415 MODULE_LICENSE("GPL"); 416 MODULE_ALIAS("platform:leds-netxbig"); 417