summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-07-30 13:02:29 -0700
committerGerrit <chrome-bot@google.com>2012-07-30 15:22:41 -0700
commit2da2e72dd31c592fd3946b03eb927e8616a62808 (patch)
tree0b06c2f1ca267d4aa96d7061469d192097982408
parent7adb0aad167779fce70fc167af4455d5f8e18ab6 (diff)
downloadchrome-ec-2da2e72dd31c592fd3946b03eb927e8616a62808.tar.gz
Add support for ACPI read/write commands
This is needed to support the kernel keyboard backlight driver through ACPI. Also adds a few other memory addresses for testing this interface - version, test, and test-compliment. BUG=chrome-os-partner:12001 TEST=manual - query next ACPI event io_write8 0x66 0x84 io_read8 0x62 0x00 - read ACPI memmap version io_write8 0x66 0x80 io_write8 0x62 0 io_read8 0x62 0x01 - extra command writes shouldn't crash io_write8 0x66 0x80 io_write8 0x66 0x80 io_write8 0x62 1 - extra data writes shouldn't crash either io_write8 0x62 1 io_write8 0x62 1 - write test address io_write8 0x66 0x81 io_write8 0x62 1 io_write8 0x62 0x2a - read it back io_write8 0x66 0x80 io_write8 0x62 1 io_read8 0x62 0x2a - read back test compliment io_write8 0x66 0x80 io_write8 0x62 2 io_read8 0x62 0xd5 - set keyboard backlight to 50% io_write8 0x66 0x81 io_write8 0x62 3 io_write8 0x62 50 - read it back io_write8 0x66 0x80 io_write8 0x62 3 io_read8 0x62 0x32 Change-Id: I619fdbd322cdef8ffffbb882b3bbb587e364334d Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/28714 Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
-rw-r--r--chip/lm4/lpc.c147
-rw-r--r--chip/lm4/registers.h6
-rw-r--r--include/ec_commands.h85
3 files changed, 180 insertions, 58 deletions
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c
index 91ce43a198..c7e1d988cf 100644
--- a/chip/lm4/lpc.c
+++ b/chip/lm4/lpc.c
@@ -13,6 +13,7 @@
#include "i8042.h"
#include "lpc.h"
#include "port80.h"
+#include "pwm.h"
#include "registers.h"
#include "system.h"
#include "task.h"
@@ -26,12 +27,10 @@
#define LPC_SYSJUMP_TAG 0x4c50 /* "LP" */
-/* Bit masks for LPCCH?ST */
-#define LPC_STATUS_MASK_BUSY (1 << 12)
-#define LPC_STATUS_MASK_SMI (1 << 10)
-#define LPC_STATUS_MASK_SCI (1 << 9)
-#define LPC_STATUS_MASK_PRESENT (1 << 8)
-#define LPC_STATUS_MASK_TOH (1 << 0) /* TO Host bit */
+static uint8_t acpi_cmd; /* Last received ACPI command */
+static uint8_t acpi_addr; /* First byte of data after ACPI command */
+static int acpi_data_count; /* Number of data writes after command */
+static uint8_t acpi_mem_test; /* Test byte in ACPI memory space */
static uint32_t host_events; /* Currently pending SCI/SMI events */
static uint32_t event_mask[3]; /* Event masks for each type */
@@ -56,7 +55,6 @@ static void configure_gpio(void)
gpio_set_alternate_function(LM4_GPIO_M, 0x33, 0x0f);
}
-
static void wait_irq_sent(void)
{
/* TODO: udelay() is not graceful. Since the SIRQRIS is almost not
@@ -190,14 +188,14 @@ static void lpc_send_response(struct host_cmd_handler_args *args)
/* Clear the busy bit */
task_disable_irq(LM4_IRQ_LPC);
- LM4_LPC_ST(LPC_CH_CMD) &= ~LPC_STATUS_MASK_BUSY;
+ LM4_LPC_ST(LPC_CH_CMD) &= ~LM4_LPC_ST_BUSY;
task_enable_irq(LM4_IRQ_LPC);
}
/* Return true if the TOH is still set */
int lpc_keyboard_has_char(void)
{
- return (LM4_LPC_ST(LPC_CH_KEYBOARD) & LPC_STATUS_MASK_TOH) ? 1 : 0;
+ return (LM4_LPC_ST(LPC_CH_KEYBOARD) & LM4_LPC_ST_TOH) ? 1 : 0;
}
@@ -217,7 +215,7 @@ void lpc_keyboard_clear_buffer(void)
/* Make sure the previous TOH and IRQ has been sent out. */
wait_irq_sent();
- LM4_LPC_ST(LPC_CH_KEYBOARD) &= ~LPC_STATUS_MASK_TOH;
+ LM4_LPC_ST(LPC_CH_KEYBOARD) &= ~LM4_LPC_ST_TOH;
/* Ensure there is no TOH set in this period. */
wait_irq_sent();
@@ -269,18 +267,18 @@ static void update_host_event_status(void) {
if (host_events & event_mask[LPC_HOST_EVENT_SMI]) {
/* Only generate SMI for first event */
- if (!(LM4_LPC_ST(LPC_CH_ACPI) & LPC_STATUS_MASK_SMI))
+ if (!(LM4_LPC_ST(LPC_CH_ACPI) & LM4_LPC_ST_SMI))
need_smi = 1;
- LM4_LPC_ST(LPC_CH_ACPI) |= LPC_STATUS_MASK_SMI;
+ LM4_LPC_ST(LPC_CH_ACPI) |= LM4_LPC_ST_SMI;
} else
- LM4_LPC_ST(LPC_CH_ACPI) &= ~LPC_STATUS_MASK_SMI;
+ LM4_LPC_ST(LPC_CH_ACPI) &= ~LM4_LPC_ST_SMI;
if (host_events & event_mask[LPC_HOST_EVENT_SCI]) {
/* Generate SCI for every event */
need_sci = 1;
- LM4_LPC_ST(LPC_CH_ACPI) |= LPC_STATUS_MASK_SCI;
+ LM4_LPC_ST(LPC_CH_ACPI) |= LM4_LPC_ST_SCI;
} else
- LM4_LPC_ST(LPC_CH_ACPI) &= ~LPC_STATUS_MASK_SCI;
+ LM4_LPC_ST(LPC_CH_ACPI) &= ~LM4_LPC_ST_SCI;
/* Copy host events to mapped memory */
*(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = host_events;
@@ -321,44 +319,99 @@ uint32_t lpc_get_host_event_mask(enum lpc_host_event_type type)
return event_mask[type];
}
-/* Handle an ACPI command */
-static void handle_acpi_command(void)
+/**
+ * Handle command (is_cmd=1) or data (is_cmd=0) writes to ACPI I/O ports.
+ */
+static void handle_acpi_write(int is_cmd)
{
- int cmd;
- int result = 0;
- int i;
+ int data = 0;
/* Set the busy bit */
- LM4_LPC_ST(LPC_CH_ACPI) |= LPC_STATUS_MASK_BUSY;
+ LM4_LPC_ST(LPC_CH_ACPI) |= LM4_LPC_ST_BUSY;
- /*
- * Read the command byte and pass to the host command handler.
- * This clears the FRMH bit in the status byte.
- */
- cmd = LPC_POOL_ACPI[0];
+ /* Read command/data; this clears the FRMH status bit. */
+ if (is_cmd) {
+ acpi_cmd = LPC_POOL_ACPI[0];
+ acpi_data_count = 0;
+ } else {
+ data = LPC_POOL_ACPI[0];
+ /*
+ * The first data byte is the ACPI memory address for
+ * read/write commands.
+ */
+ if (!acpi_data_count++)
+ acpi_addr = data;
+ }
+
+ /* Process complete commands */
+ if (acpi_cmd == EC_CMD_ACPI_READ && acpi_data_count == 1) {
+ /* ACPI read cmd + addr */
+ int result = 0;
+
+ switch (acpi_addr) {
+ case EC_ACPI_MEM_VERSION:
+ result = EC_ACPI_MEM_VERSION_CURRENT;
+ break;
+ case EC_ACPI_MEM_TEST:
+ result = acpi_mem_test;
+ break;
+ case EC_ACPI_MEM_TEST_COMPLIMENT:
+ result = 0xff - acpi_mem_test;
+ break;
+#ifdef CONFIG_TASK_PWM
+ case EC_ACPI_MEM_KEYBOARD_BACKLIGHT:
+ /*
+ * TODO: not very satisfying that LPC knows directly
+ * about the keyboard backlight, but for now this is
+ * good enough and less code than defining a new
+ * console command interface just for ACPI read/write.
+ */
+ result = pwm_get_keyboard_backlight();
+ break;
+#endif
+ default:
+ break;
+ }
+
+ /* Send the result byte */
+ CPRINTF("[%T ACPI read 0x%02x = 0x%02x]\n", acpi_addr, result);
+ LPC_POOL_ACPI[1] = result;
+
+ } else if (acpi_cmd == EC_CMD_ACPI_WRITE && acpi_data_count == 2) {
+ /* ACPI write cmd + addr + data */
+ CPRINTF("[%T ACPI write 0x%02x = 0x%02x]\n", acpi_addr, data);
+ switch (acpi_addr) {
+ case EC_ACPI_MEM_TEST:
+ acpi_mem_test = data;
+ break;
+#ifdef CONFIG_TASK_PWM
+ case EC_ACPI_MEM_KEYBOARD_BACKLIGHT:
+ pwm_set_keyboard_backlight(data);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ } else if (acpi_cmd == EC_CMD_ACPI_QUERY_EVENT && !acpi_data_count) {
+ /* Clear and return the lowest host event */
+ int evt_index = 0;
+ int i;
- /* Process the command */
- switch (cmd) {
- case EC_CMD_ACPI_QUERY_EVENT:
for (i = 0; i < 32; i++) {
if (host_events & (1 << i)) {
host_clear_events(1 << i);
- result = i + 1; /* Events are 1-based */
+ evt_index = i + 1; /* Events are 1-based */
break;
}
}
- break;
- default:
- /* Something we don't handle; ignore it */
- break;
+ CPRINTF("[%T ACPI query = %d]\n", evt_index);
+ LPC_POOL_ACPI[1] = evt_index;
}
- /* Write the response */
- LPC_POOL_ACPI[1] = result;
-
/* Clear the busy bit */
- LM4_LPC_ST(LPC_CH_ACPI) &= ~LPC_STATUS_MASK_BUSY;
+ LM4_LPC_ST(LPC_CH_ACPI) &= ~LM4_LPC_ST_BUSY;
/*
* ACPI 5.0-12.6.1: Generate SCI for Input Buffer Empty / Output Buffer
@@ -368,9 +421,11 @@ static void handle_acpi_command(void)
}
/**
- * We have received an unexpected ACPI request on the normal command channel
- * from an old firmware/kernel, try to somewhat answer it.
+ * Handle unexpected ACPI query request on the normal command channel from an
+ * old API firmware/kernel. No need to handle other ACPI commands on the
+ * normal command channel, because old firmware/kernel only supported query.
*/
+/* TODO: remove when link EVT is deprecated. */
static int acpi_on_bad_channel(struct host_cmd_handler_args *args)
{
int i;
@@ -450,14 +505,16 @@ static void lpc_interrupt(void)
LM4_LPC_LPCIC = mis;
#ifdef CONFIG_TASK_HOSTCMD
- /* Handle ACPI command writes */
+ /* Handle ACPI command and data writes */
if (mis & LM4_LPC_INT_MASK(LPC_CH_ACPI, 4))
- handle_acpi_command();
+ handle_acpi_write(1);
+ if (mis & LM4_LPC_INT_MASK(LPC_CH_ACPI, 2))
+ handle_acpi_write(0);
/* Handle user command writes */
if (mis & LM4_LPC_INT_MASK(LPC_CH_CMD, 4)) {
/* Set the busy bit */
- LM4_LPC_ST(LPC_CH_CMD) |= LPC_STATUS_MASK_BUSY;
+ LM4_LPC_ST(LPC_CH_CMD) |= LM4_LPC_ST_BUSY;
/*
* Read the command byte. This clears the FRMH bit in the
@@ -557,8 +614,8 @@ static int lpc_init(void)
LM4_LPC_ADR(LPC_CH_ACPI) = EC_LPC_ADDR_ACPI_DATA;
LM4_LPC_CTL(LPC_CH_ACPI) = (LPC_POOL_OFFS_ACPI << (5 - 1));
LM4_LPC_ST(LPC_CH_ACPI) = 0;
- /* Unmask interrupt for host command writes */
- LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_ACPI, 4);
+ /* Unmask interrupt for host command and data writes */
+ LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_ACPI, 6);
/*
* Set LPC channel 1 to I/O address 0x80 (data), single endpoint,
diff --git a/chip/lm4/registers.h b/chip/lm4/registers.h
index 4cef5af088..6578e104e7 100644
--- a/chip/lm4/registers.h
+++ b/chip/lm4/registers.h
@@ -118,6 +118,12 @@ static inline int lm4_lpc_addr(int ch, int offset)
#define LM4LPCREG(ch, offset) LM4REG(lm4_lpc_addr(ch, offset))
#define LM4_LPC_CTL(ch) LM4LPCREG(ch, 0x000)
#define LM4_LPC_ST(ch) LM4LPCREG(ch, 0x004)
+#define LM4_LPC_ST_TOH (1 << 0) /* TO Host bit */
+#define LM4_LPC_ST_CMD (1 << 3) /* Last from-host byte was command */
+#define LM4_LPC_ST_PRESENT (1 << 8)
+#define LM4_LPC_ST_SCI (1 << 9)
+#define LM4_LPC_ST_SMI (1 << 10)
+#define LM4_LPC_ST_BUSY (1 << 12)
#define LM4_LPC_ADR(ch) LM4LPCREG(ch, 0x008)
#define LM4_LPC_POOL_BYTES 1024 /* Size of LPCPOOL in bytes */
#define LM4_LPC_LPCPOOL ((volatile unsigned char *)0x40080400)
diff --git a/include/ec_commands.h b/include/ec_commands.h
index f6680ca8b0..c94fed5c96 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -50,15 +50,14 @@
#define EC_LPC_ADDR_OLD_PARAM 0x880
#define EC_OLD_PARAM_SIZE 0x080 /* Size of param area in bytes */
-
/* EC command register bit functions */
-#define EC_LPC_CMDR_DATA (1 << 0)
-#define EC_LPC_CMDR_PENDING (1 << 1)
-#define EC_LPC_CMDR_BUSY (1 << 2)
-#define EC_LPC_CMDR_CMD (1 << 3)
-#define EC_LPC_CMDR_ACPI_BRST (1 << 4)
-#define EC_LPC_CMDR_SCI (1 << 5)
-#define EC_LPC_CMDR_SMI (1 << 6)
+#define EC_LPC_CMDR_DATA (1 << 0) /* Data ready for host to read */
+#define EC_LPC_CMDR_PENDING (1 << 1) /* Write pending to EC */
+#define EC_LPC_CMDR_BUSY (1 << 2) /* EC is busy processing a command */
+#define EC_LPC_CMDR_CMD (1 << 3) /* Last host write was a command */
+#define EC_LPC_CMDR_ACPI_BRST (1 << 4) /* Burst mode (not used) */
+#define EC_LPC_CMDR_SCI (1 << 5) /* SCI event is pending */
+#define EC_LPC_CMDR_SMI (1 << 6) /* SMI event is pending */
#define EC_LPC_ADDR_MEMMAP 0x900
#define EC_MEMMAP_SIZE 255 /* ACPI IO buffer max is 255 bytes */
@@ -807,6 +806,14 @@ struct ec_response_temp_sensor_get_info {
} __packed;
/*****************************************************************************/
+
+/*
+ * Note: host commands 0x80 - 0x87 are reserved to avoid conflict with ACPI
+ * commands accidentally sent to the wrong interface. See the ACPI section
+ * below.
+ */
+
+/*****************************************************************************/
/* Host event commands */
/*
@@ -940,23 +947,75 @@ struct ec_params_reboot_ec {
/*****************************************************************************/
/*
- * Special commands
+ * ACPI commands
*
- * These do not follow the normal rules for commands. See each command for
- * details.
+ * These are valid ONLY on the ACPI command/data port.
*/
/*
+ * ACPI Read Embedded Controller
+ *
+ * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*).
+ *
+ * Use the following sequence:
+ *
+ * - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD
+ * - Wait for EC_LPC_CMDR_PENDING bit to clear
+ * - Write address to EC_LPC_ADDR_ACPI_DATA
+ * - Wait for EC_LPC_CMDR_DATA bit to set
+ * - Read value from EC_LPC_ADDR_ACPI_DATA
+ */
+#define EC_CMD_ACPI_READ 0x80
+
+/*
+ * ACPI Write Embedded Controller
+ *
+ * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*).
+ *
+ * Use the following sequence:
+ *
+ * - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD
+ * - Wait for EC_LPC_CMDR_PENDING bit to clear
+ * - Write address to EC_LPC_ADDR_ACPI_DATA
+ * - Wait for EC_LPC_CMDR_PENDING bit to clear
+ * - Write value to EC_LPC_ADDR_ACPI_DATA
+ */
+#define EC_CMD_ACPI_WRITE 0x81
+
+/*
* ACPI Query Embedded Controller
*
* This clears the lowest-order bit in the currently pending host events, and
* sets the result code to the 1-based index of the bit (event 0x00000001 = 1,
* event 0x80000000 = 32), or 0 if no event was pending.
- *
- * This command is valid ONLY on port 62/66.
*/
#define EC_CMD_ACPI_QUERY_EVENT 0x84
+/* Valid addresses in ACPI memory space, for read/write commands */
+/* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */
+#define EC_ACPI_MEM_VERSION 0x00
+/*
+ * Test location; writing value here updates test compliment byte to (0xff -
+ * value).
+ */
+#define EC_ACPI_MEM_TEST 0x01
+/* Test compliment; writes here are ignored. */
+#define EC_ACPI_MEM_TEST_COMPLIMENT 0x02
+/* Keyboard backlight brightness percent (0 - 100) */
+#define EC_ACPI_MEM_KEYBOARD_BACKLIGHT 0x03
+
+/* Current version of ACPI memory address space */
+#define EC_ACPI_MEM_VERSION_CURRENT 1
+
+
+/*****************************************************************************/
+/*
+ * Special commands
+ *
+ * These do not follow the normal rules for commands. See each command for
+ * details.
+ */
+
/*
* Reboot NOW
*