blob: 986ccde6bc7032a95090eb6686eef34ba4ef0ce7 [file] [log] [blame]
Pali Roháreb96c0f2022-07-28 13:06:24 +02001// SPDX-License-Identifier: GPL-2.0+
2// (C) 2022 Pali Rohár <pali@kernel.org>
3
4#include <common.h>
5#include <dm.h>
6#include <i2c.h>
7#include <asm/gpio.h>
8#include <linux/log2.h>
9
10enum commands_e {
11 CMD_GET_STATUS_WORD = 0x01,
12 CMD_GENERAL_CONTROL = 0x02,
13
14 /* available if STS_FEATURES_SUPPORTED bit set in status word */
15 CMD_GET_FEATURES = 0x10,
16
17 /* available if FEAT_EXT_CMDS bit is set in features */
18 CMD_GET_EXT_STATUS_DWORD = 0x11,
19 CMD_EXT_CONTROL = 0x12,
20 CMD_GET_EXT_CONTROL_STATUS = 0x13,
21};
22
23/* CMD_GET_STATUS_WORD */
24enum sts_word_e {
25 STS_MCU_TYPE_MASK = GENMASK(1, 0),
26 STS_MCU_TYPE_STM32 = 0,
27 STS_MCU_TYPE_GD32 = 1,
28 STS_MCU_TYPE_MKL = 2,
29 STS_FEATURES_SUPPORTED = BIT(2),
30 STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
31 STS_CARD_DET = BIT(4),
32 STS_MSATA_IND = BIT(5),
33 STS_USB30_OVC = BIT(6),
34 STS_USB31_OVC = BIT(7),
35 STS_USB30_PWRON = BIT(8),
36 STS_USB31_PWRON = BIT(9),
37 STS_ENABLE_4V5 = BIT(10),
38 STS_BUTTON_MODE = BIT(11),
39 STS_BUTTON_PRESSED = BIT(12),
40 STS_BUTTON_COUNTER_MASK = GENMASK(15, 13)
41};
42
43/* CMD_GENERAL_CONTROL */
44enum ctl_byte_e {
45 CTL_LIGHT_RST = BIT(0),
46 CTL_HARD_RST = BIT(1),
47 /*CTL_RESERVED = BIT(2),*/
48 CTL_USB30_PWRON = BIT(3),
49 CTL_USB31_PWRON = BIT(4),
50 CTL_ENABLE_4V5 = BIT(5),
51 CTL_BUTTON_MODE = BIT(6),
52 CTL_BOOTLOADER = BIT(7)
53};
54
55/* CMD_GET_FEATURES */
56enum features_e {
57 FEAT_EXT_CMDS = BIT(1),
58};
59
60struct turris_omnia_mcu_info {
61 u16 features;
62};
63
64static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
65{
66 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
67
68 switch (offset) {
69 /* bank 0 */
70 case 0 ... 15:
71 switch (offset) {
72 case ilog2(STS_USB30_PWRON):
73 case ilog2(STS_USB31_PWRON):
74 case ilog2(STS_ENABLE_4V5):
75 case ilog2(STS_BUTTON_MODE):
76 return GPIOF_OUTPUT;
77 default:
78 return GPIOF_INPUT;
79 }
80
81 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
82 case (16 + 0) ... (16 + 31):
83 if (!(info->features & FEAT_EXT_CMDS))
84 return -EINVAL;
85 return GPIOF_INPUT;
86
87 /* bank 2 - supported only when FEAT_EXT_CMDS is set */
88 case (16 + 32 + 0) ... (16 + 32 + 15):
89 if (!(info->features & FEAT_EXT_CMDS))
90 return -EINVAL;
91 return GPIOF_OUTPUT;
92
93 default:
94 return -EINVAL;
95 }
96}
97
98static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
99{
100 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
101 u8 val16[2];
102 u8 val32[4];
103 int ret;
104
105 switch (offset) {
106 /* bank 0 */
107 case 0 ... 15:
108 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
109 if (ret)
110 return ret;
111 return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
112
113 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
114 case (16 + 0) ... (16 + 31):
115 if (!(info->features & FEAT_EXT_CMDS))
116 return -EINVAL;
117 ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
118 if (ret)
119 return ret;
120 return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
121 ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
122
123 /* bank 2 - supported only when FEAT_EXT_CMDS is set */
124 case (16 + 32 + 0) ... (16 + 32 + 15):
125 if (!(info->features & FEAT_EXT_CMDS))
126 return -EINVAL;
127 ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
128 if (ret)
129 return ret;
130 return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
131
132 default:
133 return -EINVAL;
134 }
135}
136
137static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
138{
139 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
Pali Rohára93fa242022-08-01 12:11:13 +0200140 u8 val16[2];
141 u8 val32[4];
Pali Roháreb96c0f2022-07-28 13:06:24 +0200142
143 switch (offset) {
144 /* bank 0 */
Pali Rohára93fa242022-08-01 12:11:13 +0200145 case 0 ... 15:
146 switch (offset) {
147 case ilog2(STS_USB30_PWRON):
148 val16[1] = CTL_USB30_PWRON;
149 break;
150 case ilog2(STS_USB31_PWRON):
151 val16[1] = CTL_USB31_PWRON;
152 break;
153 case ilog2(STS_ENABLE_4V5):
154 val16[1] = CTL_ENABLE_4V5;
155 break;
156 case ilog2(STS_BUTTON_MODE):
157 val16[1] = CTL_BUTTON_MODE;
158 break;
159 default:
160 return -EINVAL;
161 }
162 val16[0] = value ? val16[1] : 0;
163 return dm_i2c_write(dev, CMD_GENERAL_CONTROL, val16, sizeof(val16));
Pali Roháreb96c0f2022-07-28 13:06:24 +0200164
165 /* bank 2 - supported only when FEAT_EXT_CMDS is set */
166 case (16 + 32 + 0) ... (16 + 32 + 15):
167 if (!(info->features & FEAT_EXT_CMDS))
168 return -EINVAL;
Pali Rohára93fa242022-08-01 12:11:13 +0200169 val32[3] = BIT(offset - 16 - 32) >> 8;
170 val32[2] = BIT(offset - 16 - 32) & 0xff;
171 val32[1] = value ? val32[3] : 0;
172 val32[0] = value ? val32[2] : 0;
173 return dm_i2c_write(dev, CMD_EXT_CONTROL, val32, sizeof(val32));
Pali Roháreb96c0f2022-07-28 13:06:24 +0200174
175 default:
176 return -EINVAL;
177 }
Pali Roháreb96c0f2022-07-28 13:06:24 +0200178}
179
180static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
181{
182 int ret;
183
184 ret = turris_omnia_mcu_get_function(dev, offset);
185 if (ret < 0)
186 return ret;
187 else if (ret != GPIOF_INPUT)
188 return -EOPNOTSUPP;
189
190 return 0;
191}
192
193static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
194{
195 int ret;
196
197 ret = turris_omnia_mcu_get_function(dev, offset);
198 if (ret < 0)
199 return ret;
200 else if (ret != GPIOF_OUTPUT)
201 return -EOPNOTSUPP;
202
203 return turris_omnia_mcu_set_value(dev, offset, value);
204}
205
206static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
207 struct ofnode_phandle_args *args)
208{
209 uint bank, gpio, flags, offset;
210 int ret;
211
212 if (args->args_count != 3)
213 return -EINVAL;
214
215 bank = args->args[0];
216 gpio = args->args[1];
217 flags = args->args[2];
218
219 switch (bank) {
220 case 0:
221 if (gpio >= 16)
222 return -EINVAL;
223 offset = gpio;
224 break;
225 case 1:
226 if (gpio >= 32)
227 return -EINVAL;
228 offset = 16 + gpio;
229 break;
230 case 2:
231 if (gpio >= 16)
232 return -EINVAL;
233 offset = 16 + 32 + gpio;
234 break;
235 default:
236 return -EINVAL;
237 }
238
239 ret = turris_omnia_mcu_get_function(dev, offset);
240 if (ret < 0)
241 return ret;
242
243 desc->offset = offset;
244 desc->flags = gpio_flags_xlate(flags);
245
246 return 0;
247}
248
249static const struct dm_gpio_ops turris_omnia_mcu_ops = {
250 .direction_input = turris_omnia_mcu_direction_input,
251 .direction_output = turris_omnia_mcu_direction_output,
252 .get_value = turris_omnia_mcu_get_value,
253 .set_value = turris_omnia_mcu_set_value,
254 .get_function = turris_omnia_mcu_get_function,
255 .xlate = turris_omnia_mcu_xlate,
256};
257
258static int turris_omnia_mcu_probe(struct udevice *dev)
259{
260 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
261 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
262 u16 status;
263 u8 val[2];
264 int ret;
265
266 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
267 if (ret) {
268 printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
269 return ret;
270 }
271
272 status = ((u16)val[1] << 8) | val[0];
273
274 if (status & STS_FEATURES_SUPPORTED) {
275 ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
276 if (ret) {
277 printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
278 return ret;
279 }
280 info->features = ((u16)val[1] << 8) | val[0];
281 }
282
283 uc_priv->bank_name = "mcu_";
284
285 if (info->features & FEAT_EXT_CMDS)
286 uc_priv->gpio_count = 16 + 32 + 16;
287 else
288 uc_priv->gpio_count = 16;
289
290 return 0;
291}
292
293static const struct udevice_id turris_omnia_mcu_ids[] = {
294 { .compatible = "cznic,turris-omnia-mcu" },
295 { }
296};
297
298U_BOOT_DRIVER(turris_omnia_mcu) = {
299 .name = "turris-omnia-mcu",
300 .id = UCLASS_GPIO,
301 .ops = &turris_omnia_mcu_ops,
302 .probe = turris_omnia_mcu_probe,
303 .plat_auto = sizeof(struct turris_omnia_mcu_info),
304 .of_match = turris_omnia_mcu_ids,
305};