summaryrefslogtreecommitdiff
path: root/arch/arc/plat-axs10x/axs10x.c
blob: 9701c93f315d5e1a1b19c882b2dce6c0d5e1c24f (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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/*
 * AXS101/AXS103 Software Development Platform
 *
 * Copyright (C) 2013-15 Synopsys, Inc. (www.synopsys.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <linux/libfdt.h>

#include <asm/asm-offsets.h>
#include <asm/io.h>
#include <asm/mach_desc.h>
#include <asm/mcip.h>

#define AXS_MB_CGU		0xE0010000
#define AXS_MB_CREG		0xE0011000

#define CREG_MB_IRQ_MUX		(AXS_MB_CREG + 0x214)
#define CREG_MB_SW_RESET	(AXS_MB_CREG + 0x220)
#define CREG_MB_VER		(AXS_MB_CREG + 0x230)
#define CREG_MB_CONFIG		(AXS_MB_CREG + 0x234)

#define AXC001_CREG		0xF0001000
#define AXC001_GPIO_INTC	0xF0003000

static void __init axs10x_enable_gpio_intc_wire(void)
{
	/*
	 * Peripherals on CPU Card and Mother Board are wired to cpu intc via
	 * intermediate DW APB GPIO blocks (mainly for debouncing)
	 *
	 *         ---------------------
	 *        |  snps,arc700-intc |
	 *        ---------------------
	 *          | #7          | #15
	 * -------------------   -------------------
	 * | snps,dw-apb-gpio |  | snps,dw-apb-gpio |
	 * -------------------   -------------------
	 *        | #12                     |
	 *        |                 [ Debug UART on cpu card ]
	 *        |
	 * ------------------------
	 * | snps,dw-apb-intc (MB)|
	 * ------------------------
	 *  |      |       |      |
	 * [eth] [uart]        [... other perip on Main Board]
	 *
	 * Current implementation of "irq-dw-apb-ictl" driver doesn't work well
	 * with stacked INTCs. In particular problem happens if its master INTC
	 * not yet instantiated. See discussion here -
	 * https://lkml.org/lkml/2015/3/4/755
	 *
	 * So setup the first gpio block as a passive pass thru and hide it from
	 * DT hardware topology - connect MB intc directly to cpu intc
	 * The GPIO "wire" needs to be init nevertheless (here)
	 *
	 * One side adv is that peripheral interrupt handling avoids one nested
	 * intc ISR hop
	 */
#define GPIO_INTEN		(AXC001_GPIO_INTC + 0x30)
#define GPIO_INTMASK		(AXC001_GPIO_INTC + 0x34)
#define GPIO_INTTYPE_LEVEL	(AXC001_GPIO_INTC + 0x38)
#define GPIO_INT_POLARITY	(AXC001_GPIO_INTC + 0x3c)
#define MB_TO_GPIO_IRQ		12

	iowrite32(~(1 << MB_TO_GPIO_IRQ), (void __iomem *) GPIO_INTMASK);
	iowrite32(0, (void __iomem *) GPIO_INTTYPE_LEVEL);
	iowrite32(~0, (void __iomem *) GPIO_INT_POLARITY);
	iowrite32(1 << MB_TO_GPIO_IRQ, (void __iomem *) GPIO_INTEN);
}

static inline void __init
write_cgu_reg(uint32_t value, void __iomem *reg, void __iomem *lock_reg)
{
	unsigned int loops = 128 * 1024, ctr;

	iowrite32(value, reg);

	ctr = loops;
	while (((ioread32(lock_reg) & 1) == 1) && ctr--) /* wait for unlock */
		cpu_relax();

	ctr = loops;
	while (((ioread32(lock_reg) & 1) == 0) && ctr--) /* wait for re-lock */
		cpu_relax();
}

