Commit | Line | Data |
---|---|---|
c20b5c33 | 1 | /* |
4d033556 | 2 | * early_printk_mrst.c - early consoles for Intel MID platforms |
c20b5c33 FT |
3 | * |
4 | * Copyright (c) 2008-2010, Intel Corporation | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; version 2 | |
9 | * of the License. | |
10 | */ | |
11 | ||
4d033556 FT |
12 | /* |
13 | * This file implements two early consoles named mrst and hsu. | |
14 | * mrst is based on Maxim3110 spi-uart device, it exists in both | |
15 | * Moorestown and Medfield platforms, while hsu is based on a High | |
16 | * Speed UART device which only exists in the Medfield platform | |
17 | */ | |
18 | ||
19 | #include <linux/serial_reg.h> | |
20 | #include <linux/serial_mfd.h> | |
c20b5c33 FT |
21 | #include <linux/kmsg_dump.h> |
22 | #include <linux/console.h> | |
23 | #include <linux/kernel.h> | |
4d033556 | 24 | #include <linux/delay.h> |
c20b5c33 FT |
25 | #include <linux/init.h> |
26 | #include <linux/io.h> | |
27 | ||
28 | #include <asm/fixmap.h> | |
29 | #include <asm/pgtable.h> | |
30 | #include <asm/mrst.h> | |
31 | ||
32 | #define MRST_SPI_TIMEOUT 0x200000 | |
33 | #define MRST_REGBASE_SPI0 0xff128000 | |
34 | #define MRST_REGBASE_SPI1 0xff128400 | |
35 | #define MRST_CLK_SPI0_REG 0xff11d86c | |
36 | ||
37 | /* Bit fields in CTRLR0 */ | |
38 | #define SPI_DFS_OFFSET 0 | |
39 | ||
40 | #define SPI_FRF_OFFSET 4 | |
41 | #define SPI_FRF_SPI 0x0 | |
42 | #define SPI_FRF_SSP 0x1 | |
43 | #define SPI_FRF_MICROWIRE 0x2 | |
44 | #define SPI_FRF_RESV 0x3 | |
45 | ||
46 | #define SPI_MODE_OFFSET 6 | |
47 | #define SPI_SCPH_OFFSET 6 | |
48 | #define SPI_SCOL_OFFSET 7 | |
49 | #define SPI_TMOD_OFFSET 8 | |
50 | #define SPI_TMOD_TR 0x0 /* xmit & recv */ | |
51 | #define SPI_TMOD_TO 0x1 /* xmit only */ | |
52 | #define SPI_TMOD_RO 0x2 /* recv only */ | |
53 | #define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ | |
54 | ||
55 | #define SPI_SLVOE_OFFSET 10 | |
56 | #define SPI_SRL_OFFSET 11 | |
57 | #define SPI_CFS_OFFSET 12 | |
58 | ||
59 | /* Bit fields in SR, 7 bits */ | |
60 | #define SR_MASK 0x7f /* cover 7 bits */ | |
61 | #define SR_BUSY (1 << 0) | |
62 | #define SR_TF_NOT_FULL (1 << 1) | |
63 | #define SR_TF_EMPT (1 << 2) | |
64 | #define SR_RF_NOT_EMPT (1 << 3) | |
65 | #define SR_RF_FULL (1 << 4) | |
66 | #define SR_TX_ERR (1 << 5) | |
67 | #define SR_DCOL (1 << 6) | |
68 | ||
69 | struct dw_spi_reg { | |
70 | u32 ctrl0; | |
71 | u32 ctrl1; | |
72 | u32 ssienr; | |
73 | u32 mwcr; | |
74 | u32 ser; | |
75 | u32 baudr; | |
76 | u32 txfltr; | |
77 | u32 rxfltr; | |
78 | u32 txflr; | |
79 | u32 rxflr; | |
80 | u32 sr; | |
81 | u32 imr; | |
82 | u32 isr; | |
83 | u32 risr; | |
84 | u32 txoicr; | |
85 | u32 rxoicr; | |
86 | u32 rxuicr; | |
87 | u32 msticr; | |
88 | u32 icr; | |
89 | u32 dmacr; | |
90 | u32 dmatdlr; | |
91 | u32 dmardlr; | |
92 | u32 idr; | |
93 | u32 version; | |
94 | ||
95 | /* Currently operates as 32 bits, though only the low 16 bits matter */ | |
96 | u32 dr; | |
97 | } __packed; | |
98 | ||
99 | #define dw_readl(dw, name) __raw_readl(&(dw)->name) | |
100 | #define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name) | |
101 | ||
102 | /* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */ | |
103 | static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; | |
104 | ||
105 | static u32 *pclk_spi0; | |
106 | /* Always contains an accessable address, start with 0 */ | |
107 | static struct dw_spi_reg *pspi; | |
108 | ||
109 | static struct kmsg_dumper dw_dumper; | |
110 | static int dumper_registered; | |
111 | ||
112 | static void dw_kmsg_dump(struct kmsg_dumper *dumper, | |
113 | enum kmsg_dump_reason reason, | |
114 | const char *s1, unsigned long l1, | |
115 | const char *s2, unsigned long l2) | |
116 | { | |
117 | int i; | |
118 | ||
119 | /* When run to this, we'd better re-init the HW */ | |
120 | mrst_early_console_init(); | |
121 | ||
122 | for (i = 0; i < l1; i++) | |
123 | early_mrst_console.write(&early_mrst_console, s1 + i, 1); | |
124 | for (i = 0; i < l2; i++) | |
125 | early_mrst_console.write(&early_mrst_console, s2 + i, 1); | |
126 | } | |
127 | ||
128 | /* Set the ratio rate to 115200, 8n1, IRQ disabled */ | |
129 | static void max3110_write_config(void) | |
130 | { | |
131 | u16 config; | |
132 | ||
133 | config = 0xc001; | |
134 | dw_writel(pspi, dr, config); | |
135 | } | |
136 | ||
137 | /* Translate char to a eligible word and send to max3110 */ | |
138 | static void max3110_write_data(char c) | |
139 | { | |
140 | u16 data; | |
141 | ||
142 | data = 0x8000 | c; | |
143 | dw_writel(pspi, dr, data); | |
144 | } | |
145 | ||
146 | void mrst_early_console_init(void) | |
147 | { | |
148 | u32 ctrlr0 = 0; | |
149 | u32 spi0_cdiv; | |
150 | u32 freq; /* Freqency info only need be searched once */ | |
151 | ||
152 | /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ | |
153 | pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | |
154 | MRST_CLK_SPI0_REG); | |
155 | spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; | |
156 | freq = 100000000 / (spi0_cdiv + 1); | |
157 | ||
158 | if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) | |
159 | mrst_spi_paddr = MRST_REGBASE_SPI1; | |
160 | ||
161 | pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | |
162 | mrst_spi_paddr); | |
163 | ||
164 | /* Disable SPI controller */ | |
165 | dw_writel(pspi, ssienr, 0); | |
166 | ||
167 | /* Set control param, 8 bits, transmit only mode */ | |
168 | ctrlr0 = dw_readl(pspi, ctrl0); | |
169 | ||
170 | ctrlr0 &= 0xfcc0; | |
171 | ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) | |
172 | | (SPI_TMOD_TO << SPI_TMOD_OFFSET); | |
173 | dw_writel(pspi, ctrl0, ctrlr0); | |
174 | ||
175 | /* | |
176 | * Change the spi0 clk to comply with 115200 bps, use 100000 to | |
177 | * calculate the clk dividor to make the clock a little slower | |
178 | * than real baud rate. | |
179 | */ | |
180 | dw_writel(pspi, baudr, freq/100000); | |
181 | ||
182 | /* Disable all INT for early phase */ | |
183 | dw_writel(pspi, imr, 0x0); | |
184 | ||
185 | /* Set the cs to spi-uart */ | |
186 | dw_writel(pspi, ser, 0x2); | |
187 | ||
188 | /* Enable the HW, the last step for HW init */ | |
189 | dw_writel(pspi, ssienr, 0x1); | |
190 | ||
191 | /* Set the default configuration */ | |
192 | max3110_write_config(); | |
193 | ||
194 | /* Register the kmsg dumper */ | |
195 | if (!dumper_registered) { | |
196 | dw_dumper.dump = dw_kmsg_dump; | |
197 | kmsg_dump_register(&dw_dumper); | |
198 | dumper_registered = 1; | |
199 | } | |
200 | } | |
201 | ||
202 | /* Slave select should be called in the read/write function */ | |
203 | static void early_mrst_spi_putc(char c) | |
204 | { | |
205 | unsigned int timeout; | |
206 | u32 sr; | |
207 | ||
208 | timeout = MRST_SPI_TIMEOUT; | |
209 | /* Early putc needs to make sure the TX FIFO is not full */ | |
210 | while (--timeout) { | |
211 | sr = dw_readl(pspi, sr); | |
212 | if (!(sr & SR_TF_NOT_FULL)) | |
213 | cpu_relax(); | |
214 | else | |
215 | break; | |
216 | } | |
217 | ||
218 | if (!timeout) | |
219 | pr_warning("MRST earlycon: timed out\n"); | |
220 | else | |
221 | max3110_write_data(c); | |
222 | } | |
223 | ||
224 | /* Early SPI only uses polling mode */ | |
225 | static void early_mrst_spi_write(struct console *con, const char *str, unsigned n) | |
226 | { | |
227 | int i; | |
228 | ||
229 | for (i = 0; i < n && *str; i++) { | |
230 | if (*str == '\n') | |
231 | early_mrst_spi_putc('\r'); | |
232 | early_mrst_spi_putc(*str); | |
233 | str++; | |
234 | } | |
235 | } | |
236 | ||
237 | struct console early_mrst_console = { | |
238 | .name = "earlymrst", | |
239 | .write = early_mrst_spi_write, | |
240 | .flags = CON_PRINTBUFFER, | |
241 | .index = -1, | |
242 | }; | |
4d033556 FT |
243 | |
244 | /* | |
245 | * Following is the early console based on Medfield HSU (High | |
246 | * Speed UART) device. | |
247 | */ | |
248 | #define HSU_PORT2_PADDR 0xffa28180 | |
249 | ||
250 | static void __iomem *phsu; | |
251 | ||
252 | void hsu_early_console_init(void) | |
253 | { | |
254 | u8 lcr; | |
255 | ||
256 | phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | |
257 | HSU_PORT2_PADDR); | |
258 | ||
259 | /* Disable FIFO */ | |
260 | writeb(0x0, phsu + UART_FCR); | |
261 | ||
262 | /* Set to default 115200 bps, 8n1 */ | |
263 | lcr = readb(phsu + UART_LCR); | |
264 | writeb((0x80 | lcr), phsu + UART_LCR); | |
265 | writeb(0x18, phsu + UART_DLL); | |
266 | writeb(lcr, phsu + UART_LCR); | |
267 | writel(0x3600, phsu + UART_MUL*4); | |
268 | ||
269 | writeb(0x8, phsu + UART_MCR); | |
270 | writeb(0x7, phsu + UART_FCR); | |
271 | writeb(0x3, phsu + UART_LCR); | |
272 | ||
273 | /* Clear IRQ status */ | |
274 | readb(phsu + UART_LSR); | |
275 | readb(phsu + UART_RX); | |
276 | readb(phsu + UART_IIR); | |
277 | readb(phsu + UART_MSR); | |
278 | ||
279 | /* Enable FIFO */ | |
280 | writeb(0x7, phsu + UART_FCR); | |
281 | } | |
282 | ||
283 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) | |
284 | ||
285 | static void early_hsu_putc(char ch) | |
286 | { | |
287 | unsigned int timeout = 10000; /* 10ms */ | |
288 | u8 status; | |
289 | ||
290 | while (--timeout) { | |
291 | status = readb(phsu + UART_LSR); | |
292 | if (status & BOTH_EMPTY) | |
293 | break; | |
294 | udelay(1); | |
295 | } | |
296 | ||
297 | /* Only write the char when there was no timeout */ | |
298 | if (timeout) | |
299 | writeb(ch, phsu + UART_TX); | |
300 | } | |
301 | ||
302 | static void early_hsu_write(struct console *con, const char *str, unsigned n) | |
303 | { | |
304 | int i; | |
305 | ||
306 | for (i = 0; i < n && *str; i++) { | |
307 | if (*str == '\n') | |
308 | early_hsu_putc('\r'); | |
309 | early_hsu_putc(*str); | |
310 | str++; | |
311 | } | |
312 | } | |
313 | ||
314 | struct console early_hsu_console = { | |
315 | .name = "earlyhsu", | |
316 | .write = early_hsu_write, | |
317 | .flags = CON_PRINTBUFFER, | |
318 | .index = -1, | |
319 | }; |