Skip to content

Commit

Permalink
[ARM] Orion: implement power-off method for Kurobox Pro
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sylver Bruneau authored and Lennert Buytenhek committed Jun 22, 2008
1 parent 2850a03 commit a0087f2
Showing 1 changed file with 143 additions and 4 deletions.
147 changes: 143 additions & 4 deletions arch/arm/mach-orion5x/kurobox_pro-setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/nand.h>
#include <linux/mv643xx_eth.h>
#include <linux/i2c.h>
#include <linux/serial_reg.h>
#include <linux/ata_platform.h>
#include <asm/mach-types.h>
#include <asm/gpio.h>
Expand Down Expand Up @@ -174,6 +176,140 @@ static struct mv_sata_platform_data kurobox_pro_sata_data = {
.n_ports = 2,
};

/*****************************************************************************
* Kurobox Pro specific power off method via UART1-attached microcontroller
****************************************************************************/

#define UART1_REG(x) (UART1_VIRT_BASE + ((UART_##x) << 2))

static int kurobox_pro_miconread(unsigned char *buf, int count)
{
int i;
int timeout;

for (i = 0; i < count; i++) {
timeout = 10;

while (!(readl(UART1_REG(LSR)) & UART_LSR_DR)) {
if (--timeout == 0)
break;
udelay(1000);
}

if (timeout == 0)
break;
buf[i] = readl(UART1_REG(RX));
}

/* return read bytes */
return i;
}

static int kurobox_pro_miconwrite(const unsigned char *buf, int count)
{
int i = 0;

while (count--) {
while (!(readl(UART1_REG(LSR)) & UART_LSR_THRE))
barrier();
writel(buf[i++], UART1_REG(TX));
}

return 0;
}

static int kurobox_pro_miconsend(const unsigned char *data, int count)
{
int i;
unsigned char checksum = 0;
unsigned char recv_buf[40];
unsigned char send_buf[40];
unsigned char correct_ack[3];
int retry = 2;

/* Generate checksum */
for (i = 0; i < count; i++)
checksum -= data[i];

do {
/* Send data */
kurobox_pro_miconwrite(data, count);

/* send checksum */
kurobox_pro_miconwrite(&checksum, 1);

if (kurobox_pro_miconread(recv_buf, sizeof(recv_buf)) <= 3) {
printk(KERN_ERR ">%s: receive failed.\n", __func__);

/* send preamble to clear the receive buffer */
memset(&send_buf, 0xff, sizeof(send_buf));
kurobox_pro_miconwrite(send_buf, sizeof(send_buf));

/* make dummy reads */
mdelay(100);
kurobox_pro_miconread(recv_buf, sizeof(recv_buf));
} else {
/* Generate expected ack */
correct_ack[0] = 0x01;
correct_ack[1] = data[1];
correct_ack[2] = 0x00;

/* checksum Check */
if ((recv_buf[0] + recv_buf[1] + recv_buf[2] +
recv_buf[3]) & 0xFF) {
printk(KERN_ERR ">%s: Checksum Error : "
"Received data[%02x, %02x, %02x, %02x]"
"\n", __func__, recv_buf[0],
recv_buf[1], recv_buf[2], recv_buf[3]);
} else {
/* Check Received Data */
if (correct_ack[0] == recv_buf[0] &&
correct_ack[1] == recv_buf[1] &&
correct_ack[2] == recv_buf[2]) {
/* Interval for next command */
mdelay(10);

/* Receive ACK */
return 0;
}
}
/* Received NAK or illegal Data */
printk(KERN_ERR ">%s: Error : NAK or Illegal Data "
"Received\n", __func__);
}
} while (retry--);

/* Interval for next command */
mdelay(10);

return -1;
}

static void kurobox_pro_power_off(void)
{
const unsigned char watchdogkill[] = {0x01, 0x35, 0x00};
const unsigned char shutdownwait[] = {0x00, 0x0c};
const unsigned char poweroff[] = {0x00, 0x06};
/* 38400 baud divisor */
const unsigned divisor = ((ORION5X_TCLK + (8 * 38400)) / (16 * 38400));

pr_info("%s: triggering power-off...\n", __func__);

/* hijack uart1 and reset into sane state (38400,8n1,even parity) */
writel(0x83, UART1_REG(LCR));
writel(divisor & 0xff, UART1_REG(DLL));
writel((divisor >> 8) & 0xff, UART1_REG(DLM));
writel(0x1b, UART1_REG(LCR));
writel(0x00, UART1_REG(IER));
writel(0x07, UART1_REG(FCR));
writel(0x00, UART1_REG(MCR));

/* Send the commands to shutdown the Kurobox Pro */
kurobox_pro_miconsend(watchdogkill, sizeof(watchdogkill)) ;
kurobox_pro_miconsend(shutdownwait, sizeof(shutdownwait)) ;
kurobox_pro_miconsend(poweroff, sizeof(poweroff));
}

/*****************************************************************************
* General Setup
****************************************************************************/
Expand All @@ -194,10 +330,10 @@ static struct orion5x_mpp_mode kurobox_pro_mpp_modes[] __initdata = {
{ 13, MPP_SATA_LED }, /* SATA 1 presence */
{ 14, MPP_SATA_LED }, /* SATA 0 active */
{ 15, MPP_SATA_LED }, /* SATA 1 active */
{ 16, MPP_UNUSED },
{ 17, MPP_UNUSED },
{ 18, MPP_UNUSED },
{ 19, MPP_UNUSED },
{ 16, MPP_UART }, /* UART1 RXD */
{ 17, MPP_UART }, /* UART1 TXD */
{ 18, MPP_UART }, /* UART1 CTSn */
{ 19, MPP_UART }, /* UART1 RTSn */
{ -1 },
};

Expand Down Expand Up @@ -231,6 +367,9 @@ static void __init kurobox_pro_init(void)
}

i2c_register_board_info(0, &kurobox_pro_i2c_rtc, 1);

/* register Kurobox Pro specific power-off method */
pm_power_off = kurobox_pro_power_off;
}

#ifdef CONFIG_MACH_KUROBOX_PRO
Expand Down

0 comments on commit a0087f2

Please sign in to comment.