aboutsummaryrefslogtreecommitdiff
path: root/arch/arm64/mach/helanx-dt.c
blob: 80939cee0f0b11d7ac247cd367902545234c0064 (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
/*
 *  linux/arch/arm64/mach/mmpx-dt.c
 *
 *  Copyright (C) 2012 Marvell Technology Group Ltd.
 *  Author: Dongjiu Geng <djgeng@marvell.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
 *  publishhed by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqchip/mmp.h>
#include <linux/of_platform.h>
#include <linux/clocksource.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/memblock.h>

#include <asm/mach/arch.h>
#include <asm/mcpm.h>

#include "regs-addr.h"
#include "mmpx-dt.h"
#include "soc_camera_dkb.h"

#ifdef CONFIG_SD8XXX_RFKILL
#include <linux/sd8x_rfkill.h>
#endif

#ifdef CONFIG_LEDS_REGULATOR

static struct platform_device keypad_backlight = {
	.name = "button-backlight",
};

#endif

unsigned int mmp_chip_id;
EXPORT_SYMBOL(mmp_chip_id);

static const struct of_dev_auxdata helanx_auxdata_lookup[] __initconst = {
	OF_DEV_AUXDATA("mrvl,mmp-sspa-dai", 0xd128dc00, "mmp-sspa-dai.0", NULL),
	OF_DEV_AUXDATA("mrvl,mmp-sspa-dai", 0xd128dd00, "mmp-sspa-dai.1", NULL),
	OF_DEV_AUXDATA("marvell,map-card", 0, "sound", NULL),
#ifdef CONFIG_SOC_CAMERA_S5K8AA
	OF_DEV_AUXDATA("soc-camera-pdrv", 0, "soc-camera-pdrv.0", &soc_camera_desc_0),
#endif
#ifdef CONFIG_SOC_CAMERA_SR200
	OF_DEV_AUXDATA("soc-camera-pdrv", 1, "soc-camera-pdrv.1", &soc_camera_desc_1),
#endif
#ifdef CONFIG_SOC_CAMERA_SP0A20_ECS
	OF_DEV_AUXDATA("soc-camera-pdrv", 2, "soc-camera-pdrv.2",
			&soc_camera_desc_2),
#endif
#ifdef CONFIG_SOC_CAMERA_SP2529_ECS
	OF_DEV_AUXDATA("soc-camera-pdrv", 3, "soc-camera-pdrv.3",
			&soc_camera_desc_4),
#endif
	OF_DEV_AUXDATA("marvell,mmp-disp", 0xd420b000, "mmp-disp", NULL),
#ifdef CONFIG_SD8XXX_RFKILL
	OF_DEV_AUXDATA("mrvl,sd8x-rfkill", 0, "sd8x-rfkill", NULL),
#endif
	OF_DEV_AUXDATA("marvell,pxa27x-keypad", 0xd4012000, "pxa27x-keypad", NULL),
#ifdef CONFIG_LEDS_REGULATOR
	OF_DEV_AUXDATA("regulator-leds", 0, "leds-regulator",
						&keypad_backlight),
#endif
#ifdef CONFIG_SPI_PXA2XX
	OF_DEV_AUXDATA("marvell,pxa910-spi", 0xd401c000, "pxa910-ssp.2", NULL),
#endif
	{}
};

#define MPMU_PHYS_BASE		0xd4050000
#define MPMU_APRR		(0x1020)
#define MPMU_WDTPCR		(0x0200)

/* wdt and cp use the clock */
static __init void enable_pxawdt_clock(void)
{
	void __iomem *mpmu_base;

	mpmu_base = ioremap(MPMU_PHYS_BASE, SZ_8K);
	if (!mpmu_base) {
		pr_err("ioremap mpmu_base failed\n");
		return;
	}

	/* reset/enable WDT clock */
	writel(0x7, mpmu_base + MPMU_WDTPCR);
	readl(mpmu_base + MPMU_WDTPCR);
	writel(0x3, mpmu_base + MPMU_WDTPCR);

	iounmap(mpmu_base);
}

#define GENERIC_COUNTER_PHYS_BASE	0xd4101000
#define CNTCR				0x00 /* Counter Control Register */
#define CNTCR_EN			(1 << 0) /* The counter is enabled */
#define CNTCR_HDBG			(1 << 1) /* Halt on debug */

#define APBC_PHY_BASE			0xd4015000
#define APBC_COUNTER_CLK_SEL		0x64
#define FREQ_HW_CTRL			0x1
static __init void enable_arch_timer(void)
{
	void __iomem *apbc_base, *tmr_base;
	u32 tmp;

	apbc_base = ioremap(APBC_PHY_BASE, SZ_4K);
	if (!apbc_base) {
		pr_err("ioremap apbc_base failed\n");
		return;
	}

	tmr_base = ioremap(GENERIC_COUNTER_PHYS_BASE, SZ_4K);
	if (!tmr_base) {
		pr_err("opremap tmr_base failed\n");
		iounmap(apbc_base);
		return;
	}

	tmp = readl(apbc_base + APBC_COUNTER_CLK_SEL);

	/* Default is 26M/32768 = 0x319 */
	if ((tmp >> 16) != 0x319) {
		pr_warn("Generic Counter step of Low Freq is not right\n");
		iounmap(apbc_base);
		iounmap(tmr_base);
		return;
	}
	/*
	 * bit0 = 1: Generic Counter Frequency control by hardware VCTCXO_EN
	 * VCTCXO_EN = 1, Generic Counter Frequency is 26Mhz;
	 * VCTCXO_EN = 0, Generic Counter Frequency is 32KHz.
	 */
	writel(tmp | FREQ_HW_CTRL, apbc_base + APBC_COUNTER_CLK_SEL);

	/*
	 * NOTE: can not read CNTCR before write, otherwise write will fail
	 * Halt on debug;
	 * start the counter
	 */
	writel(CNTCR_HDBG | CNTCR_EN, tmr_base + CNTCR);

	iounmap(tmr_base);
	iounmap(apbc_base);
}

/* Common APB clock register bit definitions */
#define APBC_APBCLK	(1 << 0)  /* APB Bus Clock Enable */
#define APBC_FNCLK	(1 << 1)  /* Functional Clock Enable */
#define APBC_RST	(1 << 2)  /* Reset Generation */

/* Functional Clock Selection Mask */
#define APBC_FNCLKSEL(x)	(((x) & 0xf) << 4)

#define APBC_TIMER0		0x34
#define APBC_TIMER1		0x44
#define APBC_TIMER2		0x68
static u32 timer_clkreg[] = {
	APBC_TIMER0, APBC_TIMER1, APBC_TIMER2
};

static __init void enable_soc_timer(void)
{
	void __iomem *apbc_base;
	struct device_node *np;
	int tid;

	apbc_base = ioremap(APBC_PHY_BASE, SZ_4K);
	if (!apbc_base) {
		pr_err("ioremap apbc_base failed\n");
		return;
	}

	/* timer APMU clock initialization */
	for_each_compatible_node(np, NULL, "marvell,mmp-timer") {
		if (of_device_is_available(np) &&
			!of_property_read_u32(np, "marvell,timer-id", &tid)) {
			if (tid >= ARRAY_SIZE(timer_clkreg))
				continue;
			/* Select the configurable clock rate to be 3.25MHz */
			writel(APBC_APBCLK | APBC_RST, apbc_base + timer_clkreg[tid]);
			writel(APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3),
				apbc_base + timer_clkreg[tid]);
		}
	}

	iounmap(apbc_base);
}

