From c72851df73af67a72e72d04484aff917cb5c6c21 Mon Sep 17 00:00:00 2001 From: samin Date: Fri, 14 Jan 2022 15:10:00 +0800 Subject: [PATCH] soc:starfive: add jh7110 pmu driver. The JH7110 PMU can dynamically switch on or off power domians and set the power-on and power-off sequence. API Instructions refer to include/soc/starfive/jh7110_pmu.h Signed-off-by: samin --- drivers/soc/starfive/Kconfig | 8 + drivers/soc/starfive/Makefile | 3 + drivers/soc/starfive/jh7110_pmu.c | 327 ++++++++++++++++++++++++++++++++++++++ include/soc/starfive/jh7110_pmu.h | 104 ++++++++++++ 4 files changed, 442 insertions(+) create mode 100644 drivers/soc/starfive/Kconfig create mode 100644 drivers/soc/starfive/Makefile create mode 100644 drivers/soc/starfive/jh7110_pmu.c create mode 100644 include/soc/starfive/jh7110_pmu.h diff --git a/drivers/soc/starfive/Kconfig b/drivers/soc/starfive/Kconfig new file mode 100644 index 0000000..d246df4 --- /dev/null +++ b/drivers/soc/starfive/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 + +config STARFIVE_PMU + bool "Support PMU for StarFive Soc" + depends on SOC_STARFIVE || COMPILE_TEST + default SOC_STARFIVE_JH7110 + help + Support PMU for StarFive Soc. diff --git a/drivers/soc/starfive/Makefile b/drivers/soc/starfive/Makefile new file mode 100644 index 0000000..296eb75 --- /dev/null +++ b/drivers/soc/starfive/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_STARFIVE_PMU) += jh7110_pmu.o diff --git a/drivers/soc/starfive/jh7110_pmu.c b/drivers/soc/starfive/jh7110_pmu.c new file mode 100644 index 0000000..5e50220 --- /dev/null +++ b/drivers/soc/starfive/jh7110_pmu.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PMU driver for the StarFive JH7110 SoC + * + * Copyright (C) 2022 samin + */ +#include +#include +#include +#include +#include +#include +#include + +/* register define */ +#define HW_EVENT_TURN_ON_MASK 0x04 +#define HW_EVENT_TURN_OFF_MASK 0x08 +#define SW_TURN_ON_POWER_MODE 0x0C +#define SW_TURN_OFF_POWER_MODE 0x10 +#define THE_SHOLD_SEQ_TIMEOUT 0x14 +#define POWER_DOMAIN_CASCADE_0 0x18 +#define POWER_DOMAIN_CASCADE_1 0x1C +#define POWER_DOMAIN_CASCADE_2 0x20 +#define POWER_DOMAIN_CASCADE_3 0x24 +#define POWER_DOMAIN_CASCADE_4 0x28 +#define POWER_DOMAIN_CASCADE_5 0x2C +#define POWER_DOMAIN_CASCADE_6 0x30 +#define POWER_DOMAIN_CASCADE_7 0x34 +#define POWER_DOMAIN_CASCADE_8 0x38 +#define POWER_DOMAIN_CASCADE_9 0x3C +#define POWER_DOMAIN_CASCADE_10 0x40 +#define SW_ENCOURAGE 0x44 +#define PMU_INT_MASK 0x48 +#define PCH_BYPASS 0x4C +#define PCH_PSTATE 0x50 +#define PCH_TIMEOUT 0x54 +#define LP_TIMEOUT 0x58 +#define HW_TURN_ON_MODE 0x5C +#define CURR_POWER_MODE 0x80 +#define CURR_SEQ_STATE 0x84 +#define PMU_EVENT_STATUS 0x88 +#define PMU_INT_STATUS 0x8C +#define HW_EVENT_RECORD 0x90 +#define HW_EVENT_TYPE_RECORD 0x94 +#define PCH_PACTIVE_STATUS 0x98 + +/* pmu int status */ +#define INT_SEQ_DONE_EVENT BIT(0) +#define INT_HW_REQ_EVENT BIT(1) +#define INT_SW_FAIL_EVENT GENMASK(3, 2) +#define INT_HW_FAIL_EVENT GENMASK(5, 4) +#define INT_PCH_FAIL_EVENT GENMASK(8, 6) +#define PMU_INT_EVENT_ALL GENMASK(8, 0) + +/* sw encourage cfg */ +#define SW_MODE_ENCOURAGE_EN_LO 0x05 +#define SW_MODE_ENCOURAGE_EN_HI 0x50 +#define SW_MODE_ENCOURAGE_DIS_LO 0x0A +#define SW_MODE_ENCOURAGE_DIS_HI 0xA0 +#define SW_MODE_ENCOURAGE_ON 0xFF + +u32 power_domain_cascade[] = { + GENMASK(4, 0), + GENMASK(9, 5), + GENMASK(14, 10), + GENMASK(19, 15), + GENMASK(24, 20), + GENMASK(29, 25) +}; + +static void __iomem *pmu_base; + +struct starfive_pmu { + struct device *dev; + spinlock_t lock; + int irq; +}; + +static inline u32 pmu_readl(u32 offset) +{ + return readl(pmu_base + offset); +} + +static inline void pmu_writel(u32 val, u32 offset) +{ + writel(val, pmu_base + offset); +} + +static bool pmu_get_current_power_mode(u32 domain) +{ + return pmu_readl(CURR_POWER_MODE) & domain; +} + +static void starfive_pmu_int_enable(u32 mask, bool enable) +{ + u32 val = pmu_readl(PMU_INT_MASK); + + if (enable) + val |= mask; + else + val &= ~mask; + + pmu_writel(val, PMU_INT_MASK); +} +/* + * mask the hw_evnet + */ +static void starfive_pmu_hw_event_turn_on_mask(u32 hw_event, bool mask) +{ + u32 val = pmu_readl(HW_EVENT_TURN_ON_MASK); + + if (mask) + val |= hw_event; + else + val &= ~hw_event; + + pmu_writel(val, HW_EVENT_TURN_ON_MASK); +} + +void starfive_power_domain_set(u32 domain, bool enable) +{ + u32 val, mode; + u32 encourage_lo, encourage_hi; + + if (!(pmu_get_current_power_mode(domain) ^ enable)) { + pr_info("[pmu]domain is already %#x %sable status.\n", + domain, enable ? "en" : "dis"); + return; + } + + if (enable) { + mode = SW_TURN_ON_POWER_MODE; + encourage_lo = SW_MODE_ENCOURAGE_EN_LO; + encourage_hi = SW_MODE_ENCOURAGE_EN_HI; + } else { + mode = SW_TURN_OFF_POWER_MODE; + encourage_lo = SW_MODE_ENCOURAGE_DIS_LO; + encourage_hi = SW_MODE_ENCOURAGE_DIS_HI; + } + + pr_info("[pmu]domain: %#x %sable\n", domain, enable ? "en" : "dis"); + val = pmu_readl(mode); + val |= domain; + pmu_writel(val, mode); + + /* write SW_ENCOURAGE to make the configuration take effect */ + pmu_writel(SW_MODE_ENCOURAGE_ON, SW_ENCOURAGE); + pmu_writel(encourage_lo, SW_ENCOURAGE); + pmu_writel(encourage_hi, SW_ENCOURAGE); +} +EXPORT_SYMBOL(starfive_power_domain_set); + +void starfive_power_domain_set_by_hwevent(u32 domain, u32 event, bool enable) +{ + u32 val; + + val = pmu_readl(HW_TURN_ON_MODE); + + if (enable) + val |= domain; + else + val &= ~domain; + + pmu_writel(val, HW_TURN_ON_MODE); + + starfive_pmu_hw_event_turn_on_mask(event, enable); +} +EXPORT_SYMBOL(starfive_power_domain_set_by_hwevent); + +static irqreturn_t starfive_pmu_interrupt(int irq, void *data) +{ + struct starfive_pmu *pmu = data; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&pmu->lock, flags); + /* disable interrupts */ + starfive_pmu_hw_event_turn_on_mask(PMU_INT_EVENT_ALL, false); + val = pmu_readl(PMU_INT_STATUS); + + if (val & INT_SEQ_DONE_EVENT) + dev_info(pmu->dev, "sequence done.\n"); + if (val & INT_HW_REQ_EVENT) + dev_info(pmu->dev, "hardware encourage requestion.\n"); + if (val & INT_SW_FAIL_EVENT) + dev_err(pmu->dev, "software encourage fail.\n"); + if (val & INT_HW_FAIL_EVENT) + dev_err(pmu->dev, "hardware encourage fail.\n"); + if (val & INT_PCH_FAIL_EVENT) + dev_err(pmu->dev, "p-channel fail event.\n"); + + /* clear interrupts */ + pmu_writel(val, PMU_INT_STATUS); + pmu_writel(val, PMU_EVENT_STATUS); + + starfive_pmu_hw_event_turn_on_mask(PMU_INT_EVENT_ALL, true); + spin_unlock_irqrestore(&pmu->lock, flags); + + return IRQ_HANDLED; +} + +static int starfive_pmu_pad_order_get(u32 domain, bool on_off) +{ + unsigned int group; + u32 val, offset; + + group = domain / 3; + offset = (domain % 3) << 1; + + val = pmu_readl(POWER_DOMAIN_CASCADE_0 + group * 4); + if (on_off) + val &= power_domain_cascade[offset + 1]; + else + val &= power_domain_cascade[offset]; + + return val; +} + +static void starfive_pmu_pad_order_set(u32 domain, bool on_off, u32 order) +{ + unsigned int group; + u32 val, offset; + + group = domain / 3; + offset = (domain % 3) << 1; + + val = pmu_readl(POWER_DOMAIN_CASCADE_0 + group * 4); + if (on_off) + val |= (order << __ffs(power_domain_cascade[offset + 1])); + else + val |= (order << __ffs(power_domain_cascade[offset])); + + pmu_writel(val, POWER_DOMAIN_CASCADE_0 + group * 4); +} + +int starfive_power_domain_order_on_get(u32 domain) +{ + return starfive_pmu_pad_order_get(domain, true); +} +EXPORT_SYMBOL(starfive_power_domain_order_on_get); + +int starfive_power_domain_order_off_get(u32 domain) +{ + return starfive_pmu_pad_order_get(domain, false); +} +EXPORT_SYMBOL(starfive_power_domain_order_off_get); + +void starfive_power_domain_order_on_set(u32 domain, u32 order) +{ + starfive_pmu_pad_order_set(domain, true, order); +} +EXPORT_SYMBOL(starfive_power_domain_order_on_set); + +void starfive_power_domain_order_off_set(u32 domain, u32 order) +{ + starfive_pmu_pad_order_set(domain, false, order); +} +EXPORT_SYMBOL(starfive_power_domain_order_off_set); + +static int starfive_pmu_probe(struct platform_device *pdev) +{ + struct starfive_pmu *pmu; + struct device *dev = &pdev->dev; + int ret; + + pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL); + if (!pmu) + return -ENOMEM; + + pmu->dev = dev; + dev->driver_data = pmu; + + pmu_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pmu_base)) + return PTR_ERR(pmu_base); + + pmu->irq = platform_get_irq(pdev, 0); + if (pmu->irq < 0) + return pmu->irq; + + ret = devm_request_irq(dev, pmu->irq, starfive_pmu_interrupt, + 0, pdev->name, pmu); + if (ret) + dev_err(dev, "request irq failed.\n"); + + spin_lock_init(&pmu->lock); + starfive_pmu_int_enable(PMU_INT_EVENT_ALL, true); + + return ret; +} + +static int starfive_pmu_remove(struct platform_device *dev) +{ + starfive_pmu_int_enable(PMU_INT_EVENT_ALL, false); + + return 0; +} + +static const struct of_device_id starfive_pmu_dt_ids[] = { + { .compatible = "starfive,jh7110-pmu" }, + { /* sentinel */ } +}; + +static struct platform_driver starfive_pmu_driver = { + .probe = starfive_pmu_probe, + .remove = starfive_pmu_remove, + .driver = { + .name = "starfive-pmu", + .of_match_table = starfive_pmu_dt_ids, + }, +}; + +static int __init starfive_pmu_init(void) +{ + return platform_driver_register(&starfive_pmu_driver); +} + +static void __exit starfive_pmu_exit(void) +{ + platform_driver_unregister(&starfive_pmu_driver); +} +subsys_initcall(starfive_pmu_init); +module_exit(starfive_pmu_exit); + +MODULE_AUTHOR("samin "); +MODULE_DESCRIPTION("StarFive JH7110 PMU Device Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/starfive/jh7110_pmu.h b/include/soc/starfive/jh7110_pmu.h new file mode 100644 index 0000000..d549696 --- /dev/null +++ b/include/soc/starfive/jh7110_pmu.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Reset driver for the StarFive JH7110 SoC + * + * Copyright (C) 2022 samin + */ + +#ifndef __SOC_STARFIVE_JH7110_PMU_H__ +#define __SOC_STARFIVE_JH7110_PMU_H__ + +#include + +/* SW/HW Power domain id */ +#define POWER_DOMAIN_SYSTOP (1 << 0) +#define POWER_DOMAIN_CPU (1 << 1) +#define POWER_DOMAIN_GPUA (1 << 2) +#define POWER_DOMAIN_VDEC (1 << 3) +#define POWER_DOMAIN_JPU POWER_DOMAIN_VDEC +#define POWER_DOMAIN_VOUT (1 << 4) +#define POWER_DOMAIN_ISP (1 << 5) +#define POWER_DOMAIN_VENC (1 << 6) +#define POWER_DOMAIN_GPUB (1 << 7) + +enum PMU_HARD_EVENT { + RTC_EVENT = 0, + GMAC_EVENT, + RFU, + RGPIO0_EVENT, + RGPIO1_EVENT, + RGPIO2_EVENT, + RGPIO3_EVENT, + GPU_EVENT, +}; + +/* + * @func: starfive_power_domain_set + * @dec: power domain turn-on/off by software + * @domain: power domain id + * POWER_DOMAIN_SYSTOP: + * POWER_DOMAIN_CPU + * POWER_DOMAIN_GPUA + * POWER_DOMAIN_VDEC + * POWER_DOMAIN_VOUT + * POWER_DOMAIN_ISP + * POWER_DOMAIN_VENC + * POWER_DOMAIN_GPUB + * @enable: 1:enable 0:disable + */ +void starfive_power_domain_set(u32 domain, bool enable); + +/* + * @func: starfive_pmu_hw_encourage + * @dec: power domain turn-on/off by HW envent(interrupt) + * @domain: power domain id + * @event: Hardware trigger event. PMU_HARD_EVENT: + RTC_EVENT = 0, + GMAC_EVENT, + RFU, + RGPIO0_EVENT, + RGPIO1_EVENT, + RGPIO2_EVENT, + RGPIO3_EVENT, + GPU_EVENT, + * @enable: 1:enable 0:disable + * + * @for example: + * starfive_power_domain_set_by_hwevent(POWER_DOMAIN_VDEC, RTC_EVENT, 0); + * + * Means that when the RTC alarm is interrupted, the hardware + * is triggered to close the power domain of VDEC. + */ +void starfive_power_domain_set_by_hwevent(u32 domain, u32 event, bool enable); + +/* + * @func: starfive_power_domain_order_on_get + * @dec: PMU power domian power on order get. + * @domian: powerff domain id + */ +int starfive_power_domain_order_on_get(u32 domain); + +/* + * @func: starfive_power_domain_order_off_get + * @dec: PMU power domian power off order get. + * @domian: power domain id + */ +int starfive_power_domain_order_off_get(u32 domain); + +/* + * @func: starfive_power_domain_order_on_set + * @dec: PMU power domian power on order set. + * @domian: powerff domain id + * @order: the poweron order of domain + */ +void starfive_power_domain_order_on_set(u32 domain, u32 order); + +/* + * @func: starfive_power_domain_order_off_set + * @dec: PMU power domian power off order set. + * @domian: power domain id + * @order: the poweroff order of domain + */ +void starfive_power_domain_order_off_set(u32 domain, u32 order); + +#endif /* __SOC_STARFIVE_JH7110_PMU_H__ */ -- 2.7.4