Blame view

drivers/leds/leds-bcm6358.c 5.21 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
2
3
4
5
  /*
   * Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c
   *
   * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com>
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
   */
  #include <linux/delay.h>
  #include <linux/io.h>
  #include <linux/leds.h>
  #include <linux/module.h>
  #include <linux/of.h>
  #include <linux/platform_device.h>
  #include <linux/spinlock.h>
  
  #define BCM6358_REG_MODE		0x0
  #define BCM6358_REG_CTRL		0x4
  
  #define BCM6358_SLED_CLKDIV_MASK	3
  #define BCM6358_SLED_CLKDIV_1		0
  #define BCM6358_SLED_CLKDIV_2		1
  #define BCM6358_SLED_CLKDIV_4		2
  #define BCM6358_SLED_CLKDIV_8		3
  
  #define BCM6358_SLED_POLARITY		BIT(2)
  #define BCM6358_SLED_BUSY		BIT(3)
  
  #define BCM6358_SLED_MAX_COUNT		32
  #define BCM6358_SLED_WAIT		100
  
  /**
   * struct bcm6358_led - state container for bcm6358 based LEDs
   * @cdev: LED class device for this LED
   * @mem: memory resource
   * @lock: memory lock
   * @pin: LED pin number
   * @active_low: LED is active low
   */
  struct bcm6358_led {
  	struct led_classdev cdev;
  	void __iomem *mem;
  	spinlock_t *lock;
  	unsigned long pin;
  	bool active_low;
  };
  
  static void bcm6358_led_write(void __iomem *reg, unsigned long data)
  {
4ba113b6d   Álvaro Fernández Rojas   leds: bcm6358: ad...
48
  #ifdef CONFIG_CPU_BIG_ENDIAN
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
49
  	iowrite32be(data, reg);
4ba113b6d   Álvaro Fernández Rojas   leds: bcm6358: ad...
50
51
52
  #else
  	writel(data, reg);
  #endif
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
53
54
55
56
  }
  
  static unsigned long bcm6358_led_read(void __iomem *reg)
  {
4ba113b6d   Álvaro Fernández Rojas   leds: bcm6358: ad...
57
  #ifdef CONFIG_CPU_BIG_ENDIAN
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
58
  	return ioread32be(reg);
4ba113b6d   Álvaro Fernández Rojas   leds: bcm6358: ad...
59
60
61
  #else
  	return readl(reg);
  #endif
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
62
63
64
65
66
67
68
69
70
71
72
73
  }
  
  static unsigned long bcm6358_led_busy(void __iomem *mem)
  {
  	unsigned long val;
  
  	while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
  		BCM6358_SLED_BUSY)
  		udelay(BCM6358_SLED_WAIT);
  
  	return val;
  }
