blob: da9a6efe6d58bb4d84033af59ef0d2e61ea7e93d [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>
Pali Rohár5e4d24c2022-07-28 13:06:24 +02008#include <asm/gpio.h>
9#include <linux/log2.h>
10
Pali Rohár5e4d24c2022-07-28 13:06:24 +020011struct turris_omnia_mcu_info {
12 u16 features;
13};
14
15static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
16{
17 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
18
19 switch (offset) {
20 /* bank 0 */
21 case 0 ... 15:
22 switch (offset) {
23 case ilog2(STS_USB30_PWRON):
24 case ilog2(STS_USB31_PWRON):
25 case ilog2(STS_ENABLE_4V5):
26 case ilog2(STS_BUTTON_MODE):
27 return GPIOF_OUTPUT;
28 default:
29 return GPIOF_INPUT;
30 }
31
32 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
33 case (16 + 0) ... (16 + 31):
34 if (!(info->features & FEAT_EXT_CMDS))
35 return -EINVAL;
36 return GPIOF_INPUT;
37
Pali Rohár54bcd842022-09-22 13:25:13 +020038 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Rohár5e4d24c2022-07-28 13:06:24 +020039 case (16 + 32 + 0) ... (16 + 32 + 15):
40 if (!(info->features & FEAT_EXT_CMDS))
41 return -EINVAL;
Pali Rohár54bcd842022-09-22 13:25:13 +020042 if (!(info->features & FEAT_PERIPH_MCU))
43 return -EINVAL;
Pali Rohár5e4d24c2022-07-28 13:06:24 +020044 return GPIOF_OUTPUT;
45
46 default:
47 return -EINVAL;
48 }
49}
50
51static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
52{
53 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
54 u8 val16[2];
55 u8 val32[4];
56 int ret;
57
58 switch (offset) {
59 /* bank 0 */
60 case 0 ... 15:
61 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
62 if (ret)
63 return ret;
64 return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
65
66 /* bank 1 - supported only when FEAT_EXT_CMDS is set */
67 case (16 + 0) ... (16 + 31):
68 if (!(info->features & FEAT_EXT_CMDS))
69 return -EINVAL;
70 ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
71 if (ret)
72 return ret;
73 return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
74 ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
75
Pali Rohár54bcd842022-09-22 13:25:13 +020076 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Rohár5e4d24c2022-07-28 13:06:24 +020077 case (16 + 32 + 0) ... (16 + 32 + 15):
78 if (!(info->features & FEAT_EXT_CMDS))
79 return -EINVAL;
Pali Rohár54bcd842022-09-22 13:25:13 +020080 if (!(info->features & FEAT_PERIPH_MCU))
81 return -EINVAL;
Pali Rohár5e4d24c2022-07-28 13:06:24 +020082 ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
83 if (ret)
84 return ret;
85 return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
86
87 default:
88 return -EINVAL;
89 }
90}
91
92static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
93{
94 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
Pali Rohárc9593742022-08-01 12:11:13 +020095 u8 val16[2];
96 u8 val32[4];
Pali Rohár5e4d24c2022-07-28 13:06:24 +020097
98 switch (offset) {
99 /* bank 0 */
Pali Rohárc9593742022-08-01 12:11:13 +0200100 case 0 ... 15:
101 switch (offset) {
102 case ilog2(STS_USB30_PWRON):
103 val16[1] = CTL_USB30_PWRON;
104 break;
105 case ilog2(STS_USB31_PWRON):
106 val16[1] = CTL_USB31_PWRON;
107 break;
108 case ilog2(STS_ENABLE_4V5):
109 val16[1] = CTL_ENABLE_4V5;
110 break;
111 case ilog2(STS_BUTTON_MODE):
112 val16[1] = CTL_BUTTON_MODE;
113 break;
114 default:
115 return -EINVAL;
116 }
117 val16[0] = value ? val16[1] : 0;
118 return dm_i2c_write(dev, CMD_GENERAL_CONTROL, val16, sizeof(val16));
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200119
Pali Rohár54bcd842022-09-22 13:25:13 +0200120 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200121 case (16 + 32 + 0) ... (16 + 32 + 15):
122 if (!(info->features & FEAT_EXT_CMDS))
123 return -EINVAL;
Pali Rohár54bcd842022-09-22 13:25:13 +0200124 if (!(info->features & FEAT_PERIPH_MCU))
125 return -EINVAL;
Pali Rohárc9593742022-08-01 12:11:13 +0200126 val32[3] = BIT(offset - 16 - 32) >> 8;
127 val32[2] = BIT(offset - 16 - 32) & 0xff;
128 val32[1] = value ? val32[3] : 0;
129 val32[0] = value ? val32[2] : 0;
130 return dm_i2c_write(dev, CMD_EXT_CONTROL, val32, sizeof(val32));
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200131
132 default:
133 return -EINVAL;
134 }
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200135}
136
137static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
138{
139 int ret;
140
141 ret = turris_omnia_mcu_get_function(dev, offset);
142 if (ret < 0)
143 return ret;
144 else if (ret != GPIOF_INPUT)
145 return -EOPNOTSUPP;
146
147 return 0;
148}
149
150static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
151{
152 int ret;
153
154 ret = turris_omnia_mcu_get_function(dev, offset);
155 if (ret < 0)
156 return ret;
157 else if (ret != GPIOF_OUTPUT)
158 return -EOPNOTSUPP;
159
160 return turris_omnia_mcu_set_value(dev, offset, value);
161}
162
163static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
164 struct ofnode_phandle_args *args)
165{
166 uint bank, gpio, flags, offset;
167 int ret;
168
169 if (args->args_count != 3)
170 return -EINVAL;
171
172 bank = args->args[0];
173 gpio = args->args[1];
174 flags = args->args[2];
175
176 switch (bank) {
177 case 0:
178 if (gpio >= 16)
179 return -EINVAL;
180 offset = gpio;
181 break;
182 case 1:
183 if (gpio >= 32)
184 return -EINVAL;
185 offset = 16 + gpio;
186 break;
187 case 2:
188 if (gpio >= 16)
189 return -EINVAL;
190 offset = 16 + 32 + gpio;
191 break;
192 default:
193 return -EINVAL;
194 }
195
196 ret = turris_omnia_mcu_get_function(dev, offset);
197 if (ret < 0)
198 return ret;
199
200 desc->offset = offset;
201 desc->flags = gpio_flags_xlate(flags);
202
203 return 0;
204}
205
206static const struct dm_gpio_ops turris_omnia_mcu_ops = {
207 .direction_input = turris_omnia_mcu_direction_input,
208 .direction_output = turris_omnia_mcu_direction_output,
209 .get_value = turris_omnia_mcu_get_value,
210 .set_value = turris_omnia_mcu_set_value,
211 .get_function = turris_omnia_mcu_get_function,
212 .xlate = turris_omnia_mcu_xlate,
213};
214
215static int turris_omnia_mcu_probe(struct udevice *dev)
216{
217 struct turris_omnia_mcu_info *info = dev_get_plat(dev);
218 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
219 u16 status;
220 u8 val[2];
221 int ret;
222
223 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
224 if (ret) {
225 printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
226 return ret;
227 }
228
229 status = ((u16)val[1] << 8) | val[0];
230
231 if (status & STS_FEATURES_SUPPORTED) {
232 ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
233 if (ret) {
234 printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
235 return ret;
236 }
237 info->features = ((u16)val[1] << 8) | val[0];
238 }
239
240 uc_priv->bank_name = "mcu_";
241
Pali Rohár54bcd842022-09-22 13:25:13 +0200242 if ((info->features & FEAT_EXT_CMDS) && (info->features & FEAT_PERIPH_MCU))
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200243 uc_priv->gpio_count = 16 + 32 + 16;
Pali Rohár54bcd842022-09-22 13:25:13 +0200244 else if (info->features & FEAT_EXT_CMDS)
245 uc_priv->gpio_count = 16 + 32;
Pali Rohár5e4d24c2022-07-28 13:06:24 +0200246 else
247 uc_priv->gpio_count = 16;
248
249 return 0;
250}
251
252static const struct udevice_id turris_omnia_mcu_ids[] = {
253 { .compatible = "cznic,turris-omnia-mcu" },
254 { }
255};
256
257U_BOOT_DRIVER(turris_omnia_mcu) = {
258 .name = "turris-omnia-mcu",
259 .id = UCLASS_GPIO,
260 .ops = &turris_omnia_mcu_ops,
261 .probe = turris_omnia_mcu_probe,
262 .plat_auto = sizeof(struct turris_omnia_mcu_info),
263 .of_match = turris_omnia_mcu_ids,
264};