aboutsummaryrefslogtreecommitdiff
path: root/board/renesas/v3msk/cpld.c
blob: aed616ac85981118c344a2b69aa564a321e5fefb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
// SPDX-License-Identifier: GPL-2.0+
/*
 * V3MSK board CPLD access support
 *
 * Copyright (C) 2019 Renesas Electronics Corporation
 * Copyright (C) 2019 Cogent Embedded, Inc.
 *
 */

#include <common.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <dm.h>
#include <errno.h>
#include <linux/err.h>
#include <sysreset.h>
#include <linux/delay.h>
#include <command.h>

#define CPLD_ADDR_PRODUCT_L		0x000 /* R */
#define CPLD_ADDR_PRODUCT_H		0x001 /* R */
#define CPLD_ADDR_CPLD_VERSION_D	0x002 /* R */
#define CPLD_ADDR_CPLD_VERSION_Y	0x003 /* R */
#define CPLD_ADDR_MODE_SET_L		0x004 /* R/W */
#define CPLD_ADDR_MODE_SET_H		0x005 /* R/W */
#define CPLD_ADDR_MODE_APPLIED_L	0x006 /* R */
#define CPLD_ADDR_MODE_APPLIED_H	0x007 /* R */
#define CPLD_ADDR_DIPSW			0x008 /* R */
#define CPLD_ADDR_RESET			0x00A /* R/W */
#define CPLD_ADDR_POWER_CFG		0x00B /* R/W */
#define CPLD_ADDR_PERI_CFG1		0x00C /* R/W */
#define CPLD_ADDR_PERI_CFG2		0x00D /* R/W */
#define CPLD_ADDR_LEDS			0x00E /* R/W */
#define CPLD_ADDR_PCB_VERSION		0x300 /* R */
#define CPLD_ADDR_SOC_VERSION		0x301 /* R */
#define CPLD_ADDR_PCB_SN_L		0x302 /* R */
#define CPLD_ADDR_PCB_SN_H		0x303 /* R */

#define MDIO_DELAY			10 /* microseconds */

#define CPLD_MAX_GPIOS			2

struct renesas_v3msk_sysreset_priv {
	struct gpio_desc	miso;
	struct gpio_desc	mosi;
	struct gpio_desc	mdc;
	struct gpio_desc	enablez;
	/*
	 * V3MSK Videobox Mini board has CANFD PHY connected
	 * we must shutdown this chip to use bb pins
	 */
	struct gpio_desc	gpios[CPLD_MAX_GPIOS];
};

