Merge branch 'drm-patches' of master.kernel.org:/pub/scm/linux/kernel/git/airlied...
[linux-2.6] / arch / powerpc / platforms / embedded6xx / ls_uart.c
1 #include <linux/workqueue.h>
2 #include <linux/string.h>
3 #include <linux/delay.h>
4 #include <linux/serial_reg.h>
5 #include <linux/serial_8250.h>
6 #include <asm/io.h>
7 #include <asm/mpc10x.h>
8 #include <asm/ppc_sys.h>
9 #include <asm/prom.h>
10 #include <asm/termbits.h>
11
12 static void __iomem *avr_addr;
13 static unsigned long avr_clock;
14
15 static struct work_struct wd_work;
16
17 static void wd_stop(struct work_struct *unused)
18 {
19         const char string[] = "AAAAFFFFJJJJ>>>>VVVV>>>>ZZZZVVVVKKKK";
20         int i = 0, rescue = 8;
21         int len = strlen(string);
22
23         while (rescue--) {
24                 int j;
25                 char lsr = in_8(avr_addr + UART_LSR);
26
27                 if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) {
28                         for (j = 0; j < 16 && i < len; j++, i++)
29                                 out_8(avr_addr + UART_TX, string[i]);
30                         if (i == len) {
31                                 /* Read "OK" back: 4ms for the last "KKKK"
32                                    plus a couple bytes back */
33                                 msleep(7);
34                                 printk("linkstation: disarming the AVR watchdog: ");
35                                 while (in_8(avr_addr + UART_LSR) & UART_LSR_DR)
36                                         printk("%c", in_8(avr_addr + UART_RX));
37                                 break;
38                         }
39                 }
40                 msleep(17);
41         }
42         printk("\n");
43 }
44
45 #define AVR_QUOT(clock) ((clock) + 8 * 9600) / (16 * 9600)
46
47 void avr_uart_configure(void)
48 {
49         unsigned char cval = UART_LCR_WLEN8;
50         unsigned int quot = AVR_QUOT(avr_clock);
51
52         if (!avr_addr || !avr_clock)
53                 return;
54
55         out_8(avr_addr + UART_LCR, cval);                       /* initialise UART */
56         out_8(avr_addr + UART_MCR, 0);
57         out_8(avr_addr + UART_IER, 0);
58
59         cval |= UART_LCR_STOP | UART_LCR_PARITY | UART_LCR_EPAR;
60
61         out_8(avr_addr + UART_LCR, cval);                       /* Set character format */
62
63         out_8(avr_addr + UART_LCR, cval | UART_LCR_DLAB);       /* set DLAB */
64         out_8(avr_addr + UART_DLL, quot & 0xff);                /* LS of divisor */
65         out_8(avr_addr + UART_DLM, quot >> 8);                  /* MS of divisor */
66         out_8(avr_addr + UART_LCR, cval);                       /* reset DLAB */
67         out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO);       /* enable FIFO */
68 }
69
70 void avr_uart_send(const char c)
71 {
72         if (!avr_addr || !avr_clock)
73                 return;
74
75         out_8(avr_addr + UART_TX, c);
76         out_8(avr_addr + UART_TX, c);
77         out_8(avr_addr + UART_TX, c);
78         out_8(avr_addr + UART_TX, c);
79 }
80
81 static void __init ls_uart_init(void)
82 {
83         local_irq_disable();
84
85 #ifndef CONFIG_SERIAL_8250
86         out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO);       /* enable FIFO */
87         out_8(avr_addr + UART_FCR, UART_FCR_ENABLE_FIFO |
88               UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);       /* clear FIFOs */
89         out_8(avr_addr + UART_FCR, 0);
90         out_8(avr_addr + UART_IER, 0);
91
92         /* Clear up interrupts */
93         (void) in_8(avr_addr + UART_LSR);
94         (void) in_8(avr_addr + UART_RX);
95         (void) in_8(avr_addr + UART_IIR);
96         (void) in_8(avr_addr + UART_MSR);
97 #endif
98         avr_uart_configure();
99
100         local_irq_enable();
101 }
102
103 static int __init ls_uarts_init(void)
104 {
105         struct device_node *avr;
106         phys_addr_t phys_addr;
107         int len;
108
109         avr = of_find_node_by_path("/soc10x/serial@80004500");
110         if (!avr)
111                 return -EINVAL;
112
113         avr_clock = *(u32*)of_get_property(avr, "clock-frequency", &len);
114         phys_addr = ((u32*)of_get_property(avr, "reg", &len))[0];
115
116         if (!avr_clock || !phys_addr)
117                 return -EINVAL;
118
119         avr_addr = ioremap(phys_addr, 32);
120         if (!avr_addr)
121                 return -EFAULT;
122
123         ls_uart_init();
124
125         INIT_WORK(&wd_work, wd_stop);
126         schedule_work(&wd_work);
127
128         return 0;
129 }
130
131 late_initcall(ls_uarts_init);