static void __init axs10x_print_board_ver(unsigned int creg, const char *str)
{
	union ver {
		struct {
#ifdef CONFIG_CPU_BIG_ENDIAN
			unsigned int pad:11, y:12, m:4, d:5;
#else
			unsigned int d:5, m:4, y:12, pad:11;
#endif
		};
		unsigned int val;
	} board;

	board.val = ioread32((void __iomem *)creg);
	pr_info("AXS: %s FPGA Date: %u-%u-%u\n", str, board.d, board.m,
		board.y);
}

static void __init axs10x_early_init(void)
{
	int mb_rev;
	char mb[32];

	/* Determine motherboard version */
	if (ioread32((void __iomem *) CREG_MB_CONFIG) & (1 << 28))
		mb_rev = 3;	/* HT-3 (rev3.0) */
	else
		mb_rev = 2;	/* HT-2 (rev2.0) */

	axs10x_enable_gpio_intc_wire();

	scnprintf(mb, 32, "MainBoard v%d", mb_rev);
	axs10x_print_board_ver(CREG_MB_VER, mb);
}

#ifdef CONFIG_AXS101

#define CREG_CPU_ADDR_770	(AXC001_CREG + 0x20)
#define CREG_CPU_ADDR_TUNN	(AXC001_CREG + 0x60)
#define CREG_CPU_ADDR_770_UPD	(AXC001_CREG + 0x34)
#define CREG_CPU_ADDR_TUNN_UPD	(AXC001_CREG + 0x74)

#define CREG_CPU_ARC770_IRQ_MUX	(AXC001_CREG + 0x114)
#define CREG_CPU_GPIO_UART_MUX	(AXC001_CREG + 0x120)

/*
 * Set up System Memory Map for ARC cpu / peripherals controllers
 *
 * Each AXI master has a 4GB memory map specified as 16 apertures of 256MB, each
 * of which maps to a corresponding 256MB aperture in Target slave memory map.
 *
 * e.g. ARC cpu AXI Master's aperture 8 (0x8000_0000) is mapped to aperture 0
 * (0x0000_0000) of DDR Port 0 (slave #1)
 *
 * Access from cpu to MB controllers such as GMAC is setup using AXI Tunnel:
 * which has master/slaves on both ends.
 * e.g. aperture 14 (0xE000_0000) of ARC cpu is mapped to aperture 14
 * (0xE000_0000) of CPU Card AXI Tunnel slave (slave #3) which is mapped to
 * MB AXI Tunnel Master, which also has a mem map setup
 *
 * In the reverse direction, MB AXI Masters (e.g. GMAC) mem map is setup
 * to map to MB AXI Tunnel slave which connects to CPU Card AXI Tunnel Master
 */
struct aperture {
	unsigned int slave_sel:4, slave_off:4, pad:24;
};

/* CPU Card target slaves */
#define AXC001_SLV_NONE			0
#define AXC001_SLV_DDR_PORT0		1
#define AXC001_SLV_SRAM			2
#define AXC001_SLV_AXI_TUNNEL		3
#define AXC001_SLV_AXI2APB		6
#define AXC001_SLV_DDR_PORT1		7

/* MB AXI Target slaves */
#define AXS_MB_SLV_NONE			0
#define AXS_MB_SLV_AXI_TUNNEL_CPU	1
#define AXS_MB_SLV_AXI_TUNNEL_HAPS	2
#define AXS_MB_SLV_SRAM			3
#define AXS_MB_SLV_CONTROL		4

/* MB AXI masters */
#define AXS_MB_MST_TUNNEL_CPU		0
#define AXS_MB_MST_USB_OHCI		10

/*
 * memmap for ARC core on CPU Card
 */
static const struct aperture axc001_memmap[16] = {
	{AXC001_SLV_AXI_TUNNEL,		0x0},
	{AXC001_SLV_AXI_TUNNEL,		0x1},
	{AXC001_SLV_SRAM,		0x0}, /* 0x2000_0000: Local SRAM */
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_DDR_PORT0,		0x0}, /* 0x8000_0000: DDR   0..256M */
	{AXC001_SLV_DDR_PORT0,		0x1}, /* 0x9000_0000: DDR 256..512M */
	{AXC001_SLV_DDR_PORT0,		0x2},
	{AXC001_SLV_DDR_PORT0,		0x3},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_AXI_TUNNEL,		0xD},
	{AXC001_SLV_AXI_TUNNEL,		0xE}, /* MB: CREG, CGU... */
	{AXC001_SLV_AXI2APB,		0x0}, /* CPU Card local CREG, CGU... */
};

