xref: /openbmc/u-boot/drivers/led/led_bcm6358.c (revision fabbeb33)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
4  */
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 
34 struct bcm6358_led_priv {
35 	void __iomem *regs;
36 	uint8_t pin;
37 	bool active_low;
38 };
39 
40 static 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 
46 static 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 
54 static 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 
65 static 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 
82 static 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 
107 static const struct led_ops bcm6358_led_ops = {
108 	.get_state = bcm6358_led_get_state,
109 	.set_state = bcm6358_led_set_state,
110 };
111 
112 static int bcm6358_led_probe(struct udevice *dev)
113 {
114 	struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
115 
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 
122 		regs = dev_remap_addr(dev);
123 		if (!regs)
124 			return -EINVAL;
125 
126 		if (dev_read_bool(dev, "brcm,clk-dat-low"))
127 			set_bits |= LED_CTRL_POL_MASK;
128 		clk_div = dev_read_u32_default(dev, "brcm,clk-div",
129 					       LED_CTRL_CLK_1);
130 		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 
153 		priv->regs = dev_remap_addr(dev);
154 		if (!priv->regs)
155 			return -EINVAL;
156 
157 		pin = dev_read_u32_default(dev, "reg", LEDS_MAX);
158 		if (pin >= LEDS_MAX)
159 			return -EINVAL;
160 
161 		priv->pin = pin;
162 
163 		if (dev_read_bool(dev, "active-low"))
164 			priv->active_low = true;
165 	}
166 
167 	return 0;
168 }
169 
170 static int bcm6358_led_bind(struct udevice *parent)
171 {
172 	ofnode node;
173 
174 	dev_for_each_subnode(node, parent) {
175 		struct led_uc_plat *uc_plat;
176 		struct udevice *dev;
177 		const char *label;
178 		int ret;
179 
180 		label = ofnode_read_string(node, "label");
181 		if (!label) {
182 			debug("%s: node %s has no label\n", __func__,
183 			      ofnode_get_name(node));
184 			return -EINVAL;
185 		}
186 
187 		ret = device_bind_driver_to_node(parent, "bcm6358-led",
188 						 ofnode_get_name(node),
189 						 node, &dev);
190 		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 
200 static const struct udevice_id bcm6358_led_ids[] = {
201 	{ .compatible = "brcm,bcm6358-leds" },
202 	{ /* sentinel */ }
203 };
204 
205 U_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 };
214