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 int cmd; 46 unsigned int slow; 47 bool can_sleep; 48 unsigned char sata; /* True when SATA mode active. */ 49 rwlock_t rw_lock; /* Lock GPIOs. */ 50 int num_modes; 51 struct ns2_led_modval *modval; 52 }; 53 54 static int ns2_led_get_mode(struct ns2_led_data *led_dat, 55 enum ns2_led_modes *mode) 56 { 57 int i; 58 int ret = -EINVAL; 59 int cmd_level; 60 int slow_level; 61 62 cmd_level = gpio_get_value_cansleep(led_dat->cmd); 63 slow_level = gpio_get_value_cansleep(led_dat->slow); 64 65 for (i = 0; i < led_dat->num_modes; i++) { 66 if (cmd_level == led_dat->modval[i].cmd_level && 67 slow_level == led_dat->modval[i].slow_level) { 68 *mode = led_dat->modval[i].mode; 69 ret = 0; 70 break; 71 } 72 } 73 74 return ret; 75 } 76 77 static void ns2_led_set_mode(struct ns2_led_data *led_dat, 78 enum ns2_led_modes mode) 79 { 80 int i; 81 bool found = false; 82 unsigned long flags; 83 84 for (i = 0; i < led_dat->num_modes; i++) 85 if (mode == led_dat->modval[i].mode) { 86 found = true; 87 break; 88 } 89 90 if (!found) 91 return; 92 93 write_lock_irqsave(&led_dat->rw_lock, flags); 94 95 if (!led_dat->can_sleep) { 96 gpio_set_value(led_dat->cmd, 97 led_dat->modval[i].cmd_level); 98 gpio_set_value(led_dat->slow, 99 led_dat->modval[i].slow_level); 100 goto exit_unlock; 101 } 102 103 gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level); 104 gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level); 105 106 exit_unlock: 107 write_unlock_irqrestore(&led_dat->rw_lock, flags); 108 } 109 110 static void ns2_led_set(struct led_classdev *led_cdev, 111 enum led_brightness value) 112 { 113 struct ns2_led_data *led_dat = 114 container_of(led_cdev, struct ns2_led_data, cdev); 115 enum ns2_led_modes mode; 116 117 if (value == LED_OFF) 118 mode = NS_V2_LED_OFF; 119 else if (led_dat->sata) 120 mode = NS_V2_LED_SATA; 121 else 122 mode = NS_V2_LED_ON; 123 124 ns2_led_set_mode(led_dat, mode); 125 } 126 127 static int ns2_led_set_blocking(struct led_classdev *led_cdev, 128 enum led_brightness value) 129 { 130 ns2_led_set(led_cdev, value); 131 return 0; 132 } 133 134 static ssize_t ns2_led_sata_store(struct device *dev, 135 struct device_attribute *attr, 136 const char *buff, size_t count) 137 { 138 struct led_classdev *led_cdev = dev_get_drvdata(dev); 139 struct ns2_led_data *led_dat = 140 container_of(led_cdev, struct ns2_led_data, cdev); 141 int ret; 142 unsigned long enable; 143 144 ret = kstrtoul(buff, 10, &enable); 145 if (ret < 0) 146 return ret; 147 148 enable = !!enable; 149 150 if (led_dat->sata == enable) 151 goto exit; 152 153 led_dat->sata = enable; 154 155 if (!led_get_brightness(led_cdev)) 156 goto exit; 157 158 if (enable) 159 ns2_led_set_mode(led_dat, NS_V2_LED_SATA); 160 else 161 ns2_led_set_mode(led_dat, NS_V2_LED_ON); 162 163 exit: 164 return count; 165 } 166 167 static ssize_t ns2_led_sata_show(struct device *dev, 168 struct device_attribute *attr, char *buf) 169 { 170 struct led_classdev *led_cdev = dev_get_drvdata(dev); 171 struct ns2_led_data *led_dat = 172 container_of(led_cdev, struct ns2_led_data, cdev); 173 174 return sprintf(buf, "%d\n", led_dat->sata); 175 } 176 177 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); 178 179 static struct attribute *ns2_led_attrs[] = { 180 &dev_attr_sata.attr, 181 NULL 182 }; 183 ATTRIBUTE_GROUPS(ns2_led); 184 185 static int 186 create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, 187 const struct ns2_led *template) 188 { 189 int ret; 190 enum ns2_led_modes mode; 191 192 ret = devm_gpio_request_one(&pdev->dev, template->cmd, 193 gpio_get_value_cansleep(template->cmd) ? 194 GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 195 template->name); 196 if (ret) { 197 dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", 198 template->name); 199 return ret; 200 } 201 202 ret = devm_gpio_request_one(&pdev->dev, template->slow, 203 gpio_get_value_cansleep(template->slow) ? 204 GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 205 template->name); 206 if (ret) { 207 dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", 208 template->name); 209 return ret; 210 } 211 212 rwlock_init(&led_dat->rw_lock); 213 214 led_dat->cdev.name = template->name; 215 led_dat->cdev.default_trigger = template->default_trigger; 216 led_dat->cdev.blink_set = NULL; 217 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 218 led_dat->cdev.groups = ns2_led_groups; 219 led_dat->cmd = template->cmd; 220 led_dat->slow = template->slow; 221 led_dat->can_sleep = gpio_cansleep(led_dat->cmd) | 222 gpio_cansleep(led_dat->slow); 223 if (led_dat->can_sleep) 224 led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking; 225 else 226 led_dat->cdev.brightness_set = ns2_led_set; 227 led_dat->modval = template->modval; 228 led_dat->num_modes = template->num_modes; 229 230 ret = ns2_led_get_mode(led_dat, &mode); 231 if (ret < 0) 232 return ret; 233 234 /* Set LED initial state. */ 235 led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; 236 led_dat->cdev.brightness = 237 (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; 238 239 ret = led_classdev_register(&pdev->dev, &led_dat->cdev); 240 if (ret < 0) 241 return ret; 242 243 return 0; 244 } 245 246 static void delete_ns2_led(struct ns2_led_data *led_dat) 247 { 248 led_classdev_unregister(&led_dat->cdev); 249 } 250 251 #ifdef CONFIG_OF_GPIO 252 /* 253 * Translate OpenFirmware node properties into platform_data. 254 */ 255 static int 256 ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) 257 { 258 struct device_node *np = dev->of_node; 259 struct device_node *child; 260 struct ns2_led *led, *leds; 261 int num_leds = 0; 262 263 num_leds = of_get_child_count(np); 264 if (!num_leds) 265 return -ENODEV; 266 267 leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led), 268 GFP_KERNEL); 269 if (!leds) 270 return -ENOMEM; 271 272 led = leds; 273 for_each_child_of_node(np, child) { 274 const char *string; 275 int ret, i, num_modes; 276 struct ns2_led_modval *modval; 277 278 ret = of_get_named_gpio(child, "cmd-gpio", 0); 279 if (ret < 0) 280 return ret; 281 led->cmd = ret; 282 ret = of_get_named_gpio(child, "slow-gpio", 0); 283 if (ret < 0) 284 return ret; 285 led->slow = ret; 286 ret = of_property_read_string(child, "label", &string); 287 led->name = (ret == 0) ? string : child->name; 288 ret = of_property_read_string(child, "linux,default-trigger", 289 &string); 290 if (ret == 0) 291 led->default_trigger = string; 292 293 ret = of_property_count_u32_elems(child, "modes-map"); 294 if (ret < 0 || ret % 3) { 295 dev_err(dev, 296 "Missing or malformed modes-map property\n"); 297 return -EINVAL; 298 } 299 300 num_modes = ret / 3; 301 modval = devm_kcalloc(dev, 302 num_modes, 303 sizeof(struct ns2_led_modval), 304 GFP_KERNEL); 305 if (!modval) 306 return -ENOMEM; 307 308 for (i = 0; i < num_modes; i++) { 309 of_property_read_u32_index(child, 310 "modes-map", 3 * i, 311 (u32 *) &modval[i].mode); 312 of_property_read_u32_index(child, 313 "modes-map", 3 * i + 1, 314 (u32 *) &modval[i].cmd_level); 315 of_property_read_u32_index(child, 316 "modes-map", 3 * i + 2, 317 (u32 *) &modval[i].slow_level); 318 } 319 320 led->num_modes = num_modes; 321 led->modval = modval; 322 323 led++; 324 } 325 326 pdata->leds = leds; 327 pdata->num_leds = num_leds; 328 329 return 0; 330 } 331 332 static const struct of_device_id of_ns2_leds_match[] = { 333 { .compatible = "lacie,ns2-leds", }, 334 {}, 335 }; 336 MODULE_DEVICE_TABLE(of, of_ns2_leds_match); 337 #endif /* CONFIG_OF_GPIO */ 338 339 struct ns2_led_priv { 340 int num_leds; 341 struct ns2_led_data leds_data[]; 342 }; 343 344 static inline int sizeof_ns2_led_priv(int num_leds) 345 { 346 return sizeof(struct ns2_led_priv) + 347 (sizeof(struct ns2_led_data) * num_leds); 348 } 349 350 static int ns2_led_probe(struct platform_device *pdev) 351 { 352 struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 353 struct ns2_led_priv *priv; 354 int i; 355 int ret; 356 357 #ifdef CONFIG_OF_GPIO 358 if (!pdata) { 359 pdata = devm_kzalloc(&pdev->dev, 360 sizeof(struct ns2_led_platform_data), 361 GFP_KERNEL); 362 if (!pdata) 363 return -ENOMEM; 364 365 ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); 366 if (ret) 367 return ret; 368 } 369 #else 370 if (!pdata) 371 return -EINVAL; 372 #endif /* CONFIG_OF_GPIO */ 373 374 priv = devm_kzalloc(&pdev->dev, 375 sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL); 376 if (!priv) 377 return -ENOMEM; 378 priv->num_leds = pdata->num_leds; 379 380 for (i = 0; i < priv->num_leds; i++) { 381 ret = create_ns2_led(pdev, &priv->leds_data[i], 382 &pdata->leds[i]); 383 if (ret < 0) { 384 for (i = i - 1; i >= 0; i--) 385 delete_ns2_led(&priv->leds_data[i]); 386 return ret; 387 } 388 } 389 390 platform_set_drvdata(pdev, priv); 391 392 return 0; 393 } 394 395 static int ns2_led_remove(struct platform_device *pdev) 396 { 397 int i; 398 struct ns2_led_priv *priv; 399 400 priv = platform_get_drvdata(pdev); 401 402 for (i = 0; i < priv->num_leds; i++) 403 delete_ns2_led(&priv->leds_data[i]); 404 405 return 0; 406 } 407 408 static struct platform_driver ns2_led_driver = { 409 .probe = ns2_led_probe, 410 .remove = ns2_led_remove, 411 .driver = { 412 .name = "leds-ns2", 413 .of_match_table = of_match_ptr(of_ns2_leds_match), 414 }, 415 }; 416 417 module_platform_driver(ns2_led_driver); 418 419 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 420 MODULE_DESCRIPTION("Network Space v2 LED driver"); 421 MODULE_LICENSE("GPL"); 422 MODULE_ALIAS("platform:leds-ns2"); 423