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; | |
b595076a | 106 | /* Always contains an accessible address, start with 0 */ |
c20b5c33 FT |
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, | |
e2ae715d | 113 | enum kmsg_dump_reason reason) |
c20b5c33 | 114 | { |
e2ae715d KS |
115 | static char line[1024]; |
116 | size_t len; | |
c20b5c33 FT |
117 | |
118 | /* When run to this, we'd better re-init the HW */ | |
119 | mrst_early_console_init(); | |
120 | ||
e2ae715d KS |
121 | while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) |
122 | early_mrst_console.write(&early_mrst_console, line, len); | |
c20b5c33 FT |
123 | } |
124 | ||
125 | /* Set the ratio rate to 115200, 8n1, IRQ disabled */ | |
126 | static void max3110_write_config(void) | |
127 | { | |
128 | u16 config; | |
129 | ||
130 | config = 0xc001; | |
131 | dw_writel(pspi, dr, config); | |
132 | } | |
133 | ||
134 | /* Translate char to a eligible word and send to max3110 */ | |
135 | static void max3110_write_data(char c) | |
136 | { | |
137 | u16 data; | |
138 | ||
139 | data = 0x8000 | c; | |
140 | dw_writel(pspi, dr, data); | |
141 | } | |
142 | ||
143 | void mrst_early_console_init(void) | |
144 | { | |
145 | u32 ctrlr0 = 0; | |
146 | u32 spi0_cdiv; | |
147 | u32 freq; /* Freqency info only need be searched once */ | |
148 | ||
149 | /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ | |
150 | pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | |
151 | MRST_CLK_SPI0_REG); | |
152 | spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; | |
153 | freq = 100000000 / (spi0_cdiv + 1); | |
154 | ||
155 | if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) | |
156 | mrst_spi_paddr = MRST_REGBASE_SPI1; | |
157 | ||
158 | pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, | |
159 | mrst_spi_paddr); | |
160 | ||
161 | /* Disable SPI controller */ | |
162 | dw_writel(pspi, ssienr, 0); | |
163 | ||
164 | /* Set control param, 8 bits, transmit only mode */ | |
165 | ctrlr0 = dw_readl(pspi, ctrl0); | |
166 | ||
167 | ctrlr0 &= 0xfcc0; | |
168 | ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) | |
169 | | (SPI_TMOD_TO << SPI_TMOD_OFFSET); | |
170 | dw_writel(pspi, ctrl0, ctrlr0); | |
171 | ||
172 | /* | |
173 | * Change the spi0 clk to comply with 115200 bps, use 100000 to | |
174 | * calculate the clk dividor to make the clock a little slower | |
175 | * than real baud rate. | |
176 | */ | |
177 | dw_writel(pspi, baudr, freq/100000); | |
178 | ||
179 | /* Disable all INT for early phase */ | |
180 | dw_writel(pspi, imr, 0x0); | |
181 | ||
182 | /* Set the cs to spi-uart */ | |
183 | dw_writel(pspi, ser, 0x2); | |
184 | ||
185 | /* Enable the HW, the last step for HW init */ | |
186 | dw_writel(pspi, ssienr, 0x1); | |
187 | ||
188 | /* Set the default configuration */ | |
189 | max3110_write_config(); | |
190 | ||
191 | /* Register the kmsg dumper */ | |
192 | if (!dumper_registered) { | |
193 | dw_dumper.dump = dw_kmsg_dump; | |
194 | kmsg_dump_register(&dw_dumper); | |
195 | dumper_registered = 1; | |
196 | } | |
197 | } | |
198 | ||
199 | /* Slave select should be called in the read/write function */ | |
200 | static void early_mrst_spi_putc(char c) | |
201 | { | |
202 | unsigned int timeout; | |
203 | u32 sr; | |
204 | ||
205 | timeout = MRST_SPI_TIMEOUT; | |
206 | /* Early putc needs to make sure the TX FIFO is not full */ | |
207 | while (--timeout) { | |
208 | sr = dw_readl(pspi, sr); | |
209 | if (!(sr & SR_TF_NOT_FULL)) | |
210 | cpu_relax(); | |
211 | else | |
212 | break; | |
213 | } | |
214 | ||
215 | if (!timeout) | |
216 | pr_warning("MRST earlycon: timed out\n"); | |
217 | else | |
218 | max3110_write_data(c); | |
219 | } | |
220 | ||
221 | /* Early SPI only uses polling mode */ | |
222 | static void early_mrst_spi_write(struct console *con, const char *str, unsigned n) | |
223 | { | |
224 | int i; | |
225 | ||
226 | for (i = 0; i < n && *str; i++) { | |
227 | if (*str == '\n') | |
228 | early_mrst_spi_putc('\r'); | |
229 | early_mrst_spi_putc(*str); | |
230 | str++; | |
231 | } | |
232 | } | |
233 | ||
234 | struct console early_mrst_console = { | |
235 | .name = "earlymrst", | |
236 | .write = early_mrst_spi_write, | |
237 | .flags = CON_PRINTBUFFER, | |
238 | .index = -1, | |
239 | }; | |
4d033556 FT |
240 | |
241 | /* | |
242 | * Following is the early console based on Medfield HSU (High | |
243 | * Speed UART) device. | |
244 | */ | |
b82e324b | 245 | #define HSU_PORT_BASE 0xffa28080 |
4d033556 FT |
246 | |
247 | static void __iomem *phsu; | |
248 | ||
b82e324b | 249 | void hsu_early_console_init(const char *s) |
4d033556 | 250 | { |
b82e324b | 251 | unsigned long paddr, port = 0; |
4d033556 FT |
252 | u8 lcr; |
253 | ||
b82e324b MW |
254 | /* |
255 | * Select the early HSU console port if specified by user in the | |
256 | * kernel command line. | |
257 | */ | |
258 | if (*s && !kstrtoul(s, 10, &port)) | |
259 | port = clamp_val(port, 0, 2); | |
260 | ||
261 | paddr = HSU_PORT_BASE + port * 0x80; | |
262 | phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); | |
4d033556 FT |
263 | |
264 | /* Disable FIFO */ | |
265 | writeb(0x0, phsu + UART_FCR); | |
266 | ||
267 | /* Set to default 115200 bps, 8n1 */ | |
268 | lcr = readb(phsu + UART_LCR); | |
269 | writeb((0x80 | lcr), phsu + UART_LCR); | |
270 | writeb(0x18, phsu + UART_DLL); | |
271 | writeb(lcr, phsu + UART_LCR); | |
272 | writel(0x3600, phsu + UART_MUL*4); | |
273 | ||
274 | writeb(0x8, phsu + UART_MCR); | |
275 | writeb(0x7, phsu + UART_FCR); | |
276 | writeb(0x3, phsu + UART_LCR); | |
277 | ||
278 | /* Clear IRQ status */ | |
279 | readb(phsu + UART_LSR); | |
280 | readb(phsu + UART_RX); | |
281 | readb(phsu + UART_IIR); | |
282 | readb(phsu + UART_MSR); | |
283 | ||
284 | /* Enable FIFO */ | |
285 | writeb(0x7, phsu + UART_FCR); | |
286 | } | |
287 | ||
288 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) | |
289 | ||
290 | static void early_hsu_putc(char ch) | |
291 | { | |
292 | unsigned int timeout = 10000; /* 10ms */ | |
293 | u8 status; | |
294 | ||
295 | while (--timeout) { | |
296 | status = readb(phsu + UART_LSR); | |
297 | if (status & BOTH_EMPTY) | |
298 | break; | |
299 | udelay(1); | |
300 | } | |
301 | ||
302 | /* Only write the char when there was no timeout */ | |
303 | if (timeout) | |
304 | writeb(ch, phsu + UART_TX); | |
305 | } | |
306 | ||
307 | static void early_hsu_write(struct console *con, const char *str, unsigned n) | |
308 | { | |
309 | int i; | |
310 | ||
311 | for (i = 0; i < n && *str; i++) { | |
312 | if (*str == '\n') | |
313 | early_hsu_putc('\r'); | |
314 | early_hsu_putc(*str); | |
315 | str++; | |
316 | } | |
317 | } | |
318 | ||
319 | struct console early_hsu_console = { | |
320 | .name = "earlyhsu", | |
321 | .write = early_hsu_write, | |
322 | .flags = CON_PRINTBUFFER, | |
323 | .index = -1, | |
324 | }; |