Re: [PATCH v5 3/4] Watchdog: sp5100_tco: Add initialization using EFCH MMIO

From: Guenter Roeck
Date: Wed Feb 02 2022 - 14:51:47 EST


On Wed, Feb 02, 2022 at 09:35:24AM -0600, Terry Bowman wrote:
> cd6h/cd7h port I/O can be disabled on recent AMD hardware. Read
> accesses to disabled cd6h/cd7h port I/O will return F's and written
> data is dropped. It is recommended to replace the cd6h/cd7h
> port I/O with MMIO.
>
> Co-developed-by: Robert Richter <rrichter@xxxxxxx>
> Signed-off-by: Robert Richter <rrichter@xxxxxxx>
> Signed-off-by: Terry Bowman <terry.bowman@xxxxxxx>
> Tested-by: Jean Delvare <jdelvare@xxxxxxx>
> Reviewed-by: Jean Delvare <jdelvare@xxxxxxx>

Reviewed-by: Guenter Roeck <linux@xxxxxxxxxxxx>

> ---
> drivers/watchdog/sp5100_tco.c | 100 +++++++++++++++++++++++++++++++++-
> drivers/watchdog/sp5100_tco.h | 5 ++
> 2 files changed, 104 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c
> index 8db7504f0aa4..e02399ea8730 100644
> --- a/drivers/watchdog/sp5100_tco.c
> +++ b/drivers/watchdog/sp5100_tco.c
> @@ -49,7 +49,7 @@
> /* internal variables */
>
> enum tco_reg_layout {
> - sp5100, sb800, efch
> + sp5100, sb800, efch, efch_mmio
> };
>
> struct sp5100_tco {
> @@ -209,6 +209,8 @@ static void tco_timer_enable(struct sp5100_tco *tco)
> ~EFCH_PM_WATCHDOG_DISABLE,
> EFCH_PM_DECODEEN_SECOND_RES);
> break;
> + default:
> + break;
> }
> }
>
> @@ -307,6 +309,99 @@ static int sp5100_tco_timer_init(struct sp5100_tco *tco)
> return 0;
> }
>
> +static u8 efch_read_pm_reg8(void __iomem *addr, u8 index)
> +{
> + return readb(addr + index);
> +}
> +
> +static void efch_update_pm_reg8(void __iomem *addr, u8 index, u8 reset, u8 set)
> +{
> + u8 val;
> +
> + val = readb(addr + index);
> + val &= reset;
> + val |= set;
> + writeb(val, addr + index);
> +}
> +
> +static void tco_timer_enable_mmio(void __iomem *addr)
> +{
> + efch_update_pm_reg8(addr, EFCH_PM_DECODEEN3,
> + ~EFCH_PM_WATCHDOG_DISABLE,
> + EFCH_PM_DECODEEN_SECOND_RES);
> +}
> +
> +static int sp5100_tco_setupdevice_mmio(struct device *dev,
> + struct watchdog_device *wdd)
> +{
> + struct sp5100_tco *tco = watchdog_get_drvdata(wdd);
> + const char *dev_name = SB800_DEVNAME;
> + u32 mmio_addr = 0, alt_mmio_addr = 0;
> + struct resource *res;
> + void __iomem *addr;
> + int ret;
> + u32 val;
> +
> + res = request_mem_region_muxed(EFCH_PM_ACPI_MMIO_PM_ADDR,
> + EFCH_PM_ACPI_MMIO_PM_SIZE,
> + "sp5100_tco");
> +
> + if (!res) {
> + dev_err(dev,
> + "Memory region 0x%08x already in use\n",
> + EFCH_PM_ACPI_MMIO_PM_ADDR);
> + return -EBUSY;
> + }
> +
> + addr = ioremap(EFCH_PM_ACPI_MMIO_PM_ADDR, EFCH_PM_ACPI_MMIO_PM_SIZE);
> + if (!addr) {
> + dev_err(dev, "Address mapping failed\n");
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + /*
> + * EFCH_PM_DECODEEN_WDT_TMREN is dual purpose. This bitfield
> + * enables sp5100_tco register MMIO space decoding. The bitfield
> + * also starts the timer operation. Enable if not already enabled.
> + */
> + val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN);
> + if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) {
> + efch_update_pm_reg8(addr, EFCH_PM_DECODEEN, 0xff,
> + EFCH_PM_DECODEEN_WDT_TMREN);
> + }
> +
> + /* Error if the timer could not be enabled */
> + val = efch_read_pm_reg8(addr, EFCH_PM_DECODEEN);
> + if (!(val & EFCH_PM_DECODEEN_WDT_TMREN)) {
> + dev_err(dev, "Failed to enable the timer\n");
> + ret = -EFAULT;
> + goto out;
> + }
> +
> + mmio_addr = EFCH_PM_WDT_ADDR;
> +
> + /* Determine alternate MMIO base address */
> + val = efch_read_pm_reg8(addr, EFCH_PM_ISACONTROL);
> + if (val & EFCH_PM_ISACONTROL_MMIOEN)
> + alt_mmio_addr = EFCH_PM_ACPI_MMIO_ADDR +
> + EFCH_PM_ACPI_MMIO_WDT_OFFSET;
> +
> + ret = sp5100_tco_prepare_base(tco, mmio_addr, alt_mmio_addr, dev_name);
> + if (!ret) {
> + tco_timer_enable_mmio(addr);
> + ret = sp5100_tco_timer_init(tco);
> + }
> +
> +out:
> + if (addr)
> + iounmap(addr);
> +
> + release_resource(res);
> +
> + return ret;
> +}
> +
> static int sp5100_tco_setupdevice(struct device *dev,
> struct watchdog_device *wdd)
> {
> @@ -316,6 +411,9 @@ static int sp5100_tco_setupdevice(struct device *dev,
> u32 alt_mmio_addr = 0;
> int ret;
>
> + if (tco->tco_reg_layout == efch_mmio)
> + return sp5100_tco_setupdevice_mmio(dev, wdd);
> +
> /* Request the IO ports used by this driver */
> if (!request_muxed_region(SP5100_IO_PM_INDEX_REG,
> SP5100_PM_IOPORTS_SIZE, "sp5100_tco")) {
> diff --git a/drivers/watchdog/sp5100_tco.h b/drivers/watchdog/sp5100_tco.h
> index daee872f9b71..8ca1b215e3ce 100644
> --- a/drivers/watchdog/sp5100_tco.h
> +++ b/drivers/watchdog/sp5100_tco.h
> @@ -83,4 +83,9 @@
> #define EFCH_PM_ISACONTROL_MMIOEN BIT(1)
>
> #define EFCH_PM_ACPI_MMIO_ADDR 0xfed80000
> +#define EFCH_PM_ACPI_MMIO_PM_OFFSET 0x00000300
> #define EFCH_PM_ACPI_MMIO_WDT_OFFSET 0x00000b00
> +
> +#define EFCH_PM_ACPI_MMIO_PM_ADDR (EFCH_PM_ACPI_MMIO_ADDR + \
> + EFCH_PM_ACPI_MMIO_PM_OFFSET)
> +#define EFCH_PM_ACPI_MMIO_PM_SIZE 8
> --
> 2.30.2
>