From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A735E320CD9 for ; Mon, 25 May 2026 08:18:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.43 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779697097; cv=none; b=IcMOmnPixaRDPB4czzfjyMCOU3feqGAUc1E1+IphkU3iGWQ0zbqMyNpLo6Ukq5yKr/PUHj4PSSNhSwSqYwvyrciTMnz/SsfRgTuWIAfveu0tV/tIbLfqYsqZGPoZ1L3IRPKaGTZH3PsYQEJoKj+CNuoSA4oOD8f+DM9VS1t05bo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779697097; c=relaxed/simple; bh=re8ddQP05V6O8wWbsYDeqIM9uwIWJUDRjwnvjw2ZmKU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=Fad4pQsVpeHWzSV0FtVFo4t3yk3i/sDxpGoyBFSVjiCXHgGv6pgWqGYCQVZcNc6ifFLb9tnXJKPER0lRc6tK7/8d7j5m66I4Rw9A/m/0TFPxK3hxh5ZlEHaeeEAHAfzG8VcT+FEXv23rQk4n8vuA1uhO0rbnGkKCLU1pdC2NEP8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gu+C0JkT; arc=none smtp.client-ip=209.85.216.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gu+C0JkT" Received: by mail-pj1-f43.google.com with SMTP id 98e67ed59e1d1-36ac67f489aso520978a91.0 for ; Mon, 25 May 2026 01:18:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779697095; x=1780301895; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=j60RMl9UvWmQgF6EgLZrjWrvexkwfiUj1muXHEyjyl0=; b=gu+C0JkTSUOULLMfIR+Sprux787NG4/TM82lGunW2QCCAAV/5xiEx+rNanjgn6wAJd viyNNGUOF2mKdShuPZdIWdo4Q38/+JZ2jlvJ21AIb6fAWIrrGhpihPZoL3sLDFaloInC OyyYkW+M7nYj7t94nhSZxGtf6wktLD5Ma9t854P9cpjE6NdxoRSvHHDf3bJz/frS7ZKt 6iZniks9S7qH+JkTzgeVeq7uYN5DiIo7pqf4Ht9E+44OnEkuM74mrCjYSaCIKYHU2J5H QObIXKBo/BQPTngJf9VK7Ljfgd61wmR97tDnVssPGi/aciw+Eo5XsLrDaTpEQkyNiQvV FmgA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779697095; x=1780301895; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=j60RMl9UvWmQgF6EgLZrjWrvexkwfiUj1muXHEyjyl0=; b=B/Zbh+iW2ICRy9fOACG0VmWTD/QHpm/snaP6ZJkaaRAPTaeGG6CQKfTcq926ojlzzo 7froBbMn8ifxxpssyzN038FI281wUw7UO6fpfpBjCOny8SXOSVBV+iFAaVaSmhhImvBb duA+S9WQEQG57jBYfnIqn0JENqqtkOmmyysO08ue2F8eUSsXQHM0dUUJHz0luyB+58gt ReyTTgV+tkQbNogTD1CxrbwbB0cL2l0Noj9cTxW+61nfh+Y6SPBDS8GvlBYqIypX4lRb RJ+oxKyKpFjVCQexJ24/oec40c9T91f3kpPbjUZNCWGcS2FmzSLYxasngGUKbb4nOSTb mddg== X-Forwarded-Encrypted: i=1; AFNElJ+hz+epXpkgLPIrOvXtbBoB3y0Kwrpwl8+IwS39E6xCvZ0oFLLrS1j639GhuZVOGcNvWR1cgP9dcRjEWPk=@vger.kernel.org X-Gm-Message-State: AOJu0Yw3Al2hwjizEhCHoVVXLco2PkO4DiaL+OqvX7azNauAlocZu6KU qIpOaLSQxqH0Prez0ijvIbt36RPNuQjGZLHUEEMW2kUYt8qRTbjQfU4IbC6KZg== X-Gm-Gg: Acq92OGU/wWNANtztTboDQLkKs7NI/g9QmAO09iZoYdL8cHC8lvG4KqK/sHqqcce7Vv CveHH2Qr8VRscLs+OkhOZK3S+KrbMFuqpw39NusZU/bz9UfMhjN7L3FDNOIsJFAes8taxNbIvnw 5N8MVsYtjVHytrJXe0kmmfNyWagxV/yilPmsamECpI5JgOk1zilDOyWYFSnw8GhuQ9AlSsfcQRF i0ZEag+cXE5zqJSJdTj0Sg5VECeEYzZ4KtLS+NjGPjmrgwDXZE+dj0S/oROviVI4rgqrgQabPHp FlyGyj1uPG1IewpFWfOPWmGOBtPoMSCqJRy5bu2rtn9xRKkHmo1vzXc8xbQ9f7KdnUgKalXGeVN Oe6fyF0Ecxp1xl5EbKQIuI5iMSPx2MkCc9K1o3V/XCX8QvQFAyi2GO/rEmNNNaehp+Ri9TJbtRT lYhXUi3xS2PaKYqcRYOo7qOCwt/jsPVWrxFMayc2yemJxlU9ZHiU3ExGRVsmiNVxEAyg== X-Received: by 2002:a17:90b:48c7:b0:368:3fa8:dbfa with SMTP id 98e67ed59e1d1-36a6bcba085mr8699494a91.7.1779697094922; Mon, 25 May 2026 01:18:14 -0700 (PDT) Received: from hcdev-d520mt2.. (60-250-196-139.hinet-ip.hinet.net. [60.250.196.139]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-36a721c7cf9sm8964102a91.10.2026.05.25.01.18.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 25 May 2026 01:18:14 -0700 (PDT) From: a0282524688@gmail.com To: Lee Jones , Ming Yu Cc: Ming Yu , linux-kernel@vger.kernel.org Subject: [PATCH v5 7/7] mfd: nct6694: Add Host Interface (HIF) eSPI transport driver Date: Mon, 25 May 2026 16:17:36 +0800 Message-Id: <20260525081736.2904310-8-a0282524688@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260525081736.2904310-1-a0282524688@gmail.com> References: <20260525081736.2904310-1-a0282524688@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Ming Yu Add support for the Host Interface (HIF) transport via eSPI for the Nuvoton NCT6694 peripheral expander. This transport driver initializes the Super-I/O to configure the device's shared memory base address and SIRQ. It provides the required I/O callbacks (read_msg/write_msg) utilizing memory-mapped I/O and regmap for command transactions. The initialized device config is then passed to the MFD core (nct6694_core_probe) for sub-device registration and IRQ domain setup. Signed-off-by: Ming Yu --- Changes since version 4: - Split from the monolithic v4 patch. - Adapted to re-use the newly introduced nct6694_core_probe() and abstracted I/O APIs. Changes since version 3: - Remove redundant module type macro definitions from sub-device drivers that are now provided by the shared header , fixing -Wmacro-redefined warnings. Changes since version 2: - Restore per-device IDA and mfd_add_hotplug_devices()/PLATFORM_DEVID_AUTO to avoid child device ID conflicts with multiple NCT6694 chips. - Validate irq_find_mapping() return value before dispatching IRQs. - Check superio_enter() return value in nct6694_irq_init(). Changes since version 1: - Drop function pointers from Super-I/O access and use static inline helpers with proper types. drivers/mfd/Kconfig | 16 ++ drivers/mfd/Makefile | 1 + drivers/mfd/nct6694-hif.c | 529 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 546 insertions(+) create mode 100644 drivers/mfd/nct6694-hif.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b5b30371da59..a67c73f1fdd4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1174,6 +1174,22 @@ config MFD_NCT6694 It is selected automatically by the transport interface drivers. +config MFD_NCT6694_HIF + tristate "Nuvoton NCT6694 HIF (eSPI) interface support" + depends on HAS_IOPORT && ACPI + select MFD_NCT6694 + select REGMAP_MMIO + help + This enables support for the Nuvoton NCT6694 peripheral expander + connected via the Host Interface (HIF) using eSPI transport. + + The transport driver uses Super-I/O mapping and shared memory to + communicate with the NCT6694 firmware. Enable this option if you + are using the NCT6694 over an eSPI interface on an ACPI platform. + + To compile this driver as a module, choose M here: the module + will be called nct6694-hif. + config MFD_NCT6694_USB tristate "Nuvoton NCT6694 USB interface support" select MFD_NCT6694 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 668a69dc66a7..a0ce7d79c4f2 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -125,6 +125,7 @@ obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o obj-$(CONFIG_MFD_PF1550) += pf1550.o obj-$(CONFIG_MFD_NCT6694) += nct6694-core.o +obj-$(CONFIG_MFD_NCT6694_HIF) += nct6694-hif.o obj-$(CONFIG_MFD_NCT6694_USB) += nct6694-usb.o obj-$(CONFIG_MFD_CORE) += mfd-core.o diff --git a/drivers/mfd/nct6694-hif.c b/drivers/mfd/nct6694-hif.c new file mode 100644 index 000000000000..0a8273f4b1a1 --- /dev/null +++ b/drivers/mfd/nct6694-hif.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Nuvoton Technology Corp. + * + * Nuvoton NCT6694 host-interface (eSPI) transport driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "nct6694-hif" + +#define NCT6694_POLL_INTERVAL_US 10 +#define NCT6694_POLL_TIMEOUT_US 10000 + +/* + * Super-I/O registers + */ +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_LD_SHM 0x0F /* Logical device shared memory control */ + +#define SIO_REG_SHM_ENABLE 0x30 /* Enable shared memory */ +#define SIO_REG_SHM_BASE_ADDR 0x60 /* Shared memory base address (2 bytes) */ +#define SIO_REG_SHM_IRQ_NR 0x70 /* Shared memory interrupt number */ + +#define SIO_REG_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ +#define SIO_REG_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SIO_NCT6694B_ID 0xD029 +#define SIO_NCT6694D_ID 0x5832 + +/* + * Super-I/O Shared Memory Logical Device registers + */ +#define NCT6694_SHM_COFS_STS 0x2E +#define NCT6694_SHM_COFS_STS_COFS4W BIT(7) + +#define NCT6694_SHM_COFS_CTL2 0x3B +#define NCT6694_SHM_COFS_CTL2_COFS4W_IE BIT(3) + +#define NCT6694_SHM_INTR_STATUS 0x9C /* Interrupt status register (4 bytes) */ + +enum nct6694_chips { + NCT6694B = 0, + NCT6694D, +}; + +struct __packed nct6694_msg { + struct nct6694_cmd_header cmd_header; + struct nct6694_response_header response_header; + unsigned char data[]; +}; + +struct nct6694_sio_data { + enum nct6694_chips chip; + int sioreg; /* Super-I/O index port */ +}; + +struct nct6694_hif_data { + struct regmap *regmap; + struct mutex msg_lock; + struct nct6694_sio_data *sio_data; + void __iomem *msg_base; + unsigned int shm_base; +}; + +static const char * const nct6694_chip_names[] = { + [NCT6694B] = "NCT6694B", + [NCT6694D] = "NCT6694D", +}; + +/* + * Super-I/O functions. + */ +static inline int superio_enter(struct nct6694_sio_data *sio_data) +{ + int ioreg = sio_data->sioreg; + + /* + * Try to reserve and for exclusive access. + */ + if (!request_muxed_region(ioreg, 2, DRVNAME)) + return -EBUSY; + + outb(SIO_REG_UNLOCK_KEY, ioreg); + outb(SIO_REG_UNLOCK_KEY, ioreg); + + return 0; +} + +static inline void superio_exit(struct nct6694_sio_data *sio_data) +{ + int ioreg = sio_data->sioreg; + + outb(SIO_REG_LOCK_KEY, ioreg); + + release_region(ioreg, 2); +} + +static inline void superio_select(struct nct6694_sio_data *sio_data, int ld) +{ + int ioreg = sio_data->sioreg; + + outb(SIO_REG_LDSEL, ioreg); + outb(ld, ioreg + 1); +} + +static inline int superio_inb(struct nct6694_sio_data *sio_data, int reg) +{ + int ioreg = sio_data->sioreg; + + outb(reg, ioreg); + return inb(ioreg + 1); +} + +static inline int superio_inw(struct nct6694_sio_data *sio_data, int reg) +{ + int ioreg = sio_data->sioreg; + int val; + + outb(reg++, ioreg); + val = inb(ioreg + 1) << 8; + outb(reg, ioreg); + val |= inb(ioreg + 1); + + return val; +} + +static inline void superio_outb(struct nct6694_sio_data *sio_data, int reg, u8 val) +{ + int ioreg = sio_data->sioreg; + + outb(reg, ioreg); + outb(val, ioreg + 1); +} + +static int nct6694_sio_find(struct nct6694_sio_data *sio_data, u8 sioreg) +{ + int ret; + u16 devid; + + sio_data->sioreg = sioreg; + + ret = superio_enter(sio_data); + if (ret) + return ret; + + /* Check Chip ID */ + devid = superio_inw(sio_data, SIO_REG_DEVID); + switch (devid) { + case SIO_NCT6694B_ID: + sio_data->chip = NCT6694B; + break; + case SIO_NCT6694D_ID: + sio_data->chip = NCT6694D; + break; + default: + pr_debug("Unsupported device ID: 0x%04x\n", devid); + goto err; + } + + pr_info("Found %s at %#x\n", nct6694_chip_names[sio_data->chip], sio_data->sioreg); + + superio_exit(sio_data); + + return 0; + +err: + superio_exit(sio_data); + return -ENODEV; +} + +static const struct mfd_cell nct6694_hif_devs[] = { + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + MFD_CELL_NAME("nct6694-gpio"), + + MFD_CELL_NAME("nct6694-i2c"), + MFD_CELL_NAME("nct6694-i2c"), + MFD_CELL_NAME("nct6694-i2c"), + MFD_CELL_NAME("nct6694-i2c"), + MFD_CELL_NAME("nct6694-i2c"), + MFD_CELL_NAME("nct6694-i2c"), + + MFD_CELL_NAME("nct6694-canfd"), + MFD_CELL_NAME("nct6694-canfd"), +}; + +static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status) +{ + switch (err_status) { + case NCT6694_NO_ERROR: + return 0; + case NCT6694_NOT_SUPPORT_ERROR: + dev_err(nct6694->dev, "Command is not supported!\n"); + break; + case NCT6694_NO_RESPONSE_ERROR: + dev_warn(nct6694->dev, "Command received no response!\n"); + break; + case NCT6694_TIMEOUT_ERROR: + dev_warn(nct6694->dev, "Command timed out!\n"); + break; + case NCT6694_PENDING: + dev_err(nct6694->dev, "Command is pending!\n"); + break; + default: + return -EINVAL; + } + + return -EIO; +} + +static int nct6694_xfer_msg(struct nct6694 *nct6694, + const struct nct6694_cmd_header *cmd_hd, + u8 hctrl, void *buf) +{ + struct nct6694_hif_data *hdata = nct6694->priv; + void __iomem *hdr = hdata->msg_base + offsetof(struct nct6694_msg, cmd_header); + struct nct6694_cmd_header cmd = *cmd_hd; + struct nct6694_response_header resp; + u16 len = le16_to_cpu(cmd.len); + u8 status; + int ret; + + guard(mutex)(&hdata->msg_lock); + + /* Wait until the previous command is completed */ + ret = readb_poll_timeout(hdr + offsetof(struct nct6694_cmd_header, hctrl), + status, status == 0, NCT6694_POLL_INTERVAL_US, + NCT6694_POLL_TIMEOUT_US); + if (ret) + return ret; + + /* + * Write cmd header fields, but skip hctrl — writing to it triggers + * firmware command processing and must be deferred until data is ready. + */ + memcpy_toio(hdr, &cmd, offsetof(struct nct6694_cmd_header, hctrl)); + memcpy_toio(hdr + offsetof(struct nct6694_cmd_header, rsv2), &cmd.rsv2, + sizeof(cmd) - offsetof(struct nct6694_cmd_header, rsv2)); + + if (hctrl == NCT6694_HCTRL_SET && len) + memcpy_toio(hdata->msg_base + offsetof(struct nct6694_msg, data), + buf, len); + + /* Write hctrl last to trigger command processing */ + writeb(hctrl, hdr + offsetof(struct nct6694_cmd_header, hctrl)); + + ret = readb_poll_timeout(hdr + offsetof(struct nct6694_cmd_header, hctrl), + status, status == 0, NCT6694_POLL_INTERVAL_US, + NCT6694_POLL_TIMEOUT_US); + if (ret) + return ret; + + memcpy_fromio(&resp, hdata->msg_base + offsetof(struct nct6694_msg, response_header), + sizeof(resp)); + + ret = nct6694_response_err_handling(nct6694, resp.sts); + if (ret) + return ret; + + if (le16_to_cpu(resp.len)) + memcpy_fromio(buf, hdata->msg_base + offsetof(struct nct6694_msg, data), + min(len, le16_to_cpu(resp.len))); + + return 0; +} + +/** + * nct6694_hif_read_msg() - Send a command and read response data via HIF + * @nct6694: NCT6694 device data + * @cmd_hd: command header + * @buf: buffer to store response data + * + * Return: 0 on success or negative errno on failure. + */ +static int nct6694_hif_read_msg(struct nct6694 *nct6694, + const struct nct6694_cmd_header *cmd_hd, + void *buf) +{ + struct nct6694_hif_data *hdata = nct6694->priv; + + if (cmd_hd->mod == NCT6694_RPT_MOD) + return regmap_bulk_read(hdata->regmap, + le16_to_cpu(cmd_hd->offset), + buf, le16_to_cpu(cmd_hd->len)); + return nct6694_xfer_msg(nct6694, cmd_hd, NCT6694_HCTRL_GET, buf); +} + +/** + * nct6694_hif_write_msg() - Send a command with data payload via HIF + * @nct6694: NCT6694 device data + * @cmd_hd: command header + * @buf: buffer containing data to send + * + * Return: 0 on success or negative errno on failure. + */ +static int nct6694_hif_write_msg(struct nct6694 *nct6694, + const struct nct6694_cmd_header *cmd_hd, + void *buf) +{ + struct nct6694_hif_data *hdata = nct6694->priv; + + if (cmd_hd->mod == NCT6694_RPT_MOD) + return regmap_bulk_write(hdata->regmap, + le16_to_cpu(cmd_hd->offset), + buf, le16_to_cpu(cmd_hd->len)); + return nct6694_xfer_msg(nct6694, cmd_hd, NCT6694_HCTRL_SET, buf); +} + +static const struct regmap_config nct6694_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_stride = 1, +}; + +static irqreturn_t nct6694_irq_handler(int irq, void *data) +{ + struct nct6694 *nct6694 = data; + struct nct6694_hif_data *hdata = nct6694->priv; + u8 reg_data[4]; + u32 intr_status; + int ret; + + /* Check interrupt status is set */ + if (!(inb(hdata->shm_base + NCT6694_SHM_COFS_STS) & NCT6694_SHM_COFS_STS_COFS4W)) + return IRQ_NONE; + + /* Clear interrupt status */ + outb(NCT6694_SHM_COFS_STS_COFS4W, hdata->shm_base + NCT6694_SHM_COFS_STS); + + ret = regmap_bulk_read(hdata->regmap, NCT6694_SHM_INTR_STATUS, + reg_data, ARRAY_SIZE(reg_data)); + if (ret) + return IRQ_NONE; + + intr_status = get_unaligned_le32(reg_data); + + while (intr_status) { + int hwirq = __ffs(intr_status); + unsigned int virq = irq_find_mapping(nct6694->domain, hwirq); + + if (virq) + generic_handle_irq_safe(virq); + intr_status &= ~BIT(hwirq); + } + + return IRQ_HANDLED; +} + +static void nct6694_irq_release(void *data) +{ + struct nct6694 *nct6694 = data; + struct nct6694_hif_data *hdata = nct6694->priv; + unsigned char cofs_ctl2; + + /* Disable SIRQ interrupt */ + cofs_ctl2 = inb(hdata->shm_base + NCT6694_SHM_COFS_CTL2); + cofs_ctl2 &= ~NCT6694_SHM_COFS_CTL2_COFS4W_IE; + outb(cofs_ctl2, hdata->shm_base + NCT6694_SHM_COFS_CTL2); +} + +static int nct6694_irq_init(struct nct6694 *nct6694, int irq) +{ + struct nct6694_hif_data *hdata = nct6694->priv; + struct nct6694_sio_data *sio_data = hdata->sio_data; + unsigned char cofs_ctl2; + int ret; + + /* Set SIRQ number */ + ret = superio_enter(sio_data); + if (ret) + return ret; + + superio_select(sio_data, SIO_REG_LD_SHM); + + if (!superio_inb(sio_data, SIO_REG_SHM_ENABLE)) { + superio_exit(sio_data); + return -EIO; + } + + hdata->shm_base = superio_inw(sio_data, SIO_REG_SHM_BASE_ADDR); + + superio_outb(sio_data, SIO_REG_SHM_IRQ_NR, irq); + + superio_exit(sio_data); + + /* Enable SIRQ interrupt */ + cofs_ctl2 = inb(hdata->shm_base + NCT6694_SHM_COFS_CTL2); + cofs_ctl2 |= NCT6694_SHM_COFS_CTL2_COFS4W_IE; + outb(cofs_ctl2, hdata->shm_base + NCT6694_SHM_COFS_CTL2); + + return 0; +} + +static void nct6694_core_remove_action(void *data) +{ + struct nct6694 *nct6694 = data; + + nct6694_core_remove(nct6694); +} + +static const u8 sio_addrs[] = { 0x2e, 0x4e }; + +static int nct6694_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nct6694_sio_data *sio_data; + struct nct6694_hif_data *hdata; + struct nct6694 *data; + void __iomem *rpt_base, *msg_base; + int ret, i, irq; + + sio_data = devm_kzalloc(dev, sizeof(*sio_data), GFP_KERNEL); + if (!sio_data) + return -ENOMEM; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL); + if (!hdata) + return -ENOMEM; + + rpt_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rpt_base)) + return PTR_ERR(rpt_base); + msg_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(msg_base)) + return PTR_ERR(msg_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + for (i = 0; i < ARRAY_SIZE(sio_addrs); i++) { + ret = nct6694_sio_find(sio_data, sio_addrs[i]); + if (!ret) + break; + } + if (ret) + return ret; + + hdata->sio_data = sio_data; + hdata->msg_base = msg_base; + hdata->regmap = devm_regmap_init_mmio(dev, rpt_base, &nct6694_regmap_config); + if (IS_ERR(hdata->regmap)) + return PTR_ERR(hdata->regmap); + + data->dev = dev; + data->priv = hdata; + data->read_msg = nct6694_hif_read_msg; + data->write_msg = nct6694_hif_write_msg; + + ret = devm_mutex_init(dev, &hdata->msg_lock); + if (ret) + return ret; + + ret = nct6694_core_probe(dev, data, nct6694_hif_devs, ARRAY_SIZE(nct6694_hif_devs)); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, nct6694_core_remove_action, data); + if (ret) + return ret; + + ret = nct6694_irq_init(data, irq); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, nct6694_irq_release, data); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, irq, NULL, nct6694_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(dev), data); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + return 0; +} + +static const struct acpi_device_id nct6694_acpi_ids[] = { + { "NTN0538", 0 }, + {} +}; + +static struct platform_driver nct6694_driver = { + .driver = { + .name = DRVNAME, + .acpi_match_table = nct6694_acpi_ids, + }, + .probe = nct6694_probe, +}; +module_platform_driver(nct6694_driver); + +MODULE_DESCRIPTION("Nuvoton NCT6694 host-interface transport driver"); +MODULE_AUTHOR("Ming Yu "); +MODULE_LICENSE("GPL"); -- 2.34.1