#define APMU_SDH0      0x54
static void __init pxa1908_sdhc_reset_all(void)
{
	unsigned int reg_tmp;
	void __iomem *apmu_base;

	apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
	if (!apmu_base) {
		pr_err("failed to get apmu_base va\n");
		return;
	}

	/* use bit0 to reset all 3 sdh controls */
	reg_tmp = __raw_readl(apmu_base + APMU_SDH0);
	__raw_writel(reg_tmp & (~1), apmu_base + APMU_SDH0);
	udelay(10);
	__raw_writel(reg_tmp | (1), apmu_base + APMU_SDH0);
}

#define PMU_USB_CLK_RES_CTRL	0x5c
static void __init pxa1908_usb_reset_all(void)
{
	unsigned int reg_tmp;
	void __iomem *apmu_base;

	apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
	if (!apmu_base) {
		pr_err("failed to get apmu_base va\n");
		return;
	}

	/* use bit0 and bit3 to reset all usb controls */
	__raw_writel(0, apmu_base + PMU_USB_CLK_RES_CTRL);
	udelay(10);
	reg_tmp = __raw_readl(apmu_base + PMU_USB_CLK_RES_CTRL);
	if (reg_tmp)
		pr_err("failed to clear usb controller\n");
}

static void __init helanx_irq_init(void)
{
	irqchip_init();
	/* only for wake up */
	mmp_of_wakeup_init();
}