/*
 * memmap for CPU Card AXI Tunnel Master (for access by MB controllers)
 * GMAC (MB) -> MB AXI Tunnel slave -> CPU Card AXI Tunnel Master -> DDR
 */
static const struct aperture axc001_axi_tunnel_memmap[16] = {
	{AXC001_SLV_AXI_TUNNEL,		0x0},
	{AXC001_SLV_AXI_TUNNEL,		0x1},
	{AXC001_SLV_SRAM,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_DDR_PORT1,		0x0},
	{AXC001_SLV_DDR_PORT1,		0x1},
	{AXC001_SLV_DDR_PORT1,		0x2},
	{AXC001_SLV_DDR_PORT1,		0x3},
	{AXC001_SLV_NONE,		0x0},
	{AXC001_SLV_AXI_TUNNEL,		0xD},
	{AXC001_SLV_AXI_TUNNEL,		0xE},
	{AXC001_SLV_AXI2APB,		0x0},
};

/*
 * memmap for MB AXI Masters
 * Same mem map for all perip controllers as well as MB AXI Tunnel Master
 */
static const struct aperture axs_mb_memmap[16] = {
	{AXS_MB_SLV_SRAM,		0x0},
	{AXS_MB_SLV_SRAM,		0x0},
	{AXS_MB_SLV_NONE,		0x0},
	{AXS_MB_SLV_NONE,		0x0},
	{AXS_MB_SLV_NONE,		0x0},
	{AXS_MB_SLV_NONE,		0x0},
	{AXS_MB_SLV_NONE,		0x0},
	{AXS_MB_SLV_NONE,		0x0},
	{AXS_MB_SLV_AXI_TUNNEL_CPU,	0x8},	/* DDR on CPU Card */
	{AXS_MB_SLV_AXI_TUNNEL_CPU,	0x9},	/* DDR on CPU Card */
	{AXS_MB_SLV_AXI_TUNNEL_CPU,	0xA},
	{AXS_MB_SLV_AXI_TUNNEL_CPU,	0xB},
	{AXS_MB_SLV_NONE,		0x0},
	{AXS_MB_SLV_AXI_TUNNEL_HAPS,	0xD},
	{AXS_MB_SLV_CONTROL,		0x0},	/* MB Local CREG, CGU... */
	{AXS_MB_SLV_AXI_TUNNEL_CPU,	0xF},
};

static noinline void __init
axs101_set_memmap(void __iomem *base, const struct aperture map[16])
{
	unsigned int slave_select, slave_offset;
	int i;

	slave_select = slave_offset = 0;
	for (i = 0; i < 8; i++) {
		slave_select |= map[i].slave_sel << (i << 2);
		slave_offset |= map[i].slave_off << (i << 2);
	}

	iowrite32(slave_select, base + 0x0);	/* SLV0 */
	iowrite32(slave_offset, base + 0x8);	/* OFFSET0 */

	slave_select = slave_offset = 0;
	for (i = 0; i < 8; i++) {
		slave_select |= map[i+8].slave_sel << (i << 2);
		slave_offset |= map[i+8].slave_off << (i << 2);
	}

	iowrite32(slave_select, base + 0x4);	/* SLV1 */
	iowrite32(slave_offset, base + 0xC);	/* OFFSET1 */
}

