344 lines
9.7 KiB
Diff
344 lines
9.7 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Aun-Ali Zaidi <admin@kodeit.net>
|
|
Date: Sun, 17 Nov 2019 23:12:16 +0100
|
|
Subject: applesmc: basic mmio interface implementation
|
|
|
|
This change introduces a basic MMIO-based
|
|
interface implementation required to communicate
|
|
with the SMC on T2 Macs. The MMIO interface is
|
|
enabled only when it's supported on the running
|
|
system.
|
|
|
|
The MMIO interface replaces legacy port-based SMC
|
|
key reads, writes and metadata requests (getting
|
|
key by index and getting key info).
|
|
|
|
(Based on patch by @mcmrarm)
|
|
|
|
Signed-off-by: Aun-Ali Zaidi <admin@kodeit.net>
|
|
---
|
|
drivers/hwmon/applesmc.c | 237 +++++++++-
|
|
1 file changed, 231 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/hwmon/applesmc.c
|
|
+++ b/drivers/hwmon/applesmc.c
|
|
@@ -42,6 +42,18 @@
|
|
|
|
#define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */
|
|
|
|
+#define APPLESMC_IOMEM_KEY_DATA 0
|
|
+#define APPLESMC_IOMEM_KEY_STATUS 0x4005
|
|
+#define APPLESMC_IOMEM_KEY_NAME 0x78
|
|
+#define APPLESMC_IOMEM_KEY_DATA_LEN 0x7D
|
|
+#define APPLESMC_IOMEM_KEY_SMC_ID 0x7E
|
|
+#define APPLESMC_IOMEM_KEY_CMD 0x7F
|
|
+#define APPLESMC_IOMEM_MIN_SIZE 0x4006
|
|
+
|
|
+#define APPLESMC_IOMEM_KEY_TYPE_CODE 0
|
|
+#define APPLESMC_IOMEM_KEY_TYPE_DATA_LEN 5
|
|
+#define APPLESMC_IOMEM_KEY_TYPE_FLAGS 6
|
|
+
|
|
#define APPLESMC_MAX_DATA_LENGTH 32
|
|
|
|
/* Apple SMC status bits */
|
|
@@ -138,10 +150,13 @@ struct applesmc_registers {
|
|
|
|
struct applesmc_device {
|
|
struct acpi_device *dev;
|
|
+ struct device *ldev;
|
|
struct applesmc_registers reg;
|
|
|
|
- bool port_base_set;
|
|
+ bool port_base_set, iomem_base_set;
|
|
u16 port_base;
|
|
+ u8 *__iomem iomem_base;
|
|
+ u32 iomem_base_addr, iomem_base_size;
|
|
|
|
s16 rest_x;
|
|
s16 rest_y;
|
|
@@ -347,16 +362,156 @@ static int port_get_smc_key_info(struct applesmc_device *smc,
|
|
return 0;
|
|
}
|
|
|
|
+
|
|
+/*
|
|
+ * MMIO based communication.
|
|
+ * TODO: Use updated mechanism for cmd timeout/retry
|
|
+ */
|
|
+
|
|
+static void iomem_clear_status(struct applesmc_device *smc)
|
|
+{
|
|
+ if (ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_STATUS))
|
|
+ iowrite8(0, smc->iomem_base + APPLESMC_IOMEM_KEY_STATUS);
|
|
+}
|
|
+
|
|
+static int iomem_wait_read(struct applesmc_device *smc)
|
|
+{
|
|
+ u8 status;
|
|
+ int us;
|
|
+ int i;
|
|
+
|
|
+ us = APPLESMC_MIN_WAIT;
|
|
+ for (i = 0; i < 24 ; i++) {
|
|
+ status = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_STATUS);
|
|
+ if (status & 0x20)
|
|
+ return 0;
|
|
+ usleep_range(us, us * 2);
|
|
+ if (i > 9)
|
|
+ us <<= 1;
|
|
+ }
|
|
+
|
|
+ dev_warn(smc->ldev, "%s... timeout\n", __func__);
|
|
+ return -EIO;
|
|
+}
|
|
+
|
|
+static int iomem_read_smc(struct applesmc_device *smc, u8 cmd, const char *key,
|
|
+ u8 *buffer, u8 len)
|
|
+{
|
|
+ u8 err, remote_len;
|
|
+ u32 key_int = *((u32 *) key);
|
|
+
|
|
+ iomem_clear_status(smc);
|
|
+ iowrite32(key_int, smc->iomem_base + APPLESMC_IOMEM_KEY_NAME);
|
|
+ iowrite32(0, smc->iomem_base + APPLESMC_IOMEM_KEY_SMC_ID);
|
|
+ iowrite32(cmd, smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
|
|
+
|
|
+ if (iomem_wait_read(smc))
|
|
+ return -EIO;
|
|
+
|
|
+ err = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
|
|
+ if (err != 0) {
|
|
+ dev_warn(smc->ldev, "read_smc_mmio(%x %8x/%.4s) failed: %u\n",
|
|
+ cmd, key_int, key, err);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if (cmd == APPLESMC_READ_CMD) {
|
|
+ remote_len = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_DATA_LEN);
|
|
+ if (remote_len != len) {
|
|
+ dev_warn(smc->ldev,
|
|
+ "read_smc_mmio(%x %8x/%.4s) failed: buffer length mismatch (remote = %u, requested = %u)\n",
|
|
+ cmd, key_int, key, remote_len, len);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else {
|
|
+ remote_len = len;
|
|
+ }
|
|
+
|
|
+ memcpy_fromio(buffer, smc->iomem_base + APPLESMC_IOMEM_KEY_DATA,
|
|
+ remote_len);
|
|
+
|
|
+ dev_dbg(smc->ldev, "read_smc_mmio(%x %8x/%.4s): buflen=%u reslen=%u\n",
|
|
+ cmd, key_int, key, len, remote_len);
|
|
+ print_hex_dump_bytes("read_smc_mmio(): ", DUMP_PREFIX_NONE, buffer, remote_len);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iomem_get_smc_key_type(struct applesmc_device *smc, const char *key,
|
|
+ struct applesmc_entry *e)
|
|
+{
|
|
+ u8 err;
|
|
+ u8 cmd = APPLESMC_GET_KEY_TYPE_CMD;
|
|
+ u32 key_int = *((u32 *) key);
|
|
+
|
|
+ iomem_clear_status(smc);
|
|
+ iowrite32(key_int, smc->iomem_base + APPLESMC_IOMEM_KEY_NAME);
|
|
+ iowrite32(0, smc->iomem_base + APPLESMC_IOMEM_KEY_SMC_ID);
|
|
+ iowrite32(cmd, smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
|
|
+
|
|
+ if (iomem_wait_read(smc))
|
|
+ return -EIO;
|
|
+
|
|
+ err = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
|
|
+ if (err != 0) {
|
|
+ dev_warn(smc->ldev, "get_smc_key_type_mmio(%.4s) failed: %u\n", key, err);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ e->len = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_TYPE_DATA_LEN);
|
|
+ *((uint32_t *) e->type) = ioread32(
|
|
+ smc->iomem_base + APPLESMC_IOMEM_KEY_TYPE_CODE);
|
|
+ e->flags = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_TYPE_FLAGS);
|
|
+
|
|
+ dev_dbg(smc->ldev, "get_smc_key_type_mmio(%.4s): len=%u type=%.4s flags=%x\n",
|
|
+ key, e->len, e->type, e->flags);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int iomem_write_smc(struct applesmc_device *smc, u8 cmd, const char *key,
|
|
+ const u8 *buffer, u8 len)
|
|
+{
|
|
+ u8 err;
|
|
+ u32 key_int = *((u32 *) key);
|
|
+
|
|
+ iomem_clear_status(smc);
|
|
+ iowrite32(key_int, smc->iomem_base + APPLESMC_IOMEM_KEY_NAME);
|
|
+ memcpy_toio(smc->iomem_base + APPLESMC_IOMEM_KEY_DATA, buffer, len);
|
|
+ iowrite32(len, smc->iomem_base + APPLESMC_IOMEM_KEY_DATA_LEN);
|
|
+ iowrite32(0, smc->iomem_base + APPLESMC_IOMEM_KEY_SMC_ID);
|
|
+ iowrite32(cmd, smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
|
|
+
|
|
+ if (iomem_wait_read(smc))
|
|
+ return -EIO;
|
|
+
|
|
+ err = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_CMD);
|
|
+ if (err != 0) {
|
|
+ dev_warn(smc->ldev, "write_smc_mmio(%x %.4s) failed: %u\n", cmd, key, err);
|
|
+ print_hex_dump_bytes("write_smc_mmio(): ", DUMP_PREFIX_NONE, buffer, len);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ dev_dbg(smc->ldev, "write_smc_mmio(%x %.4s): buflen=%u\n", cmd, key, len);
|
|
+ print_hex_dump_bytes("write_smc_mmio(): ", DUMP_PREFIX_NONE, buffer, len);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
static int read_smc(struct applesmc_device *smc, const char *key,
|
|
u8 *buffer, u8 len)
|
|
{
|
|
- return port_read_smc(smc, APPLESMC_READ_CMD, key, buffer, len);
|
|
+ if (smc->iomem_base_set)
|
|
+ return iomem_read_smc(smc, APPLESMC_READ_CMD, key, buffer, len);
|
|
+ else
|
|
+ return port_read_smc(smc, APPLESMC_READ_CMD, key, buffer, len);
|
|
}
|
|
|
|
static int write_smc(struct applesmc_device *smc, const char *key,
|
|
const u8 *buffer, u8 len)
|
|
{
|
|
- return port_write_smc(smc, APPLESMC_WRITE_CMD, key, buffer, len);
|
|
+ if (smc->iomem_base_set)
|
|
+ return iomem_write_smc(smc, APPLESMC_WRITE_CMD, key, buffer, len);
|
|
+ else
|
|
+ return port_write_smc(smc, APPLESMC_WRITE_CMD, key, buffer, len);
|
|
}
|
|
|
|
static int get_smc_key_by_index(struct applesmc_device *smc,
|
|
@@ -365,14 +520,21 @@ static int get_smc_key_by_index(struct applesmc_device *smc,
|
|
__be32 be;
|
|
|
|
be = cpu_to_be32(index);
|
|
- return port_read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD,
|
|
- (const char *) &be, (u8 *) key, 4);
|
|
+ if (smc->iomem_base_set)
|
|
+ return iomem_read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD,
|
|
+ (const char *) &be, (u8 *) key, 4);
|
|
+ else
|
|
+ return port_read_smc(smc, APPLESMC_GET_KEY_BY_INDEX_CMD,
|
|
+ (const char *) &be, (u8 *) key, 4);
|
|
}
|
|
|
|
static int get_smc_key_info(struct applesmc_device *smc, const char *key,
|
|
struct applesmc_entry *info)
|
|
{
|
|
- return port_get_smc_key_info(smc, key, info);
|
|
+ if (smc->iomem_base_set)
|
|
+ return iomem_get_smc_key_type(smc, key, info);
|
|
+ else
|
|
+ return port_get_smc_key_info(smc, key, info);
|
|
}
|
|
|
|
static int read_register_count(struct applesmc_device *smc,
|
|
@@ -746,6 +908,7 @@ static int applesmc_add(struct acpi_device *dev)
|
|
if (!smc)
|
|
return -ENOMEM;
|
|
smc->dev = dev;
|
|
+ smc->ldev = &dev->dev;
|
|
mutex_init(&smc->reg.mutex);
|
|
|
|
dev_set_drvdata(&dev->dev, smc);
|
|
@@ -807,6 +970,20 @@ static acpi_status applesmc_walk_resources(struct acpi_resource *res,
|
|
}
|
|
return AE_OK;
|
|
|
|
+ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
|
|
+ if (!smc->iomem_base_set) {
|
|
+ if (res->data.fixed_memory32.address_length <
|
|
+ APPLESMC_IOMEM_MIN_SIZE) {
|
|
+ dev_warn(smc->ldev, "found iomem but it's too small: %u\n",
|
|
+ res->data.fixed_memory32.address_length);
|
|
+ return AE_OK;
|
|
+ }
|
|
+ smc->iomem_base_addr = res->data.fixed_memory32.address;
|
|
+ smc->iomem_base_size = res->data.fixed_memory32.address_length;
|
|
+ smc->iomem_base_set = true;
|
|
+ }
|
|
+ return AE_OK;
|
|
+
|
|
case ACPI_RESOURCE_TYPE_END_TAG:
|
|
if (smc->port_base_set)
|
|
return AE_OK;
|
|
@@ -818,6 +995,8 @@ static acpi_status applesmc_walk_resources(struct acpi_resource *res,
|
|
}
|
|
}
|
|
|
|
+static int applesmc_try_enable_iomem(struct applesmc_device *smc);
|
|
+
|
|
static int applesmc_init_resources(struct applesmc_device *smc)
|
|
{
|
|
int ret;
|
|
@@ -830,11 +1009,57 @@ static int applesmc_init_resources(struct applesmc_device *smc)
|
|
if (!request_region(smc->port_base, APPLESMC_NR_PORTS, "applesmc"))
|
|
return -ENXIO;
|
|
|
|
+ if (smc->iomem_base_set) {
|
|
+ if (applesmc_try_enable_iomem(smc))
|
|
+ smc->iomem_base_set = false;
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
+static int applesmc_try_enable_iomem(struct applesmc_device *smc)
|
|
+{
|
|
+ u8 test_val, ldkn_version;
|
|
+
|
|
+ dev_dbg(smc->ldev, "Trying to enable iomem based communication\n");
|
|
+ smc->iomem_base = ioremap(smc->iomem_base_addr, smc->iomem_base_size);
|
|
+ if (!smc->iomem_base)
|
|
+ goto out;
|
|
+
|
|
+ /* Apple's driver does this check for some reason */
|
|
+ test_val = ioread8(smc->iomem_base + APPLESMC_IOMEM_KEY_STATUS);
|
|
+ if (test_val == 0xff) {
|
|
+ dev_warn(smc->ldev,
|
|
+ "iomem enable failed: initial status is 0xff (is %x)\n",
|
|
+ test_val);
|
|
+ goto out_iomem;
|
|
+ }
|
|
+
|
|
+ if (read_smc(smc, "LDKN", &ldkn_version, 1)) {
|
|
+ dev_warn(smc->ldev, "iomem enable failed: ldkn read failed\n");
|
|
+ goto out_iomem;
|
|
+ }
|
|
+
|
|
+ if (ldkn_version < 2) {
|
|
+ dev_warn(smc->ldev,
|
|
+ "iomem enable failed: ldkn version %u is less than minimum (2)\n",
|
|
+ ldkn_version);
|
|
+ goto out_iomem;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out_iomem:
|
|
+ iounmap(smc->iomem_base);
|
|
+
|
|
+out:
|
|
+ return -ENXIO;
|
|
+}
|
|
+
|
|
static void applesmc_free_resources(struct applesmc_device *smc)
|
|
{
|
|
+ if (smc->iomem_base_set)
|
|
+ iounmap(smc->iomem_base);
|
|
release_region(smc->port_base, APPLESMC_NR_PORTS);
|
|
}
|
|
|
|
--
|
|
Armbian
|
|
|