xref: /OK3568_Linux_fs/kernel/drivers/leds/leds-netxbig.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * leds-netxbig.c - Driver for the 2Big and 5Big Network series LEDs
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2010 LaCie
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author: Simon Guinot <sguinot@lacie.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/irq.h>
12*4882a593Smuzhiyun #include <linux/slab.h>
13*4882a593Smuzhiyun #include <linux/spinlock.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
16*4882a593Smuzhiyun #include <linux/leds.h>
17*4882a593Smuzhiyun #include <linux/of.h>
18*4882a593Smuzhiyun #include <linux/of_platform.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun struct netxbig_gpio_ext {
21*4882a593Smuzhiyun 	struct gpio_desc **addr;
22*4882a593Smuzhiyun 	int		num_addr;
23*4882a593Smuzhiyun 	struct gpio_desc **data;
24*4882a593Smuzhiyun 	int		num_data;
25*4882a593Smuzhiyun 	struct gpio_desc *enable;
26*4882a593Smuzhiyun };
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun enum netxbig_led_mode {
29*4882a593Smuzhiyun 	NETXBIG_LED_OFF,
30*4882a593Smuzhiyun 	NETXBIG_LED_ON,
31*4882a593Smuzhiyun 	NETXBIG_LED_SATA,
32*4882a593Smuzhiyun 	NETXBIG_LED_TIMER1,
33*4882a593Smuzhiyun 	NETXBIG_LED_TIMER2,
34*4882a593Smuzhiyun 	NETXBIG_LED_MODE_NUM,
35*4882a593Smuzhiyun };
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #define NETXBIG_LED_INVALID_MODE NETXBIG_LED_MODE_NUM
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun struct netxbig_led_timer {
40*4882a593Smuzhiyun 	unsigned long		delay_on;
41*4882a593Smuzhiyun 	unsigned long		delay_off;
42*4882a593Smuzhiyun 	enum netxbig_led_mode	mode;
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun struct netxbig_led {
46*4882a593Smuzhiyun 	const char	*name;
47*4882a593Smuzhiyun 	const char	*default_trigger;
48*4882a593Smuzhiyun 	int		mode_addr;
49*4882a593Smuzhiyun 	int		*mode_val;
50*4882a593Smuzhiyun 	int		bright_addr;
51*4882a593Smuzhiyun 	int		bright_max;
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun struct netxbig_led_platform_data {
55*4882a593Smuzhiyun 	struct netxbig_gpio_ext	*gpio_ext;
56*4882a593Smuzhiyun 	struct netxbig_led_timer *timer;
57*4882a593Smuzhiyun 	int			num_timer;
58*4882a593Smuzhiyun 	struct netxbig_led	*leds;
59*4882a593Smuzhiyun 	int			num_leds;
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun /*
63*4882a593Smuzhiyun  * GPIO extension bus.
64*4882a593Smuzhiyun  */
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun static DEFINE_SPINLOCK(gpio_ext_lock);
67*4882a593Smuzhiyun 
gpio_ext_set_addr(struct netxbig_gpio_ext * gpio_ext,int addr)68*4882a593Smuzhiyun static void gpio_ext_set_addr(struct netxbig_gpio_ext *gpio_ext, int addr)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	int pin;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	for (pin = 0; pin < gpio_ext->num_addr; pin++)
73*4882a593Smuzhiyun 		gpiod_set_value(gpio_ext->addr[pin], (addr >> pin) & 1);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
gpio_ext_set_data(struct netxbig_gpio_ext * gpio_ext,int data)76*4882a593Smuzhiyun static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun 	int pin;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	for (pin = 0; pin < gpio_ext->num_data; pin++)
81*4882a593Smuzhiyun 		gpiod_set_value(gpio_ext->data[pin], (data >> pin) & 1);
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun 
gpio_ext_enable_select(struct netxbig_gpio_ext * gpio_ext)84*4882a593Smuzhiyun static void gpio_ext_enable_select(struct netxbig_gpio_ext *gpio_ext)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun 	/* Enable select is done on the raising edge. */
87*4882a593Smuzhiyun 	gpiod_set_value(gpio_ext->enable, 0);
88*4882a593Smuzhiyun 	gpiod_set_value(gpio_ext->enable, 1);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
gpio_ext_set_value(struct netxbig_gpio_ext * gpio_ext,int addr,int value)91*4882a593Smuzhiyun static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext,
92*4882a593Smuzhiyun 			       int addr, int value)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	unsigned long flags;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	spin_lock_irqsave(&gpio_ext_lock, flags);
97*4882a593Smuzhiyun 	gpio_ext_set_addr(gpio_ext, addr);
98*4882a593Smuzhiyun 	gpio_ext_set_data(gpio_ext, value);
99*4882a593Smuzhiyun 	gpio_ext_enable_select(gpio_ext);
100*4882a593Smuzhiyun 	spin_unlock_irqrestore(&gpio_ext_lock, flags);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun /*
104*4882a593Smuzhiyun  * Class LED driver.
105*4882a593Smuzhiyun  */
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun struct netxbig_led_data {
108*4882a593Smuzhiyun 	struct netxbig_gpio_ext	*gpio_ext;
109*4882a593Smuzhiyun 	struct led_classdev	cdev;
110*4882a593Smuzhiyun 	int			mode_addr;
111*4882a593Smuzhiyun 	int			*mode_val;
112*4882a593Smuzhiyun 	int			bright_addr;
113*4882a593Smuzhiyun 	struct			netxbig_led_timer *timer;
114*4882a593Smuzhiyun 	int			num_timer;
115*4882a593Smuzhiyun 	enum netxbig_led_mode	mode;
116*4882a593Smuzhiyun 	int			sata;
117*4882a593Smuzhiyun 	spinlock_t		lock;
118*4882a593Smuzhiyun };
119*4882a593Smuzhiyun 
netxbig_led_get_timer_mode(enum netxbig_led_mode * mode,unsigned long delay_on,unsigned long delay_off,struct netxbig_led_timer * timer,int num_timer)120*4882a593Smuzhiyun static int netxbig_led_get_timer_mode(enum netxbig_led_mode *mode,
121*4882a593Smuzhiyun 				      unsigned long delay_on,
122*4882a593Smuzhiyun 				      unsigned long delay_off,
123*4882a593Smuzhiyun 				      struct netxbig_led_timer *timer,
124*4882a593Smuzhiyun 				      int num_timer)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	int i;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	for (i = 0; i < num_timer; i++) {
129*4882a593Smuzhiyun 		if (timer[i].delay_on == delay_on &&
130*4882a593Smuzhiyun 		    timer[i].delay_off == delay_off) {
131*4882a593Smuzhiyun 			*mode = timer[i].mode;
132*4882a593Smuzhiyun 			return 0;
133*4882a593Smuzhiyun 		}
134*4882a593Smuzhiyun 	}
135*4882a593Smuzhiyun 	return -EINVAL;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
netxbig_led_blink_set(struct led_classdev * led_cdev,unsigned long * delay_on,unsigned long * delay_off)138*4882a593Smuzhiyun static int netxbig_led_blink_set(struct led_classdev *led_cdev,
139*4882a593Smuzhiyun 				 unsigned long *delay_on,
140*4882a593Smuzhiyun 				 unsigned long *delay_off)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun 	struct netxbig_led_data *led_dat =
143*4882a593Smuzhiyun 		container_of(led_cdev, struct netxbig_led_data, cdev);
144*4882a593Smuzhiyun 	enum netxbig_led_mode mode;
145*4882a593Smuzhiyun 	int mode_val;
146*4882a593Smuzhiyun 	int ret;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	/* Look for a LED mode with the requested timer frequency. */
149*4882a593Smuzhiyun 	ret = netxbig_led_get_timer_mode(&mode, *delay_on, *delay_off,
150*4882a593Smuzhiyun 					 led_dat->timer, led_dat->num_timer);
151*4882a593Smuzhiyun 	if (ret < 0)
152*4882a593Smuzhiyun 		return ret;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	mode_val = led_dat->mode_val[mode];
155*4882a593Smuzhiyun 	if (mode_val == NETXBIG_LED_INVALID_MODE)
156*4882a593Smuzhiyun 		return -EINVAL;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	spin_lock_irq(&led_dat->lock);
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val);
161*4882a593Smuzhiyun 	led_dat->mode = mode;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	spin_unlock_irq(&led_dat->lock);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	return 0;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun 
netxbig_led_set(struct led_classdev * led_cdev,enum led_brightness value)168*4882a593Smuzhiyun static void netxbig_led_set(struct led_classdev *led_cdev,
169*4882a593Smuzhiyun 			    enum led_brightness value)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	struct netxbig_led_data *led_dat =
172*4882a593Smuzhiyun 		container_of(led_cdev, struct netxbig_led_data, cdev);
173*4882a593Smuzhiyun 	enum netxbig_led_mode mode;
174*4882a593Smuzhiyun 	int mode_val;
175*4882a593Smuzhiyun 	int set_brightness = 1;
176*4882a593Smuzhiyun 	unsigned long flags;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	spin_lock_irqsave(&led_dat->lock, flags);
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	if (value == LED_OFF) {
181*4882a593Smuzhiyun 		mode = NETXBIG_LED_OFF;
182*4882a593Smuzhiyun 		set_brightness = 0;
183*4882a593Smuzhiyun 	} else {
184*4882a593Smuzhiyun 		if (led_dat->sata)
185*4882a593Smuzhiyun 			mode = NETXBIG_LED_SATA;
186*4882a593Smuzhiyun 		else if (led_dat->mode == NETXBIG_LED_OFF)
187*4882a593Smuzhiyun 			mode = NETXBIG_LED_ON;
188*4882a593Smuzhiyun 		else /* Keep 'timer' mode. */
189*4882a593Smuzhiyun 			mode = led_dat->mode;
190*4882a593Smuzhiyun 	}
191*4882a593Smuzhiyun 	mode_val = led_dat->mode_val[mode];
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val);
194*4882a593Smuzhiyun 	led_dat->mode = mode;
195*4882a593Smuzhiyun 	/*
196*4882a593Smuzhiyun 	 * Note that the brightness register is shared between all the
197*4882a593Smuzhiyun 	 * SATA LEDs. So, change the brightness setting for a single
198*4882a593Smuzhiyun 	 * SATA LED will affect all the others.
199*4882a593Smuzhiyun 	 */
200*4882a593Smuzhiyun 	if (set_brightness)
201*4882a593Smuzhiyun 		gpio_ext_set_value(led_dat->gpio_ext,
202*4882a593Smuzhiyun 				   led_dat->bright_addr, value);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	spin_unlock_irqrestore(&led_dat->lock, flags);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
netxbig_led_sata_store(struct device * dev,struct device_attribute * attr,const char * buff,size_t count)207*4882a593Smuzhiyun static ssize_t netxbig_led_sata_store(struct device *dev,
208*4882a593Smuzhiyun 				      struct device_attribute *attr,
209*4882a593Smuzhiyun 				      const char *buff, size_t count)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
212*4882a593Smuzhiyun 	struct netxbig_led_data *led_dat =
213*4882a593Smuzhiyun 		container_of(led_cdev, struct netxbig_led_data, cdev);
214*4882a593Smuzhiyun 	unsigned long enable;
215*4882a593Smuzhiyun 	enum netxbig_led_mode mode;
216*4882a593Smuzhiyun 	int mode_val;
217*4882a593Smuzhiyun 	int ret;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	ret = kstrtoul(buff, 10, &enable);
220*4882a593Smuzhiyun 	if (ret < 0)
221*4882a593Smuzhiyun 		return ret;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	enable = !!enable;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	spin_lock_irq(&led_dat->lock);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	if (led_dat->sata == enable) {
228*4882a593Smuzhiyun 		ret = count;
229*4882a593Smuzhiyun 		goto exit_unlock;
230*4882a593Smuzhiyun 	}
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	if (led_dat->mode != NETXBIG_LED_ON &&
233*4882a593Smuzhiyun 	    led_dat->mode != NETXBIG_LED_SATA)
234*4882a593Smuzhiyun 		mode = led_dat->mode; /* Keep modes 'off' and 'timer'. */
235*4882a593Smuzhiyun 	else if (enable)
236*4882a593Smuzhiyun 		mode = NETXBIG_LED_SATA;
237*4882a593Smuzhiyun 	else
238*4882a593Smuzhiyun 		mode = NETXBIG_LED_ON;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	mode_val = led_dat->mode_val[mode];
241*4882a593Smuzhiyun 	if (mode_val == NETXBIG_LED_INVALID_MODE) {
242*4882a593Smuzhiyun 		ret = -EINVAL;
243*4882a593Smuzhiyun 		goto exit_unlock;
244*4882a593Smuzhiyun 	}
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val);
247*4882a593Smuzhiyun 	led_dat->mode = mode;
248*4882a593Smuzhiyun 	led_dat->sata = enable;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	ret = count;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun exit_unlock:
253*4882a593Smuzhiyun 	spin_unlock_irq(&led_dat->lock);
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	return ret;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
netxbig_led_sata_show(struct device * dev,struct device_attribute * attr,char * buf)258*4882a593Smuzhiyun static ssize_t netxbig_led_sata_show(struct device *dev,
259*4882a593Smuzhiyun 				     struct device_attribute *attr, char *buf)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
262*4882a593Smuzhiyun 	struct netxbig_led_data *led_dat =
263*4882a593Smuzhiyun 		container_of(led_cdev, struct netxbig_led_data, cdev);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", led_dat->sata);
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun static DEVICE_ATTR(sata, 0644, netxbig_led_sata_show, netxbig_led_sata_store);
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun static struct attribute *netxbig_led_attrs[] = {
271*4882a593Smuzhiyun 	&dev_attr_sata.attr,
272*4882a593Smuzhiyun 	NULL
273*4882a593Smuzhiyun };
274*4882a593Smuzhiyun ATTRIBUTE_GROUPS(netxbig_led);
275*4882a593Smuzhiyun 
create_netxbig_led(struct platform_device * pdev,struct netxbig_led_platform_data * pdata,struct netxbig_led_data * led_dat,const struct netxbig_led * template)276*4882a593Smuzhiyun static int create_netxbig_led(struct platform_device *pdev,
277*4882a593Smuzhiyun 			      struct netxbig_led_platform_data *pdata,
278*4882a593Smuzhiyun 			      struct netxbig_led_data *led_dat,
279*4882a593Smuzhiyun 			      const struct netxbig_led *template)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun 	spin_lock_init(&led_dat->lock);
282*4882a593Smuzhiyun 	led_dat->gpio_ext = pdata->gpio_ext;
283*4882a593Smuzhiyun 	led_dat->cdev.name = template->name;
284*4882a593Smuzhiyun 	led_dat->cdev.default_trigger = template->default_trigger;
285*4882a593Smuzhiyun 	led_dat->cdev.blink_set = netxbig_led_blink_set;
286*4882a593Smuzhiyun 	led_dat->cdev.brightness_set = netxbig_led_set;
287*4882a593Smuzhiyun 	/*
288*4882a593Smuzhiyun 	 * Because the GPIO extension bus don't allow to read registers
289*4882a593Smuzhiyun 	 * value, there is no way to probe the LED initial state.
290*4882a593Smuzhiyun 	 * So, the initial sysfs LED value for the "brightness" and "sata"
291*4882a593Smuzhiyun 	 * attributes are inconsistent.
292*4882a593Smuzhiyun 	 *
293*4882a593Smuzhiyun 	 * Note that the initial LED state can't be reconfigured.
294*4882a593Smuzhiyun 	 * The reason is that the LED behaviour must stay uniform during
295*4882a593Smuzhiyun 	 * the whole boot process (bootloader+linux).
296*4882a593Smuzhiyun 	 */
297*4882a593Smuzhiyun 	led_dat->sata = 0;
298*4882a593Smuzhiyun 	led_dat->cdev.brightness = LED_OFF;
299*4882a593Smuzhiyun 	led_dat->cdev.max_brightness = template->bright_max;
300*4882a593Smuzhiyun 	led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
301*4882a593Smuzhiyun 	led_dat->mode_addr = template->mode_addr;
302*4882a593Smuzhiyun 	led_dat->mode_val = template->mode_val;
303*4882a593Smuzhiyun 	led_dat->bright_addr = template->bright_addr;
304*4882a593Smuzhiyun 	led_dat->timer = pdata->timer;
305*4882a593Smuzhiyun 	led_dat->num_timer = pdata->num_timer;
306*4882a593Smuzhiyun 	/*
307*4882a593Smuzhiyun 	 * If available, expose the SATA activity blink capability through
308*4882a593Smuzhiyun 	 * a "sata" sysfs attribute.
309*4882a593Smuzhiyun 	 */
310*4882a593Smuzhiyun 	if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE)
311*4882a593Smuzhiyun 		led_dat->cdev.groups = netxbig_led_groups;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	return devm_led_classdev_register(&pdev->dev, &led_dat->cdev);
314*4882a593Smuzhiyun }
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun /**
317*4882a593Smuzhiyun  * netxbig_gpio_ext_remove() - Clean up GPIO extension data
318*4882a593Smuzhiyun  * @data: managed resource data to clean up
319*4882a593Smuzhiyun  *
320*4882a593Smuzhiyun  * Since we pick GPIO descriptors from another device than the device our
321*4882a593Smuzhiyun  * driver is probing to, we need to register a specific callback to free
322*4882a593Smuzhiyun  * these up using managed resources.
323*4882a593Smuzhiyun  */
netxbig_gpio_ext_remove(void * data)324*4882a593Smuzhiyun static void netxbig_gpio_ext_remove(void *data)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun 	struct netxbig_gpio_ext *gpio_ext = data;
327*4882a593Smuzhiyun 	int i;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	for (i = 0; i < gpio_ext->num_addr; i++)
330*4882a593Smuzhiyun 		gpiod_put(gpio_ext->addr[i]);
331*4882a593Smuzhiyun 	for (i = 0; i < gpio_ext->num_data; i++)
332*4882a593Smuzhiyun 		gpiod_put(gpio_ext->data[i]);
333*4882a593Smuzhiyun 	gpiod_put(gpio_ext->enable);
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun /**
337*4882a593Smuzhiyun  * netxbig_gpio_ext_get() - Obtain GPIO extension device data
338*4882a593Smuzhiyun  * @dev: main LED device
339*4882a593Smuzhiyun  * @gpio_ext_dev: the GPIO extension device
340*4882a593Smuzhiyun  * @gpio_ext: the data structure holding the GPIO extension data
341*4882a593Smuzhiyun  *
342*4882a593Smuzhiyun  * This function walks the subdevice that only contain GPIO line
343*4882a593Smuzhiyun  * handles in the device tree and obtains the GPIO descriptors from that
344*4882a593Smuzhiyun  * device.
345*4882a593Smuzhiyun  */
netxbig_gpio_ext_get(struct device * dev,struct device * gpio_ext_dev,struct netxbig_gpio_ext * gpio_ext)346*4882a593Smuzhiyun static int netxbig_gpio_ext_get(struct device *dev,
347*4882a593Smuzhiyun 				struct device *gpio_ext_dev,
348*4882a593Smuzhiyun 				struct netxbig_gpio_ext *gpio_ext)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun 	struct gpio_desc **addr, **data;
351*4882a593Smuzhiyun 	int num_addr, num_data;
352*4882a593Smuzhiyun 	struct gpio_desc *gpiod;
353*4882a593Smuzhiyun 	int ret;
354*4882a593Smuzhiyun 	int i;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	ret = gpiod_count(gpio_ext_dev, "addr");
357*4882a593Smuzhiyun 	if (ret < 0) {
358*4882a593Smuzhiyun 		dev_err(dev,
359*4882a593Smuzhiyun 			"Failed to count GPIOs in DT property addr-gpios\n");
360*4882a593Smuzhiyun 		return ret;
361*4882a593Smuzhiyun 	}
362*4882a593Smuzhiyun 	num_addr = ret;
363*4882a593Smuzhiyun 	addr = devm_kcalloc(dev, num_addr, sizeof(*addr), GFP_KERNEL);
364*4882a593Smuzhiyun 	if (!addr)
365*4882a593Smuzhiyun 		return -ENOMEM;
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	/*
368*4882a593Smuzhiyun 	 * We cannot use devm_ managed resources with these GPIO descriptors
369*4882a593Smuzhiyun 	 * since they are associated with the "GPIO extension device" which
370*4882a593Smuzhiyun 	 * does not probe any driver. The device tree parser will however
371*4882a593Smuzhiyun 	 * populate a platform device for it so we can anyway obtain the
372*4882a593Smuzhiyun 	 * GPIO descriptors from the device.
373*4882a593Smuzhiyun 	 */
374*4882a593Smuzhiyun 	for (i = 0; i < num_addr; i++) {
375*4882a593Smuzhiyun 		gpiod = gpiod_get_index(gpio_ext_dev, "addr", i,
376*4882a593Smuzhiyun 					GPIOD_OUT_LOW);
377*4882a593Smuzhiyun 		if (IS_ERR(gpiod))
378*4882a593Smuzhiyun 			return PTR_ERR(gpiod);
379*4882a593Smuzhiyun 		gpiod_set_consumer_name(gpiod, "GPIO extension addr");
380*4882a593Smuzhiyun 		addr[i] = gpiod;
381*4882a593Smuzhiyun 	}
382*4882a593Smuzhiyun 	gpio_ext->addr = addr;
383*4882a593Smuzhiyun 	gpio_ext->num_addr = num_addr;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	ret = gpiod_count(gpio_ext_dev, "data");
386*4882a593Smuzhiyun 	if (ret < 0) {
387*4882a593Smuzhiyun 		dev_err(dev,
388*4882a593Smuzhiyun 			"Failed to count GPIOs in DT property data-gpios\n");
389*4882a593Smuzhiyun 		return ret;
390*4882a593Smuzhiyun 	}
391*4882a593Smuzhiyun 	num_data = ret;
392*4882a593Smuzhiyun 	data = devm_kcalloc(dev, num_data, sizeof(*data), GFP_KERNEL);
393*4882a593Smuzhiyun 	if (!data)
394*4882a593Smuzhiyun 		return -ENOMEM;
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	for (i = 0; i < num_data; i++) {
397*4882a593Smuzhiyun 		gpiod = gpiod_get_index(gpio_ext_dev, "data", i,
398*4882a593Smuzhiyun 					GPIOD_OUT_LOW);
399*4882a593Smuzhiyun 		if (IS_ERR(gpiod))
400*4882a593Smuzhiyun 			return PTR_ERR(gpiod);
401*4882a593Smuzhiyun 		gpiod_set_consumer_name(gpiod, "GPIO extension data");
402*4882a593Smuzhiyun 		data[i] = gpiod;
403*4882a593Smuzhiyun 	}
404*4882a593Smuzhiyun 	gpio_ext->data = data;
405*4882a593Smuzhiyun 	gpio_ext->num_data = num_data;
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	gpiod = gpiod_get(gpio_ext_dev, "enable", GPIOD_OUT_LOW);
408*4882a593Smuzhiyun 	if (IS_ERR(gpiod)) {
409*4882a593Smuzhiyun 		dev_err(dev,
410*4882a593Smuzhiyun 			"Failed to get GPIO from DT property enable-gpio\n");
411*4882a593Smuzhiyun 		return PTR_ERR(gpiod);
412*4882a593Smuzhiyun 	}
413*4882a593Smuzhiyun 	gpiod_set_consumer_name(gpiod, "GPIO extension enable");
414*4882a593Smuzhiyun 	gpio_ext->enable = gpiod;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	return devm_add_action_or_reset(dev, netxbig_gpio_ext_remove, gpio_ext);
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun 
netxbig_leds_get_of_pdata(struct device * dev,struct netxbig_led_platform_data * pdata)419*4882a593Smuzhiyun static int netxbig_leds_get_of_pdata(struct device *dev,
420*4882a593Smuzhiyun 				     struct netxbig_led_platform_data *pdata)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun 	struct device_node *np = dev_of_node(dev);
423*4882a593Smuzhiyun 	struct device_node *gpio_ext_np;
424*4882a593Smuzhiyun 	struct platform_device *gpio_ext_pdev;
425*4882a593Smuzhiyun 	struct device *gpio_ext_dev;
426*4882a593Smuzhiyun 	struct device_node *child;
427*4882a593Smuzhiyun 	struct netxbig_gpio_ext *gpio_ext;
428*4882a593Smuzhiyun 	struct netxbig_led_timer *timers;
429*4882a593Smuzhiyun 	struct netxbig_led *leds, *led;
430*4882a593Smuzhiyun 	int num_timers;
431*4882a593Smuzhiyun 	int num_leds = 0;
432*4882a593Smuzhiyun 	int ret;
433*4882a593Smuzhiyun 	int i;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	/* GPIO extension */
436*4882a593Smuzhiyun 	gpio_ext_np = of_parse_phandle(np, "gpio-ext", 0);
437*4882a593Smuzhiyun 	if (!gpio_ext_np) {
438*4882a593Smuzhiyun 		dev_err(dev, "Failed to get DT handle gpio-ext\n");
439*4882a593Smuzhiyun 		return -EINVAL;
440*4882a593Smuzhiyun 	}
441*4882a593Smuzhiyun 	gpio_ext_pdev = of_find_device_by_node(gpio_ext_np);
442*4882a593Smuzhiyun 	if (!gpio_ext_pdev) {
443*4882a593Smuzhiyun 		dev_err(dev, "Failed to find platform device for gpio-ext\n");
444*4882a593Smuzhiyun 		return -ENODEV;
445*4882a593Smuzhiyun 	}
446*4882a593Smuzhiyun 	gpio_ext_dev = &gpio_ext_pdev->dev;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	gpio_ext = devm_kzalloc(dev, sizeof(*gpio_ext), GFP_KERNEL);
449*4882a593Smuzhiyun 	if (!gpio_ext) {
450*4882a593Smuzhiyun 		of_node_put(gpio_ext_np);
451*4882a593Smuzhiyun 		ret = -ENOMEM;
452*4882a593Smuzhiyun 		goto put_device;
453*4882a593Smuzhiyun 	}
454*4882a593Smuzhiyun 	ret = netxbig_gpio_ext_get(dev, gpio_ext_dev, gpio_ext);
455*4882a593Smuzhiyun 	of_node_put(gpio_ext_np);
456*4882a593Smuzhiyun 	if (ret)
457*4882a593Smuzhiyun 		goto put_device;
458*4882a593Smuzhiyun 	pdata->gpio_ext = gpio_ext;
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	/* Timers (optional) */
461*4882a593Smuzhiyun 	ret = of_property_count_u32_elems(np, "timers");
462*4882a593Smuzhiyun 	if (ret > 0) {
463*4882a593Smuzhiyun 		if (ret % 3) {
464*4882a593Smuzhiyun 			ret = -EINVAL;
465*4882a593Smuzhiyun 			goto put_device;
466*4882a593Smuzhiyun 		}
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 		num_timers = ret / 3;
469*4882a593Smuzhiyun 		timers = devm_kcalloc(dev, num_timers, sizeof(*timers),
470*4882a593Smuzhiyun 				      GFP_KERNEL);
471*4882a593Smuzhiyun 		if (!timers) {
472*4882a593Smuzhiyun 			ret = -ENOMEM;
473*4882a593Smuzhiyun 			goto put_device;
474*4882a593Smuzhiyun 		}
475*4882a593Smuzhiyun 		for (i = 0; i < num_timers; i++) {
476*4882a593Smuzhiyun 			u32 tmp;
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 			of_property_read_u32_index(np, "timers", 3 * i,
479*4882a593Smuzhiyun 						   &timers[i].mode);
480*4882a593Smuzhiyun 			if (timers[i].mode >= NETXBIG_LED_MODE_NUM) {
481*4882a593Smuzhiyun 				ret = -EINVAL;
482*4882a593Smuzhiyun 				goto put_device;
483*4882a593Smuzhiyun 			}
484*4882a593Smuzhiyun 			of_property_read_u32_index(np, "timers",
485*4882a593Smuzhiyun 						   3 * i + 1, &tmp);
486*4882a593Smuzhiyun 			timers[i].delay_on = tmp;
487*4882a593Smuzhiyun 			of_property_read_u32_index(np, "timers",
488*4882a593Smuzhiyun 						   3 * i + 2, &tmp);
489*4882a593Smuzhiyun 			timers[i].delay_off = tmp;
490*4882a593Smuzhiyun 		}
491*4882a593Smuzhiyun 		pdata->timer = timers;
492*4882a593Smuzhiyun 		pdata->num_timer = num_timers;
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	/* LEDs */
496*4882a593Smuzhiyun 	num_leds = of_get_available_child_count(np);
497*4882a593Smuzhiyun 	if (!num_leds) {
498*4882a593Smuzhiyun 		dev_err(dev, "No LED subnodes found in DT\n");
499*4882a593Smuzhiyun 		ret = -ENODEV;
500*4882a593Smuzhiyun 		goto put_device;
501*4882a593Smuzhiyun 	}
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	leds = devm_kcalloc(dev, num_leds, sizeof(*leds), GFP_KERNEL);
504*4882a593Smuzhiyun 	if (!leds) {
505*4882a593Smuzhiyun 		ret = -ENOMEM;
506*4882a593Smuzhiyun 		goto put_device;
507*4882a593Smuzhiyun 	}
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	led = leds;
510*4882a593Smuzhiyun 	for_each_available_child_of_node(np, child) {
511*4882a593Smuzhiyun 		const char *string;
512*4882a593Smuzhiyun 		int *mode_val;
513*4882a593Smuzhiyun 		int num_modes;
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 		ret = of_property_read_u32(child, "mode-addr",
516*4882a593Smuzhiyun 					   &led->mode_addr);
517*4882a593Smuzhiyun 		if (ret)
518*4882a593Smuzhiyun 			goto err_node_put;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 		ret = of_property_read_u32(child, "bright-addr",
521*4882a593Smuzhiyun 					   &led->bright_addr);
522*4882a593Smuzhiyun 		if (ret)
523*4882a593Smuzhiyun 			goto err_node_put;
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 		ret = of_property_read_u32(child, "max-brightness",
526*4882a593Smuzhiyun 					   &led->bright_max);
527*4882a593Smuzhiyun 		if (ret)
528*4882a593Smuzhiyun 			goto err_node_put;
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 		mode_val =
531*4882a593Smuzhiyun 			devm_kcalloc(dev,
532*4882a593Smuzhiyun 				     NETXBIG_LED_MODE_NUM, sizeof(*mode_val),
533*4882a593Smuzhiyun 				     GFP_KERNEL);
534*4882a593Smuzhiyun 		if (!mode_val) {
535*4882a593Smuzhiyun 			ret = -ENOMEM;
536*4882a593Smuzhiyun 			goto err_node_put;
537*4882a593Smuzhiyun 		}
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 		for (i = 0; i < NETXBIG_LED_MODE_NUM; i++)
540*4882a593Smuzhiyun 			mode_val[i] = NETXBIG_LED_INVALID_MODE;
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 		ret = of_property_count_u32_elems(child, "mode-val");
543*4882a593Smuzhiyun 		if (ret < 0 || ret % 2) {
544*4882a593Smuzhiyun 			ret = -EINVAL;
545*4882a593Smuzhiyun 			goto err_node_put;
546*4882a593Smuzhiyun 		}
547*4882a593Smuzhiyun 		num_modes = ret / 2;
548*4882a593Smuzhiyun 		if (num_modes > NETXBIG_LED_MODE_NUM) {
549*4882a593Smuzhiyun 			ret = -EINVAL;
550*4882a593Smuzhiyun 			goto err_node_put;
551*4882a593Smuzhiyun 		}
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 		for (i = 0; i < num_modes; i++) {
554*4882a593Smuzhiyun 			int mode;
555*4882a593Smuzhiyun 			int val;
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 			of_property_read_u32_index(child,
558*4882a593Smuzhiyun 						   "mode-val", 2 * i, &mode);
559*4882a593Smuzhiyun 			of_property_read_u32_index(child,
560*4882a593Smuzhiyun 						   "mode-val", 2 * i + 1, &val);
561*4882a593Smuzhiyun 			if (mode >= NETXBIG_LED_MODE_NUM) {
562*4882a593Smuzhiyun 				ret = -EINVAL;
563*4882a593Smuzhiyun 				goto err_node_put;
564*4882a593Smuzhiyun 			}
565*4882a593Smuzhiyun 			mode_val[mode] = val;
566*4882a593Smuzhiyun 		}
567*4882a593Smuzhiyun 		led->mode_val = mode_val;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 		if (!of_property_read_string(child, "label", &string))
570*4882a593Smuzhiyun 			led->name = string;
571*4882a593Smuzhiyun 		else
572*4882a593Smuzhiyun 			led->name = child->name;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 		if (!of_property_read_string(child,
575*4882a593Smuzhiyun 					     "linux,default-trigger", &string))
576*4882a593Smuzhiyun 			led->default_trigger = string;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 		led++;
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	pdata->leds = leds;
582*4882a593Smuzhiyun 	pdata->num_leds = num_leds;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	return 0;
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun err_node_put:
587*4882a593Smuzhiyun 	of_node_put(child);
588*4882a593Smuzhiyun put_device:
589*4882a593Smuzhiyun 	put_device(gpio_ext_dev);
590*4882a593Smuzhiyun 	return ret;
591*4882a593Smuzhiyun }
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun static const struct of_device_id of_netxbig_leds_match[] = {
594*4882a593Smuzhiyun 	{ .compatible = "lacie,netxbig-leds", },
595*4882a593Smuzhiyun 	{},
596*4882a593Smuzhiyun };
597*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, of_netxbig_leds_match);
598*4882a593Smuzhiyun 
netxbig_led_probe(struct platform_device * pdev)599*4882a593Smuzhiyun static int netxbig_led_probe(struct platform_device *pdev)
600*4882a593Smuzhiyun {
601*4882a593Smuzhiyun 	struct netxbig_led_platform_data *pdata;
602*4882a593Smuzhiyun 	struct netxbig_led_data *leds_data;
603*4882a593Smuzhiyun 	int i;
604*4882a593Smuzhiyun 	int ret;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
607*4882a593Smuzhiyun 	if (!pdata)
608*4882a593Smuzhiyun 		return -ENOMEM;
609*4882a593Smuzhiyun 	ret = netxbig_leds_get_of_pdata(&pdev->dev, pdata);
610*4882a593Smuzhiyun 	if (ret)
611*4882a593Smuzhiyun 		return ret;
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	leds_data = devm_kcalloc(&pdev->dev,
614*4882a593Smuzhiyun 				 pdata->num_leds, sizeof(*leds_data),
615*4882a593Smuzhiyun 				 GFP_KERNEL);
616*4882a593Smuzhiyun 	if (!leds_data)
617*4882a593Smuzhiyun 		return -ENOMEM;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	for (i = 0; i < pdata->num_leds; i++) {
620*4882a593Smuzhiyun 		ret = create_netxbig_led(pdev, pdata,
621*4882a593Smuzhiyun 					 &leds_data[i], &pdata->leds[i]);
622*4882a593Smuzhiyun 		if (ret < 0)
623*4882a593Smuzhiyun 			return ret;
624*4882a593Smuzhiyun 	}
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	return 0;
627*4882a593Smuzhiyun }
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun static struct platform_driver netxbig_led_driver = {
630*4882a593Smuzhiyun 	.probe		= netxbig_led_probe,
631*4882a593Smuzhiyun 	.driver		= {
632*4882a593Smuzhiyun 		.name		= "leds-netxbig",
633*4882a593Smuzhiyun 		.of_match_table	= of_netxbig_leds_match,
634*4882a593Smuzhiyun 	},
635*4882a593Smuzhiyun };
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun module_platform_driver(netxbig_led_driver);
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
640*4882a593Smuzhiyun MODULE_DESCRIPTION("LED driver for LaCie xBig Network boards");
641*4882a593Smuzhiyun MODULE_LICENSE("GPL");
642*4882a593Smuzhiyun MODULE_ALIAS("platform:leds-netxbig");
643