This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / arch / powerpc / platforms / embedded6xx / ls_uart.c
diff --git a/arch/powerpc/platforms/embedded6xx/ls_uart.c b/arch/powerpc/platforms/embedded6xx/ls_uart.c
new file mode 100644 (file)
index 0000000..0e83776
--- /dev/null
@@ -0,0 +1,131 @@
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_8250.h>
+#include <asm/io.h>
+#include <asm/mpc10x.h>
+#include <asm/ppc_sys.h>
+#include <asm/prom.h>
+#include <asm/termbits.h>
+
+static void __iomem *avr_addr;
+static unsigned long avr_clock;
+
+static struct work_struct wd_work;
+
+static void wd_stop(struct work_struct *unused)
+{
+       const char string[] = "AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK";
+       int i = 0, rescue = 8;
+       int len = strlen(string);
+
+       while (rescue--) {
+               int j;
+               char lsr = in_8(avr_addr + UART_LSR);
+
+               if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) {
+                       for (j = 0; j < 16 && i < len; j++, i++)
+                               out_8(avr_addr + UART_TX, string[i]);
+                       if (i == len) {
+                               /* Read "OK" back: 4ms for the last "KKKK"
+                                  plus a couple bytes back */
+                               msleep(7);
+                               printk("linkstation: disarming the AVR watchdog: ");
+                               while (in_8(avr_addr + UART_LSR) & UART_LSR_DR)
+                                       printk("%c", in_8(avr_addr + UART_RX));
+                               break;
+                       }
+               }
+               msleep(17);
+       }
+       printk("\n");
+}
+
+#define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600)
+
+void avr_uart_configure(void)
+{
+       unsigned char cval = UART_LCR_WLEN8;
+       unsigned int quot = AVR_QUOT(avr_clock);
+
+       if (!avr_addr || !avr_clock)
+               return;
+
+       out_8(avr_addr + UART_LCR, cval);                       /* initialise UART */
+       out_8(avr_addr + UART_MCR, 0);
+       out_8(avr_addr + UART_IER, 0);
+
+       cval |= UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR;
+
+       out_8(avr_addr + UART_LCR, cval);                       /* Set character format */
+
+       out_8(avr_addr + UART_LCR, cval | UART_LCR_DLAB);       /* set DLAB */
+       out_8(avr_addr + UART_DLL, quot & 0xff);                /* LS of divisor */
+       out_8(avr_addr + UART_DLM, quot >> 8);                  /* MS of divisor */
+       out_8(avr_addr + UART_LCR, cval);                       /* reset DLAB */
+       out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO);       /* enable FIFO */
+}
+
+void avr_uart_send(const char c)
+{
+       if (!avr_addr || !avr_clock)
+               return;
+
+       out_8(avr_addr + UART_TX, c);
+       out_8(avr_addr + UART_TX, c);
+       out_8(avr_addr + UART_TX, c);
+       out_8(avr_addr + UART_TX, c);
+}
+
+static void __init ls_uart_init(void)
+{
+       local_irq_disable();
+
+#ifndef CONFIG_SERIAL_8250
+       out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO);       /* enable FIFO */
+       out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO |
+             UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);       /* clear FIFOs */
+       out_8(avr_addr + UART_FCR, 0);
+       out_8(avr_addr + UART_IER, 0);
+
+       /* Clear up interrupts */
+       (void) in_8(avr_addr + UART_LSR);
+       (void) in_8(avr_addr + UART_RX);
+       (void) in_8(avr_addr + UART_IIR);
+       (void) in_8(avr_addr + UART_MSR);
+#endif
+       avr_uart_configure();
+
+       local_irq_enable();
+}
+
+static int __init ls_uarts_init(void)
+{
+       struct device_node *avr;
+       phys_addr_t phys_addr;
+       int len;
+
+       avr = of_find_node_by_path("/soc10x/serial@80004500");
+       if (!avr)
+               return -EINVAL;
+
+       avr_clock = *(u32*)get_property(avr, "clock-frequency", &len);
+       phys_addr = ((u32*)get_property(avr, "reg", &len))[0];
+
+       if (!avr_clock || !phys_addr)
+               return -EINVAL;
+
+       avr_addr = ioremap(phys_addr, 32);
+       if (!avr_addr)
+               return -EFAULT;
+
+       ls_uart_init();
+
+       INIT_WORK(&wd_work, wd_stop);
+       schedule_work(&wd_work);
+
+       return 0;
+}
+
+late_initcall(ls_uarts_init);