diff options
author | Sylver Bruneau <sylver.bruneau@googlemail.com> | 2008-04-30 02:14:58 -0400 |
---|---|---|
committer | Lennert Buytenhek <buytenh@marvell.com> | 2008-06-22 16:44:49 -0400 |
commit | a0087f2fcf5cb4e16502f5334168fbe304af318b (patch) | |
tree | 7c2465ccefd0bb67b4ca079724319a634bf3a64c /arch/arm/mach-orion5x | |
parent | 2850a037734eac0af2e6abc4e8be986c3edfd834 (diff) |
[ARM] Orion: implement power-off method for Kurobox Pro
This patch implements the communication with the microcontroller on the
Kurobox Pro and Linkstation Pro/Live boards. This is allowing to send
the commands needed to power-off the board correctly.
Signed-off-by: Sylver Bruneau <sylver.bruneau@googlemail.com>
Acked-by: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Diffstat (limited to 'arch/arm/mach-orion5x')
-rw-r--r-- | arch/arm/mach-orion5x/kurobox_pro-setup.c | 147 |
1 files changed, 143 insertions, 4 deletions
diff --git a/arch/arm/mach-orion5x/kurobox_pro-setup.c b/arch/arm/mach-orion5x/kurobox_pro-setup.c index 8b4a8aa5a836..37b4faa6ace7 100644 --- a/arch/arm/mach-orion5x/kurobox_pro-setup.c +++ b/arch/arm/mach-orion5x/kurobox_pro-setup.c | |||
@@ -13,10 +13,12 @@ | |||
13 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
14 | #include <linux/pci.h> | 14 | #include <linux/pci.h> |
15 | #include <linux/irq.h> | 15 | #include <linux/irq.h> |
16 | #include <linux/delay.h> | ||
16 | #include <linux/mtd/physmap.h> | 17 | #include <linux/mtd/physmap.h> |
17 | #include <linux/mtd/nand.h> | 18 | #include <linux/mtd/nand.h> |
18 | #include <linux/mv643xx_eth.h> | 19 | #include <linux/mv643xx_eth.h> |
19 | #include <linux/i2c.h> | 20 | #include <linux/i2c.h> |
21 | #include <linux/serial_reg.h> | ||
20 | #include <linux/ata_platform.h> | 22 | #include <linux/ata_platform.h> |
21 | #include <asm/mach-types.h> | 23 | #include <asm/mach-types.h> |
22 | #include <asm/gpio.h> | 24 | #include <asm/gpio.h> |
@@ -175,6 +177,140 @@ static struct mv_sata_platform_data kurobox_pro_sata_data = { | |||
175 | }; | 177 | }; |
176 | 178 | ||
177 | /***************************************************************************** | 179 | /***************************************************************************** |
180 | * Kurobox Pro specific power off method via UART1-attached microcontroller | ||
181 | ****************************************************************************/ | ||
182 | |||
183 | #define UART1_REG(x) (UART1_VIRT_BASE + ((UART_##x) << 2)) | ||
184 | |||
185 | static int kurobox_pro_miconread(unsigned char *buf, int count) | ||
186 | { | ||
187 | int i; | ||
188 | int timeout; | ||
189 | |||
190 | for (i = 0; i < count; i++) { | ||
191 | timeout = 10; | ||
192 | |||
193 | while (!(readl(UART1_REG(LSR)) & UART_LSR_DR)) { | ||
194 | if (--timeout == 0) | ||
195 | break; | ||
196 | udelay(1000); | ||
197 | } | ||
198 | |||
199 | if (timeout == 0) | ||
200 | break; | ||
201 | buf[i] = readl(UART1_REG(RX)); | ||
202 | } | ||
203 | |||
204 | /* return read bytes */ | ||
205 | return i; | ||
206 | } | ||
207 | |||
208 | static int kurobox_pro_miconwrite(const unsigned char *buf, int count) | ||
209 | { | ||
210 | int i = 0; | ||
211 | |||
212 | while (count--) { | ||
213 | while (!(readl(UART1_REG(LSR)) & UART_LSR_THRE)) | ||
214 | barrier(); | ||
215 | writel(buf[i++], UART1_REG(TX)); | ||
216 | } | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int kurobox_pro_miconsend(const unsigned char *data, int count) | ||
222 | { | ||
223 | int i; | ||
224 | unsigned char checksum = 0; | ||
225 | unsigned char recv_buf[40]; | ||
226 | unsigned char send_buf[40]; | ||
227 | unsigned char correct_ack[3]; | ||
228 | int retry = 2; | ||
229 | |||
230 | /* Generate checksum */ | ||
231 | for (i = 0; i < count; i++) | ||
232 | checksum -= data[i]; | ||
233 | |||
234 | do { | ||
235 | /* Send data */ | ||
236 | kurobox_pro_miconwrite(data, count); | ||
237 | |||
238 | /* send checksum */ | ||
239 | kurobox_pro_miconwrite(&checksum, 1); | ||
240 | |||
241 | if (kurobox_pro_miconread(recv_buf, sizeof(recv_buf)) <= 3) { | ||
242 | printk(KERN_ERR ">%s: receive failed.\n", __func__); | ||
243 | |||
244 | /* send preamble to clear the receive buffer */ | ||
245 | memset(&send_buf, 0xff, sizeof(send_buf)); | ||
246 | kurobox_pro_miconwrite(send_buf, sizeof(send_buf)); | ||
247 | |||
248 | /* make dummy reads */ | ||
249 | mdelay(100); | ||
250 | kurobox_pro_miconread(recv_buf, sizeof(recv_buf)); | ||
251 | } else { | ||
252 | /* Generate expected ack */ | ||
253 | correct_ack[0] = 0x01; | ||
254 | correct_ack[1] = data[1]; | ||
255 | correct_ack[2] = 0x00; | ||
256 | |||
257 | /* checksum Check */ | ||
258 | if ((recv_buf[0] + recv_buf[1] + recv_buf[2] + | ||
259 | recv_buf[3]) & 0xFF) { | ||
260 | printk(KERN_ERR ">%s: Checksum Error : " | ||
261 | "Received data[%02x, %02x, %02x, %02x]" | ||
262 | "\n", __func__, recv_buf[0], | ||
263 | recv_buf[1], recv_buf[2], recv_buf[3]); | ||
264 | } else { | ||
265 | /* Check Received Data */ | ||
266 | if (correct_ack[0] == recv_buf[0] && | ||
267 | correct_ack[1] == recv_buf[1] && | ||
268 | correct_ack[2] == recv_buf[2]) { | ||
269 | /* Interval for next command */ | ||
270 | mdelay(10); | ||
271 | |||
272 | /* Receive ACK */ | ||
273 | return 0; | ||
274 | } | ||
275 | } | ||
276 | /* Received NAK or illegal Data */ | ||
277 | printk(KERN_ERR ">%s: Error : NAK or Illegal Data " | ||
278 | "Received\n", __func__); | ||
279 | } | ||
280 | } while (retry--); | ||
281 | |||
282 | /* Interval for next command */ | ||
283 | mdelay(10); | ||
284 | |||
285 | return -1; | ||
286 | } | ||
287 | |||
288 | static void kurobox_pro_power_off(void) | ||
289 | { | ||
290 | const unsigned char watchdogkill[] = {0x01, 0x35, 0x00}; | ||
291 | const unsigned char shutdownwait[] = {0x00, 0x0c}; | ||
292 | const unsigned char poweroff[] = {0x00, 0x06}; | ||
293 | /* 38400 baud divisor */ | ||
294 | const unsigned divisor = ((ORION5X_TCLK + (8 * 38400)) / (16 * 38400)); | ||
295 | |||
296 | pr_info("%s: triggering power-off...\n", __func__); | ||
297 | |||
298 | /* hijack uart1 and reset into sane state (38400,8n1,even parity) */ | ||
299 | writel(0x83, UART1_REG(LCR)); | ||
300 | writel(divisor & 0xff, UART1_REG(DLL)); | ||
301 | writel((divisor >> 8) & 0xff, UART1_REG(DLM)); | ||
302 | writel(0x1b, UART1_REG(LCR)); | ||
303 | writel(0x00, UART1_REG(IER)); | ||
304 | writel(0x07, UART1_REG(FCR)); | ||
305 | writel(0x00, UART1_REG(MCR)); | ||
306 | |||
307 | /* Send the commands to shutdown the Kurobox Pro */ | ||
308 | kurobox_pro_miconsend(watchdogkill, sizeof(watchdogkill)) ; | ||
309 | kurobox_pro_miconsend(shutdownwait, sizeof(shutdownwait)) ; | ||
310 | kurobox_pro_miconsend(poweroff, sizeof(poweroff)); | ||
311 | } | ||
312 | |||
313 | /***************************************************************************** | ||
178 | * General Setup | 314 | * General Setup |
179 | ****************************************************************************/ | 315 | ****************************************************************************/ |
180 | static struct orion5x_mpp_mode kurobox_pro_mpp_modes[] __initdata = { | 316 | static struct orion5x_mpp_mode kurobox_pro_mpp_modes[] __initdata = { |
@@ -194,10 +330,10 @@ static struct orion5x_mpp_mode kurobox_pro_mpp_modes[] __initdata = { | |||
194 | { 13, MPP_SATA_LED }, /* SATA 1 presence */ | 330 | { 13, MPP_SATA_LED }, /* SATA 1 presence */ |
195 | { 14, MPP_SATA_LED }, /* SATA 0 active */ | 331 | { 14, MPP_SATA_LED }, /* SATA 0 active */ |
196 | { 15, MPP_SATA_LED }, /* SATA 1 active */ | 332 | { 15, MPP_SATA_LED }, /* SATA 1 active */ |
197 | { 16, MPP_UNUSED }, | 333 | { 16, MPP_UART }, /* UART1 RXD */ |
198 | { 17, MPP_UNUSED }, | 334 | { 17, MPP_UART }, /* UART1 TXD */ |
199 | { 18, MPP_UNUSED }, | 335 | { 18, MPP_UART }, /* UART1 CTSn */ |
200 | { 19, MPP_UNUSED }, | 336 | { 19, MPP_UART }, /* UART1 RTSn */ |
201 | { -1 }, | 337 | { -1 }, |
202 | }; | 338 | }; |
203 | 339 | ||
@@ -231,6 +367,9 @@ static void __init kurobox_pro_init(void) | |||
231 | } | 367 | } |
232 | 368 | ||
233 | i2c_register_board_info(0, &kurobox_pro_i2c_rtc, 1); | 369 | i2c_register_board_info(0, &kurobox_pro_i2c_rtc, 1); |
370 | |||
371 | /* register Kurobox Pro specific power-off method */ | ||
372 | pm_power_off = kurobox_pro_power_off; | ||
234 | } | 373 | } |
235 | 374 | ||
236 | #ifdef CONFIG_MACH_KUROBOX_PRO | 375 | #ifdef CONFIG_MACH_KUROBOX_PRO |