static void __init axs101_early_init(void)
{
	int i;

	/* ARC 770D memory view */
	axs101_set_memmap((void __iomem *) CREG_CPU_ADDR_770, axc001_memmap);
	iowrite32(1, (void __iomem *) CREG_CPU_ADDR_770_UPD);

	/* AXI tunnel memory map (incoming traffic from MB into CPU Card */
	axs101_set_memmap((void __iomem *) CREG_CPU_ADDR_TUNN,
			      axc001_axi_tunnel_memmap);
	iowrite32(1, (void __iomem *) CREG_CPU_ADDR_TUNN_UPD);

	/* MB peripherals memory map */
	for (i = AXS_MB_MST_TUNNEL_CPU; i <= AXS_MB_MST_USB_OHCI; i++)
		axs101_set_memmap((void __iomem *) AXS_MB_CREG + (i << 4),
				      axs_mb_memmap);

	iowrite32(0x3ff, (void __iomem *) AXS_MB_CREG + 0x100); /* Update */

	/* GPIO pins 18 and 19 are used as UART rx and tx, respectively. */
	iowrite32(0x01, (void __iomem *) CREG_CPU_GPIO_UART_MUX);

	/* Set up the MB interrupt system: mux interrupts to GPIO7) */
	iowrite32(0x01, (void __iomem *) CREG_MB_IRQ_MUX);

	/* reset ethernet and ULPI interfaces */
	iowrite32(0x18, (void __iomem *) CREG_MB_SW_RESET);

	/* map GPIO 14:10 to ARC 9:5 (IRQ mux change for MB v2 onwards) */
	iowrite32(0x52, (void __iomem *) CREG_CPU_ARC770_IRQ_MUX);

	axs10x_early_init();
}

#endif	/* CONFIG_AXS101 */

#ifdef CONFIG_AXS103

#define AXC003_CGU	0xF0000000
#define AXC003_CREG	0xF0001000
#define AXC003_MST_AXI_TUNNEL	0
#define AXC003_MST_HS38		1

#define CREG_CPU_AXI_M0_IRQ_MUX	(AXC003_CREG + 0x440)
#define CREG_CPU_GPIO_UART_MUX	(AXC003_CREG + 0x480)
#define CREG_CPU_TUN_IO_CTRL	(AXC003_CREG + 0x494)


union pll_reg {
	struct {
#ifdef CONFIG_CPU_BIG_ENDIAN
		unsigned int pad:17, noupd:1, bypass:1, edge:1, high:6, low:6;
#else
		unsigned int low:6, high:6, edge:1, bypass:1, noupd:1, pad:17;
#endif
	};
	unsigned int val;
};

static unsigned int __init axs103_get_freq(void)
{
	union pll_reg idiv, fbdiv, odiv;
	unsigned int f = 33333333;

	idiv.val = ioread32((void __iomem *)AXC003_CGU + 0x80 + 0);
	fbdiv.val = ioread32((void __iomem *)AXC003_CGU + 0x80 + 4);
	odiv.val = ioread32((void __iomem *)AXC003_CGU + 0x80 + 8);

	if (idiv.bypass != 1)
		f = f / (idiv.low + idiv.high);

	if (fbdiv.bypass != 1)
		f = f * (fbdiv.low + fbdiv.high);

	if (odiv.bypass != 1)
		f = f / (odiv.low + odiv.high);

	f = (f + 500000) / 1000000; /* Rounding */
	return f;
}

static inline unsigned int __init encode_div(unsigned int id, int upd)
{
	union pll_reg div;

	div.val = 0;

	div.noupd = !upd;
	div.bypass = id == 1 ? 1 : 0;
	div.edge = (id%2 == 0) ? 0 : 1;  /* 0 = rising */
	div.low = (id%2 == 0) ? id >> 1 : (id >> 1)+1;
	div.high = id >> 1;

	return div.val;
}

noinline static void __init
axs103_set_freq(unsigned int id, unsigned int fd, unsigned int od)
{
	write_cgu_reg(encode_div(id, 0),
		      (void __iomem *)AXC003_CGU + 0x80 + 0,
		      (void __iomem *)AXC003_CGU + 0x110);

	write_cgu_reg(encode_div(fd, 0),
		      (void __iomem *)AXC003_CGU + 0x80 + 4,
		      (void __iomem *)AXC003_CGU + 0x110);

	write_cgu_reg(encode_div(od, 1),
		      (void __iomem *)AXC003_CGU + 0x80 + 8,
		      (void __iomem *)AXC003_CGU + 0x110);
}

