blob: 260755ac746fd6e2613037f32d326f3e36720fba [file] [log] [blame]
Valentine Barshake734fdd2019-04-23 23:44:57 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * V3MSK board CPLD access support
4 *
5 * Copyright (C) 2019 Renesas Electronics Corporation
6 * Copyright (C) 2019 Cogent Embedded, Inc.
7 *
8 */
9
Valentine Barshake734fdd2019-04-23 23:44:57 +030010#include <asm/gpio.h>
11#include <asm/io.h>
12#include <dm.h>
13#include <errno.h>
14#include <linux/err.h>
15#include <sysreset.h>
16#include <linux/delay.h>
17#include <command.h>
18
19#define CPLD_ADDR_PRODUCT_L 0x000 /* R */
20#define CPLD_ADDR_PRODUCT_H 0x001 /* R */
21#define CPLD_ADDR_CPLD_VERSION_D 0x002 /* R */
22#define CPLD_ADDR_CPLD_VERSION_Y 0x003 /* R */
23#define CPLD_ADDR_MODE_SET_L 0x004 /* R/W */
24#define CPLD_ADDR_MODE_SET_H 0x005 /* R/W */
25#define CPLD_ADDR_MODE_APPLIED_L 0x006 /* R */
26#define CPLD_ADDR_MODE_APPLIED_H 0x007 /* R */
27#define CPLD_ADDR_DIPSW 0x008 /* R */
28#define CPLD_ADDR_RESET 0x00A /* R/W */
29#define CPLD_ADDR_POWER_CFG 0x00B /* R/W */
30#define CPLD_ADDR_PERI_CFG1 0x00C /* R/W */
31#define CPLD_ADDR_PERI_CFG2 0x00D /* R/W */
32#define CPLD_ADDR_LEDS 0x00E /* R/W */
33#define CPLD_ADDR_PCB_VERSION 0x300 /* R */
34#define CPLD_ADDR_SOC_VERSION 0x301 /* R */
35#define CPLD_ADDR_PCB_SN_L 0x302 /* R */
36#define CPLD_ADDR_PCB_SN_H 0x303 /* R */
37
38#define MDIO_DELAY 10 /* microseconds */
39
40#define CPLD_MAX_GPIOS 2
41
42struct renesas_v3msk_sysreset_priv {
43 struct gpio_desc miso;
44 struct gpio_desc mosi;
45 struct gpio_desc mdc;
46 struct gpio_desc enablez;
47 /*
48 * V3MSK Videobox Mini board has CANFD PHY connected
49 * we must shutdown this chip to use bb pins
50 */
51 struct gpio_desc gpios[CPLD_MAX_GPIOS];
52};
53
54static void mdio_bb_active_mdio(struct renesas_v3msk_sysreset_priv *priv)
55{
56 dm_gpio_set_dir_flags(&priv->mosi, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
57}
58
59static void mdio_bb_tristate_mdio(struct renesas_v3msk_sysreset_priv *priv)
60{
61 dm_gpio_set_dir_flags(&priv->mosi, GPIOD_IS_IN);
62}
63
64static void mdio_bb_set_mdio(struct renesas_v3msk_sysreset_priv *priv, int val)
65{
66 dm_gpio_set_value(&priv->mosi, val);
67}
68
69static int mdio_bb_get_mdio(struct renesas_v3msk_sysreset_priv *priv)
70{
71 return dm_gpio_get_value(&priv->miso);
72}
73
74static void mdio_bb_set_mdc(struct renesas_v3msk_sysreset_priv *priv, int val)
75{
76 dm_gpio_set_value(&priv->mdc, val);
77}
78
79static void mdio_bb_delay(void)
80{
81 udelay(MDIO_DELAY);
82}
83
84/* Send the preamble, address, and register (common to read and write) */
85static void mdio_bb_pre(struct renesas_v3msk_sysreset_priv *priv,
86 u8 op, u8 addr, u8 reg)
87{
88 int i;
89
90 /* 32-bit preamble */
91 mdio_bb_active_mdio(priv);
92 mdio_bb_set_mdio(priv, 1);
93 for (i = 0; i < 32; i++) {
94 mdio_bb_set_mdc(priv, 0);
95 mdio_bb_delay();
96 mdio_bb_set_mdc(priv, 1);
97 mdio_bb_delay();
98 }
99 /* send the ST (2-bits of '01') */
100 mdio_bb_set_mdio(priv, 0);
101 mdio_bb_set_mdc(priv, 0);
102 mdio_bb_delay();
103 mdio_bb_set_mdc(priv, 1);
104 mdio_bb_delay();
105 mdio_bb_set_mdio(priv, 1);
106 mdio_bb_set_mdc(priv, 0);
107 mdio_bb_delay();
108 mdio_bb_set_mdc(priv, 1);
109 mdio_bb_delay();
110 /* send the OP (2-bits of Opcode: '10'-read, '01'-write) */
111 mdio_bb_set_mdio(priv, op >> 1);
112 mdio_bb_set_mdc(priv, 0);
113 mdio_bb_delay();
114 mdio_bb_set_mdc(priv, 1);
115 mdio_bb_delay();
116 mdio_bb_set_mdio(priv, op & 1);
117 mdio_bb_set_mdc(priv, 0);
118 mdio_bb_delay();
119 mdio_bb_set_mdc(priv, 1);
120 mdio_bb_delay();
121 /* send the PA5 (5-bits of PHY address) */
122 for (i = 0; i < 5; i++) {
123 mdio_bb_set_mdio(priv, addr & 0x10); /* MSB first */
124 mdio_bb_set_mdc(priv, 0);
125 mdio_bb_delay();
126 mdio_bb_set_mdc(priv, 1);
127 mdio_bb_delay();
128 addr <<= 1;
129 }
130 /* send the RA5 (5-bits of register address) */
131 for (i = 0; i < 5; i++) {
132 mdio_bb_set_mdio(priv, reg & 0x10); /* MSB first */
133 mdio_bb_set_mdc(priv, 0);
134 mdio_bb_delay();
135 mdio_bb_set_mdc(priv, 1);
136 mdio_bb_delay();
137 reg <<= 1;
138 }
139}
140
141static int mdio_bb_read(struct renesas_v3msk_sysreset_priv *priv,
142 u8 addr, u8 reg)
143{
144 int i;
145 u16 data = 0;
146
147 mdio_bb_pre(priv, 2, addr, reg);
148 /* tri-state MDIO */
149 mdio_bb_tristate_mdio(priv);
150 /* read TA (2-bits of turn-around, last bit must be '0') */
151 mdio_bb_set_mdc(priv, 0);
152 mdio_bb_delay();
153 mdio_bb_set_mdc(priv, 1);
154 mdio_bb_delay();
155 mdio_bb_set_mdc(priv, 0);
156 mdio_bb_delay();
157 mdio_bb_set_mdc(priv, 1);
158 mdio_bb_delay();
159 /* check the turnaround bit: the PHY should drive line to zero */
160 if (mdio_bb_get_mdio(priv) != 0) {
161 printf("PHY didn't drive TA low\n");
162 for (i = 0; i < 32; i++) {
163 mdio_bb_set_mdc(priv, 0);
164 mdio_bb_delay();
165 mdio_bb_set_mdc(priv, 1);
166 mdio_bb_delay();
167 }
168 /* There is no PHY, set value to 0xFFFF */
169 return 0xFFFF;
170 }
171 mdio_bb_set_mdc(priv, 0);
172 mdio_bb_delay();
173 /* read 16-bits of data */
174 for (i = 0; i < 16; i++) {
175 mdio_bb_set_mdc(priv, 1);
176 mdio_bb_delay();
177 data <<= 1;
178 data |= mdio_bb_get_mdio(priv);
179 mdio_bb_set_mdc(priv, 0);
180 mdio_bb_delay();
181 }
182
183 mdio_bb_set_mdc(priv, 1);
184 mdio_bb_delay();
185 mdio_bb_set_mdc(priv, 0);
186 mdio_bb_delay();
187 mdio_bb_set_mdc(priv, 1);
188 mdio_bb_delay();
189
190 debug("cpld_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, data);
191
192 return data;
193}
194
195static void mdio_bb_write(struct renesas_v3msk_sysreset_priv *priv,
196 u8 addr, u8 reg, u16 val)
197{
198 int i;
199
200 mdio_bb_pre(priv, 1, addr, reg);
201 /* send the TA (2-bits of turn-around '10') */
202 mdio_bb_set_mdio(priv, 1);
203 mdio_bb_set_mdc(priv, 0);
204 mdio_bb_delay();
205 mdio_bb_set_mdc(priv, 1);
206 mdio_bb_delay();
207 mdio_bb_set_mdio(priv, 0);
208 mdio_bb_set_mdc(priv, 0);
209 mdio_bb_delay();
210 mdio_bb_set_mdc(priv, 1);
211 mdio_bb_delay();
212 /* write 16-bits of data */
213 for (i = 0; i < 16; i++) {
214 mdio_bb_set_mdio(priv, val & 0x8000); /* MSB first */
215 mdio_bb_set_mdc(priv, 0);
216 mdio_bb_delay();
217 mdio_bb_set_mdc(priv, 1);
218 mdio_bb_delay();
219 val <<= 1;
220 }
221 /* tri-state MDIO */
222 mdio_bb_tristate_mdio(priv);
223 mdio_bb_set_mdc(priv, 0);
224 mdio_bb_delay();
225 mdio_bb_set_mdc(priv, 1);
226 mdio_bb_delay();
227}
228
229static u16 cpld_read(struct udevice *dev, u16 addr)
230{
231 struct renesas_v3msk_sysreset_priv *priv = dev_get_priv(dev);
232
233 /* random flash reads require 2 reads: first read is unreliable */
234 if (addr >= CPLD_ADDR_PCB_VERSION)
235 mdio_bb_read(priv, addr >> 5, addr & 0x1f);
236
237 return mdio_bb_read(priv, addr >> 5, addr & 0x1f);
238}
239
240static void cpld_write(struct udevice *dev, u16 addr, u16 data)
241{
242 struct renesas_v3msk_sysreset_priv *priv = dev_get_priv(dev);
243
244 mdio_bb_write(priv, addr >> 5, addr & 0x1f, data);
245}
246
247static int do_cpld(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
248{
249 struct udevice *dev;
250 u16 addr, val;
251 int ret;
252
253 ret = uclass_get_device_by_driver(UCLASS_SYSRESET,
254 DM_DRIVER_GET(sysreset_renesas_v3msk),
255 &dev);
256 if (ret)
257 return ret;
258
259 if (argc == 2 && strcmp(argv[1], "info") == 0) {
260 printf("Product: 0x%08x\n",
261 (cpld_read(dev, CPLD_ADDR_PRODUCT_H) << 16) |
262 cpld_read(dev, CPLD_ADDR_PRODUCT_L));
263 printf("CPLD version: 0x%08x\n",
264 (cpld_read(dev, CPLD_ADDR_CPLD_VERSION_Y) << 16) |
265 cpld_read(dev, CPLD_ADDR_CPLD_VERSION_D));
266 printf("Mode setting (MD0..26): 0x%08x\n",
267 (cpld_read(dev, CPLD_ADDR_MODE_APPLIED_H) << 16) |
268 cpld_read(dev, CPLD_ADDR_MODE_APPLIED_L));
269 printf("DIPSW (SW4, SW5): 0x%02x, 0x%x\n",
270 (cpld_read(dev, CPLD_ADDR_DIPSW) & 0xff) ^ 0xff,
271 (cpld_read(dev, CPLD_ADDR_DIPSW) >> 8) ^ 0xf);
272 printf("Power config: 0x%08x\n",
273 cpld_read(dev, CPLD_ADDR_POWER_CFG));
274 printf("Periferals config: 0x%08x\n",
275 (cpld_read(dev, CPLD_ADDR_PERI_CFG2) << 16) |
276 cpld_read(dev, CPLD_ADDR_PERI_CFG1));
277 printf("PCB version: %d.%d\n",
278 cpld_read(dev, CPLD_ADDR_PCB_VERSION) >> 8,
279 cpld_read(dev, CPLD_ADDR_PCB_VERSION) & 0xff);
280 printf("SOC version: %d.%d\n",
281 cpld_read(dev, CPLD_ADDR_SOC_VERSION) >> 8,
282 cpld_read(dev, CPLD_ADDR_SOC_VERSION) & 0xff);
283 printf("PCB S/N: %d\n",
284 (cpld_read(dev, CPLD_ADDR_PCB_SN_H) << 16) |
285 cpld_read(dev, CPLD_ADDR_PCB_SN_L));
286 return 0;
287 }
288
289 if (argc < 3)
290 return CMD_RET_USAGE;
291
292 addr = simple_strtoul(argv[2], NULL, 16);
293 if (!(addr >= CPLD_ADDR_PRODUCT_L && addr <= CPLD_ADDR_LEDS)) {
294 printf("cpld invalid addr\n");
295 return CMD_RET_USAGE;
296 }
297
298 if (argc == 3 && strcmp(argv[1], "read") == 0) {
299 printf("0x%x\n", cpld_read(dev, addr));
300 } else if (argc == 4 && strcmp(argv[1], "write") == 0) {
301 val = simple_strtoul(argv[3], NULL, 16);
302 cpld_write(dev, addr, val);
303 }
304
305 return 0;
306}
307
308U_BOOT_CMD(cpld, 4, 1, do_cpld,
309 "CPLD access",
310 "info\n"
311 "cpld read addr\n"
312 "cpld write addr val\n"
313);
314
315static int renesas_v3msk_sysreset_request(struct udevice *dev, enum sysreset_t type)
316{
317 cpld_write(dev, CPLD_ADDR_RESET, 1);
318
319 return -EINPROGRESS;
320}
321
322static int renesas_v3msk_sysreset_probe(struct udevice *dev)
323{
324 struct renesas_v3msk_sysreset_priv *priv = dev_get_priv(dev);
325
326 if (gpio_request_by_name(dev, "gpio-miso", 0, &priv->miso,
327 GPIOD_IS_IN))
328 return -EINVAL;
329
330 if (gpio_request_by_name(dev, "gpio-mosi", 0, &priv->mosi,
331 GPIOD_IS_OUT))
332 return -EINVAL;
333
334 if (gpio_request_by_name(dev, "gpio-mdc", 0, &priv->mdc,
335 GPIOD_IS_OUT))
336 return -EINVAL;
337
338 if (gpio_request_by_name(dev, "gpio-enablez", 0, &priv->enablez,
339 GPIOD_IS_OUT))
340 return -EINVAL;
341
342 /* V3MSK Videobox Mini board has CANFD PHY connected
343 * we must shutdown this chip to use bb pins
344 */
345 gpio_request_list_by_name(dev, "gpios", priv->gpios, CPLD_MAX_GPIOS,
346 GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
347
348 return 0;
349}
350
351static struct sysreset_ops renesas_v3msk_sysreset = {
352 .request = renesas_v3msk_sysreset_request,
353};
354
355static const struct udevice_id renesas_v3msk_sysreset_ids[] = {
356 { .compatible = "renesas,v3msk-cpld" },
357 { /* sentinel */ }
358};
359
360U_BOOT_DRIVER(sysreset_renesas_v3msk) = {
361 .name = "renesas_v3msk_sysreset",
362 .id = UCLASS_SYSRESET,
363 .ops = &renesas_v3msk_sysreset,
364 .probe = renesas_v3msk_sysreset_probe,
365 .of_match = renesas_v3msk_sysreset_ids,
366 .priv_auto = sizeof(struct renesas_v3msk_sysreset_priv),
367};