blob: 01b86b756e14cafbc78805246c2b2ae2fe35688a [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +02002/*
3 * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +02004 */
5
6#include <common.h>
7#include <dm.h>
8#include <errno.h>
9#include <led.h>
10#include <asm/io.h>
11#include <dm/lists.h>
12
13#define LEDS_MAX 32
14#define LEDS_WAIT 100
15
16/* LED Mode register */
17#define LED_MODE_REG 0x0
18#define LED_MODE_OFF 0
19#define LED_MODE_ON 1
20#define LED_MODE_MASK 1
21
22/* LED Control register */
23#define LED_CTRL_REG 0x4
24#define LED_CTRL_CLK_MASK 0x3
25#define LED_CTRL_CLK_1 0
26#define LED_CTRL_CLK_2 1
27#define LED_CTRL_CLK_4 2
28#define LED_CTRL_CLK_8 3
29#define LED_CTRL_POL_SHIFT 2
30#define LED_CTRL_POL_MASK (1 << LED_CTRL_POL_SHIFT)
31#define LED_CTRL_BUSY_SHIFT 3
32#define LED_CTRL_BUSY_MASK (1 << LED_CTRL_BUSY_SHIFT)
33
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +020034struct bcm6358_led_priv {
35 void __iomem *regs;
36 uint8_t pin;
37 bool active_low;
38};
39
40static void bcm6358_led_busy(void __iomem *regs)
41{
42 while (readl_be(regs + LED_CTRL_REG) & LED_CTRL_BUSY_MASK)
43 udelay(LEDS_WAIT);
44}
45
46static unsigned long bcm6358_led_get_mode(struct bcm6358_led_priv *priv)
47{
48 bcm6358_led_busy(priv->regs);
49
50 return (readl_be(priv->regs + LED_MODE_REG) >> priv->pin) &
51 LED_MODE_MASK;
52}
53
54static int bcm6358_led_set_mode(struct bcm6358_led_priv *priv, uint8_t mode)
55{
56 bcm6358_led_busy(priv->regs);
57
58 clrsetbits_be32(priv->regs + LED_MODE_REG,
59 (LED_MODE_MASK << priv->pin),
60 (mode << priv->pin));
61
62 return 0;
63}
64
65static enum led_state_t bcm6358_led_get_state(struct udevice *dev)
66{
67 struct bcm6358_led_priv *priv = dev_get_priv(dev);
68 enum led_state_t state = LEDST_OFF;
69
70 switch (bcm6358_led_get_mode(priv)) {
71 case LED_MODE_OFF:
72 state = (priv->active_low ? LEDST_ON : LEDST_OFF);
73 break;
74 case LED_MODE_ON:
75 state = (priv->active_low ? LEDST_OFF : LEDST_ON);
76 break;
77 }
78
79 return state;
80}
81
82static int bcm6358_led_set_state(struct udevice *dev, enum led_state_t state)
83{
84 struct bcm6358_led_priv *priv = dev_get_priv(dev);
85 unsigned long mode;
86
87 switch (state) {
88 case LEDST_OFF:
89 mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
90 break;
91 case LEDST_ON:
92 mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
93 break;
94 case LEDST_TOGGLE:
95 if (bcm6358_led_get_state(dev) == LEDST_OFF)
96 return bcm6358_led_set_state(dev, LEDST_ON);
97 else
98 return bcm6358_led_set_state(dev, LEDST_OFF);
99 break;
100 default:
101 return -ENOSYS;
102 }
103
104 return bcm6358_led_set_mode(priv, mode);
105}
106
107static const struct led_ops bcm6358_led_ops = {
108 .get_state = bcm6358_led_get_state,
109 .set_state = bcm6358_led_set_state,
110};
111
112static int bcm6358_led_probe(struct udevice *dev)
113{
114 struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200115
116 /* Top-level LED node */
117 if (!uc_plat->label) {
118 void __iomem *regs;
119 unsigned int clk_div;
120 u32 set_bits = 0;
121
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100122 regs = dev_remap_addr(dev);
123 if (!regs)
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200124 return -EINVAL;
125
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100126 if (dev_read_bool(dev, "brcm,clk-dat-low"))
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200127 set_bits |= LED_CTRL_POL_MASK;
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100128 clk_div = dev_read_u32_default(dev, "brcm,clk-div",
129 LED_CTRL_CLK_1);
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200130 switch (clk_div) {
131 case 8:
132 set_bits |= LED_CTRL_CLK_8;
133 break;
134 case 4:
135 set_bits |= LED_CTRL_CLK_4;
136 break;
137 case 2:
138 set_bits |= LED_CTRL_CLK_2;
139 break;
140 default:
141 set_bits |= LED_CTRL_CLK_1;
142 break;
143 }
144
145 bcm6358_led_busy(regs);
146 clrsetbits_be32(regs + LED_CTRL_REG,
147 LED_CTRL_POL_MASK | LED_CTRL_CLK_MASK,
148 set_bits);
149 } else {
150 struct bcm6358_led_priv *priv = dev_get_priv(dev);
151 unsigned int pin;
152
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100153 priv->regs = dev_remap_addr(dev);
154 if (!priv->regs)
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200155 return -EINVAL;
156
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100157 pin = dev_read_u32_default(dev, "reg", LEDS_MAX);
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200158 if (pin >= LEDS_MAX)
159 return -EINVAL;
160
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200161 priv->pin = pin;
162
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100163 if (dev_read_bool(dev, "active-low"))
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200164 priv->active_low = true;
165 }
166
167 return 0;
168}
169
170static int bcm6358_led_bind(struct udevice *parent)
171{
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100172 ofnode node;
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200173
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100174 dev_for_each_subnode(node, parent) {
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200175 struct led_uc_plat *uc_plat;
176 struct udevice *dev;
177 const char *label;
178 int ret;
179
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100180 label = ofnode_read_string(node, "label");
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200181 if (!label) {
182 debug("%s: node %s has no label\n", __func__,
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100183 ofnode_get_name(node));
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200184 return -EINVAL;
185 }
186
187 ret = device_bind_driver_to_node(parent, "bcm6358-led",
Álvaro Fernández Rojasa15c16f2018-03-22 19:39:33 +0100188 ofnode_get_name(node),
189 node, &dev);
Álvaro Fernández Rojas632889c2017-05-07 20:11:30 +0200190 if (ret)
191 return ret;
192
193 uc_plat = dev_get_uclass_platdata(dev);
194 uc_plat->label = label;
195 }
196
197 return 0;
198}
199
200static const struct udevice_id bcm6358_led_ids[] = {
201 { .compatible = "brcm,bcm6358-leds" },
202 { /* sentinel */ }
203};
204
205U_BOOT_DRIVER(bcm6358_led) = {
206 .name = "bcm6358-led",
207 .id = UCLASS_LED,
208 .of_match = bcm6358_led_ids,
209 .bind = bcm6358_led_bind,
210 .probe = bcm6358_led_probe,
211 .priv_auto_alloc_size = sizeof(struct bcm6358_led_priv),
212 .ops = &bcm6358_led_ops,
213};