6e636a0a2   Álvaro Fernández Rojas   leds: bcm6358: me...
74
75
  static void bcm6358_led_set(struct led_classdev *led_cdev,
  			    enum led_brightness value)
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
76
  {
6e636a0a2   Álvaro Fernández Rojas   leds: bcm6358: me...
77
78
79
  	struct bcm6358_led *led =
  		container_of(led_cdev, struct bcm6358_led, cdev);
  	unsigned long flags, val;
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
80

6e636a0a2   Álvaro Fernández Rojas   leds: bcm6358: me...
81
  	spin_lock_irqsave(led->lock, flags);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
82
  	bcm6358_led_busy(led->mem);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
83
84
85
86
87
88
89
  	val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
  	if ((led->active_low && value == LED_OFF) ||
  	    (!led->active_low && value != LED_OFF))
  		val |= BIT(led->pin);
  	else
  		val &= ~(BIT(led->pin));
  	bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
90
91
92
93
94
95
  	spin_unlock_irqrestore(led->lock, flags);
  }
  
  static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
  		       void __iomem *mem, spinlock_t *lock)
  {
e4e912a34   Marek Behún   leds: bcm6328, bc...
96
  	struct led_init_data init_data = {};
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
97
  	struct bcm6358_led *led;
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
98
99
100
101
102
103
104
105
106
107
108
109
110
  	const char *state;
  	int rc;
  
  	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
  	if (!led)
  		return -ENOMEM;
  
  	led->pin = reg;
  	led->mem = mem;
  	led->lock = lock;
  
  	if (of_property_read_bool(nc, "active-low"))
  		led->active_low = true;
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
111
112
113
114
115
  	if (!of_property_read_string(nc, "default-state", &state)) {
  		if (!strcmp(state, "on")) {
  			led->cdev.brightness = LED_FULL;
  		} else if (!strcmp(state, "keep")) {
  			unsigned long val;
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
116
117
118
119
120
121
122
123
124
125
126
127
128
  			val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
  			val &= BIT(led->pin);
  			if ((led->active_low && !val) ||
  			    (!led->active_low && val))
  				led->cdev.brightness = LED_FULL;
  			else
  				led->cdev.brightness = LED_OFF;
  		} else {
  			led->cdev.brightness = LED_OFF;
  		}
  	} else {
  		led->cdev.brightness = LED_OFF;
  	}
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
129

42273caa1   Álvaro Fernández Rojas   leds: bcm6358: Us...
130
  	bcm6358_led_set(&led->cdev, led->cdev.brightness);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
131
  	led->cdev.brightness_set = bcm6358_led_set;
e4e912a34   Marek Behún   leds: bcm6328, bc...
132
  	init_data.fwnode = of_fwnode_handle(nc);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
133

e4e912a34   Marek Behún   leds: bcm6328, bc...
134
  	rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
135
136
137
138
139
140
141
142
143
144
145
146
  	if (rc < 0)
  		return rc;
  
  	dev_dbg(dev, "registered LED %s
  ", led->cdev.name);
  
  	return 0;
  }
  
  static int bcm6358_leds_probe(struct platform_device *pdev)
  {
  	struct device *dev = &pdev->dev;
8853c95e9   Marek Behún   leds: various: us...
147
  	struct device_node *np = dev_of_node(&pdev->dev);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
148
  	struct device_node *child;
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
149
150
151
152
  	void __iomem *mem;
  	spinlock_t *lock; /* memory lock */
  	unsigned long val;
  	u32 clk_div;
8b4423d6c   Markus Elfring   leds: bcm6358: Us...
153
  	mem = devm_platform_ioremap_resource(pdev, 0);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
  	if (IS_ERR(mem))
  		return PTR_ERR(mem);
  
  	lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
  	if (!lock)
  		return -ENOMEM;
  
  	spin_lock_init(lock);
  
  	val = bcm6358_led_busy(mem);
  	val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
  	if (of_property_read_bool(np, "brcm,clk-dat-low"))
  		val |= BCM6358_SLED_POLARITY;
  	of_property_read_u32(np, "brcm,clk-div", &clk_div);
  	switch (clk_div) {
  	case 8:
  		val |= BCM6358_SLED_CLKDIV_8;
  		break;
  	case 4:
  		val |= BCM6358_SLED_CLKDIV_4;
  		break;
  	case 2:
  		val |= BCM6358_SLED_CLKDIV_2;
  		break;
  	default:
  		val |= BCM6358_SLED_CLKDIV_1;
  		break;
  	}
  	bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
  
  	for_each_available_child_of_node(np, child) {
  		int rc;
  		u32 reg;
  
  		if (of_property_read_u32(child, "reg", &reg))
  			continue;
  
  		if (reg >= BCM6358_SLED_MAX_COUNT) {
  			dev_err(dev, "invalid LED (%u >= %d)
  ", reg,
  				BCM6358_SLED_MAX_COUNT);
  			continue;
  		}
  
  		rc = bcm6358_led(dev, child, reg, mem, lock);
4b6ba5e28   Julia Lawall   leds: bcm6358: ad...
199
200
  		if (rc < 0) {
  			of_node_put(child);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
201
  			return rc;
4b6ba5e28   Julia Lawall   leds: bcm6358: ad...
202
  		}
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
203
204
205
206
207
208
209
210
211
  	}
  
  	return 0;
  }
  
  static const struct of_device_id bcm6358_leds_of_match[] = {
  	{ .compatible = "brcm,bcm6358-leds", },
  	{ },
  };
01736f07c   Luis de Bethencourt   leds: bcm6358: Fi...
212
  MODULE_DEVICE_TABLE(of, bcm6358_leds_of_match);
589fca16c   Álvaro Fernández Rojas   leds: add BCM6358...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  
  static struct platform_driver bcm6358_leds_driver = {
  	.probe = bcm6358_leds_probe,
  	.driver = {
  		.name = "leds-bcm6358",
  		.of_match_table = bcm6358_leds_of_match,
  	},
  };
  
  module_platform_driver(bcm6358_leds_driver);
  
  MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
  MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
  MODULE_LICENSE("GPL v2");
  MODULE_ALIAS("platform:leds-bcm6358");