17f2862c3SGuennadi Liakhovetski /* 27f2862c3SGuennadi Liakhovetski * AVR power-management chip interface for the Buffalo Linkstation / 37f2862c3SGuennadi Liakhovetski * Kurobox Platform. 47f2862c3SGuennadi Liakhovetski * 57f2862c3SGuennadi Liakhovetski * Author: 2006 (c) G. Liakhovetski 67f2862c3SGuennadi Liakhovetski * g.liakhovetski@gmx.de 77f2862c3SGuennadi Liakhovetski * 87f2862c3SGuennadi Liakhovetski * This file is licensed under the terms of the GNU General Public License 97f2862c3SGuennadi Liakhovetski * version 2. This program is licensed "as is" without any warranty of 107f2862c3SGuennadi Liakhovetski * any kind, whether express or implied. 117f2862c3SGuennadi Liakhovetski */ 1204d76b93SGuennadi Liakhovetski #include <linux/workqueue.h> 1304d76b93SGuennadi Liakhovetski #include <linux/string.h> 1404d76b93SGuennadi Liakhovetski #include <linux/delay.h> 1504d76b93SGuennadi Liakhovetski #include <linux/serial_reg.h> 1604d76b93SGuennadi Liakhovetski #include <linux/serial_8250.h> 1704d76b93SGuennadi Liakhovetski #include <asm/io.h> 1804d76b93SGuennadi Liakhovetski #include <asm/prom.h> 1904d76b93SGuennadi Liakhovetski #include <asm/termbits.h> 2004d76b93SGuennadi Liakhovetski 2133d71d26SKumar Gala #include "mpc10x.h" 2233d71d26SKumar Gala 2304d76b93SGuennadi Liakhovetski static void __iomem *avr_addr; 2404d76b93SGuennadi Liakhovetski static unsigned long avr_clock; 2504d76b93SGuennadi Liakhovetski 2604d76b93SGuennadi Liakhovetski static struct work_struct wd_work; 2704d76b93SGuennadi Liakhovetski 286d5aefb8SDavid Howells static void wd_stop(struct work_struct *unused) 2904d76b93SGuennadi Liakhovetski { 3004d76b93SGuennadi Liakhovetski const char string[] = "AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK"; 3104d76b93SGuennadi Liakhovetski int i = 0, rescue = 8; 3204d76b93SGuennadi Liakhovetski int len = strlen(string); 3304d76b93SGuennadi Liakhovetski 3404d76b93SGuennadi Liakhovetski while (rescue--) { 3504d76b93SGuennadi Liakhovetski int j; 3604d76b93SGuennadi Liakhovetski char lsr = in_8(avr_addr + UART_LSR); 3704d76b93SGuennadi Liakhovetski 3804d76b93SGuennadi Liakhovetski if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) { 3904d76b93SGuennadi Liakhovetski for (j = 0; j < 16 && i < len; j++, i++) 4004d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, string[i]); 4104d76b93SGuennadi Liakhovetski if (i == len) { 4204d76b93SGuennadi Liakhovetski /* Read "OK" back: 4ms for the last "KKKK" 4304d76b93SGuennadi Liakhovetski plus a couple bytes back */ 4404d76b93SGuennadi Liakhovetski msleep(7); 4504d76b93SGuennadi Liakhovetski printk("linkstation: disarming the AVR watchdog: "); 4604d76b93SGuennadi Liakhovetski while (in_8(avr_addr + UART_LSR) & UART_LSR_DR) 4704d76b93SGuennadi Liakhovetski printk("%c", in_8(avr_addr + UART_RX)); 4804d76b93SGuennadi Liakhovetski break; 4904d76b93SGuennadi Liakhovetski } 5004d76b93SGuennadi Liakhovetski } 5104d76b93SGuennadi Liakhovetski msleep(17); 5204d76b93SGuennadi Liakhovetski } 5304d76b93SGuennadi Liakhovetski printk("\n"); 5404d76b93SGuennadi Liakhovetski } 5504d76b93SGuennadi Liakhovetski 5604d76b93SGuennadi Liakhovetski #define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600) 5704d76b93SGuennadi Liakhovetski 5804d76b93SGuennadi Liakhovetski void avr_uart_configure(void) 5904d76b93SGuennadi Liakhovetski { 6004d76b93SGuennadi Liakhovetski unsigned char cval = UART_LCR_WLEN8; 6104d76b93SGuennadi Liakhovetski unsigned int quot = AVR_QUOT(avr_clock); 6204d76b93SGuennadi Liakhovetski 6304d76b93SGuennadi Liakhovetski if (!avr_addr || !avr_clock) 6404d76b93SGuennadi Liakhovetski return; 6504d76b93SGuennadi Liakhovetski 6604d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_LCR, cval); /* initialise UART */ 6704d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_MCR, 0); 6804d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_IER, 0); 6904d76b93SGuennadi Liakhovetski 7004d76b93SGuennadi Liakhovetski cval |= UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR; 7104d76b93SGuennadi Liakhovetski 7204d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_LCR, cval); /* Set character format */ 7304d76b93SGuennadi Liakhovetski 7404d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ 7504d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_DLL, quot & 0xff); /* LS of divisor */ 7604d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_DLM, quot >> 8); /* MS of divisor */ 7704d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_LCR, cval); /* reset DLAB */ 7804d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */ 7904d76b93SGuennadi Liakhovetski } 8004d76b93SGuennadi Liakhovetski 8104d76b93SGuennadi Liakhovetski void avr_uart_send(const char c) 8204d76b93SGuennadi Liakhovetski { 8304d76b93SGuennadi Liakhovetski if (!avr_addr || !avr_clock) 8404d76b93SGuennadi Liakhovetski return; 8504d76b93SGuennadi Liakhovetski 8604d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, c); 8704d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, c); 8804d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, c); 8904d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_TX, c); 9004d76b93SGuennadi Liakhovetski } 9104d76b93SGuennadi Liakhovetski 9204d76b93SGuennadi Liakhovetski static void __init ls_uart_init(void) 9304d76b93SGuennadi Liakhovetski { 9404d76b93SGuennadi Liakhovetski local_irq_disable(); 9504d76b93SGuennadi Liakhovetski 9604d76b93SGuennadi Liakhovetski #ifndef CONFIG_SERIAL_8250 9704d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */ 9804d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO | 9904d76b93SGuennadi Liakhovetski UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); /* clear FIFOs */ 10004d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_FCR, 0); 10104d76b93SGuennadi Liakhovetski out_8(avr_addr + UART_IER, 0); 10204d76b93SGuennadi Liakhovetski 10304d76b93SGuennadi Liakhovetski /* Clear up interrupts */ 10404d76b93SGuennadi Liakhovetski (void) in_8(avr_addr + UART_LSR); 10504d76b93SGuennadi Liakhovetski (void) in_8(avr_addr + UART_RX); 10604d76b93SGuennadi Liakhovetski (void) in_8(avr_addr + UART_IIR); 10704d76b93SGuennadi Liakhovetski (void) in_8(avr_addr + UART_MSR); 10804d76b93SGuennadi Liakhovetski #endif 10904d76b93SGuennadi Liakhovetski avr_uart_configure(); 11004d76b93SGuennadi Liakhovetski 11104d76b93SGuennadi Liakhovetski local_irq_enable(); 11204d76b93SGuennadi Liakhovetski } 11304d76b93SGuennadi Liakhovetski 11404d76b93SGuennadi Liakhovetski static int __init ls_uarts_init(void) 11504d76b93SGuennadi Liakhovetski { 11604d76b93SGuennadi Liakhovetski struct device_node *avr; 11704d76b93SGuennadi Liakhovetski phys_addr_t phys_addr; 11804d76b93SGuennadi Liakhovetski int len; 11904d76b93SGuennadi Liakhovetski 12004d76b93SGuennadi Liakhovetski avr = of_find_node_by_path("/soc10x/serial@80004500"); 12104d76b93SGuennadi Liakhovetski if (!avr) 12204d76b93SGuennadi Liakhovetski return -EINVAL; 12304d76b93SGuennadi Liakhovetski 124e2eb6392SStephen Rothwell avr_clock = *(u32*)of_get_property(avr, "clock-frequency", &len); 125e2eb6392SStephen Rothwell phys_addr = ((u32*)of_get_property(avr, "reg", &len))[0]; 12604d76b93SGuennadi Liakhovetski 12704d76b93SGuennadi Liakhovetski if (!avr_clock || !phys_addr) 12804d76b93SGuennadi Liakhovetski return -EINVAL; 12904d76b93SGuennadi Liakhovetski 13004d76b93SGuennadi Liakhovetski avr_addr = ioremap(phys_addr, 32); 13104d76b93SGuennadi Liakhovetski if (!avr_addr) 13204d76b93SGuennadi Liakhovetski return -EFAULT; 13304d76b93SGuennadi Liakhovetski 13404d76b93SGuennadi Liakhovetski ls_uart_init(); 13504d76b93SGuennadi Liakhovetski 1366d5aefb8SDavid Howells INIT_WORK(&wd_work, wd_stop); 13704d76b93SGuennadi Liakhovetski schedule_work(&wd_work); 13804d76b93SGuennadi Liakhovetski 13904d76b93SGuennadi Liakhovetski return 0; 14004d76b93SGuennadi Liakhovetski } 14104d76b93SGuennadi Liakhovetski 142ca956f0eSGrant Likely machine_late_initcall(linkstation, ls_uarts_init); 143