blob: 614bd5663739bf6c27583f4730c3077161d151c3 [file] [log] [blame]
Ajay Dudanib01e5062011-12-03 23:23:42 -08001/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of Code Aurora Forum, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <string.h>
30#include <stdlib.h>
31#include <debug.h>
32#include <reg.h>
33#include <sys/types.h>
34#include <platform/iomap.h>
35#include <platform/irqs.h>
36#include <platform/interrupts.h>
37#include <platform/clock.h>
38#include <platform/gpio.h>
39#include <uart_dm.h>
40#include <gsbi.h>
41
42#ifndef NULL
43#define NULL 0
44#endif
45
46extern void dsb(void);
47
48static int uart_init_flag = 0;
49
50/* Note:
51 * This is a basic implementation of UART_DM protocol. More focus has been
52 * given on simplicity than efficiency. Few of the things to be noted are:
53 * - RX path may not be suitable for multi-threaded scenaraio because of the
54 * use of static variables. TX path shouldn't have any problem though. If
55 * multi-threaded support is required, a simple data-structure can
56 * be maintained for each thread.
57 * - Right now we are using polling method than interrupt based.
58 * - We are using legacy UART protocol without Data Mover.
59 * - Not all interrupts and error events are handled.
60 * - While waiting Watchdog hasn't been taken into consideration.
61 */
62
63#define PACK_CHARS_INTO_WORDS(a, cnt, word) { \
64 word = 0; \
65 for(int j=0; j < (int)cnt; j++) \
66 { \
67 word |= (a[j] & 0xff) \
68 << (j * 8); \
69 } \
70 }
71
72/* Static Function Prototype Declarations */
73static unsigned int msm_boot_uart_dm_gsbi_init(uint8_t id);
74static unsigned int msm_boot_uart_replace_lr_with_cr(char *data_in,
75 int num_of_chars,
76 char *data_out,
77 int *num_of_chars_out);
78static unsigned int msm_boot_uart_dm_init(uint8_t id);
79static unsigned int msm_boot_uart_dm_read(uint8_t id, unsigned int *data,
80 int wait);
81static unsigned int msm_boot_uart_dm_write(uint8_t id, char *data,
82 unsigned int num_of_chars);
83static unsigned int msm_boot_uart_dm_init_rx_transfer(uint8_t id);
84static unsigned int msm_boot_uart_dm_reset(uint8_t id);
85
86/* Keep track of gsbi vs port mapping.
87 */
88static uint8_t gsbi_lookup[4];
89
90/* Extern functions */
91void udelay(unsigned usecs);
92
93/*
94 * Helper function to replace Line Feed char "\n" with
95 * Carriage Return "\r\n".
96 * Currently keeping it simple than efficient
97 */
98static unsigned int
99msm_boot_uart_replace_lr_with_cr(char *data_in,
100 int num_of_chars,
101 char *data_out, int *num_of_chars_out)
102{
103 int i = 0, j = 0;
104
105 if ((data_in == NULL) || (data_out == NULL) || (num_of_chars < 0)) {
106 return MSM_BOOT_UART_DM_E_INVAL;
107 }
108
109 for (i = 0, j = 0; i < num_of_chars; i++, j++) {
110 if (data_in[i] == '\n') {
111 data_out[j++] = '\r';
112 }
113
114 data_out[j] = data_in[i];
115 }
116
117 *num_of_chars_out = j;
118
119 return MSM_BOOT_UART_DM_E_SUCCESS;
120}
121
122/*
123 * Initialize and configure GSBI for operation
124 */
125static unsigned int msm_boot_uart_dm_gsbi_init(uint8_t id)
126{
127 /* Configure the uart clock */
128 clock_config_uart_dm(id);
129 dsb();
130
131 /* Configure GPIO to provide connectivity between GSBI
132 product ports and chip pads */
133 gpio_config_uart_dm(id);
134 dsb();
135
136 /* Configure Data Mover for GSBI operation.
137 * Currently not supported. */
138
139 /* Configure GSBI for UART_DM protocol.
140 * I2C on 2 ports, UART (without HS flow control) on the other 2. */
141 writel(GSBI_PROTOCOL_CODE_I2C_UART << GSBI_CTRL_REG_PROTOCOL_CODE_S,
142 GSBI_CTRL_REG(id));
143 dsb();
144
145 /* Configure clock selection register for tx and rx rates.
146 * Selecting 115.2k for both RX and TX.
147 */
148 writel(UART_DM_CLK_RX_TX_BIT_RATE, MSM_BOOT_UART_DM_CSR(id));
149 dsb();
150
151 return MSM_BOOT_UART_DM_E_SUCCESS;
152}
153
154/*
155 * Reset the UART
156 */
157static unsigned int msm_boot_uart_dm_reset(uint8_t id)
158{
159 writel(MSM_BOOT_UART_DM_CMD_RESET_RX, MSM_BOOT_UART_DM_CR(id));
160 writel(MSM_BOOT_UART_DM_CMD_RESET_TX, MSM_BOOT_UART_DM_CR(id));
161 writel(MSM_BOOT_UART_DM_CMD_RESET_ERR_STAT, MSM_BOOT_UART_DM_CR(id));
162 writel(MSM_BOOT_UART_DM_CMD_RES_TX_ERR, MSM_BOOT_UART_DM_CR(id));
163 writel(MSM_BOOT_UART_DM_CMD_RES_STALE_INT, MSM_BOOT_UART_DM_CR(id));
164
165 return MSM_BOOT_UART_DM_E_SUCCESS;
166}
167
168/*
169 * Initialize UART_DM - configure clock and required registers.
170 */
171static unsigned int msm_boot_uart_dm_init(uint8_t id)
172{
173 /* Configure GSBI for uart dm */
174 msm_boot_uart_dm_gsbi_init(id);
175
176 /* Configure UART mode registers MR1 and MR2 */
177 /* Hardware flow control isn't supported */
178 writel(0x0, MSM_BOOT_UART_DM_MR1(id));
179
180 /* 8-N-1 configuration: 8 data bits - No parity - 1 stop bit */
181 writel(MSM_BOOT_UART_DM_8_N_1_MODE, MSM_BOOT_UART_DM_MR2(id));
182
183 /* Configure Interrupt Mask register IMR */
184 writel(MSM_BOOT_UART_DM_IMR_ENABLED, MSM_BOOT_UART_DM_IMR(id));
185
186 /* Configure Tx and Rx watermarks configuration registers */
187 /* TX watermark value is set to 0 - interrupt is generated when
188 * FIFO level is less than or equal to 0 */
189 writel(MSM_BOOT_UART_DM_TFW_VALUE, MSM_BOOT_UART_DM_TFWR(id));
190
191 /* RX watermark value */
192 writel(MSM_BOOT_UART_DM_RFW_VALUE, MSM_BOOT_UART_DM_RFWR(id));
193
194 /* Configure Interrupt Programming Register */
195 /* Set initial Stale timeout value */
196 writel(MSM_BOOT_UART_DM_STALE_TIMEOUT_LSB, MSM_BOOT_UART_DM_IPR(id));
197
198 /* Configure IRDA if required */
199 /* Disabling IRDA mode */
200 writel(0x0, MSM_BOOT_UART_DM_IRDA(id));
201
202 /* Configure and enable sim interface if required */
203
204 /* Configure hunt character value in HCR register */
205 /* Keep it in reset state */
206 writel(0x0, MSM_BOOT_UART_DM_HCR(id));
207
208 /* Configure Rx FIFO base address */
209 /* Both TX/RX shares same SRAM and default is half-n-half.
210 * Sticking with default value now.
211 * As such RAM size is (2^RAM_ADDR_WIDTH, 32-bit entries).
212 * We have found RAM_ADDR_WIDTH = 0x7f */
213
214 /* Issue soft reset command */
215 msm_boot_uart_dm_reset(id);
216
217 /* Enable/Disable Rx/Tx DM interfaces */
218 /* Data Mover not currently utilized. */
219 writel(0x0, MSM_BOOT_UART_DM_DMEN(id));
220
221 /* Enable transmitter and receiver */
222 writel(MSM_BOOT_UART_DM_CR_RX_ENABLE, MSM_BOOT_UART_DM_CR(id));
223 writel(MSM_BOOT_UART_DM_CR_TX_ENABLE, MSM_BOOT_UART_DM_CR(id));
224
225 /* Initialize Receive Path */
226 msm_boot_uart_dm_init_rx_transfer(id);
227
228 return MSM_BOOT_UART_DM_E_SUCCESS;
229}
230
231/*
232 * Initialize Receive Path
233 */
234static unsigned int msm_boot_uart_dm_init_rx_transfer(uint8_t id)
235{
236 writel(MSM_BOOT_UART_DM_GCMD_DIS_STALE_EVT, MSM_BOOT_UART_DM_CR(id));
237 writel(MSM_BOOT_UART_DM_CMD_RES_STALE_INT, MSM_BOOT_UART_DM_CR(id));
238 writel(MSM_BOOT_UART_DM_DMRX_DEF_VALUE, MSM_BOOT_UART_DM_DMRX(id));
239 writel(MSM_BOOT_UART_DM_GCMD_ENA_STALE_EVT, MSM_BOOT_UART_DM_CR(id));
240
241 return MSM_BOOT_UART_DM_E_SUCCESS;
242}
243
244/*
245 * UART Receive operation
246 * Reads a word from the RX FIFO.
247 */
248static unsigned int
249msm_boot_uart_dm_read(uint8_t id, unsigned int *data, int wait)
250{
251 static int rx_last_snap_count = 0;
252 static int rx_chars_read_since_last_xfer = 0;
253
254 if (data == NULL) {
255 return MSM_BOOT_UART_DM_E_INVAL;
256 }
257
258 /* We will be polling RXRDY status bit */
259 while (!(readl(MSM_BOOT_UART_DM_SR(id)) & MSM_BOOT_UART_DM_SR_RXRDY)) {
260 /* if this is not a blocking call, we'll just return */
261 if (!wait) {
262 return MSM_BOOT_UART_DM_E_RX_NOT_READY;
263 }
264 }
265
266 /* Check for Overrun error. We'll just reset Error Status */
267 if (readl(MSM_BOOT_UART_DM_SR(id)) & MSM_BOOT_UART_DM_SR_UART_OVERRUN) {
268 writel(MSM_BOOT_UART_DM_CMD_RESET_ERR_STAT,
269 MSM_BOOT_UART_DM_CR(id));
270 }
271
272 /* RX FIFO is ready; read a word. */
273 *data = readl(MSM_BOOT_UART_DM_RF(id, 0));
274
275 /* increment the total count of chars we've read so far */
276 rx_chars_read_since_last_xfer += 4;
277
278 /* Rx transfer ends when one of the conditions is met:
279 * - The number of characters received since the end of the previous xfer
280 * equals the value written to DMRX at Transfer Initialization
281 * - A stale event occurred
282 */
283
284 /* If RX transfer has not ended yet */
285 if (rx_last_snap_count == 0) {
286 /* Check if we've received stale event */
287 if (readl(MSM_BOOT_UART_DM_MISR(id)) & MSM_BOOT_UART_DM_RXSTALE) {
288 /* Send command to reset stale interrupt */
289 writel(MSM_BOOT_UART_DM_CMD_RES_STALE_INT,
290 MSM_BOOT_UART_DM_CR(id));
291 }
292
293 /* Check if we haven't read more than DMRX value */
294 else if ((unsigned int)rx_chars_read_since_last_xfer <
295 readl(MSM_BOOT_UART_DM_DMRX(id))) {
296 /* We can still continue reading before initializing RX transfer */
297 return MSM_BOOT_UART_DM_E_SUCCESS;
298 }
299
300 /* If we've reached here it means RX xfer end conditions been met */
301
302 /* Read UART_DM_RX_TOTAL_SNAP register to know how many valid chars
303 * we've read so far since last transfer */
304 rx_last_snap_count = readl(MSM_BOOT_UART_DM_RX_TOTAL_SNAP(id));
305
306 }
307
308 /* If there are still data left in FIFO we'll read them before
309 * initializing RX Transfer again */
310 if ((rx_last_snap_count - rx_chars_read_since_last_xfer) >= 0) {
311 return MSM_BOOT_UART_DM_E_SUCCESS;
312 }
313
314 msm_boot_uart_dm_init_rx_transfer(id);
315 rx_last_snap_count = 0;
316 rx_chars_read_since_last_xfer = 0;
317
318 return MSM_BOOT_UART_DM_E_SUCCESS;
319}
320
321/*
322 * UART transmit operation
323 */
324static unsigned int
325msm_boot_uart_dm_write(uint8_t id, char *data, unsigned int num_of_chars)
326{
327 unsigned int tx_word_count = 0;
328 unsigned int tx_char_left = 0, tx_char = 0;
329 unsigned int tx_word = 0;
330 int i = 0;
331 char *tx_data = NULL;
332 char new_data[1024];
333
334 if ((data == NULL) || (num_of_chars <= 0)) {
335 return MSM_BOOT_UART_DM_E_INVAL;
336 }
337
338 /* Replace line-feed (/n) with carriage-return + line-feed (/r/n) */
339
340 msm_boot_uart_replace_lr_with_cr(data, num_of_chars, new_data, &i);
341
342 tx_data = new_data;
343 num_of_chars = i;
344
345 /* Write to NO_CHARS_FOR_TX register number of characters
346 * to be transmitted. However, before writing TX_FIFO must
347 * be empty as indicated by TX_READY interrupt in IMR register
348 */
349
350 /* Check if transmit FIFO is empty.
351 * If not we'll wait for TX_READY interrupt. */
352 if (!(readl(MSM_BOOT_UART_DM_SR(id)) & MSM_BOOT_UART_DM_SR_TXEMT)) {
353 while (!
354 (readl(MSM_BOOT_UART_DM_ISR(id)) &
355 MSM_BOOT_UART_DM_TX_READY)) {
356 udelay(1);
357 /* Kick watchdog? */
358 }
359 }
360
361 /* We are here. FIFO is ready to be written. */
362 /* Write number of characters to be written */
363 writel(num_of_chars, MSM_BOOT_UART_DM_NO_CHARS_FOR_TX(id));
364
365 /* Clear TX_READY interrupt */
366 writel(MSM_BOOT_UART_DM_GCMD_RES_TX_RDY_INT, MSM_BOOT_UART_DM_CR(id));
367
368 /* We use four-character word FIFO. So we need to divide data into
369 * four characters and write in UART_DM_TF register */
370 tx_word_count = (num_of_chars % 4) ? ((num_of_chars / 4) + 1) :
371 (num_of_chars / 4);
372 tx_char_left = num_of_chars;
373
374 for (i = 0; i < (int)tx_word_count; i++) {
375 tx_char = (tx_char_left < 4) ? tx_char_left : 4;
376 PACK_CHARS_INTO_WORDS(tx_data, tx_char, tx_word);
377
378 /* Wait till TX FIFO has space */
379 while (!
380 (readl(MSM_BOOT_UART_DM_SR(id)) &
381 MSM_BOOT_UART_DM_SR_TXRDY)) {
382 udelay(1);
383 }
384
385 /* TX FIFO has space. Write the chars */
386 writel(tx_word, MSM_BOOT_UART_DM_TF(id, 0));
387 tx_char_left = num_of_chars - (i + 1) * 4;
388 tx_data = tx_data + 4;
389 }
390
391 return MSM_BOOT_UART_DM_E_SUCCESS;
392}
393
394/* Defining functions that's exposed to outside world and in coformance to
395 * existing uart implemention. These functions are being called to initialize
396 * UART and print debug messages in bootloader.
397 */
398void uart_init(uint8_t gsbi_id)
399{
400 static uint8_t port = 0;
401 char *data = "Android Bootloader - UART_DM Initialized!!!\n";
402
403 msm_boot_uart_dm_init(gsbi_id);
404 msm_boot_uart_dm_write(gsbi_id, data, 44);
405
406 ASSERT(port < ARRAY_SIZE(gsbi_lookup));
407 gsbi_lookup[port++] = gsbi_id;
408
409 /* Set UART init flag */
410 uart_init_flag = 1;
411}
412
413/* UART_DM uses four character word FIFO where as UART core
414 * uses a character FIFO. so it's really inefficient to try
415 * to write single character. But that's how dprintf has been
416 * implemented.
417 */
418int uart_putc(int port, char c)
419{
420 uint8_t gsbi_id = gsbi_lookup[port];
421
422 /* Don't do anything if UART is not initialized */
423 if (!uart_init_flag)
424 return;
425
426 msm_boot_uart_dm_write(gsbi_id, &c, 1);
427
428 return 0;
429}
430
431/* UART_DM uses four character word FIFO whereas uart_getc
432 * is supposed to read only one character. So we need to
433 * read a word and keep track of each character in the word.
434 */
435int uart_getc(int port, bool wait)
436{
437 int byte;
438 static unsigned int word = 0;
439 uint8_t gsbi_id = gsbi_lookup[port];
440
441 /* Don't do anything if UART is not initialized */
442 if (!uart_init_flag)
443 return;
444
445 if (!word) {
446 /* Read from FIFO only if it's a first read or all the four
447 * characters out of a word have been read */
448 if (msm_boot_uart_dm_read(gsbi_id, &word, wait) !=
449 MSM_BOOT_UART_DM_E_SUCCESS) {
450 return -1;
451 }
452
453 }
454
455 byte = (int)word & 0xff;
456 word = word >> 8;
457
458 return byte;
459}