162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * AVR power-management chip interface for the Buffalo Linkstation / 362306a36Sopenharmony_ci * Kurobox Platform. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: 2006 (c) G. Liakhovetski 662306a36Sopenharmony_ci * g.liakhovetski@gmx.de 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 962306a36Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of 1062306a36Sopenharmony_ci * any kind, whether express or implied. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include <linux/workqueue.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/serial_reg.h> 1662306a36Sopenharmony_ci#include <linux/serial_8250.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <asm/io.h> 2062306a36Sopenharmony_ci#include <asm/termbits.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "mpc10x.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic void __iomem *avr_addr; 2562306a36Sopenharmony_cistatic unsigned long avr_clock; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct work_struct wd_work; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void wd_stop(struct work_struct *unused) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci const char string[] = "AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK"; 3262306a36Sopenharmony_ci int i = 0, rescue = 8; 3362306a36Sopenharmony_ci int len = strlen(string); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci while (rescue--) { 3662306a36Sopenharmony_ci int j; 3762306a36Sopenharmony_ci char lsr = in_8(avr_addr + UART_LSR); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) { 4062306a36Sopenharmony_ci for (j = 0; j < 16 && i < len; j++, i++) 4162306a36Sopenharmony_ci out_8(avr_addr + UART_TX, string[i]); 4262306a36Sopenharmony_ci if (i == len) { 4362306a36Sopenharmony_ci /* Read "OK" back: 4ms for the last "KKKK" 4462306a36Sopenharmony_ci plus a couple bytes back */ 4562306a36Sopenharmony_ci msleep(7); 4662306a36Sopenharmony_ci printk("linkstation: disarming the AVR watchdog: "); 4762306a36Sopenharmony_ci while (in_8(avr_addr + UART_LSR) & UART_LSR_DR) 4862306a36Sopenharmony_ci printk("%c", in_8(avr_addr + UART_RX)); 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci msleep(17); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci printk("\n"); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_civoid avr_uart_configure(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci unsigned char cval = UART_LCR_WLEN8; 6262306a36Sopenharmony_ci unsigned int quot = AVR_QUOT(avr_clock); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!avr_addr || !avr_clock) 6562306a36Sopenharmony_ci return; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci out_8(avr_addr + UART_LCR, cval); /* initialise UART */ 6862306a36Sopenharmony_ci out_8(avr_addr + UART_MCR, 0); 6962306a36Sopenharmony_ci out_8(avr_addr + UART_IER, 0); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci cval |= UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci out_8(avr_addr + UART_LCR, cval); /* Set character format */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci out_8(avr_addr + UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ 7662306a36Sopenharmony_ci out_8(avr_addr + UART_DLL, quot & 0xff); /* LS of divisor */ 7762306a36Sopenharmony_ci out_8(avr_addr + UART_DLM, quot >> 8); /* MS of divisor */ 7862306a36Sopenharmony_ci out_8(avr_addr + UART_LCR, cval); /* reset DLAB */ 7962306a36Sopenharmony_ci out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */ 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_civoid avr_uart_send(const char c) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci if (!avr_addr || !avr_clock) 8562306a36Sopenharmony_ci return; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci out_8(avr_addr + UART_TX, c); 8862306a36Sopenharmony_ci out_8(avr_addr + UART_TX, c); 8962306a36Sopenharmony_ci out_8(avr_addr + UART_TX, c); 9062306a36Sopenharmony_ci out_8(avr_addr + UART_TX, c); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void __init ls_uart_init(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci local_irq_disable(); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#ifndef CONFIG_SERIAL_8250 9862306a36Sopenharmony_ci out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO); /* enable FIFO */ 9962306a36Sopenharmony_ci out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO | 10062306a36Sopenharmony_ci UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); /* clear FIFOs */ 10162306a36Sopenharmony_ci out_8(avr_addr + UART_FCR, 0); 10262306a36Sopenharmony_ci out_8(avr_addr + UART_IER, 0); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Clear up interrupts */ 10562306a36Sopenharmony_ci (void) in_8(avr_addr + UART_LSR); 10662306a36Sopenharmony_ci (void) in_8(avr_addr + UART_RX); 10762306a36Sopenharmony_ci (void) in_8(avr_addr + UART_IIR); 10862306a36Sopenharmony_ci (void) in_8(avr_addr + UART_MSR); 10962306a36Sopenharmony_ci#endif 11062306a36Sopenharmony_ci avr_uart_configure(); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci local_irq_enable(); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int __init ls_uarts_init(void) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct device_node *avr; 11862306a36Sopenharmony_ci struct resource res; 11962306a36Sopenharmony_ci int len, ret; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci avr = of_find_node_by_path("/soc10x/serial@80004500"); 12262306a36Sopenharmony_ci if (!avr) 12362306a36Sopenharmony_ci return -EINVAL; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci avr_clock = *(u32*)of_get_property(avr, "clock-frequency", &len); 12662306a36Sopenharmony_ci if (!avr_clock) 12762306a36Sopenharmony_ci return -EINVAL; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = of_address_to_resource(avr, 0, &res); 13062306a36Sopenharmony_ci if (ret) 13162306a36Sopenharmony_ci return ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci of_node_put(avr); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci avr_addr = ioremap(res.start, 32); 13662306a36Sopenharmony_ci if (!avr_addr) 13762306a36Sopenharmony_ci return -EFAULT; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ls_uart_init(); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci INIT_WORK(&wd_work, wd_stop); 14262306a36Sopenharmony_ci schedule_work(&wd_work); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cimachine_late_initcall(linkstation, ls_uarts_init); 148