blob: b1373ab7426721c34f981e4e7a4ccb47d13d1aed [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +02002/*
3 * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +02004 */
5
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +02006#include <dm.h>
7#include <errno.h>
8#include <led.h>
Simon Glass0f2af882020-05-10 11:40:05 -06009#include <log.h>
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +020010#include <asm/io.h>
11#include <dm/lists.h>
Simon Glassdbd79542020-05-10 11:40:11 -060012#include <linux/delay.h>
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +020013
14#define LEDS_MAX 32
15#define LEDS_WAIT 100
16
17/* LED Mode register */
18#define LED_MODE_REG 0x0
19#define LED_MODE_OFF 0
20#define LED_MODE_ON 1
21#define LED_MODE_MASK 1
22
23/* LED Control register */
24#define LED_CTRL_REG 0x4
25#define LED_CTRL_CLK_MASK 0x3
26#define LED_CTRL_CLK_1 0
27#define LED_CTRL_CLK_2 1
28#define LED_CTRL_CLK_4 2
29#define LED_CTRL_CLK_8 3
30#define LED_CTRL_POL_SHIFT 2
31#define LED_CTRL_POL_MASK (1 << LED_CTRL_POL_SHIFT)
32#define LED_CTRL_BUSY_SHIFT 3
33#define LED_CTRL_BUSY_MASK (1 << LED_CTRL_BUSY_SHIFT)
34
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +020035struct bcm6358_led_priv {
36 void __iomem *regs;
37 uint8_t pin;
38 bool active_low;
39};
40
41static void bcm6358_led_busy(void __iomem *regs)
42{
43 while (readl_be(regs + LED_CTRL_REG) & LED_CTRL_BUSY_MASK)
44 udelay(LEDS_WAIT);
45}
46
47static unsigned long bcm6358_led_get_mode(struct bcm6358_led_priv *priv)
48{
49 bcm6358_led_busy(priv->regs);
50
51 return (readl_be(priv->regs + LED_MODE_REG) >> priv->pin) &
52 LED_MODE_MASK;
53}
54
55static int bcm6358_led_set_mode(struct bcm6358_led_priv *priv, uint8_t mode)
56{
57 bcm6358_led_busy(priv->regs);
58
59 clrsetbits_be32(priv->regs + LED_MODE_REG,
60 (LED_MODE_MASK << priv->pin),
61 (mode << priv->pin));
62
63 return 0;
64}
65
66static enum led_state_t bcm6358_led_get_state(struct udevice *dev)
67{
68 struct bcm6358_led_priv *priv = dev_get_priv(dev);
69 enum led_state_t state = LEDST_OFF;
70
71 switch (bcm6358_led_get_mode(priv)) {
72 case LED_MODE_OFF:
73 state = (priv->active_low ? LEDST_ON : LEDST_OFF);
74 break;
75 case LED_MODE_ON:
76 state = (priv->active_low ? LEDST_OFF : LEDST_ON);
77 break;
78 }
79
80 return state;
81}
82
83static int bcm6358_led_set_state(struct udevice *dev, enum led_state_t state)
84{
85 struct bcm6358_led_priv *priv = dev_get_priv(dev);
86 unsigned long mode;
87
88 switch (state) {
89 case LEDST_OFF:
90 mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
91 break;
92 case LEDST_ON:
93 mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
94 break;
95 case LEDST_TOGGLE:
96 if (bcm6358_led_get_state(dev) == LEDST_OFF)
97 return bcm6358_led_set_state(dev, LEDST_ON);
98 else
99 return bcm6358_led_set_state(dev, LEDST_OFF);
100 break;
101 default:
102 return -ENOSYS;
103 }
104
105 return bcm6358_led_set_mode(priv, mode);
106}
107
108static const struct led_ops bcm6358_led_ops = {
109 .get_state = bcm6358_led_get_state,
110 .set_state = bcm6358_led_set_state,
111};
112
113static int bcm6358_led_probe(struct udevice *dev)
114{
Simon Glass71fa5b42020-12-03 16:55:18 -0700115 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200116
117 /* Top-level LED node */
118 if (!uc_plat->label) {
119 void __iomem *regs;
120 unsigned int clk_div;
121 u32 set_bits = 0;
122
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100123 regs = dev_remap_addr(dev);
124 if (!regs)
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200125 return -EINVAL;
126
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100127 if (dev_read_bool(dev, "brcm,clk-dat-low"))
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200128 set_bits |= LED_CTRL_POL_MASK;
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100129 clk_div = dev_read_u32_default(dev, "brcm,clk-div",
130 LED_CTRL_CLK_1);
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200131 switch (clk_div) {
132 case 8:
133 set_bits |= LED_CTRL_CLK_8;
134 break;
135 case 4:
136 set_bits |= LED_CTRL_CLK_4;
137 break;
138 case 2:
139 set_bits |= LED_CTRL_CLK_2;
140 break;
141 default:
142 set_bits |= LED_CTRL_CLK_1;
143 break;
144 }
145
146 bcm6358_led_busy(regs);
147 clrsetbits_be32(regs + LED_CTRL_REG,
148 LED_CTRL_POL_MASK | LED_CTRL_CLK_MASK,
149 set_bits);
150 } else {
151 struct bcm6358_led_priv *priv = dev_get_priv(dev);
152 unsigned int pin;
153
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100154 priv->regs = dev_remap_addr(dev);
155 if (!priv->regs)
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200156 return -EINVAL;
157
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100158 pin = dev_read_u32_default(dev, "reg", LEDS_MAX);
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200159 if (pin >= LEDS_MAX)
160 return -EINVAL;
161
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200162 priv->pin = pin;
163
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100164 if (dev_read_bool(dev, "active-low"))
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200165 priv->active_low = true;
166 }
167
168 return 0;
169}
170
171static int bcm6358_led_bind(struct udevice *parent)
172{
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100173 ofnode node;
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200174
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100175 dev_for_each_subnode(node, parent) {
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200176 struct udevice *dev;
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200177 int ret;
178
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200179 ret = device_bind_driver_to_node(parent, "bcm6358-led",
Álvaro Fernández Rojas9b8d1f92018-03-22 19:39:33 +0100180 ofnode_get_name(node),
181 node, &dev);
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200182 if (ret)
183 return ret;
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200184 }
185
186 return 0;
187}
188
189static const struct udevice_id bcm6358_led_ids[] = {
190 { .compatible = "brcm,bcm6358-leds" },
191 { /* sentinel */ }
192};
193
194U_BOOT_DRIVER(bcm6358_led) = {
195 .name = "bcm6358-led",
196 .id = UCLASS_LED,
197 .of_match = bcm6358_led_ids,
198 .bind = bcm6358_led_bind,
199 .probe = bcm6358_led_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700200 .priv_auto = sizeof(struct bcm6358_led_priv),
Álvaro Fernández Rojas9bcd3552017-05-07 20:11:30 +0200201 .ops = &bcm6358_led_ops,
202};