blob: c441d07d69daca24a4330d3d42cd467217e09fc6 [file] [log] [blame]
Pali Rohár5e4d24c2022-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>
Marek Behúnb5551482024-04-04 09:50:51 +02007#include <turris-omnia-mcu-interface.h>
Marek Behúnc07206f2024-04-04 09:51:01 +02008#include <asm/byteorder.h>
Pali Rohár5e4d24c2022-07-28 13:06:24 +02009#include <asm/gpio.h>
10#include <linux/log2.h>
11
Pali Rohár5e4d24c2022-07-28 13:06:24 +020012struct turris_omnia_mcu_info {
13 u16 features;
14};
15
16static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
17{
18 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
19
20 switch (offset) {
21 /* bank 0 */
22 case 0 ... 15:
23 switch (offset) {
24 case ilog2(STS_USB30_PWRON):
25 case ilog2(STS_USB31_PWRON):
26 case ilog2(STS_ENABLE_4V5):
27 case ilog2(STS_BUTTON_MODE):
28 return GPIOF_OUTPUT;
29 default:
30 return GPIOF_INPUT;
31 }
32
33 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
34 case (16 + 0) ... (16 + 31):
35 if (!(info->features & FEAT_EXT_CMDS))
36 return -EINVAL;
37 return GPIOF_INPUT;
38
Pali Rohár54bcd842022-09-22 13:25:13 +020039 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Rohár5e4d24c2022-07-28 13:06:24 +020040 case (16 + 32 + 0) ... (16 + 32 + 15):
41 if (!(info->features & FEAT_EXT_CMDS))
42 return -EINVAL;
Pali Rohár54bcd842022-09-22 13:25:13 +020043 if (!(info->features & FEAT_PERIPH_MCU))
44 return -EINVAL;
Pali Rohár5e4d24c2022-07-28 13:06:24 +020045 return GPIOF_OUTPUT;
46
47 default:
48 return -EINVAL;
49 }
50}
51
52static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
53{
54 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
Marek Behúnc07206f2024-04-04 09:51:01 +020055 u32 val32;
56 u16 val16;
Pali Rohár5e4d24c2022-07-28 13:06:24 +020057 int ret;
58
59 switch (offset) {
60 /* bank 0 */
61 case 0 ... 15:
Marek Behúnc07206f2024-04-04 09:51:01 +020062 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, (void *)&val16,
63 sizeof(val16));
Pali Rohár5e4d24c2022-07-28 13:06:24 +020064 if (ret)
65 return ret;
Marek Behúnc07206f2024-04-04 09:51:01 +020066
67 return !!(le16_to_cpu(val16) & BIT(offset));
Pali Rohár5e4d24c2022-07-28 13:06:24 +020068
69 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
70 case (16 + 0) ... (16 + 31):
71 if (!(info->features & FEAT_EXT_CMDS))
72 return -EINVAL;
Marek Behúnc07206f2024-04-04 09:51:01 +020073
74 ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, (void *)&val32,
75 sizeof(val32));
Pali Rohár5e4d24c2022-07-28 13:06:24 +020076 if (ret)
77 return ret;
Marek Behúnc07206f2024-04-04 09:51:01 +020078
79 return !!(le32_to_cpu(val32) & BIT(offset - 16));
Pali Rohár5e4d24c2022-07-28 13:06:24 +020080
Pali Rohár54bcd842022-09-22 13:25:13 +020081 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Rohár5e4d24c2022-07-28 13:06:24 +020082 case (16 + 32 + 0) ... (16 + 32 + 15):
83 if (!(info->features & FEAT_EXT_CMDS))
84 return -EINVAL;
Pali Rohár54bcd842022-09-22 13:25:13 +020085 if (!(info->features & FEAT_PERIPH_MCU))
86 return -EINVAL;
Marek Behúnc07206f2024-04-04 09:51:01 +020087
88 ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS,
89 (void *)&val16, sizeof(val16));
Pali Rohár5e4d24c2022-07-28 13:06:24 +020090 if (ret)
91 return ret;
Marek Behúnc07206f2024-04-04 09:51:01 +020092
93 return !!(le16_to_cpu(val16) & BIT(offset - 16 - 32));
Pali Rohár5e4d24c2022-07-28 13:06:24 +020094
95 default:
96 return -EINVAL;
97 }
98}
99
100static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
101{
102 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
Marek Behúnc07206f2024-04-04 09:51:01 +0200103 u16 valmask16[2];
104 u8 valmask8[2];
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200105
106 switch (offset) {
107 /* bank 0 */
Pali Rohárc9593742022-08-01 12:11:13 +0200108 case 0 ... 15:
109 switch (offset) {
110 case ilog2(STS_USB30_PWRON):
Marek Behúnc07206f2024-04-04 09:51:01 +0200111 valmask8[1] = CTL_USB30_PWRON;
Pali Rohárc9593742022-08-01 12:11:13 +0200112 break;
113 case ilog2(STS_USB31_PWRON):
Marek Behúnc07206f2024-04-04 09:51:01 +0200114 valmask8[1] = CTL_USB31_PWRON;
Pali Rohárc9593742022-08-01 12:11:13 +0200115 break;
116 case ilog2(STS_ENABLE_4V5):
Marek Behúnc07206f2024-04-04 09:51:01 +0200117 valmask8[1] = CTL_ENABLE_4V5;
Pali Rohárc9593742022-08-01 12:11:13 +0200118 break;
119 case ilog2(STS_BUTTON_MODE):
Marek Behúnc07206f2024-04-04 09:51:01 +0200120 valmask8[1] = CTL_BUTTON_MODE;
Pali Rohárc9593742022-08-01 12:11:13 +0200121 break;
122 default:
123 return -EINVAL;
124 }
Marek Behúnc07206f2024-04-04 09:51:01 +0200125
126 valmask8[0] = value ? valmask8[1] : 0;
127
128 return dm_i2c_write(dev, CMD_GENERAL_CONTROL, valmask8,
129 sizeof(valmask8));
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200130
Pali Rohár54bcd842022-09-22 13:25:13 +0200131 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200132 case (16 + 32 + 0) ... (16 + 32 + 15):
133 if (!(info->features & FEAT_EXT_CMDS))
134 return -EINVAL;
Pali Rohár54bcd842022-09-22 13:25:13 +0200135 if (!(info->features & FEAT_PERIPH_MCU))
136 return -EINVAL;
Marek Behúnc07206f2024-04-04 09:51:01 +0200137
138 valmask16[1] = cpu_to_le16(BIT(offset - 16 - 32));
139 valmask16[0] = value ? valmask16[1] : 0;
140
141 return dm_i2c_write(dev, CMD_EXT_CONTROL, (void *)valmask16,
142 sizeof(valmask16));
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200143
144 default:
145 return -EINVAL;
146 }
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200147}
148
149static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
150{
151 int ret;
152
153 ret = turris_omnia_mcu_get_function(dev, offset);
154 if (ret < 0)
155 return ret;
156 else if (ret != GPIOF_INPUT)
157 return -EOPNOTSUPP;
158
159 return 0;
160}
161
162static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
163{
164 int ret;
165
166 ret = turris_omnia_mcu_get_function(dev, offset);
167 if (ret < 0)
168 return ret;
169 else if (ret != GPIOF_OUTPUT)
170 return -EOPNOTSUPP;
171
172 return turris_omnia_mcu_set_value(dev, offset, value);
173}
174
175static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
176 struct ofnode_phandle_args *args)
177{
178 uint bank, gpio, flags, offset;
179 int ret;
180
181 if (args->args_count != 3)
182 return -EINVAL;
183
184 bank = args->args[0];
185 gpio = args->args[1];
186 flags = args->args[2];
187
188 switch (bank) {
189 case 0:
190 if (gpio >= 16)
191 return -EINVAL;
192 offset = gpio;
193 break;
194 case 1:
195 if (gpio >= 32)
196 return -EINVAL;
197 offset = 16 + gpio;
198 break;
199 case 2:
200 if (gpio >= 16)
201 return -EINVAL;
202 offset = 16 + 32 + gpio;
203 break;
204 default:
205 return -EINVAL;
206 }
207
208 ret = turris_omnia_mcu_get_function(dev, offset);
209 if (ret < 0)
210 return ret;
211
212 desc->offset = offset;
213 desc->flags = gpio_flags_xlate(flags);
214
215 return 0;
216}
217
218static const struct dm_gpio_ops turris_omnia_mcu_ops = {
219 .direction_input = turris_omnia_mcu_direction_input,
220 .direction_output = turris_omnia_mcu_direction_output,
221 .get_value = turris_omnia_mcu_get_value,
222 .set_value = turris_omnia_mcu_set_value,
223 .get_function = turris_omnia_mcu_get_function,
224 .xlate = turris_omnia_mcu_xlate,
225};
226
227static int turris_omnia_mcu_probe(struct udevice *dev)
228{
229 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
230 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
Marek Behúnc07206f2024-04-04 09:51:01 +0200231 u16 val;
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200232 int ret;
233
Marek Behúnc07206f2024-04-04 09:51:01 +0200234 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, (void *)&val, sizeof(val));
235 if (ret < 0) {
236 printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n",
237 ret);
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200238 return ret;
239 }
240
Marek Behúnc07206f2024-04-04 09:51:01 +0200241 if (le16_to_cpu(val) & STS_FEATURES_SUPPORTED) {
242 ret = dm_i2c_read(dev, CMD_GET_FEATURES, (void *)&val,
243 sizeof(val));
244 if (ret < 0) {
245 printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n",
246 ret);
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200247 return ret;
248 }
Marek Behúnc07206f2024-04-04 09:51:01 +0200249 info->features = le16_to_cpu(val);
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200250 }
251
252 uc_priv->bank_name = "mcu_";
253
Pali Rohár54bcd842022-09-22 13:25:13 +0200254 if ((info->features & FEAT_EXT_CMDS) && (info->features & FEAT_PERIPH_MCU))
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200255 uc_priv->gpio_count = 16 + 32 + 16;
Pali Rohár54bcd842022-09-22 13:25:13 +0200256 else if (info->features & FEAT_EXT_CMDS)
257 uc_priv->gpio_count = 16 + 32;
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200258 else
259 uc_priv->gpio_count = 16;
260
261 return 0;
262}
263
264static const struct udevice_id turris_omnia_mcu_ids[] = {
265 { .compatible = "cznic,turris-omnia-mcu" },
266 { }
267};
268
269U_BOOT_DRIVER(turris_omnia_mcu) = {
270 .name = "turris-omnia-mcu",
271 .id = UCLASS_GPIO,
272 .ops = &turris_omnia_mcu_ops,
273 .probe = turris_omnia_mcu_probe,
274 .plat_auto = sizeof(struct turris_omnia_mcu_info),
275 .of_match = turris_omnia_mcu_ids,
276};