blob: 2d2bf2d1dd6916125dd73b3da29b02e45e5fbf9f [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,
Pali Rohár265f9892022-09-22 13:25:13 +020019
20 /* available if FEAT_EXT_CMDS and FEAT_PERIPH_MCU bits are set in featurs */
Pali Roháreb96c0f2022-07-28 13:06:24 +020021 CMD_EXT_CONTROL = 0x12,
22 CMD_GET_EXT_CONTROL_STATUS = 0x13,
23};
24
25/* CMD_GET_STATUS_WORD */
26enum sts_word_e {
27 STS_MCU_TYPE_MASK = GENMASK(1, 0),
28 STS_MCU_TYPE_STM32 = 0,
29 STS_MCU_TYPE_GD32 = 1,
30 STS_MCU_TYPE_MKL = 2,
31 STS_FEATURES_SUPPORTED = BIT(2),
32 STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
33 STS_CARD_DET = BIT(4),
34 STS_MSATA_IND = BIT(5),
35 STS_USB30_OVC = BIT(6),
36 STS_USB31_OVC = BIT(7),
37 STS_USB30_PWRON = BIT(8),
38 STS_USB31_PWRON = BIT(9),
39 STS_ENABLE_4V5 = BIT(10),
40 STS_BUTTON_MODE = BIT(11),
41 STS_BUTTON_PRESSED = BIT(12),
42 STS_BUTTON_COUNTER_MASK = GENMASK(15, 13)
43};
44
45/* CMD_GENERAL_CONTROL */
46enum ctl_byte_e {
47 CTL_LIGHT_RST = BIT(0),
48 CTL_HARD_RST = BIT(1),
49 /*CTL_RESERVED = BIT(2),*/
50 CTL_USB30_PWRON = BIT(3),
51 CTL_USB31_PWRON = BIT(4),
52 CTL_ENABLE_4V5 = BIT(5),
53 CTL_BUTTON_MODE = BIT(6),
54 CTL_BOOTLOADER = BIT(7)
55};
56
57/* CMD_GET_FEATURES */
58enum features_e {
Pali Rohár265f9892022-09-22 13:25:13 +020059 FEAT_PERIPH_MCU = BIT(0),
Pali Roháreb96c0f2022-07-28 13:06:24 +020060 FEAT_EXT_CMDS = BIT(1),
61};
62
63struct turris_omnia_mcu_info {
64 u16 features;
65};
66
67static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
68{
69 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
70
71 switch (offset) {
72 /* bank 0 */
73 case 0 ... 15:
74 switch (offset) {
75 case ilog2(STS_USB30_PWRON):
76 case ilog2(STS_USB31_PWRON):
77 case ilog2(STS_ENABLE_4V5):
78 case ilog2(STS_BUTTON_MODE):
79 return GPIOF_OUTPUT;
80 default:
81 return GPIOF_INPUT;
82 }
83
84 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
85 case (16 + 0) ... (16 + 31):
86 if (!(info->features & FEAT_EXT_CMDS))
87 return -EINVAL;
88 return GPIOF_INPUT;
89
Pali Rohár265f9892022-09-22 13:25:13 +020090 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-07-28 13:06:24 +020091 case (16 + 32 + 0) ... (16 + 32 + 15):
92 if (!(info->features & FEAT_EXT_CMDS))
93 return -EINVAL;
Pali Rohár265f9892022-09-22 13:25:13 +020094 if (!(info->features & FEAT_PERIPH_MCU))
95 return -EINVAL;
Pali Roháreb96c0f2022-07-28 13:06:24 +020096 return GPIOF_OUTPUT;
97
98 default:
99 return -EINVAL;
100 }
101}
102
103static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
104{
105 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
106 u8 val16[2];
107 u8 val32[4];
108 int ret;
109
110 switch (offset) {
111 /* bank 0 */
112 case 0 ... 15:
113 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
114 if (ret)
115 return ret;
116 return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
117
118 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
119 case (16 + 0) ... (16 + 31):
120 if (!(info->features & FEAT_EXT_CMDS))
121 return -EINVAL;
122 ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
123 if (ret)
124 return ret;
125 return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
126 ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
127
Pali Rohár265f9892022-09-22 13:25:13 +0200128 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-07-28 13:06:24 +0200129 case (16 + 32 + 0) ... (16 + 32 + 15):
130 if (!(info->features & FEAT_EXT_CMDS))
131 return -EINVAL;
Pali Rohár265f9892022-09-22 13:25:13 +0200132 if (!(info->features & FEAT_PERIPH_MCU))
133 return -EINVAL;
Pali Roháreb96c0f2022-07-28 13:06:24 +0200134 ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
135 if (ret)
136 return ret;
137 return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
138
139 default:
140 return -EINVAL;
141 }
142}
143
144static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
145{
146 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
Pali Rohára93fa242022-08-01 12:11:13 +0200147 u8 val16[2];
148 u8 val32[4];
Pali Roháreb96c0f2022-07-28 13:06:24 +0200149
150 switch (offset) {
151 /* bank 0 */
Pali Rohára93fa242022-08-01 12:11:13 +0200152 case 0 ... 15:
153 switch (offset) {
154 case ilog2(STS_USB30_PWRON):
155 val16[1] = CTL_USB30_PWRON;
156 break;
157 case ilog2(STS_USB31_PWRON):
158 val16[1] = CTL_USB31_PWRON;
159 break;
160 case ilog2(STS_ENABLE_4V5):
161 val16[1] = CTL_ENABLE_4V5;
162 break;
163 case ilog2(STS_BUTTON_MODE):
164 val16[1] = CTL_BUTTON_MODE;
165 break;
166 default:
167 return -EINVAL;
168 }
169 val16[0] = value ? val16[1] : 0;
170 return dm_i2c_write(dev, CMD_GENERAL_CONTROL, val16, sizeof(val16));
Pali Roháreb96c0f2022-07-28 13:06:24 +0200171
Pali Rohár265f9892022-09-22 13:25:13 +0200172 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-07-28 13:06:24 +0200173 case (16 + 32 + 0) ... (16 + 32 + 15):
174 if (!(info->features & FEAT_EXT_CMDS))
175 return -EINVAL;
Pali Rohár265f9892022-09-22 13:25:13 +0200176 if (!(info->features & FEAT_PERIPH_MCU))
177 return -EINVAL;
Pali Rohára93fa242022-08-01 12:11:13 +0200178 val32[3] = BIT(offset - 16 - 32) >> 8;
179 val32[2] = BIT(offset - 16 - 32) & 0xff;
180 val32[1] = value ? val32[3] : 0;
181 val32[0] = value ? val32[2] : 0;
182 return dm_i2c_write(dev, CMD_EXT_CONTROL, val32, sizeof(val32));
Pali Roháreb96c0f2022-07-28 13:06:24 +0200183
184 default:
185 return -EINVAL;
186 }
Pali Roháreb96c0f2022-07-28 13:06:24 +0200187}
188
189static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
190{
191 int ret;
192
193 ret = turris_omnia_mcu_get_function(dev, offset);
194 if (ret < 0)
195 return ret;
196 else if (ret != GPIOF_INPUT)
197 return -EOPNOTSUPP;
198
199 return 0;
200}
201
202static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
203{
204 int ret;
205
206 ret = turris_omnia_mcu_get_function(dev, offset);
207 if (ret < 0)
208 return ret;
209 else if (ret != GPIOF_OUTPUT)
210 return -EOPNOTSUPP;
211
212 return turris_omnia_mcu_set_value(dev, offset, value);
213}
214
215static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
216 struct ofnode_phandle_args *args)
217{
218 uint bank, gpio, flags, offset;
219 int ret;
220
221 if (args->args_count != 3)
222 return -EINVAL;
223
224 bank = args->args[0];
225 gpio = args->args[1];
226 flags = args->args[2];
227
228 switch (bank) {
229 case 0:
230 if (gpio >= 16)
231 return -EINVAL;
232 offset = gpio;
233 break;
234 case 1:
235 if (gpio >= 32)
236 return -EINVAL;
237 offset = 16 + gpio;
238 break;
239 case 2:
240 if (gpio >= 16)
241 return -EINVAL;
242 offset = 16 + 32 + gpio;
243 break;
244 default:
245 return -EINVAL;
246 }
247
248 ret = turris_omnia_mcu_get_function(dev, offset);
249 if (ret < 0)
250 return ret;
251
252 desc->offset = offset;
253 desc->flags = gpio_flags_xlate(flags);
254
255 return 0;
256}
257
258static const struct dm_gpio_ops turris_omnia_mcu_ops = {
259 .direction_input = turris_omnia_mcu_direction_input,
260 .direction_output = turris_omnia_mcu_direction_output,
261 .get_value = turris_omnia_mcu_get_value,
262 .set_value = turris_omnia_mcu_set_value,
263 .get_function = turris_omnia_mcu_get_function,
264 .xlate = turris_omnia_mcu_xlate,
265};
266
267static int turris_omnia_mcu_probe(struct udevice *dev)
268{
269 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
270 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
271 u16 status;
272 u8 val[2];
273 int ret;
274
275 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
276 if (ret) {
277 printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
278 return ret;
279 }
280
281 status = ((u16)val[1] << 8) | val[0];
282
283 if (status & STS_FEATURES_SUPPORTED) {
284 ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
285 if (ret) {
286 printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
287 return ret;
288 }
289 info->features = ((u16)val[1] << 8) | val[0];
290 }
291
292 uc_priv->bank_name = "mcu_";
293
Pali Rohár265f9892022-09-22 13:25:13 +0200294 if ((info->features & FEAT_EXT_CMDS) && (info->features & FEAT_PERIPH_MCU))
Pali Roháreb96c0f2022-07-28 13:06:24 +0200295 uc_priv->gpio_count = 16 + 32 + 16;
Pali Rohár265f9892022-09-22 13:25:13 +0200296 else if (info->features & FEAT_EXT_CMDS)
297 uc_priv->gpio_count = 16 + 32;
Pali Roháreb96c0f2022-07-28 13:06:24 +0200298 else
299 uc_priv->gpio_count = 16;
300
301 return 0;
302}
303
304static const struct udevice_id turris_omnia_mcu_ids[] = {
305 { .compatible = "cznic,turris-omnia-mcu" },
306 { }
307};
308
309U_BOOT_DRIVER(turris_omnia_mcu) = {
310 .name = "turris-omnia-mcu",
311 .id = UCLASS_GPIO,
312 .ops = &turris_omnia_mcu_ops,
313 .probe = turris_omnia_mcu_probe,
314 .plat_auto = sizeof(struct turris_omnia_mcu_info),
315 .of_match = turris_omnia_mcu_ids,
316};