Add basic MMIO support to AppleSMC for T2 Macs, enabling it only when supported. This replaces the legacy port-based method for key reading, writing, and metadata operations (retrieving keys by index and obtaining key information) Signed-off-by: Subu Dwevedi <messigoatcr7nop@xxxxxxxxx> --- drivers/hwmon/applesmc.c | 223 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 213 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index fc6d6a9053ce..1be4a4026a6e 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -17,6 +17,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/acpi.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> @@ -142,7 +143,9 @@ static struct platform_device *pdev; static s16 rest_x; static s16 rest_y; static u8 backlight_state[2]; - +static u8 *__iomem mmio_base; +static bool is_mmio; +static u32 mmio_base_addr, mmio_base_size; static struct device *hwmon_dev; static struct input_dev *applesmc_idev; @@ -245,7 +248,108 @@ static int send_argument(const char *key) return 0; } -static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) +/* + * MMIO Impliementation + */ + +static void clearArbitration(void) +{ + if (ioread8(mmio_base + 0x4005)) + return iowrite8(0, mmio_base + 0x4005); +} +static int waitForKeyDone(void) +{ + int i = 1000; //millisecounds + u8 status; + + while (i) { + msleep(1); + i--; + + status = ioread8(mmio_base + 0x4005); + if (status & 0x20) + return 0; + } + + return -EIO; +} +static int mmio_read_smc(u8 cmd, const char *key, u8 *buffer, u64 len) +{ + u8 i, u = 0; + + clearArbitration(); + iowrite32(*((u32 *)key), mmio_base + 0x78); + iowrite8(0x15, mmio_base + 0x7E); + iowrite8(cmd, mmio_base + 0x7F); + + if (waitForKeyDone()) + return -EIO; + + i = ioread8(mmio_base + 0x7F); + if (i) + return -EIO; + if (cmd == APPLESMC_READ_CMD) { + i = ioread8(mmio_base + 0x7D); + if (i > len || !i) + return -EIO; + + while (u < i) { + if ((i - u) < 4) { + if ((i - u) < 2) { + buffer[u] = ioread8(mmio_base + u); + u += 1; + } else { + *(u16 *)&buffer[u] = ioread16(mmio_base + u); + u += 2; + } + } else { + *(u32 *)&buffer[u] = ioread32(mmio_base + u); + u += 4; + } + } + } else + memcpy_fromio(buffer, mmio_base + 0x0, len); + + return 0; +} +static int mmio_write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) +{ + u8 i = 0; + + clearArbitration(); + iowrite32(*((u32 *)key), mmio_base + 0x78); + while (i < len) { + if (len - i < 4) { + if (len - i < 2) { + iowrite8(buffer[i], mmio_base + i); + i += 1; + } else { + iowrite16(*(u16 *)&buffer[i], mmio_base + i); + i += 2; + } + } else { + iowrite32(*(u32 *)&buffer[i], mmio_base + i); + i += 4; + } + } + iowrite8(len, mmio_base + 0x7D); + iowrite8(0x15, mmio_base + 0x7E); + iowrite8(cmd, mmio_base + 0x7F); + if (waitForKeyDone()) + return -EIO; + + i = ioread8(mmio_base + 0x7F); + if (i) + return -EIO; + + return 0; +} + +/* + * Port Based IO implementation + * + */ +static int port_read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) { u8 status, data = 0; int i; @@ -289,7 +393,7 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) return wait_status(0, SMC_STATUS_BUSY); } -static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) +static int port_write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) { int i; int ret; @@ -317,7 +421,23 @@ static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) return wait_status(0, SMC_STATUS_BUSY); } +static int write_smc(u8 cmd, const char *key, const u8 *buffer, + u8 len) +{ + if (is_mmio) + return mmio_write_smc(cmd, key, buffer, len); + + return port_write_smc(cmd, key, buffer, len); +} +static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) +{ + if (is_mmio) + return mmio_read_smc(cmd, key, buffer, len); + + + return port_read_smc(cmd, key, buffer, len); +} static int read_register_count(unsigned int *count) { __be32 be; @@ -379,18 +499,39 @@ static const struct applesmc_entry *applesmc_get_entry_by_index(int index) if (cache->valid) goto out; + be = cpu_to_be32(index); - ret = read_smc(APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4); - if (ret) - goto out; - ret = read_smc(APPLESMC_GET_KEY_TYPE_CMD, key, info, 6); + ret = read_smc(APPLESMC_GET_KEY_BY_INDEX_CMD, (const char *) &be, (u8 *) key, 4); if (ret) goto out; + if (is_mmio) { + clearArbitration(); + iowrite32(*((u32 *)key), mmio_base + 0x78); + iowrite8(0, mmio_base + 0x7E); + iowrite8(APPLESMC_GET_KEY_TYPE_CMD, mmio_base + 0x7F); + ret = waitForKeyDone(); + if (ret) + goto out; + + ret = ioread8(mmio_base + 0x7F); + if (ret) + goto out; + + *(u32 *)cache->type = ioread32(mmio_base + 0x7F); + cache->len = ioread8(mmio_base + 5); + cache->flags = ioread8(mmio_base + 6); + + } else { + ret = read_smc(APPLESMC_GET_KEY_TYPE_CMD, key, info, 6); + if (ret) + goto out; + + cache->len = info[0]; + memcpy(cache->type, &info[1], 4); + cache->flags = info[5]; + } memcpy(cache->key, key, 4); - cache->len = info[0]; - memcpy(cache->type, &info[1], 4); - cache->flags = info[5]; cache->valid = true; out: @@ -558,7 +699,64 @@ static int applesmc_init_index(struct applesmc_registers *s) return 0; } +/* + * applesmc_init_mmio_try - Try to initialize MMIO + */ +static int applesmc_init_mmio_try(void) +{ + u8 ldkn_version; + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_device *adev; + struct acpi_resource *res; + + adev = acpi_dev_get_first_match_dev("APP0001", NULL, -1); + if (!adev) + return -ENXIO; + + status = acpi_get_current_resources(adev->handle, &buffer); + if (ACPI_FAILURE(status)) + return -ENXIO; + + res = buffer.pointer; + while (res->type != ACPI_RESOURCE_TYPE_END_TAG) { + if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { + if (res->data.fixed_memory32.address_length < 0x4006) + return -ENXIO; + + mmio_base_addr = res->data.fixed_memory32.address; + mmio_base_size = res->data.fixed_memory32.address_length; + is_mmio = true; + break; + } + res = ACPI_NEXT_RESOURCE(res); + } + kfree(buffer.pointer); + acpi_dev_put(adev); + + if (!is_mmio) + return -ENXIO; + + mmio_base = ioremap(mmio_base_addr, mmio_base_size); + + if (!mmio_base) + return -ENXIO; + if (ioread8(mmio_base + 0x4005) == 0xFF) + goto out; + + if (read_smc(APPLESMC_READ_CMD, "LDKN", &ldkn_version, 1)) + goto out; + + if (ldkn_version < 2) + goto out; + + return 0; +out: + pr_warn("cannot enable MMIO will use PMIO\n"); + iounmap(mmio_base); + return -ENXIO; +} /* * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent. */ @@ -1321,6 +1519,8 @@ static int __init applesmc_init(void) ret = -ENXIO; goto out; } + if (applesmc_init_mmio_try()) + is_mmio = false; ret = platform_driver_register(&applesmc_driver); if (ret) @@ -1407,6 +1607,9 @@ static void __exit applesmc_exit(void) applesmc_destroy_smcreg(); platform_device_unregister(pdev); platform_driver_unregister(&applesmc_driver); + if (is_mmio) + iounmap(mmio_base); + release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS); } -- 2.43.0