xref: /linux/arch/arm/mach-lpc32xx/common.c (revision df38b24fa87ba4c386e50b83befd93d0518b7ea8)
1fc982e1cSKevin Wells /*
2fc982e1cSKevin Wells  * arch/arm/mach-lpc32xx/common.c
3fc982e1cSKevin Wells  *
4fc982e1cSKevin Wells  * Author: Kevin Wells <kevin.wells@nxp.com>
5fc982e1cSKevin Wells  *
6fc982e1cSKevin Wells  * Copyright (C) 2010 NXP Semiconductors
7fc982e1cSKevin Wells  *
8fc982e1cSKevin Wells  * This program is free software; you can redistribute it and/or modify
9fc982e1cSKevin Wells  * it under the terms of the GNU General Public License as published by
10fc982e1cSKevin Wells  * the Free Software Foundation; either version 2 of the License, or
11fc982e1cSKevin Wells  * (at your option) any later version.
12fc982e1cSKevin Wells  *
13fc982e1cSKevin Wells  * This program is distributed in the hope that it will be useful,
14fc982e1cSKevin Wells  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15fc982e1cSKevin Wells  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16fc982e1cSKevin Wells  * GNU General Public License for more details.
17fc982e1cSKevin Wells  */
18fc982e1cSKevin Wells 
19fc982e1cSKevin Wells #include <linux/init.h>
20fc982e1cSKevin Wells #include <linux/platform_device.h>
21fc982e1cSKevin Wells #include <linux/interrupt.h>
22fc982e1cSKevin Wells #include <linux/irq.h>
23fc982e1cSKevin Wells #include <linux/err.h>
24fc982e1cSKevin Wells #include <linux/i2c.h>
25fc982e1cSKevin Wells #include <linux/i2c-pnx.h>
26fc982e1cSKevin Wells #include <linux/io.h>
27fc982e1cSKevin Wells 
28fc982e1cSKevin Wells #include <asm/mach/map.h>
29e39942f5SAlexandre Pereira da Silva #include <asm/system_info.h>
30fc982e1cSKevin Wells 
31fc982e1cSKevin Wells #include <mach/hardware.h>
32fc982e1cSKevin Wells #include <mach/platform.h>
33fc982e1cSKevin Wells #include "common.h"
34fc982e1cSKevin Wells 
35fc982e1cSKevin Wells /*
36fc982e1cSKevin Wells  * Returns the unique ID for the device
37fc982e1cSKevin Wells  */
38fc982e1cSKevin Wells void lpc32xx_get_uid(u32 devid[4])
39fc982e1cSKevin Wells {
40fc982e1cSKevin Wells 	int i;
41fc982e1cSKevin Wells 
42fc982e1cSKevin Wells 	for (i = 0; i < 4; i++)
43fc982e1cSKevin Wells 		devid[i] = __raw_readl(LPC32XX_CLKPWR_DEVID(i << 2));
44fc982e1cSKevin Wells }
45fc982e1cSKevin Wells 
46fc982e1cSKevin Wells /*
47fc982e1cSKevin Wells  * Returns SYSCLK source
48fc982e1cSKevin Wells  * 0 = PLL397, 1 = main oscillator
49fc982e1cSKevin Wells  */
50fc982e1cSKevin Wells int clk_is_sysclk_mainosc(void)
51fc982e1cSKevin Wells {
52fc982e1cSKevin Wells 	if ((__raw_readl(LPC32XX_CLKPWR_SYSCLK_CTRL) &
53fc982e1cSKevin Wells 		LPC32XX_CLKPWR_SYSCTRL_SYSCLKMUX) == 0)
54fc982e1cSKevin Wells 		return 1;
55fc982e1cSKevin Wells 
56fc982e1cSKevin Wells 	return 0;
57fc982e1cSKevin Wells }
58fc982e1cSKevin Wells 
59fc982e1cSKevin Wells /*
60fc982e1cSKevin Wells  * System reset via the watchdog timer
61fc982e1cSKevin Wells  */
62b23fcd90SRussell King static void lpc32xx_watchdog_reset(void)
63fc982e1cSKevin Wells {
64fc982e1cSKevin Wells 	/* Make sure WDT clocks are enabled */
65fc982e1cSKevin Wells 	__raw_writel(LPC32XX_CLKPWR_PWMCLK_WDOG_EN,
66fc982e1cSKevin Wells 		LPC32XX_CLKPWR_TIMER_CLK_CTRL);
67fc982e1cSKevin Wells 
68fc982e1cSKevin Wells 	/* Instant assert of RESETOUT_N with pulse length 1mS */
69fc982e1cSKevin Wells 	__raw_writel(13000, io_p2v(LPC32XX_WDTIM_BASE + 0x18));
70fc982e1cSKevin Wells 	__raw_writel(0x70, io_p2v(LPC32XX_WDTIM_BASE + 0xC));
71fc982e1cSKevin Wells }
72fc982e1cSKevin Wells 
73fc982e1cSKevin Wells /*
74fc982e1cSKevin Wells  * Detects and returns IRAM size for the device variation
75fc982e1cSKevin Wells  */
76fc982e1cSKevin Wells #define LPC32XX_IRAM_BANK_SIZE SZ_128K
77fc982e1cSKevin Wells static u32 iram_size;
78fc982e1cSKevin Wells u32 lpc32xx_return_iram_size(void)
79fc982e1cSKevin Wells {
80fc982e1cSKevin Wells 	if (iram_size == 0) {
81fc982e1cSKevin Wells 		u32 savedval1, savedval2;
82fc982e1cSKevin Wells 		void __iomem *iramptr1, *iramptr2;
83fc982e1cSKevin Wells 
84fc982e1cSKevin Wells 		iramptr1 = io_p2v(LPC32XX_IRAM_BASE);
85fc982e1cSKevin Wells 		iramptr2 = io_p2v(LPC32XX_IRAM_BASE + LPC32XX_IRAM_BANK_SIZE);
86fc982e1cSKevin Wells 		savedval1 = __raw_readl(iramptr1);
87fc982e1cSKevin Wells 		savedval2 = __raw_readl(iramptr2);
88fc982e1cSKevin Wells 
89fc982e1cSKevin Wells 		if (savedval1 == savedval2) {
90fc982e1cSKevin Wells 			__raw_writel(savedval2 + 1, iramptr2);
91fc982e1cSKevin Wells 			if (__raw_readl(iramptr1) == savedval2 + 1)
92fc982e1cSKevin Wells 				iram_size = LPC32XX_IRAM_BANK_SIZE;
93fc982e1cSKevin Wells 			else
94fc982e1cSKevin Wells 				iram_size = LPC32XX_IRAM_BANK_SIZE * 2;
95fc982e1cSKevin Wells 			__raw_writel(savedval2, iramptr2);
96fc982e1cSKevin Wells 		} else
97fc982e1cSKevin Wells 			iram_size = LPC32XX_IRAM_BANK_SIZE * 2;
98fc982e1cSKevin Wells 	}
99fc982e1cSKevin Wells 
100fc982e1cSKevin Wells 	return iram_size;
101fc982e1cSKevin Wells }
102fc982e1cSKevin Wells 
103fc982e1cSKevin Wells /*
104fc982e1cSKevin Wells  * Computes PLL rate from PLL register and input clock
105fc982e1cSKevin Wells  */
106fc982e1cSKevin Wells u32 clk_check_pll_setup(u32 ifreq, struct clk_pll_setup *pllsetup)
107fc982e1cSKevin Wells {
108fc982e1cSKevin Wells 	u32 ilfreq, p, m, n, fcco, fref, cfreq;
109fc982e1cSKevin Wells 	int mode;
110fc982e1cSKevin Wells 
111fc982e1cSKevin Wells 	/*
112fc982e1cSKevin Wells 	 * PLL requirements
113fc982e1cSKevin Wells 	 * ifreq must be >= 1MHz and <= 20MHz
114fc982e1cSKevin Wells 	 * FCCO must be >= 156MHz and <= 320MHz
115fc982e1cSKevin Wells 	 * FREF must be >= 1MHz and <= 27MHz
116fc982e1cSKevin Wells 	 * Assume the passed input data is not valid
117fc982e1cSKevin Wells 	 */
118fc982e1cSKevin Wells 
119fc982e1cSKevin Wells 	ilfreq = ifreq;
120fc982e1cSKevin Wells 	m = pllsetup->pll_m;
121fc982e1cSKevin Wells 	n = pllsetup->pll_n;
122fc982e1cSKevin Wells 	p = pllsetup->pll_p;
123fc982e1cSKevin Wells 
124fc982e1cSKevin Wells 	mode = (pllsetup->cco_bypass_b15 << 2) |
125fc982e1cSKevin Wells 		(pllsetup->direct_output_b14 << 1) |
126fc982e1cSKevin Wells 	pllsetup->fdbk_div_ctrl_b13;
127fc982e1cSKevin Wells 
128fc982e1cSKevin Wells 	switch (mode) {
129fc982e1cSKevin Wells 	case 0x0: /* Non-integer mode */
130fc982e1cSKevin Wells 		cfreq = (m * ilfreq) / (2 * p * n);
131fc982e1cSKevin Wells 		fcco = (m * ilfreq) / n;
132fc982e1cSKevin Wells 		fref = ilfreq / n;
133fc982e1cSKevin Wells 		break;
134fc982e1cSKevin Wells 
135fc982e1cSKevin Wells 	case 0x1: /* integer mode */
136fc982e1cSKevin Wells 		cfreq = (m * ilfreq) / n;
137fc982e1cSKevin Wells 		fcco = (m * ilfreq) / (n * 2 * p);
138fc982e1cSKevin Wells 		fref = ilfreq / n;
139fc982e1cSKevin Wells 		break;
140fc982e1cSKevin Wells 
141fc982e1cSKevin Wells 	case 0x2:
142fc982e1cSKevin Wells 	case 0x3: /* Direct mode */
143fc982e1cSKevin Wells 		cfreq = (m * ilfreq) / n;
144fc982e1cSKevin Wells 		fcco = cfreq;
145fc982e1cSKevin Wells 		fref = ilfreq / n;
146fc982e1cSKevin Wells 		break;
147fc982e1cSKevin Wells 
148fc982e1cSKevin Wells 	case 0x4:
149fc982e1cSKevin Wells 	case 0x5: /* Bypass mode */
150fc982e1cSKevin Wells 		cfreq = ilfreq / (2 * p);
151fc982e1cSKevin Wells 		fcco = 156000000;
152fc982e1cSKevin Wells 		fref = 1000000;
153fc982e1cSKevin Wells 		break;
154fc982e1cSKevin Wells 
155fc982e1cSKevin Wells 	case 0x6:
156fc982e1cSKevin Wells 	case 0x7: /* Direct bypass mode */
157fc982e1cSKevin Wells 	default:
158fc982e1cSKevin Wells 		cfreq = ilfreq;
159fc982e1cSKevin Wells 		fcco = 156000000;
160fc982e1cSKevin Wells 		fref = 1000000;
161fc982e1cSKevin Wells 		break;
162fc982e1cSKevin Wells 	}
163fc982e1cSKevin Wells 
164fc982e1cSKevin Wells 	if (fcco < 156000000 || fcco > 320000000)
165fc982e1cSKevin Wells 		cfreq = 0;
166fc982e1cSKevin Wells 
167fc982e1cSKevin Wells 	if (fref < 1000000 || fref > 27000000)
168fc982e1cSKevin Wells 		cfreq = 0;
169fc982e1cSKevin Wells 
170fc982e1cSKevin Wells 	return (u32) cfreq;
171fc982e1cSKevin Wells }
172fc982e1cSKevin Wells 
173fc982e1cSKevin Wells u32 clk_get_pclk_div(void)
174fc982e1cSKevin Wells {
175fc982e1cSKevin Wells 	return 1 + ((__raw_readl(LPC32XX_CLKPWR_HCLK_DIV) >> 2) & 0x1F);
176fc982e1cSKevin Wells }
177fc982e1cSKevin Wells 
178fc982e1cSKevin Wells static struct map_desc lpc32xx_io_desc[] __initdata = {
179fc982e1cSKevin Wells 	{
180*df38b24fSArnd Bergmann 		.virtual	= (unsigned long)IO_ADDRESS(LPC32XX_AHB0_START),
181fc982e1cSKevin Wells 		.pfn		= __phys_to_pfn(LPC32XX_AHB0_START),
182fc982e1cSKevin Wells 		.length		= LPC32XX_AHB0_SIZE,
183fc982e1cSKevin Wells 		.type		= MT_DEVICE
184fc982e1cSKevin Wells 	},
185fc982e1cSKevin Wells 	{
186*df38b24fSArnd Bergmann 		.virtual	= (unsigned long)IO_ADDRESS(LPC32XX_AHB1_START),
187fc982e1cSKevin Wells 		.pfn		= __phys_to_pfn(LPC32XX_AHB1_START),
188fc982e1cSKevin Wells 		.length		= LPC32XX_AHB1_SIZE,
189fc982e1cSKevin Wells 		.type		= MT_DEVICE
190fc982e1cSKevin Wells 	},
191fc982e1cSKevin Wells 	{
192*df38b24fSArnd Bergmann 		.virtual	= (unsigned long)IO_ADDRESS(LPC32XX_FABAPB_START),
193fc982e1cSKevin Wells 		.pfn		= __phys_to_pfn(LPC32XX_FABAPB_START),
194fc982e1cSKevin Wells 		.length		= LPC32XX_FABAPB_SIZE,
195fc982e1cSKevin Wells 		.type		= MT_DEVICE
196fc982e1cSKevin Wells 	},
197fc982e1cSKevin Wells 	{
198*df38b24fSArnd Bergmann 		.virtual	= (unsigned long)IO_ADDRESS(LPC32XX_IRAM_BASE),
199fc982e1cSKevin Wells 		.pfn		= __phys_to_pfn(LPC32XX_IRAM_BASE),
200fc982e1cSKevin Wells 		.length		= (LPC32XX_IRAM_BANK_SIZE * 2),
201fc982e1cSKevin Wells 		.type		= MT_DEVICE
202fc982e1cSKevin Wells 	},
203fc982e1cSKevin Wells };
204fc982e1cSKevin Wells 
205fc982e1cSKevin Wells void __init lpc32xx_map_io(void)
206fc982e1cSKevin Wells {
207fc982e1cSKevin Wells 	iotable_init(lpc32xx_io_desc, ARRAY_SIZE(lpc32xx_io_desc));
208fc982e1cSKevin Wells }
209b23fcd90SRussell King 
210b23fcd90SRussell King void lpc23xx_restart(char mode, const char *cmd)
211b23fcd90SRussell King {
212b23fcd90SRussell King 	switch (mode) {
213b23fcd90SRussell King 	case 's':
214b23fcd90SRussell King 	case 'h':
215b23fcd90SRussell King 		lpc32xx_watchdog_reset();
216b23fcd90SRussell King 		break;
217b23fcd90SRussell King 
218b23fcd90SRussell King 	default:
219b23fcd90SRussell King 		/* Do nothing */
220b23fcd90SRussell King 		break;
221b23fcd90SRussell King 	}
222b23fcd90SRussell King 
223b23fcd90SRussell King 	/* Wait for watchdog to reset system */
224b23fcd90SRussell King 	while (1)
225b23fcd90SRussell King 		;
226b23fcd90SRussell King }
227be20dbc8SRoland Stigge 
228e39942f5SAlexandre Pereira da Silva static int __init lpc32xx_check_uid(void)
229be20dbc8SRoland Stigge {
230be20dbc8SRoland Stigge 	u32 uid[4];
231be20dbc8SRoland Stigge 
232be20dbc8SRoland Stigge 	lpc32xx_get_uid(uid);
233be20dbc8SRoland Stigge 
234be20dbc8SRoland Stigge 	printk(KERN_INFO "LPC32XX unique ID: %08x%08x%08x%08x\n",
235be20dbc8SRoland Stigge 		uid[3], uid[2], uid[1], uid[0]);
236be20dbc8SRoland Stigge 
237e39942f5SAlexandre Pereira da Silva 	if (!system_serial_low && !system_serial_high) {
238e39942f5SAlexandre Pereira da Silva 		system_serial_low = uid[0];
239e39942f5SAlexandre Pereira da Silva 		system_serial_high = uid[1];
240e39942f5SAlexandre Pereira da Silva 	}
241e39942f5SAlexandre Pereira da Silva 
242be20dbc8SRoland Stigge 	return 1;
243be20dbc8SRoland Stigge }
244e39942f5SAlexandre Pereira da Silva arch_initcall(lpc32xx_check_uid);
245