diff options
| -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 |
