Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * AVR power-management chip interface for the Buffalo Linkstation /
0003  * Kurobox Platform.
0004  *
0005  * Author: 2006 (c) G. Liakhovetski
0006  *   g.liakhovetski@gmx.de
0007  *
0008  * This file is licensed under the terms of the GNU General Public License
0009  * version 2.  This program is licensed "as is" without any warranty of
0010  * any kind, whether express or implied.
0011  */
0012 #include <linux/workqueue.h>
0013 #include <linux/string.h>
0014 #include <linux/delay.h>
0015 #include <linux/serial_reg.h>
0016 #include <linux/serial_8250.h>
0017 #include <linux/of.h>
0018 #include <asm/io.h>
0019 #include <asm/termbits.h>
0020 
0021 #include "mpc10x.h"
0022 
0023 static void __iomem *avr_addr;
0024 static unsigned long avr_clock;
0025 
0026 static struct work_struct wd_work;
0027 
0028 static void wd_stop(struct work_struct *unused)
0029 {
0030     const char string[] = "AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK";
0031     int i = 0, rescue = 8;
0032     int len = strlen(string);
0033 
0034     while (rescue--) {
0035         int j;
0036         char lsr = in_8(avr_addr + UART_LSR);
0037 
0038         if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) {
0039             for (j = 0; j < 16 && i < len; j++, i++)
0040                 out_8(avr_addr + UART_TX, string[i]);
0041             if (i == len) {
0042                 /* Read "OK" back: 4ms for the last "KKKK"
0043                    plus a couple bytes back */
0044                 msleep(7);
0045                 printk("linkstation: disarming the AVR watchdog: ");
0046                 while (in_8(avr_addr + UART_LSR) & UART_LSR_DR)
0047                     printk("%c", in_8(avr_addr + UART_RX));
0048                 break;
0049             }
0050         }
0051         msleep(17);
0052     }
0053     printk("\n");
0054 }
0055 
0056 #define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600)
0057 
0058 void avr_uart_configure(void)
0059 {
0060     unsigned char cval = UART_LCR_WLEN8;
0061     unsigned int quot = AVR_QUOT(avr_clock);
0062 
0063     if (!avr_addr || !avr_clock)
0064         return;
0065 
0066     out_8(avr_addr + UART_LCR, cval);           /* initialise UART */
0067     out_8(avr_addr + UART_MCR, 0);
0068     out_8(avr_addr + UART_IER, 0);
0069 
0070     cval |= UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR;
0071 
0072     out_8(avr_addr + UART_LCR, cval);           /* Set character format */
0073 
0074     out_8(avr_addr + UART_LCR, cval | UART_LCR_DLAB);   /* set DLAB */
0075     out_8(avr_addr + UART_DLL, quot & 0xff);        /* LS of divisor */
0076     out_8(avr_addr + UART_DLM, quot >> 8);          /* MS of divisor */
0077     out_8(avr_addr + UART_LCR, cval);           /* reset DLAB */
0078     out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO);   /* enable FIFO */
0079 }
0080 
0081 void avr_uart_send(const char c)
0082 {
0083     if (!avr_addr || !avr_clock)
0084         return;
0085 
0086     out_8(avr_addr + UART_TX, c);
0087     out_8(avr_addr + UART_TX, c);
0088     out_8(avr_addr + UART_TX, c);
0089     out_8(avr_addr + UART_TX, c);
0090 }
0091 
0092 static void __init ls_uart_init(void)
0093 {
0094     local_irq_disable();
0095 
0096 #ifndef CONFIG_SERIAL_8250
0097     out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO);   /* enable FIFO */
0098     out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO |
0099           UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);   /* clear FIFOs */
0100     out_8(avr_addr + UART_FCR, 0);
0101     out_8(avr_addr + UART_IER, 0);
0102 
0103     /* Clear up interrupts */
0104     (void) in_8(avr_addr + UART_LSR);
0105     (void) in_8(avr_addr + UART_RX);
0106     (void) in_8(avr_addr + UART_IIR);
0107     (void) in_8(avr_addr + UART_MSR);
0108 #endif
0109     avr_uart_configure();
0110 
0111     local_irq_enable();
0112 }
0113 
0114 static int __init ls_uarts_init(void)
0115 {
0116     struct device_node *avr;
0117     phys_addr_t phys_addr;
0118     int len;
0119 
0120     avr = of_find_node_by_path("/soc10x/serial@80004500");
0121     if (!avr)
0122         return -EINVAL;
0123 
0124     avr_clock = *(u32*)of_get_property(avr, "clock-frequency", &len);
0125     phys_addr = ((u32*)of_get_property(avr, "reg", &len))[0];
0126 
0127     if (!avr_clock || !phys_addr)
0128         return -EINVAL;
0129 
0130     avr_addr = ioremap(phys_addr, 32);
0131     if (!avr_addr)
0132         return -EFAULT;
0133 
0134     ls_uart_init();
0135 
0136     INIT_WORK(&wd_work, wd_stop);
0137     schedule_work(&wd_work);
0138 
0139     return 0;
0140 }
0141 
0142 machine_late_initcall(linkstation, ls_uarts_init);