00001
00045 #include <assert.h>
00046 #include <interrupt.h>
00047 #include <status-codes.h>
00048 #include <util.h>
00049
00050 #include <chip/clk.h>
00051 #include <console/core.h>
00052 #include <console/driver.h>
00053 #include <console/serial.h>
00054 #include <hardware/usart.h>
00055
00056 struct usart_port {
00057 void *regs;
00058 unsigned long pclk_hz;
00059 struct console_driver drv;
00060 };
00061
00062 static inline struct usart_port *usart_port_of(struct console_driver *drv)
00063 {
00064 return container_of(drv, struct usart_port, drv);
00065 }
00066
00067 static void usart_tx_commit(struct console_driver *drv)
00068 {
00069 struct usart_port *usart = usart_port_of(drv);
00070 void *regs = usart->regs;
00071 unsigned long nchars;
00072 unsigned long iflags;
00073 char c;
00074
00075 iflags = cpu_irq_save();
00076 while (true) {
00077 nchars = console_buf_used(&drv->tx_buf);
00078 assert(nchars <= CONSOLE_BUF_SIZE);
00079 if (nchars == 0)
00080 break;
00081
00082 c = console_buf_extract_char(&drv->tx_buf);
00083 cpu_irq_restore(iflags);
00084
00085 while (!(usart_read(regs, CSR) & USART_BIT(TXRDY)))
00086 barrier();
00087
00088 usart_write(regs, THR, c);
00089 iflags = cpu_irq_save();
00090 }
00091 cpu_irq_restore(iflags);
00092 }
00093
00094 static void usart_tx_make_room(struct console_driver *drv, size_t goal)
00095 {
00096
00097 usart_tx_commit(drv);
00098 }
00099
00100
00101 static struct usart_port the_usart_port = {
00102 .drv.tx_commit = usart_tx_commit,
00103 .drv.tx_make_room = usart_tx_make_room,
00104 };
00105
00117 static uint32_t usart_calc_brgr(unsigned long base_hz, unsigned long baud)
00118 {
00119 unsigned long div;
00120 unsigned long cd;
00121 unsigned long fp;
00122
00123 div = (base_hz + baud / 2) / baud;
00124 cd = div / 8;
00125 fp = div % 8;
00126
00127 if (cd < 2 || (cd & ((1 << USART_CD_SIZE) - 1)) != cd)
00128 return 0;
00129
00130 return USART_BF(CD, cd) | USART_BF(FP, fp);
00131 }
00132
00133 int __serial_console_init(struct console *con, unsigned int port, void *regs,
00134 unsigned long baud_rate, unsigned long flags)
00135 {
00136 struct usart_port *usart = &the_usart_port;
00137 unsigned long pclk_hz;
00138 uint32_t cr_flags = 0;
00139
00140 if (flags & SERIAL_ENABLE_TX)
00141 cr_flags |= USART_BIT(TXEN);
00142
00143
00144 if (flags & SERIAL_ENABLE_RX)
00145 cr_flags |= USART_BIT(RXEN);
00146
00147 pclk_hz = get_usart_pclk_rate(port);
00148 usart->pclk_hz = pclk_hz;
00149 usart->regs = regs;
00150
00151
00152 if (!usart_read(regs, BRGR)) {
00153 uint32_t brgr;
00154 uint32_t mr;
00155
00156 mr = USART_BF(MODE, USART_MODE_NORMAL)
00157 | USART_BF(USCLKS, USART_USCLKS_MCK)
00158 | USART_BF(CHRL, USART_CHRL_8)
00159 | USART_BF(PAR, USART_PAR_NONE)
00160 | USART_BF(NBSTOP, USART_NBSTOP_1);
00161
00162 brgr = usart_calc_brgr((pclk_hz + 1) / 2, baud_rate);
00163 if (!brgr) {
00164 brgr = usart_calc_brgr(pclk_hz, baud_rate);
00165 if (!brgr)
00166 return -STATUS_INVALID_PARAM;
00167 mr |= USART_BIT(OVER);
00168 }
00169
00170 usart_write(regs, CR, USART_BIT(RSTRX) | USART_BIT(RSTTX));
00171 usart_write(regs, BRGR, brgr);
00172 usart_write(regs, MR, mr);
00173 }
00174
00175 usart_write(regs, CR, cr_flags);
00176
00177 con->drv = &usart->drv;
00178
00179 return 0;
00180 }