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