.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * leds-netxbig.c - Driver for the 2Big and 5Big Network series LEDs |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2010 LaCie |
---|
5 | 6 | * |
---|
6 | 7 | * 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 | 8 | */ |
---|
22 | 9 | |
---|
23 | 10 | #include <linux/module.h> |
---|
.. | .. |
---|
25 | 12 | #include <linux/slab.h> |
---|
26 | 13 | #include <linux/spinlock.h> |
---|
27 | 14 | #include <linux/platform_device.h> |
---|
28 | | -#include <linux/gpio.h> |
---|
29 | | -#include <linux/of_gpio.h> |
---|
| 15 | +#include <linux/gpio/consumer.h> |
---|
30 | 16 | #include <linux/leds.h> |
---|
31 | | -#include <linux/platform_data/leds-kirkwood-netxbig.h> |
---|
| 17 | +#include <linux/of.h> |
---|
| 18 | +#include <linux/of_platform.h> |
---|
| 19 | + |
---|
| 20 | +struct netxbig_gpio_ext { |
---|
| 21 | + struct gpio_desc **addr; |
---|
| 22 | + int num_addr; |
---|
| 23 | + struct gpio_desc **data; |
---|
| 24 | + int num_data; |
---|
| 25 | + struct gpio_desc *enable; |
---|
| 26 | +}; |
---|
| 27 | + |
---|
| 28 | +enum netxbig_led_mode { |
---|
| 29 | + NETXBIG_LED_OFF, |
---|
| 30 | + NETXBIG_LED_ON, |
---|
| 31 | + NETXBIG_LED_SATA, |
---|
| 32 | + NETXBIG_LED_TIMER1, |
---|
| 33 | + NETXBIG_LED_TIMER2, |
---|
| 34 | + NETXBIG_LED_MODE_NUM, |
---|
| 35 | +}; |
---|
| 36 | + |
---|
| 37 | +#define NETXBIG_LED_INVALID_MODE NETXBIG_LED_MODE_NUM |
---|
| 38 | + |
---|
| 39 | +struct netxbig_led_timer { |
---|
| 40 | + unsigned long delay_on; |
---|
| 41 | + unsigned long delay_off; |
---|
| 42 | + enum netxbig_led_mode mode; |
---|
| 43 | +}; |
---|
| 44 | + |
---|
| 45 | +struct netxbig_led { |
---|
| 46 | + const char *name; |
---|
| 47 | + const char *default_trigger; |
---|
| 48 | + int mode_addr; |
---|
| 49 | + int *mode_val; |
---|
| 50 | + int bright_addr; |
---|
| 51 | + int bright_max; |
---|
| 52 | +}; |
---|
| 53 | + |
---|
| 54 | +struct netxbig_led_platform_data { |
---|
| 55 | + struct netxbig_gpio_ext *gpio_ext; |
---|
| 56 | + struct netxbig_led_timer *timer; |
---|
| 57 | + int num_timer; |
---|
| 58 | + struct netxbig_led *leds; |
---|
| 59 | + int num_leds; |
---|
| 60 | +}; |
---|
32 | 61 | |
---|
33 | 62 | /* |
---|
34 | 63 | * GPIO extension bus. |
---|
.. | .. |
---|
41 | 70 | int pin; |
---|
42 | 71 | |
---|
43 | 72 | for (pin = 0; pin < gpio_ext->num_addr; pin++) |
---|
44 | | - gpio_set_value(gpio_ext->addr[pin], (addr >> pin) & 1); |
---|
| 73 | + gpiod_set_value(gpio_ext->addr[pin], (addr >> pin) & 1); |
---|
45 | 74 | } |
---|
46 | 75 | |
---|
47 | 76 | static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data) |
---|
.. | .. |
---|
49 | 78 | int pin; |
---|
50 | 79 | |
---|
51 | 80 | for (pin = 0; pin < gpio_ext->num_data; pin++) |
---|
52 | | - gpio_set_value(gpio_ext->data[pin], (data >> pin) & 1); |
---|
| 81 | + gpiod_set_value(gpio_ext->data[pin], (data >> pin) & 1); |
---|
53 | 82 | } |
---|
54 | 83 | |
---|
55 | 84 | static void gpio_ext_enable_select(struct netxbig_gpio_ext *gpio_ext) |
---|
56 | 85 | { |
---|
57 | 86 | /* Enable select is done on the raising edge. */ |
---|
58 | | - gpio_set_value(gpio_ext->enable, 0); |
---|
59 | | - gpio_set_value(gpio_ext->enable, 1); |
---|
| 87 | + gpiod_set_value(gpio_ext->enable, 0); |
---|
| 88 | + gpiod_set_value(gpio_ext->enable, 1); |
---|
60 | 89 | } |
---|
61 | 90 | |
---|
62 | 91 | static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext, |
---|
.. | .. |
---|
69 | 98 | gpio_ext_set_data(gpio_ext, value); |
---|
70 | 99 | gpio_ext_enable_select(gpio_ext); |
---|
71 | 100 | spin_unlock_irqrestore(&gpio_ext_lock, flags); |
---|
72 | | -} |
---|
73 | | - |
---|
74 | | -static int gpio_ext_init(struct platform_device *pdev, |
---|
75 | | - struct netxbig_gpio_ext *gpio_ext) |
---|
76 | | -{ |
---|
77 | | - int err; |
---|
78 | | - int i; |
---|
79 | | - |
---|
80 | | - if (unlikely(!gpio_ext)) |
---|
81 | | - return -EINVAL; |
---|
82 | | - |
---|
83 | | - /* Configure address GPIOs. */ |
---|
84 | | - for (i = 0; i < gpio_ext->num_addr; i++) { |
---|
85 | | - err = devm_gpio_request_one(&pdev->dev, gpio_ext->addr[i], |
---|
86 | | - GPIOF_OUT_INIT_LOW, |
---|
87 | | - "GPIO extension addr"); |
---|
88 | | - if (err) |
---|
89 | | - return err; |
---|
90 | | - } |
---|
91 | | - /* Configure data GPIOs. */ |
---|
92 | | - for (i = 0; i < gpio_ext->num_data; i++) { |
---|
93 | | - err = devm_gpio_request_one(&pdev->dev, gpio_ext->data[i], |
---|
94 | | - GPIOF_OUT_INIT_LOW, |
---|
95 | | - "GPIO extension data"); |
---|
96 | | - if (err) |
---|
97 | | - return err; |
---|
98 | | - } |
---|
99 | | - /* Configure "enable select" GPIO. */ |
---|
100 | | - err = devm_gpio_request_one(&pdev->dev, gpio_ext->enable, |
---|
101 | | - GPIOF_OUT_INIT_LOW, |
---|
102 | | - "GPIO extension enable"); |
---|
103 | | - if (err) |
---|
104 | | - return err; |
---|
105 | | - |
---|
106 | | - return 0; |
---|
107 | 101 | } |
---|
108 | 102 | |
---|
109 | 103 | /* |
---|
.. | .. |
---|
319 | 313 | return devm_led_classdev_register(&pdev->dev, &led_dat->cdev); |
---|
320 | 314 | } |
---|
321 | 315 | |
---|
322 | | -#ifdef CONFIG_OF_GPIO |
---|
323 | | -static int gpio_ext_get_of_pdata(struct device *dev, struct device_node *np, |
---|
324 | | - struct netxbig_gpio_ext *gpio_ext) |
---|
| 316 | +/** |
---|
| 317 | + * netxbig_gpio_ext_remove() - Clean up GPIO extension data |
---|
| 318 | + * @data: managed resource data to clean up |
---|
| 319 | + * |
---|
| 320 | + * Since we pick GPIO descriptors from another device than the device our |
---|
| 321 | + * driver is probing to, we need to register a specific callback to free |
---|
| 322 | + * these up using managed resources. |
---|
| 323 | + */ |
---|
| 324 | +static void netxbig_gpio_ext_remove(void *data) |
---|
325 | 325 | { |
---|
326 | | - int *addr, *data; |
---|
| 326 | + struct netxbig_gpio_ext *gpio_ext = data; |
---|
| 327 | + int i; |
---|
| 328 | + |
---|
| 329 | + for (i = 0; i < gpio_ext->num_addr; i++) |
---|
| 330 | + gpiod_put(gpio_ext->addr[i]); |
---|
| 331 | + for (i = 0; i < gpio_ext->num_data; i++) |
---|
| 332 | + gpiod_put(gpio_ext->data[i]); |
---|
| 333 | + gpiod_put(gpio_ext->enable); |
---|
| 334 | +} |
---|
| 335 | + |
---|
| 336 | +/** |
---|
| 337 | + * netxbig_gpio_ext_get() - Obtain GPIO extension device data |
---|
| 338 | + * @dev: main LED device |
---|
| 339 | + * @gpio_ext_dev: the GPIO extension device |
---|
| 340 | + * @gpio_ext: the data structure holding the GPIO extension data |
---|
| 341 | + * |
---|
| 342 | + * This function walks the subdevice that only contain GPIO line |
---|
| 343 | + * handles in the device tree and obtains the GPIO descriptors from that |
---|
| 344 | + * device. |
---|
| 345 | + */ |
---|
| 346 | +static int netxbig_gpio_ext_get(struct device *dev, |
---|
| 347 | + struct device *gpio_ext_dev, |
---|
| 348 | + struct netxbig_gpio_ext *gpio_ext) |
---|
| 349 | +{ |
---|
| 350 | + struct gpio_desc **addr, **data; |
---|
327 | 351 | int num_addr, num_data; |
---|
| 352 | + struct gpio_desc *gpiod; |
---|
328 | 353 | int ret; |
---|
329 | 354 | int i; |
---|
330 | 355 | |
---|
331 | | - ret = of_gpio_named_count(np, "addr-gpios"); |
---|
| 356 | + ret = gpiod_count(gpio_ext_dev, "addr"); |
---|
332 | 357 | if (ret < 0) { |
---|
333 | 358 | dev_err(dev, |
---|
334 | 359 | "Failed to count GPIOs in DT property addr-gpios\n"); |
---|
.. | .. |
---|
339 | 364 | if (!addr) |
---|
340 | 365 | return -ENOMEM; |
---|
341 | 366 | |
---|
| 367 | + /* |
---|
| 368 | + * We cannot use devm_ managed resources with these GPIO descriptors |
---|
| 369 | + * since they are associated with the "GPIO extension device" which |
---|
| 370 | + * does not probe any driver. The device tree parser will however |
---|
| 371 | + * populate a platform device for it so we can anyway obtain the |
---|
| 372 | + * GPIO descriptors from the device. |
---|
| 373 | + */ |
---|
342 | 374 | for (i = 0; i < num_addr; i++) { |
---|
343 | | - ret = of_get_named_gpio(np, "addr-gpios", i); |
---|
344 | | - if (ret < 0) |
---|
345 | | - return ret; |
---|
346 | | - addr[i] = ret; |
---|
| 375 | + gpiod = gpiod_get_index(gpio_ext_dev, "addr", i, |
---|
| 376 | + GPIOD_OUT_LOW); |
---|
| 377 | + if (IS_ERR(gpiod)) |
---|
| 378 | + return PTR_ERR(gpiod); |
---|
| 379 | + gpiod_set_consumer_name(gpiod, "GPIO extension addr"); |
---|
| 380 | + addr[i] = gpiod; |
---|
347 | 381 | } |
---|
348 | 382 | gpio_ext->addr = addr; |
---|
349 | 383 | gpio_ext->num_addr = num_addr; |
---|
350 | 384 | |
---|
351 | | - ret = of_gpio_named_count(np, "data-gpios"); |
---|
| 385 | + ret = gpiod_count(gpio_ext_dev, "data"); |
---|
352 | 386 | if (ret < 0) { |
---|
353 | 387 | dev_err(dev, |
---|
354 | 388 | "Failed to count GPIOs in DT property data-gpios\n"); |
---|
.. | .. |
---|
360 | 394 | return -ENOMEM; |
---|
361 | 395 | |
---|
362 | 396 | for (i = 0; i < num_data; i++) { |
---|
363 | | - ret = of_get_named_gpio(np, "data-gpios", i); |
---|
364 | | - if (ret < 0) |
---|
365 | | - return ret; |
---|
366 | | - data[i] = ret; |
---|
| 397 | + gpiod = gpiod_get_index(gpio_ext_dev, "data", i, |
---|
| 398 | + GPIOD_OUT_LOW); |
---|
| 399 | + if (IS_ERR(gpiod)) |
---|
| 400 | + return PTR_ERR(gpiod); |
---|
| 401 | + gpiod_set_consumer_name(gpiod, "GPIO extension data"); |
---|
| 402 | + data[i] = gpiod; |
---|
367 | 403 | } |
---|
368 | 404 | gpio_ext->data = data; |
---|
369 | 405 | gpio_ext->num_data = num_data; |
---|
370 | 406 | |
---|
371 | | - ret = of_get_named_gpio(np, "enable-gpio", 0); |
---|
372 | | - if (ret < 0) { |
---|
| 407 | + gpiod = gpiod_get(gpio_ext_dev, "enable", GPIOD_OUT_LOW); |
---|
| 408 | + if (IS_ERR(gpiod)) { |
---|
373 | 409 | dev_err(dev, |
---|
374 | 410 | "Failed to get GPIO from DT property enable-gpio\n"); |
---|
375 | | - return ret; |
---|
| 411 | + return PTR_ERR(gpiod); |
---|
376 | 412 | } |
---|
377 | | - gpio_ext->enable = ret; |
---|
| 413 | + gpiod_set_consumer_name(gpiod, "GPIO extension enable"); |
---|
| 414 | + gpio_ext->enable = gpiod; |
---|
378 | 415 | |
---|
379 | | - return 0; |
---|
| 416 | + return devm_add_action_or_reset(dev, netxbig_gpio_ext_remove, gpio_ext); |
---|
380 | 417 | } |
---|
381 | 418 | |
---|
382 | 419 | static int netxbig_leds_get_of_pdata(struct device *dev, |
---|
383 | 420 | struct netxbig_led_platform_data *pdata) |
---|
384 | 421 | { |
---|
385 | | - struct device_node *np = dev->of_node; |
---|
| 422 | + struct device_node *np = dev_of_node(dev); |
---|
386 | 423 | struct device_node *gpio_ext_np; |
---|
| 424 | + struct platform_device *gpio_ext_pdev; |
---|
| 425 | + struct device *gpio_ext_dev; |
---|
387 | 426 | struct device_node *child; |
---|
388 | 427 | struct netxbig_gpio_ext *gpio_ext; |
---|
389 | 428 | struct netxbig_led_timer *timers; |
---|
.. | .. |
---|
399 | 438 | dev_err(dev, "Failed to get DT handle gpio-ext\n"); |
---|
400 | 439 | return -EINVAL; |
---|
401 | 440 | } |
---|
| 441 | + gpio_ext_pdev = of_find_device_by_node(gpio_ext_np); |
---|
| 442 | + if (!gpio_ext_pdev) { |
---|
| 443 | + dev_err(dev, "Failed to find platform device for gpio-ext\n"); |
---|
| 444 | + return -ENODEV; |
---|
| 445 | + } |
---|
| 446 | + gpio_ext_dev = &gpio_ext_pdev->dev; |
---|
402 | 447 | |
---|
403 | 448 | gpio_ext = devm_kzalloc(dev, sizeof(*gpio_ext), GFP_KERNEL); |
---|
404 | | - if (!gpio_ext) |
---|
405 | | - return -ENOMEM; |
---|
406 | | - ret = gpio_ext_get_of_pdata(dev, gpio_ext_np, gpio_ext); |
---|
407 | | - if (ret) |
---|
408 | | - return ret; |
---|
| 449 | + if (!gpio_ext) { |
---|
| 450 | + of_node_put(gpio_ext_np); |
---|
| 451 | + ret = -ENOMEM; |
---|
| 452 | + goto put_device; |
---|
| 453 | + } |
---|
| 454 | + ret = netxbig_gpio_ext_get(dev, gpio_ext_dev, gpio_ext); |
---|
409 | 455 | of_node_put(gpio_ext_np); |
---|
| 456 | + if (ret) |
---|
| 457 | + goto put_device; |
---|
410 | 458 | pdata->gpio_ext = gpio_ext; |
---|
411 | 459 | |
---|
412 | 460 | /* Timers (optional) */ |
---|
413 | 461 | ret = of_property_count_u32_elems(np, "timers"); |
---|
414 | 462 | if (ret > 0) { |
---|
415 | | - if (ret % 3) |
---|
416 | | - return -EINVAL; |
---|
| 463 | + if (ret % 3) { |
---|
| 464 | + ret = -EINVAL; |
---|
| 465 | + goto put_device; |
---|
| 466 | + } |
---|
| 467 | + |
---|
417 | 468 | num_timers = ret / 3; |
---|
418 | 469 | timers = devm_kcalloc(dev, num_timers, sizeof(*timers), |
---|
419 | 470 | GFP_KERNEL); |
---|
420 | | - if (!timers) |
---|
421 | | - return -ENOMEM; |
---|
| 471 | + if (!timers) { |
---|
| 472 | + ret = -ENOMEM; |
---|
| 473 | + goto put_device; |
---|
| 474 | + } |
---|
422 | 475 | for (i = 0; i < num_timers; i++) { |
---|
423 | 476 | u32 tmp; |
---|
424 | 477 | |
---|
425 | 478 | of_property_read_u32_index(np, "timers", 3 * i, |
---|
426 | 479 | &timers[i].mode); |
---|
427 | | - if (timers[i].mode >= NETXBIG_LED_MODE_NUM) |
---|
428 | | - return -EINVAL; |
---|
| 480 | + if (timers[i].mode >= NETXBIG_LED_MODE_NUM) { |
---|
| 481 | + ret = -EINVAL; |
---|
| 482 | + goto put_device; |
---|
| 483 | + } |
---|
429 | 484 | of_property_read_u32_index(np, "timers", |
---|
430 | 485 | 3 * i + 1, &tmp); |
---|
431 | 486 | timers[i].delay_on = tmp; |
---|
.. | .. |
---|
438 | 493 | } |
---|
439 | 494 | |
---|
440 | 495 | /* LEDs */ |
---|
441 | | - num_leds = of_get_child_count(np); |
---|
| 496 | + num_leds = of_get_available_child_count(np); |
---|
442 | 497 | if (!num_leds) { |
---|
443 | 498 | dev_err(dev, "No LED subnodes found in DT\n"); |
---|
444 | | - return -ENODEV; |
---|
| 499 | + ret = -ENODEV; |
---|
| 500 | + goto put_device; |
---|
445 | 501 | } |
---|
446 | 502 | |
---|
447 | 503 | leds = devm_kcalloc(dev, num_leds, sizeof(*leds), GFP_KERNEL); |
---|
448 | | - if (!leds) |
---|
449 | | - return -ENOMEM; |
---|
| 504 | + if (!leds) { |
---|
| 505 | + ret = -ENOMEM; |
---|
| 506 | + goto put_device; |
---|
| 507 | + } |
---|
450 | 508 | |
---|
451 | 509 | led = leds; |
---|
452 | | - for_each_child_of_node(np, child) { |
---|
| 510 | + for_each_available_child_of_node(np, child) { |
---|
453 | 511 | const char *string; |
---|
454 | 512 | int *mode_val; |
---|
455 | 513 | int num_modes; |
---|
.. | .. |
---|
527 | 585 | |
---|
528 | 586 | err_node_put: |
---|
529 | 587 | of_node_put(child); |
---|
| 588 | +put_device: |
---|
| 589 | + put_device(gpio_ext_dev); |
---|
530 | 590 | return ret; |
---|
531 | 591 | } |
---|
532 | 592 | |
---|
.. | .. |
---|
535 | 595 | {}, |
---|
536 | 596 | }; |
---|
537 | 597 | MODULE_DEVICE_TABLE(of, of_netxbig_leds_match); |
---|
538 | | -#else |
---|
539 | | -static inline int |
---|
540 | | -netxbig_leds_get_of_pdata(struct device *dev, |
---|
541 | | - struct netxbig_led_platform_data *pdata) |
---|
542 | | -{ |
---|
543 | | - return -ENODEV; |
---|
544 | | -} |
---|
545 | | -#endif /* CONFIG_OF_GPIO */ |
---|
546 | 598 | |
---|
547 | 599 | static int netxbig_led_probe(struct platform_device *pdev) |
---|
548 | 600 | { |
---|
549 | | - struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
---|
| 601 | + struct netxbig_led_platform_data *pdata; |
---|
550 | 602 | struct netxbig_led_data *leds_data; |
---|
551 | 603 | int i; |
---|
552 | 604 | int ret; |
---|
553 | 605 | |
---|
554 | | - if (!pdata) { |
---|
555 | | - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); |
---|
556 | | - if (!pdata) |
---|
557 | | - return -ENOMEM; |
---|
558 | | - ret = netxbig_leds_get_of_pdata(&pdev->dev, pdata); |
---|
559 | | - if (ret) |
---|
560 | | - return ret; |
---|
561 | | - } |
---|
| 606 | + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); |
---|
| 607 | + if (!pdata) |
---|
| 608 | + return -ENOMEM; |
---|
| 609 | + ret = netxbig_leds_get_of_pdata(&pdev->dev, pdata); |
---|
| 610 | + if (ret) |
---|
| 611 | + return ret; |
---|
562 | 612 | |
---|
563 | 613 | leds_data = devm_kcalloc(&pdev->dev, |
---|
564 | 614 | pdata->num_leds, sizeof(*leds_data), |
---|
565 | 615 | GFP_KERNEL); |
---|
566 | 616 | if (!leds_data) |
---|
567 | 617 | return -ENOMEM; |
---|
568 | | - |
---|
569 | | - ret = gpio_ext_init(pdev, pdata->gpio_ext); |
---|
570 | | - if (ret < 0) |
---|
571 | | - return ret; |
---|
572 | 618 | |
---|
573 | 619 | for (i = 0; i < pdata->num_leds; i++) { |
---|
574 | 620 | ret = create_netxbig_led(pdev, pdata, |
---|
.. | .. |
---|
584 | 630 | .probe = netxbig_led_probe, |
---|
585 | 631 | .driver = { |
---|
586 | 632 | .name = "leds-netxbig", |
---|
587 | | - .of_match_table = of_match_ptr(of_netxbig_leds_match), |
---|
| 633 | + .of_match_table = of_netxbig_leds_match, |
---|
588 | 634 | }, |
---|
589 | 635 | }; |
---|
590 | 636 | |
---|