ARM: dts: npcm8xx: add npcm845 function node
[platform/kernel/u-boot.git] / drivers / gpio / turris_omnia_mcu.c
1 // 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
10 enum 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
20         /* available if FEAT_EXT_CMDS and FEAT_PERIPH_MCU bits are set in featurs */
21         CMD_EXT_CONTROL                     = 0x12,
22         CMD_GET_EXT_CONTROL_STATUS          = 0x13,
23 };
24
25 /* CMD_GET_STATUS_WORD */
26 enum 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 */
46 enum 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 */
58 enum features_e {
59         FEAT_PERIPH_MCU         = BIT(0),
60         FEAT_EXT_CMDS           = BIT(1),
61 };
62
63 struct turris_omnia_mcu_info {
64         u16 features;
65 };
66
67 static 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
90         /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
91         case (16 + 32 + 0) ... (16 + 32 + 15):
92                 if (!(info->features & FEAT_EXT_CMDS))
93                         return -EINVAL;
94                 if (!(info->features & FEAT_PERIPH_MCU))
95                         return -EINVAL;
96                 return GPIOF_OUTPUT;
97
98         default:
99                 return -EINVAL;
100         }
101 }
102
103 static 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
128         /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
129         case (16 + 32 + 0) ... (16 + 32 + 15):
130                 if (!(info->features & FEAT_EXT_CMDS))
131                         return -EINVAL;
132                 if (!(info->features & FEAT_PERIPH_MCU))
133                         return -EINVAL;
134                 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
144 static 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);
147         u8 val16[2];
148         u8 val32[4];
149
150         switch (offset) {
151         /* bank 0 */
152         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));
171
172         /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
173         case (16 + 32 + 0) ... (16 + 32 + 15):
174                 if (!(info->features & FEAT_EXT_CMDS))
175                         return -EINVAL;
176                 if (!(info->features & FEAT_PERIPH_MCU))
177                         return -EINVAL;
178                 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));
183
184         default:
185                 return -EINVAL;
186         }
187 }
188
189 static 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
202 static 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
215 static 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
258 static 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
267 static 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
294         if ((info->features & FEAT_EXT_CMDS) && (info->features & FEAT_PERIPH_MCU))
295                 uc_priv->gpio_count = 16 + 32 + 16;
296         else if (info->features & FEAT_EXT_CMDS)
297                 uc_priv->gpio_count = 16 + 32;
298         else
299                 uc_priv->gpio_count = 16;
300
301         return 0;
302 }
303
304 static const struct udevice_id turris_omnia_mcu_ids[] = {
305         { .compatible = "cznic,turris-omnia-mcu" },
306         { }
307 };
308
309 U_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 };