1 /* 2 * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED 3 * 4 * Copyright (C) 2010 LaCie 5 * 6 * Author: Simon Guinot <sguinot@lacie.com> 7 * 8 * Based on leds-gpio.c by Raphael Assenat <raph@8d.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25 #include <linux/kernel.h> 26 #include <linux/platform_device.h> 27 #include <linux/slab.h> 28 #include <linux/gpio.h> 29 #include <linux/leds.h> 30 #include <linux/module.h> 31 #include <linux/platform_data/leds-kirkwood-ns2.h> 32 #include <linux/of.h> 33 #include <linux/of_gpio.h> 34 #include "leds.h" 35 36 /* 37 * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED 38 * modes are available: off, on and SATA activity blinking. The LED modes are 39 * controlled through two GPIOs (command and slow): each combination of values 40 * for the command/slow GPIOs corresponds to a LED mode. 41 */ 42 43 struct ns2_led_data { 44 struct led_classdev cdev; 45 unsigned cmd; 46 unsigned slow; 47 bool can_sleep; 48 int mode_index; 49 unsigned char sata; /* True when SATA mode active. */ 50 rwlock_t rw_lock; /* Lock GPIOs. */ 51 struct work_struct work; 52 int num_modes; 53 struct ns2_led_modval *modval; 54 }; 55 56 static void ns2_led_work(struct work_struct *work) 57 { 58 struct ns2_led_data *led_dat = 59 container_of(work, struct ns2_led_data, work); 60 int i = led_dat->mode_index; 61 62 gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level); 63 gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level); 64 } 65 66 static int ns2_led_get_mode(struct ns2_led_data *led_dat, 67 enum ns2_led_modes *mode) 68 { 69 int i; 70 int ret = -EINVAL; 71 int cmd_level; 72 int slow_level; 73 74 cmd_level = gpio_get_value_cansleep(led_dat->cmd); 75 slow_level = gpio_get_value_cansleep(led_dat->slow); 76 77 for (i = 0; i < led_dat->num_modes; i++) { 78 if (cmd_level == led_dat->modval[i].cmd_level && 79 slow_level == led_dat->modval[i].slow_level) { 80 *mode = led_dat->modval[i].mode; 81 ret = 0; 82 break; 83 } 84 } 85 86 return ret; 87 } 88 89 static void ns2_led_set_mode(struct ns2_led_data *led_dat, 90 enum ns2_led_modes mode) 91 { 92 int i; 93 bool found = false; 94 unsigned long flags; 95 96 for (i = 0; i < led_dat->num_modes; i++) 97 if (mode == led_dat->modval[i].mode) { 98 found = true; 99 break; 100 } 101 102 if (!found) 103 return; 104 105 write_lock_irqsave(&led_dat->rw_lock, flags); 106 107 if (!led_dat->can_sleep) { 108 gpio_set_value(led_dat->cmd, 109 led_dat->modval[i].cmd_level); 110 gpio_set_value(led_dat->slow, 111 led_dat->modval[i].slow_level); 112 goto exit_unlock; 113 } 114 115 led_dat->mode_index = i; 116 schedule_work(&led_dat->work); 117 118 exit_unlock: 119 write_unlock_irqrestore(&led_dat->rw_lock, flags); 120 } 121 122 static void ns2_led_set(struct led_classdev *led_cdev, 123 enum led_brightness value) 124 { 125 struct ns2_led_data *led_dat = 126 container_of(led_cdev, struct ns2_led_data, cdev); 127 enum ns2_led_modes mode; 128 129 if (value == LED_OFF) 130 mode = NS_V2_LED_OFF; 131 else if (led_dat->sata) 132 mode = NS_V2_LED_SATA; 133 else 134 mode = NS_V2_LED_ON; 135 136 ns2_led_set_mode(led_dat, mode); 137 } 138 139 static ssize_t ns2_led_sata_store(struct device *dev, 140 struct device_attribute *attr, 141 const char *buff, size_t count) 142 { 143 struct led_classdev *led_cdev = dev_get_drvdata(dev); 144 struct ns2_led_data *led_dat = 145 container_of(led_cdev, struct ns2_led_data, cdev); 146 int ret; 147 unsigned long enable; 148 149 ret = kstrtoul(buff, 10, &enable); 150 if (ret < 0) 151 return ret; 152 153 enable = !!enable; 154 155 if (led_dat->sata == enable) 156 goto exit; 157 158 led_dat->sata = enable; 159 160 if (!led_get_brightness(led_cdev)) 161 goto exit; 162 163 if (enable) 164 ns2_led_set_mode(led_dat, NS_V2_LED_SATA); 165 else 166 ns2_led_set_mode(led_dat, NS_V2_LED_ON); 167 168 exit: 169 return count; 170 } 171 172 static ssize_t ns2_led_sata_show(struct device *dev, 173 struct device_attribute *attr, char *buf) 174 { 175 struct led_classdev *led_cdev = dev_get_drvdata(dev); 176 struct ns2_led_data *led_dat = 177 container_of(led_cdev, struct ns2_led_data, cdev); 178 179 return sprintf(buf, "%d\n", led_dat->sata); 180 } 181 182 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); 183 184 static struct attribute *ns2_led_attrs[] = { 185 &dev_attr_sata.attr, 186 NULL 187 }; 188 ATTRIBUTE_GROUPS(ns2_led); 189 190 static int 191 create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, 192 const struct ns2_led *template) 193 { 194 int ret; 195 enum ns2_led_modes mode; 196 197 ret = devm_gpio_request_one(&pdev->dev, template->cmd, 198 gpio_get_value_cansleep(template->cmd) ? 199 GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 200 template->name); 201 if (ret) { 202 dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", 203 template->name); 204 return ret; 205 } 206 207 ret = devm_gpio_request_one(&pdev->dev, template->slow, 208 gpio_get_value_cansleep(template->slow) ? 209 GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 210 template->name); 211 if (ret) { 212 dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", 213 template->name); 214 return ret; 215 } 216 217 rwlock_init(&led_dat->rw_lock); 218 219 led_dat->cdev.name = template->name; 220 led_dat->cdev.default_trigger = template->default_trigger; 221 led_dat->cdev.blink_set = NULL; 222 led_dat->cdev.brightness_set = ns2_led_set; 223 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 224 led_dat->cdev.groups = ns2_led_groups; 225 led_dat->cmd = template->cmd; 226 led_dat->slow = template->slow; 227 led_dat->can_sleep = gpio_cansleep(led_dat->cmd) | 228 gpio_cansleep(led_dat->slow); 229 led_dat->modval = template->modval; 230 led_dat->num_modes = template->num_modes; 231 232 ret = ns2_led_get_mode(led_dat, &mode); 233 if (ret < 0) 234 return ret; 235 236 /* Set LED initial state. */ 237 led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; 238 led_dat->cdev.brightness = 239 (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; 240 241 INIT_WORK(&led_dat->work, ns2_led_work); 242 243 ret = led_classdev_register(&pdev->dev, &led_dat->cdev); 244 if (ret < 0) 245 return ret; 246 247 return 0; 248 } 249 250 static void delete_ns2_led(struct ns2_led_data *led_dat) 251 { 252 led_classdev_unregister(&led_dat->cdev); 253 cancel_work_sync(&led_dat->work); 254 } 255 256 #ifdef CONFIG_OF_GPIO 257 /* 258 * Translate OpenFirmware node properties into platform_data. 259 */ 260 static int 261 ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) 262 { 263 struct device_node *np = dev->of_node; 264 struct device_node *child; 265 struct ns2_led *led, *leds; 266 int num_leds = 0; 267 268 num_leds = of_get_child_count(np); 269 if (!num_leds) 270 return -ENODEV; 271 272 leds = devm_kzalloc(dev, num_leds * sizeof(struct ns2_led), 273 GFP_KERNEL); 274 if (!leds) 275 return -ENOMEM; 276 277 led = leds; 278 for_each_child_of_node(np, child) { 279 const char *string; 280 int ret, i, num_modes; 281 struct ns2_led_modval *modval; 282 283 ret = of_get_named_gpio(child, "cmd-gpio", 0); 284 if (ret < 0) 285 return ret; 286 led->cmd = ret; 287 ret = of_get_named_gpio(child, "slow-gpio", 0); 288 if (ret < 0) 289 return ret; 290 led->slow = ret; 291 ret = of_property_read_string(child, "label", &string); 292 led->name = (ret == 0) ? string : child->name; 293 ret = of_property_read_string(child, "linux,default-trigger", 294 &string); 295 if (ret == 0) 296 led->default_trigger = string; 297 298 ret = of_property_count_u32_elems(child, "modes-map"); 299 if (ret < 0 || ret % 3) { 300 dev_err(dev, 301 "Missing or malformed modes-map property\n"); 302 return -EINVAL; 303 } 304 305 num_modes = ret / 3; 306 modval = devm_kzalloc(dev, 307 num_modes * sizeof(struct ns2_led_modval), 308 GFP_KERNEL); 309 if (!modval) 310 return -ENOMEM; 311 312 for (i = 0; i < num_modes; i++) { 313 of_property_read_u32_index(child, 314 "modes-map", 3 * i, 315 (u32 *) &modval[i].mode); 316 of_property_read_u32_index(child, 317 "modes-map", 3 * i + 1, 318 (u32 *) &modval[i].cmd_level); 319 of_property_read_u32_index(child, 320 "modes-map", 3 * i + 2, 321 (u32 *) &modval[i].slow_level); 322 } 323 324 led->num_modes = num_modes; 325 led->modval = modval; 326 327 led++; 328 } 329 330 pdata->leds = leds; 331 pdata->num_leds = num_leds; 332 333 return 0; 334 } 335 336 static const struct of_device_id of_ns2_leds_match[] = { 337 { .compatible = "lacie,ns2-leds", }, 338 {}, 339 }; 340 #endif /* CONFIG_OF_GPIO */ 341 342 struct ns2_led_priv { 343 int num_leds; 344 struct ns2_led_data leds_data[]; 345 }; 346 347 static inline int sizeof_ns2_led_priv(int num_leds) 348 { 349 return sizeof(struct ns2_led_priv) + 350 (sizeof(struct ns2_led_data) * num_leds); 351 } 352 353 static int ns2_led_probe(struct platform_device *pdev) 354 { 355 struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 356 struct ns2_led_priv *priv; 357 int i; 358 int ret; 359 360 #ifdef CONFIG_OF_GPIO 361 if (!pdata) { 362 pdata = devm_kzalloc(&pdev->dev, 363 sizeof(struct ns2_led_platform_data), 364 GFP_KERNEL); 365 if (!pdata) 366 return -ENOMEM; 367 368 ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); 369 if (ret) 370 return ret; 371 } 372 #else 373 if (!pdata) 374 return -EINVAL; 375 #endif /* CONFIG_OF_GPIO */ 376 377 priv = devm_kzalloc(&pdev->dev, 378 sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL); 379 if (!priv) 380 return -ENOMEM; 381 priv->num_leds = pdata->num_leds; 382 383 for (i = 0; i < priv->num_leds; i++) { 384 ret = create_ns2_led(pdev, &priv->leds_data[i], 385 &pdata->leds[i]); 386 if (ret < 0) { 387 for (i = i - 1; i >= 0; i--) 388 delete_ns2_led(&priv->leds_data[i]); 389 return ret; 390 } 391 } 392 393 platform_set_drvdata(pdev, priv); 394 395 return 0; 396 } 397 398 static int ns2_led_remove(struct platform_device *pdev) 399 { 400 int i; 401 struct ns2_led_priv *priv; 402 403 priv = platform_get_drvdata(pdev); 404 405 for (i = 0; i < priv->num_leds; i++) 406 delete_ns2_led(&priv->leds_data[i]); 407 408 return 0; 409 } 410 411 static struct platform_driver ns2_led_driver = { 412 .probe = ns2_led_probe, 413 .remove = ns2_led_remove, 414 .driver = { 415 .name = "leds-ns2", 416 .of_match_table = of_match_ptr(of_ns2_leds_match), 417 }, 418 }; 419 420 module_platform_driver(ns2_led_driver); 421 422 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 423 MODULE_DESCRIPTION("Network Space v2 LED driver"); 424 MODULE_LICENSE("GPL"); 425 MODULE_ALIAS("platform:leds-ns2"); 426