104d76b93SGuennadi Liakhovetski #include <linux/workqueue.h> 204d76b93SGuennadi Liakhovetski #include <linux/string.h> 304d76b93SGuennadi Liakhovetski #include <linux/delay.h> 404d76b93SGuennadi Liakhovetski #include <linux/serial_reg.h> 504d76b93SGuennadi Liakhovetski #include <linux/serial_8250.h> 604d76b93SGuennadi Liakhovetski #include <asm/io.h> 704d76b93SGuennadi Liakhovetski #include <asm/mpc10x.h> 804d76b93SGuennadi Liakhovetski #include <asm/ppc_sys.h> 904d76b93SGuennadi Liakhovetski #include <asm/prom.h> 1004d76b93SGuennadi Liakhovetski #include <asm/termbits.h> 1104d76b93SGuennadi Liakhovetski 1204d76b93SGuennadi Liakhovetski static void __iomem *avr_addr; 1304d76b93SGuennadi Liakhovetski static unsigned long avr_clock; 1404d76b93SGuennadi Liakhovetski 1504d76b93SGuennadi Liakhovetski static struct work_struct wd_work; 1604d76b93SGuennadi Liakhovetski 176d5aefb8SDavid Howells static void wd_stop(struct work_struct *unused) 1804d76b93SGuennadi Liakhovetski { 1904d76b93SGuennadi Liakhovetski const char string[] = "AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK"; 2004d76b93SGuennadi Liakhovetski int i = 0, rescue = 8; 2104d76b93SGuennadi Liakhovetski int len = strlen(string); 2204d76b93SGuennadi Liakhovetski 2304d76b93SGuennadi Liakhovetski while (rescue--) { 2404d76b93SGuennadi Liakhovetski int j; 2504d76b93SGuennadi Liakhovetski char lsr = in_8(avr_addr + UART_LSR); 2604d76b93SGuennadi Liakhovetski 2704d76b93SGuennadi Liakhovetski if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) { 2804d76b93SGuennadi Liakhovetski for (j = 0; j < 16 && i < len; j++, i++) 2904d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, string[i]); 3004d76b93SGuennadi Liakhovetski if (i == len) { 3104d76b93SGuennadi Liakhovetski /* Read "OK" back: 4ms for the last "KKKK" 3204d76b93SGuennadi Liakhovetski plus a couple bytes back */ 3304d76b93SGuennadi Liakhovetski msleep(7); 3404d76b93SGuennadi Liakhovetski printk("linkstation: disarming the AVR watchdog: "); 3504d76b93SGuennadi Liakhovetski while (in_8(avr_addr + UART_LSR) & UART_LSR_DR) 3604d76b93SGuennadi Liakhovetski printk("%c", in_8(avr_addr + UART_RX)); 3704d76b93SGuennadi Liakhovetski break; 3804d76b93SGuennadi Liakhovetski } 3904d76b93SGuennadi Liakhovetski } 4004d76b93SGuennadi Liakhovetski msleep(17); 4104d76b93SGuennadi Liakhovetski } 4204d76b93SGuennadi Liakhovetski printk("\n"); 4304d76b93SGuennadi Liakhovetski } 4404d76b93SGuennadi Liakhovetski 4504d76b93SGuennadi Liakhovetski #define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600) 4604d76b93SGuennadi Liakhovetski 4704d76b93SGuennadi Liakhovetski void avr_uart_configure(void) 4804d76b93SGuennadi Liakhovetski { 4904d76b93SGuennadi Liakhovetski unsigned char cval = UART_LCR_WLEN8; 5004d76b93SGuennadi Liakhovetski unsigned int quot = AVR_QUOT(avr_clock); 5104d76b93SGuennadi Liakhovetski 5204d76b93SGuennadi Liakhovetski if (!avr_addr || !avr_clock) 5304d76b93SGuennadi Liakhovetski return; 5404d76b93SGuennadi Liakhovetski 5504d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_LCR, cval); /* initialise UART */ 5604d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_MCR, 0); 5704d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_IER, 0); 5804d76b93SGuennadi Liakhovetski 5904d76b93SGuennadi Liakhovetski cval |= UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR; 6004d76b93SGuennadi Liakhovetski 6104d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_LCR, cval); /* Set character format */ 6204d76b93SGuennadi Liakhovetski 6304d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ 6404d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_DLL, quot & 0xff); /* LS of divisor */ 6504d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_DLM, quot >> 8); /* MS of divisor */ 6604d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_LCR, cval); /* reset DLAB */ 6704d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */ 6804d76b93SGuennadi Liakhovetski } 6904d76b93SGuennadi Liakhovetski 7004d76b93SGuennadi Liakhovetski void avr_uart_send(const char c) 7104d76b93SGuennadi Liakhovetski { 7204d76b93SGuennadi Liakhovetski if (!avr_addr || !avr_clock) 7304d76b93SGuennadi Liakhovetski return; 7404d76b93SGuennadi Liakhovetski 7504d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, c); 7604d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, c); 7704d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, c); 7804d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, c); 7904d76b93SGuennadi Liakhovetski } 8004d76b93SGuennadi Liakhovetski 8104d76b93SGuennadi Liakhovetski static void __init ls_uart_init(void) 8204d76b93SGuennadi Liakhovetski { 8304d76b93SGuennadi Liakhovetski local_irq_disable(); 8404d76b93SGuennadi Liakhovetski 8504d76b93SGuennadi Liakhovetski #ifndef CONFIG_SERIAL_8250 8604d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */ 8704d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO | 8804d76b93SGuennadi Liakhovetski UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); /* clear FIFOs */ 8904d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_FCR, 0); 9004d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_IER, 0); 9104d76b93SGuennadi Liakhovetski 9204d76b93SGuennadi Liakhovetski /* Clear up interrupts */ 9304d76b93SGuennadi Liakhovetski (void) in_8(avr_addr + UART_LSR); 9404d76b93SGuennadi Liakhovetski (void) in_8(avr_addr + UART_RX); 9504d76b93SGuennadi Liakhovetski (void) in_8(avr_addr + UART_IIR); 9604d76b93SGuennadi Liakhovetski (void) in_8(avr_addr + UART_MSR); 9704d76b93SGuennadi Liakhovetski #endif 9804d76b93SGuennadi Liakhovetski avr_uart_configure(); 9904d76b93SGuennadi Liakhovetski 10004d76b93SGuennadi Liakhovetski local_irq_enable(); 10104d76b93SGuennadi Liakhovetski } 10204d76b93SGuennadi Liakhovetski 10304d76b93SGuennadi Liakhovetski static int __init ls_uarts_init(void) 10404d76b93SGuennadi Liakhovetski { 10504d76b93SGuennadi Liakhovetski struct device_node *avr; 10604d76b93SGuennadi Liakhovetski phys_addr_t phys_addr; 10704d76b93SGuennadi Liakhovetski int len; 10804d76b93SGuennadi Liakhovetski 10904d76b93SGuennadi Liakhovetski avr = of_find_node_by_path("/soc10x/serial@80004500"); 11004d76b93SGuennadi Liakhovetski if (!avr) 11104d76b93SGuennadi Liakhovetski return -EINVAL; 11204d76b93SGuennadi Liakhovetski 113e2eb6392SStephen Rothwell avr_clock = *(u32*)of_get_property(avr, "clock-frequency", &len); 114e2eb6392SStephen Rothwell phys_addr = ((u32*)of_get_property(avr, "reg", &len))[0]; 11504d76b93SGuennadi Liakhovetski 11604d76b93SGuennadi Liakhovetski if (!avr_clock || !phys_addr) 11704d76b93SGuennadi Liakhovetski return -EINVAL; 11804d76b93SGuennadi Liakhovetski 11904d76b93SGuennadi Liakhovetski avr_addr = ioremap(phys_addr, 32); 12004d76b93SGuennadi Liakhovetski if (!avr_addr) 12104d76b93SGuennadi Liakhovetski return -EFAULT; 12204d76b93SGuennadi Liakhovetski 12304d76b93SGuennadi Liakhovetski ls_uart_init(); 12404d76b93SGuennadi Liakhovetski 1256d5aefb8SDavid Howells INIT_WORK(&wd_work, wd_stop); 12604d76b93SGuennadi Liakhovetski schedule_work(&wd_work); 12704d76b93SGuennadi Liakhovetski 12804d76b93SGuennadi Liakhovetski return 0; 12904d76b93SGuennadi Liakhovetski } 13004d76b93SGuennadi Liakhovetski 13104d76b93SGuennadi Liakhovetski late_initcall(ls_uarts_init); 132