add arm mach_types update again
[openwrt.git] / target / linux / orion / patches / 015-implement_power-off_method_for_kurobox_pro.patch
1 From: Sylver Bruneau <sylver.bruneau@googlemail.com>
2
3 This patch implements the communication with the microcontroller on the
4 Kurobox Pro and Linkstation Pro/Live boards.  This is allowing to send
5 the commands needed to power-off the board correctly.
6
7 Signed-off-by: Sylver Bruneau <sylver.bruneau@googlemail.com>
8 ---
9  arch/arm/mach-orion5x/kurobox_pro-setup.c |  147 ++++++++++++++++++++++++++++-
10  1 files changed, 143 insertions(+), 4 deletions(-)
11
12 --- a/arch/arm/mach-orion5x/kurobox_pro-setup.c
13 +++ b/arch/arm/mach-orion5x/kurobox_pro-setup.c
14 @@ -13,10 +13,12 @@
15  #include <linux/platform_device.h>
16  #include <linux/pci.h>
17  #include <linux/irq.h>
18 +#include <linux/delay.h>
19  #include <linux/mtd/physmap.h>
20  #include <linux/mtd/nand.h>
21  #include <linux/mv643xx_eth.h>
22  #include <linux/i2c.h>
23 +#include <linux/serial_reg.h>
24  #include <linux/ata_platform.h>
25  #include <asm/mach-types.h>
26  #include <asm/gpio.h>
27 @@ -177,6 +179,140 @@
28  };
29  
30  /*****************************************************************************
31 + * Kurobox Pro specific power off method via UART1-attached microcontroller
32 + ****************************************************************************/
33 +
34 +#define UART1_REG(x)   (UART1_VIRT_BASE + ((UART_##x) << 2))
35 +
36 +static int kurobox_pro_miconread(unsigned char *buf, int count)
37 +{
38 +       int i;
39 +       int timeout;
40 +
41 +       for (i = 0; i < count; i++) {
42 +               timeout = 10;
43 +
44 +               while (!(readl(UART1_REG(LSR)) & UART_LSR_DR)) {
45 +                       if (--timeout == 0)
46 +                               break;
47 +                       udelay(1000);
48 +               }
49 +
50 +               if (timeout == 0)
51 +                       break;
52 +               buf[i] = readl(UART1_REG(RX));
53 +       }
54 +
55 +       /* return read bytes */
56 +       return i;
57 +}
58 +
59 +static int kurobox_pro_miconwrite(const unsigned char *buf, int count)
60 +{
61 +       int i = 0;
62 +
63 +       while (count--) {
64 +               while (!(readl(UART1_REG(LSR)) & UART_LSR_THRE))
65 +                       barrier();
66 +               writel(buf[i++], UART1_REG(TX));
67 +       }
68 +
69 +       return 0;
70 +}
71 +
72 +static int kurobox_pro_miconsend(const unsigned char *data, int count)
73 +{
74 +       int i;
75 +       unsigned char checksum = 0;
76 +       unsigned char recv_buf[40];
77 +       unsigned char send_buf[40];
78 +       unsigned char correct_ack[3];
79 +       int retry = 2;
80 +
81 +       /* Generate checksum */
82 +       for (i = 0; i < count; i++)
83 +               checksum -=  data[i];
84 +
85 +       do {
86 +               /* Send data */
87 +               kurobox_pro_miconwrite(data, count);
88 +
89 +               /* send checksum */
90 +               kurobox_pro_miconwrite(&checksum, 1);
91 +
92 +               if (kurobox_pro_miconread(recv_buf, sizeof(recv_buf)) <= 3) {
93 +                       printk(KERN_ERR ">%s: receive failed.\n", __func__);
94 +
95 +                       /* send preamble to clear the receive buffer */
96 +                       memset(&send_buf, 0xff, sizeof(send_buf));
97 +                       kurobox_pro_miconwrite(send_buf, sizeof(send_buf));
98 +
99 +                       /* make dummy reads */
100 +                       mdelay(100);
101 +                       kurobox_pro_miconread(recv_buf, sizeof(recv_buf));
102 +               } else {
103 +                       /* Generate expected ack */
104 +                       correct_ack[0] = 0x01;
105 +                       correct_ack[1] = data[1];
106 +                       correct_ack[2] = 0x00;
107 +
108 +                       /* checksum Check */
109 +                       if ((recv_buf[0] + recv_buf[1] + recv_buf[2] +
110 +                            recv_buf[3]) & 0xFF) {
111 +                               printk(KERN_ERR ">%s: Checksum Error : "
112 +                                       "Received data[%02x, %02x, %02x, %02x]"
113 +                                       "\n", __func__, recv_buf[0],
114 +                                       recv_buf[1], recv_buf[2], recv_buf[3]);
115 +                       } else {
116 +                               /* Check Received Data */
117 +                               if (correct_ack[0] == recv_buf[0] &&
118 +                                   correct_ack[1] == recv_buf[1] &&
119 +                                   correct_ack[2] == recv_buf[2]) {
120 +                                       /* Interval for next command */
121 +                                       mdelay(10);
122 +
123 +                                       /* Receive ACK */
124 +                                       return 0;
125 +                               }
126 +                       }
127 +                       /* Received NAK or illegal Data */
128 +                       printk(KERN_ERR ">%s: Error : NAK or Illegal Data "
129 +                                       "Received\n", __func__);
130 +               }
131 +       } while (retry--);
132 +
133 +       /* Interval for next command */
134 +       mdelay(10);
135 +
136 +       return -1;
137 +}
138 +
139 +static void kurobox_pro_power_off(void)
140 +{
141 +       const unsigned char watchdogkill[]      = {0x01, 0x35, 0x00};
142 +       const unsigned char shutdownwait[]      = {0x00, 0x0c};
143 +       const unsigned char poweroff[]          = {0x00, 0x06};
144 +       /* 38400 baud divisor */
145 +       const unsigned divisor = ((ORION5X_TCLK + (8 * 38400)) / (16 * 38400));
146 +
147 +       pr_info("%s: triggering power-off...\n", __func__);
148 +
149 +       /* hijack uart1 and reset into sane state (38400,8n1,even parity) */
150 +       writel(0x83, UART1_REG(LCR));
151 +       writel(divisor & 0xff, UART1_REG(DLL));
152 +       writel((divisor >> 8) & 0xff, UART1_REG(DLM));
153 +       writel(0x1b, UART1_REG(LCR));
154 +       writel(0x00, UART1_REG(IER));
155 +       writel(0x07, UART1_REG(FCR));
156 +       writel(0x00, UART1_REG(MCR));
157 +
158 +       /* Send the commands to shutdown the Kurobox Pro */
159 +       kurobox_pro_miconsend(watchdogkill, sizeof(watchdogkill)) ;
160 +       kurobox_pro_miconsend(shutdownwait, sizeof(shutdownwait)) ;
161 +       kurobox_pro_miconsend(poweroff, sizeof(poweroff));
162 +}
163 +
164 +/*****************************************************************************
165   * General Setup
166   ****************************************************************************/
167  
168 @@ -203,10 +339,10 @@
169         orion5x_mpp_conf(13, MPP_SATA_LED);     /* SATA 1 presence */
170         orion5x_mpp_conf(14, MPP_SATA_LED);     /* SATA 0 active */
171         orion5x_mpp_conf(15, MPP_SATA_LED);     /* SATA 1 active */
172 -       orion5x_mpp_conf(16, MPP_UNUSED);
173 -       orion5x_mpp_conf(17, MPP_UNUSED);
174 -       orion5x_mpp_conf(18, MPP_UNUSED);
175 -       orion5x_mpp_conf(19, MPP_UNUSED);
176 +       orion5x_mpp_conf(16, MPP_UART);         /* UART1 RXD */
177 +       orion5x_mpp_conf(17, MPP_UART);         /* UART1 TXD */
178 +       orion5x_mpp_conf(18, MPP_UART);         /* UART1 CTSn */
179 +       orion5x_mpp_conf(19, MPP_UART);         /* UART1 RTSn */
180  
181         /*
182          * Configure peripherals.
183 @@ -229,6 +365,9 @@
184         }
185  
186         i2c_register_board_info(0, &kurobox_pro_i2c_rtc, 1);
187 +
188 +       /* register Kurobox Pro specific power-off method */
189 +       pm_power_off = kurobox_pro_power_off;
190  }
191  
192  #ifdef CONFIG_MACH_KUROBOX_PRO