static void __init axs103_early_init(void)
{
	int offset = fdt_path_offset(initial_boot_params, "/cpu_card/core_clk");
	const struct fdt_property *prop = fdt_get_property(initial_boot_params,
							   offset,
							   "clock-frequency",
							   NULL);
	u32 freq = be32_to_cpu(*(u32*)(prop->data)) / 1000000, orig = freq;

	/*
	 * AXS103 configurations for SMP/QUAD configurations share device tree
	 * which defaults to 90 MHz. However recent failures of Quad config
	 * revealed P&R timing violations so clamp it down to safe 50 MHz
	 * Instead of duplicating defconfig/DT for SMP/QUAD, add a small hack
	 *
	 * This hack is really hacky as of now. Fix it properly by getting the
	 * number of cores as return value of platform's early SMP callback
	 */
#ifdef CONFIG_ARC_MCIP
	unsigned int num_cores = (read_aux_reg(ARC_REG_MCIP_BCR) >> 16) & 0x3F;
	if (num_cores > 2)
		freq = 50;
	else if (num_cores == 2)
		freq = 75;
#endif

	switch (freq) {
	case 33:
		axs103_set_freq(1, 1, 1);
		break;
	case 50:
		axs103_set_freq(1, 30, 20);
		break;
	case 75:
		axs103_set_freq(2, 45, 10);
		break;
	case 90:
		axs103_set_freq(2, 54, 10);
		break;
	case 100:
		axs103_set_freq(1, 30, 10);
		break;
	case 125:
		axs103_set_freq(2, 45,  6);
		break;
	default:
		/*
		 * In this case, core_frequency derived from
		 * DT "clock-frequency" might not match with board value.
		 * Hence update it to match the board value.
		 */
		freq = axs103_get_freq();
		break;
	}

	pr_info("Freq is %dMHz\n", freq);

	/* Patching .dtb in-place with new core clock value */
	if (freq != orig ) {
		freq = cpu_to_be32(freq * 1000000);
		fdt_setprop_inplace(initial_boot_params, offset,
				    "clock-frequency", &freq, sizeof(freq));
	}

	/* Memory maps already config in pre-bootloader */

	/* set GPIO mux to UART */
	iowrite32(0x01, (void __iomem *) CREG_CPU_GPIO_UART_MUX);

	iowrite32((0x00100000U | 0x000C0000U | 0x00003322U),
		  (void __iomem *) CREG_CPU_TUN_IO_CTRL);

	/* Set up the AXS_MB interrupt system.*/
	iowrite32(12, (void __iomem *) (CREG_CPU_AXI_M0_IRQ_MUX
					 + (AXC003_MST_HS38 << 2)));

	/* connect ICTL - Main Board with GPIO line */
	iowrite32(0x01, (void __iomem *) CREG_MB_IRQ_MUX);

	axs10x_print_board_ver(AXC003_CREG + 4088, "AXC003 CPU Card");

	axs10x_early_init();
}
#endif

#ifdef CONFIG_AXS101

static const char *axs101_compat[] __initconst = {
	"snps,axs101",
	NULL,
};

MACHINE_START(AXS101, "axs101")
	.dt_compat	= axs101_compat,
	.init_early	= axs101_early_init,
MACHINE_END

#endif	/* CONFIG_AXS101 */

#ifdef CONFIG_AXS103

static const char *axs103_compat[] __initconst = {
	"snps,axs103",
	NULL,
};

MACHINE_START(AXS103, "axs103")
	.dt_compat	= axs103_compat,
	.init_early	= axs103_early_init,
MACHINE_END

/*
 * For the VDK OS-kit, to get the offset to pid and command fields
 */
char coware_swa_pid_offset[TASK_PID];
char coware_swa_comm_offset[TASK_COMM];

#endif	/* CONFIG_AXS103 */