static void mdio_bb_active_mdio(struct renesas_v3msk_sysreset_priv *priv)
{
	dm_gpio_set_dir_flags(&priv->mosi, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
}

static void mdio_bb_tristate_mdio(struct renesas_v3msk_sysreset_priv *priv)
{
	dm_gpio_set_dir_flags(&priv->mosi, GPIOD_IS_IN);
}

static void mdio_bb_set_mdio(struct renesas_v3msk_sysreset_priv *priv, int val)
{
	dm_gpio_set_value(&priv->mosi, val);
}

static int mdio_bb_get_mdio(struct renesas_v3msk_sysreset_priv *priv)
{
	return dm_gpio_get_value(&priv->miso);
}

static void mdio_bb_set_mdc(struct renesas_v3msk_sysreset_priv *priv, int val)
{
	dm_gpio_set_value(&priv->mdc, val);
}

static void mdio_bb_delay(void)
{
	udelay(MDIO_DELAY);
}

/* Send the preamble, address, and register (common to read and write) */
static void mdio_bb_pre(struct renesas_v3msk_sysreset_priv *priv,
			u8 op, u8 addr, u8 reg)
{
	int i;

	/* 32-bit preamble */
	mdio_bb_active_mdio(priv);
	mdio_bb_set_mdio(priv, 1);
	for (i = 0; i < 32; i++) {
		mdio_bb_set_mdc(priv, 0);
		mdio_bb_delay();
		mdio_bb_set_mdc(priv, 1);
		mdio_bb_delay();
	}
	/* send the ST (2-bits of '01') */
	mdio_bb_set_mdio(priv, 0);
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	mdio_bb_set_mdio(priv, 1);
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	/* send the OP (2-bits of Opcode: '10'-read, '01'-write) */
	mdio_bb_set_mdio(priv, op >> 1);
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	mdio_bb_set_mdio(priv, op & 1);
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	/* send the PA5 (5-bits of PHY address) */
	for (i = 0; i < 5; i++) {
		mdio_bb_set_mdio(priv, addr & 0x10); /* MSB first */
		mdio_bb_set_mdc(priv, 0);
		mdio_bb_delay();
		mdio_bb_set_mdc(priv, 1);
		mdio_bb_delay();
		addr <<= 1;
	}
	/* send the RA5 (5-bits of register address) */
	for (i = 0; i < 5; i++) {
		mdio_bb_set_mdio(priv, reg & 0x10); /* MSB first */
		mdio_bb_set_mdc(priv, 0);
		mdio_bb_delay();
		mdio_bb_set_mdc(priv, 1);
		mdio_bb_delay();
		reg <<= 1;
	}
}

static int mdio_bb_read(struct renesas_v3msk_sysreset_priv *priv,
			u8 addr, u8 reg)
{
	int i;
	u16 data = 0;

	mdio_bb_pre(priv, 2, addr, reg);
	/* tri-state MDIO */
	mdio_bb_tristate_mdio(priv);
	/* read TA (2-bits of turn-around, last bit must be '0') */
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	/* check the turnaround bit: the PHY should drive line to zero */
	if (mdio_bb_get_mdio(priv) != 0) {
		printf("PHY didn't drive TA low\n");
		for (i = 0; i < 32; i++) {
			mdio_bb_set_mdc(priv, 0);
			mdio_bb_delay();
			mdio_bb_set_mdc(priv, 1);
			mdio_bb_delay();
		}
		/* There is no PHY, set value to 0xFFFF */
		return 0xFFFF;
	}
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	/* read 16-bits of data */
	for (i = 0; i < 16; i++) {
		mdio_bb_set_mdc(priv, 1);
		mdio_bb_delay();
		data <<= 1;
		data |= mdio_bb_get_mdio(priv);
		mdio_bb_set_mdc(priv, 0);
		mdio_bb_delay();
	}

	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();

	debug("cpld_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, data);

	return data;
}

static void mdio_bb_write(struct renesas_v3msk_sysreset_priv *priv,
			  u8 addr, u8 reg, u16 val)
{
	int i;

	mdio_bb_pre(priv, 1, addr, reg);
	/* send the TA (2-bits of turn-around '10') */
	mdio_bb_set_mdio(priv, 1);
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	mdio_bb_set_mdio(priv, 0);
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
	/* write 16-bits of data */
	for (i = 0; i < 16; i++) {
		mdio_bb_set_mdio(priv, val & 0x8000); /* MSB first */
		mdio_bb_set_mdc(priv, 0);
		mdio_bb_delay();
		mdio_bb_set_mdc(priv, 1);
		mdio_bb_delay();
		val <<= 1;
	}
	/* tri-state MDIO */
	mdio_bb_tristate_mdio(priv);
	mdio_bb_set_mdc(priv, 0);
	mdio_bb_delay();
	mdio_bb_set_mdc(priv, 1);
	mdio_bb_delay();
}

static u16 cpld_read(struct udevice *dev, u16 addr)
{
	struct renesas_v3msk_sysreset_priv *priv = dev_get_priv(dev);

	/* random flash reads require 2 reads: first read is unreliable */
	if (addr >= CPLD_ADDR_PCB_VERSION)
		mdio_bb_read(priv, addr >> 5, addr & 0x1f);

	return mdio_bb_read(priv, addr >> 5, addr & 0x1f);
}

static void cpld_write(struct udevice *dev, u16 addr, u16 data)
{
	struct renesas_v3msk_sysreset_priv *priv = dev_get_priv(dev);

	mdio_bb_write(priv, addr >> 5, addr & 0x1f, data);
}

static int do_cpld(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
{
	struct udevice *dev;
	u16 addr, val;
	int ret;

	ret = uclass_get_device_by_driver(UCLASS_SYSRESET,
					  DM_DRIVER_GET(sysreset_renesas_v3msk),
					  &dev);
	if (ret)
		return ret;

	if (argc == 2 && strcmp(argv[1], "info") == 0) {
		printf("Product:                0x%08x\n",
		       (cpld_read(dev, CPLD_ADDR_PRODUCT_H) << 16) |
			cpld_read(dev, CPLD_ADDR_PRODUCT_L));
		printf("CPLD version:           0x%08x\n",
		       (cpld_read(dev, CPLD_ADDR_CPLD_VERSION_Y) << 16) |
			cpld_read(dev, CPLD_ADDR_CPLD_VERSION_D));
		printf("Mode setting (MD0..26): 0x%08x\n",
		       (cpld_read(dev, CPLD_ADDR_MODE_APPLIED_H) << 16) |
			cpld_read(dev, CPLD_ADDR_MODE_APPLIED_L));
		printf("DIPSW (SW4, SW5):       0x%02x, 0x%x\n",
		       (cpld_read(dev, CPLD_ADDR_DIPSW) & 0xff) ^ 0xff,
		       (cpld_read(dev, CPLD_ADDR_DIPSW) >> 8) ^ 0xf);
		printf("Power config:           0x%08x\n",
		       cpld_read(dev, CPLD_ADDR_POWER_CFG));
		printf("Periferals config:      0x%08x\n",
		       (cpld_read(dev, CPLD_ADDR_PERI_CFG2) << 16) |
			cpld_read(dev, CPLD_ADDR_PERI_CFG1));
		printf("PCB version:            %d.%d\n",
		       cpld_read(dev, CPLD_ADDR_PCB_VERSION) >> 8,
		       cpld_read(dev, CPLD_ADDR_PCB_VERSION) & 0xff);
		printf("SOC version:            %d.%d\n",
		       cpld_read(dev, CPLD_ADDR_SOC_VERSION) >> 8,
		       cpld_read(dev, CPLD_ADDR_SOC_VERSION) & 0xff);
		printf("PCB S/N:                %d\n",
		       (cpld_read(dev, CPLD_ADDR_PCB_SN_H) << 16) |
			cpld_read(dev, CPLD_ADDR_PCB_SN_L));
		return 0;
	}

	if (argc < 3)
		return CMD_RET_USAGE;

	addr = simple_strtoul(argv[2], NULL, 16);
	if (!(addr >= CPLD_ADDR_PRODUCT_L && addr <= CPLD_ADDR_LEDS)) {
		printf("cpld invalid addr\n");
		return CMD_RET_USAGE;
	}

	if (argc == 3 && strcmp(argv[1], "read") == 0) {
		printf("0x%x\n", cpld_read(dev, addr));
	} else if (argc == 4 && strcmp(argv[1], "write") == 0) {
		val = simple_strtoul(argv[3], NULL, 16);
		cpld_write(dev, addr, val);
	}

	return 0;
}

U_BOOT_CMD(cpld, 4, 1, do_cpld,
	   "CPLD access",
	   "info\n"
	   "cpld read addr\n"
	   "cpld write addr val\n"
);

static int renesas_v3msk_sysreset_request(struct udevice *dev, enum sysreset_t type)
{
	cpld_write(dev, CPLD_ADDR_RESET, 1);

	return -EINPROGRESS;
}

static int renesas_v3msk_sysreset_probe(struct udevice *dev)
{
	struct renesas_v3msk_sysreset_priv *priv = dev_get_priv(dev);

	if (gpio_request_by_name(dev, "gpio-miso", 0, &priv->miso,
				 GPIOD_IS_IN))
		return -EINVAL;

	if (gpio_request_by_name(dev, "gpio-mosi", 0, &priv->mosi,
				 GPIOD_IS_OUT))
		return -EINVAL;

	if (gpio_request_by_name(dev, "gpio-mdc", 0, &priv->mdc,
				 GPIOD_IS_OUT))
		return -EINVAL;

	if (gpio_request_by_name(dev, "gpio-enablez", 0, &priv->enablez,
				 GPIOD_IS_OUT))
		return -EINVAL;

	/* V3MSK Videobox Mini board has CANFD PHY connected
	 * we must shutdown this chip to use bb pins
	 */
	gpio_request_list_by_name(dev, "gpios", priv->gpios, CPLD_MAX_GPIOS,
				  GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);

	return 0;
}

static struct sysreset_ops renesas_v3msk_sysreset = {
	.request	= renesas_v3msk_sysreset_request,
};

static const struct udevice_id renesas_v3msk_sysreset_ids[] = {
	{ .compatible = "renesas,v3msk-cpld" },
	{ /* sentinel */ }
};

U_BOOT_DRIVER(sysreset_renesas_v3msk) = {
	.name		= "renesas_v3msk_sysreset",
	.id		= UCLASS_SYSRESET,
	.ops		= &renesas_v3msk_sysreset,
	.probe		= renesas_v3msk_sysreset_probe,
	.of_match	= renesas_v3msk_sysreset_ids,
	.priv_auto	= sizeof(struct renesas_v3msk_sysreset_priv),
};