void __init helanx_timer_init(void)
{
	void __iomem *chip_id;

	regs_addr_iomap();

	/* this is early, initialize mmp_chip_id here */
	chip_id = regs_addr_get_va(REGS_ADDR_CIU);
	mmp_chip_id = readl_relaxed(chip_id);

	enable_pxawdt_clock();

#ifdef CONFIG_ARM_ARCH_TIMER
	enable_arch_timer();
#endif

	enable_soc_timer();

#ifdef CONFIG_ARM
	of_clk_init(NULL);
#endif
	clocksource_of_init();

	pxa1908_usb_reset_all();
	pxa1908_sdhc_reset_all();
}

void __init helanx_init_machine(void)
{
	of_platform_populate(NULL, of_default_bus_match_table,
			     helanx_auxdata_lookup, &platform_bus);
}

#ifdef CONFIG_ARM
bool __init helanx_smp_init_ops(void)
{
	mcpm_smp_set_ops();

	return true;
}
#endif

static u32 cp_area_size = 0x02000000;
static u32 cp_area_addr = 0x06000000;
static int __init early_cpmem(char *p)
{
	char *endp;

	cp_area_size = memparse(p, &endp);
	if (*endp == '@')
		cp_area_addr = memparse(endp + 1, NULL);

	return 0;
}
early_param("cpmem", early_cpmem);

static void pxa_reserve_cp_memblock(void)
{
	/* Reserve memory for CP */
	if (cp_area_size) {
		/* panic when CP memory is larger than 64MB */
		BUG_ON(cp_area_size > (1 << 26));
		BUG_ON(memblock_reserve(cp_area_addr, cp_area_size) != 0);
		pr_info("Reserved CP memory: 0x%x@0x%08x\n", cp_area_size, cp_area_addr);
	}
}

static void __init helanx_reserve(void)
{
	pxa_reserve_cp_memblock();
#ifdef CONFIG_MRVL_LOG
	pxa_reserve_logmem();
#endif
}

static const char * const pxa1908_dt_board_compat[] __initconst = {
	"marvell,pxa1908",
	NULL,
};

DT_MACHINE_START(PXA1908_DT, "PXA1908")
#ifdef CONFIG_ARM
	.smp_init	= smp_init_ops(helanx_smp_init_ops),
#endif
	.init_time      = helanx_timer_init,
	.init_irq	= helanx_irq_init,
	.init_machine   = helanx_init_machine,
	.dt_compat      = pxa1908_dt_board_compat,
	.reserve        = helanx_reserve,
MACHINE_END

static const char * const pxa1936_dt_board_compat[] __initconst = {
	"marvell,pxa1936",
	NULL,
};

DT_MACHINE_START(PXA1936_DT, "PXA1936")
	.init_time      = helanx_timer_init,
	.init_irq	= helanx_irq_init,
	.init_machine   = helanx_init_machine,
	.dt_compat      = pxa1936_dt_board_compat,
	.reserve        = helanx_reserve,
MACHINE_END