blob: 40ced261e3ce3a8ffe4350d325e123f9987c033d [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>
Marek Behúnbb42a5b2024-04-04 09:50:51 +02007#include <turris-omnia-mcu-interface.h>
Marek Behúnd38a2be2024-04-04 09:51:01 +02008#include <asm/byteorder.h>
Pali Roháreb96c0f2022-07-28 13:06:24 +02009#include <asm/gpio.h>
10#include <linux/log2.h>
11
Pali Roháreb96c0f2022-07-28 13:06:24 +020012struct turris_omnia_mcu_info {
Marek Behúnc0132152024-04-04 09:51:02 +020013 u32 features;
Pali Roháreb96c0f2022-07-28 13:06:24 +020014};
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ár265f9892022-09-22 13:25:13 +020039 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-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ár265f9892022-09-22 13:25:13 +020043 if (!(info->features & FEAT_PERIPH_MCU))
44 return -EINVAL;
Pali Roháreb96c0f2022-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únd38a2be2024-04-04 09:51:01 +020055 u32 val32;
56 u16 val16;
Pali Roháreb96c0f2022-07-28 13:06:24 +020057 int ret;
58
59 switch (offset) {
60 /* bank 0 */
61 case 0 ... 15:
Marek Behúnd38a2be2024-04-04 09:51:01 +020062 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, (void *)&val16,
63 sizeof(val16));
Pali Roháreb96c0f2022-07-28 13:06:24 +020064 if (ret)
65 return ret;
Marek Behúnd38a2be2024-04-04 09:51:01 +020066
67 return !!(le16_to_cpu(val16) & BIT(offset));
Pali Roháreb96c0f2022-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únd38a2be2024-04-04 09:51:01 +020073
74 ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, (void *)&val32,
75 sizeof(val32));
Pali Roháreb96c0f2022-07-28 13:06:24 +020076 if (ret)
77 return ret;
Marek Behúnd38a2be2024-04-04 09:51:01 +020078
79 return !!(le32_to_cpu(val32) & BIT(offset - 16));
Pali Roháreb96c0f2022-07-28 13:06:24 +020080
Pali Rohár265f9892022-09-22 13:25:13 +020081 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-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ár265f9892022-09-22 13:25:13 +020085 if (!(info->features & FEAT_PERIPH_MCU))
86 return -EINVAL;
Marek Behúnd38a2be2024-04-04 09:51:01 +020087
88 ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS,
89 (void *)&val16, sizeof(val16));
Pali Roháreb96c0f2022-07-28 13:06:24 +020090 if (ret)
91 return ret;
Marek Behúnd38a2be2024-04-04 09:51:01 +020092
93 return !!(le16_to_cpu(val16) & BIT(offset - 16 - 32));
Pali Roháreb96c0f2022-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únd38a2be2024-04-04 09:51:01 +0200103 u16 valmask16[2];
104 u8 valmask8[2];
Pali Roháreb96c0f2022-07-28 13:06:24 +0200105
106 switch (offset) {
107 /* bank 0 */
Pali Rohára93fa242022-08-01 12:11:13 +0200108 case 0 ... 15:
109 switch (offset) {
110 case ilog2(STS_USB30_PWRON):
Marek Behúnd38a2be2024-04-04 09:51:01 +0200111 valmask8[1] = CTL_USB30_PWRON;
Pali Rohára93fa242022-08-01 12:11:13 +0200112 break;
113 case ilog2(STS_USB31_PWRON):
Marek Behúnd38a2be2024-04-04 09:51:01 +0200114 valmask8[1] = CTL_USB31_PWRON;
Pali Rohára93fa242022-08-01 12:11:13 +0200115 break;
116 case ilog2(STS_ENABLE_4V5):
Marek Behúnd38a2be2024-04-04 09:51:01 +0200117 valmask8[1] = CTL_ENABLE_4V5;
Pali Rohára93fa242022-08-01 12:11:13 +0200118 break;
119 case ilog2(STS_BUTTON_MODE):
Marek Behúnd38a2be2024-04-04 09:51:01 +0200120 valmask8[1] = CTL_BUTTON_MODE;
Pali Rohára93fa242022-08-01 12:11:13 +0200121 break;
122 default:
123 return -EINVAL;
124 }
Marek Behúnd38a2be2024-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áreb96c0f2022-07-28 13:06:24 +0200130
Pali Rohár265f9892022-09-22 13:25:13 +0200131 /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
Pali Roháreb96c0f2022-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ár265f9892022-09-22 13:25:13 +0200135 if (!(info->features & FEAT_PERIPH_MCU))
136 return -EINVAL;
Marek Behúnd38a2be2024-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áreb96c0f2022-07-28 13:06:24 +0200143
144 default:
145 return -EINVAL;
146 }
Pali Roháreb96c0f2022-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únc0132152024-04-04 09:51:02 +0200231 u32 dword;
232 u16 word;
Pali Roháreb96c0f2022-07-28 13:06:24 +0200233 int ret;
234
Marek Behúnc0132152024-04-04 09:51:02 +0200235 ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, (void *)&word, sizeof(word));
Marek Behúnd38a2be2024-04-04 09:51:01 +0200236 if (ret < 0) {
237 printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n",
238 ret);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200239 return ret;
240 }
241
Marek Behúnc0132152024-04-04 09:51:02 +0200242 if (le16_to_cpu(word) & STS_FEATURES_SUPPORTED) {
243 /* try read 32-bit features */
244 ret = dm_i2c_read(dev, CMD_GET_FEATURES, (void *)&dword,
245 sizeof(dword));
Marek Behúnd38a2be2024-04-04 09:51:01 +0200246 if (ret < 0) {
Marek Behúnc0132152024-04-04 09:51:02 +0200247 /* try read 16-bit features */
248 ret = dm_i2c_read(dev, CMD_GET_FEATURES, (void *)&word,
249 sizeof(word));
250 if (ret < 0) {
251 printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n",
252 ret);
253 return ret;
254 }
255
256 info->features = le16_to_cpu(word);
257 } else {
258 info->features = le32_to_cpu(dword);
259 if (info->features & FEAT_FROM_BIT_16_INVALID)
260 info->features &= GENMASK(15, 0);
Pali Roháreb96c0f2022-07-28 13:06:24 +0200261 }
Pali Roháreb96c0f2022-07-28 13:06:24 +0200262 }
263
264 uc_priv->bank_name = "mcu_";
265
Pali Rohár265f9892022-09-22 13:25:13 +0200266 if ((info->features & FEAT_EXT_CMDS) && (info->features & FEAT_PERIPH_MCU))
Pali Roháreb96c0f2022-07-28 13:06:24 +0200267 uc_priv->gpio_count = 16 + 32 + 16;
Pali Rohár265f9892022-09-22 13:25:13 +0200268 else if (info->features & FEAT_EXT_CMDS)
269 uc_priv->gpio_count = 16 + 32;
Pali Roháreb96c0f2022-07-28 13:06:24 +0200270 else
271 uc_priv->gpio_count = 16;
272
273 return 0;
274}
275
276static const struct udevice_id turris_omnia_mcu_ids[] = {
277 { .compatible = "cznic,turris-omnia-mcu" },
278 { }
279};
280
281U_BOOT_DRIVER(turris_omnia_mcu) = {
282 .name = "turris-omnia-mcu",
283 .id = UCLASS_GPIO,
284 .ops = &turris_omnia_mcu_ops,
285 .probe = turris_omnia_mcu_probe,
286 .plat_auto = sizeof(struct turris_omnia_mcu_info),
287 .of_match = turris_omnia_mcu_ids,
288};