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

leds: netxbig: add device tree binding

This patch adds device tree support for the netxbig LEDs.

This also introduces a additionnal DT binding for the GPIO extension bus
(netxbig-gpio-ext) used to configure the LEDs. Since this bus could also
be used to control other devices, then it seems more suitable to have it
in a separate DT binding.

Signed-off-by: Simon Guinot <simon.guinot@sequanux.org>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>

authored by

Simon Guinot and committed by
Jacek Anaszewski
2976b179 1c7b9d0e

+391 -22
+22
Documentation/devicetree/bindings/gpio/netxbig-gpio-ext.txt
··· 1 + Binding for the GPIO extension bus found on some LaCie/Seagate boards 2 + (Example: 2Big/5Big Network v2, 2Big NAS). 3 + 4 + Required properties: 5 + - compatible: "lacie,netxbig-gpio-ext". 6 + - addr-gpios: GPIOs representing the address register (LSB -> MSB). 7 + - data-gpios: GPIOs representing the data register (LSB -> MSB). 8 + - enable-gpio: latches the new configuration (address, data) on raising edge. 9 + 10 + Example: 11 + 12 + netxbig_gpio_ext: netxbig-gpio-ext { 13 + compatible = "lacie,netxbig-gpio-ext"; 14 + 15 + addr-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH 16 + &gpio1 16 GPIO_ACTIVE_HIGH 17 + &gpio1 17 GPIO_ACTIVE_HIGH>; 18 + data-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH 19 + &gpio1 13 GPIO_ACTIVE_HIGH 20 + &gpio1 14 GPIO_ACTIVE_HIGH>; 21 + enable-gpio = <&gpio0 29 GPIO_ACTIVE_HIGH>; 22 + };
+92
Documentation/devicetree/bindings/leds/leds-netxbig.txt
··· 1 + Binding for the CPLD LEDs (GPIO extension bus) found on some LaCie/Seagate 2 + boards (Example: 2Big/5Big Network v2, 2Big NAS). 3 + 4 + Required properties: 5 + - compatible: "lacie,netxbig-leds". 6 + - gpio-ext: Phandle for the gpio-ext bus. 7 + 8 + Optional properties: 9 + - timers: Timer array. Each timer entry is represented by three integers: 10 + Mode (gpio-ext bus), delay_on and delay_off. 11 + 12 + Each LED is represented as a sub-node of the netxbig-leds device. 13 + 14 + Required sub-node properties: 15 + - mode-addr: Mode register address on gpio-ext bus. 16 + - mode-val: Mode to value mapping. Each entry is represented by two integers: 17 + A mode and the corresponding value on the gpio-ext bus. 18 + - bright-addr: Brightness register address on gpio-ext bus. 19 + - max-brightness: Maximum brightness value. 20 + 21 + Optional sub-node properties: 22 + - label: Name for this LED. If omitted, the label is taken from the node name. 23 + - linux,default-trigger: Trigger assigned to the LED. 24 + 25 + Example: 26 + 27 + netxbig-leds { 28 + compatible = "lacie,netxbig-leds"; 29 + 30 + gpio-ext = &gpio_ext; 31 + 32 + timers = <NETXBIG_LED_TIMER1 500 500 33 + NETXBIG_LED_TIMER2 500 1000>; 34 + 35 + blue-power { 36 + label = "netxbig:blue:power"; 37 + mode-addr = <0>; 38 + mode-val = <NETXBIG_LED_OFF 0 39 + NETXBIG_LED_ON 1 40 + NETXBIG_LED_TIMER1 3 41 + NETXBIG_LED_TIMER2 7>; 42 + bright-addr = <1>; 43 + max-brightness = <7>; 44 + }; 45 + red-power { 46 + label = "netxbig:red:power"; 47 + mode-addr = <0>; 48 + mode-val = <NETXBIG_LED_OFF 0 49 + NETXBIG_LED_ON 2 50 + NETXBIG_LED_TIMER1 4>; 51 + bright-addr = <1>; 52 + max-brightness = <7>; 53 + }; 54 + blue-sata0 { 55 + label = "netxbig:blue:sata0"; 56 + mode-addr = <3>; 57 + mode-val = <NETXBIG_LED_OFF 0 58 + NETXBIG_LED_ON 7 59 + NETXBIG_LED_SATA 1 60 + NETXBIG_LED_TIMER1 3>; 61 + bright-addr = <2>; 62 + max-brightness = <7>; 63 + }; 64 + red-sata0 { 65 + label = "netxbig:red:sata0"; 66 + mode-addr = <3>; 67 + mode-val = <NETXBIG_LED_OFF 0 68 + NETXBIG_LED_ON 2 69 + NETXBIG_LED_TIMER1 4>; 70 + bright-addr = <2>; 71 + max-brightness = <7>; 72 + }; 73 + blue-sata1 { 74 + label = "netxbig:blue:sata1"; 75 + mode-addr = <4>; 76 + mode-val = <NETXBIG_LED_OFF 0 77 + NETXBIG_LED_ON 7 78 + NETXBIG_LED_SATA 1 79 + NETXBIG_LED_TIMER1 3>; 80 + bright-addr = <2>; 81 + max-brightness = <7>; 82 + }; 83 + red-sata1 { 84 + label = "netxbig:red:sata1"; 85 + mode-addr = <4>; 86 + mode-val = <NETXBIG_LED_OFF 0 87 + NETXBIG_LED_ON 2 88 + NETXBIG_LED_TIMER1 4>; 89 + bright-addr = <2>; 90 + max-brightness = <7>; 91 + }; 92 + };
+258 -22
drivers/leds/leds-netxbig.c
··· 26 26 #include <linux/spinlock.h> 27 27 #include <linux/platform_device.h> 28 28 #include <linux/gpio.h> 29 + #include <linux/of_gpio.h> 29 30 #include <linux/leds.h> 30 31 #include <linux/platform_data/leds-kirkwood-netxbig.h> 31 32 ··· 139 138 enum netxbig_led_mode mode; 140 139 int sata; 141 140 spinlock_t lock; 141 + }; 142 + 143 + struct netxbig_led_priv { 144 + struct netxbig_led_platform_data *pdata; 145 + struct netxbig_led_data leds_data[]; 142 146 }; 143 147 144 148 static int netxbig_led_get_timer_mode(enum netxbig_led_mode *mode, ··· 310 304 led_classdev_unregister(&led_dat->cdev); 311 305 } 312 306 313 - static int 314 - create_netxbig_led(struct platform_device *pdev, 315 - struct netxbig_led_data *led_dat, 316 - const struct netxbig_led *template) 307 + static int create_netxbig_led(struct platform_device *pdev, int led, 308 + struct netxbig_led_priv *priv) 317 309 { 318 - struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 310 + struct netxbig_led_platform_data *pdata = priv->pdata; 311 + struct netxbig_led_data *led_dat = &priv->leds_data[led]; 312 + const struct netxbig_led *template = &priv->pdata->leds[led]; 319 313 320 314 spin_lock_init(&led_dat->lock); 321 315 led_dat->gpio_ext = pdata->gpio_ext; ··· 339 333 led_dat->mode_addr = template->mode_addr; 340 334 led_dat->mode_val = template->mode_val; 341 335 led_dat->bright_addr = template->bright_addr; 342 - led_dat->bright_max = (1 << pdata->gpio_ext->num_data) - 1; 336 + led_dat->bright_max = template->bright_max; 343 337 led_dat->timer = pdata->timer; 344 338 led_dat->num_timer = pdata->num_timer; 345 339 /* ··· 352 346 return led_classdev_register(&pdev->dev, &led_dat->cdev); 353 347 } 354 348 349 + #ifdef CONFIG_OF_GPIO 350 + static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np, 351 + struct netxbig_gpio_ext *gpio_ext) 352 + { 353 + int *addr, *data; 354 + int num_addr, num_data; 355 + int ret; 356 + int i; 357 + 358 + ret = of_gpio_named_count(np, "addr-gpios"); 359 + if (ret < 0) { 360 + dev_err(dev, 361 + "Failed to count GPIOs in DT property addr-gpios\n"); 362 + return ret; 363 + } 364 + num_addr = ret; 365 + addr = devm_kzalloc(dev, num_addr * sizeof(*addr), GFP_KERNEL); 366 + if (!addr) 367 + return -ENOMEM; 368 + 369 + for (i = 0; i < num_addr; i++) { 370 + ret = of_get_named_gpio(np, "addr-gpios", i); 371 + if (ret < 0) 372 + return ret; 373 + addr[i] = ret; 374 + } 375 + gpio_ext->addr = addr; 376 + gpio_ext->num_addr = num_addr; 377 + 378 + ret = of_gpio_named_count(np, "data-gpios"); 379 + if (ret < 0) { 380 + dev_err(dev, 381 + "Failed to count GPIOs in DT property data-gpios\n"); 382 + return ret; 383 + } 384 + num_data = ret; 385 + data = devm_kzalloc(dev, num_data * sizeof(*data), GFP_KERNEL); 386 + if (!data) 387 + return -ENOMEM; 388 + 389 + for (i = 0; i < num_data; i++) { 390 + ret = of_get_named_gpio(np, "data-gpios", i); 391 + if (ret < 0) 392 + return ret; 393 + data[i] = ret; 394 + } 395 + gpio_ext->data = data; 396 + gpio_ext->num_data = num_data; 397 + 398 + ret = of_get_named_gpio(np, "enable-gpio", 0); 399 + if (ret < 0) { 400 + dev_err(dev, 401 + "Failed to get GPIO from DT property enable-gpio\n"); 402 + return ret; 403 + } 404 + gpio_ext->enable = ret; 405 + 406 + return 0; 407 + } 408 + 409 + static int netxbig_leds_get_of_pdata(struct device *dev, 410 + struct netxbig_led_platform_data *pdata) 411 + { 412 + struct device_node *np = dev->of_node; 413 + struct device_node *gpio_ext_np; 414 + struct device_node *child; 415 + struct netxbig_gpio_ext *gpio_ext; 416 + struct netxbig_led_timer *timers; 417 + struct netxbig_led *leds, *led; 418 + int num_timers; 419 + int num_leds = 0; 420 + int ret; 421 + int i; 422 + 423 + /* GPIO extension */ 424 + gpio_ext_np = of_parse_phandle(np, "gpio-ext", 0); 425 + if (!gpio_ext_np) { 426 + dev_err(dev, "Failed to get DT handle gpio-ext\n"); 427 + return -EINVAL; 428 + } 429 + 430 + gpio_ext = devm_kzalloc(dev, sizeof(*gpio_ext), GFP_KERNEL); 431 + if (!gpio_ext) 432 + return -ENOMEM; 433 + ret = gpio_ext_get_of_pdata(dev, gpio_ext_np, gpio_ext); 434 + if (ret) 435 + return ret; 436 + of_node_put(gpio_ext_np); 437 + pdata->gpio_ext = gpio_ext; 438 + 439 + /* Timers (optional) */ 440 + ret = of_property_count_u32_elems(np, "timers"); 441 + if (ret > 0) { 442 + if (ret % 3) 443 + return -EINVAL; 444 + num_timers = ret / 3; 445 + timers = devm_kzalloc(dev, num_timers * sizeof(*timers), 446 + GFP_KERNEL); 447 + if (!timers) 448 + return -ENOMEM; 449 + for (i = 0; i < num_timers; i++) { 450 + u32 tmp; 451 + 452 + of_property_read_u32_index(np, "timers", 3 * i, 453 + &timers[i].mode); 454 + if (timers[i].mode >= NETXBIG_LED_MODE_NUM) 455 + return -EINVAL; 456 + of_property_read_u32_index(np, "timers", 457 + 3 * i + 1, &tmp); 458 + timers[i].delay_on = tmp; 459 + of_property_read_u32_index(np, "timers", 460 + 3 * i + 2, &tmp); 461 + timers[i].delay_off = tmp; 462 + } 463 + pdata->timer = timers; 464 + pdata->num_timer = num_timers; 465 + } 466 + 467 + /* LEDs */ 468 + num_leds = of_get_child_count(np); 469 + if (!num_leds) { 470 + dev_err(dev, "No LED subnodes found in DT\n"); 471 + return -ENODEV; 472 + } 473 + 474 + leds = devm_kzalloc(dev, num_leds * sizeof(*leds), GFP_KERNEL); 475 + if (!leds) 476 + return -ENOMEM; 477 + 478 + led = leds; 479 + for_each_child_of_node(np, child) { 480 + const char *string; 481 + int *mode_val; 482 + int num_modes; 483 + 484 + ret = of_property_read_u32(child, "mode-addr", 485 + &led->mode_addr); 486 + if (ret) 487 + goto err_node_put; 488 + 489 + ret = of_property_read_u32(child, "bright-addr", 490 + &led->bright_addr); 491 + if (ret) 492 + goto err_node_put; 493 + 494 + ret = of_property_read_u32(child, "max-brightness", 495 + &led->bright_max); 496 + if (ret) 497 + goto err_node_put; 498 + 499 + mode_val = 500 + devm_kzalloc(dev, 501 + NETXBIG_LED_MODE_NUM * sizeof(*mode_val), 502 + GFP_KERNEL); 503 + if (!mode_val) { 504 + ret = -ENOMEM; 505 + goto err_node_put; 506 + } 507 + 508 + for (i = 0; i < NETXBIG_LED_MODE_NUM; i++) 509 + mode_val[i] = NETXBIG_LED_INVALID_MODE; 510 + 511 + ret = of_property_count_u32_elems(child, "mode-val"); 512 + if (ret < 0 || ret % 2) { 513 + ret = -EINVAL; 514 + goto err_node_put; 515 + } 516 + num_modes = ret / 2; 517 + if (num_modes > NETXBIG_LED_MODE_NUM) { 518 + ret = -EINVAL; 519 + goto err_node_put; 520 + } 521 + 522 + for (i = 0; i < num_modes; i++) { 523 + int mode; 524 + int val; 525 + 526 + of_property_read_u32_index(child, 527 + "mode-val", 2 * i, &mode); 528 + of_property_read_u32_index(child, 529 + "mode-val", 2 * i + 1, &val); 530 + if (mode >= NETXBIG_LED_MODE_NUM) { 531 + ret = -EINVAL; 532 + goto err_node_put; 533 + } 534 + mode_val[mode] = val; 535 + } 536 + led->mode_val = mode_val; 537 + 538 + if (!of_property_read_string(child, "label", &string)) 539 + led->name = string; 540 + else 541 + led->name = child->name; 542 + 543 + if (!of_property_read_string(child, 544 + "linux,default-trigger", &string)) 545 + led->default_trigger = string; 546 + 547 + led++; 548 + } 549 + 550 + pdata->leds = leds; 551 + pdata->num_leds = num_leds; 552 + 553 + return 0; 554 + 555 + err_node_put: 556 + of_node_put(child); 557 + return ret; 558 + } 559 + 560 + static const struct of_device_id of_netxbig_leds_match[] = { 561 + { .compatible = "lacie,netxbig-leds", }, 562 + {}, 563 + }; 564 + #else 565 + static inline int 566 + netxbig_leds_get_of_pdata(struct device *dev, 567 + struct netxbig_led_platform_data *pdata) 568 + { 569 + return -ENODEV; 570 + } 571 + #endif /* CONFIG_OF_GPIO */ 572 + 355 573 static int netxbig_led_probe(struct platform_device *pdev) 356 574 { 357 575 struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 358 - struct netxbig_led_data *leds_data; 576 + struct netxbig_led_priv *priv; 359 577 int i; 360 578 int ret; 361 579 362 - if (!pdata) 363 - return -EINVAL; 580 + if (!pdata) { 581 + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 582 + if (!pdata) 583 + return -ENOMEM; 584 + ret = netxbig_leds_get_of_pdata(&pdev->dev, pdata); 585 + if (ret) 586 + return ret; 587 + } 364 588 365 - leds_data = devm_kzalloc(&pdev->dev, 366 - sizeof(struct netxbig_led_data) * pdata->num_leds, GFP_KERNEL); 367 - if (!leds_data) 589 + priv = devm_kzalloc(&pdev->dev, sizeof(*priv) + 590 + pdata->num_leds * sizeof(struct netxbig_led_data), 591 + GFP_KERNEL); 592 + if (!priv) 368 593 return -ENOMEM; 594 + priv->pdata = pdata; 369 595 370 596 ret = gpio_ext_init(pdata->gpio_ext); 371 597 if (ret < 0) 372 598 return ret; 373 599 374 600 for (i = 0; i < pdata->num_leds; i++) { 375 - ret = create_netxbig_led(pdev, &leds_data[i], &pdata->leds[i]); 601 + ret = create_netxbig_led(pdev, i, priv); 376 602 if (ret < 0) 377 603 goto err_free_leds; 378 604 } 379 - 380 - platform_set_drvdata(pdev, leds_data); 605 + platform_set_drvdata(pdev, priv); 381 606 382 607 return 0; 383 608 384 609 err_free_leds: 385 610 for (i = i - 1; i >= 0; i--) 386 - delete_netxbig_led(&leds_data[i]); 611 + delete_netxbig_led(&priv->leds_data[i]); 387 612 388 613 gpio_ext_free(pdata->gpio_ext); 389 614 return ret; ··· 622 385 623 386 static int netxbig_led_remove(struct platform_device *pdev) 624 387 { 625 - struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 626 - struct netxbig_led_data *leds_data; 388 + struct netxbig_led_priv *priv = platform_get_drvdata(pdev); 389 + struct netxbig_led_platform_data *pdata = priv->pdata; 627 390 int i; 628 391 629 - leds_data = platform_get_drvdata(pdev); 630 - 631 392 for (i = 0; i < pdata->num_leds; i++) 632 - delete_netxbig_led(&leds_data[i]); 393 + delete_netxbig_led(&priv->leds_data[i]); 633 394 634 395 gpio_ext_free(pdata->gpio_ext); 635 396 ··· 638 403 .probe = netxbig_led_probe, 639 404 .remove = netxbig_led_remove, 640 405 .driver = { 641 - .name = "leds-netxbig", 406 + .name = "leds-netxbig", 407 + .of_match_table = of_match_ptr(of_netxbig_leds_match), 642 408 }, 643 409 }; 644 410
+18
include/dt-bindings/leds/leds-netxbig.h
··· 1 + /* 2 + * This header provides constants for netxbig LED bindings. 3 + * 4 + * This file is licensed under the terms of the GNU General Public 5 + * License version 2. This program is licensed "as is" without any 6 + * warranty of any kind, whether express or implied. 7 + */ 8 + 9 + #ifndef _DT_BINDINGS_LEDS_NETXBIG_H 10 + #define _DT_BINDINGS_LEDS_NETXBIG_H 11 + 12 + #define NETXBIG_LED_OFF 0 13 + #define NETXBIG_LED_ON 1 14 + #define NETXBIG_LED_SATA 2 15 + #define NETXBIG_LED_TIMER1 3 16 + #define NETXBIG_LED_TIMER2 4 17 + 18 + #endif /* _DT_BINDINGS_LEDS_NETXBIG_H */
+1
include/linux/platform_data/leds-kirkwood-netxbig.h
··· 40 40 int mode_addr; 41 41 int *mode_val; 42 42 int bright_addr; 43 + int bright_max; 43 44 }; 44 45 45 46 struct netxbig_led_platform_data {