From: Zoltan HERPAI Date: Sun, 1 Sep 2024 14:06:29 +0000 (+0000) Subject: starfive: add patches for 6.6 X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=e2e2fc3cd0dd72b85a9d6bacbabb8937d9295dd3;p=openwrt%2Fstaging%2Fxback.git starfive: add patches for 6.6 Add updated patches for 6.6. DMA/cache-handling patches have been reworked / backported from upstream. Signed-off-by: Zoltan HERPAI --- diff --git a/target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch b/target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch new file mode 100644 index 0000000000..d21f5efebb --- /dev/null +++ b/target/linux/starfive/patches-6.6/0001-clk-starfive-jh7110-sys-Fix-lower-rate-of-CPUfreq-by.patch @@ -0,0 +1,76 @@ +From 69275b667bd930cf5d5f577ba0ab1987c9d13987 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Mon, 21 Aug 2023 23:29:15 +0800 +Subject: [PATCH 001/116] clk: starfive: jh7110-sys: Fix lower rate of CPUfreq + by setting PLL0 rate to 1.5GHz + +CPUfreq supports 4 cpu frequency loads on 375/500/750/1500MHz. +But now PLL0 rate is 1GHz and the cpu frequency loads become +333/500/500/1000MHz in fact. + +So PLL0 rate should be set to 1.5GHz. Change the parent of cpu_root clock +and the divider of cpu_core before the setting. + +Reviewed-by: Hal Feng +Fixes: e2c510d6d630 ("riscv: dts: starfive: Add cpu scaling for JH7110 SoC") +Signed-off-by: Xingyu Wu +--- + .../clk/starfive/clk-starfive-jh7110-sys.c | 47 ++++++++++++++++++- + 1 file changed, 46 insertions(+), 1 deletion(-) + +--- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c ++++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c +@@ -501,7 +501,52 @@ static int __init jh7110_syscrg_probe(st + if (ret) + return ret; + +- return jh7110_reset_controller_register(priv, "rst-sys", 0); ++ ret = jh7110_reset_controller_register(priv, "rst-sys", 0); ++ if (ret) ++ return ret; ++ ++ /* ++ * Set PLL0 rate to 1.5GHz ++ * In order to not affect the cpu when the PLL0 rate is changing, ++ * we need to switch the parent of cpu_root clock to osc clock first, ++ * and then switch back after setting the PLL0 rate. ++ */ ++ pllclk = clk_get(priv->dev, "pll0_out"); ++ if (!IS_ERR(pllclk)) { ++ struct clk *osc = clk_get(&pdev->dev, "osc"); ++ struct clk *cpu_root = priv->reg[JH7110_SYSCLK_CPU_ROOT].hw.clk; ++ struct clk *cpu_core = priv->reg[JH7110_SYSCLK_CPU_CORE].hw.clk; ++ ++ if (IS_ERR(osc)) { ++ clk_put(pllclk); ++ return PTR_ERR(osc); ++ } ++ ++ /* ++ * CPU need voltage regulation by CPUfreq if set 1.5GHz. ++ * So in this driver, cpu_core need to be set the divider to be 2 first ++ * and will be 750M after setting parent. ++ */ ++ ret = clk_set_rate(cpu_core, clk_get_rate(cpu_core) / 2); ++ if (ret) ++ goto failed_set; ++ ++ ret = clk_set_parent(cpu_root, osc); ++ if (ret) ++ goto failed_set; ++ ++ ret = clk_set_rate(pllclk, 1500000000); ++ if (ret) ++ goto failed_set; ++ ++ ret = clk_set_parent(cpu_root, pllclk); ++ ++failed_set: ++ clk_put(pllclk); ++ clk_put(osc); ++ } ++ ++ return ret; + } + + static const struct of_device_id jh7110_syscrg_match[] = { diff --git a/target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch b/target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch new file mode 100644 index 0000000000..2b5318bb44 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0002-dt-bindings-timer-Add-timer-for-StarFive-JH7110-SoC.patch @@ -0,0 +1,114 @@ +From 7d0dbcbc079e4f72b69f53442b7759da6ebc4f87 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Thu, 19 Oct 2023 13:34:59 +0800 +Subject: [PATCH 002/116] dt-bindings: timer: Add timer for StarFive JH7110 SoC + +Add bindings for the timer on the JH7110 RISC-V SoC +by StarFive Technology Ltd. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Xingyu Wu +--- + .../bindings/timer/starfive,jh7110-timer.yaml | 96 +++++++++++++++++++ + 1 file changed, 96 insertions(+) + create mode 100644 Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml +@@ -0,0 +1,96 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/timer/starfive,jh7110-timer.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: StarFive JH7110 Timer ++ ++maintainers: ++ - Xingyu Wu ++ - Samin Guo ++ ++description: ++ This timer has four free-running 32 bit counters in StarFive JH7110 SoC. ++ And each channel(counter) triggers an interrupt when timeout. They support ++ one-shot mode and continuous-run mode. ++ ++properties: ++ compatible: ++ const: starfive,jh7110-timer ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ items: ++ - description: channel 0 ++ - description: channel 1 ++ - description: channel 2 ++ - description: channel 3 ++ ++ clocks: ++ items: ++ - description: timer APB ++ - description: channel 0 ++ - description: channel 1 ++ - description: channel 2 ++ - description: channel 3 ++ ++ clock-names: ++ items: ++ - const: apb ++ - const: ch0 ++ - const: ch1 ++ - const: ch2 ++ - const: ch3 ++ ++ resets: ++ items: ++ - description: timer APB ++ - description: channel 0 ++ - description: channel 1 ++ - description: channel 2 ++ - description: channel 3 ++ ++ reset-names: ++ items: ++ - const: apb ++ - const: ch0 ++ - const: ch1 ++ - const: ch2 ++ - const: ch3 ++ ++required: ++ - compatible ++ - reg ++ - interrupts ++ - clocks ++ - clock-names ++ - resets ++ - reset-names ++ ++additionalProperties: false ++ ++examples: ++ - | ++ timer@13050000 { ++ compatible = "starfive,jh7110-timer"; ++ reg = <0x13050000 0x10000>; ++ interrupts = <69>, <70>, <71> ,<72>; ++ clocks = <&clk 124>, ++ <&clk 125>, ++ <&clk 126>, ++ <&clk 127>, ++ <&clk 128>; ++ clock-names = "apb", "ch0", "ch1", ++ "ch2", "ch3"; ++ resets = <&rst 117>, ++ <&rst 118>, ++ <&rst 119>, ++ <&rst 120>, ++ <&rst 121>; ++ reset-names = "apb", "ch0", "ch1", ++ "ch2", "ch3"; ++ }; ++ diff --git a/target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch b/target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch new file mode 100644 index 0000000000..68b9c38d5b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0003-clocksource-Add-JH7110-timer-driver.patch @@ -0,0 +1,428 @@ +From 7cb47848f8a10aed6e050c0ea483b4bb5eaa62a4 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Thu, 19 Oct 2023 13:35:00 +0800 +Subject: [PATCH 003/116] clocksource: Add JH7110 timer driver + +Add timer driver for the StarFive JH7110 SoC. + +Signed-off-by: Xingyu Wu +--- + drivers/clocksource/Kconfig | 11 + + drivers/clocksource/Makefile | 1 + + drivers/clocksource/timer-jh7110.c | 380 +++++++++++++++++++++++++++++ + 3 files changed, 392 insertions(+) + create mode 100644 drivers/clocksource/timer-jh7110.c + +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -641,6 +641,17 @@ config RISCV_TIMER + is accessed via both the SBI and the rdcycle instruction. This is + required for all RISC-V systems. + ++config STARFIVE_JH7110_TIMER ++ bool "Timer for the STARFIVE JH7110 SoC" ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ select TIMER_OF ++ select CLKSRC_MMIO ++ default ARCH_STARFIVE ++ help ++ This enables the timer for StarFive JH7110 SoC. On RISC-V platform, ++ the system has started RISCV_TIMER, but you can also use this timer ++ which can provide four channels to do a lot more things on JH7110 SoC. ++ + config CLINT_TIMER + bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST + depends on GENERIC_SCHED_CLOCK && RISCV +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic- + obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o + obj-$(CONFIG_X86_NUMACHIP) += numachip.o + obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o ++obj-$(CONFIG_STARFIVE_JH7110_TIMER) += timer-jh7110.o + obj-$(CONFIG_CLINT_TIMER) += timer-clint.o + obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o + obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o +--- /dev/null ++++ b/drivers/clocksource/timer-jh7110.c +@@ -0,0 +1,380 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Starfive JH7110 Timer driver ++ * ++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. ++ * ++ * Author: ++ * Xingyu Wu ++ * Samin Guo ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ ++#define JH7110_TIMER_CH_LEN 0x40 ++#define JH7110_TIMER_CH_BASE(x) ((x) * JH7110_TIMER_CH_LEN) ++#define JH7110_TIMER_CH_MAX 4 ++ ++#define JH7110_CLOCK_SOURCE_RATING 200 ++#define JH7110_VALID_BITS 32 ++#define JH7110_DELAY_US 0 ++#define JH7110_TIMEOUT_US 10000 ++#define JH7110_CLOCKEVENT_RATING 300 ++#define JH7110_TIMER_MAX_TICKS 0xffffffff ++#define JH7110_TIMER_MIN_TICKS 0xf ++#define JH7110_TIMER_RELOAD_VALUE 0 ++ ++#define JH7110_TIMER_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status for channel0~4 */ ++#define JH7110_TIMER_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run */ ++#define JH7110_TIMER_LOAD 0x08 /* RW: load value to counter */ ++#define JH7110_TIMER_ENABLE 0x10 /* RW[0]: timer enable register */ ++#define JH7110_TIMER_RELOAD 0x14 /* RW: write 1 or 0 both reload counter */ ++#define JH7110_TIMER_VALUE 0x18 /* RO: timer value register */ ++#define JH7110_TIMER_INT_CLR 0x20 /* RW: timer interrupt clear register */ ++#define JH7110_TIMER_INT_MASK 0x24 /* RW[0]: timer interrupt mask register */ ++ ++#define JH7110_TIMER_INT_CLR_ENA BIT(0) ++#define JH7110_TIMER_INT_CLR_AVA_MASK BIT(1) ++ ++struct jh7110_clkevt { ++ struct clock_event_device evt; ++ struct clocksource cs; ++ bool cs_is_valid; ++ struct clk *clk; ++ struct reset_control *rst; ++ u32 rate; ++ u32 reload_val; ++ void __iomem *base; ++ char name[sizeof("jh7110-timer.chX")]; ++}; ++ ++struct jh7110_timer_priv { ++ struct clk *pclk; ++ struct reset_control *prst; ++ struct jh7110_clkevt clkevt[JH7110_TIMER_CH_MAX]; ++}; ++ ++/* 0:continuous-run mode, 1:single-run mode */ ++enum jh7110_timer_mode { ++ JH7110_TIMER_MODE_CONTIN, ++ JH7110_TIMER_MODE_SINGLE, ++}; ++ ++/* Interrupt Mask, 0:Unmask, 1:Mask */ ++enum jh7110_timer_int_mask { ++ JH7110_TIMER_INT_ENA, ++ JH7110_TIMER_INT_DIS, ++}; ++ ++enum jh7110_timer_enable { ++ JH7110_TIMER_DIS, ++ JH7110_TIMER_ENA, ++}; ++ ++static inline struct jh7110_clkevt *to_jh7110_clkevt(struct clock_event_device *evt) ++{ ++ return container_of(evt, struct jh7110_clkevt, evt); ++} ++ ++/* ++ * BIT(0): Read value represent channel int status. ++ * Write 1 to this bit to clear interrupt. Write 0 has no effects. ++ * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. ++ */ ++static inline int jh7110_timer_int_clear(struct jh7110_clkevt *clkevt) ++{ ++ u32 value; ++ int ret; ++ ++ /* Waiting interrupt can be cleared */ ++ ret = readl_poll_timeout_atomic(clkevt->base + JH7110_TIMER_INT_CLR, value, ++ !(value & JH7110_TIMER_INT_CLR_AVA_MASK), ++ JH7110_DELAY_US, JH7110_TIMEOUT_US); ++ if (!ret) ++ writel(JH7110_TIMER_INT_CLR_ENA, clkevt->base + JH7110_TIMER_INT_CLR); ++ ++ return ret; ++} ++ ++static int jh7110_timer_start(struct jh7110_clkevt *clkevt) ++{ ++ int ret; ++ ++ /* Disable and clear interrupt first */ ++ writel(JH7110_TIMER_INT_DIS, clkevt->base + JH7110_TIMER_INT_MASK); ++ ret = jh7110_timer_int_clear(clkevt); ++ if (ret) ++ return ret; ++ ++ writel(JH7110_TIMER_INT_ENA, clkevt->base + JH7110_TIMER_INT_MASK); ++ writel(JH7110_TIMER_ENA, clkevt->base + JH7110_TIMER_ENABLE); ++ ++ return 0; ++} ++ ++static int jh7110_timer_shutdown(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); ++ return jh7110_timer_int_clear(clkevt); ++} ++ ++static void jh7110_timer_suspend(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ clkevt->reload_val = readl(clkevt->base + JH7110_TIMER_LOAD); ++ jh7110_timer_shutdown(evt); ++} ++ ++static void jh7110_timer_resume(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ writel(clkevt->reload_val, clkevt->base + JH7110_TIMER_LOAD); ++ writel(JH7110_TIMER_RELOAD_VALUE, clkevt->base + JH7110_TIMER_RELOAD); ++ jh7110_timer_start(clkevt); ++} ++ ++static int jh7110_timer_tick_resume(struct clock_event_device *evt) ++{ ++ jh7110_timer_resume(evt); ++ ++ return 0; ++} ++ ++/* IRQ handler for the timer */ ++static irqreturn_t jh7110_timer_interrupt(int irq, void *priv) ++{ ++ struct clock_event_device *evt = (struct clock_event_device *)priv; ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ if (jh7110_timer_int_clear(clkevt)) ++ return IRQ_NONE; ++ ++ if (evt->event_handler) ++ evt->event_handler(evt); ++ ++ return IRQ_HANDLED; ++} ++ ++static int jh7110_timer_set_periodic(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ u32 periodic = DIV_ROUND_CLOSEST(clkevt->rate, HZ); ++ ++ writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL); ++ writel(periodic, clkevt->base + JH7110_TIMER_LOAD); ++ ++ return jh7110_timer_start(clkevt); ++} ++ ++static int jh7110_timer_set_oneshot(struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL); ++ writel(JH7110_TIMER_MAX_TICKS, clkevt->base + JH7110_TIMER_LOAD); ++ ++ return jh7110_timer_start(clkevt); ++} ++ ++static int jh7110_timer_set_next_event(unsigned long next, ++ struct clock_event_device *evt) ++{ ++ struct jh7110_clkevt *clkevt = to_jh7110_clkevt(evt); ++ ++ writel(JH7110_TIMER_MODE_SINGLE, clkevt->base + JH7110_TIMER_CTL); ++ writel(next, clkevt->base + JH7110_TIMER_LOAD); ++ ++ return jh7110_timer_start(clkevt); ++} ++ ++static void jh7110_set_clockevent(struct clock_event_device *evt) ++{ ++ evt->features = CLOCK_EVT_FEAT_PERIODIC | ++ CLOCK_EVT_FEAT_ONESHOT | ++ CLOCK_EVT_FEAT_DYNIRQ; ++ evt->set_state_shutdown = jh7110_timer_shutdown; ++ evt->set_state_periodic = jh7110_timer_set_periodic; ++ evt->set_state_oneshot = jh7110_timer_set_oneshot; ++ evt->set_state_oneshot_stopped = jh7110_timer_shutdown; ++ evt->tick_resume = jh7110_timer_tick_resume; ++ evt->set_next_event = jh7110_timer_set_next_event; ++ evt->suspend = jh7110_timer_suspend; ++ evt->resume = jh7110_timer_resume; ++ evt->rating = JH7110_CLOCKEVENT_RATING; ++} ++ ++static u64 jh7110_timer_clocksource_read(struct clocksource *cs) ++{ ++ struct jh7110_clkevt *clkevt = container_of(cs, struct jh7110_clkevt, cs); ++ ++ return (u64)readl(clkevt->base + JH7110_TIMER_VALUE); ++} ++ ++static int jh7110_clocksource_init(struct jh7110_clkevt *clkevt) ++{ ++ int ret; ++ ++ clkevt->cs.name = clkevt->name; ++ clkevt->cs.rating = JH7110_CLOCK_SOURCE_RATING; ++ clkevt->cs.read = jh7110_timer_clocksource_read; ++ clkevt->cs.mask = CLOCKSOURCE_MASK(JH7110_VALID_BITS); ++ clkevt->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; ++ ++ ret = clocksource_register_hz(&clkevt->cs, clkevt->rate); ++ if (ret) ++ return ret; ++ ++ clkevt->cs_is_valid = true; /* clocksource register done */ ++ writel(JH7110_TIMER_MODE_CONTIN, clkevt->base + JH7110_TIMER_CTL); ++ writel(JH7110_TIMER_MAX_TICKS, clkevt->base + JH7110_TIMER_LOAD); ++ ++ return jh7110_timer_start(clkevt); ++} ++ ++static void jh7110_clockevents_register(struct jh7110_clkevt *clkevt) ++{ ++ clkevt->rate = clk_get_rate(clkevt->clk); ++ ++ jh7110_set_clockevent(&clkevt->evt); ++ clkevt->evt.name = clkevt->name; ++ clkevt->evt.cpumask = cpu_possible_mask; ++ ++ clockevents_config_and_register(&clkevt->evt, clkevt->rate, ++ JH7110_TIMER_MIN_TICKS, JH7110_TIMER_MAX_TICKS); ++} ++ ++static void jh7110_timer_release(void *data) ++{ ++ struct jh7110_timer_priv *priv = data; ++ int i; ++ ++ for (i = 0; i < JH7110_TIMER_CH_MAX; i++) { ++ /* Disable each channel of timer */ ++ if (priv->clkevt[i].base) ++ writel(JH7110_TIMER_DIS, priv->clkevt[i].base + JH7110_TIMER_ENABLE); ++ ++ /* Avoid no initialization in the loop of the probe */ ++ if (!IS_ERR_OR_NULL(priv->clkevt[i].rst)) ++ reset_control_assert(priv->clkevt[i].rst); ++ ++ if (priv->clkevt[i].cs_is_valid) ++ clocksource_unregister(&priv->clkevt[i].cs); ++ } ++ ++ reset_control_assert(priv->prst); ++} ++ ++static int jh7110_timer_probe(struct platform_device *pdev) ++{ ++ struct jh7110_timer_priv *priv; ++ struct jh7110_clkevt *clkevt; ++ char name[sizeof("chX")]; ++ int ch; ++ int ret; ++ void __iomem *base; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(base)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(base), ++ "failed to map registers\n"); ++ ++ priv->prst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); ++ if (IS_ERR(priv->prst)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->prst), ++ "failed to get apb reset\n"); ++ ++ priv->pclk = devm_clk_get_enabled(&pdev->dev, "apb"); ++ if (IS_ERR(priv->pclk)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), ++ "failed to get & enable apb clock\n"); ++ ++ ret = reset_control_deassert(priv->prst); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed to deassert apb reset\n"); ++ ++ ret = devm_add_action_or_reset(&pdev->dev, jh7110_timer_release, priv); ++ if (ret) ++ return ret; ++ ++ for (ch = 0; ch < JH7110_TIMER_CH_MAX; ch++) { ++ clkevt = &priv->clkevt[ch]; ++ snprintf(name, sizeof(name), "ch%d", ch); ++ ++ clkevt->base = base + JH7110_TIMER_CH_BASE(ch); ++ /* Ensure timer is disabled */ ++ writel(JH7110_TIMER_DIS, clkevt->base + JH7110_TIMER_ENABLE); ++ ++ clkevt->rst = devm_reset_control_get_exclusive(&pdev->dev, name); ++ if (IS_ERR(clkevt->rst)) ++ return PTR_ERR(clkevt->rst); ++ ++ clkevt->clk = devm_clk_get_enabled(&pdev->dev, name); ++ if (IS_ERR(clkevt->clk)) ++ return PTR_ERR(clkevt->clk); ++ ++ ret = reset_control_deassert(clkevt->rst); ++ if (ret) ++ return ret; ++ ++ clkevt->evt.irq = platform_get_irq(pdev, ch); ++ if (clkevt->evt.irq < 0) ++ return clkevt->evt.irq; ++ ++ snprintf(clkevt->name, sizeof(clkevt->name), "jh7110-timer.ch%d", ch); ++ jh7110_clockevents_register(clkevt); ++ ++ ret = devm_request_irq(&pdev->dev, clkevt->evt.irq, jh7110_timer_interrupt, ++ IRQF_TIMER | IRQF_IRQPOLL, ++ clkevt->name, &clkevt->evt); ++ if (ret) ++ return ret; ++ ++ ret = jh7110_clocksource_init(clkevt); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id jh7110_timer_match[] = { ++ { .compatible = "starfive,jh7110-timer", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, jh7110_timer_match); ++ ++static struct platform_driver jh7110_timer_driver = { ++ .probe = jh7110_timer_probe, ++ .driver = { ++ .name = "jh7110-timer", ++ .of_match_table = jh7110_timer_match, ++ }, ++}; ++module_platform_driver(jh7110_timer_driver); ++ ++MODULE_AUTHOR("Xingyu Wu "); ++MODULE_DESCRIPTION("StarFive JH7110 timer driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch b/target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch new file mode 100644 index 0000000000..9c1e78b29b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0004-dt-bindings-mmc-starfive-Remove-properties-from-requ.patch @@ -0,0 +1,34 @@ +From b513eb2cabee212ba1a23839f18c0026a21e653e Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Fri, 22 Sep 2023 14:28:32 +0800 +Subject: [PATCH 004/116] dt-bindings: mmc: starfive: Remove properties from + required + +Due to the change of tuning implementation, it's no longer necessary to +use the "starfive,sysreg" property in dts, so remove it from required. + +Signed-off-by: William Qiu +Acked-by: Conor Dooley +Reviewed-by: Emil Renner Berthing +Link: https://lore.kernel.org/r/20230922062834.39212-2-william.qiu@starfivetech.com +Signed-off-by: Ulf Hansson +--- + Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml | 2 -- + 1 file changed, 2 deletions(-) + +--- a/Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml ++++ b/Documentation/devicetree/bindings/mmc/starfive,jh7110-mmc.yaml +@@ -55,7 +55,6 @@ required: + - clocks + - clock-names + - interrupts +- - starfive,sysreg + + unevaluatedProperties: false + +@@ -73,5 +72,4 @@ examples: + fifo-depth = <32>; + fifo-watermark-aligned; + data-addr = <0>; +- starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>; + }; diff --git a/target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch b/target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch new file mode 100644 index 0000000000..b8ea96f3c8 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0005-mmc-starfive-Change-tuning-implementation.patch @@ -0,0 +1,199 @@ +From 3ae8cec8fd28e18847edb67dfea04718c2f3369f Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Fri, 22 Sep 2023 14:28:33 +0800 +Subject: [PATCH 005/116] mmc: starfive: Change tuning implementation + +Before, we used syscon to achieve tuning, but the actual measurement +showed little effect, so the tuning implementation was modified here, +and it was realized by reading and writing the UHS_REG_EXT register. + +Signed-off-by: William Qiu +Reviewed-by: Emil Renner Berthing +Link: https://lore.kernel.org/r/20230922062834.39212-3-william.qiu@starfivetech.com +Signed-off-by: Ulf Hansson +--- + drivers/mmc/host/dw_mmc-starfive.c | 137 +++++++++-------------------- + 1 file changed, 40 insertions(+), 97 deletions(-) + +--- a/drivers/mmc/host/dw_mmc-starfive.c ++++ b/drivers/mmc/host/dw_mmc-starfive.c +@@ -5,6 +5,7 @@ + * Copyright (c) 2022 StarFive Technology Co., Ltd. + */ + ++#include + #include + #include + #include +@@ -20,13 +21,7 @@ + #define ALL_INT_CLR 0x1ffff + #define MAX_DELAY_CHAIN 32 + +-struct starfive_priv { +- struct device *dev; +- struct regmap *reg_syscon; +- u32 syscon_offset; +- u32 syscon_shift; +- u32 syscon_mask; +-}; ++#define STARFIVE_SMPL_PHASE GENMASK(20, 16) + + static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios) + { +@@ -44,117 +39,65 @@ static void dw_mci_starfive_set_ios(stru + } + } + ++static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase) ++{ ++ /* change driver phase and sample phase */ ++ u32 reg_value = mci_readl(host, UHS_REG_EXT); ++ ++ /* In UHS_REG_EXT, only 5 bits valid in DRV_PHASE and SMPL_PHASE */ ++ reg_value &= ~STARFIVE_SMPL_PHASE; ++ reg_value |= FIELD_PREP(STARFIVE_SMPL_PHASE, smpl_phase); ++ mci_writel(host, UHS_REG_EXT, reg_value); ++ ++ /* We should delay 1ms wait for timing setting finished. */ ++ mdelay(1); ++} ++ + static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, + u32 opcode) + { + static const int grade = MAX_DELAY_CHAIN; + struct dw_mci *host = slot->host; +- struct starfive_priv *priv = host->priv; +- int rise_point = -1, fall_point = -1; +- int err, prev_err = 0; +- int i; +- bool found = 0; +- u32 regval; +- +- /* +- * Use grade as the max delay chain, and use the rise_point and +- * fall_point to ensure the best sampling point of a data input +- * signals. +- */ +- for (i = 0; i < grade; i++) { +- regval = i << priv->syscon_shift; +- err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset, +- priv->syscon_mask, regval); +- if (err) +- return err; +- mci_writel(host, RINTSTS, ALL_INT_CLR); +- +- err = mmc_send_tuning(slot->mmc, opcode, NULL); +- if (!err) +- found = 1; +- +- if (i > 0) { +- if (err && !prev_err) +- fall_point = i - 1; +- if (!err && prev_err) +- rise_point = i; +- } ++ int smpl_phase, smpl_raise = -1, smpl_fall = -1; ++ int ret; + +- if (rise_point != -1 && fall_point != -1) +- goto tuning_out; ++ for (smpl_phase = 0; smpl_phase < grade; smpl_phase++) { ++ dw_mci_starfive_set_sample_phase(host, smpl_phase); ++ mci_writel(host, RINTSTS, ALL_INT_CLR); + +- prev_err = err; +- err = 0; +- } ++ ret = mmc_send_tuning(slot->mmc, opcode, NULL); + +-tuning_out: +- if (found) { +- if (rise_point == -1) +- rise_point = 0; +- if (fall_point == -1) +- fall_point = grade - 1; +- if (fall_point < rise_point) { +- if ((rise_point + fall_point) > +- (grade - 1)) +- i = fall_point / 2; +- else +- i = (rise_point + grade - 1) / 2; +- } else { +- i = (rise_point + fall_point) / 2; ++ if (!ret && smpl_raise < 0) { ++ smpl_raise = smpl_phase; ++ } else if (ret && smpl_raise >= 0) { ++ smpl_fall = smpl_phase - 1; ++ break; + } +- +- regval = i << priv->syscon_shift; +- err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset, +- priv->syscon_mask, regval); +- if (err) +- return err; +- mci_writel(host, RINTSTS, ALL_INT_CLR); +- +- dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i); +- } else { +- dev_err(host->dev, "No valid delay chain! use default\n"); +- err = -EINVAL; + } + +- mci_writel(host, RINTSTS, ALL_INT_CLR); +- return err; +-} +- +-static int dw_mci_starfive_parse_dt(struct dw_mci *host) +-{ +- struct of_phandle_args args; +- struct starfive_priv *priv; +- int ret; ++ if (smpl_phase >= grade) ++ smpl_fall = grade - 1; + +- priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- ret = of_parse_phandle_with_fixed_args(host->dev->of_node, +- "starfive,sysreg", 3, 0, &args); +- if (ret) { +- dev_err(host->dev, "Failed to parse starfive,sysreg\n"); +- return -EINVAL; ++ if (smpl_raise < 0) { ++ smpl_phase = 0; ++ dev_err(host->dev, "No valid delay chain! use default\n"); ++ ret = -EINVAL; ++ goto out; + } + +- priv->reg_syscon = syscon_node_to_regmap(args.np); +- of_node_put(args.np); +- if (IS_ERR(priv->reg_syscon)) +- return PTR_ERR(priv->reg_syscon); +- +- priv->syscon_offset = args.args[0]; +- priv->syscon_shift = args.args[1]; +- priv->syscon_mask = args.args[2]; +- +- host->priv = priv; ++ smpl_phase = (smpl_raise + smpl_fall) / 2; ++ dev_dbg(host->dev, "Found valid delay chain! use it [delay=%d]\n", smpl_phase); ++ ret = 0; + +- return 0; ++out: ++ dw_mci_starfive_set_sample_phase(host, smpl_phase); ++ mci_writel(host, RINTSTS, ALL_INT_CLR); ++ return ret; + } + + static const struct dw_mci_drv_data starfive_data = { + .common_caps = MMC_CAP_CMD23, + .set_ios = dw_mci_starfive_set_ios, +- .parse_dt = dw_mci_starfive_parse_dt, + .execute_tuning = dw_mci_starfive_execute_tuning, + }; + diff --git a/target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch b/target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch new file mode 100644 index 0000000000..aafbee5d42 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0006-dt-bindings-pwm-Add-bindings-for-OpenCores-PWM-Contr.patch @@ -0,0 +1,79 @@ +From e366df2ff64e9f93a5b35eea6a198b005d5a0911 Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Fri, 22 Dec 2023 17:45:45 +0800 +Subject: [PATCH 006/116] dt-bindings: pwm: Add bindings for OpenCores PWM + Controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add bindings for OpenCores PWM Controller. + +Signed-off-by: William Qiu +Reviewed-by: Hal Feng +Reviewed-by: Conor Dooley +Reviewed-by: Uwe Kleine-König +Acked-by: Uwe Kleine-König +--- + .../bindings/pwm/opencores,pwm.yaml | 55 +++++++++++++++++++ + 1 file changed, 55 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pwm/opencores,pwm.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pwm/opencores,pwm.yaml +@@ -0,0 +1,55 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/pwm/opencores,pwm.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: OpenCores PWM controller ++ ++maintainers: ++ - William Qiu ++ ++description: ++ The OpenCores PTC ip core contains a PWM controller. When operating in PWM ++ mode, the PTC core generates binary signal with user-programmable low and ++ high periods. All PTC counters and registers are 32-bit. ++ ++allOf: ++ - $ref: pwm.yaml# ++ ++properties: ++ compatible: ++ items: ++ - enum: ++ - starfive,jh7100-pwm ++ - starfive,jh7110-pwm ++ - const: opencores,pwm-v1 ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ maxItems: 1 ++ ++ resets: ++ maxItems: 1 ++ ++ "#pwm-cells": ++ const: 3 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ ++additionalProperties: false ++ ++examples: ++ - | ++ pwm@12490000 { ++ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1"; ++ reg = <0x12490000 0x10000>; ++ clocks = <&clkgen 181>; ++ resets = <&rstgen 109>; ++ #pwm-cells = <3>; ++ }; diff --git a/target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch b/target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch new file mode 100644 index 0000000000..27e510d86a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0007-pwm-opencores-Add-PWM-driver-support.patch @@ -0,0 +1,285 @@ +From 644bfe581dde9b762460a4916da4d71c148be06e Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Fri, 22 Dec 2023 17:45:46 +0800 +Subject: [PATCH 007/116] pwm: opencores: Add PWM driver support + +Add driver for OpenCores PWM Controller. And add compatibility code +which based on StarFive SoC. + +Co-developed-by: Hal Feng +Signed-off-by: Hal Feng +Signed-off-by: William Qiu +--- + drivers/pwm/Kconfig | 12 ++ + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-ocores.c | 233 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 246 insertions(+) + create mode 100644 drivers/pwm/pwm-ocores.c + +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -434,6 +434,18 @@ config PWM_NTXEC + controller found in certain e-book readers designed by the original + design manufacturer Netronix. + ++config PWM_OCORES ++ tristate "OpenCores PWM support" ++ depends on HAS_IOMEM && OF ++ depends on COMMON_CLK ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ help ++ If you say yes to this option, support will be included for the ++ OpenCores PWM. For details see https://opencores.org/projects/ptc. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-ocores. ++ + config PWM_OMAP_DMTIMER + tristate "OMAP Dual-Mode Timer PWM support" + depends on OF +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm- + obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o + obj-$(CONFIG_PWM_MXS) += pwm-mxs.o + obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o ++obj-$(CONFIG_PWM_OCORES) += pwm-ocores.o + obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o + obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o + obj-$(CONFIG_PWM_PXA) += pwm-pxa.o +--- /dev/null ++++ b/drivers/pwm/pwm-ocores.c +@@ -0,0 +1,233 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * OpenCores PWM Driver ++ * ++ * https://opencores.org/projects/ptc ++ * ++ * Copyright (C) 2018-2023 StarFive Technology Co., Ltd. ++ * ++ * Limitations: ++ * - The hardware only do inverted polarity. ++ * - The hardware minimum period / duty_cycle is (1 / pwm_apb clock frequency) ns. ++ * - The hardware maximum period / duty_cycle is (U32_MAX / pwm_apb clock frequency) ns. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* OCPWM_CTRL register bits*/ ++#define REG_OCPWM_EN BIT(0) ++#define REG_OCPWM_ECLK BIT(1) ++#define REG_OCPWM_NEC BIT(2) ++#define REG_OCPWM_OE BIT(3) ++#define REG_OCPWM_SIGNLE BIT(4) ++#define REG_OCPWM_INTE BIT(5) ++#define REG_OCPWM_INT BIT(6) ++#define REG_OCPWM_CNTRRST BIT(7) ++#define REG_OCPWM_CAPTE BIT(8) ++ ++struct ocores_pwm_device { ++ struct pwm_chip chip; ++ struct clk *clk; ++ struct reset_control *rst; ++ const struct ocores_pwm_data *data; ++ void __iomem *regs; ++ u32 clk_rate; /* PWM APB clock frequency */ ++}; ++ ++struct ocores_pwm_data { ++ void __iomem *(*get_ch_base)(void __iomem *base, unsigned int channel); ++}; ++ ++static inline u32 ocores_readl(struct ocores_pwm_device *ddata, ++ unsigned int channel, ++ unsigned int offset) ++{ ++ void __iomem *base = ddata->data->get_ch_base ? ++ ddata->data->get_ch_base(ddata->regs, channel) : ddata->regs; ++ ++ return readl(base + offset); ++} ++ ++static inline void ocores_writel(struct ocores_pwm_device *ddata, ++ unsigned int channel, ++ unsigned int offset, u32 val) ++{ ++ void __iomem *base = ddata->data->get_ch_base ? ++ ddata->data->get_ch_base(ddata->regs, channel) : ddata->regs; ++ ++ writel(val, base + offset); ++} ++ ++static inline struct ocores_pwm_device *chip_to_ocores(struct pwm_chip *chip) ++{ ++ return container_of(chip, struct ocores_pwm_device, chip); ++} ++ ++static void __iomem *starfive_jh71x0_get_ch_base(void __iomem *base, ++ unsigned int channel) ++{ ++ unsigned int offset = (channel > 3 ? 1 << 15 : 0) + (channel & 3) * 0x10; ++ ++ return base + offset; ++} ++ ++static int ocores_pwm_get_state(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ struct ocores_pwm_device *ddata = chip_to_ocores(chip); ++ u32 period_data, duty_data, ctrl_data; ++ ++ period_data = ocores_readl(ddata, pwm->hwpwm, 0x8); ++ duty_data = ocores_readl(ddata, pwm->hwpwm, 0x4); ++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC); ++ ++ state->period = DIV_ROUND_UP_ULL((u64)period_data * NSEC_PER_SEC, ddata->clk_rate); ++ state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty_data * NSEC_PER_SEC, ddata->clk_rate); ++ state->polarity = PWM_POLARITY_INVERSED; ++ state->enabled = (ctrl_data & REG_OCPWM_EN) ? true : false; ++ ++ return 0; ++} ++ ++static int ocores_pwm_apply(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const struct pwm_state *state) ++{ ++ struct ocores_pwm_device *ddata = chip_to_ocores(chip); ++ u32 ctrl_data = 0; ++ u64 period_data, duty_data; ++ ++ if (state->polarity != PWM_POLARITY_INVERSED) ++ return -EINVAL; ++ ++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC); ++ ocores_writel(ddata, pwm->hwpwm, 0xC, 0); ++ ++ period_data = DIV_ROUND_DOWN_ULL(state->period * ddata->clk_rate, NSEC_PER_SEC); ++ if (period_data <= U32_MAX) ++ ocores_writel(ddata, pwm->hwpwm, 0x8, (u32)period_data); ++ else ++ return -EINVAL; ++ ++ duty_data = DIV_ROUND_DOWN_ULL(state->duty_cycle * ddata->clk_rate, NSEC_PER_SEC); ++ if (duty_data <= U32_MAX) ++ ocores_writel(ddata, pwm->hwpwm, 0x4, (u32)duty_data); ++ else ++ return -EINVAL; ++ ++ ocores_writel(ddata, pwm->hwpwm, 0xC, 0); ++ ++ if (state->enabled) { ++ ctrl_data = ocores_readl(ddata, pwm->hwpwm, 0xC); ++ ocores_writel(ddata, pwm->hwpwm, 0xC, ctrl_data | REG_OCPWM_EN | REG_OCPWM_OE); ++ } ++ ++ return 0; ++} ++ ++static const struct pwm_ops ocores_pwm_ops = { ++ .get_state = ocores_pwm_get_state, ++ .apply = ocores_pwm_apply, ++}; ++ ++static const struct ocores_pwm_data jh7100_pwm_data = { ++ .get_ch_base = starfive_jh71x0_get_ch_base, ++}; ++ ++static const struct ocores_pwm_data jh7110_pwm_data = { ++ .get_ch_base = starfive_jh71x0_get_ch_base, ++}; ++ ++static const struct of_device_id ocores_pwm_of_match[] = { ++ { .compatible = "opencores,pwm-v1" }, ++ { .compatible = "starfive,jh7100-pwm", .data = &jh7100_pwm_data}, ++ { .compatible = "starfive,jh7110-pwm", .data = &jh7110_pwm_data}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, ocores_pwm_of_match); ++ ++static void ocores_reset_control_assert(void *data) ++{ ++ reset_control_assert(data); ++} ++ ++static int ocores_pwm_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *id; ++ struct device *dev = &pdev->dev; ++ struct ocores_pwm_device *ddata; ++ struct pwm_chip *chip; ++ int ret; ++ ++ id = of_match_device(ocores_pwm_of_match, dev); ++ if (!id) ++ return -EINVAL; ++ ++ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); ++ if (!ddata) ++ return -ENOMEM; ++ ++ ddata->data = id->data; ++ chip = &ddata->chip; ++ chip->dev = dev; ++ chip->ops = &ocores_pwm_ops; ++ chip->npwm = 8; ++ chip->of_pwm_n_cells = 3; ++ ++ ddata->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(ddata->regs)) ++ return dev_err_probe(dev, PTR_ERR(ddata->regs), ++ "Unable to map IO resources\n"); ++ ++ ddata->clk = devm_clk_get_enabled(dev, NULL); ++ if (IS_ERR(ddata->clk)) ++ return dev_err_probe(dev, PTR_ERR(ddata->clk), ++ "Unable to get pwm's clock\n"); ++ ++ ddata->rst = devm_reset_control_get_optional_exclusive(dev, NULL); ++ if (IS_ERR(ddata->rst)) ++ return dev_err_probe(dev, PTR_ERR(ddata->rst), ++ "Unable to get pwm's reset\n"); ++ ++ reset_control_deassert(ddata->rst); ++ ++ ret = devm_add_action_or_reset(dev, ocores_reset_control_assert, ddata->rst); ++ if (ret) ++ return ret; ++ ++ ddata->clk_rate = clk_get_rate(ddata->clk); ++ if (ddata->clk_rate <= 0) ++ return dev_err_probe(dev, ddata->clk_rate, ++ "Unable to get clock's rate\n"); ++ ++ ret = devm_pwmchip_add(dev, chip); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "Could not register PWM chip\n"); ++ ++ platform_set_drvdata(pdev, ddata); ++ ++ return ret; ++} ++ ++static struct platform_driver ocores_pwm_driver = { ++ .probe = ocores_pwm_probe, ++ .driver = { ++ .name = "ocores-pwm", ++ .of_match_table = ocores_pwm_of_match, ++ }, ++}; ++module_platform_driver(ocores_pwm_driver); ++ ++MODULE_AUTHOR("Jieqin Chen"); ++MODULE_AUTHOR("Hal Feng "); ++MODULE_DESCRIPTION("OpenCores PWM PTC driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch b/target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch new file mode 100644 index 0000000000..a6ede31342 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0008-crypto-starfive-remove-unnecessary-alignmask-for-aha.patch @@ -0,0 +1,117 @@ +From 7d9521cad6474d45e9056176982e6da54d40bc19 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Sun, 22 Oct 2023 01:10:42 -0700 +Subject: [PATCH 008/116] crypto: starfive - remove unnecessary alignmask for + ahashes + +The crypto API's support for alignmasks for ahash algorithms is nearly +useless, as its only effect is to cause the API to align the key and +result buffers. The drivers that happen to be specifying an alignmask +for ahash rarely actually need it. When they do, it's easily fixable, +especially considering that these buffers cannot be used for DMA. + +In preparation for removing alignmask support from ahash, this patch +makes the starfive driver no longer use it. This driver did actually +rely on it, but only for storing to the result buffer using int stores +in starfive_hash_copy_hash(). This patch makes +starfive_hash_copy_hash() use put_unaligned() instead. (It really +should use a specific endianness, but that's an existing bug.) + +Signed-off-by: Eric Biggers +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-hash.c | 13 ++----------- + 1 file changed, 2 insertions(+), 11 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-hash.c ++++ b/drivers/crypto/starfive/jh7110-hash.c +@@ -209,7 +209,8 @@ static int starfive_hash_copy_hash(struc + data = (u32 *)req->result; + + for (count = 0; count < mlen; count++) +- data[count] = readl(ctx->cryp->base + STARFIVE_HASH_SHARDR); ++ put_unaligned(readl(ctx->cryp->base + STARFIVE_HASH_SHARDR), ++ &data[count]); + + return 0; + } +@@ -628,7 +629,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -658,7 +658,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -687,7 +686,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -717,7 +715,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -746,7 +743,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -776,7 +772,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -805,7 +800,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -835,7 +829,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -864,7 +857,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, +@@ -894,7 +886,6 @@ static struct ahash_engine_alg algs_sha2 + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 3, + .cra_module = THIS_MODULE, + } + }, diff --git a/target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch b/target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch new file mode 100644 index 0000000000..145c01377d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0009-crypto-starfive-Update-driver-dependencies.patch @@ -0,0 +1,25 @@ +From 52de0270ed6453727936b5a793dc367d75280e84 Mon Sep 17 00:00:00 2001 +From: Jia Jie Ho +Date: Wed, 15 Nov 2023 01:12:13 +0800 +Subject: [PATCH 009/116] crypto: starfive - Update driver dependencies + +Change AMBA_PL08X to required dependency as the hash ops depends on it +for data transfer. + +Signed-off-by: Jia Jie Ho +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/crypto/starfive/Kconfig ++++ b/drivers/crypto/starfive/Kconfig +@@ -4,7 +4,7 @@ + + config CRYPTO_DEV_JH7110 + tristate "StarFive JH7110 cryptographic engine driver" +- depends on SOC_STARFIVE || AMBA_PL08X || COMPILE_TEST ++ depends on (SOC_STARFIVE && AMBA_PL08X) || COMPILE_TEST + depends on HAS_DMA + select CRYPTO_ENGINE + select CRYPTO_HMAC diff --git a/target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch b/target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch new file mode 100644 index 0000000000..c94009d4ff --- /dev/null +++ b/target/linux/starfive/patches-6.6/0010-crypto-starfive-RSA-poll-csr-for-done-status.patch @@ -0,0 +1,200 @@ +From 68a1bbb99455fd5ea80b7e21ec726f369abc9572 Mon Sep 17 00:00:00 2001 +From: Jia Jie Ho +Date: Wed, 15 Nov 2023 01:12:14 +0800 +Subject: [PATCH 010/116] crypto: starfive - RSA poll csr for done status + +Hardware could not clear irq status without resetting the entire module. +Driver receives irq immediately when mask bit is cleared causing +intermittent errors in RSA calculations. Switch to use csr polling for +done status instead. + +Signed-off-by: Jia Jie Ho +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-cryp.c | 8 ----- + drivers/crypto/starfive/jh7110-cryp.h | 10 +++++- + drivers/crypto/starfive/jh7110-rsa.c | 49 +++++++-------------------- + 3 files changed, 22 insertions(+), 45 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-cryp.c ++++ b/drivers/crypto/starfive/jh7110-cryp.c +@@ -109,12 +109,6 @@ static irqreturn_t starfive_cryp_irq(int + tasklet_schedule(&cryp->hash_done); + } + +- if (status & STARFIVE_IE_FLAG_PKA_DONE) { +- mask |= STARFIVE_IE_MASK_PKA_DONE; +- writel(mask, cryp->base + STARFIVE_IE_MASK_OFFSET); +- complete(&cryp->pka_done); +- } +- + return IRQ_HANDLED; + } + +@@ -159,8 +153,6 @@ static int starfive_cryp_probe(struct pl + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->rst), + "Error getting hardware reset line\n"); + +- init_completion(&cryp->pka_done); +- + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; +--- a/drivers/crypto/starfive/jh7110-cryp.h ++++ b/drivers/crypto/starfive/jh7110-cryp.h +@@ -125,6 +125,15 @@ union starfive_pka_cacr { + }; + }; + ++union starfive_pka_casr { ++ u32 v; ++ struct { ++#define STARFIVE_PKA_DONE BIT(0) ++ u32 done :1; ++ u32 rsvd_0 :31; ++ }; ++}; ++ + struct starfive_rsa_key { + u8 *n; + u8 *e; +@@ -183,7 +192,6 @@ struct starfive_cryp_dev { + struct crypto_engine *engine; + struct tasklet_struct aes_done; + struct tasklet_struct hash_done; +- struct completion pka_done; + size_t assoclen; + size_t total_in; + size_t total_out; +--- a/drivers/crypto/starfive/jh7110-rsa.c ++++ b/drivers/crypto/starfive/jh7110-rsa.c +@@ -6,13 +6,7 @@ + */ + + #include +-#include +-#include +-#include +-#include + #include +-#include +-#include + #include + #include + #include +@@ -28,13 +22,13 @@ + #define STARFIVE_PKA_CAER_OFFSET (STARFIVE_PKA_REGS_OFFSET + 0x108) + #define STARFIVE_PKA_CANR_OFFSET (STARFIVE_PKA_REGS_OFFSET + 0x208) + +-// R^2 mod N and N0' ++/* R ^ 2 mod N and N0' */ + #define CRYPTO_CMD_PRE 0x0 +-// A * R mod N ==> A ++/* A * R mod N ==> A */ + #define CRYPTO_CMD_ARN 0x5 +-// A * E * R mod N ==> A ++/* A * E * R mod N ==> A */ + #define CRYPTO_CMD_AERN 0x6 +-// A * A * R mod N ==> A ++/* A * A * R mod N ==> A */ + #define CRYPTO_CMD_AARN 0x7 + + #define STARFIVE_RSA_MAX_KEYSZ 256 +@@ -43,21 +37,10 @@ + static inline int starfive_pka_wait_done(struct starfive_cryp_ctx *ctx) + { + struct starfive_cryp_dev *cryp = ctx->cryp; ++ u32 status; + +- return wait_for_completion_timeout(&cryp->pka_done, +- usecs_to_jiffies(100000)); +-} +- +-static inline void starfive_pka_irq_mask_clear(struct starfive_cryp_ctx *ctx) +-{ +- struct starfive_cryp_dev *cryp = ctx->cryp; +- u32 stat; +- +- stat = readl(cryp->base + STARFIVE_IE_MASK_OFFSET); +- stat &= ~STARFIVE_IE_MASK_PKA_DONE; +- writel(stat, cryp->base + STARFIVE_IE_MASK_OFFSET); +- +- reinit_completion(&cryp->pka_done); ++ return readl_relaxed_poll_timeout(cryp->base + STARFIVE_PKA_CASR_OFFSET, status, ++ status & STARFIVE_PKA_DONE, 10, 100000); + } + + static void starfive_rsa_free_key(struct starfive_rsa_key *key) +@@ -114,10 +97,9 @@ static int starfive_rsa_montgomery_form( + rctx->csr.pka.not_r2 = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + return -ETIMEDOUT; + + for (loop = 0; loop <= opsize; loop++) +@@ -136,10 +118,9 @@ static int starfive_rsa_montgomery_form( + rctx->csr.pka.start = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + return -ETIMEDOUT; + } else { + rctx->csr.pka.v = 0; +@@ -151,10 +132,9 @@ static int starfive_rsa_montgomery_form( + rctx->csr.pka.pre_expf = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + return -ETIMEDOUT; + + for (loop = 0; loop <= count; loop++) +@@ -172,10 +152,9 @@ static int starfive_rsa_montgomery_form( + rctx->csr.pka.start = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + return -ETIMEDOUT; + } + +@@ -226,11 +205,10 @@ static int starfive_rsa_cpu_start(struct + rctx->csr.pka.start = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + + ret = -ETIMEDOUT; +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + goto rsa_err; + + if (mlen) { +@@ -242,10 +220,9 @@ static int starfive_rsa_cpu_start(struct + rctx->csr.pka.start = 1; + rctx->csr.pka.ie = 1; + +- starfive_pka_irq_mask_clear(ctx); + writel(rctx->csr.pka.v, cryp->base + STARFIVE_PKA_CACR_OFFSET); + +- if (!starfive_pka_wait_done(ctx)) ++ if (starfive_pka_wait_done(ctx)) + goto rsa_err; + } + } diff --git a/target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch b/target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch new file mode 100644 index 0000000000..268e4055e0 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0011-crypto-starfive-Pad-adata-with-zeroes.patch @@ -0,0 +1,46 @@ +From eea9f2c55cf944bbd5cdd43eb655416a867846af Mon Sep 17 00:00:00 2001 +From: Jia Jie Ho +Date: Mon, 20 Nov 2023 11:12:42 +0800 +Subject: [PATCH 011/116] crypto: starfive - Pad adata with zeroes + +Aad requires padding with zeroes up to 15 bytes in some cases. This +patch increases the allocated buffer size for aad and prevents the +driver accessing uninitialized memory region. + +v1->v2: Specify reason for alloc size change in descriptions. + +Signed-off-by: Jia Jie Ho +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-aes.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-aes.c ++++ b/drivers/crypto/starfive/jh7110-aes.c +@@ -500,7 +500,7 @@ static int starfive_aes_prepare_req(stru + scatterwalk_start(&cryp->out_walk, rctx->out_sg); + + if (cryp->assoclen) { +- rctx->adata = kzalloc(ALIGN(cryp->assoclen, AES_BLOCK_SIZE), GFP_KERNEL); ++ rctx->adata = kzalloc(cryp->assoclen + AES_BLOCK_SIZE, GFP_KERNEL); + if (!rctx->adata) + return dev_err_probe(cryp->dev, -ENOMEM, + "Failed to alloc memory for adata"); +@@ -569,7 +569,7 @@ static int starfive_aes_aead_do_one_req( + struct starfive_cryp_ctx *ctx = + crypto_aead_ctx(crypto_aead_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; +- struct starfive_cryp_request_ctx *rctx = ctx->rctx; ++ struct starfive_cryp_request_ctx *rctx; + u32 block[AES_BLOCK_32]; + u32 stat; + int err; +@@ -579,6 +579,8 @@ static int starfive_aes_aead_do_one_req( + if (err) + return err; + ++ rctx = ctx->rctx; ++ + if (!cryp->assoclen) + goto write_text; + diff --git a/target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch b/target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch new file mode 100644 index 0000000000..f376edb60e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0012-crypto-starfive-Remove-cfb-and-ofb.patch @@ -0,0 +1,125 @@ +From 922b213ad22f93fb2788ce119084622ab3d25bf8 Mon Sep 17 00:00:00 2001 +From: Herbert Xu +Date: Thu, 30 Nov 2023 18:12:55 +0800 +Subject: [PATCH 012/116] crypto: starfive - Remove cfb and ofb + +Remove the unused CFB/OFB implementation. + +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-aes.c | 71 +-------------------------- + drivers/crypto/starfive/jh7110-cryp.h | 2 - + 2 files changed, 1 insertion(+), 72 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-aes.c ++++ b/drivers/crypto/starfive/jh7110-aes.c +@@ -262,12 +262,7 @@ static int starfive_aes_hw_init(struct s + rctx->csr.aes.mode = hw_mode; + rctx->csr.aes.cmode = !is_encrypt(cryp); + rctx->csr.aes.ie = 1; +- +- if (hw_mode == STARFIVE_AES_MODE_CFB || +- hw_mode == STARFIVE_AES_MODE_OFB) +- rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_128; +- else +- rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1; ++ rctx->csr.aes.stmode = STARFIVE_AES_MODE_XFB_1; + + if (cryp->side_chan) { + rctx->csr.aes.delay_aes = 1; +@@ -294,8 +289,6 @@ static int starfive_aes_hw_init(struct s + starfive_aes_ccm_init(ctx); + starfive_aes_aead_hw_start(ctx, hw_mode); + break; +- case STARFIVE_AES_MODE_OFB: +- case STARFIVE_AES_MODE_CFB: + case STARFIVE_AES_MODE_CBC: + case STARFIVE_AES_MODE_CTR: + starfive_aes_write_iv(ctx, (void *)cryp->req.sreq->iv); +@@ -785,26 +778,6 @@ static int starfive_aes_cbc_decrypt(stru + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CBC); + } + +-static int starfive_aes_cfb_encrypt(struct skcipher_request *req) +-{ +- return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB | FLG_ENCRYPT); +-} +- +-static int starfive_aes_cfb_decrypt(struct skcipher_request *req) +-{ +- return starfive_aes_crypt(req, STARFIVE_AES_MODE_CFB); +-} +- +-static int starfive_aes_ofb_encrypt(struct skcipher_request *req) +-{ +- return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB | FLG_ENCRYPT); +-} +- +-static int starfive_aes_ofb_decrypt(struct skcipher_request *req) +-{ +- return starfive_aes_crypt(req, STARFIVE_AES_MODE_OFB); +-} +- + static int starfive_aes_ctr_encrypt(struct skcipher_request *req) + { + return starfive_aes_crypt(req, STARFIVE_AES_MODE_CTR | FLG_ENCRYPT); +@@ -903,48 +876,6 @@ static struct skcipher_engine_alg skciph + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, +- .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 0xf, +- .cra_module = THIS_MODULE, +- }, +- .op = { +- .do_one_request = starfive_aes_do_one_req, +- }, +-}, { +- .base.init = starfive_aes_init_tfm, +- .base.setkey = starfive_aes_setkey, +- .base.encrypt = starfive_aes_cfb_encrypt, +- .base.decrypt = starfive_aes_cfb_decrypt, +- .base.min_keysize = AES_MIN_KEY_SIZE, +- .base.max_keysize = AES_MAX_KEY_SIZE, +- .base.ivsize = AES_BLOCK_SIZE, +- .base.base = { +- .cra_name = "cfb(aes)", +- .cra_driver_name = "starfive-cfb-aes", +- .cra_priority = 200, +- .cra_flags = CRYPTO_ALG_ASYNC, +- .cra_blocksize = 1, +- .cra_ctxsize = sizeof(struct starfive_cryp_ctx), +- .cra_alignmask = 0xf, +- .cra_module = THIS_MODULE, +- }, +- .op = { +- .do_one_request = starfive_aes_do_one_req, +- }, +-}, { +- .base.init = starfive_aes_init_tfm, +- .base.setkey = starfive_aes_setkey, +- .base.encrypt = starfive_aes_ofb_encrypt, +- .base.decrypt = starfive_aes_ofb_decrypt, +- .base.min_keysize = AES_MIN_KEY_SIZE, +- .base.max_keysize = AES_MAX_KEY_SIZE, +- .base.ivsize = AES_BLOCK_SIZE, +- .base.base = { +- .cra_name = "ofb(aes)", +- .cra_driver_name = "starfive-ofb-aes", +- .cra_priority = 200, +- .cra_flags = CRYPTO_ALG_ASYNC, +- .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 0xf, + .cra_module = THIS_MODULE, +--- a/drivers/crypto/starfive/jh7110-cryp.h ++++ b/drivers/crypto/starfive/jh7110-cryp.h +@@ -50,8 +50,6 @@ union starfive_aes_csr { + u32 ccm_start :1; + #define STARFIVE_AES_MODE_ECB 0x0 + #define STARFIVE_AES_MODE_CBC 0x1 +-#define STARFIVE_AES_MODE_CFB 0x2 +-#define STARFIVE_AES_MODE_OFB 0x3 + #define STARFIVE_AES_MODE_CTR 0x4 + #define STARFIVE_AES_MODE_CCM 0x5 + #define STARFIVE_AES_MODE_GCM 0x6 diff --git a/target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch b/target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch new file mode 100644 index 0000000000..2044b72a35 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0013-crypto-starfive-Remove-unneeded-NULL-checks.patch @@ -0,0 +1,33 @@ +From 0dbdc763f10c5cfa968dffc290a7c060ee740172 Mon Sep 17 00:00:00 2001 +From: Jia Jie Ho +Date: Mon, 4 Dec 2023 11:02:39 +0800 +Subject: [PATCH 013/116] crypto: starfive - Remove unneeded NULL checks + +NULL check before kfree_sensitive function is not needed. + +Signed-off-by: Jia Jie Ho +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202311301702.LxswfETY-lkp@intel.com/ +Signed-off-by: Herbert Xu +--- + drivers/crypto/starfive/jh7110-rsa.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/crypto/starfive/jh7110-rsa.c ++++ b/drivers/crypto/starfive/jh7110-rsa.c +@@ -45,12 +45,9 @@ static inline int starfive_pka_wait_done + + static void starfive_rsa_free_key(struct starfive_rsa_key *key) + { +- if (key->d) +- kfree_sensitive(key->d); +- if (key->e) +- kfree_sensitive(key->e); +- if (key->n) +- kfree_sensitive(key->n); ++ kfree_sensitive(key->d); ++ kfree_sensitive(key->e); ++ kfree_sensitive(key->n); + memset(key, 0, sizeof(*key)); + } + diff --git a/target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch b/target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch new file mode 100644 index 0000000000..430571565a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0014-dt-bindings-PCI-Add-PLDA-XpressRICH-PCIe-host-common.patch @@ -0,0 +1,183 @@ +From 708695ebf1a779de9a1fd2f72f7938afa6c14ada Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:51 +0800 +Subject: [PATCH 014/116] dt-bindings: PCI: Add PLDA XpressRICH PCIe host + common properties + +Add PLDA XpressRICH PCIe host common properties dt-binding doc. +PolarFire PCIe host using PLDA IP. Move common properties from Microchip +PolarFire PCIe host to PLDA files. + +Signed-off-by: Minda Chen +Reviewed-by: Hal Feng +Reviewed-by: Conor Dooley +Reviewed-by: Rob Herring +Tested-by: John Clark +--- + .../bindings/pci/microchip,pcie-host.yaml | 55 +------------- + .../pci/plda,xpressrich3-axi-common.yaml | 75 +++++++++++++++++++ + 2 files changed, 76 insertions(+), 54 deletions(-) + create mode 100644 Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml + +--- a/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml ++++ b/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml +@@ -10,21 +10,13 @@ maintainers: + - Daire McNamara + + allOf: +- - $ref: /schemas/pci/pci-bus.yaml# ++ - $ref: plda,xpressrich3-axi-common.yaml# + - $ref: /schemas/interrupt-controller/msi-controller.yaml# + + properties: + compatible: + const: microchip,pcie-host-1.0 # PolarFire + +- reg: +- maxItems: 2 +- +- reg-names: +- items: +- - const: cfg +- - const: apb +- + clocks: + description: + Fabric Interface Controllers, FICs, are the interface between the FPGA +@@ -52,18 +44,6 @@ properties: + items: + pattern: '^fic[0-3]$' + +- interrupts: +- minItems: 1 +- items: +- - description: PCIe host controller +- - description: builtin MSI controller +- +- interrupt-names: +- minItems: 1 +- items: +- - const: pcie +- - const: msi +- + ranges: + maxItems: 1 + +@@ -71,39 +51,6 @@ properties: + minItems: 1 + maxItems: 6 + +- msi-controller: +- description: Identifies the node as an MSI controller. +- +- msi-parent: +- description: MSI controller the device is capable of using. +- +- interrupt-controller: +- type: object +- properties: +- '#address-cells': +- const: 0 +- +- '#interrupt-cells': +- const: 1 +- +- interrupt-controller: true +- +- required: +- - '#address-cells' +- - '#interrupt-cells' +- - interrupt-controller +- +- additionalProperties: false +- +-required: +- - reg +- - reg-names +- - "#interrupt-cells" +- - interrupts +- - interrupt-map-mask +- - interrupt-map +- - msi-controller +- + unevaluatedProperties: false + + examples: +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml +@@ -0,0 +1,75 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/pci/plda,xpressrich3-axi-common.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: PLDA XpressRICH PCIe host common properties ++ ++maintainers: ++ - Daire McNamara ++ - Kevin Xie ++ ++description: ++ Generic PLDA XpressRICH PCIe host common properties. ++ ++allOf: ++ - $ref: /schemas/pci/pci-bus.yaml# ++ ++properties: ++ reg: ++ maxItems: 2 ++ ++ reg-names: ++ items: ++ - const: cfg ++ - const: apb ++ ++ interrupts: ++ minItems: 1 ++ items: ++ - description: PCIe host controller ++ - description: builtin MSI controller ++ ++ interrupt-names: ++ minItems: 1 ++ items: ++ - const: pcie ++ - const: msi ++ ++ msi-controller: ++ description: Identifies the node as an MSI controller. ++ ++ msi-parent: ++ description: MSI controller the device is capable of using. ++ ++ interrupt-controller: ++ type: object ++ properties: ++ '#address-cells': ++ const: 0 ++ ++ '#interrupt-cells': ++ const: 1 ++ ++ interrupt-controller: true ++ ++ required: ++ - '#address-cells' ++ - '#interrupt-cells' ++ - interrupt-controller ++ ++ additionalProperties: false ++ ++required: ++ - reg ++ - reg-names ++ - interrupts ++ - msi-controller ++ - "#interrupt-cells" ++ - interrupt-map-mask ++ - interrupt-map ++ ++additionalProperties: true ++ ++... diff --git a/target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch b/target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch new file mode 100644 index 0000000000..9e7717fa5d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0015-PCI-microchip-Move-pcie-microchip-host.c-to-plda-dir.patch @@ -0,0 +1,2523 @@ +From df67154aa92efdc774a8536ece6f431e7850aca2 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:52 +0800 +Subject: [PATCH 015/116] PCI: microchip: Move pcie-microchip-host.c to plda + directory + +For Microchip Polarfire PCIe host is PLDA XpressRich IP, move to plda +directory. Prepare for refactoring the codes. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + drivers/pci/controller/Kconfig | 9 +-------- + drivers/pci/controller/Makefile | 2 +- + drivers/pci/controller/plda/Kconfig | 14 ++++++++++++++ + drivers/pci/controller/plda/Makefile | 2 ++ + .../controller/{ => plda}/pcie-microchip-host.c | 2 +- + 5 files changed, 19 insertions(+), 10 deletions(-) + create mode 100644 drivers/pci/controller/plda/Kconfig + create mode 100644 drivers/pci/controller/plda/Makefile + rename drivers/pci/controller/{ => plda}/pcie-microchip-host.c (99%) + +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -215,14 +215,6 @@ config PCIE_MT7621 + help + This selects a driver for the MediaTek MT7621 PCIe Controller. + +-config PCIE_MICROCHIP_HOST +- tristate "Microchip AXI PCIe controller" +- depends on PCI_MSI && OF +- select PCI_HOST_COMMON +- help +- Say Y here if you want kernel to support the Microchip AXI PCIe +- Host Bridge driver. +- + config PCI_HYPERV_INTERFACE + tristate "Microsoft Hyper-V PCI Interface" + depends on ((X86 && X86_64) || ARM64) && HYPERV && PCI_MSI +@@ -345,4 +337,5 @@ config PCIE_XILINX_CPM + source "drivers/pci/controller/cadence/Kconfig" + source "drivers/pci/controller/dwc/Kconfig" + source "drivers/pci/controller/mobiveil/Kconfig" ++source "drivers/pci/controller/plda/Kconfig" + endmenu +--- a/drivers/pci/controller/Makefile ++++ b/drivers/pci/controller/Makefile +@@ -32,7 +32,6 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-r + obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o + obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o + obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie-mediatek-gen3.o +-obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o + obj-$(CONFIG_VMD) += vmd.o + obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o + obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o +@@ -43,6 +42,7 @@ obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621 + # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW + obj-y += dwc/ + obj-y += mobiveil/ ++obj-y += plda/ + + + # The following drivers are for devices that use the generic ACPI +--- /dev/null ++++ b/drivers/pci/controller/plda/Kconfig +@@ -0,0 +1,14 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++menu "PLDA-based PCIe controllers" ++ depends on PCI ++ ++config PCIE_MICROCHIP_HOST ++ tristate "Microchip AXI PCIe controller" ++ depends on PCI_MSI && OF ++ select PCI_HOST_COMMON ++ help ++ Say Y here if you want kernel to support the Microchip AXI PCIe ++ Host Bridge driver. ++ ++endmenu +--- /dev/null ++++ b/drivers/pci/controller/plda/Makefile +@@ -0,0 +1,2 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o +--- a/drivers/pci/controller/pcie-microchip-host.c ++++ /dev/null +@@ -1,1216 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Microchip AXI PCIe Bridge host controller driver +- * +- * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved. +- * +- * Author: Daire McNamara +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "../pci.h" +- +-/* Number of MSI IRQs */ +-#define MC_MAX_NUM_MSI_IRQS 32 +- +-/* PCIe Bridge Phy and Controller Phy offsets */ +-#define MC_PCIE1_BRIDGE_ADDR 0x00008000u +-#define MC_PCIE1_CTRL_ADDR 0x0000a000u +- +-#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) +-#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) +- +-/* PCIe Bridge Phy Regs */ +-#define PCIE_PCI_IRQ_DW0 0xa8 +-#define MSIX_CAP_MASK BIT(31) +-#define NUM_MSI_MSGS_MASK GENMASK(6, 4) +-#define NUM_MSI_MSGS_SHIFT 4 +- +-#define IMASK_LOCAL 0x180 +-#define DMA_END_ENGINE_0_MASK 0x00000000u +-#define DMA_END_ENGINE_0_SHIFT 0 +-#define DMA_END_ENGINE_1_MASK 0x00000000u +-#define DMA_END_ENGINE_1_SHIFT 1 +-#define DMA_ERROR_ENGINE_0_MASK 0x00000100u +-#define DMA_ERROR_ENGINE_0_SHIFT 8 +-#define DMA_ERROR_ENGINE_1_MASK 0x00000200u +-#define DMA_ERROR_ENGINE_1_SHIFT 9 +-#define A_ATR_EVT_POST_ERR_MASK 0x00010000u +-#define A_ATR_EVT_POST_ERR_SHIFT 16 +-#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u +-#define A_ATR_EVT_FETCH_ERR_SHIFT 17 +-#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u +-#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 +-#define A_ATR_EVT_DOORBELL_MASK 0x00000000u +-#define A_ATR_EVT_DOORBELL_SHIFT 19 +-#define P_ATR_EVT_POST_ERR_MASK 0x00100000u +-#define P_ATR_EVT_POST_ERR_SHIFT 20 +-#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u +-#define P_ATR_EVT_FETCH_ERR_SHIFT 21 +-#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u +-#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 +-#define P_ATR_EVT_DOORBELL_MASK 0x00000000u +-#define P_ATR_EVT_DOORBELL_SHIFT 23 +-#define PM_MSI_INT_INTA_MASK 0x01000000u +-#define PM_MSI_INT_INTA_SHIFT 24 +-#define PM_MSI_INT_INTB_MASK 0x02000000u +-#define PM_MSI_INT_INTB_SHIFT 25 +-#define PM_MSI_INT_INTC_MASK 0x04000000u +-#define PM_MSI_INT_INTC_SHIFT 26 +-#define PM_MSI_INT_INTD_MASK 0x08000000u +-#define PM_MSI_INT_INTD_SHIFT 27 +-#define PM_MSI_INT_INTX_MASK 0x0f000000u +-#define PM_MSI_INT_INTX_SHIFT 24 +-#define PM_MSI_INT_MSI_MASK 0x10000000u +-#define PM_MSI_INT_MSI_SHIFT 28 +-#define PM_MSI_INT_AER_EVT_MASK 0x20000000u +-#define PM_MSI_INT_AER_EVT_SHIFT 29 +-#define PM_MSI_INT_EVENTS_MASK 0x40000000u +-#define PM_MSI_INT_EVENTS_SHIFT 30 +-#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u +-#define PM_MSI_INT_SYS_ERR_SHIFT 31 +-#define NUM_LOCAL_EVENTS 15 +-#define ISTATUS_LOCAL 0x184 +-#define IMASK_HOST 0x188 +-#define ISTATUS_HOST 0x18c +-#define IMSI_ADDR 0x190 +-#define ISTATUS_MSI 0x194 +- +-/* PCIe Master table init defines */ +-#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u +-#define ATR0_PCIE_ATR_SIZE 0x25 +-#define ATR0_PCIE_ATR_SIZE_SHIFT 1 +-#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u +-#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u +-#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu +-#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u +- +-/* PCIe AXI slave table init defines */ +-#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u +-#define ATR_SIZE_SHIFT 1 +-#define ATR_IMPL_ENABLE 1 +-#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u +-#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u +-#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu +-#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u +-#define PCIE_TX_RX_INTERFACE 0x00000000u +-#define PCIE_CONFIG_INTERFACE 0x00000001u +- +-#define ATR_ENTRY_SIZE 32 +- +-/* PCIe Controller Phy Regs */ +-#define SEC_ERROR_EVENT_CNT 0x20 +-#define DED_ERROR_EVENT_CNT 0x24 +-#define SEC_ERROR_INT 0x28 +-#define SEC_ERROR_INT_TX_RAM_SEC_ERR_INT GENMASK(3, 0) +-#define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4) +-#define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8) +-#define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12) +-#define SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT GENMASK(15, 0) +-#define NUM_SEC_ERROR_INTS (4) +-#define SEC_ERROR_INT_MASK 0x2c +-#define DED_ERROR_INT 0x30 +-#define DED_ERROR_INT_TX_RAM_DED_ERR_INT GENMASK(3, 0) +-#define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4) +-#define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8) +-#define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12) +-#define DED_ERROR_INT_ALL_RAM_DED_ERR_INT GENMASK(15, 0) +-#define NUM_DED_ERROR_INTS (4) +-#define DED_ERROR_INT_MASK 0x34 +-#define ECC_CONTROL 0x38 +-#define ECC_CONTROL_TX_RAM_INJ_ERROR_0 BIT(0) +-#define ECC_CONTROL_TX_RAM_INJ_ERROR_1 BIT(1) +-#define ECC_CONTROL_TX_RAM_INJ_ERROR_2 BIT(2) +-#define ECC_CONTROL_TX_RAM_INJ_ERROR_3 BIT(3) +-#define ECC_CONTROL_RX_RAM_INJ_ERROR_0 BIT(4) +-#define ECC_CONTROL_RX_RAM_INJ_ERROR_1 BIT(5) +-#define ECC_CONTROL_RX_RAM_INJ_ERROR_2 BIT(6) +-#define ECC_CONTROL_RX_RAM_INJ_ERROR_3 BIT(7) +-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8) +-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9) +-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10) +-#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11) +-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12) +-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13) +-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14) +-#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15) +-#define ECC_CONTROL_TX_RAM_ECC_BYPASS BIT(24) +-#define ECC_CONTROL_RX_RAM_ECC_BYPASS BIT(25) +-#define ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS BIT(26) +-#define ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS BIT(27) +-#define PCIE_EVENT_INT 0x14c +-#define PCIE_EVENT_INT_L2_EXIT_INT BIT(0) +-#define PCIE_EVENT_INT_HOTRST_EXIT_INT BIT(1) +-#define PCIE_EVENT_INT_DLUP_EXIT_INT BIT(2) +-#define PCIE_EVENT_INT_MASK GENMASK(2, 0) +-#define PCIE_EVENT_INT_L2_EXIT_INT_MASK BIT(16) +-#define PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK BIT(17) +-#define PCIE_EVENT_INT_DLUP_EXIT_INT_MASK BIT(18) +-#define PCIE_EVENT_INT_ENB_MASK GENMASK(18, 16) +-#define PCIE_EVENT_INT_ENB_SHIFT 16 +-#define NUM_PCIE_EVENTS (3) +- +-/* PCIe Config space MSI capability structure */ +-#define MC_MSI_CAP_CTRL_OFFSET 0xe0u +- +-/* Events */ +-#define EVENT_PCIE_L2_EXIT 0 +-#define EVENT_PCIE_HOTRST_EXIT 1 +-#define EVENT_PCIE_DLUP_EXIT 2 +-#define EVENT_SEC_TX_RAM_SEC_ERR 3 +-#define EVENT_SEC_RX_RAM_SEC_ERR 4 +-#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR 5 +-#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR 6 +-#define EVENT_DED_TX_RAM_DED_ERR 7 +-#define EVENT_DED_RX_RAM_DED_ERR 8 +-#define EVENT_DED_PCIE2AXI_RAM_DED_ERR 9 +-#define EVENT_DED_AXI2PCIE_RAM_DED_ERR 10 +-#define EVENT_LOCAL_DMA_END_ENGINE_0 11 +-#define EVENT_LOCAL_DMA_END_ENGINE_1 12 +-#define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13 +-#define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14 +-#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15 +-#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16 +-#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17 +-#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18 +-#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19 +-#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20 +-#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21 +-#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22 +-#define EVENT_LOCAL_PM_MSI_INT_INTX 23 +-#define EVENT_LOCAL_PM_MSI_INT_MSI 24 +-#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25 +-#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26 +-#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27 +-#define NUM_EVENTS 28 +- +-#define PCIE_EVENT_CAUSE(x, s) \ +- [EVENT_PCIE_ ## x] = { __stringify(x), s } +- +-#define SEC_ERROR_CAUSE(x, s) \ +- [EVENT_SEC_ ## x] = { __stringify(x), s } +- +-#define DED_ERROR_CAUSE(x, s) \ +- [EVENT_DED_ ## x] = { __stringify(x), s } +- +-#define LOCAL_EVENT_CAUSE(x, s) \ +- [EVENT_LOCAL_ ## x] = { __stringify(x), s } +- +-#define PCIE_EVENT(x) \ +- .base = MC_PCIE_CTRL_ADDR, \ +- .offset = PCIE_EVENT_INT, \ +- .mask_offset = PCIE_EVENT_INT, \ +- .mask_high = 1, \ +- .mask = PCIE_EVENT_INT_ ## x ## _INT, \ +- .enb_mask = PCIE_EVENT_INT_ENB_MASK +- +-#define SEC_EVENT(x) \ +- .base = MC_PCIE_CTRL_ADDR, \ +- .offset = SEC_ERROR_INT, \ +- .mask_offset = SEC_ERROR_INT_MASK, \ +- .mask = SEC_ERROR_INT_ ## x ## _INT, \ +- .mask_high = 1, \ +- .enb_mask = 0 +- +-#define DED_EVENT(x) \ +- .base = MC_PCIE_CTRL_ADDR, \ +- .offset = DED_ERROR_INT, \ +- .mask_offset = DED_ERROR_INT_MASK, \ +- .mask_high = 1, \ +- .mask = DED_ERROR_INT_ ## x ## _INT, \ +- .enb_mask = 0 +- +-#define LOCAL_EVENT(x) \ +- .base = MC_PCIE_BRIDGE_ADDR, \ +- .offset = ISTATUS_LOCAL, \ +- .mask_offset = IMASK_LOCAL, \ +- .mask_high = 0, \ +- .mask = x ## _MASK, \ +- .enb_mask = 0 +- +-#define PCIE_EVENT_TO_EVENT_MAP(x) \ +- { PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x } +- +-#define SEC_ERROR_TO_EVENT_MAP(x) \ +- { SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x } +- +-#define DED_ERROR_TO_EVENT_MAP(x) \ +- { DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x } +- +-#define LOCAL_STATUS_TO_EVENT_MAP(x) \ +- { x ## _MASK, EVENT_LOCAL_ ## x } +- +-struct event_map { +- u32 reg_mask; +- u32 event_bit; +-}; +- +-struct mc_msi { +- struct mutex lock; /* Protect used bitmap */ +- struct irq_domain *msi_domain; +- struct irq_domain *dev_domain; +- u32 num_vectors; +- u64 vector_phy; +- DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); +-}; +- +-struct mc_pcie { +- void __iomem *axi_base_addr; +- struct device *dev; +- struct irq_domain *intx_domain; +- struct irq_domain *event_domain; +- raw_spinlock_t lock; +- struct mc_msi msi; +-}; +- +-struct cause { +- const char *sym; +- const char *str; +-}; +- +-static const struct cause event_cause[NUM_EVENTS] = { +- PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"), +- PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"), +- PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"), +- SEC_ERROR_CAUSE(TX_RAM_SEC_ERR, "sec error in tx buffer"), +- SEC_ERROR_CAUSE(RX_RAM_SEC_ERR, "sec error in rx buffer"), +- SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR, "sec error in pcie2axi buffer"), +- SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR, "sec error in axi2pcie buffer"), +- DED_ERROR_CAUSE(TX_RAM_DED_ERR, "ded error in tx buffer"), +- DED_ERROR_CAUSE(RX_RAM_DED_ERR, "ded error in rx buffer"), +- DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR, "ded error in pcie2axi buffer"), +- DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR, "ded error in axi2pcie buffer"), +- LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"), +- LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"), +- LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"), +- LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"), +- LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"), +- LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"), +- LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"), +- LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"), +- LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"), +- LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"), +- LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"), +-}; +- +-static struct event_map pcie_event_to_event[] = { +- PCIE_EVENT_TO_EVENT_MAP(L2_EXIT), +- PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT), +- PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT), +-}; +- +-static struct event_map sec_error_to_event[] = { +- SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR), +- SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR), +- SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR), +- SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR), +-}; +- +-static struct event_map ded_error_to_event[] = { +- DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR), +- DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR), +- DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR), +- DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR), +-}; +- +-static struct event_map local_status_to_event[] = { +- LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0), +- LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1), +- LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0), +- LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1), +- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL), +- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR), +- LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS), +- LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR), +-}; +- +-static struct { +- u32 base; +- u32 offset; +- u32 mask; +- u32 shift; +- u32 enb_mask; +- u32 mask_high; +- u32 mask_offset; +-} event_descs[] = { +- { PCIE_EVENT(L2_EXIT) }, +- { PCIE_EVENT(HOTRST_EXIT) }, +- { PCIE_EVENT(DLUP_EXIT) }, +- { SEC_EVENT(TX_RAM_SEC_ERR) }, +- { SEC_EVENT(RX_RAM_SEC_ERR) }, +- { SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) }, +- { SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) }, +- { DED_EVENT(TX_RAM_DED_ERR) }, +- { DED_EVENT(RX_RAM_DED_ERR) }, +- { DED_EVENT(PCIE2AXI_RAM_DED_ERR) }, +- { DED_EVENT(AXI2PCIE_RAM_DED_ERR) }, +- { LOCAL_EVENT(DMA_END_ENGINE_0) }, +- { LOCAL_EVENT(DMA_END_ENGINE_1) }, +- { LOCAL_EVENT(DMA_ERROR_ENGINE_0) }, +- { LOCAL_EVENT(DMA_ERROR_ENGINE_1) }, +- { LOCAL_EVENT(A_ATR_EVT_POST_ERR) }, +- { LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) }, +- { LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) }, +- { LOCAL_EVENT(A_ATR_EVT_DOORBELL) }, +- { LOCAL_EVENT(P_ATR_EVT_POST_ERR) }, +- { LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) }, +- { LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) }, +- { LOCAL_EVENT(P_ATR_EVT_DOORBELL) }, +- { LOCAL_EVENT(PM_MSI_INT_INTX) }, +- { LOCAL_EVENT(PM_MSI_INT_MSI) }, +- { LOCAL_EVENT(PM_MSI_INT_AER_EVT) }, +- { LOCAL_EVENT(PM_MSI_INT_EVENTS) }, +- { LOCAL_EVENT(PM_MSI_INT_SYS_ERR) }, +-}; +- +-static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" }; +- +-static struct mc_pcie *port; +- +-static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) +-{ +- struct mc_msi *msi = &port->msi; +- u16 reg; +- u8 queue_size; +- +- /* Fixup MSI enable flag */ +- reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); +- reg |= PCI_MSI_FLAGS_ENABLE; +- writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); +- +- /* Fixup PCI MSI queue flags */ +- queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg); +- reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size); +- writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); +- +- /* Fixup MSI addr fields */ +- writel_relaxed(lower_32_bits(msi->vector_phy), +- ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO); +- writel_relaxed(upper_32_bits(msi->vector_phy), +- ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); +-} +- +-static void mc_handle_msi(struct irq_desc *desc) +-{ +- struct mc_pcie *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct device *dev = port->dev; +- struct mc_msi *msi = &port->msi; +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- unsigned long status; +- u32 bit; +- int ret; +- +- chained_irq_enter(chip, desc); +- +- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- if (status & PM_MSI_INT_MSI_MASK) { +- writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); +- status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); +- for_each_set_bit(bit, &status, msi->num_vectors) { +- ret = generic_handle_domain_irq(msi->dev_domain, bit); +- if (ret) +- dev_err_ratelimited(dev, "bad MSI IRQ %d\n", +- bit); +- } +- } +- +- chained_irq_exit(chip, desc); +-} +- +-static void mc_msi_bottom_irq_ack(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- u32 bitpos = data->hwirq; +- +- writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); +-} +- +-static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- phys_addr_t addr = port->msi.vector_phy; +- +- msg->address_lo = lower_32_bits(addr); +- msg->address_hi = upper_32_bits(addr); +- msg->data = data->hwirq; +- +- dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", +- (int)data->hwirq, msg->address_hi, msg->address_lo); +-} +- +-static int mc_msi_set_affinity(struct irq_data *irq_data, +- const struct cpumask *mask, bool force) +-{ +- return -EINVAL; +-} +- +-static struct irq_chip mc_msi_bottom_irq_chip = { +- .name = "Microchip MSI", +- .irq_ack = mc_msi_bottom_irq_ack, +- .irq_compose_msi_msg = mc_compose_msi_msg, +- .irq_set_affinity = mc_msi_set_affinity, +-}; +- +-static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, +- unsigned int nr_irqs, void *args) +-{ +- struct mc_pcie *port = domain->host_data; +- struct mc_msi *msi = &port->msi; +- unsigned long bit; +- +- mutex_lock(&msi->lock); +- bit = find_first_zero_bit(msi->used, msi->num_vectors); +- if (bit >= msi->num_vectors) { +- mutex_unlock(&msi->lock); +- return -ENOSPC; +- } +- +- set_bit(bit, msi->used); +- +- irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, +- domain->host_data, handle_edge_irq, NULL, NULL); +- +- mutex_unlock(&msi->lock); +- +- return 0; +-} +- +-static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, +- unsigned int nr_irqs) +-{ +- struct irq_data *d = irq_domain_get_irq_data(domain, virq); +- struct mc_pcie *port = irq_data_get_irq_chip_data(d); +- struct mc_msi *msi = &port->msi; +- +- mutex_lock(&msi->lock); +- +- if (test_bit(d->hwirq, msi->used)) +- __clear_bit(d->hwirq, msi->used); +- else +- dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); +- +- mutex_unlock(&msi->lock); +-} +- +-static const struct irq_domain_ops msi_domain_ops = { +- .alloc = mc_irq_msi_domain_alloc, +- .free = mc_irq_msi_domain_free, +-}; +- +-static struct irq_chip mc_msi_irq_chip = { +- .name = "Microchip PCIe MSI", +- .irq_ack = irq_chip_ack_parent, +- .irq_mask = pci_msi_mask_irq, +- .irq_unmask = pci_msi_unmask_irq, +-}; +- +-static struct msi_domain_info mc_msi_domain_info = { +- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | +- MSI_FLAG_PCI_MSIX), +- .chip = &mc_msi_irq_chip, +-}; +- +-static int mc_allocate_msi_domains(struct mc_pcie *port) +-{ +- struct device *dev = port->dev; +- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); +- struct mc_msi *msi = &port->msi; +- +- mutex_init(&port->msi.lock); +- +- msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, +- &msi_domain_ops, port); +- if (!msi->dev_domain) { +- dev_err(dev, "failed to create IRQ domain\n"); +- return -ENOMEM; +- } +- +- msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, +- msi->dev_domain); +- if (!msi->msi_domain) { +- dev_err(dev, "failed to create MSI domain\n"); +- irq_domain_remove(msi->dev_domain); +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static void mc_handle_intx(struct irq_desc *desc) +-{ +- struct mc_pcie *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct device *dev = port->dev; +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- unsigned long status; +- u32 bit; +- int ret; +- +- chained_irq_enter(chip, desc); +- +- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- if (status & PM_MSI_INT_INTX_MASK) { +- status &= PM_MSI_INT_INTX_MASK; +- status >>= PM_MSI_INT_INTX_SHIFT; +- for_each_set_bit(bit, &status, PCI_NUM_INTX) { +- ret = generic_handle_domain_irq(port->intx_domain, bit); +- if (ret) +- dev_err_ratelimited(dev, "bad INTx IRQ %d\n", +- bit); +- } +- } +- +- chained_irq_exit(chip, desc); +-} +- +-static void mc_ack_intx_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- +- writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); +-} +- +-static void mc_mask_intx_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- unsigned long flags; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- u32 val; +- +- raw_spin_lock_irqsave(&port->lock, flags); +- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); +- val &= ~mask; +- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +- raw_spin_unlock_irqrestore(&port->lock, flags); +-} +- +-static void mc_unmask_intx_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- unsigned long flags; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- u32 val; +- +- raw_spin_lock_irqsave(&port->lock, flags); +- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); +- val |= mask; +- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +- raw_spin_unlock_irqrestore(&port->lock, flags); +-} +- +-static struct irq_chip mc_intx_irq_chip = { +- .name = "Microchip PCIe INTx", +- .irq_ack = mc_ack_intx_irq, +- .irq_mask = mc_mask_intx_irq, +- .irq_unmask = mc_unmask_intx_irq, +-}; +- +-static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) +-{ +- irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, domain->host_data); +- +- return 0; +-} +- +-static const struct irq_domain_ops intx_domain_ops = { +- .map = mc_pcie_intx_map, +-}; +- +-static inline u32 reg_to_event(u32 reg, struct event_map field) +-{ +- return (reg & field.reg_mask) ? BIT(field.event_bit) : 0; +-} +- +-static u32 pcie_events(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT); +- u32 val = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++) +- val |= reg_to_event(reg, pcie_event_to_event[i]); +- +- return val; +-} +- +-static u32 sec_errors(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT); +- u32 val = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++) +- val |= reg_to_event(reg, sec_error_to_event[i]); +- +- return val; +-} +- +-static u32 ded_errors(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT); +- u32 val = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++) +- val |= reg_to_event(reg, ded_error_to_event[i]); +- +- return val; +-} +- +-static u32 local_events(struct mc_pcie *port) +-{ +- void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- u32 val = 0; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++) +- val |= reg_to_event(reg, local_status_to_event[i]); +- +- return val; +-} +- +-static u32 get_events(struct mc_pcie *port) +-{ +- u32 events = 0; +- +- events |= pcie_events(port); +- events |= sec_errors(port); +- events |= ded_errors(port); +- events |= local_events(port); +- +- return events; +-} +- +-static irqreturn_t mc_event_handler(int irq, void *dev_id) +-{ +- struct mc_pcie *port = dev_id; +- struct device *dev = port->dev; +- struct irq_data *data; +- +- data = irq_domain_get_irq_data(port->event_domain, irq); +- +- if (event_cause[data->hwirq].str) +- dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str); +- else +- dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq); +- +- return IRQ_HANDLED; +-} +- +-static void mc_handle_event(struct irq_desc *desc) +-{ +- struct mc_pcie *port = irq_desc_get_handler_data(desc); +- unsigned long events; +- u32 bit; +- struct irq_chip *chip = irq_desc_get_chip(desc); +- +- chained_irq_enter(chip, desc); +- +- events = get_events(port); +- +- for_each_set_bit(bit, &events, NUM_EVENTS) +- generic_handle_domain_irq(port->event_domain, bit); +- +- chained_irq_exit(chip, desc); +-} +- +-static void mc_ack_event_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- u32 event = data->hwirq; +- void __iomem *addr; +- u32 mask; +- +- addr = port->axi_base_addr + event_descs[event].base + +- event_descs[event].offset; +- mask = event_descs[event].mask; +- mask |= event_descs[event].enb_mask; +- +- writel_relaxed(mask, addr); +-} +- +-static void mc_mask_event_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- u32 event = data->hwirq; +- void __iomem *addr; +- u32 mask; +- u32 val; +- +- addr = port->axi_base_addr + event_descs[event].base + +- event_descs[event].mask_offset; +- mask = event_descs[event].mask; +- if (event_descs[event].enb_mask) { +- mask <<= PCIE_EVENT_INT_ENB_SHIFT; +- mask &= PCIE_EVENT_INT_ENB_MASK; +- } +- +- if (!event_descs[event].mask_high) +- mask = ~mask; +- +- raw_spin_lock(&port->lock); +- val = readl_relaxed(addr); +- if (event_descs[event].mask_high) +- val |= mask; +- else +- val &= mask; +- +- writel_relaxed(val, addr); +- raw_spin_unlock(&port->lock); +-} +- +-static void mc_unmask_event_irq(struct irq_data *data) +-{ +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- u32 event = data->hwirq; +- void __iomem *addr; +- u32 mask; +- u32 val; +- +- addr = port->axi_base_addr + event_descs[event].base + +- event_descs[event].mask_offset; +- mask = event_descs[event].mask; +- +- if (event_descs[event].enb_mask) +- mask <<= PCIE_EVENT_INT_ENB_SHIFT; +- +- if (event_descs[event].mask_high) +- mask = ~mask; +- +- if (event_descs[event].enb_mask) +- mask &= PCIE_EVENT_INT_ENB_MASK; +- +- raw_spin_lock(&port->lock); +- val = readl_relaxed(addr); +- if (event_descs[event].mask_high) +- val &= mask; +- else +- val |= mask; +- writel_relaxed(val, addr); +- raw_spin_unlock(&port->lock); +-} +- +-static struct irq_chip mc_event_irq_chip = { +- .name = "Microchip PCIe EVENT", +- .irq_ack = mc_ack_event_irq, +- .irq_mask = mc_mask_event_irq, +- .irq_unmask = mc_unmask_event_irq, +-}; +- +-static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) +-{ +- irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, domain->host_data); +- +- return 0; +-} +- +-static const struct irq_domain_ops event_domain_ops = { +- .map = mc_pcie_event_map, +-}; +- +-static inline void mc_pcie_deinit_clk(void *data) +-{ +- struct clk *clk = data; +- +- clk_disable_unprepare(clk); +-} +- +-static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id) +-{ +- struct clk *clk; +- int ret; +- +- clk = devm_clk_get_optional(dev, id); +- if (IS_ERR(clk)) +- return clk; +- if (!clk) +- return clk; +- +- ret = clk_prepare_enable(clk); +- if (ret) +- return ERR_PTR(ret); +- +- devm_add_action_or_reset(dev, mc_pcie_deinit_clk, clk); +- +- return clk; +-} +- +-static int mc_pcie_init_clks(struct device *dev) +-{ +- int i; +- struct clk *fic; +- +- /* +- * PCIe may be clocked via Fabric Interface using between 1 and 4 +- * clocks. Scan DT for clocks and enable them if present +- */ +- for (i = 0; i < ARRAY_SIZE(poss_clks); i++) { +- fic = mc_pcie_init_clk(dev, poss_clks[i]); +- if (IS_ERR(fic)) +- return PTR_ERR(fic); +- } +- +- return 0; +-} +- +-static int mc_pcie_init_irq_domains(struct mc_pcie *port) +-{ +- struct device *dev = port->dev; +- struct device_node *node = dev->of_node; +- struct device_node *pcie_intc_node; +- +- /* Setup INTx */ +- pcie_intc_node = of_get_next_child(node, NULL); +- if (!pcie_intc_node) { +- dev_err(dev, "failed to find PCIe Intc node\n"); +- return -EINVAL; +- } +- +- port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, +- &event_domain_ops, port); +- if (!port->event_domain) { +- dev_err(dev, "failed to get event domain\n"); +- of_node_put(pcie_intc_node); +- return -ENOMEM; +- } +- +- irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); +- +- port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, +- &intx_domain_ops, port); +- if (!port->intx_domain) { +- dev_err(dev, "failed to get an INTx IRQ domain\n"); +- of_node_put(pcie_intc_node); +- return -ENOMEM; +- } +- +- irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); +- +- of_node_put(pcie_intc_node); +- raw_spin_lock_init(&port->lock); +- +- return mc_allocate_msi_domains(port); +-} +- +-static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, +- phys_addr_t axi_addr, phys_addr_t pci_addr, +- size_t size) +-{ +- u32 atr_sz = ilog2(size) - 1; +- u32 val; +- +- if (index == 0) +- val = PCIE_CONFIG_INTERFACE; +- else +- val = PCIE_TX_RX_INTERFACE; +- +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_PARAM); +- +- val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | +- ATR_IMPL_ENABLE; +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_SRCADDR_PARAM); +- +- val = upper_32_bits(axi_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_SRC_ADDR); +- +- val = lower_32_bits(pci_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_ADDR_LSB); +- +- val = upper_32_bits(pci_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_ADDR_UDW); +- +- val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); +- val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); +- writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); +- writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); +-} +- +-static int mc_pcie_setup_windows(struct platform_device *pdev, +- struct mc_pcie *port) +-{ +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- struct pci_host_bridge *bridge = platform_get_drvdata(pdev); +- struct resource_entry *entry; +- u64 pci_addr; +- u32 index = 1; +- +- resource_list_for_each_entry(entry, &bridge->windows) { +- if (resource_type(entry->res) == IORESOURCE_MEM) { +- pci_addr = entry->res->start - entry->offset; +- mc_pcie_setup_window(bridge_base_addr, index, +- entry->res->start, pci_addr, +- resource_size(entry->res)); +- index++; +- } +- } +- +- return 0; +-} +- +-static inline void mc_clear_secs(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- +- writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + +- SEC_ERROR_INT); +- writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); +-} +- +-static inline void mc_clear_deds(struct mc_pcie *port) +-{ +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- +- writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + +- DED_ERROR_INT); +- writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); +-} +- +-static void mc_disable_interrupts(struct mc_pcie *port) +-{ +- void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +- u32 val; +- +- /* Ensure ECC bypass is enabled */ +- val = ECC_CONTROL_TX_RAM_ECC_BYPASS | +- ECC_CONTROL_RX_RAM_ECC_BYPASS | +- ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS | +- ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS; +- writel_relaxed(val, ctrl_base_addr + ECC_CONTROL); +- +- /* Disable SEC errors and clear any outstanding */ +- writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + +- SEC_ERROR_INT_MASK); +- mc_clear_secs(port); +- +- /* Disable DED errors and clear any outstanding */ +- writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + +- DED_ERROR_INT_MASK); +- mc_clear_deds(port); +- +- /* Disable local interrupts and clear any outstanding */ +- writel_relaxed(0, bridge_base_addr + IMASK_LOCAL); +- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL); +- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI); +- +- /* Disable PCIe events and clear any outstanding */ +- val = PCIE_EVENT_INT_L2_EXIT_INT | +- PCIE_EVENT_INT_HOTRST_EXIT_INT | +- PCIE_EVENT_INT_DLUP_EXIT_INT | +- PCIE_EVENT_INT_L2_EXIT_INT_MASK | +- PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK | +- PCIE_EVENT_INT_DLUP_EXIT_INT_MASK; +- writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT); +- +- /* Disable host interrupts and clear any outstanding */ +- writel_relaxed(0, bridge_base_addr + IMASK_HOST); +- writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); +-} +- +-static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) +-{ +- struct device *dev = &pdev->dev; +- int irq; +- int i, intx_irq, msi_irq, event_irq; +- int ret; +- +- ret = mc_pcie_init_irq_domains(port); +- if (ret) { +- dev_err(dev, "failed creating IRQ domains\n"); +- return ret; +- } +- +- irq = platform_get_irq(pdev, 0); +- if (irq < 0) +- return -ENODEV; +- +- for (i = 0; i < NUM_EVENTS; i++) { +- event_irq = irq_create_mapping(port->event_domain, i); +- if (!event_irq) { +- dev_err(dev, "failed to map hwirq %d\n", i); +- return -ENXIO; +- } +- +- ret = devm_request_irq(dev, event_irq, mc_event_handler, +- 0, event_cause[i].sym, port); +- if (ret) { +- dev_err(dev, "failed to request IRQ %d\n", event_irq); +- return ret; +- } +- } +- +- intx_irq = irq_create_mapping(port->event_domain, +- EVENT_LOCAL_PM_MSI_INT_INTX); +- if (!intx_irq) { +- dev_err(dev, "failed to map INTx interrupt\n"); +- return -ENXIO; +- } +- +- /* Plug the INTx chained handler */ +- irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port); +- +- msi_irq = irq_create_mapping(port->event_domain, +- EVENT_LOCAL_PM_MSI_INT_MSI); +- if (!msi_irq) +- return -ENXIO; +- +- /* Plug the MSI chained handler */ +- irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port); +- +- /* Plug the main event chained handler */ +- irq_set_chained_handler_and_data(irq, mc_handle_event, port); +- +- return 0; +-} +- +-static int mc_platform_init(struct pci_config_window *cfg) +-{ +- struct device *dev = cfg->parent; +- struct platform_device *pdev = to_platform_device(dev); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- int ret; +- +- /* Configure address translation table 0 for PCIe config space */ +- mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, +- cfg->res.start, +- resource_size(&cfg->res)); +- +- /* Need some fixups in config space */ +- mc_pcie_enable_msi(port, cfg->win); +- +- /* Configure non-config space outbound ranges */ +- ret = mc_pcie_setup_windows(pdev, port); +- if (ret) +- return ret; +- +- /* Address translation is up; safe to enable interrupts */ +- ret = mc_init_interrupts(pdev, port); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int mc_host_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- void __iomem *bridge_base_addr; +- int ret; +- u32 val; +- +- port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); +- if (!port) +- return -ENOMEM; +- +- port->dev = dev; +- +- port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); +- if (IS_ERR(port->axi_base_addr)) +- return PTR_ERR(port->axi_base_addr); +- +- mc_disable_interrupts(port); +- +- bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- +- /* Allow enabling MSI by disabling MSI-X */ +- val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); +- val &= ~MSIX_CAP_MASK; +- writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0); +- +- /* Pick num vectors from bitfile programmed onto FPGA fabric */ +- val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); +- val &= NUM_MSI_MSGS_MASK; +- val >>= NUM_MSI_MSGS_SHIFT; +- +- port->msi.num_vectors = 1 << val; +- +- /* Pick vector address from design */ +- port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); +- +- ret = mc_pcie_init_clks(dev); +- if (ret) { +- dev_err(dev, "failed to get clock resources, error %d\n", ret); +- return -ENODEV; +- } +- +- return pci_host_common_probe(pdev); +-} +- +-static const struct pci_ecam_ops mc_ecam_ops = { +- .init = mc_platform_init, +- .pci_ops = { +- .map_bus = pci_ecam_map_bus, +- .read = pci_generic_config_read, +- .write = pci_generic_config_write, +- } +-}; +- +-static const struct of_device_id mc_pcie_of_match[] = { +- { +- .compatible = "microchip,pcie-host-1.0", +- .data = &mc_ecam_ops, +- }, +- {}, +-}; +- +-MODULE_DEVICE_TABLE(of, mc_pcie_of_match); +- +-static struct platform_driver mc_pcie_driver = { +- .probe = mc_host_probe, +- .driver = { +- .name = "microchip-pcie", +- .of_match_table = mc_pcie_of_match, +- .suppress_bind_attrs = true, +- }, +-}; +- +-builtin_platform_driver(mc_pcie_driver); +-MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION("Microchip PCIe host controller driver"); +-MODULE_AUTHOR("Daire McNamara "); +--- /dev/null ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -0,0 +1,1216 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Microchip AXI PCIe Bridge host controller driver ++ * ++ * Copyright (c) 2018 - 2020 Microchip Corporation. All rights reserved. ++ * ++ * Author: Daire McNamara ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../../pci.h" ++ ++/* Number of MSI IRQs */ ++#define MC_MAX_NUM_MSI_IRQS 32 ++ ++/* PCIe Bridge Phy and Controller Phy offsets */ ++#define MC_PCIE1_BRIDGE_ADDR 0x00008000u ++#define MC_PCIE1_CTRL_ADDR 0x0000a000u ++ ++#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) ++#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) ++ ++/* PCIe Bridge Phy Regs */ ++#define PCIE_PCI_IRQ_DW0 0xa8 ++#define MSIX_CAP_MASK BIT(31) ++#define NUM_MSI_MSGS_MASK GENMASK(6, 4) ++#define NUM_MSI_MSGS_SHIFT 4 ++ ++#define IMASK_LOCAL 0x180 ++#define DMA_END_ENGINE_0_MASK 0x00000000u ++#define DMA_END_ENGINE_0_SHIFT 0 ++#define DMA_END_ENGINE_1_MASK 0x00000000u ++#define DMA_END_ENGINE_1_SHIFT 1 ++#define DMA_ERROR_ENGINE_0_MASK 0x00000100u ++#define DMA_ERROR_ENGINE_0_SHIFT 8 ++#define DMA_ERROR_ENGINE_1_MASK 0x00000200u ++#define DMA_ERROR_ENGINE_1_SHIFT 9 ++#define A_ATR_EVT_POST_ERR_MASK 0x00010000u ++#define A_ATR_EVT_POST_ERR_SHIFT 16 ++#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u ++#define A_ATR_EVT_FETCH_ERR_SHIFT 17 ++#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u ++#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 ++#define A_ATR_EVT_DOORBELL_MASK 0x00000000u ++#define A_ATR_EVT_DOORBELL_SHIFT 19 ++#define P_ATR_EVT_POST_ERR_MASK 0x00100000u ++#define P_ATR_EVT_POST_ERR_SHIFT 20 ++#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u ++#define P_ATR_EVT_FETCH_ERR_SHIFT 21 ++#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u ++#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 ++#define P_ATR_EVT_DOORBELL_MASK 0x00000000u ++#define P_ATR_EVT_DOORBELL_SHIFT 23 ++#define PM_MSI_INT_INTA_MASK 0x01000000u ++#define PM_MSI_INT_INTA_SHIFT 24 ++#define PM_MSI_INT_INTB_MASK 0x02000000u ++#define PM_MSI_INT_INTB_SHIFT 25 ++#define PM_MSI_INT_INTC_MASK 0x04000000u ++#define PM_MSI_INT_INTC_SHIFT 26 ++#define PM_MSI_INT_INTD_MASK 0x08000000u ++#define PM_MSI_INT_INTD_SHIFT 27 ++#define PM_MSI_INT_INTX_MASK 0x0f000000u ++#define PM_MSI_INT_INTX_SHIFT 24 ++#define PM_MSI_INT_MSI_MASK 0x10000000u ++#define PM_MSI_INT_MSI_SHIFT 28 ++#define PM_MSI_INT_AER_EVT_MASK 0x20000000u ++#define PM_MSI_INT_AER_EVT_SHIFT 29 ++#define PM_MSI_INT_EVENTS_MASK 0x40000000u ++#define PM_MSI_INT_EVENTS_SHIFT 30 ++#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u ++#define PM_MSI_INT_SYS_ERR_SHIFT 31 ++#define NUM_LOCAL_EVENTS 15 ++#define ISTATUS_LOCAL 0x184 ++#define IMASK_HOST 0x188 ++#define ISTATUS_HOST 0x18c ++#define IMSI_ADDR 0x190 ++#define ISTATUS_MSI 0x194 ++ ++/* PCIe Master table init defines */ ++#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u ++#define ATR0_PCIE_ATR_SIZE 0x25 ++#define ATR0_PCIE_ATR_SIZE_SHIFT 1 ++#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u ++#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u ++#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu ++#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u ++ ++/* PCIe AXI slave table init defines */ ++#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u ++#define ATR_SIZE_SHIFT 1 ++#define ATR_IMPL_ENABLE 1 ++#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u ++#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u ++#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu ++#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u ++#define PCIE_TX_RX_INTERFACE 0x00000000u ++#define PCIE_CONFIG_INTERFACE 0x00000001u ++ ++#define ATR_ENTRY_SIZE 32 ++ ++/* PCIe Controller Phy Regs */ ++#define SEC_ERROR_EVENT_CNT 0x20 ++#define DED_ERROR_EVENT_CNT 0x24 ++#define SEC_ERROR_INT 0x28 ++#define SEC_ERROR_INT_TX_RAM_SEC_ERR_INT GENMASK(3, 0) ++#define SEC_ERROR_INT_RX_RAM_SEC_ERR_INT GENMASK(7, 4) ++#define SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT GENMASK(11, 8) ++#define SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT GENMASK(15, 12) ++#define SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT GENMASK(15, 0) ++#define NUM_SEC_ERROR_INTS (4) ++#define SEC_ERROR_INT_MASK 0x2c ++#define DED_ERROR_INT 0x30 ++#define DED_ERROR_INT_TX_RAM_DED_ERR_INT GENMASK(3, 0) ++#define DED_ERROR_INT_RX_RAM_DED_ERR_INT GENMASK(7, 4) ++#define DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT GENMASK(11, 8) ++#define DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT GENMASK(15, 12) ++#define DED_ERROR_INT_ALL_RAM_DED_ERR_INT GENMASK(15, 0) ++#define NUM_DED_ERROR_INTS (4) ++#define DED_ERROR_INT_MASK 0x34 ++#define ECC_CONTROL 0x38 ++#define ECC_CONTROL_TX_RAM_INJ_ERROR_0 BIT(0) ++#define ECC_CONTROL_TX_RAM_INJ_ERROR_1 BIT(1) ++#define ECC_CONTROL_TX_RAM_INJ_ERROR_2 BIT(2) ++#define ECC_CONTROL_TX_RAM_INJ_ERROR_3 BIT(3) ++#define ECC_CONTROL_RX_RAM_INJ_ERROR_0 BIT(4) ++#define ECC_CONTROL_RX_RAM_INJ_ERROR_1 BIT(5) ++#define ECC_CONTROL_RX_RAM_INJ_ERROR_2 BIT(6) ++#define ECC_CONTROL_RX_RAM_INJ_ERROR_3 BIT(7) ++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_0 BIT(8) ++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_1 BIT(9) ++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_2 BIT(10) ++#define ECC_CONTROL_PCIE2AXI_RAM_INJ_ERROR_3 BIT(11) ++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_0 BIT(12) ++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_1 BIT(13) ++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_2 BIT(14) ++#define ECC_CONTROL_AXI2PCIE_RAM_INJ_ERROR_3 BIT(15) ++#define ECC_CONTROL_TX_RAM_ECC_BYPASS BIT(24) ++#define ECC_CONTROL_RX_RAM_ECC_BYPASS BIT(25) ++#define ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS BIT(26) ++#define ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS BIT(27) ++#define PCIE_EVENT_INT 0x14c ++#define PCIE_EVENT_INT_L2_EXIT_INT BIT(0) ++#define PCIE_EVENT_INT_HOTRST_EXIT_INT BIT(1) ++#define PCIE_EVENT_INT_DLUP_EXIT_INT BIT(2) ++#define PCIE_EVENT_INT_MASK GENMASK(2, 0) ++#define PCIE_EVENT_INT_L2_EXIT_INT_MASK BIT(16) ++#define PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK BIT(17) ++#define PCIE_EVENT_INT_DLUP_EXIT_INT_MASK BIT(18) ++#define PCIE_EVENT_INT_ENB_MASK GENMASK(18, 16) ++#define PCIE_EVENT_INT_ENB_SHIFT 16 ++#define NUM_PCIE_EVENTS (3) ++ ++/* PCIe Config space MSI capability structure */ ++#define MC_MSI_CAP_CTRL_OFFSET 0xe0u ++ ++/* Events */ ++#define EVENT_PCIE_L2_EXIT 0 ++#define EVENT_PCIE_HOTRST_EXIT 1 ++#define EVENT_PCIE_DLUP_EXIT 2 ++#define EVENT_SEC_TX_RAM_SEC_ERR 3 ++#define EVENT_SEC_RX_RAM_SEC_ERR 4 ++#define EVENT_SEC_PCIE2AXI_RAM_SEC_ERR 5 ++#define EVENT_SEC_AXI2PCIE_RAM_SEC_ERR 6 ++#define EVENT_DED_TX_RAM_DED_ERR 7 ++#define EVENT_DED_RX_RAM_DED_ERR 8 ++#define EVENT_DED_PCIE2AXI_RAM_DED_ERR 9 ++#define EVENT_DED_AXI2PCIE_RAM_DED_ERR 10 ++#define EVENT_LOCAL_DMA_END_ENGINE_0 11 ++#define EVENT_LOCAL_DMA_END_ENGINE_1 12 ++#define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13 ++#define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14 ++#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15 ++#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16 ++#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17 ++#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18 ++#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19 ++#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20 ++#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21 ++#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22 ++#define EVENT_LOCAL_PM_MSI_INT_INTX 23 ++#define EVENT_LOCAL_PM_MSI_INT_MSI 24 ++#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25 ++#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26 ++#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27 ++#define NUM_EVENTS 28 ++ ++#define PCIE_EVENT_CAUSE(x, s) \ ++ [EVENT_PCIE_ ## x] = { __stringify(x), s } ++ ++#define SEC_ERROR_CAUSE(x, s) \ ++ [EVENT_SEC_ ## x] = { __stringify(x), s } ++ ++#define DED_ERROR_CAUSE(x, s) \ ++ [EVENT_DED_ ## x] = { __stringify(x), s } ++ ++#define LOCAL_EVENT_CAUSE(x, s) \ ++ [EVENT_LOCAL_ ## x] = { __stringify(x), s } ++ ++#define PCIE_EVENT(x) \ ++ .base = MC_PCIE_CTRL_ADDR, \ ++ .offset = PCIE_EVENT_INT, \ ++ .mask_offset = PCIE_EVENT_INT, \ ++ .mask_high = 1, \ ++ .mask = PCIE_EVENT_INT_ ## x ## _INT, \ ++ .enb_mask = PCIE_EVENT_INT_ENB_MASK ++ ++#define SEC_EVENT(x) \ ++ .base = MC_PCIE_CTRL_ADDR, \ ++ .offset = SEC_ERROR_INT, \ ++ .mask_offset = SEC_ERROR_INT_MASK, \ ++ .mask = SEC_ERROR_INT_ ## x ## _INT, \ ++ .mask_high = 1, \ ++ .enb_mask = 0 ++ ++#define DED_EVENT(x) \ ++ .base = MC_PCIE_CTRL_ADDR, \ ++ .offset = DED_ERROR_INT, \ ++ .mask_offset = DED_ERROR_INT_MASK, \ ++ .mask_high = 1, \ ++ .mask = DED_ERROR_INT_ ## x ## _INT, \ ++ .enb_mask = 0 ++ ++#define LOCAL_EVENT(x) \ ++ .base = MC_PCIE_BRIDGE_ADDR, \ ++ .offset = ISTATUS_LOCAL, \ ++ .mask_offset = IMASK_LOCAL, \ ++ .mask_high = 0, \ ++ .mask = x ## _MASK, \ ++ .enb_mask = 0 ++ ++#define PCIE_EVENT_TO_EVENT_MAP(x) \ ++ { PCIE_EVENT_INT_ ## x ## _INT, EVENT_PCIE_ ## x } ++ ++#define SEC_ERROR_TO_EVENT_MAP(x) \ ++ { SEC_ERROR_INT_ ## x ## _INT, EVENT_SEC_ ## x } ++ ++#define DED_ERROR_TO_EVENT_MAP(x) \ ++ { DED_ERROR_INT_ ## x ## _INT, EVENT_DED_ ## x } ++ ++#define LOCAL_STATUS_TO_EVENT_MAP(x) \ ++ { x ## _MASK, EVENT_LOCAL_ ## x } ++ ++struct event_map { ++ u32 reg_mask; ++ u32 event_bit; ++}; ++ ++struct mc_msi { ++ struct mutex lock; /* Protect used bitmap */ ++ struct irq_domain *msi_domain; ++ struct irq_domain *dev_domain; ++ u32 num_vectors; ++ u64 vector_phy; ++ DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); ++}; ++ ++struct mc_pcie { ++ void __iomem *axi_base_addr; ++ struct device *dev; ++ struct irq_domain *intx_domain; ++ struct irq_domain *event_domain; ++ raw_spinlock_t lock; ++ struct mc_msi msi; ++}; ++ ++struct cause { ++ const char *sym; ++ const char *str; ++}; ++ ++static const struct cause event_cause[NUM_EVENTS] = { ++ PCIE_EVENT_CAUSE(L2_EXIT, "L2 exit event"), ++ PCIE_EVENT_CAUSE(HOTRST_EXIT, "Hot reset exit event"), ++ PCIE_EVENT_CAUSE(DLUP_EXIT, "DLUP exit event"), ++ SEC_ERROR_CAUSE(TX_RAM_SEC_ERR, "sec error in tx buffer"), ++ SEC_ERROR_CAUSE(RX_RAM_SEC_ERR, "sec error in rx buffer"), ++ SEC_ERROR_CAUSE(PCIE2AXI_RAM_SEC_ERR, "sec error in pcie2axi buffer"), ++ SEC_ERROR_CAUSE(AXI2PCIE_RAM_SEC_ERR, "sec error in axi2pcie buffer"), ++ DED_ERROR_CAUSE(TX_RAM_DED_ERR, "ded error in tx buffer"), ++ DED_ERROR_CAUSE(RX_RAM_DED_ERR, "ded error in rx buffer"), ++ DED_ERROR_CAUSE(PCIE2AXI_RAM_DED_ERR, "ded error in pcie2axi buffer"), ++ DED_ERROR_CAUSE(AXI2PCIE_RAM_DED_ERR, "ded error in axi2pcie buffer"), ++ LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_0, "dma engine 0 error"), ++ LOCAL_EVENT_CAUSE(DMA_ERROR_ENGINE_1, "dma engine 1 error"), ++ LOCAL_EVENT_CAUSE(A_ATR_EVT_POST_ERR, "axi write request error"), ++ LOCAL_EVENT_CAUSE(A_ATR_EVT_FETCH_ERR, "axi read request error"), ++ LOCAL_EVENT_CAUSE(A_ATR_EVT_DISCARD_ERR, "axi read timeout"), ++ LOCAL_EVENT_CAUSE(P_ATR_EVT_POST_ERR, "pcie write request error"), ++ LOCAL_EVENT_CAUSE(P_ATR_EVT_FETCH_ERR, "pcie read request error"), ++ LOCAL_EVENT_CAUSE(P_ATR_EVT_DISCARD_ERR, "pcie read timeout"), ++ LOCAL_EVENT_CAUSE(PM_MSI_INT_AER_EVT, "aer event"), ++ LOCAL_EVENT_CAUSE(PM_MSI_INT_EVENTS, "pm/ltr/hotplug event"), ++ LOCAL_EVENT_CAUSE(PM_MSI_INT_SYS_ERR, "system error"), ++}; ++ ++static struct event_map pcie_event_to_event[] = { ++ PCIE_EVENT_TO_EVENT_MAP(L2_EXIT), ++ PCIE_EVENT_TO_EVENT_MAP(HOTRST_EXIT), ++ PCIE_EVENT_TO_EVENT_MAP(DLUP_EXIT), ++}; ++ ++static struct event_map sec_error_to_event[] = { ++ SEC_ERROR_TO_EVENT_MAP(TX_RAM_SEC_ERR), ++ SEC_ERROR_TO_EVENT_MAP(RX_RAM_SEC_ERR), ++ SEC_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_SEC_ERR), ++ SEC_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_SEC_ERR), ++}; ++ ++static struct event_map ded_error_to_event[] = { ++ DED_ERROR_TO_EVENT_MAP(TX_RAM_DED_ERR), ++ DED_ERROR_TO_EVENT_MAP(RX_RAM_DED_ERR), ++ DED_ERROR_TO_EVENT_MAP(PCIE2AXI_RAM_DED_ERR), ++ DED_ERROR_TO_EVENT_MAP(AXI2PCIE_RAM_DED_ERR), ++}; ++ ++static struct event_map local_status_to_event[] = { ++ LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_0), ++ LOCAL_STATUS_TO_EVENT_MAP(DMA_END_ENGINE_1), ++ LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_0), ++ LOCAL_STATUS_TO_EVENT_MAP(DMA_ERROR_ENGINE_1), ++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_POST_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_FETCH_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DISCARD_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(A_ATR_EVT_DOORBELL), ++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_POST_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_FETCH_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DISCARD_ERR), ++ LOCAL_STATUS_TO_EVENT_MAP(P_ATR_EVT_DOORBELL), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_INTX), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_MSI), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_AER_EVT), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_EVENTS), ++ LOCAL_STATUS_TO_EVENT_MAP(PM_MSI_INT_SYS_ERR), ++}; ++ ++static struct { ++ u32 base; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 enb_mask; ++ u32 mask_high; ++ u32 mask_offset; ++} event_descs[] = { ++ { PCIE_EVENT(L2_EXIT) }, ++ { PCIE_EVENT(HOTRST_EXIT) }, ++ { PCIE_EVENT(DLUP_EXIT) }, ++ { SEC_EVENT(TX_RAM_SEC_ERR) }, ++ { SEC_EVENT(RX_RAM_SEC_ERR) }, ++ { SEC_EVENT(PCIE2AXI_RAM_SEC_ERR) }, ++ { SEC_EVENT(AXI2PCIE_RAM_SEC_ERR) }, ++ { DED_EVENT(TX_RAM_DED_ERR) }, ++ { DED_EVENT(RX_RAM_DED_ERR) }, ++ { DED_EVENT(PCIE2AXI_RAM_DED_ERR) }, ++ { DED_EVENT(AXI2PCIE_RAM_DED_ERR) }, ++ { LOCAL_EVENT(DMA_END_ENGINE_0) }, ++ { LOCAL_EVENT(DMA_END_ENGINE_1) }, ++ { LOCAL_EVENT(DMA_ERROR_ENGINE_0) }, ++ { LOCAL_EVENT(DMA_ERROR_ENGINE_1) }, ++ { LOCAL_EVENT(A_ATR_EVT_POST_ERR) }, ++ { LOCAL_EVENT(A_ATR_EVT_FETCH_ERR) }, ++ { LOCAL_EVENT(A_ATR_EVT_DISCARD_ERR) }, ++ { LOCAL_EVENT(A_ATR_EVT_DOORBELL) }, ++ { LOCAL_EVENT(P_ATR_EVT_POST_ERR) }, ++ { LOCAL_EVENT(P_ATR_EVT_FETCH_ERR) }, ++ { LOCAL_EVENT(P_ATR_EVT_DISCARD_ERR) }, ++ { LOCAL_EVENT(P_ATR_EVT_DOORBELL) }, ++ { LOCAL_EVENT(PM_MSI_INT_INTX) }, ++ { LOCAL_EVENT(PM_MSI_INT_MSI) }, ++ { LOCAL_EVENT(PM_MSI_INT_AER_EVT) }, ++ { LOCAL_EVENT(PM_MSI_INT_EVENTS) }, ++ { LOCAL_EVENT(PM_MSI_INT_SYS_ERR) }, ++}; ++ ++static char poss_clks[][5] = { "fic0", "fic1", "fic2", "fic3" }; ++ ++static struct mc_pcie *port; ++ ++static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) ++{ ++ struct mc_msi *msi = &port->msi; ++ u16 reg; ++ u8 queue_size; ++ ++ /* Fixup MSI enable flag */ ++ reg = readw_relaxed(ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); ++ reg |= PCI_MSI_FLAGS_ENABLE; ++ writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); ++ ++ /* Fixup PCI MSI queue flags */ ++ queue_size = FIELD_GET(PCI_MSI_FLAGS_QMASK, reg); ++ reg |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, queue_size); ++ writew_relaxed(reg, ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_FLAGS); ++ ++ /* Fixup MSI addr fields */ ++ writel_relaxed(lower_32_bits(msi->vector_phy), ++ ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_LO); ++ writel_relaxed(upper_32_bits(msi->vector_phy), ++ ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); ++} ++ ++static void mc_handle_msi(struct irq_desc *desc) ++{ ++ struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = port->dev; ++ struct mc_msi *msi = &port->msi; ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ unsigned long status; ++ u32 bit; ++ int ret; ++ ++ chained_irq_enter(chip, desc); ++ ++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ if (status & PM_MSI_INT_MSI_MASK) { ++ writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); ++ status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); ++ for_each_set_bit(bit, &status, msi->num_vectors) { ++ ret = generic_handle_domain_irq(msi->dev_domain, bit); ++ if (ret) ++ dev_err_ratelimited(dev, "bad MSI IRQ %d\n", ++ bit); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void mc_msi_bottom_irq_ack(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ u32 bitpos = data->hwirq; ++ ++ writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); ++} ++ ++static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ phys_addr_t addr = port->msi.vector_phy; ++ ++ msg->address_lo = lower_32_bits(addr); ++ msg->address_hi = upper_32_bits(addr); ++ msg->data = data->hwirq; ++ ++ dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", ++ (int)data->hwirq, msg->address_hi, msg->address_lo); ++} ++ ++static int mc_msi_set_affinity(struct irq_data *irq_data, ++ const struct cpumask *mask, bool force) ++{ ++ return -EINVAL; ++} ++ ++static struct irq_chip mc_msi_bottom_irq_chip = { ++ .name = "Microchip MSI", ++ .irq_ack = mc_msi_bottom_irq_ack, ++ .irq_compose_msi_msg = mc_compose_msi_msg, ++ .irq_set_affinity = mc_msi_set_affinity, ++}; ++ ++static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, ++ unsigned int nr_irqs, void *args) ++{ ++ struct mc_pcie *port = domain->host_data; ++ struct mc_msi *msi = &port->msi; ++ unsigned long bit; ++ ++ mutex_lock(&msi->lock); ++ bit = find_first_zero_bit(msi->used, msi->num_vectors); ++ if (bit >= msi->num_vectors) { ++ mutex_unlock(&msi->lock); ++ return -ENOSPC; ++ } ++ ++ set_bit(bit, msi->used); ++ ++ irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, ++ domain->host_data, handle_edge_irq, NULL, NULL); ++ ++ mutex_unlock(&msi->lock); ++ ++ return 0; ++} ++ ++static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, ++ unsigned int nr_irqs) ++{ ++ struct irq_data *d = irq_domain_get_irq_data(domain, virq); ++ struct mc_pcie *port = irq_data_get_irq_chip_data(d); ++ struct mc_msi *msi = &port->msi; ++ ++ mutex_lock(&msi->lock); ++ ++ if (test_bit(d->hwirq, msi->used)) ++ __clear_bit(d->hwirq, msi->used); ++ else ++ dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); ++ ++ mutex_unlock(&msi->lock); ++} ++ ++static const struct irq_domain_ops msi_domain_ops = { ++ .alloc = mc_irq_msi_domain_alloc, ++ .free = mc_irq_msi_domain_free, ++}; ++ ++static struct irq_chip mc_msi_irq_chip = { ++ .name = "Microchip PCIe MSI", ++ .irq_ack = irq_chip_ack_parent, ++ .irq_mask = pci_msi_mask_irq, ++ .irq_unmask = pci_msi_unmask_irq, ++}; ++ ++static struct msi_domain_info mc_msi_domain_info = { ++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | ++ MSI_FLAG_PCI_MSIX), ++ .chip = &mc_msi_irq_chip, ++}; ++ ++static int mc_allocate_msi_domains(struct mc_pcie *port) ++{ ++ struct device *dev = port->dev; ++ struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); ++ struct mc_msi *msi = &port->msi; ++ ++ mutex_init(&port->msi.lock); ++ ++ msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, ++ &msi_domain_ops, port); ++ if (!msi->dev_domain) { ++ dev_err(dev, "failed to create IRQ domain\n"); ++ return -ENOMEM; ++ } ++ ++ msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, ++ msi->dev_domain); ++ if (!msi->msi_domain) { ++ dev_err(dev, "failed to create MSI domain\n"); ++ irq_domain_remove(msi->dev_domain); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void mc_handle_intx(struct irq_desc *desc) ++{ ++ struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = port->dev; ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ unsigned long status; ++ u32 bit; ++ int ret; ++ ++ chained_irq_enter(chip, desc); ++ ++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ if (status & PM_MSI_INT_INTX_MASK) { ++ status &= PM_MSI_INT_INTX_MASK; ++ status >>= PM_MSI_INT_INTX_SHIFT; ++ for_each_set_bit(bit, &status, PCI_NUM_INTX) { ++ ret = generic_handle_domain_irq(port->intx_domain, bit); ++ if (ret) ++ dev_err_ratelimited(dev, "bad INTx IRQ %d\n", ++ bit); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void mc_ack_intx_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ ++ writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); ++} ++ ++static void mc_mask_intx_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ unsigned long flags; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ u32 val; ++ ++ raw_spin_lock_irqsave(&port->lock, flags); ++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); ++ val &= ~mask; ++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); ++ raw_spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static void mc_unmask_intx_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ unsigned long flags; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ u32 val; ++ ++ raw_spin_lock_irqsave(&port->lock, flags); ++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); ++ val |= mask; ++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); ++ raw_spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static struct irq_chip mc_intx_irq_chip = { ++ .name = "Microchip PCIe INTx", ++ .irq_ack = mc_ack_intx_irq, ++ .irq_mask = mc_mask_intx_irq, ++ .irq_unmask = mc_unmask_intx_irq, ++}; ++ ++static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops intx_domain_ops = { ++ .map = mc_pcie_intx_map, ++}; ++ ++static inline u32 reg_to_event(u32 reg, struct event_map field) ++{ ++ return (reg & field.reg_mask) ? BIT(field.event_bit) : 0; ++} ++ ++static u32 pcie_events(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT); ++ u32 val = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pcie_event_to_event); i++) ++ val |= reg_to_event(reg, pcie_event_to_event[i]); ++ ++ return val; ++} ++ ++static u32 sec_errors(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT); ++ u32 val = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(sec_error_to_event); i++) ++ val |= reg_to_event(reg, sec_error_to_event[i]); ++ ++ return val; ++} ++ ++static u32 ded_errors(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT); ++ u32 val = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ded_error_to_event); i++) ++ val |= reg_to_event(reg, ded_error_to_event[i]); ++ ++ return val; ++} ++ ++static u32 local_events(struct mc_pcie *port) ++{ ++ void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ u32 val = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(local_status_to_event); i++) ++ val |= reg_to_event(reg, local_status_to_event[i]); ++ ++ return val; ++} ++ ++static u32 get_events(struct mc_pcie *port) ++{ ++ u32 events = 0; ++ ++ events |= pcie_events(port); ++ events |= sec_errors(port); ++ events |= ded_errors(port); ++ events |= local_events(port); ++ ++ return events; ++} ++ ++static irqreturn_t mc_event_handler(int irq, void *dev_id) ++{ ++ struct mc_pcie *port = dev_id; ++ struct device *dev = port->dev; ++ struct irq_data *data; ++ ++ data = irq_domain_get_irq_data(port->event_domain, irq); ++ ++ if (event_cause[data->hwirq].str) ++ dev_err_ratelimited(dev, "%s\n", event_cause[data->hwirq].str); ++ else ++ dev_err_ratelimited(dev, "bad event IRQ %ld\n", data->hwirq); ++ ++ return IRQ_HANDLED; ++} ++ ++static void mc_handle_event(struct irq_desc *desc) ++{ ++ struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ unsigned long events; ++ u32 bit; ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ ++ chained_irq_enter(chip, desc); ++ ++ events = get_events(port); ++ ++ for_each_set_bit(bit, &events, NUM_EVENTS) ++ generic_handle_domain_irq(port->event_domain, bit); ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void mc_ack_event_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ u32 event = data->hwirq; ++ void __iomem *addr; ++ u32 mask; ++ ++ addr = port->axi_base_addr + event_descs[event].base + ++ event_descs[event].offset; ++ mask = event_descs[event].mask; ++ mask |= event_descs[event].enb_mask; ++ ++ writel_relaxed(mask, addr); ++} ++ ++static void mc_mask_event_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ u32 event = data->hwirq; ++ void __iomem *addr; ++ u32 mask; ++ u32 val; ++ ++ addr = port->axi_base_addr + event_descs[event].base + ++ event_descs[event].mask_offset; ++ mask = event_descs[event].mask; ++ if (event_descs[event].enb_mask) { ++ mask <<= PCIE_EVENT_INT_ENB_SHIFT; ++ mask &= PCIE_EVENT_INT_ENB_MASK; ++ } ++ ++ if (!event_descs[event].mask_high) ++ mask = ~mask; ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(addr); ++ if (event_descs[event].mask_high) ++ val |= mask; ++ else ++ val &= mask; ++ ++ writel_relaxed(val, addr); ++ raw_spin_unlock(&port->lock); ++} ++ ++static void mc_unmask_event_irq(struct irq_data *data) ++{ ++ struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ u32 event = data->hwirq; ++ void __iomem *addr; ++ u32 mask; ++ u32 val; ++ ++ addr = port->axi_base_addr + event_descs[event].base + ++ event_descs[event].mask_offset; ++ mask = event_descs[event].mask; ++ ++ if (event_descs[event].enb_mask) ++ mask <<= PCIE_EVENT_INT_ENB_SHIFT; ++ ++ if (event_descs[event].mask_high) ++ mask = ~mask; ++ ++ if (event_descs[event].enb_mask) ++ mask &= PCIE_EVENT_INT_ENB_MASK; ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(addr); ++ if (event_descs[event].mask_high) ++ val &= mask; ++ else ++ val |= mask; ++ writel_relaxed(val, addr); ++ raw_spin_unlock(&port->lock); ++} ++ ++static struct irq_chip mc_event_irq_chip = { ++ .name = "Microchip PCIe EVENT", ++ .irq_ack = mc_ack_event_irq, ++ .irq_mask = mc_mask_event_irq, ++ .irq_unmask = mc_unmask_event_irq, ++}; ++ ++static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops event_domain_ops = { ++ .map = mc_pcie_event_map, ++}; ++ ++static inline void mc_pcie_deinit_clk(void *data) ++{ ++ struct clk *clk = data; ++ ++ clk_disable_unprepare(clk); ++} ++ ++static inline struct clk *mc_pcie_init_clk(struct device *dev, const char *id) ++{ ++ struct clk *clk; ++ int ret; ++ ++ clk = devm_clk_get_optional(dev, id); ++ if (IS_ERR(clk)) ++ return clk; ++ if (!clk) ++ return clk; ++ ++ ret = clk_prepare_enable(clk); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ devm_add_action_or_reset(dev, mc_pcie_deinit_clk, clk); ++ ++ return clk; ++} ++ ++static int mc_pcie_init_clks(struct device *dev) ++{ ++ int i; ++ struct clk *fic; ++ ++ /* ++ * PCIe may be clocked via Fabric Interface using between 1 and 4 ++ * clocks. Scan DT for clocks and enable them if present ++ */ ++ for (i = 0; i < ARRAY_SIZE(poss_clks); i++) { ++ fic = mc_pcie_init_clk(dev, poss_clks[i]); ++ if (IS_ERR(fic)) ++ return PTR_ERR(fic); ++ } ++ ++ return 0; ++} ++ ++static int mc_pcie_init_irq_domains(struct mc_pcie *port) ++{ ++ struct device *dev = port->dev; ++ struct device_node *node = dev->of_node; ++ struct device_node *pcie_intc_node; ++ ++ /* Setup INTx */ ++ pcie_intc_node = of_get_next_child(node, NULL); ++ if (!pcie_intc_node) { ++ dev_err(dev, "failed to find PCIe Intc node\n"); ++ return -EINVAL; ++ } ++ ++ port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, ++ &event_domain_ops, port); ++ if (!port->event_domain) { ++ dev_err(dev, "failed to get event domain\n"); ++ of_node_put(pcie_intc_node); ++ return -ENOMEM; ++ } ++ ++ irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); ++ ++ port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, ++ &intx_domain_ops, port); ++ if (!port->intx_domain) { ++ dev_err(dev, "failed to get an INTx IRQ domain\n"); ++ of_node_put(pcie_intc_node); ++ return -ENOMEM; ++ } ++ ++ irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); ++ ++ of_node_put(pcie_intc_node); ++ raw_spin_lock_init(&port->lock); ++ ++ return mc_allocate_msi_domains(port); ++} ++ ++static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, ++ phys_addr_t axi_addr, phys_addr_t pci_addr, ++ size_t size) ++{ ++ u32 atr_sz = ilog2(size) - 1; ++ u32 val; ++ ++ if (index == 0) ++ val = PCIE_CONFIG_INTERFACE; ++ else ++ val = PCIE_TX_RX_INTERFACE; ++ ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_PARAM); ++ ++ val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | ++ ATR_IMPL_ENABLE; ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_SRCADDR_PARAM); ++ ++ val = upper_32_bits(axi_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_SRC_ADDR); ++ ++ val = lower_32_bits(pci_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_ADDR_LSB); ++ ++ val = upper_32_bits(pci_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_ADDR_UDW); ++ ++ val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); ++ val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); ++ writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); ++ writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); ++} ++ ++static int mc_pcie_setup_windows(struct platform_device *pdev, ++ struct mc_pcie *port) ++{ ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ struct pci_host_bridge *bridge = platform_get_drvdata(pdev); ++ struct resource_entry *entry; ++ u64 pci_addr; ++ u32 index = 1; ++ ++ resource_list_for_each_entry(entry, &bridge->windows) { ++ if (resource_type(entry->res) == IORESOURCE_MEM) { ++ pci_addr = entry->res->start - entry->offset; ++ mc_pcie_setup_window(bridge_base_addr, index, ++ entry->res->start, pci_addr, ++ resource_size(entry->res)); ++ index++; ++ } ++ } ++ ++ return 0; ++} ++ ++static inline void mc_clear_secs(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ ++ writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + ++ SEC_ERROR_INT); ++ writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); ++} ++ ++static inline void mc_clear_deds(struct mc_pcie *port) ++{ ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ ++ writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + ++ DED_ERROR_INT); ++ writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); ++} ++ ++static void mc_disable_interrupts(struct mc_pcie *port) ++{ ++ void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; ++ u32 val; ++ ++ /* Ensure ECC bypass is enabled */ ++ val = ECC_CONTROL_TX_RAM_ECC_BYPASS | ++ ECC_CONTROL_RX_RAM_ECC_BYPASS | ++ ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS | ++ ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS; ++ writel_relaxed(val, ctrl_base_addr + ECC_CONTROL); ++ ++ /* Disable SEC errors and clear any outstanding */ ++ writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + ++ SEC_ERROR_INT_MASK); ++ mc_clear_secs(port); ++ ++ /* Disable DED errors and clear any outstanding */ ++ writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + ++ DED_ERROR_INT_MASK); ++ mc_clear_deds(port); ++ ++ /* Disable local interrupts and clear any outstanding */ ++ writel_relaxed(0, bridge_base_addr + IMASK_LOCAL); ++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL); ++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI); ++ ++ /* Disable PCIe events and clear any outstanding */ ++ val = PCIE_EVENT_INT_L2_EXIT_INT | ++ PCIE_EVENT_INT_HOTRST_EXIT_INT | ++ PCIE_EVENT_INT_DLUP_EXIT_INT | ++ PCIE_EVENT_INT_L2_EXIT_INT_MASK | ++ PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK | ++ PCIE_EVENT_INT_DLUP_EXIT_INT_MASK; ++ writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT); ++ ++ /* Disable host interrupts and clear any outstanding */ ++ writel_relaxed(0, bridge_base_addr + IMASK_HOST); ++ writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); ++} ++ ++static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) ++{ ++ struct device *dev = &pdev->dev; ++ int irq; ++ int i, intx_irq, msi_irq, event_irq; ++ int ret; ++ ++ ret = mc_pcie_init_irq_domains(port); ++ if (ret) { ++ dev_err(dev, "failed creating IRQ domains\n"); ++ return ret; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return -ENODEV; ++ ++ for (i = 0; i < NUM_EVENTS; i++) { ++ event_irq = irq_create_mapping(port->event_domain, i); ++ if (!event_irq) { ++ dev_err(dev, "failed to map hwirq %d\n", i); ++ return -ENXIO; ++ } ++ ++ ret = devm_request_irq(dev, event_irq, mc_event_handler, ++ 0, event_cause[i].sym, port); ++ if (ret) { ++ dev_err(dev, "failed to request IRQ %d\n", event_irq); ++ return ret; ++ } ++ } ++ ++ intx_irq = irq_create_mapping(port->event_domain, ++ EVENT_LOCAL_PM_MSI_INT_INTX); ++ if (!intx_irq) { ++ dev_err(dev, "failed to map INTx interrupt\n"); ++ return -ENXIO; ++ } ++ ++ /* Plug the INTx chained handler */ ++ irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port); ++ ++ msi_irq = irq_create_mapping(port->event_domain, ++ EVENT_LOCAL_PM_MSI_INT_MSI); ++ if (!msi_irq) ++ return -ENXIO; ++ ++ /* Plug the MSI chained handler */ ++ irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port); ++ ++ /* Plug the main event chained handler */ ++ irq_set_chained_handler_and_data(irq, mc_handle_event, port); ++ ++ return 0; ++} ++ ++static int mc_platform_init(struct pci_config_window *cfg) ++{ ++ struct device *dev = cfg->parent; ++ struct platform_device *pdev = to_platform_device(dev); ++ void __iomem *bridge_base_addr = ++ port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ int ret; ++ ++ /* Configure address translation table 0 for PCIe config space */ ++ mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, ++ cfg->res.start, ++ resource_size(&cfg->res)); ++ ++ /* Need some fixups in config space */ ++ mc_pcie_enable_msi(port, cfg->win); ++ ++ /* Configure non-config space outbound ranges */ ++ ret = mc_pcie_setup_windows(pdev, port); ++ if (ret) ++ return ret; ++ ++ /* Address translation is up; safe to enable interrupts */ ++ ret = mc_init_interrupts(pdev, port); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int mc_host_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ void __iomem *bridge_base_addr; ++ int ret; ++ u32 val; ++ ++ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); ++ if (!port) ++ return -ENOMEM; ++ ++ port->dev = dev; ++ ++ port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); ++ if (IS_ERR(port->axi_base_addr)) ++ return PTR_ERR(port->axi_base_addr); ++ ++ mc_disable_interrupts(port); ++ ++ bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ ++ /* Allow enabling MSI by disabling MSI-X */ ++ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); ++ val &= ~MSIX_CAP_MASK; ++ writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0); ++ ++ /* Pick num vectors from bitfile programmed onto FPGA fabric */ ++ val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); ++ val &= NUM_MSI_MSGS_MASK; ++ val >>= NUM_MSI_MSGS_SHIFT; ++ ++ port->msi.num_vectors = 1 << val; ++ ++ /* Pick vector address from design */ ++ port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); ++ ++ ret = mc_pcie_init_clks(dev); ++ if (ret) { ++ dev_err(dev, "failed to get clock resources, error %d\n", ret); ++ return -ENODEV; ++ } ++ ++ return pci_host_common_probe(pdev); ++} ++ ++static const struct pci_ecam_ops mc_ecam_ops = { ++ .init = mc_platform_init, ++ .pci_ops = { ++ .map_bus = pci_ecam_map_bus, ++ .read = pci_generic_config_read, ++ .write = pci_generic_config_write, ++ } ++}; ++ ++static const struct of_device_id mc_pcie_of_match[] = { ++ { ++ .compatible = "microchip,pcie-host-1.0", ++ .data = &mc_ecam_ops, ++ }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, mc_pcie_of_match); ++ ++static struct platform_driver mc_pcie_driver = { ++ .probe = mc_host_probe, ++ .driver = { ++ .name = "microchip-pcie", ++ .of_match_table = mc_pcie_of_match, ++ .suppress_bind_attrs = true, ++ }, ++}; ++ ++builtin_platform_driver(mc_pcie_driver); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Microchip PCIe host controller driver"); ++MODULE_AUTHOR("Daire McNamara "); diff --git a/target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch b/target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch new file mode 100644 index 0000000000..0aacec2769 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0016-PCI-microchip-Move-PLDA-IP-register-macros-to-pcie-p.patch @@ -0,0 +1,259 @@ +From eca1b864bb2d4e8d9811506979560a89351c2e37 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:53 +0800 +Subject: [PATCH 016/116] PCI: microchip: Move PLDA IP register macros to + pcie-plda.h + +Move PLDA PCIe host controller IP registers macros to pcie-plda.h, +including bridge registers and local IRQ event number. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 108 +++--------------- + drivers/pci/controller/plda/pcie-plda.h | 108 ++++++++++++++++++ + 2 files changed, 124 insertions(+), 92 deletions(-) + create mode 100644 drivers/pci/controller/plda/pcie-plda.h + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -19,6 +19,7 @@ + #include + + #include "../../pci.h" ++#include "pcie-plda.h" + + /* Number of MSI IRQs */ + #define MC_MAX_NUM_MSI_IRQS 32 +@@ -30,84 +31,6 @@ + #define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) + #define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) + +-/* PCIe Bridge Phy Regs */ +-#define PCIE_PCI_IRQ_DW0 0xa8 +-#define MSIX_CAP_MASK BIT(31) +-#define NUM_MSI_MSGS_MASK GENMASK(6, 4) +-#define NUM_MSI_MSGS_SHIFT 4 +- +-#define IMASK_LOCAL 0x180 +-#define DMA_END_ENGINE_0_MASK 0x00000000u +-#define DMA_END_ENGINE_0_SHIFT 0 +-#define DMA_END_ENGINE_1_MASK 0x00000000u +-#define DMA_END_ENGINE_1_SHIFT 1 +-#define DMA_ERROR_ENGINE_0_MASK 0x00000100u +-#define DMA_ERROR_ENGINE_0_SHIFT 8 +-#define DMA_ERROR_ENGINE_1_MASK 0x00000200u +-#define DMA_ERROR_ENGINE_1_SHIFT 9 +-#define A_ATR_EVT_POST_ERR_MASK 0x00010000u +-#define A_ATR_EVT_POST_ERR_SHIFT 16 +-#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u +-#define A_ATR_EVT_FETCH_ERR_SHIFT 17 +-#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u +-#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 +-#define A_ATR_EVT_DOORBELL_MASK 0x00000000u +-#define A_ATR_EVT_DOORBELL_SHIFT 19 +-#define P_ATR_EVT_POST_ERR_MASK 0x00100000u +-#define P_ATR_EVT_POST_ERR_SHIFT 20 +-#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u +-#define P_ATR_EVT_FETCH_ERR_SHIFT 21 +-#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u +-#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 +-#define P_ATR_EVT_DOORBELL_MASK 0x00000000u +-#define P_ATR_EVT_DOORBELL_SHIFT 23 +-#define PM_MSI_INT_INTA_MASK 0x01000000u +-#define PM_MSI_INT_INTA_SHIFT 24 +-#define PM_MSI_INT_INTB_MASK 0x02000000u +-#define PM_MSI_INT_INTB_SHIFT 25 +-#define PM_MSI_INT_INTC_MASK 0x04000000u +-#define PM_MSI_INT_INTC_SHIFT 26 +-#define PM_MSI_INT_INTD_MASK 0x08000000u +-#define PM_MSI_INT_INTD_SHIFT 27 +-#define PM_MSI_INT_INTX_MASK 0x0f000000u +-#define PM_MSI_INT_INTX_SHIFT 24 +-#define PM_MSI_INT_MSI_MASK 0x10000000u +-#define PM_MSI_INT_MSI_SHIFT 28 +-#define PM_MSI_INT_AER_EVT_MASK 0x20000000u +-#define PM_MSI_INT_AER_EVT_SHIFT 29 +-#define PM_MSI_INT_EVENTS_MASK 0x40000000u +-#define PM_MSI_INT_EVENTS_SHIFT 30 +-#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u +-#define PM_MSI_INT_SYS_ERR_SHIFT 31 +-#define NUM_LOCAL_EVENTS 15 +-#define ISTATUS_LOCAL 0x184 +-#define IMASK_HOST 0x188 +-#define ISTATUS_HOST 0x18c +-#define IMSI_ADDR 0x190 +-#define ISTATUS_MSI 0x194 +- +-/* PCIe Master table init defines */ +-#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u +-#define ATR0_PCIE_ATR_SIZE 0x25 +-#define ATR0_PCIE_ATR_SIZE_SHIFT 1 +-#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u +-#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u +-#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu +-#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u +- +-/* PCIe AXI slave table init defines */ +-#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u +-#define ATR_SIZE_SHIFT 1 +-#define ATR_IMPL_ENABLE 1 +-#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u +-#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u +-#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu +-#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u +-#define PCIE_TX_RX_INTERFACE 0x00000000u +-#define PCIE_CONFIG_INTERFACE 0x00000001u +- +-#define ATR_ENTRY_SIZE 32 +- + /* PCIe Controller Phy Regs */ + #define SEC_ERROR_EVENT_CNT 0x20 + #define DED_ERROR_EVENT_CNT 0x24 +@@ -179,20 +102,21 @@ + #define EVENT_LOCAL_DMA_END_ENGINE_1 12 + #define EVENT_LOCAL_DMA_ERROR_ENGINE_0 13 + #define EVENT_LOCAL_DMA_ERROR_ENGINE_1 14 +-#define EVENT_LOCAL_A_ATR_EVT_POST_ERR 15 +-#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR 16 +-#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR 17 +-#define EVENT_LOCAL_A_ATR_EVT_DOORBELL 18 +-#define EVENT_LOCAL_P_ATR_EVT_POST_ERR 19 +-#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR 20 +-#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR 21 +-#define EVENT_LOCAL_P_ATR_EVT_DOORBELL 22 +-#define EVENT_LOCAL_PM_MSI_INT_INTX 23 +-#define EVENT_LOCAL_PM_MSI_INT_MSI 24 +-#define EVENT_LOCAL_PM_MSI_INT_AER_EVT 25 +-#define EVENT_LOCAL_PM_MSI_INT_EVENTS 26 +-#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR 27 +-#define NUM_EVENTS 28 ++#define NUM_MC_EVENTS 15 ++#define EVENT_LOCAL_A_ATR_EVT_POST_ERR (NUM_MC_EVENTS + PLDA_AXI_POST_ERR) ++#define EVENT_LOCAL_A_ATR_EVT_FETCH_ERR (NUM_MC_EVENTS + PLDA_AXI_FETCH_ERR) ++#define EVENT_LOCAL_A_ATR_EVT_DISCARD_ERR (NUM_MC_EVENTS + PLDA_AXI_DISCARD_ERR) ++#define EVENT_LOCAL_A_ATR_EVT_DOORBELL (NUM_MC_EVENTS + PLDA_AXI_DOORBELL) ++#define EVENT_LOCAL_P_ATR_EVT_POST_ERR (NUM_MC_EVENTS + PLDA_PCIE_POST_ERR) ++#define EVENT_LOCAL_P_ATR_EVT_FETCH_ERR (NUM_MC_EVENTS + PLDA_PCIE_FETCH_ERR) ++#define EVENT_LOCAL_P_ATR_EVT_DISCARD_ERR (NUM_MC_EVENTS + PLDA_PCIE_DISCARD_ERR) ++#define EVENT_LOCAL_P_ATR_EVT_DOORBELL (NUM_MC_EVENTS + PLDA_PCIE_DOORBELL) ++#define EVENT_LOCAL_PM_MSI_INT_INTX (NUM_MC_EVENTS + PLDA_INTX) ++#define EVENT_LOCAL_PM_MSI_INT_MSI (NUM_MC_EVENTS + PLDA_MSI) ++#define EVENT_LOCAL_PM_MSI_INT_AER_EVT (NUM_MC_EVENTS + PLDA_AER_EVENT) ++#define EVENT_LOCAL_PM_MSI_INT_EVENTS (NUM_MC_EVENTS + PLDA_MISC_EVENTS) ++#define EVENT_LOCAL_PM_MSI_INT_SYS_ERR (NUM_MC_EVENTS + PLDA_SYS_ERR) ++#define NUM_EVENTS (NUM_MC_EVENTS + PLDA_INT_EVENT_NUM) + + #define PCIE_EVENT_CAUSE(x, s) \ + [EVENT_PCIE_ ## x] = { __stringify(x), s } +--- /dev/null ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -0,0 +1,108 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * PLDA PCIe host controller driver ++ */ ++ ++#ifndef _PCIE_PLDA_H ++#define _PCIE_PLDA_H ++ ++/* PCIe Bridge Phy Regs */ ++#define PCIE_PCI_IRQ_DW0 0xa8 ++#define MSIX_CAP_MASK BIT(31) ++#define NUM_MSI_MSGS_MASK GENMASK(6, 4) ++#define NUM_MSI_MSGS_SHIFT 4 ++ ++#define IMASK_LOCAL 0x180 ++#define DMA_END_ENGINE_0_MASK 0x00000000u ++#define DMA_END_ENGINE_0_SHIFT 0 ++#define DMA_END_ENGINE_1_MASK 0x00000000u ++#define DMA_END_ENGINE_1_SHIFT 1 ++#define DMA_ERROR_ENGINE_0_MASK 0x00000100u ++#define DMA_ERROR_ENGINE_0_SHIFT 8 ++#define DMA_ERROR_ENGINE_1_MASK 0x00000200u ++#define DMA_ERROR_ENGINE_1_SHIFT 9 ++#define A_ATR_EVT_POST_ERR_MASK 0x00010000u ++#define A_ATR_EVT_POST_ERR_SHIFT 16 ++#define A_ATR_EVT_FETCH_ERR_MASK 0x00020000u ++#define A_ATR_EVT_FETCH_ERR_SHIFT 17 ++#define A_ATR_EVT_DISCARD_ERR_MASK 0x00040000u ++#define A_ATR_EVT_DISCARD_ERR_SHIFT 18 ++#define A_ATR_EVT_DOORBELL_MASK 0x00000000u ++#define A_ATR_EVT_DOORBELL_SHIFT 19 ++#define P_ATR_EVT_POST_ERR_MASK 0x00100000u ++#define P_ATR_EVT_POST_ERR_SHIFT 20 ++#define P_ATR_EVT_FETCH_ERR_MASK 0x00200000u ++#define P_ATR_EVT_FETCH_ERR_SHIFT 21 ++#define P_ATR_EVT_DISCARD_ERR_MASK 0x00400000u ++#define P_ATR_EVT_DISCARD_ERR_SHIFT 22 ++#define P_ATR_EVT_DOORBELL_MASK 0x00000000u ++#define P_ATR_EVT_DOORBELL_SHIFT 23 ++#define PM_MSI_INT_INTA_MASK 0x01000000u ++#define PM_MSI_INT_INTA_SHIFT 24 ++#define PM_MSI_INT_INTB_MASK 0x02000000u ++#define PM_MSI_INT_INTB_SHIFT 25 ++#define PM_MSI_INT_INTC_MASK 0x04000000u ++#define PM_MSI_INT_INTC_SHIFT 26 ++#define PM_MSI_INT_INTD_MASK 0x08000000u ++#define PM_MSI_INT_INTD_SHIFT 27 ++#define PM_MSI_INT_INTX_MASK 0x0f000000u ++#define PM_MSI_INT_INTX_SHIFT 24 ++#define PM_MSI_INT_MSI_MASK 0x10000000u ++#define PM_MSI_INT_MSI_SHIFT 28 ++#define PM_MSI_INT_AER_EVT_MASK 0x20000000u ++#define PM_MSI_INT_AER_EVT_SHIFT 29 ++#define PM_MSI_INT_EVENTS_MASK 0x40000000u ++#define PM_MSI_INT_EVENTS_SHIFT 30 ++#define PM_MSI_INT_SYS_ERR_MASK 0x80000000u ++#define PM_MSI_INT_SYS_ERR_SHIFT 31 ++#define NUM_LOCAL_EVENTS 15 ++#define ISTATUS_LOCAL 0x184 ++#define IMASK_HOST 0x188 ++#define ISTATUS_HOST 0x18c ++#define IMSI_ADDR 0x190 ++#define ISTATUS_MSI 0x194 ++ ++/* PCIe Master table init defines */ ++#define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u ++#define ATR0_PCIE_ATR_SIZE 0x25 ++#define ATR0_PCIE_ATR_SIZE_SHIFT 1 ++#define ATR0_PCIE_WIN0_SRC_ADDR 0x604u ++#define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u ++#define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu ++#define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u ++ ++/* PCIe AXI slave table init defines */ ++#define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u ++#define ATR_SIZE_SHIFT 1 ++#define ATR_IMPL_ENABLE 1 ++#define ATR0_AXI4_SLV0_SRC_ADDR 0x804u ++#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u ++#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu ++#define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u ++#define PCIE_TX_RX_INTERFACE 0x00000000u ++#define PCIE_CONFIG_INTERFACE 0x00000001u ++ ++#define ATR_ENTRY_SIZE 32 ++ ++enum plda_int_event { ++ PLDA_AXI_POST_ERR, ++ PLDA_AXI_FETCH_ERR, ++ PLDA_AXI_DISCARD_ERR, ++ PLDA_AXI_DOORBELL, ++ PLDA_PCIE_POST_ERR, ++ PLDA_PCIE_FETCH_ERR, ++ PLDA_PCIE_DISCARD_ERR, ++ PLDA_PCIE_DOORBELL, ++ PLDA_INTX, ++ PLDA_MSI, ++ PLDA_AER_EVENT, ++ PLDA_MISC_EVENTS, ++ PLDA_SYS_ERR, ++ PLDA_INT_EVENT_NUM ++}; ++ ++#define PLDA_NUM_DMA_EVENTS 16 ++ ++#define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) ++ ++#endif diff --git a/target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch b/target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch new file mode 100644 index 0000000000..bc3f909116 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0017-PCI-microchip-Add-bridge_addr-field-to-struct-mc_pci.patch @@ -0,0 +1,107 @@ +From 6ee4d4568314425079ae88229bb9abbff9b92b8b Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:54 +0800 +Subject: [PATCH 017/116] PCI: microchip: Add bridge_addr field to struct + mc_pcie + +For bridge address base is common PLDA field, Add this to struct mc_pcie +first. + +INTx and MSI codes interrupts codes will get the bridge base address from +port->bridge_addr. These codes will be changed to common codes. +axi_base_addr is Microchip its own data. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 23 ++++++++----------- + 1 file changed, 9 insertions(+), 14 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -195,6 +195,7 @@ struct mc_pcie { + struct irq_domain *event_domain; + raw_spinlock_t lock; + struct mc_msi msi; ++ void __iomem *bridge_addr; + }; + + struct cause { +@@ -339,8 +340,7 @@ static void mc_handle_msi(struct irq_des + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; + struct mc_msi *msi = &port->msi; +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long status; + u32 bit; + int ret; +@@ -365,8 +365,7 @@ static void mc_handle_msi(struct irq_des + static void mc_msi_bottom_irq_ack(struct irq_data *data) + { + struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + u32 bitpos = data->hwirq; + + writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); +@@ -488,8 +487,7 @@ static void mc_handle_intx(struct irq_de + struct mc_pcie *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long status; + u32 bit; + int ret; +@@ -514,8 +512,7 @@ static void mc_handle_intx(struct irq_de + static void mc_ack_intx_irq(struct irq_data *data) + { + struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + + writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); +@@ -524,8 +521,7 @@ static void mc_ack_intx_irq(struct irq_d + static void mc_mask_intx_irq(struct irq_data *data) + { + struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + u32 val; +@@ -540,8 +536,7 @@ static void mc_mask_intx_irq(struct irq_ + static void mc_unmask_intx_irq(struct irq_data *data) + { + struct mc_pcie *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + u32 val; +@@ -896,8 +891,7 @@ static void mc_pcie_setup_window(void __ + static int mc_pcie_setup_windows(struct platform_device *pdev, + struct mc_pcie *port) + { +- void __iomem *bridge_base_addr = +- port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ void __iomem *bridge_base_addr = port->bridge_addr; + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); + struct resource_entry *entry; + u64 pci_addr; +@@ -1081,6 +1075,7 @@ static int mc_host_probe(struct platform + mc_disable_interrupts(port); + + bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; ++ port->bridge_addr = bridge_base_addr; + + /* Allow enabling MSI by disabling MSI-X */ + val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); diff --git a/target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch b/target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch new file mode 100644 index 0000000000..a9e7b8975b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0018-PCI-microchip-Rename-two-PCIe-data-structures.patch @@ -0,0 +1,347 @@ +From 7c1c679bdd0b6b727248edbee77836024c935f91 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:55 +0800 +Subject: [PATCH 018/116] PCI: microchip: Rename two PCIe data structures + +Add PLDA PCIe related data structures by rename data structure name from +mc_* to plda_*. + +axi_base_addr is stayed in struct mc_pcie for it's microchip its own data. + +The event interrupt codes is still using struct mc_pcie because the event +interrupt codes can not be re-used. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 96 ++++++++++--------- + 1 file changed, 53 insertions(+), 43 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -22,7 +22,7 @@ + #include "pcie-plda.h" + + /* Number of MSI IRQs */ +-#define MC_MAX_NUM_MSI_IRQS 32 ++#define PLDA_MAX_NUM_MSI_IRQS 32 + + /* PCIe Bridge Phy and Controller Phy offsets */ + #define MC_PCIE1_BRIDGE_ADDR 0x00008000u +@@ -179,25 +179,29 @@ struct event_map { + u32 event_bit; + }; + +-struct mc_msi { ++struct plda_msi { + struct mutex lock; /* Protect used bitmap */ + struct irq_domain *msi_domain; + struct irq_domain *dev_domain; + u32 num_vectors; + u64 vector_phy; +- DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); ++ DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS); + }; + +-struct mc_pcie { +- void __iomem *axi_base_addr; ++struct plda_pcie_rp { + struct device *dev; + struct irq_domain *intx_domain; + struct irq_domain *event_domain; + raw_spinlock_t lock; +- struct mc_msi msi; ++ struct plda_msi msi; + void __iomem *bridge_addr; + }; + ++struct mc_pcie { ++ struct plda_pcie_rp plda; ++ void __iomem *axi_base_addr; ++}; ++ + struct cause { + const char *sym; + const char *str; +@@ -313,7 +317,7 @@ static struct mc_pcie *port; + + static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *ecam) + { +- struct mc_msi *msi = &port->msi; ++ struct plda_msi *msi = &port->plda.msi; + u16 reg; + u8 queue_size; + +@@ -336,10 +340,10 @@ static void mc_pcie_enable_msi(struct mc + + static void mc_handle_msi(struct irq_desc *desc) + { +- struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; +- struct mc_msi *msi = &port->msi; ++ struct plda_msi *msi = &port->msi; + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long status; + u32 bit; +@@ -364,7 +368,7 @@ static void mc_handle_msi(struct irq_des + + static void mc_msi_bottom_irq_ack(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + u32 bitpos = data->hwirq; + +@@ -373,7 +377,7 @@ static void mc_msi_bottom_irq_ack(struct + + static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + phys_addr_t addr = port->msi.vector_phy; + + msg->address_lo = lower_32_bits(addr); +@@ -400,8 +404,8 @@ static struct irq_chip mc_msi_bottom_irq + static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) + { +- struct mc_pcie *port = domain->host_data; +- struct mc_msi *msi = &port->msi; ++ struct plda_pcie_rp *port = domain->host_data; ++ struct plda_msi *msi = &port->msi; + unsigned long bit; + + mutex_lock(&msi->lock); +@@ -425,8 +429,8 @@ static void mc_irq_msi_domain_free(struc + unsigned int nr_irqs) + { + struct irq_data *d = irq_domain_get_irq_data(domain, virq); +- struct mc_pcie *port = irq_data_get_irq_chip_data(d); +- struct mc_msi *msi = &port->msi; ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); ++ struct plda_msi *msi = &port->msi; + + mutex_lock(&msi->lock); + +@@ -456,11 +460,11 @@ static struct msi_domain_info mc_msi_dom + .chip = &mc_msi_irq_chip, + }; + +-static int mc_allocate_msi_domains(struct mc_pcie *port) ++static int mc_allocate_msi_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); +- struct mc_msi *msi = &port->msi; ++ struct plda_msi *msi = &port->msi; + + mutex_init(&port->msi.lock); + +@@ -484,7 +488,7 @@ static int mc_allocate_msi_domains(struc + + static void mc_handle_intx(struct irq_desc *desc) + { +- struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = port->dev; + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -511,7 +515,7 @@ static void mc_handle_intx(struct irq_de + + static void mc_ack_intx_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); + +@@ -520,7 +524,7 @@ static void mc_ack_intx_irq(struct irq_d + + static void mc_mask_intx_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +@@ -535,7 +539,7 @@ static void mc_mask_intx_irq(struct irq_ + + static void mc_unmask_intx_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; + unsigned long flags; + u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +@@ -625,21 +629,22 @@ static u32 local_events(struct mc_pcie * + return val; + } + +-static u32 get_events(struct mc_pcie *port) ++static u32 get_events(struct plda_pcie_rp *port) + { ++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 events = 0; + +- events |= pcie_events(port); +- events |= sec_errors(port); +- events |= ded_errors(port); +- events |= local_events(port); ++ events |= pcie_events(mc_port); ++ events |= sec_errors(mc_port); ++ events |= ded_errors(mc_port); ++ events |= local_events(mc_port); + + return events; + } + + static irqreturn_t mc_event_handler(int irq, void *dev_id) + { +- struct mc_pcie *port = dev_id; ++ struct plda_pcie_rp *port = dev_id; + struct device *dev = port->dev; + struct irq_data *data; + +@@ -655,7 +660,7 @@ static irqreturn_t mc_event_handler(int + + static void mc_handle_event(struct irq_desc *desc) + { +- struct mc_pcie *port = irq_desc_get_handler_data(desc); ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + unsigned long events; + u32 bit; + struct irq_chip *chip = irq_desc_get_chip(desc); +@@ -672,12 +677,13 @@ static void mc_handle_event(struct irq_d + + static void mc_ack_event_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 event = data->hwirq; + void __iomem *addr; + u32 mask; + +- addr = port->axi_base_addr + event_descs[event].base + ++ addr = mc_port->axi_base_addr + event_descs[event].base + + event_descs[event].offset; + mask = event_descs[event].mask; + mask |= event_descs[event].enb_mask; +@@ -687,13 +693,14 @@ static void mc_ack_event_irq(struct irq_ + + static void mc_mask_event_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 event = data->hwirq; + void __iomem *addr; + u32 mask; + u32 val; + +- addr = port->axi_base_addr + event_descs[event].base + ++ addr = mc_port->axi_base_addr + event_descs[event].base + + event_descs[event].mask_offset; + mask = event_descs[event].mask; + if (event_descs[event].enb_mask) { +@@ -717,13 +724,14 @@ static void mc_mask_event_irq(struct irq + + static void mc_unmask_event_irq(struct irq_data *data) + { +- struct mc_pcie *port = irq_data_get_irq_chip_data(data); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 event = data->hwirq; + void __iomem *addr; + u32 mask; + u32 val; + +- addr = port->axi_base_addr + event_descs[event].base + ++ addr = mc_port->axi_base_addr + event_descs[event].base + + event_descs[event].mask_offset; + mask = event_descs[event].mask; + +@@ -811,7 +819,7 @@ static int mc_pcie_init_clks(struct devi + return 0; + } + +-static int mc_pcie_init_irq_domains(struct mc_pcie *port) ++static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; + struct device_node *node = dev->of_node; +@@ -889,7 +897,7 @@ static void mc_pcie_setup_window(void __ + } + + static int mc_pcie_setup_windows(struct platform_device *pdev, +- struct mc_pcie *port) ++ struct plda_pcie_rp *port) + { + void __iomem *bridge_base_addr = port->bridge_addr; + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); +@@ -970,7 +978,7 @@ static void mc_disable_interrupts(struct + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + } + +-static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) ++static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) + { + struct device *dev = &pdev->dev; + int irq; +@@ -1043,12 +1051,12 @@ static int mc_platform_init(struct pci_c + mc_pcie_enable_msi(port, cfg->win); + + /* Configure non-config space outbound ranges */ +- ret = mc_pcie_setup_windows(pdev, port); ++ ret = mc_pcie_setup_windows(pdev, &port->plda); + if (ret) + return ret; + + /* Address translation is up; safe to enable interrupts */ +- ret = mc_init_interrupts(pdev, port); ++ ret = mc_init_interrupts(pdev, &port->plda); + if (ret) + return ret; + +@@ -1059,6 +1067,7 @@ static int mc_host_probe(struct platform + { + struct device *dev = &pdev->dev; + void __iomem *bridge_base_addr; ++ struct plda_pcie_rp *plda; + int ret; + u32 val; + +@@ -1066,7 +1075,8 @@ static int mc_host_probe(struct platform + if (!port) + return -ENOMEM; + +- port->dev = dev; ++ plda = &port->plda; ++ plda->dev = dev; + + port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(port->axi_base_addr)) +@@ -1075,7 +1085,7 @@ static int mc_host_probe(struct platform + mc_disable_interrupts(port); + + bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; +- port->bridge_addr = bridge_base_addr; ++ plda->bridge_addr = bridge_base_addr; + + /* Allow enabling MSI by disabling MSI-X */ + val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); +@@ -1087,10 +1097,10 @@ static int mc_host_probe(struct platform + val &= NUM_MSI_MSGS_MASK; + val >>= NUM_MSI_MSGS_SHIFT; + +- port->msi.num_vectors = 1 << val; ++ plda->msi.num_vectors = 1 << val; + + /* Pick vector address from design */ +- port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); ++ plda->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); + + ret = mc_pcie_init_clks(dev); + if (ret) { diff --git a/target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch b/target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch new file mode 100644 index 0000000000..0823502cfd --- /dev/null +++ b/target/linux/starfive/patches-6.6/0019-PCI-microchip-Move-PCIe-host-data-structures-to-plda.patch @@ -0,0 +1,87 @@ +From a53cf9b237dd53c9538b51a8df592888935c8411 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:56 +0800 +Subject: [PATCH 019/116] PCI: microchip: Move PCIe host data structures to + plda-pcie.h + +Move the common data structures definition to head file for these two data +structures can be re-used. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 20 ------------------ + drivers/pci/controller/plda/pcie-plda.h | 21 +++++++++++++++++++ + 2 files changed, 21 insertions(+), 20 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -21,9 +21,6 @@ + #include "../../pci.h" + #include "pcie-plda.h" + +-/* Number of MSI IRQs */ +-#define PLDA_MAX_NUM_MSI_IRQS 32 +- + /* PCIe Bridge Phy and Controller Phy offsets */ + #define MC_PCIE1_BRIDGE_ADDR 0x00008000u + #define MC_PCIE1_CTRL_ADDR 0x0000a000u +@@ -179,23 +176,6 @@ struct event_map { + u32 event_bit; + }; + +-struct plda_msi { +- struct mutex lock; /* Protect used bitmap */ +- struct irq_domain *msi_domain; +- struct irq_domain *dev_domain; +- u32 num_vectors; +- u64 vector_phy; +- DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS); +-}; +- +-struct plda_pcie_rp { +- struct device *dev; +- struct irq_domain *intx_domain; +- struct irq_domain *event_domain; +- raw_spinlock_t lock; +- struct plda_msi msi; +- void __iomem *bridge_addr; +-}; + + struct mc_pcie { + struct plda_pcie_rp plda; +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -6,6 +6,9 @@ + #ifndef _PCIE_PLDA_H + #define _PCIE_PLDA_H + ++/* Number of MSI IRQs */ ++#define PLDA_MAX_NUM_MSI_IRQS 32 ++ + /* PCIe Bridge Phy Regs */ + #define PCIE_PCI_IRQ_DW0 0xa8 + #define MSIX_CAP_MASK BIT(31) +@@ -105,4 +108,22 @@ enum plda_int_event { + + #define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) + ++struct plda_msi { ++ struct mutex lock; /* Protect used bitmap */ ++ struct irq_domain *msi_domain; ++ struct irq_domain *dev_domain; ++ u32 num_vectors; ++ u64 vector_phy; ++ DECLARE_BITMAP(used, PLDA_MAX_NUM_MSI_IRQS); ++}; ++ ++struct plda_pcie_rp { ++ struct device *dev; ++ struct irq_domain *intx_domain; ++ struct irq_domain *event_domain; ++ raw_spinlock_t lock; ++ struct plda_msi msi; ++ void __iomem *bridge_addr; ++}; ++ + #endif diff --git a/target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch b/target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch new file mode 100644 index 0000000000..2bed600216 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0020-PCI-microchip-Rename-two-setup-functions.patch @@ -0,0 +1,76 @@ +From d0e56d1ef7398bbf76be6e48d77943cbf644688e Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:57 +0800 +Subject: [PATCH 020/116] PCI: microchip: Rename two setup functions + +Rename two setup functions to plda prefix. Prepare to re-use these two +setup function. + +For two setup functions names are similar, rename mc_pcie_setup_windows() +to plda_pcie_setup_iomems(). + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 24 +++++++++---------- + 1 file changed, 12 insertions(+), 12 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -838,9 +838,9 @@ static int mc_pcie_init_irq_domains(stru + return mc_allocate_msi_domains(port); + } + +-static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, +- phys_addr_t axi_addr, phys_addr_t pci_addr, +- size_t size) ++static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, ++ phys_addr_t axi_addr, phys_addr_t pci_addr, ++ size_t size) + { + u32 atr_sz = ilog2(size) - 1; + u32 val; +@@ -876,8 +876,8 @@ static void mc_pcie_setup_window(void __ + writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); + } + +-static int mc_pcie_setup_windows(struct platform_device *pdev, +- struct plda_pcie_rp *port) ++static int plda_pcie_setup_iomems(struct platform_device *pdev, ++ struct plda_pcie_rp *port) + { + void __iomem *bridge_base_addr = port->bridge_addr; + struct pci_host_bridge *bridge = platform_get_drvdata(pdev); +@@ -888,9 +888,9 @@ static int mc_pcie_setup_windows(struct + resource_list_for_each_entry(entry, &bridge->windows) { + if (resource_type(entry->res) == IORESOURCE_MEM) { + pci_addr = entry->res->start - entry->offset; +- mc_pcie_setup_window(bridge_base_addr, index, +- entry->res->start, pci_addr, +- resource_size(entry->res)); ++ plda_pcie_setup_window(bridge_base_addr, index, ++ entry->res->start, pci_addr, ++ resource_size(entry->res)); + index++; + } + } +@@ -1023,15 +1023,15 @@ static int mc_platform_init(struct pci_c + int ret; + + /* Configure address translation table 0 for PCIe config space */ +- mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, +- cfg->res.start, +- resource_size(&cfg->res)); ++ plda_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, ++ cfg->res.start, ++ resource_size(&cfg->res)); + + /* Need some fixups in config space */ + mc_pcie_enable_msi(port, cfg->win); + + /* Configure non-config space outbound ranges */ +- ret = mc_pcie_setup_windows(pdev, &port->plda); ++ ret = plda_pcie_setup_iomems(pdev, &port->plda); + if (ret) + return ret; + diff --git a/target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch b/target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch new file mode 100644 index 0000000000..de6868b9f6 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0021-PCI-microchip-Change-the-argument-of-plda_pcie_setup.patch @@ -0,0 +1,49 @@ +From 2fd7c88ef318fd39023ce1eb73f37a29fbd25d74 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:58 +0800 +Subject: [PATCH 021/116] PCI: microchip: Change the argument of + plda_pcie_setup_iomems() + +If other vendor do not select PCI_HOST_COMMON, the driver data is not +struct pci_host_bridge. + +Move calling platform_get_drvdata() to mc_platform_init(). + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + drivers/pci/controller/plda/pcie-microchip-host.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -876,11 +876,10 @@ static void plda_pcie_setup_window(void + writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); + } + +-static int plda_pcie_setup_iomems(struct platform_device *pdev, ++static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, + struct plda_pcie_rp *port) + { + void __iomem *bridge_base_addr = port->bridge_addr; +- struct pci_host_bridge *bridge = platform_get_drvdata(pdev); + struct resource_entry *entry; + u64 pci_addr; + u32 index = 1; +@@ -1018,6 +1017,7 @@ static int mc_platform_init(struct pci_c + { + struct device *dev = cfg->parent; + struct platform_device *pdev = to_platform_device(dev); ++ struct pci_host_bridge *bridge = platform_get_drvdata(pdev); + void __iomem *bridge_base_addr = + port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + int ret; +@@ -1031,7 +1031,7 @@ static int mc_platform_init(struct pci_c + mc_pcie_enable_msi(port, cfg->win); + + /* Configure non-config space outbound ranges */ +- ret = plda_pcie_setup_iomems(pdev, &port->plda); ++ ret = plda_pcie_setup_iomems(bridge, &port->plda); + if (ret) + return ret; + diff --git a/target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch b/target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch new file mode 100644 index 0000000000..62f458be4a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0022-PCI-microchip-Move-setup-functions-to-pcie-plda-host.patch @@ -0,0 +1,200 @@ +From 201ce99897ff5fff2612cb633406e90c1b2acbcf Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:05:59 +0800 +Subject: [PATCH 022/116] PCI: microchip: Move setup functions to + pcie-plda-host.c + +Move setup functions to common pcie-plda-host.c. So these two functions +can be re-used. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + drivers/pci/controller/plda/Kconfig | 4 + + drivers/pci/controller/plda/Makefile | 1 + + .../pci/controller/plda/pcie-microchip-host.c | 59 --------------- + drivers/pci/controller/plda/pcie-plda-host.c | 74 +++++++++++++++++++ + drivers/pci/controller/plda/pcie-plda.h | 5 ++ + 5 files changed, 84 insertions(+), 59 deletions(-) + create mode 100644 drivers/pci/controller/plda/pcie-plda-host.c + +--- a/drivers/pci/controller/plda/Kconfig ++++ b/drivers/pci/controller/plda/Kconfig +@@ -3,10 +3,14 @@ + menu "PLDA-based PCIe controllers" + depends on PCI + ++config PCIE_PLDA_HOST ++ bool ++ + config PCIE_MICROCHIP_HOST + tristate "Microchip AXI PCIe controller" + depends on PCI_MSI && OF + select PCI_HOST_COMMON ++ select PCIE_PLDA_HOST + help + Say Y here if you want kernel to support the Microchip AXI PCIe + Host Bridge driver. +--- a/drivers/pci/controller/plda/Makefile ++++ b/drivers/pci/controller/plda/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o + obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -838,65 +838,6 @@ static int mc_pcie_init_irq_domains(stru + return mc_allocate_msi_domains(port); + } + +-static void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, +- phys_addr_t axi_addr, phys_addr_t pci_addr, +- size_t size) +-{ +- u32 atr_sz = ilog2(size) - 1; +- u32 val; +- +- if (index == 0) +- val = PCIE_CONFIG_INTERFACE; +- else +- val = PCIE_TX_RX_INTERFACE; +- +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_PARAM); +- +- val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | +- ATR_IMPL_ENABLE; +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_SRCADDR_PARAM); +- +- val = upper_32_bits(axi_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_SRC_ADDR); +- +- val = lower_32_bits(pci_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_ADDR_LSB); +- +- val = upper_32_bits(pci_addr); +- writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + +- ATR0_AXI4_SLV0_TRSL_ADDR_UDW); +- +- val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); +- val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); +- writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); +- writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); +-} +- +-static int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, +- struct plda_pcie_rp *port) +-{ +- void __iomem *bridge_base_addr = port->bridge_addr; +- struct resource_entry *entry; +- u64 pci_addr; +- u32 index = 1; +- +- resource_list_for_each_entry(entry, &bridge->windows) { +- if (resource_type(entry->res) == IORESOURCE_MEM) { +- pci_addr = entry->res->start - entry->offset; +- plda_pcie_setup_window(bridge_base_addr, index, +- entry->res->start, pci_addr, +- resource_size(entry->res)); +- index++; +- } +- } +- +- return 0; +-} +- + static inline void mc_clear_secs(struct mc_pcie *port) + { + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +--- /dev/null ++++ b/drivers/pci/controller/plda/pcie-plda-host.c +@@ -0,0 +1,74 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * PLDA PCIe XpressRich host controller driver ++ * ++ * Copyright (C) 2023 Microchip Co. Ltd ++ * ++ * Author: Daire McNamara ++ */ ++ ++#include ++#include ++ ++#include "pcie-plda.h" ++ ++void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, ++ phys_addr_t axi_addr, phys_addr_t pci_addr, ++ size_t size) ++{ ++ u32 atr_sz = ilog2(size) - 1; ++ u32 val; ++ ++ if (index == 0) ++ val = PCIE_CONFIG_INTERFACE; ++ else ++ val = PCIE_TX_RX_INTERFACE; ++ ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_PARAM); ++ ++ val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | ++ ATR_IMPL_ENABLE; ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_SRCADDR_PARAM); ++ ++ val = upper_32_bits(axi_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_SRC_ADDR); ++ ++ val = lower_32_bits(pci_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_ADDR_LSB); ++ ++ val = upper_32_bits(pci_addr); ++ writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + ++ ATR0_AXI4_SLV0_TRSL_ADDR_UDW); ++ ++ val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); ++ val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); ++ writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); ++ writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); ++} ++EXPORT_SYMBOL_GPL(plda_pcie_setup_window); ++ ++int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, ++ struct plda_pcie_rp *port) ++{ ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ struct resource_entry *entry; ++ u64 pci_addr; ++ u32 index = 1; ++ ++ resource_list_for_each_entry(entry, &bridge->windows) { ++ if (resource_type(entry->res) == IORESOURCE_MEM) { ++ pci_addr = entry->res->start - entry->offset; ++ plda_pcie_setup_window(bridge_base_addr, index, ++ entry->res->start, pci_addr, ++ resource_size(entry->res)); ++ index++; ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -126,4 +126,9 @@ struct plda_pcie_rp { + void __iomem *bridge_addr; + }; + ++void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, ++ phys_addr_t axi_addr, phys_addr_t pci_addr, ++ size_t size); ++int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, ++ struct plda_pcie_rp *port); + #endif diff --git a/target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch b/target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch new file mode 100644 index 0000000000..09c1633b6f --- /dev/null +++ b/target/linux/starfive/patches-6.6/0023-PCI-microchip-Rename-interrupt-related-functions.patch @@ -0,0 +1,336 @@ +From 2a48bc45dcf8cbe736b594d013cfa9d682214c43 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:00 +0800 +Subject: [PATCH 023/116] PCI: microchip: Rename interrupt related functions + +Rename mc_* to plda_* for IRQ functions and related IRQ domain ops data +instances. + +MSI, INTx interrupt code and IRQ init code are all can be re-used. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 109 +++++++++--------- + 1 file changed, 57 insertions(+), 52 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -318,7 +318,7 @@ static void mc_pcie_enable_msi(struct mc + ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); + } + +-static void mc_handle_msi(struct irq_desc *desc) ++static void plda_handle_msi(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); +@@ -346,7 +346,7 @@ static void mc_handle_msi(struct irq_des + chained_irq_exit(chip, desc); + } + +-static void mc_msi_bottom_irq_ack(struct irq_data *data) ++static void plda_msi_bottom_irq_ack(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -355,7 +355,7 @@ static void mc_msi_bottom_irq_ack(struct + writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); + } + +-static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ++static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + phys_addr_t addr = port->msi.vector_phy; +@@ -368,21 +368,23 @@ static void mc_compose_msi_msg(struct ir + (int)data->hwirq, msg->address_hi, msg->address_lo); + } + +-static int mc_msi_set_affinity(struct irq_data *irq_data, +- const struct cpumask *mask, bool force) ++static int plda_msi_set_affinity(struct irq_data *irq_data, ++ const struct cpumask *mask, bool force) + { + return -EINVAL; + } + +-static struct irq_chip mc_msi_bottom_irq_chip = { +- .name = "Microchip MSI", +- .irq_ack = mc_msi_bottom_irq_ack, +- .irq_compose_msi_msg = mc_compose_msi_msg, +- .irq_set_affinity = mc_msi_set_affinity, ++static struct irq_chip plda_msi_bottom_irq_chip = { ++ .name = "PLDA MSI", ++ .irq_ack = plda_msi_bottom_irq_ack, ++ .irq_compose_msi_msg = plda_compose_msi_msg, ++ .irq_set_affinity = plda_msi_set_affinity, + }; + +-static int mc_irq_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, +- unsigned int nr_irqs, void *args) ++static int plda_irq_msi_domain_alloc(struct irq_domain *domain, ++ unsigned int virq, ++ unsigned int nr_irqs, ++ void *args) + { + struct plda_pcie_rp *port = domain->host_data; + struct plda_msi *msi = &port->msi; +@@ -397,7 +399,7 @@ static int mc_irq_msi_domain_alloc(struc + + set_bit(bit, msi->used); + +- irq_domain_set_info(domain, virq, bit, &mc_msi_bottom_irq_chip, ++ irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, + domain->host_data, handle_edge_irq, NULL, NULL); + + mutex_unlock(&msi->lock); +@@ -405,8 +407,9 @@ static int mc_irq_msi_domain_alloc(struc + return 0; + } + +-static void mc_irq_msi_domain_free(struct irq_domain *domain, unsigned int virq, +- unsigned int nr_irqs) ++static void plda_irq_msi_domain_free(struct irq_domain *domain, ++ unsigned int virq, ++ unsigned int nr_irqs) + { + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); +@@ -423,24 +426,24 @@ static void mc_irq_msi_domain_free(struc + } + + static const struct irq_domain_ops msi_domain_ops = { +- .alloc = mc_irq_msi_domain_alloc, +- .free = mc_irq_msi_domain_free, ++ .alloc = plda_irq_msi_domain_alloc, ++ .free = plda_irq_msi_domain_free, + }; + +-static struct irq_chip mc_msi_irq_chip = { +- .name = "Microchip PCIe MSI", ++static struct irq_chip plda_msi_irq_chip = { ++ .name = "PLDA PCIe MSI", + .irq_ack = irq_chip_ack_parent, + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, + }; + +-static struct msi_domain_info mc_msi_domain_info = { ++static struct msi_domain_info plda_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), +- .chip = &mc_msi_irq_chip, ++ .chip = &plda_msi_irq_chip, + }; + +-static int mc_allocate_msi_domains(struct plda_pcie_rp *port) ++static int plda_allocate_msi_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); +@@ -455,7 +458,8 @@ static int mc_allocate_msi_domains(struc + return -ENOMEM; + } + +- msi->msi_domain = pci_msi_create_irq_domain(fwnode, &mc_msi_domain_info, ++ msi->msi_domain = pci_msi_create_irq_domain(fwnode, ++ &plda_msi_domain_info, + msi->dev_domain); + if (!msi->msi_domain) { + dev_err(dev, "failed to create MSI domain\n"); +@@ -466,7 +470,7 @@ static int mc_allocate_msi_domains(struc + return 0; + } + +-static void mc_handle_intx(struct irq_desc *desc) ++static void plda_handle_intx(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); +@@ -493,7 +497,7 @@ static void mc_handle_intx(struct irq_de + chained_irq_exit(chip, desc); + } + +-static void mc_ack_intx_irq(struct irq_data *data) ++static void plda_ack_intx_irq(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -502,7 +506,7 @@ static void mc_ack_intx_irq(struct irq_d + writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); + } + +-static void mc_mask_intx_irq(struct irq_data *data) ++static void plda_mask_intx_irq(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -517,7 +521,7 @@ static void mc_mask_intx_irq(struct irq_ + raw_spin_unlock_irqrestore(&port->lock, flags); + } + +-static void mc_unmask_intx_irq(struct irq_data *data) ++static void plda_unmask_intx_irq(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); + void __iomem *bridge_base_addr = port->bridge_addr; +@@ -532,24 +536,24 @@ static void mc_unmask_intx_irq(struct ir + raw_spin_unlock_irqrestore(&port->lock, flags); + } + +-static struct irq_chip mc_intx_irq_chip = { +- .name = "Microchip PCIe INTx", +- .irq_ack = mc_ack_intx_irq, +- .irq_mask = mc_mask_intx_irq, +- .irq_unmask = mc_unmask_intx_irq, ++static struct irq_chip plda_intx_irq_chip = { ++ .name = "PLDA PCIe INTx", ++ .irq_ack = plda_ack_intx_irq, ++ .irq_mask = plda_mask_intx_irq, ++ .irq_unmask = plda_unmask_intx_irq, + }; + +-static int mc_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) ++static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) + { +- irq_set_chip_and_handler(irq, &mc_intx_irq_chip, handle_level_irq); ++ irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; + } + + static const struct irq_domain_ops intx_domain_ops = { +- .map = mc_pcie_intx_map, ++ .map = plda_pcie_intx_map, + }; + + static inline u32 reg_to_event(u32 reg, struct event_map field) +@@ -609,7 +613,7 @@ static u32 local_events(struct mc_pcie * + return val; + } + +-static u32 get_events(struct plda_pcie_rp *port) ++static u32 mc_get_events(struct plda_pcie_rp *port) + { + struct mc_pcie *mc_port = container_of(port, struct mc_pcie, plda); + u32 events = 0; +@@ -638,7 +642,7 @@ static irqreturn_t mc_event_handler(int + return IRQ_HANDLED; + } + +-static void mc_handle_event(struct irq_desc *desc) ++static void plda_handle_event(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); + unsigned long events; +@@ -647,7 +651,7 @@ static void mc_handle_event(struct irq_d + + chained_irq_enter(chip, desc); + +- events = get_events(port); ++ events = mc_get_events(port); + + for_each_set_bit(bit, &events, NUM_EVENTS) + generic_handle_domain_irq(port->event_domain, bit); +@@ -741,8 +745,8 @@ static struct irq_chip mc_event_irq_chip + .irq_unmask = mc_unmask_event_irq, + }; + +-static int mc_pcie_event_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) ++static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) + { + irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); +@@ -750,8 +754,8 @@ static int mc_pcie_event_map(struct irq_ + return 0; + } + +-static const struct irq_domain_ops event_domain_ops = { +- .map = mc_pcie_event_map, ++static const struct irq_domain_ops plda_event_domain_ops = { ++ .map = plda_pcie_event_map, + }; + + static inline void mc_pcie_deinit_clk(void *data) +@@ -799,7 +803,7 @@ static int mc_pcie_init_clks(struct devi + return 0; + } + +-static int mc_pcie_init_irq_domains(struct plda_pcie_rp *port) ++static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; + struct device_node *node = dev->of_node; +@@ -813,7 +817,8 @@ static int mc_pcie_init_irq_domains(stru + } + + port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, +- &event_domain_ops, port); ++ &plda_event_domain_ops, ++ port); + if (!port->event_domain) { + dev_err(dev, "failed to get event domain\n"); + of_node_put(pcie_intc_node); +@@ -835,7 +840,7 @@ static int mc_pcie_init_irq_domains(stru + of_node_put(pcie_intc_node); + raw_spin_lock_init(&port->lock); + +- return mc_allocate_msi_domains(port); ++ return plda_allocate_msi_domains(port); + } + + static inline void mc_clear_secs(struct mc_pcie *port) +@@ -898,14 +903,14 @@ static void mc_disable_interrupts(struct + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + } + +-static int mc_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) ++static int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) + { + struct device *dev = &pdev->dev; + int irq; + int i, intx_irq, msi_irq, event_irq; + int ret; + +- ret = mc_pcie_init_irq_domains(port); ++ ret = plda_pcie_init_irq_domains(port); + if (ret) { + dev_err(dev, "failed creating IRQ domains\n"); + return ret; +@@ -938,7 +943,7 @@ static int mc_init_interrupts(struct pla + } + + /* Plug the INTx chained handler */ +- irq_set_chained_handler_and_data(intx_irq, mc_handle_intx, port); ++ irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); + + msi_irq = irq_create_mapping(port->event_domain, + EVENT_LOCAL_PM_MSI_INT_MSI); +@@ -946,10 +951,10 @@ static int mc_init_interrupts(struct pla + return -ENXIO; + + /* Plug the MSI chained handler */ +- irq_set_chained_handler_and_data(msi_irq, mc_handle_msi, port); ++ irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); + + /* Plug the main event chained handler */ +- irq_set_chained_handler_and_data(irq, mc_handle_event, port); ++ irq_set_chained_handler_and_data(irq, plda_handle_event, port); + + return 0; + } +@@ -977,7 +982,7 @@ static int mc_platform_init(struct pci_c + return ret; + + /* Address translation is up; safe to enable interrupts */ +- ret = mc_init_interrupts(pdev, &port->plda); ++ ret = plda_init_interrupts(pdev, &port->plda); + if (ret) + return ret; + diff --git a/target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch b/target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch new file mode 100644 index 0000000000..4071feb1b5 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0024-PCI-microchip-Add-num_events-field-to-struct-plda_pc.patch @@ -0,0 +1,65 @@ +From ab04dadb45a4150c9fd55b97fdd7397f4739a629 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:01 +0800 +Subject: [PATCH 024/116] PCI: microchip: Add num_events field to struct + plda_pcie_rp + +The number of events is different across platforms. In order to share +interrupt processing code, add a variable that defines the number of +events so that it can be set per-platform instead of hardcoding it. + +Signed-off-by: Minda Chen +Reviewed-by: Conor Dooley +--- + drivers/pci/controller/plda/pcie-microchip-host.c | 8 +++++--- + drivers/pci/controller/plda/pcie-plda.h | 1 + + 2 files changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -653,7 +653,7 @@ static void plda_handle_event(struct irq + + events = mc_get_events(port); + +- for_each_set_bit(bit, &events, NUM_EVENTS) ++ for_each_set_bit(bit, &events, port->num_events) + generic_handle_domain_irq(port->event_domain, bit); + + chained_irq_exit(chip, desc); +@@ -816,7 +816,8 @@ static int plda_pcie_init_irq_domains(st + return -EINVAL; + } + +- port->event_domain = irq_domain_add_linear(pcie_intc_node, NUM_EVENTS, ++ port->event_domain = irq_domain_add_linear(pcie_intc_node, ++ port->num_events, + &plda_event_domain_ops, + port); + if (!port->event_domain) { +@@ -920,7 +921,7 @@ static int plda_init_interrupts(struct p + if (irq < 0) + return -ENODEV; + +- for (i = 0; i < NUM_EVENTS; i++) { ++ for (i = 0; i < port->num_events; i++) { + event_irq = irq_create_mapping(port->event_domain, i); + if (!event_irq) { + dev_err(dev, "failed to map hwirq %d\n", i); +@@ -1012,6 +1013,7 @@ static int mc_host_probe(struct platform + + bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + plda->bridge_addr = bridge_base_addr; ++ plda->num_events = NUM_EVENTS; + + /* Allow enabling MSI by disabling MSI-X */ + val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -124,6 +124,7 @@ struct plda_pcie_rp { + raw_spinlock_t lock; + struct plda_msi msi; + void __iomem *bridge_addr; ++ int num_events; + }; + + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, diff --git a/target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch b/target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch new file mode 100644 index 0000000000..2b299e01e9 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0025-PCI-microchip-Add-request_event_irq-callback-functio.patch @@ -0,0 +1,114 @@ +From 9f202f211cc79eefecbb03715c884e54eb95a62c Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:02 +0800 +Subject: [PATCH 025/116] PCI: microchip: Add request_event_irq() callback + function + +As PLDA dts binding doc(Documentation/devicetree/bindings/pci/ +plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt +controller. Microchip Polarfire PCIe add some PCIe interrupts base on +PLDA IP interrupt controller. + +Microchip Polarfire PCIe additional intrerrupts: +EVENT_PCIE_L2_EXIT +EVENT_PCIE_HOTRST_EXIT +EVENT_PCIE_DLUP_EXIT +EVENT_SEC_TX_RAM_SEC_ERR +EVENT_SEC_RX_RAM_SEC_ERR +.... + +Both codes of register interrupts and mc_event_handler() contain +additional interrupts symbol names, these can not be re-used. So add a +new plda_event_handler() functions, which implements PLDA interrupt +defalt handler. Add request_event_irq() callback function to +compat Microchip Polorfire PCIe additional interrupts. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 31 ++++++++++++++++--- + drivers/pci/controller/plda/pcie-plda.h | 5 +++ + 2 files changed, 32 insertions(+), 4 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -642,6 +642,11 @@ static irqreturn_t mc_event_handler(int + return IRQ_HANDLED; + } + ++static irqreturn_t plda_event_handler(int irq, void *dev_id) ++{ ++ return IRQ_HANDLED; ++} ++ + static void plda_handle_event(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +@@ -803,6 +808,17 @@ static int mc_pcie_init_clks(struct devi + return 0; + } + ++static int mc_request_event_irq(struct plda_pcie_rp *plda, int event_irq, ++ int event) ++{ ++ return devm_request_irq(plda->dev, event_irq, mc_event_handler, ++ 0, event_cause[event].sym, plda); ++} ++ ++static const struct plda_event mc_event = { ++ .request_event_irq = mc_request_event_irq, ++}; ++ + static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) + { + struct device *dev = port->dev; +@@ -904,7 +920,9 @@ static void mc_disable_interrupts(struct + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + } + +-static int plda_init_interrupts(struct platform_device *pdev, struct plda_pcie_rp *port) ++static int plda_init_interrupts(struct platform_device *pdev, ++ struct plda_pcie_rp *port, ++ const struct plda_event *event) + { + struct device *dev = &pdev->dev; + int irq; +@@ -928,8 +946,13 @@ static int plda_init_interrupts(struct p + return -ENXIO; + } + +- ret = devm_request_irq(dev, event_irq, mc_event_handler, +- 0, event_cause[i].sym, port); ++ if (event->request_event_irq) ++ ret = event->request_event_irq(port, event_irq, i); ++ else ++ ret = devm_request_irq(dev, event_irq, ++ plda_event_handler, ++ 0, NULL, port); ++ + if (ret) { + dev_err(dev, "failed to request IRQ %d\n", event_irq); + return ret; +@@ -983,7 +1006,7 @@ static int mc_platform_init(struct pci_c + return ret; + + /* Address translation is up; safe to enable interrupts */ +- ret = plda_init_interrupts(pdev, &port->plda); ++ ret = plda_init_interrupts(pdev, &port->plda, &mc_event); + if (ret) + return ret; + +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -127,6 +127,11 @@ struct plda_pcie_rp { + int num_events; + }; + ++struct plda_event { ++ int (*request_event_irq)(struct plda_pcie_rp *pcie, ++ int event_irq, int event); ++}; ++ + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size); diff --git a/target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch b/target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch new file mode 100644 index 0000000000..85b8143268 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0026-PCI-microchip-Add-INTx-and-MSI-event-num-to-struct-p.patch @@ -0,0 +1,56 @@ +From 3cdc20d9cc029ba9444be111bf4e55fd5331ccbe Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:03 +0800 +Subject: [PATCH 026/116] PCI: microchip: Add INTx and MSI event num to struct + plda_event + +The INTx and MSI interrupt event num is different in Microchip and +StarFive platform. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + drivers/pci/controller/plda/pcie-microchip-host.c | 6 ++++-- + drivers/pci/controller/plda/pcie-plda.h | 2 ++ + 2 files changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -817,6 +817,8 @@ static int mc_request_event_irq(struct p + + static const struct plda_event mc_event = { + .request_event_irq = mc_request_event_irq, ++ .intx_event = EVENT_LOCAL_PM_MSI_INT_INTX, ++ .msi_event = EVENT_LOCAL_PM_MSI_INT_MSI, + }; + + static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) +@@ -960,7 +962,7 @@ static int plda_init_interrupts(struct p + } + + intx_irq = irq_create_mapping(port->event_domain, +- EVENT_LOCAL_PM_MSI_INT_INTX); ++ event->intx_event); + if (!intx_irq) { + dev_err(dev, "failed to map INTx interrupt\n"); + return -ENXIO; +@@ -970,7 +972,7 @@ static int plda_init_interrupts(struct p + irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); + + msi_irq = irq_create_mapping(port->event_domain, +- EVENT_LOCAL_PM_MSI_INT_MSI); ++ event->msi_event); + if (!msi_irq) + return -ENXIO; + +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -130,6 +130,8 @@ struct plda_pcie_rp { + struct plda_event { + int (*request_event_irq)(struct plda_pcie_rp *pcie, + int event_irq, int event); ++ int intx_event; ++ int msi_event; + }; + + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, diff --git a/target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch b/target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch new file mode 100644 index 0000000000..a0cb4789b8 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0027-PCI-microchip-Add-get_events-callback-and-add-PLDA-g.patch @@ -0,0 +1,167 @@ +From b4a38ef87661f21fe2fb3e085ae98f25f78aaf99 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:04 +0800 +Subject: [PATCH 027/116] PCI: microchip: Add get_events() callback and add + PLDA get_event() + +As PLDA dts binding doc(Documentation/devicetree/bindings/pci/ +plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt +controller. + +PolarFire implements its own PCIe interrupts, additional to the regular +PCIe interrupts, due to lack of an MSI controller, so the interrupt to +event number mapping is different to the PLDA regular interrupts, +necessitating a custom get_events() implementation. + +Microchip Polarfire PCIe additional intrerrupts: +EVENT_PCIE_L2_EXIT +EVENT_PCIE_HOTRST_EXIT +EVENT_PCIE_DLUP_EXIT +EVENT_SEC_TX_RAM_SEC_ERR +EVENT_SEC_RX_RAM_SEC_ERR +.... + +plda_get_events() adds interrupt register to PLDA local event num mapping +codes. All The PLDA interrupts can be seen in new added graph. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 35 ++++++++++++++++++- + drivers/pci/controller/plda/pcie-plda.h | 32 +++++++++++++++++ + 2 files changed, 66 insertions(+), 1 deletion(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -626,6 +626,26 @@ static u32 mc_get_events(struct plda_pci + return events; + } + ++static u32 plda_get_events(struct plda_pcie_rp *port) ++{ ++ u32 events, val, origin; ++ ++ origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL); ++ ++ /* MSI event and sys events */ ++ val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT; ++ events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1); ++ ++ /* INTx events */ ++ if (origin & PM_MSI_INT_INTX_MASK) ++ events |= BIT(PM_MSI_INT_INTX_SHIFT); ++ ++ /* remains are same with register */ ++ events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0); ++ ++ return events; ++} ++ + static irqreturn_t mc_event_handler(int irq, void *dev_id) + { + struct plda_pcie_rp *port = dev_id; +@@ -656,7 +676,7 @@ static void plda_handle_event(struct irq + + chained_irq_enter(chip, desc); + +- events = mc_get_events(port); ++ events = port->event_ops->get_events(port); + + for_each_set_bit(bit, &events, port->num_events) + generic_handle_domain_irq(port->event_domain, bit); +@@ -750,6 +770,10 @@ static struct irq_chip mc_event_irq_chip + .irq_unmask = mc_unmask_event_irq, + }; + ++static const struct plda_event_ops plda_event_ops = { ++ .get_events = plda_get_events, ++}; ++ + static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) + { +@@ -815,6 +839,10 @@ static int mc_request_event_irq(struct p + 0, event_cause[event].sym, plda); + } + ++static const struct plda_event_ops mc_event_ops = { ++ .get_events = mc_get_events, ++}; ++ + static const struct plda_event mc_event = { + .request_event_irq = mc_request_event_irq, + .intx_event = EVENT_LOCAL_PM_MSI_INT_INTX, +@@ -931,6 +959,9 @@ static int plda_init_interrupts(struct p + int i, intx_irq, msi_irq, event_irq; + int ret; + ++ if (!port->event_ops) ++ port->event_ops = &plda_event_ops; ++ + ret = plda_pcie_init_irq_domains(port); + if (ret) { + dev_err(dev, "failed creating IRQ domains\n"); +@@ -1007,6 +1038,8 @@ static int mc_platform_init(struct pci_c + if (ret) + return ret; + ++ port->plda.event_ops = &mc_event_ops; ++ + /* Address translation is up; safe to enable interrupts */ + ret = plda_init_interrupts(pdev, &port->plda, &mc_event); + if (ret) +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -58,6 +58,7 @@ + #define PM_MSI_INT_EVENTS_SHIFT 30 + #define PM_MSI_INT_SYS_ERR_MASK 0x80000000u + #define PM_MSI_INT_SYS_ERR_SHIFT 31 ++#define SYS_AND_MSI_MASK GENMASK(31, 28) + #define NUM_LOCAL_EVENTS 15 + #define ISTATUS_LOCAL 0x184 + #define IMASK_HOST 0x188 +@@ -108,6 +109,36 @@ enum plda_int_event { + + #define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) + ++/* ++ * PLDA interrupt register ++ * ++ * 31 27 23 15 7 0 ++ * +--+--+--+-+------+-+-+-+-+-+-+-+-+-----------+-----------+ ++ * |12|11|10|9| intx |7|6|5|4|3|2|1|0| DMA error | DMA end | ++ * +--+--+--+-+------+-+-+-+-+-+-+-+-+-----------+-----------+ ++ * bit 0-7 DMA interrupt end : reserved for vendor implement ++ * bit 8-15 DMA error : reserved for vendor implement ++ * 0: AXI post error (PLDA_AXI_POST_ERR) ++ * 1: AXI fetch error (PLDA_AXI_FETCH_ERR) ++ * 2: AXI discard error (PLDA_AXI_DISCARD_ERR) ++ * 3: AXI doorbell (PLDA_PCIE_DOORBELL) ++ * 4: PCIe post error (PLDA_PCIE_POST_ERR) ++ * 5: PCIe fetch error (PLDA_PCIE_FETCH_ERR) ++ * 6: PCIe discard error (PLDA_PCIE_DISCARD_ERR) ++ * 7: PCIe doorbell (PLDA_PCIE_DOORBELL) ++ * 8: 4 INTx interruts (PLDA_INTX) ++ * 9: MSI interrupt (PLDA_MSI) ++ * 10: AER event (PLDA_AER_EVENT) ++ * 11: PM/LTR/Hotplug (PLDA_MISC_EVENTS) ++ * 12: System error (PLDA_SYS_ERR) ++ */ ++ ++struct plda_pcie_rp; ++ ++struct plda_event_ops { ++ u32 (*get_events)(struct plda_pcie_rp *pcie); ++}; ++ + struct plda_msi { + struct mutex lock; /* Protect used bitmap */ + struct irq_domain *msi_domain; +@@ -123,6 +154,7 @@ struct plda_pcie_rp { + struct irq_domain *event_domain; + raw_spinlock_t lock; + struct plda_msi msi; ++ const struct plda_event_ops *event_ops; + void __iomem *bridge_addr; + int num_events; + }; diff --git a/target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch b/target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch new file mode 100644 index 0000000000..9ed8119b1e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0028-PCI-microchip-Add-event-irqchip-field-to-host-port-a.patch @@ -0,0 +1,144 @@ +From 229ea8e7b674eb5c9bc4f70d43df1bd02a79862a Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:05 +0800 +Subject: [PATCH 028/116] PCI: microchip: Add event irqchip field to host port + and add PLDA irqchip + +As PLDA dts binding doc(Documentation/devicetree/bindings/pci/ +plda,xpressrich3-axi-common.yaml) showes, PLDA PCIe contains an interrupt +controller. + +Microchip PolarFire PCIE event IRQs includes PLDA interrupts and +Polarfire their own interrupts. The interrupt irqchip ops includes +ack/mask/unmask interrupt ops, which will write correct registers. +Microchip Polarfire PCIe additional interrupts require to write Polarfire +SoC self-defined registers. So Microchip PCIe event irqchip ops can not +be re-used. + +To support PLDA its own event IRQ process, implements PLDA irqchip ops and +add event irqchip field to struct pcie_plda_rp. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 66 ++++++++++++++++++- + drivers/pci/controller/plda/pcie-plda.h | 5 +- + 2 files changed, 69 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -770,6 +770,64 @@ static struct irq_chip mc_event_irq_chip + .irq_unmask = mc_unmask_event_irq, + }; + ++static u32 plda_hwirq_to_mask(int hwirq) ++{ ++ u32 mask; ++ ++ /* hwirq 23 - 0 are the same with register */ ++ if (hwirq < EVENT_PM_MSI_INT_INTX) ++ mask = BIT(hwirq); ++ else if (hwirq == EVENT_PM_MSI_INT_INTX) ++ mask = PM_MSI_INT_INTX_MASK; ++ else ++ mask = BIT(hwirq + PCI_NUM_INTX - 1); ++ ++ return mask; ++} ++ ++static void plda_ack_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ ++ writel_relaxed(plda_hwirq_to_mask(data->hwirq), ++ port->bridge_addr + ISTATUS_LOCAL); ++} ++ ++static void plda_mask_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ u32 mask, val; ++ ++ mask = plda_hwirq_to_mask(data->hwirq); ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); ++ val &= ~mask; ++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); ++ raw_spin_unlock(&port->lock); ++} ++ ++static void plda_unmask_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ u32 mask, val; ++ ++ mask = plda_hwirq_to_mask(data->hwirq); ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); ++ val |= mask; ++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); ++ raw_spin_unlock(&port->lock); ++} ++ ++static struct irq_chip plda_event_irq_chip = { ++ .name = "PLDA PCIe EVENT", ++ .irq_ack = plda_ack_event_irq, ++ .irq_mask = plda_mask_event_irq, ++ .irq_unmask = plda_unmask_event_irq, ++}; ++ + static const struct plda_event_ops plda_event_ops = { + .get_events = plda_get_events, + }; +@@ -777,7 +835,9 @@ static const struct plda_event_ops plda_ + static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) + { +- irq_set_chip_and_handler(irq, &mc_event_irq_chip, handle_level_irq); ++ struct plda_pcie_rp *port = (void *)domain->host_data; ++ ++ irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + + return 0; +@@ -962,6 +1022,9 @@ static int plda_init_interrupts(struct p + if (!port->event_ops) + port->event_ops = &plda_event_ops; + ++ if (!port->event_irq_chip) ++ port->event_irq_chip = &plda_event_irq_chip; ++ + ret = plda_pcie_init_irq_domains(port); + if (ret) { + dev_err(dev, "failed creating IRQ domains\n"); +@@ -1039,6 +1102,7 @@ static int mc_platform_init(struct pci_c + return ret; + + port->plda.event_ops = &mc_event_ops; ++ port->plda.event_irq_chip = &mc_event_irq_chip; + + /* Address translation is up; safe to enable interrupts */ + ret = plda_init_interrupts(pdev, &port->plda, &mc_event); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -107,7 +107,9 @@ enum plda_int_event { + + #define PLDA_NUM_DMA_EVENTS 16 + +-#define PLDA_MAX_INT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) ++#define EVENT_PM_MSI_INT_INTX (PLDA_NUM_DMA_EVENTS + PLDA_INTX) ++#define EVENT_PM_MSI_INT_MSI (PLDA_NUM_DMA_EVENTS + PLDA_MSI) ++#define PLDA_MAX_EVENT_NUM (PLDA_NUM_DMA_EVENTS + PLDA_INT_EVENT_NUM) + + /* + * PLDA interrupt register +@@ -155,6 +157,7 @@ struct plda_pcie_rp { + raw_spinlock_t lock; + struct plda_msi msi; + const struct plda_event_ops *event_ops; ++ const struct irq_chip *event_irq_chip; + void __iomem *bridge_addr; + int num_events; + }; diff --git a/target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch b/target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch new file mode 100644 index 0000000000..840e334d20 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0029-PCI-microchip-Move-IRQ-functions-to-pcie-plda-host.c.patch @@ -0,0 +1,1028 @@ +From 6be452d8e61594790ae57b282a612ec0df473e61 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:06 +0800 +Subject: [PATCH 029/116] PCI: microchip: Move IRQ functions to + pcie-plda-host.c + +Move IRQ related functions to pcie-plda-host.c for re-use these codes. +Now Refactoring codes complete. + +Including MSI, INTx, event interrupts and IRQ init functions. + +Signed-off-by: Minda Chen +Acked-by: Conor Dooley +--- + .../pci/controller/plda/pcie-microchip-host.c | 467 ----------------- + drivers/pci/controller/plda/pcie-plda-host.c | 472 ++++++++++++++++++ + drivers/pci/controller/plda/pcie-plda.h | 3 + + 3 files changed, 475 insertions(+), 467 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -318,244 +318,6 @@ static void mc_pcie_enable_msi(struct mc + ecam + MC_MSI_CAP_CTRL_OFFSET + PCI_MSI_ADDRESS_HI); + } + +-static void plda_handle_msi(struct irq_desc *desc) +-{ +- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct device *dev = port->dev; +- struct plda_msi *msi = &port->msi; +- void __iomem *bridge_base_addr = port->bridge_addr; +- unsigned long status; +- u32 bit; +- int ret; +- +- chained_irq_enter(chip, desc); +- +- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- if (status & PM_MSI_INT_MSI_MASK) { +- writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); +- status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); +- for_each_set_bit(bit, &status, msi->num_vectors) { +- ret = generic_handle_domain_irq(msi->dev_domain, bit); +- if (ret) +- dev_err_ratelimited(dev, "bad MSI IRQ %d\n", +- bit); +- } +- } +- +- chained_irq_exit(chip, desc); +-} +- +-static void plda_msi_bottom_irq_ack(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = port->bridge_addr; +- u32 bitpos = data->hwirq; +- +- writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); +-} +- +-static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- phys_addr_t addr = port->msi.vector_phy; +- +- msg->address_lo = lower_32_bits(addr); +- msg->address_hi = upper_32_bits(addr); +- msg->data = data->hwirq; +- +- dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", +- (int)data->hwirq, msg->address_hi, msg->address_lo); +-} +- +-static int plda_msi_set_affinity(struct irq_data *irq_data, +- const struct cpumask *mask, bool force) +-{ +- return -EINVAL; +-} +- +-static struct irq_chip plda_msi_bottom_irq_chip = { +- .name = "PLDA MSI", +- .irq_ack = plda_msi_bottom_irq_ack, +- .irq_compose_msi_msg = plda_compose_msi_msg, +- .irq_set_affinity = plda_msi_set_affinity, +-}; +- +-static int plda_irq_msi_domain_alloc(struct irq_domain *domain, +- unsigned int virq, +- unsigned int nr_irqs, +- void *args) +-{ +- struct plda_pcie_rp *port = domain->host_data; +- struct plda_msi *msi = &port->msi; +- unsigned long bit; +- +- mutex_lock(&msi->lock); +- bit = find_first_zero_bit(msi->used, msi->num_vectors); +- if (bit >= msi->num_vectors) { +- mutex_unlock(&msi->lock); +- return -ENOSPC; +- } +- +- set_bit(bit, msi->used); +- +- irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, +- domain->host_data, handle_edge_irq, NULL, NULL); +- +- mutex_unlock(&msi->lock); +- +- return 0; +-} +- +-static void plda_irq_msi_domain_free(struct irq_domain *domain, +- unsigned int virq, +- unsigned int nr_irqs) +-{ +- struct irq_data *d = irq_domain_get_irq_data(domain, virq); +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); +- struct plda_msi *msi = &port->msi; +- +- mutex_lock(&msi->lock); +- +- if (test_bit(d->hwirq, msi->used)) +- __clear_bit(d->hwirq, msi->used); +- else +- dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); +- +- mutex_unlock(&msi->lock); +-} +- +-static const struct irq_domain_ops msi_domain_ops = { +- .alloc = plda_irq_msi_domain_alloc, +- .free = plda_irq_msi_domain_free, +-}; +- +-static struct irq_chip plda_msi_irq_chip = { +- .name = "PLDA PCIe MSI", +- .irq_ack = irq_chip_ack_parent, +- .irq_mask = pci_msi_mask_irq, +- .irq_unmask = pci_msi_unmask_irq, +-}; +- +-static struct msi_domain_info plda_msi_domain_info = { +- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | +- MSI_FLAG_PCI_MSIX), +- .chip = &plda_msi_irq_chip, +-}; +- +-static int plda_allocate_msi_domains(struct plda_pcie_rp *port) +-{ +- struct device *dev = port->dev; +- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); +- struct plda_msi *msi = &port->msi; +- +- mutex_init(&port->msi.lock); +- +- msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, +- &msi_domain_ops, port); +- if (!msi->dev_domain) { +- dev_err(dev, "failed to create IRQ domain\n"); +- return -ENOMEM; +- } +- +- msi->msi_domain = pci_msi_create_irq_domain(fwnode, +- &plda_msi_domain_info, +- msi->dev_domain); +- if (!msi->msi_domain) { +- dev_err(dev, "failed to create MSI domain\n"); +- irq_domain_remove(msi->dev_domain); +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static void plda_handle_intx(struct irq_desc *desc) +-{ +- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); +- struct device *dev = port->dev; +- void __iomem *bridge_base_addr = port->bridge_addr; +- unsigned long status; +- u32 bit; +- int ret; +- +- chained_irq_enter(chip, desc); +- +- status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); +- if (status & PM_MSI_INT_INTX_MASK) { +- status &= PM_MSI_INT_INTX_MASK; +- status >>= PM_MSI_INT_INTX_SHIFT; +- for_each_set_bit(bit, &status, PCI_NUM_INTX) { +- ret = generic_handle_domain_irq(port->intx_domain, bit); +- if (ret) +- dev_err_ratelimited(dev, "bad INTx IRQ %d\n", +- bit); +- } +- } +- +- chained_irq_exit(chip, desc); +-} +- +-static void plda_ack_intx_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = port->bridge_addr; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- +- writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); +-} +- +-static void plda_mask_intx_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = port->bridge_addr; +- unsigned long flags; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- u32 val; +- +- raw_spin_lock_irqsave(&port->lock, flags); +- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); +- val &= ~mask; +- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +- raw_spin_unlock_irqrestore(&port->lock, flags); +-} +- +-static void plda_unmask_intx_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- void __iomem *bridge_base_addr = port->bridge_addr; +- unsigned long flags; +- u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); +- u32 val; +- +- raw_spin_lock_irqsave(&port->lock, flags); +- val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); +- val |= mask; +- writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); +- raw_spin_unlock_irqrestore(&port->lock, flags); +-} +- +-static struct irq_chip plda_intx_irq_chip = { +- .name = "PLDA PCIe INTx", +- .irq_ack = plda_ack_intx_irq, +- .irq_mask = plda_mask_intx_irq, +- .irq_unmask = plda_unmask_intx_irq, +-}; +- +-static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) +-{ +- irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, domain->host_data); +- +- return 0; +-} +- +-static const struct irq_domain_ops intx_domain_ops = { +- .map = plda_pcie_intx_map, +-}; +- + static inline u32 reg_to_event(u32 reg, struct event_map field) + { + return (reg & field.reg_mask) ? BIT(field.event_bit) : 0; +@@ -626,26 +388,6 @@ static u32 mc_get_events(struct plda_pci + return events; + } + +-static u32 plda_get_events(struct plda_pcie_rp *port) +-{ +- u32 events, val, origin; +- +- origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL); +- +- /* MSI event and sys events */ +- val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT; +- events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1); +- +- /* INTx events */ +- if (origin & PM_MSI_INT_INTX_MASK) +- events |= BIT(PM_MSI_INT_INTX_SHIFT); +- +- /* remains are same with register */ +- events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0); +- +- return events; +-} +- + static irqreturn_t mc_event_handler(int irq, void *dev_id) + { + struct plda_pcie_rp *port = dev_id; +@@ -662,28 +404,6 @@ static irqreturn_t mc_event_handler(int + return IRQ_HANDLED; + } + +-static irqreturn_t plda_event_handler(int irq, void *dev_id) +-{ +- return IRQ_HANDLED; +-} +- +-static void plda_handle_event(struct irq_desc *desc) +-{ +- struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +- unsigned long events; +- u32 bit; +- struct irq_chip *chip = irq_desc_get_chip(desc); +- +- chained_irq_enter(chip, desc); +- +- events = port->event_ops->get_events(port); +- +- for_each_set_bit(bit, &events, port->num_events) +- generic_handle_domain_irq(port->event_domain, bit); +- +- chained_irq_exit(chip, desc); +-} +- + static void mc_ack_event_irq(struct irq_data *data) + { + struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +@@ -770,83 +490,6 @@ static struct irq_chip mc_event_irq_chip + .irq_unmask = mc_unmask_event_irq, + }; + +-static u32 plda_hwirq_to_mask(int hwirq) +-{ +- u32 mask; +- +- /* hwirq 23 - 0 are the same with register */ +- if (hwirq < EVENT_PM_MSI_INT_INTX) +- mask = BIT(hwirq); +- else if (hwirq == EVENT_PM_MSI_INT_INTX) +- mask = PM_MSI_INT_INTX_MASK; +- else +- mask = BIT(hwirq + PCI_NUM_INTX - 1); +- +- return mask; +-} +- +-static void plda_ack_event_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- +- writel_relaxed(plda_hwirq_to_mask(data->hwirq), +- port->bridge_addr + ISTATUS_LOCAL); +-} +- +-static void plda_mask_event_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- u32 mask, val; +- +- mask = plda_hwirq_to_mask(data->hwirq); +- +- raw_spin_lock(&port->lock); +- val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); +- val &= ~mask; +- writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); +- raw_spin_unlock(&port->lock); +-} +- +-static void plda_unmask_event_irq(struct irq_data *data) +-{ +- struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); +- u32 mask, val; +- +- mask = plda_hwirq_to_mask(data->hwirq); +- +- raw_spin_lock(&port->lock); +- val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); +- val |= mask; +- writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); +- raw_spin_unlock(&port->lock); +-} +- +-static struct irq_chip plda_event_irq_chip = { +- .name = "PLDA PCIe EVENT", +- .irq_ack = plda_ack_event_irq, +- .irq_mask = plda_mask_event_irq, +- .irq_unmask = plda_unmask_event_irq, +-}; +- +-static const struct plda_event_ops plda_event_ops = { +- .get_events = plda_get_events, +-}; +- +-static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, +- irq_hw_number_t hwirq) +-{ +- struct plda_pcie_rp *port = (void *)domain->host_data; +- +- irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq); +- irq_set_chip_data(irq, domain->host_data); +- +- return 0; +-} +- +-static const struct irq_domain_ops plda_event_domain_ops = { +- .map = plda_pcie_event_map, +-}; +- + static inline void mc_pcie_deinit_clk(void *data) + { + struct clk *clk = data; +@@ -909,47 +552,6 @@ static const struct plda_event mc_event + .msi_event = EVENT_LOCAL_PM_MSI_INT_MSI, + }; + +-static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) +-{ +- struct device *dev = port->dev; +- struct device_node *node = dev->of_node; +- struct device_node *pcie_intc_node; +- +- /* Setup INTx */ +- pcie_intc_node = of_get_next_child(node, NULL); +- if (!pcie_intc_node) { +- dev_err(dev, "failed to find PCIe Intc node\n"); +- return -EINVAL; +- } +- +- port->event_domain = irq_domain_add_linear(pcie_intc_node, +- port->num_events, +- &plda_event_domain_ops, +- port); +- if (!port->event_domain) { +- dev_err(dev, "failed to get event domain\n"); +- of_node_put(pcie_intc_node); +- return -ENOMEM; +- } +- +- irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); +- +- port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, +- &intx_domain_ops, port); +- if (!port->intx_domain) { +- dev_err(dev, "failed to get an INTx IRQ domain\n"); +- of_node_put(pcie_intc_node); +- return -ENOMEM; +- } +- +- irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); +- +- of_node_put(pcie_intc_node); +- raw_spin_lock_init(&port->lock); +- +- return plda_allocate_msi_domains(port); +-} +- + static inline void mc_clear_secs(struct mc_pcie *port) + { + void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; +@@ -1010,75 +612,6 @@ static void mc_disable_interrupts(struct + writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + } + +-static int plda_init_interrupts(struct platform_device *pdev, +- struct plda_pcie_rp *port, +- const struct plda_event *event) +-{ +- struct device *dev = &pdev->dev; +- int irq; +- int i, intx_irq, msi_irq, event_irq; +- int ret; +- +- if (!port->event_ops) +- port->event_ops = &plda_event_ops; +- +- if (!port->event_irq_chip) +- port->event_irq_chip = &plda_event_irq_chip; +- +- ret = plda_pcie_init_irq_domains(port); +- if (ret) { +- dev_err(dev, "failed creating IRQ domains\n"); +- return ret; +- } +- +- irq = platform_get_irq(pdev, 0); +- if (irq < 0) +- return -ENODEV; +- +- for (i = 0; i < port->num_events; i++) { +- event_irq = irq_create_mapping(port->event_domain, i); +- if (!event_irq) { +- dev_err(dev, "failed to map hwirq %d\n", i); +- return -ENXIO; +- } +- +- if (event->request_event_irq) +- ret = event->request_event_irq(port, event_irq, i); +- else +- ret = devm_request_irq(dev, event_irq, +- plda_event_handler, +- 0, NULL, port); +- +- if (ret) { +- dev_err(dev, "failed to request IRQ %d\n", event_irq); +- return ret; +- } +- } +- +- intx_irq = irq_create_mapping(port->event_domain, +- event->intx_event); +- if (!intx_irq) { +- dev_err(dev, "failed to map INTx interrupt\n"); +- return -ENXIO; +- } +- +- /* Plug the INTx chained handler */ +- irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); +- +- msi_irq = irq_create_mapping(port->event_domain, +- event->msi_event); +- if (!msi_irq) +- return -ENXIO; +- +- /* Plug the MSI chained handler */ +- irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); +- +- /* Plug the main event chained handler */ +- irq_set_chained_handler_and_data(irq, plda_handle_event, port); +- +- return 0; +-} +- + static int mc_platform_init(struct pci_config_window *cfg) + { + struct device *dev = cfg->parent; +--- a/drivers/pci/controller/plda/pcie-plda-host.c ++++ b/drivers/pci/controller/plda/pcie-plda-host.c +@@ -7,11 +7,483 @@ + * Author: Daire McNamara + */ + ++#include ++#include ++#include + #include + #include + + #include "pcie-plda.h" + ++static void plda_handle_msi(struct irq_desc *desc) ++{ ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = port->dev; ++ struct plda_msi *msi = &port->msi; ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ unsigned long status; ++ u32 bit; ++ int ret; ++ ++ chained_irq_enter(chip, desc); ++ ++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ if (status & PM_MSI_INT_MSI_MASK) { ++ writel_relaxed(status & PM_MSI_INT_MSI_MASK, ++ bridge_base_addr + ISTATUS_LOCAL); ++ status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); ++ for_each_set_bit(bit, &status, msi->num_vectors) { ++ ret = generic_handle_domain_irq(msi->dev_domain, bit); ++ if (ret) ++ dev_err_ratelimited(dev, "bad MSI IRQ %d\n", ++ bit); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void plda_msi_bottom_irq_ack(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ u32 bitpos = data->hwirq; ++ ++ writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); ++} ++ ++static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ phys_addr_t addr = port->msi.vector_phy; ++ ++ msg->address_lo = lower_32_bits(addr); ++ msg->address_hi = upper_32_bits(addr); ++ msg->data = data->hwirq; ++ ++ dev_dbg(port->dev, "msi#%x address_hi %#x address_lo %#x\n", ++ (int)data->hwirq, msg->address_hi, msg->address_lo); ++} ++ ++static int plda_msi_set_affinity(struct irq_data *irq_data, ++ const struct cpumask *mask, bool force) ++{ ++ return -EINVAL; ++} ++ ++static struct irq_chip plda_msi_bottom_irq_chip = { ++ .name = "PLDA MSI", ++ .irq_ack = plda_msi_bottom_irq_ack, ++ .irq_compose_msi_msg = plda_compose_msi_msg, ++ .irq_set_affinity = plda_msi_set_affinity, ++}; ++ ++static int plda_irq_msi_domain_alloc(struct irq_domain *domain, ++ unsigned int virq, ++ unsigned int nr_irqs, ++ void *args) ++{ ++ struct plda_pcie_rp *port = domain->host_data; ++ struct plda_msi *msi = &port->msi; ++ unsigned long bit; ++ ++ mutex_lock(&msi->lock); ++ bit = find_first_zero_bit(msi->used, msi->num_vectors); ++ if (bit >= msi->num_vectors) { ++ mutex_unlock(&msi->lock); ++ return -ENOSPC; ++ } ++ ++ set_bit(bit, msi->used); ++ ++ irq_domain_set_info(domain, virq, bit, &plda_msi_bottom_irq_chip, ++ domain->host_data, handle_edge_irq, NULL, NULL); ++ ++ mutex_unlock(&msi->lock); ++ ++ return 0; ++} ++ ++static void plda_irq_msi_domain_free(struct irq_domain *domain, ++ unsigned int virq, ++ unsigned int nr_irqs) ++{ ++ struct irq_data *d = irq_domain_get_irq_data(domain, virq); ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(d); ++ struct plda_msi *msi = &port->msi; ++ ++ mutex_lock(&msi->lock); ++ ++ if (test_bit(d->hwirq, msi->used)) ++ __clear_bit(d->hwirq, msi->used); ++ else ++ dev_err(port->dev, "trying to free unused MSI%lu\n", d->hwirq); ++ ++ mutex_unlock(&msi->lock); ++} ++ ++static const struct irq_domain_ops msi_domain_ops = { ++ .alloc = plda_irq_msi_domain_alloc, ++ .free = plda_irq_msi_domain_free, ++}; ++ ++static struct irq_chip plda_msi_irq_chip = { ++ .name = "PLDA PCIe MSI", ++ .irq_ack = irq_chip_ack_parent, ++ .irq_mask = pci_msi_mask_irq, ++ .irq_unmask = pci_msi_unmask_irq, ++}; ++ ++static struct msi_domain_info plda_msi_domain_info = { ++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | ++ MSI_FLAG_PCI_MSIX), ++ .chip = &plda_msi_irq_chip, ++}; ++ ++static int plda_allocate_msi_domains(struct plda_pcie_rp *port) ++{ ++ struct device *dev = port->dev; ++ struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); ++ struct plda_msi *msi = &port->msi; ++ ++ mutex_init(&port->msi.lock); ++ ++ msi->dev_domain = irq_domain_add_linear(NULL, msi->num_vectors, ++ &msi_domain_ops, port); ++ if (!msi->dev_domain) { ++ dev_err(dev, "failed to create IRQ domain\n"); ++ return -ENOMEM; ++ } ++ ++ msi->msi_domain = pci_msi_create_irq_domain(fwnode, ++ &plda_msi_domain_info, ++ msi->dev_domain); ++ if (!msi->msi_domain) { ++ dev_err(dev, "failed to create MSI domain\n"); ++ irq_domain_remove(msi->dev_domain); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void plda_handle_intx(struct irq_desc *desc) ++{ ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct device *dev = port->dev; ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ unsigned long status; ++ u32 bit; ++ int ret; ++ ++ chained_irq_enter(chip, desc); ++ ++ status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); ++ if (status & PM_MSI_INT_INTX_MASK) { ++ status &= PM_MSI_INT_INTX_MASK; ++ status >>= PM_MSI_INT_INTX_SHIFT; ++ for_each_set_bit(bit, &status, PCI_NUM_INTX) { ++ ret = generic_handle_domain_irq(port->intx_domain, bit); ++ if (ret) ++ dev_err_ratelimited(dev, "bad INTx IRQ %d\n", ++ bit); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static void plda_ack_intx_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ ++ writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); ++} ++ ++static void plda_mask_intx_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ unsigned long flags; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ u32 val; ++ ++ raw_spin_lock_irqsave(&port->lock, flags); ++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); ++ val &= ~mask; ++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); ++ raw_spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static void plda_unmask_intx_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ void __iomem *bridge_base_addr = port->bridge_addr; ++ unsigned long flags; ++ u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); ++ u32 val; ++ ++ raw_spin_lock_irqsave(&port->lock, flags); ++ val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); ++ val |= mask; ++ writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); ++ raw_spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static struct irq_chip plda_intx_irq_chip = { ++ .name = "PLDA PCIe INTx", ++ .irq_ack = plda_ack_intx_irq, ++ .irq_mask = plda_mask_intx_irq, ++ .irq_unmask = plda_unmask_intx_irq, ++}; ++ ++static int plda_pcie_intx_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_and_handler(irq, &plda_intx_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops intx_domain_ops = { ++ .map = plda_pcie_intx_map, ++}; ++ ++static u32 plda_get_events(struct plda_pcie_rp *port) ++{ ++ u32 events, val, origin; ++ ++ origin = readl_relaxed(port->bridge_addr + ISTATUS_LOCAL); ++ ++ /* MSI event and sys events */ ++ val = (origin & SYS_AND_MSI_MASK) >> PM_MSI_INT_MSI_SHIFT; ++ events = val << (PM_MSI_INT_MSI_SHIFT - PCI_NUM_INTX + 1); ++ ++ /* INTx events */ ++ if (origin & PM_MSI_INT_INTX_MASK) ++ events |= BIT(PM_MSI_INT_INTX_SHIFT); ++ ++ /* remains are same with register */ ++ events |= origin & GENMASK(P_ATR_EVT_DOORBELL_SHIFT, 0); ++ ++ return events; ++} ++ ++static irqreturn_t plda_event_handler(int irq, void *dev_id) ++{ ++ return IRQ_HANDLED; ++} ++ ++static void plda_handle_event(struct irq_desc *desc) ++{ ++ struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); ++ unsigned long events; ++ u32 bit; ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ ++ chained_irq_enter(chip, desc); ++ ++ events = port->event_ops->get_events(port); ++ ++ for_each_set_bit(bit, &events, port->num_events) ++ generic_handle_domain_irq(port->event_domain, bit); ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static u32 plda_hwirq_to_mask(int hwirq) ++{ ++ u32 mask; ++ ++ /* hwirq 23 - 0 are the same with register */ ++ if (hwirq < EVENT_PM_MSI_INT_INTX) ++ mask = BIT(hwirq); ++ else if (hwirq == EVENT_PM_MSI_INT_INTX) ++ mask = PM_MSI_INT_INTX_MASK; ++ else ++ mask = BIT(hwirq + PCI_NUM_INTX - 1); ++ ++ return mask; ++} ++ ++static void plda_ack_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ ++ writel_relaxed(plda_hwirq_to_mask(data->hwirq), ++ port->bridge_addr + ISTATUS_LOCAL); ++} ++ ++static void plda_mask_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ u32 mask, val; ++ ++ mask = plda_hwirq_to_mask(data->hwirq); ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); ++ val &= ~mask; ++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); ++ raw_spin_unlock(&port->lock); ++} ++ ++static void plda_unmask_event_irq(struct irq_data *data) ++{ ++ struct plda_pcie_rp *port = irq_data_get_irq_chip_data(data); ++ u32 mask, val; ++ ++ mask = plda_hwirq_to_mask(data->hwirq); ++ ++ raw_spin_lock(&port->lock); ++ val = readl_relaxed(port->bridge_addr + IMASK_LOCAL); ++ val |= mask; ++ writel_relaxed(val, port->bridge_addr + IMASK_LOCAL); ++ raw_spin_unlock(&port->lock); ++} ++ ++static struct irq_chip plda_event_irq_chip = { ++ .name = "PLDA PCIe EVENT", ++ .irq_ack = plda_ack_event_irq, ++ .irq_mask = plda_mask_event_irq, ++ .irq_unmask = plda_unmask_event_irq, ++}; ++ ++static const struct plda_event_ops plda_event_ops = { ++ .get_events = plda_get_events, ++}; ++ ++static int plda_pcie_event_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ struct plda_pcie_rp *port = (void *)domain->host_data; ++ ++ irq_set_chip_and_handler(irq, port->event_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, domain->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops plda_event_domain_ops = { ++ .map = plda_pcie_event_map, ++}; ++ ++static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port) ++{ ++ struct device *dev = port->dev; ++ struct device_node *node = dev->of_node; ++ struct device_node *pcie_intc_node; ++ ++ /* Setup INTx */ ++ pcie_intc_node = of_get_next_child(node, NULL); ++ if (!pcie_intc_node) { ++ dev_err(dev, "failed to find PCIe Intc node\n"); ++ return -EINVAL; ++ } ++ ++ port->event_domain = irq_domain_add_linear(pcie_intc_node, ++ port->num_events, ++ &plda_event_domain_ops, ++ port); ++ if (!port->event_domain) { ++ dev_err(dev, "failed to get event domain\n"); ++ of_node_put(pcie_intc_node); ++ return -ENOMEM; ++ } ++ ++ irq_domain_update_bus_token(port->event_domain, DOMAIN_BUS_NEXUS); ++ ++ port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, ++ &intx_domain_ops, port); ++ if (!port->intx_domain) { ++ dev_err(dev, "failed to get an INTx IRQ domain\n"); ++ of_node_put(pcie_intc_node); ++ return -ENOMEM; ++ } ++ ++ irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED); ++ ++ of_node_put(pcie_intc_node); ++ raw_spin_lock_init(&port->lock); ++ ++ return plda_allocate_msi_domains(port); ++} ++ ++int plda_init_interrupts(struct platform_device *pdev, ++ struct plda_pcie_rp *port, ++ const struct plda_event *event) ++{ ++ struct device *dev = &pdev->dev; ++ int irq; ++ int i, intx_irq, msi_irq, event_irq; ++ int ret; ++ ++ if (!port->event_ops) ++ port->event_ops = &plda_event_ops; ++ ++ if (!port->event_irq_chip) ++ port->event_irq_chip = &plda_event_irq_chip; ++ ++ ret = plda_pcie_init_irq_domains(port); ++ if (ret) { ++ dev_err(dev, "failed creating IRQ domains\n"); ++ return ret; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) ++ return -ENODEV; ++ ++ for (i = 0; i < port->num_events; i++) { ++ event_irq = irq_create_mapping(port->event_domain, i); ++ if (!event_irq) { ++ dev_err(dev, "failed to map hwirq %d\n", i); ++ return -ENXIO; ++ } ++ ++ if (event->request_event_irq) ++ ret = event->request_event_irq(port, event_irq, i); ++ else ++ ret = devm_request_irq(dev, event_irq, ++ plda_event_handler, ++ 0, NULL, port); ++ ++ if (ret) { ++ dev_err(dev, "failed to request IRQ %d\n", event_irq); ++ return ret; ++ } ++ } ++ ++ intx_irq = irq_create_mapping(port->event_domain, ++ event->intx_event); ++ if (!intx_irq) { ++ dev_err(dev, "failed to map INTx interrupt\n"); ++ return -ENXIO; ++ } ++ ++ /* Plug the INTx chained handler */ ++ irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); ++ ++ msi_irq = irq_create_mapping(port->event_domain, ++ event->msi_event); ++ if (!msi_irq) ++ return -ENXIO; ++ ++ /* Plug the MSI chained handler */ ++ irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); ++ ++ /* Plug the main event chained handler */ ++ irq_set_chained_handler_and_data(irq, plda_handle_event, port); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(plda_init_interrupts); ++ + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size) +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -169,6 +169,9 @@ struct plda_event { + int msi_event; + }; + ++int plda_init_interrupts(struct platform_device *pdev, ++ struct plda_pcie_rp *port, ++ const struct plda_event *event); + void plda_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, + phys_addr_t axi_addr, phys_addr_t pci_addr, + size_t size); diff --git a/target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch b/target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch new file mode 100644 index 0000000000..dcd2310e5e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0030-pci-plda-Add-event-bitmap-field-to-struct-plda_pcie_.patch @@ -0,0 +1,67 @@ +From 142fc300fd7511a217783dcfa342031d8ad70188 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:07 +0800 +Subject: [PATCH 030/116] pci: plda: Add event bitmap field to struct + plda_pcie_rp + +For PLDA DMA interrupts are not all implemented. The non-implemented +interrupts should be masked. So add a bitmap field to mask the non- +implemented interrupts. + +Signed-off-by: Minda Chen +--- + drivers/pci/controller/plda/pcie-microchip-host.c | 1 + + drivers/pci/controller/plda/pcie-plda-host.c | 6 ++++-- + drivers/pci/controller/plda/pcie-plda.h | 1 + + 3 files changed, 6 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-microchip-host.c ++++ b/drivers/pci/controller/plda/pcie-microchip-host.c +@@ -636,6 +636,7 @@ static int mc_platform_init(struct pci_c + + port->plda.event_ops = &mc_event_ops; + port->plda.event_irq_chip = &mc_event_irq_chip; ++ port->plda.events_bitmap = GENMASK(NUM_EVENTS - 1, 0); + + /* Address translation is up; safe to enable interrupts */ + ret = plda_init_interrupts(pdev, &port->plda, &mc_event); +--- a/drivers/pci/controller/plda/pcie-plda-host.c ++++ b/drivers/pci/controller/plda/pcie-plda-host.c +@@ -290,6 +290,7 @@ static void plda_handle_event(struct irq + + events = port->event_ops->get_events(port); + ++ events &= port->events_bitmap; + for_each_set_bit(bit, &events, port->num_events) + generic_handle_domain_irq(port->event_domain, bit); + +@@ -420,8 +421,9 @@ int plda_init_interrupts(struct platform + { + struct device *dev = &pdev->dev; + int irq; +- int i, intx_irq, msi_irq, event_irq; ++ int intx_irq, msi_irq, event_irq; + int ret; ++ u32 i; + + if (!port->event_ops) + port->event_ops = &plda_event_ops; +@@ -439,7 +441,7 @@ int plda_init_interrupts(struct platform + if (irq < 0) + return -ENODEV; + +- for (i = 0; i < port->num_events; i++) { ++ for_each_set_bit(i, &port->events_bitmap, port->num_events) { + event_irq = irq_create_mapping(port->event_domain, i); + if (!event_irq) { + dev_err(dev, "failed to map hwirq %d\n", i); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -159,6 +159,7 @@ struct plda_pcie_rp { + const struct plda_event_ops *event_ops; + const struct irq_chip *event_irq_chip; + void __iomem *bridge_addr; ++ unsigned long events_bitmap; + int num_events; + }; + diff --git a/target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch b/target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch new file mode 100644 index 0000000000..eb22849da6 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0031-PCI-plda-Add-host-init-deinit-and-map-bus-functions.patch @@ -0,0 +1,256 @@ +From 3b9991438094dc472dacb4555603bdc379653411 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:08 +0800 +Subject: [PATCH 031/116] PCI: plda: Add host init/deinit and map bus functions + +Add PLDA host plda_pcie_host_init()/plda_pcie_host_deinit() and map bus +function. So vendor can use it to init PLDA PCIe host core. + +Signed-off-by: Minda Chen +Reviewed-by: Mason Huo +--- + drivers/pci/controller/plda/pcie-plda-host.c | 131 +++++++++++++++++-- + drivers/pci/controller/plda/pcie-plda.h | 22 ++++ + 2 files changed, 139 insertions(+), 14 deletions(-) + +--- a/drivers/pci/controller/plda/pcie-plda-host.c ++++ b/drivers/pci/controller/plda/pcie-plda-host.c +@@ -3,6 +3,7 @@ + * PLDA PCIe XpressRich host controller driver + * + * Copyright (C) 2023 Microchip Co. Ltd ++ * StarFive Co. Ltd + * + * Author: Daire McNamara + */ +@@ -15,6 +16,15 @@ + + #include "pcie-plda.h" + ++void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, ++ int where) ++{ ++ struct plda_pcie_rp *pcie = bus->sysdata; ++ ++ return pcie->config_base + PCIE_ECAM_OFFSET(bus->number, devfn, where); ++} ++EXPORT_SYMBOL_GPL(plda_pcie_map_bus); ++ + static void plda_handle_msi(struct irq_desc *desc) + { + struct plda_pcie_rp *port = irq_desc_get_handler_data(desc); +@@ -420,9 +430,7 @@ int plda_init_interrupts(struct platform + const struct plda_event *event) + { + struct device *dev = &pdev->dev; +- int irq; +- int intx_irq, msi_irq, event_irq; +- int ret; ++ int event_irq, ret; + u32 i; + + if (!port->event_ops) +@@ -437,8 +445,8 @@ int plda_init_interrupts(struct platform + return ret; + } + +- irq = platform_get_irq(pdev, 0); +- if (irq < 0) ++ port->irq = platform_get_irq(pdev, 0); ++ if (port->irq < 0) + return -ENODEV; + + for_each_set_bit(i, &port->events_bitmap, port->num_events) { +@@ -461,26 +469,26 @@ int plda_init_interrupts(struct platform + } + } + +- intx_irq = irq_create_mapping(port->event_domain, +- event->intx_event); +- if (!intx_irq) { ++ port->intx_irq = irq_create_mapping(port->event_domain, ++ event->intx_event); ++ if (!port->intx_irq) { + dev_err(dev, "failed to map INTx interrupt\n"); + return -ENXIO; + } + + /* Plug the INTx chained handler */ +- irq_set_chained_handler_and_data(intx_irq, plda_handle_intx, port); ++ irq_set_chained_handler_and_data(port->intx_irq, plda_handle_intx, port); + +- msi_irq = irq_create_mapping(port->event_domain, +- event->msi_event); +- if (!msi_irq) ++ port->msi_irq = irq_create_mapping(port->event_domain, ++ event->msi_event); ++ if (!port->msi_irq) + return -ENXIO; + + /* Plug the MSI chained handler */ +- irq_set_chained_handler_and_data(msi_irq, plda_handle_msi, port); ++ irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port); + + /* Plug the main event chained handler */ +- irq_set_chained_handler_and_data(irq, plda_handle_event, port); ++ irq_set_chained_handler_and_data(port->irq, plda_handle_event, port); + + return 0; + } +@@ -546,3 +554,98 @@ int plda_pcie_setup_iomems(struct pci_ho + return 0; + } + EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems); ++ ++static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie) ++{ ++ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); ++ irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL); ++ irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL); ++ ++ irq_domain_remove(pcie->msi.msi_domain); ++ irq_domain_remove(pcie->msi.dev_domain); ++ ++ irq_domain_remove(pcie->intx_domain); ++ irq_domain_remove(pcie->event_domain); ++} ++ ++int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops, ++ const struct plda_event *plda_event) ++{ ++ struct device *dev = port->dev; ++ struct pci_host_bridge *bridge; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct resource *cfg_res; ++ int ret; ++ ++ pdev = to_platform_device(dev); ++ ++ port->bridge_addr = ++ devm_platform_ioremap_resource_byname(pdev, "apb"); ++ ++ if (IS_ERR(port->bridge_addr)) ++ return dev_err_probe(dev, PTR_ERR(port->bridge_addr), ++ "failed to map reg memory\n"); ++ ++ cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); ++ if (!cfg_res) ++ return dev_err_probe(dev, -ENODEV, ++ "failed to get config memory\n"); ++ ++ port->config_base = devm_ioremap_resource(dev, cfg_res); ++ if (IS_ERR(port->config_base)) ++ return dev_err_probe(dev, PTR_ERR(port->config_base), ++ "failed to map config memory\n"); ++ ++ bridge = devm_pci_alloc_host_bridge(dev, 0); ++ if (!bridge) ++ return dev_err_probe(dev, -ENOMEM, ++ "failed to alloc bridge\n"); ++ ++ if (port->host_ops && port->host_ops->host_init) { ++ ret = port->host_ops->host_init(port); ++ if (ret) ++ return ret; ++ } ++ ++ port->bridge = bridge; ++ plda_pcie_setup_window(port->bridge_addr, 0, cfg_res->start, 0, ++ resource_size(cfg_res)); ++ plda_pcie_setup_iomems(bridge, port); ++ plda_set_default_msi(&port->msi); ++ ret = plda_init_interrupts(pdev, port, plda_event); ++ if (ret) ++ goto err_host; ++ ++ /* Set default bus ops */ ++ bridge->ops = ops; ++ bridge->sysdata = port; ++ ++ ret = pci_host_probe(bridge); ++ if (ret < 0) { ++ dev_err_probe(dev, ret, "failed to probe pci host\n"); ++ goto err_probe; ++ } ++ ++ return ret; ++ ++err_probe: ++ plda_pcie_irq_domain_deinit(port); ++err_host: ++ if (port->host_ops && port->host_ops->host_deinit) ++ port->host_ops->host_deinit(port); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(plda_pcie_host_init); ++ ++void plda_pcie_host_deinit(struct plda_pcie_rp *port) ++{ ++ pci_stop_root_bus(port->bridge->bus); ++ pci_remove_root_bus(port->bridge->bus); ++ ++ plda_pcie_irq_domain_deinit(port); ++ ++ if (port->host_ops && port->host_ops->host_deinit) ++ port->host_ops->host_deinit(port); ++} ++EXPORT_SYMBOL_GPL(plda_pcie_host_deinit); +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -141,6 +141,11 @@ struct plda_event_ops { + u32 (*get_events)(struct plda_pcie_rp *pcie); + }; + ++struct plda_pcie_host_ops { ++ int (*host_init)(struct plda_pcie_rp *pcie); ++ void (*host_deinit)(struct plda_pcie_rp *pcie); ++}; ++ + struct plda_msi { + struct mutex lock; /* Protect used bitmap */ + struct irq_domain *msi_domain; +@@ -152,14 +157,20 @@ struct plda_msi { + + struct plda_pcie_rp { + struct device *dev; ++ struct pci_host_bridge *bridge; + struct irq_domain *intx_domain; + struct irq_domain *event_domain; + raw_spinlock_t lock; + struct plda_msi msi; + const struct plda_event_ops *event_ops; + const struct irq_chip *event_irq_chip; ++ const struct plda_pcie_host_ops *host_ops; + void __iomem *bridge_addr; ++ void __iomem *config_base; + unsigned long events_bitmap; ++ int irq; ++ int msi_irq; ++ int intx_irq; + int num_events; + }; + +@@ -170,6 +181,8 @@ struct plda_event { + int msi_event; + }; + ++void __iomem *plda_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, ++ int where); + int plda_init_interrupts(struct platform_device *pdev, + struct plda_pcie_rp *port, + const struct plda_event *event); +@@ -178,4 +191,13 @@ void plda_pcie_setup_window(void __iomem + size_t size); + int plda_pcie_setup_iomems(struct pci_host_bridge *bridge, + struct plda_pcie_rp *port); ++int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops, ++ const struct plda_event *plda_event); ++void plda_pcie_host_deinit(struct plda_pcie_rp *pcie); ++ ++static inline void plda_set_default_msi(struct plda_msi *msi) ++{ ++ msi->vector_phy = IMSI_ADDR; ++ msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS; ++} + #endif diff --git a/target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch b/target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch new file mode 100644 index 0000000000..9e33f7d68a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0032-dt-bindings-PCI-Add-StarFive-JH7110-PCIe-controller.patch @@ -0,0 +1,140 @@ +From bc3f8207d9f0af3cb96a7eae232074a644a175f6 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:09 +0800 +Subject: [PATCH 032/116] dt-bindings: PCI: Add StarFive JH7110 PCIe controller + +Add StarFive JH7110 SoC PCIe controller dt-bindings. JH7110 using PLDA +XpressRICH PCIe host controller IP. + +Signed-off-by: Minda Chen +Reviewed-by: Hal Feng +Reviewed-by: Conor Dooley +Reviewed-by: Rob Herring +--- + .../bindings/pci/starfive,jh7110-pcie.yaml | 120 ++++++++++++++++++ + 1 file changed, 120 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml +@@ -0,0 +1,120 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/pci/starfive,jh7110-pcie.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: StarFive JH7110 PCIe host controller ++ ++maintainers: ++ - Kevin Xie ++ ++allOf: ++ - $ref: plda,xpressrich3-axi-common.yaml# ++ ++properties: ++ compatible: ++ const: starfive,jh7110-pcie ++ ++ clocks: ++ items: ++ - description: NOC bus clock ++ - description: Transport layer clock ++ - description: AXI MST0 clock ++ - description: APB clock ++ ++ clock-names: ++ items: ++ - const: noc ++ - const: tl ++ - const: axi_mst0 ++ - const: apb ++ ++ resets: ++ items: ++ - description: AXI MST0 reset ++ - description: AXI SLAVE0 reset ++ - description: AXI SLAVE reset ++ - description: PCIE BRIDGE reset ++ - description: PCIE CORE reset ++ - description: PCIE APB reset ++ ++ reset-names: ++ items: ++ - const: mst0 ++ - const: slv0 ++ - const: slv ++ - const: brg ++ - const: core ++ - const: apb ++ ++ starfive,stg-syscon: ++ $ref: /schemas/types.yaml#/definitions/phandle-array ++ description: ++ The phandle to System Register Controller syscon node. ++ ++ perst-gpios: ++ description: GPIO controlled connection to PERST# signal ++ maxItems: 1 ++ ++ phys: ++ description: ++ Specified PHY is attached to PCIe controller. ++ maxItems: 1 ++ ++required: ++ - clocks ++ - resets ++ - starfive,stg-syscon ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ #include ++ soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ pcie@940000000 { ++ compatible = "starfive,jh7110-pcie"; ++ reg = <0x9 0x40000000 0x0 0x10000000>, ++ <0x0 0x2b000000 0x0 0x1000000>; ++ reg-names = "cfg", "apb"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ #interrupt-cells = <1>; ++ device_type = "pci"; ++ ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, ++ <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; ++ starfive,stg-syscon = <&stg_syscon>; ++ bus-range = <0x0 0xff>; ++ interrupt-parent = <&plic>; ++ interrupts = <56>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>, ++ <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>, ++ <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>, ++ <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>; ++ msi-controller; ++ clocks = <&syscrg 86>, ++ <&stgcrg 10>, ++ <&stgcrg 8>, ++ <&stgcrg 9>; ++ clock-names = "noc", "tl", "axi_mst0", "apb"; ++ resets = <&stgcrg 11>, ++ <&stgcrg 12>, ++ <&stgcrg 13>, ++ <&stgcrg 14>, ++ <&stgcrg 15>, ++ <&stgcrg 16>; ++ perst-gpios = <&gpios 26 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy0>; ++ ++ pcie_intc0: interrupt-controller { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ }; ++ }; ++ }; diff --git a/target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch b/target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch new file mode 100644 index 0000000000..cc50dfe68e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0033-PCI-Add-PCIE_RESET_CONFIG_DEVICE_WAIT_MS-waiting-tim.patch @@ -0,0 +1,55 @@ +From abb20b7b8f5e3a7f36dbd6264e6d346275434154 Mon Sep 17 00:00:00 2001 +From: Kevin Xie +Date: Mon, 8 Jan 2024 19:06:10 +0800 +Subject: [PATCH 033/116] PCI: Add PCIE_RESET_CONFIG_DEVICE_WAIT_MS waiting + time value + +Add the PCIE_RESET_CONFIG_DEVICE_WAIT_MS macro to define the minimum +waiting time between exit from a conventional reset and sending the +first configuration request to the device. + +As described in PCI base specification r6.0, section 6.6.1 , there are two different use cases of the value: + + - "With a Downstream Port that does not support Link speeds greater + than 5.0 GT/s, software must wait a minimum of 100 ms following exit + from a Conventional Reset before sending a Configuration Request to + the device immediately below that Port." + + - "With a Downstream Port that supports Link speeds greater than + 5.0 GT/s, software must wait a minimum of 100 ms after Link training + completes before sending a Configuration Request to the device + immediately below that Port." + +Signed-off-by: Kevin Xie +Reviewed-by: Mason Huo +Acked-by: Bjorn Helgaas +--- + drivers/pci/pci.h | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/pci/pci.h ++++ b/drivers/pci/pci.h +@@ -19,6 +19,22 @@ + */ + #define PCIE_PME_TO_L2_TIMEOUT_US 10000 + ++/* ++ * As described in PCI base specification r6.0, section 6.6.1 , there are two different use cases of the value: ++ * ++ * - "With a Downstream Port that does not support Link speeds greater ++ * than 5.0 GT/s, software must wait a minimum of 100 ms following exit ++ * from a Conventional Reset before sending a Configuration Request to ++ * the device immediately below that Port." ++ * ++ * - "With a Downstream Port that supports Link speeds greater than ++ * 5.0 GT/s, software must wait a minimum of 100 ms after Link training ++ * completes before sending a Configuration Request to the device ++ * immediately below that Port." ++ */ ++#define PCIE_RESET_CONFIG_DEVICE_WAIT_MS 100 ++ + extern const unsigned char pcie_link_speed[]; + extern bool pci_early_dump; + diff --git a/target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch b/target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch new file mode 100644 index 0000000000..45a549f489 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0034-PCI-starfive-Add-JH7110-PCIe-controller.patch @@ -0,0 +1,623 @@ +From 323aedef34315b758dc30ba23e2cabca259bb4b2 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Mon, 8 Jan 2024 19:06:11 +0800 +Subject: [PATCH 034/116] PCI: starfive: Add JH7110 PCIe controller + +Add StarFive JH7110 SoC PCIe controller platform driver codes, JH7110 +with PLDA host PCIe core. + +Signed-off-by: Minda Chen +Co-developed-by: Kevin Xie +Reviewed-by: Mason Huo +--- + drivers/pci/controller/plda/Kconfig | 12 + + drivers/pci/controller/plda/Makefile | 1 + + drivers/pci/controller/plda/pcie-plda.h | 71 ++- + drivers/pci/controller/plda/pcie-starfive.c | 473 ++++++++++++++++++++ + 4 files changed, 556 insertions(+), 1 deletion(-) + create mode 100644 drivers/pci/controller/plda/pcie-starfive.c + +--- a/drivers/pci/controller/plda/Kconfig ++++ b/drivers/pci/controller/plda/Kconfig +@@ -15,4 +15,16 @@ config PCIE_MICROCHIP_HOST + Say Y here if you want kernel to support the Microchip AXI PCIe + Host Bridge driver. + ++config PCIE_STARFIVE_HOST ++ tristate "StarFive PCIe host controller" ++ depends on PCI_MSI && OF ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ select PCIE_PLDA_HOST ++ help ++ Say Y here if you want to support the StarFive PCIe controller in ++ host mode. StarFive PCIe controller uses PLDA PCIe core. ++ ++ If you choose to build this driver as module it will be dynamically ++ linked and module will be called pcie-starfive.ko. ++ + endmenu +--- a/drivers/pci/controller/plda/Makefile ++++ b/drivers/pci/controller/plda/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_PCIE_PLDA_HOST) += pcie-plda-host.o + obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o ++obj-$(CONFIG_PCIE_STARFIVE_HOST) += pcie-starfive.o +--- a/drivers/pci/controller/plda/pcie-plda.h ++++ b/drivers/pci/controller/plda/pcie-plda.h +@@ -10,10 +10,20 @@ + #define PLDA_MAX_NUM_MSI_IRQS 32 + + /* PCIe Bridge Phy Regs */ ++#define GEN_SETTINGS 0x80 ++#define RP_ENABLE 1 ++#define PCIE_PCI_IDS_DW1 0x9c ++#define IDS_CLASS_CODE_SHIFT 16 ++#define REVISION_ID_MASK GENMASK(7, 0) ++#define CLASS_CODE_ID_MASK GENMASK(31, 8) + #define PCIE_PCI_IRQ_DW0 0xa8 + #define MSIX_CAP_MASK BIT(31) + #define NUM_MSI_MSGS_MASK GENMASK(6, 4) + #define NUM_MSI_MSGS_SHIFT 4 ++#define PCI_MISC 0xb4 ++#define PHY_FUNCTION_DIS BIT(15) ++#define PCIE_WINROM 0xfc ++#define PREF_MEM_WIN_64_SUPPORT BIT(3) + + #define IMASK_LOCAL 0x180 + #define DMA_END_ENGINE_0_MASK 0x00000000u +@@ -65,6 +75,8 @@ + #define ISTATUS_HOST 0x18c + #define IMSI_ADDR 0x190 + #define ISTATUS_MSI 0x194 ++#define PMSG_SUPPORT_RX 0x3f0 ++#define PMSG_LTR_SUPPORT BIT(2) + + /* PCIe Master table init defines */ + #define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u +@@ -86,6 +98,8 @@ + #define PCIE_TX_RX_INTERFACE 0x00000000u + #define PCIE_CONFIG_INTERFACE 0x00000001u + ++#define CONFIG_SPACE_ADDR_OFFSET 0x1000u ++ + #define ATR_ENTRY_SIZE 32 + + enum plda_int_event { +@@ -200,4 +214,59 @@ static inline void plda_set_default_msi( + msi->vector_phy = IMSI_ADDR; + msi->num_vectors = PLDA_MAX_NUM_MSI_IRQS; + } +-#endif ++ ++static inline void plda_pcie_enable_root_port(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ value = readl_relaxed(plda->bridge_addr + GEN_SETTINGS); ++ value |= RP_ENABLE; ++ writel_relaxed(value, plda->bridge_addr + GEN_SETTINGS); ++} ++ ++static inline void plda_pcie_set_standard_class(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ /* set class code and reserve revision id */ ++ value = readl_relaxed(plda->bridge_addr + PCIE_PCI_IDS_DW1); ++ value &= REVISION_ID_MASK; ++ value |= (PCI_CLASS_BRIDGE_PCI << IDS_CLASS_CODE_SHIFT); ++ writel_relaxed(value, plda->bridge_addr + PCIE_PCI_IDS_DW1); ++} ++ ++static inline void plda_pcie_set_pref_win_64bit(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ value = readl_relaxed(plda->bridge_addr + PCIE_WINROM); ++ value |= PREF_MEM_WIN_64_SUPPORT; ++ writel_relaxed(value, plda->bridge_addr + PCIE_WINROM); ++} ++ ++static inline void plda_pcie_disable_ltr(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ value = readl_relaxed(plda->bridge_addr + PMSG_SUPPORT_RX); ++ value &= ~PMSG_LTR_SUPPORT; ++ writel_relaxed(value, plda->bridge_addr + PMSG_SUPPORT_RX); ++} ++ ++static inline void plda_pcie_disable_func(struct plda_pcie_rp *plda) ++{ ++ u32 value; ++ ++ value = readl_relaxed(plda->bridge_addr + PCI_MISC); ++ value |= PHY_FUNCTION_DIS; ++ writel_relaxed(value, plda->bridge_addr + PCI_MISC); ++} ++ ++static inline void plda_pcie_write_rc_bar(struct plda_pcie_rp *plda, u64 val) ++{ ++ void __iomem *addr = plda->bridge_addr + CONFIG_SPACE_ADDR_OFFSET; ++ ++ writel_relaxed(lower_32_bits(val), addr + PCI_BASE_ADDRESS_0); ++ writel_relaxed(upper_32_bits(val), addr + PCI_BASE_ADDRESS_1); ++} ++#endif /* _PCIE_PLDA_H */ +--- /dev/null ++++ b/drivers/pci/controller/plda/pcie-starfive.c +@@ -0,0 +1,473 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * PCIe host controller driver for StarFive JH7110 Soc. ++ * ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../../pci.h" ++ ++#include "pcie-plda.h" ++ ++#define PCIE_FUNC_NUM 4 ++ ++/* system control */ ++#define STG_SYSCON_PCIE0_BASE 0x48 ++#define STG_SYSCON_PCIE1_BASE 0x1f8 ++ ++#define STG_SYSCON_AR_OFFSET 0x78 ++#define STG_SYSCON_AXI4_SLVL_AR_MASK GENMASK(22, 8) ++#define STG_SYSCON_AXI4_SLVL_PHY_AR(x) FIELD_PREP(GENMASK(20, 17), x) ++#define STG_SYSCON_AW_OFFSET 0x7c ++#define STG_SYSCON_AXI4_SLVL_AW_MASK GENMASK(14, 0) ++#define STG_SYSCON_AXI4_SLVL_PHY_AW(x) FIELD_PREP(GENMASK(12, 9), x) ++#define STG_SYSCON_CLKREQ BIT(22) ++#define STG_SYSCON_CKREF_SRC_MASK GENMASK(19, 18) ++#define STG_SYSCON_RP_NEP_OFFSET 0xe8 ++#define STG_SYSCON_K_RP_NEP BIT(8) ++#define STG_SYSCON_LNKSTA_OFFSET 0x170 ++#define DATA_LINK_ACTIVE BIT(5) ++ ++/* Parameters for the waiting for link up routine */ ++#define LINK_WAIT_MAX_RETRIES 10 ++#define LINK_WAIT_USLEEP_MIN 90000 ++#define LINK_WAIT_USLEEP_MAX 100000 ++ ++struct starfive_jh7110_pcie { ++ struct plda_pcie_rp plda; ++ struct reset_control *resets; ++ struct clk_bulk_data *clks; ++ struct regmap *reg_syscon; ++ struct gpio_desc *power_gpio; ++ struct gpio_desc *reset_gpio; ++ struct phy *phy; ++ ++ unsigned int stg_pcie_base; ++ int num_clks; ++}; ++ ++/* ++ * The BAR0/1 of bridge should be hidden during enumeration to ++ * avoid the sizing and resource allocation by PCIe core. ++ */ ++static bool starfive_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn, ++ int offset) ++{ ++ if (pci_is_root_bus(bus) && !devfn && ++ (offset == PCI_BASE_ADDRESS_0 || offset == PCI_BASE_ADDRESS_1)) ++ return true; ++ ++ return false; ++} ++ ++static int starfive_pcie_config_write(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 value) ++{ ++ if (starfive_pcie_hide_rc_bar(bus, devfn, where)) ++ return PCIBIOS_SUCCESSFUL; ++ ++ return pci_generic_config_write(bus, devfn, where, size, value); ++} ++ ++static int starfive_pcie_config_read(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *value) ++{ ++ if (starfive_pcie_hide_rc_bar(bus, devfn, where)) { ++ *value = 0; ++ return PCIBIOS_SUCCESSFUL; ++ } ++ ++ return pci_generic_config_read(bus, devfn, where, size, value); ++} ++ ++static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie, ++ struct device *dev) ++{ ++ int domain_nr; ++ ++ pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks); ++ if (pcie->num_clks < 0) ++ return dev_err_probe(dev, pcie->num_clks, ++ "failed to get pcie clocks\n"); ++ ++ pcie->resets = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(pcie->resets)) ++ return dev_err_probe(dev, PTR_ERR(pcie->resets), ++ "failed to get pcie resets"); ++ ++ pcie->reg_syscon = ++ syscon_regmap_lookup_by_phandle(dev->of_node, ++ "starfive,stg-syscon"); ++ ++ if (IS_ERR(pcie->reg_syscon)) ++ return dev_err_probe(dev, PTR_ERR(pcie->reg_syscon), ++ "failed to parse starfive,stg-syscon\n"); ++ ++ pcie->phy = devm_phy_optional_get(dev, NULL); ++ if (IS_ERR(pcie->phy)) ++ return dev_err_probe(dev, PTR_ERR(pcie->phy), ++ "failed to get pcie phy\n"); ++ ++ domain_nr = of_get_pci_domain_nr(dev->of_node); ++ ++ if (domain_nr < 0 || domain_nr > 1) ++ return dev_err_probe(dev, -ENODEV, ++ "failed to get valid pcie domain\n"); ++ ++ if (domain_nr == 0) ++ pcie->stg_pcie_base = STG_SYSCON_PCIE0_BASE; ++ else ++ pcie->stg_pcie_base = STG_SYSCON_PCIE1_BASE; ++ ++ pcie->reset_gpio = devm_gpiod_get_optional(dev, "perst", ++ GPIOD_OUT_HIGH); ++ if (IS_ERR(pcie->reset_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pcie->reset_gpio), ++ "failed to get perst-gpio\n"); ++ ++ pcie->power_gpio = devm_gpiod_get_optional(dev, "enable", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(pcie->power_gpio)) ++ return dev_err_probe(dev, PTR_ERR(pcie->power_gpio), ++ "failed to get power-gpio\n"); ++ ++ return 0; ++} ++ ++static struct pci_ops starfive_pcie_ops = { ++ .map_bus = plda_pcie_map_bus, ++ .read = starfive_pcie_config_read, ++ .write = starfive_pcie_config_write, ++}; ++ ++static int starfive_pcie_clk_rst_init(struct starfive_jh7110_pcie *pcie) ++{ ++ struct device *dev = pcie->plda.dev; ++ int ret; ++ ++ ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to enable clocks\n"); ++ ++ ret = reset_control_deassert(pcie->resets); ++ if (ret) { ++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); ++ dev_err_probe(dev, ret, "failed to deassert resets\n"); ++ } ++ ++ return ret; ++} ++ ++static void starfive_pcie_clk_rst_deinit(struct starfive_jh7110_pcie *pcie) ++{ ++ reset_control_assert(pcie->resets); ++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); ++} ++ ++static bool starfive_pcie_link_up(struct plda_pcie_rp *plda) ++{ ++ struct starfive_jh7110_pcie *pcie = ++ container_of(plda, struct starfive_jh7110_pcie, plda); ++ int ret; ++ u32 stg_reg_val; ++ ++ ret = regmap_read(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_LNKSTA_OFFSET, ++ &stg_reg_val); ++ if (ret) { ++ dev_err(pcie->plda.dev, "failed to read link status\n"); ++ return false; ++ } ++ ++ return !!(stg_reg_val & DATA_LINK_ACTIVE); ++} ++ ++static int starfive_pcie_host_wait_for_link(struct starfive_jh7110_pcie *pcie) ++{ ++ int retries; ++ ++ /* Check if the link is up or not */ ++ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { ++ if (starfive_pcie_link_up(&pcie->plda)) { ++ dev_info(pcie->plda.dev, "port link up\n"); ++ return 0; ++ } ++ usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++static int starfive_pcie_enable_phy(struct device *dev, ++ struct starfive_jh7110_pcie *pcie) ++{ ++ int ret; ++ ++ if (!pcie->phy) ++ return 0; ++ ++ ret = phy_init(pcie->phy); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "failed to initialize pcie phy\n"); ++ ++ ret = phy_set_mode(pcie->phy, PHY_MODE_PCIE); ++ if (ret) { ++ dev_err_probe(dev, ret, "failed to set pcie mode\n"); ++ goto err_phy_on; ++ } ++ ++ ret = phy_power_on(pcie->phy); ++ if (ret) { ++ dev_err_probe(dev, ret, "failed to power on pcie phy\n"); ++ goto err_phy_on; ++ } ++ ++ return 0; ++ ++err_phy_on: ++ phy_exit(pcie->phy); ++ return ret; ++} ++ ++static void starfive_pcie_disable_phy(struct starfive_jh7110_pcie *pcie) ++{ ++ phy_power_off(pcie->phy); ++ phy_exit(pcie->phy); ++} ++ ++static void starfive_pcie_host_deinit(struct plda_pcie_rp *plda) ++{ ++ struct starfive_jh7110_pcie *pcie = ++ container_of(plda, struct starfive_jh7110_pcie, plda); ++ ++ starfive_pcie_clk_rst_deinit(pcie); ++ if (pcie->power_gpio) ++ gpiod_set_value_cansleep(pcie->power_gpio, 0); ++ starfive_pcie_disable_phy(pcie); ++} ++ ++static int starfive_pcie_host_init(struct plda_pcie_rp *plda) ++{ ++ struct starfive_jh7110_pcie *pcie = ++ container_of(plda, struct starfive_jh7110_pcie, plda); ++ struct device *dev = plda->dev; ++ int ret; ++ int i; ++ ++ ret = starfive_pcie_enable_phy(dev, pcie); ++ if (ret) ++ return ret; ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_RP_NEP_OFFSET, ++ STG_SYSCON_K_RP_NEP, STG_SYSCON_K_RP_NEP); ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET, ++ STG_SYSCON_CKREF_SRC_MASK, ++ FIELD_PREP(STG_SYSCON_CKREF_SRC_MASK, 2)); ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET, ++ STG_SYSCON_CLKREQ, STG_SYSCON_CLKREQ); ++ ++ ret = starfive_pcie_clk_rst_init(pcie); ++ if (ret) ++ return ret; ++ ++ if (pcie->power_gpio) ++ gpiod_set_value_cansleep(pcie->power_gpio, 1); ++ ++ if (pcie->reset_gpio) ++ gpiod_set_value_cansleep(pcie->reset_gpio, 1); ++ ++ /* Disable physical functions except #0 */ ++ for (i = 1; i < PCIE_FUNC_NUM; i++) { ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AR_OFFSET, ++ STG_SYSCON_AXI4_SLVL_AR_MASK, ++ STG_SYSCON_AXI4_SLVL_PHY_AR(i)); ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET, ++ STG_SYSCON_AXI4_SLVL_AW_MASK, ++ STG_SYSCON_AXI4_SLVL_PHY_AW(i)); ++ ++ plda_pcie_disable_func(plda); ++ } ++ ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AR_OFFSET, ++ STG_SYSCON_AXI4_SLVL_AR_MASK, 0); ++ regmap_update_bits(pcie->reg_syscon, ++ pcie->stg_pcie_base + STG_SYSCON_AW_OFFSET, ++ STG_SYSCON_AXI4_SLVL_AW_MASK, 0); ++ ++ plda_pcie_enable_root_port(plda); ++ plda_pcie_write_rc_bar(plda, 0); ++ ++ /* PCIe PCI Standard Configuration Identification Settings. */ ++ plda_pcie_set_standard_class(plda); ++ ++ /* ++ * The LTR message forwarding of PCIe Message Reception was set by core ++ * as default, but the forward id & addr are also need to be reset. ++ * If we do not disable LTR message forwarding here, or set a legal ++ * forwarding address, the kernel will get stuck after the driver probe. ++ * To workaround, disable the LTR message forwarding support on ++ * PCIe Message Reception. ++ */ ++ plda_pcie_disable_ltr(plda); ++ ++ /* Prefetchable memory window 64-bit addressing support */ ++ plda_pcie_set_pref_win_64bit(plda); ++ ++ /* ++ * Ensure that PERST has been asserted for at least 100 ms, ++ * the sleep value is T_PVPERL from PCIe CEM spec r2.0 (Table 2-4) ++ */ ++ msleep(100); ++ if (pcie->reset_gpio) ++ gpiod_set_value_cansleep(pcie->reset_gpio, 0); ++ ++ /* ++ * With a Downstream Port (<=5GT/s), software must wait a minimum ++ * of 100ms following exit from a conventional reset before ++ * sending a configuration request to the device. ++ */ ++ msleep(PCIE_RESET_CONFIG_DEVICE_WAIT_MS); ++ ++ if (starfive_pcie_host_wait_for_link(pcie)) ++ dev_info(dev, "port link down\n"); ++ ++ return 0; ++} ++ ++static const struct plda_pcie_host_ops sf_host_ops = { ++ .host_init = starfive_pcie_host_init, ++ .host_deinit = starfive_pcie_host_deinit, ++}; ++ ++static const struct plda_event stf_pcie_event = { ++ .intx_event = EVENT_PM_MSI_INT_INTX, ++ .msi_event = EVENT_PM_MSI_INT_MSI ++}; ++ ++static int starfive_pcie_probe(struct platform_device *pdev) ++{ ++ struct starfive_jh7110_pcie *pcie; ++ struct device *dev = &pdev->dev; ++ struct plda_pcie_rp *plda; ++ int ret; ++ ++ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); ++ if (!pcie) ++ return -ENOMEM; ++ ++ plda = &pcie->plda; ++ plda->dev = dev; ++ ++ ret = starfive_pcie_parse_dt(pcie, dev); ++ if (ret) ++ return ret; ++ ++ plda->host_ops = &sf_host_ops; ++ plda->num_events = PLDA_MAX_EVENT_NUM; ++ /* mask doorbell event */ ++ plda->events_bitmap = GENMASK(PLDA_INT_EVENT_NUM - 1, 0) ++ & ~BIT(PLDA_AXI_DOORBELL) ++ & ~BIT(PLDA_PCIE_DOORBELL); ++ plda->events_bitmap <<= PLDA_NUM_DMA_EVENTS; ++ ret = plda_pcie_host_init(&pcie->plda, &starfive_pcie_ops, ++ &stf_pcie_event); ++ if (ret) ++ return ret; ++ ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_sync(&pdev->dev); ++ platform_set_drvdata(pdev, pcie); ++ ++ return 0; ++} ++ ++static void starfive_pcie_remove(struct platform_device *pdev) ++{ ++ struct starfive_jh7110_pcie *pcie = platform_get_drvdata(pdev); ++ ++ pm_runtime_put(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ plda_pcie_host_deinit(&pcie->plda); ++ platform_set_drvdata(pdev, NULL); ++} ++ ++static int starfive_pcie_suspend_noirq(struct device *dev) ++{ ++ struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev); ++ ++ clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); ++ starfive_pcie_disable_phy(pcie); ++ ++ return 0; ++} ++ ++static int starfive_pcie_resume_noirq(struct device *dev) ++{ ++ struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = starfive_pcie_enable_phy(dev, pcie); ++ if (ret) ++ return ret; ++ ++ ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); ++ if (ret) { ++ dev_err(dev, "failed to enable clocks\n"); ++ starfive_pcie_disable_phy(pcie); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops starfive_pcie_pm_ops = { ++ NOIRQ_SYSTEM_SLEEP_PM_OPS(starfive_pcie_suspend_noirq, ++ starfive_pcie_resume_noirq) ++}; ++ ++static const struct of_device_id starfive_pcie_of_match[] = { ++ { .compatible = "starfive,jh7110-pcie", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, starfive_pcie_of_match); ++ ++static struct platform_driver starfive_pcie_driver = { ++ .driver = { ++ .name = "pcie-starfive", ++ .of_match_table = of_match_ptr(starfive_pcie_of_match), ++ .pm = pm_sleep_ptr(&starfive_pcie_pm_ops), ++ }, ++ .probe = starfive_pcie_probe, ++ .remove_new = starfive_pcie_remove, ++}; ++module_platform_driver(starfive_pcie_driver); ++ ++MODULE_DESCRIPTION("StarFive JH7110 PCIe host driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch b/target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch new file mode 100644 index 0000000000..cf14242f4a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0035-ASoC-dt-bindings-Add-StarFive-JH7110-PWM-DAC-control.patch @@ -0,0 +1,97 @@ +From a306724fd4f32808d1e27efbd87019d56f60db20 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Mon, 14 Aug 2023 16:06:16 +0800 +Subject: [PATCH 035/116] ASoC: dt-bindings: Add StarFive JH7110 PWM-DAC + controller + +Add bindings for the PWM-DAC controller on the JH7110 +RISC-V SoC by StarFive Ltd. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Hal Feng +Link: https://lore.kernel.org/r/20230814080618.10036-2-hal.feng@starfivetech.com +Signed-off-by: Mark Brown +--- + .../sound/starfive,jh7110-pwmdac.yaml | 76 +++++++++++++++++++ + 1 file changed, 76 insertions(+) + create mode 100644 Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml +@@ -0,0 +1,76 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/sound/starfive,jh7110-pwmdac.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: StarFive JH7110 PWM-DAC Controller ++ ++description: ++ The PWM-DAC Controller uses PWM square wave generators plus RC filters to ++ form a DAC for audio play in StarFive JH7110 SoC. This audio play controller ++ supports 16 bit audio format, up to 48K sampling frequency, up to left and ++ right dual channels. ++ ++maintainers: ++ - Hal Feng ++ ++allOf: ++ - $ref: dai-common.yaml# ++ ++properties: ++ compatible: ++ const: starfive,jh7110-pwmdac ++ ++ reg: ++ maxItems: 1 ++ ++ clocks: ++ items: ++ - description: PWMDAC APB ++ - description: PWMDAC CORE ++ ++ clock-names: ++ items: ++ - const: apb ++ - const: core ++ ++ resets: ++ maxItems: 1 ++ description: PWMDAC APB ++ ++ dmas: ++ maxItems: 1 ++ description: TX DMA Channel ++ ++ dma-names: ++ const: tx ++ ++ "#sound-dai-cells": ++ const: 0 ++ ++required: ++ - compatible ++ - reg ++ - clocks ++ - clock-names ++ - resets ++ - dmas ++ - dma-names ++ - "#sound-dai-cells" ++ ++additionalProperties: false ++ ++examples: ++ - | ++ pwmdac@100b0000 { ++ compatible = "starfive,jh7110-pwmdac"; ++ reg = <0x100b0000 0x1000>; ++ clocks = <&syscrg 157>, ++ <&syscrg 158>; ++ clock-names = "apb", "core"; ++ resets = <&syscrg 96>; ++ dmas = <&dma 22>; ++ dma-names = "tx"; ++ #sound-dai-cells = <0>; ++ }; diff --git a/target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch b/target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch new file mode 100644 index 0000000000..acc859317b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0036-ASoC-starfive-Add-JH7110-PWM-DAC-driver.patch @@ -0,0 +1,574 @@ +From a79d2ec524012e35e32a2c4ae2401d0aa763697d Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Mon, 14 Aug 2023 16:06:17 +0800 +Subject: [PATCH 036/116] ASoC: starfive: Add JH7110 PWM-DAC driver + +Add PWM-DAC driver support for the StarFive JH7110 SoC. + +Reviewed-by: Walker Chen +Signed-off-by: Hal Feng +Link: https://lore.kernel.org/r/20230814080618.10036-3-hal.feng@starfivetech.com +Signed-off-by: Mark Brown +--- + sound/soc/starfive/Kconfig | 9 + + sound/soc/starfive/Makefile | 1 + + sound/soc/starfive/jh7110_pwmdac.c | 529 +++++++++++++++++++++++++++++ + 3 files changed, 539 insertions(+) + create mode 100644 sound/soc/starfive/jh7110_pwmdac.c + +--- a/sound/soc/starfive/Kconfig ++++ b/sound/soc/starfive/Kconfig +@@ -7,6 +7,15 @@ config SND_SOC_STARFIVE + the Starfive SoCs' Audio interfaces. You will also need to + select the audio interfaces to support below. + ++config SND_SOC_JH7110_PWMDAC ++ tristate "JH7110 PWM-DAC device driver" ++ depends on HAVE_CLK && SND_SOC_STARFIVE ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ select SND_SOC_SPDIF ++ help ++ Say Y or M if you want to add support for StarFive JH7110 ++ PWM-DAC driver. ++ + config SND_SOC_JH7110_TDM + tristate "JH7110 TDM device driver" + depends on HAVE_CLK && SND_SOC_STARFIVE +--- a/sound/soc/starfive/Makefile ++++ b/sound/soc/starfive/Makefile +@@ -1,2 +1,3 @@ + # StarFive Platform Support ++obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o + obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o +--- /dev/null ++++ b/sound/soc/starfive/jh7110_pwmdac.c +@@ -0,0 +1,529 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * jh7110_pwmdac.c -- StarFive JH7110 PWM-DAC driver ++ * ++ * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. ++ * ++ * Authors: Jenny Zhang ++ * Curry Zhang ++ * Xingyu Wu ++ * Hal Feng ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define JH7110_PWMDAC_WDATA 0x00 ++#define JH7110_PWMDAC_CTRL 0x04 ++ #define JH7110_PWMDAC_ENABLE BIT(0) ++ #define JH7110_PWMDAC_SHIFT BIT(1) ++ #define JH7110_PWMDAC_DUTY_CYCLE_SHIFT 2 ++ #define JH7110_PWMDAC_DUTY_CYCLE_MASK GENMASK(3, 2) ++ #define JH7110_PWMDAC_CNT_N_SHIFT 4 ++ #define JH7110_PWMDAC_CNT_N_MASK GENMASK(12, 4) ++ #define JH7110_PWMDAC_DATA_CHANGE BIT(13) ++ #define JH7110_PWMDAC_DATA_MODE BIT(14) ++ #define JH7110_PWMDAC_DATA_SHIFT_SHIFT 15 ++ #define JH7110_PWMDAC_DATA_SHIFT_MASK GENMASK(17, 15) ++ ++enum JH7110_PWMDAC_SHIFT_VAL { ++ PWMDAC_SHIFT_8 = 0, ++ PWMDAC_SHIFT_10, ++}; ++ ++enum JH7110_PWMDAC_DUTY_CYCLE_VAL { ++ PWMDAC_CYCLE_LEFT = 0, ++ PWMDAC_CYCLE_RIGHT, ++ PWMDAC_CYCLE_CENTER, ++}; ++ ++enum JH7110_PWMDAC_CNT_N_VAL { ++ PWMDAC_SAMPLE_CNT_1 = 1, ++ PWMDAC_SAMPLE_CNT_2, ++ PWMDAC_SAMPLE_CNT_3, ++ PWMDAC_SAMPLE_CNT_512 = 512, /* max */ ++}; ++ ++enum JH7110_PWMDAC_DATA_CHANGE_VAL { ++ NO_CHANGE = 0, ++ CHANGE, ++}; ++ ++enum JH7110_PWMDAC_DATA_MODE_VAL { ++ UNSIGNED_DATA = 0, ++ INVERTER_DATA_MSB, ++}; ++ ++enum JH7110_PWMDAC_DATA_SHIFT_VAL { ++ PWMDAC_DATA_LEFT_SHIFT_BIT_0 = 0, ++ PWMDAC_DATA_LEFT_SHIFT_BIT_1, ++ PWMDAC_DATA_LEFT_SHIFT_BIT_2, ++ PWMDAC_DATA_LEFT_SHIFT_BIT_3, ++ PWMDAC_DATA_LEFT_SHIFT_BIT_4, ++ PWMDAC_DATA_LEFT_SHIFT_BIT_5, ++ PWMDAC_DATA_LEFT_SHIFT_BIT_6, ++ PWMDAC_DATA_LEFT_SHIFT_BIT_7, ++}; ++ ++struct jh7110_pwmdac_cfg { ++ enum JH7110_PWMDAC_SHIFT_VAL shift; ++ enum JH7110_PWMDAC_DUTY_CYCLE_VAL duty_cycle; ++ u16 cnt_n; ++ enum JH7110_PWMDAC_DATA_CHANGE_VAL data_change; ++ enum JH7110_PWMDAC_DATA_MODE_VAL data_mode; ++ enum JH7110_PWMDAC_DATA_SHIFT_VAL data_shift; ++}; ++ ++struct jh7110_pwmdac_dev { ++ void __iomem *base; ++ resource_size_t mapbase; ++ struct jh7110_pwmdac_cfg cfg; ++ ++ struct clk_bulk_data clks[2]; ++ struct reset_control *rst_apb; ++ struct device *dev; ++ struct snd_dmaengine_dai_dma_data play_dma_data; ++ u32 saved_ctrl; ++}; ++ ++static inline void jh7110_pwmdac_write_reg(void __iomem *io_base, int reg, u32 val) ++{ ++ writel(val, io_base + reg); ++} ++ ++static inline u32 jh7110_pwmdac_read_reg(void __iomem *io_base, int reg) ++{ ++ return readl(io_base + reg); ++} ++ ++static void jh7110_pwmdac_set_enable(struct jh7110_pwmdac_dev *dev, bool enable) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ if (enable) ++ value |= JH7110_PWMDAC_ENABLE; ++ else ++ value &= ~JH7110_PWMDAC_ENABLE; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_shift(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ if (dev->cfg.shift == PWMDAC_SHIFT_8) ++ value &= ~JH7110_PWMDAC_SHIFT; ++ else if (dev->cfg.shift == PWMDAC_SHIFT_10) ++ value |= JH7110_PWMDAC_SHIFT; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_duty_cycle(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ value &= ~JH7110_PWMDAC_DUTY_CYCLE_MASK; ++ value |= (dev->cfg.duty_cycle & 0x3) << JH7110_PWMDAC_DUTY_CYCLE_SHIFT; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_cnt_n(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ value &= ~JH7110_PWMDAC_CNT_N_MASK; ++ value |= ((dev->cfg.cnt_n - 1) & 0x1ff) << JH7110_PWMDAC_CNT_N_SHIFT; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_data_change(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ if (dev->cfg.data_change == NO_CHANGE) ++ value &= ~JH7110_PWMDAC_DATA_CHANGE; ++ else if (dev->cfg.data_change == CHANGE) ++ value |= JH7110_PWMDAC_DATA_CHANGE; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_data_mode(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ if (dev->cfg.data_mode == UNSIGNED_DATA) ++ value &= ~JH7110_PWMDAC_DATA_MODE; ++ else if (dev->cfg.data_mode == INVERTER_DATA_MSB) ++ value |= JH7110_PWMDAC_DATA_MODE; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set_data_shift(struct jh7110_pwmdac_dev *dev) ++{ ++ u32 value; ++ ++ value = jh7110_pwmdac_read_reg(dev->base, JH7110_PWMDAC_CTRL); ++ value &= ~JH7110_PWMDAC_DATA_SHIFT_MASK; ++ value |= (dev->cfg.data_shift & 0x7) << JH7110_PWMDAC_DATA_SHIFT_SHIFT; ++ ++ jh7110_pwmdac_write_reg(dev->base, JH7110_PWMDAC_CTRL, value); ++} ++ ++static void jh7110_pwmdac_set(struct jh7110_pwmdac_dev *dev) ++{ ++ jh7110_pwmdac_set_shift(dev); ++ jh7110_pwmdac_set_duty_cycle(dev); ++ jh7110_pwmdac_set_cnt_n(dev); ++ jh7110_pwmdac_set_enable(dev, true); ++ ++ jh7110_pwmdac_set_data_change(dev); ++ jh7110_pwmdac_set_data_mode(dev); ++ jh7110_pwmdac_set_data_shift(dev); ++} ++ ++static void jh7110_pwmdac_stop(struct jh7110_pwmdac_dev *dev) ++{ ++ jh7110_pwmdac_set_enable(dev, false); ++} ++ ++static int jh7110_pwmdac_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); ++ struct snd_soc_dai_link *dai_link = rtd->dai_link; ++ ++ dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; ++ ++ return 0; ++} ++ ++static int jh7110_pwmdac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev); ++ unsigned long core_clk_rate; ++ int ret; ++ ++ switch (params_rate(params)) { ++ case 8000: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3; ++ core_clk_rate = 6144000; ++ break; ++ case 11025: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_2; ++ core_clk_rate = 5644800; ++ break; ++ case 16000: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_3; ++ core_clk_rate = 12288000; ++ break; ++ case 22050: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ core_clk_rate = 5644800; ++ break; ++ case 32000: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ core_clk_rate = 8192000; ++ break; ++ case 44100: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ core_clk_rate = 11289600; ++ break; ++ case 48000: ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ core_clk_rate = 12288000; ++ break; ++ default: ++ dev_err(dai->dev, "%d rate not supported\n", ++ params_rate(params)); ++ return -EINVAL; ++ } ++ ++ switch (params_channels(params)) { ++ case 1: ++ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; ++ break; ++ case 2: ++ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ break; ++ default: ++ dev_err(dai->dev, "%d channels not supported\n", ++ params_channels(params)); ++ return -EINVAL; ++ } ++ ++ /* ++ * The clock rate always rounds down when using clk_set_rate() ++ * so increase the rate a bit ++ */ ++ core_clk_rate += 64; ++ jh7110_pwmdac_set(dev); ++ ++ ret = clk_set_rate(dev->clks[1].clk, core_clk_rate); ++ if (ret) ++ return dev_err_probe(dai->dev, ret, ++ "failed to set rate %lu for core clock\n", ++ core_clk_rate); ++ ++ return 0; ++} ++ ++static int jh7110_pwmdac_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct jh7110_pwmdac_dev *dev = snd_soc_dai_get_drvdata(dai); ++ int ret = 0; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ jh7110_pwmdac_set(dev); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ jh7110_pwmdac_stop(dev); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int jh7110_pwmdac_crg_enable(struct jh7110_pwmdac_dev *dev, bool enable) ++{ ++ int ret; ++ ++ if (enable) { ++ ret = clk_bulk_prepare_enable(ARRAY_SIZE(dev->clks), dev->clks); ++ if (ret) ++ return dev_err_probe(dev->dev, ret, ++ "failed to enable pwmdac clocks\n"); ++ ++ ret = reset_control_deassert(dev->rst_apb); ++ if (ret) { ++ dev_err(dev->dev, "failed to deassert pwmdac apb reset\n"); ++ goto err_rst_apb; ++ } ++ } else { ++ clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks); ++ } ++ ++ return 0; ++ ++err_rst_apb: ++ clk_bulk_disable_unprepare(ARRAY_SIZE(dev->clks), dev->clks); ++ ++ return ret; ++} ++ ++static int jh7110_pwmdac_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct jh7110_pwmdac_dev *dev = dev_get_drvdata(dai->dev); ++ ++ snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, NULL); ++ snd_soc_dai_set_drvdata(dai, dev); ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = { ++ .startup = jh7110_pwmdac_startup, ++ .hw_params = jh7110_pwmdac_hw_params, ++ .trigger = jh7110_pwmdac_trigger, ++}; ++ ++static const struct snd_soc_component_driver jh7110_pwmdac_component = { ++ .name = "jh7110-pwmdac", ++}; ++ ++static struct snd_soc_dai_driver jh7110_pwmdac_dai = { ++ .name = "jh7110-pwmdac", ++ .id = 0, ++ .probe = jh7110_pwmdac_dai_probe, ++ .playback = { ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ }, ++ .ops = &jh7110_pwmdac_dai_ops, ++}; ++ ++static int jh7110_pwmdac_runtime_suspend(struct device *dev) ++{ ++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); ++ ++ return jh7110_pwmdac_crg_enable(pwmdac, false); ++} ++ ++static int jh7110_pwmdac_runtime_resume(struct device *dev) ++{ ++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); ++ ++ return jh7110_pwmdac_crg_enable(pwmdac, true); ++} ++ ++static int jh7110_pwmdac_system_suspend(struct device *dev) ++{ ++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); ++ ++ /* save the CTRL register value */ ++ pwmdac->saved_ctrl = jh7110_pwmdac_read_reg(pwmdac->base, ++ JH7110_PWMDAC_CTRL); ++ return pm_runtime_force_suspend(dev); ++} ++ ++static int jh7110_pwmdac_system_resume(struct device *dev) ++{ ++ struct jh7110_pwmdac_dev *pwmdac = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pm_runtime_force_resume(dev); ++ if (ret) ++ return ret; ++ ++ /* restore the CTRL register value */ ++ jh7110_pwmdac_write_reg(pwmdac->base, JH7110_PWMDAC_CTRL, ++ pwmdac->saved_ctrl); ++ return 0; ++} ++ ++static const struct dev_pm_ops jh7110_pwmdac_pm_ops = { ++ RUNTIME_PM_OPS(jh7110_pwmdac_runtime_suspend, ++ jh7110_pwmdac_runtime_resume, NULL) ++ SYSTEM_SLEEP_PM_OPS(jh7110_pwmdac_system_suspend, ++ jh7110_pwmdac_system_resume) ++}; ++ ++static void jh7110_pwmdac_init_params(struct jh7110_pwmdac_dev *dev) ++{ ++ dev->cfg.shift = PWMDAC_SHIFT_8; ++ dev->cfg.duty_cycle = PWMDAC_CYCLE_CENTER; ++ dev->cfg.cnt_n = PWMDAC_SAMPLE_CNT_1; ++ dev->cfg.data_change = NO_CHANGE; ++ dev->cfg.data_mode = INVERTER_DATA_MSB; ++ dev->cfg.data_shift = PWMDAC_DATA_LEFT_SHIFT_BIT_0; ++ ++ dev->play_dma_data.addr = dev->mapbase + JH7110_PWMDAC_WDATA; ++ dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dev->play_dma_data.fifo_size = 1; ++ dev->play_dma_data.maxburst = 16; ++} ++ ++static int jh7110_pwmdac_probe(struct platform_device *pdev) ++{ ++ struct jh7110_pwmdac_dev *dev; ++ struct resource *res; ++ int ret; ++ ++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); ++ if (IS_ERR(dev->base)) ++ return PTR_ERR(dev->base); ++ ++ dev->mapbase = res->start; ++ ++ dev->clks[0].id = "apb"; ++ dev->clks[1].id = "core"; ++ ++ ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(dev->clks), dev->clks); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, ++ "failed to get pwmdac clocks\n"); ++ ++ dev->rst_apb = devm_reset_control_get_exclusive(&pdev->dev, NULL); ++ if (IS_ERR(dev->rst_apb)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(dev->rst_apb), ++ "failed to get pwmdac apb reset\n"); ++ ++ jh7110_pwmdac_init_params(dev); ++ ++ dev->dev = &pdev->dev; ++ dev_set_drvdata(&pdev->dev, dev); ++ ret = devm_snd_soc_register_component(&pdev->dev, ++ &jh7110_pwmdac_component, ++ &jh7110_pwmdac_dai, 1); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed to register dai\n"); ++ ++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); ++ if (ret) ++ return dev_err_probe(&pdev->dev, ret, "failed to register pcm\n"); ++ ++ pm_runtime_enable(dev->dev); ++ if (!pm_runtime_enabled(&pdev->dev)) { ++ ret = jh7110_pwmdac_runtime_resume(&pdev->dev); ++ if (ret) ++ goto err_pm_disable; ++ } ++ ++ return 0; ++ ++err_pm_disable: ++ pm_runtime_disable(&pdev->dev); ++ ++ return ret; ++} ++ ++static int jh7110_pwmdac_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ return 0; ++} ++ ++static const struct of_device_id jh7110_pwmdac_of_match[] = { ++ { .compatible = "starfive,jh7110-pwmdac" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, jh7110_pwmdac_of_match); ++ ++static struct platform_driver jh7110_pwmdac_driver = { ++ .driver = { ++ .name = "jh7110-pwmdac", ++ .of_match_table = jh7110_pwmdac_of_match, ++ .pm = pm_ptr(&jh7110_pwmdac_pm_ops), ++ }, ++ .probe = jh7110_pwmdac_probe, ++ .remove = jh7110_pwmdac_remove, ++}; ++module_platform_driver(jh7110_pwmdac_driver); ++ ++MODULE_AUTHOR("Jenny Zhang"); ++MODULE_AUTHOR("Curry Zhang"); ++MODULE_AUTHOR("Xingyu Wu "); ++MODULE_AUTHOR("Hal Feng "); ++MODULE_DESCRIPTION("StarFive JH7110 PWM-DAC driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch b/target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch new file mode 100644 index 0000000000..4833067bac --- /dev/null +++ b/target/linux/starfive/patches-6.6/0037-ASoC-Update-jh7110-PWM-DAC-for-ops-move.patch @@ -0,0 +1,32 @@ +From c6b693f990e1f89ab5af0a139da31401b8cda74f Mon Sep 17 00:00:00 2001 +From: Mark Brown +Date: Mon, 11 Sep 2023 23:48:39 +0100 +Subject: [PATCH 037/116] ASoC: Update jh7110 PWM DAC for ops move + +For some reason the JH7110 PWM DAC driver made it through build testing +in spite of not being updated for the move of probe() to the ops struct. +Make the required update. + +Signed-off-by: Mark Brown +--- + sound/soc/starfive/jh7110_pwmdac.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/sound/soc/starfive/jh7110_pwmdac.c ++++ b/sound/soc/starfive/jh7110_pwmdac.c +@@ -357,6 +357,7 @@ static int jh7110_pwmdac_dai_probe(struc + } + + static const struct snd_soc_dai_ops jh7110_pwmdac_dai_ops = { ++ .probe = jh7110_pwmdac_dai_probe, + .startup = jh7110_pwmdac_startup, + .hw_params = jh7110_pwmdac_hw_params, + .trigger = jh7110_pwmdac_trigger, +@@ -369,7 +370,6 @@ static const struct snd_soc_component_dr + static struct snd_soc_dai_driver jh7110_pwmdac_dai = { + .name = "jh7110-pwmdac", + .id = 0, +- .probe = jh7110_pwmdac_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, diff --git a/target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch b/target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch new file mode 100644 index 0000000000..9b5f553f8c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0038-ASoC-starfive-jh7110-pwmdac-Convert-to-platform-remo.patch @@ -0,0 +1,52 @@ +From 312c3c407c363f0ec7417d2d389cbe936c503729 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= +Date: Sat, 14 Oct 2023 00:19:49 +0200 +Subject: [PATCH 038/116] ASoC: starfive/jh7110-pwmdac: Convert to platform + remove callback returning void +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The .remove() callback for a platform driver returns an int which makes +many driver authors wrongly assume it's possible to do error handling by +returning an error code. However the value returned is ignored (apart +from emitting a warning) and this typically results in resource leaks. + +To improve here there is a quest to make the remove callback return +void. In the first step of this quest all drivers are converted to +.remove_new(), which already returns void. Eventually after all drivers +are converted, .remove_new() will be renamed to .remove(). + +Trivially convert this driver from always returning zero in the remove +callback to the void returning variant. + +Signed-off-by: Uwe Kleine-König +Link: https://lore.kernel.org/r/20231013221945.1489203-12-u.kleine-koenig@pengutronix.de +Signed-off-by: Mark Brown +--- + sound/soc/starfive/jh7110_pwmdac.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/sound/soc/starfive/jh7110_pwmdac.c ++++ b/sound/soc/starfive/jh7110_pwmdac.c +@@ -498,10 +498,9 @@ err_pm_disable: + return ret; + } + +-static int jh7110_pwmdac_remove(struct platform_device *pdev) ++static void jh7110_pwmdac_remove(struct platform_device *pdev) + { + pm_runtime_disable(&pdev->dev); +- return 0; + } + + static const struct of_device_id jh7110_pwmdac_of_match[] = { +@@ -517,7 +516,7 @@ static struct platform_driver jh7110_pwm + .pm = pm_ptr(&jh7110_pwmdac_pm_ops), + }, + .probe = jh7110_pwmdac_probe, +- .remove = jh7110_pwmdac_remove, ++ .remove_new = jh7110_pwmdac_remove, + }; + module_platform_driver(jh7110_pwmdac_driver); + diff --git a/target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch b/target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch new file mode 100644 index 0000000000..bcc9af2d2c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0039-dt-bindings-power-Add-power-domain-header-for-JH7110.patch @@ -0,0 +1,35 @@ +From 8d84bac6d7471ba2e29b33d19a2ef887822e9cce Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 13 Sep 2023 14:54:25 +0100 +Subject: [PATCH 039/116] dt-bindings: power: Add power-domain header for + JH7110 + +Add power-domain header for JH7110 SoC, it can use to operate dphy +power. + +Signed-off-by: Changhuang Liang +Signed-off-by: Conor Dooley +Link: https://lore.kernel.org/r/20230913-grumbly-rewrite-34c85539f2ed@spud +Signed-off-by: Ulf Hansson +--- + include/dt-bindings/power/starfive,jh7110-pmu.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/include/dt-bindings/power/starfive,jh7110-pmu.h ++++ b/include/dt-bindings/power/starfive,jh7110-pmu.h +@@ -1,6 +1,6 @@ + /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + /* +- * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. + * Author: Walker Chen + */ + #ifndef __DT_BINDINGS_POWER_JH7110_POWER_H__ +@@ -14,4 +14,7 @@ + #define JH7110_PD_ISP 5 + #define JH7110_PD_VENC 6 + ++#define JH7110_PD_DPHY_TX 0 ++#define JH7110_PD_DPHY_RX 1 ++ + #endif diff --git a/target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch b/target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch new file mode 100644 index 0000000000..335193fbb6 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0040-pmdomain-starfive-Replace-SOC_STARFIVE-with-ARCH_STA.patch @@ -0,0 +1,31 @@ +From 0ac8e8b0e65d242f455401df0cc6c6d4772216e6 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 13 Sep 2023 14:54:26 +0100 +Subject: [PATCH 040/116] pmdomain: starfive: Replace SOC_STARFIVE with + ARCH_STARFIVE + +Using ARCH_FOO symbol is preferred than SOC_FOO. + +Reviewed-by: Conor Dooley +Reviewed-by: Walker Chen +Signed-off-by: Changhuang Liang +Signed-off-by: Conor Dooley +Link: https://lore.kernel.org/r/20230913-legibly-treachery-567cffcb5604@spud +Signed-off-by: Ulf Hansson +--- + drivers/soc/starfive/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/soc/starfive/Kconfig ++++ b/drivers/soc/starfive/Kconfig +@@ -3,8 +3,8 @@ + config JH71XX_PMU + bool "Support PMU for StarFive JH71XX Soc" + depends on PM +- depends on SOC_STARFIVE || COMPILE_TEST +- default SOC_STARFIVE ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ default ARCH_STARFIVE + select PM_GENERIC_DOMAINS + help + Say 'y' here to enable support power domain support. diff --git a/target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch b/target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch new file mode 100644 index 0000000000..7425dd6dbb --- /dev/null +++ b/target/linux/starfive/patches-6.6/0041-pmdomain-starfive-Extract-JH7110-pmu-private-operati.patch @@ -0,0 +1,179 @@ +From a1ba60e35ca7f1b85243054556ecde2e259619e1 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 13 Sep 2023 14:54:27 +0100 +Subject: [PATCH 041/116] pmdomain: starfive: Extract JH7110 pmu private + operations + +Move JH7110 private operation into private data of compatible. Convenient +to add AON PMU which would not have interrupts property. + +Signed-off-by: Changhuang Liang +Reviewed-by: Walker Chen +Signed-off-by: Conor Dooley +Link: https://lore.kernel.org/r/20230913-slideshow-luckiness-38ff17de84c6@spud +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/starfive/jh71xx-pmu.c | 89 ++++++++++++++++++-------- + 1 file changed, 62 insertions(+), 27 deletions(-) + +--- a/drivers/pmdomain/starfive/jh71xx-pmu.c ++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c +@@ -51,9 +51,17 @@ struct jh71xx_domain_info { + u8 bit; + }; + ++struct jh71xx_pmu; ++struct jh71xx_pmu_dev; ++ + struct jh71xx_pmu_match_data { + const struct jh71xx_domain_info *domain_info; + int num_domains; ++ unsigned int pmu_status; ++ int (*pmu_parse_irq)(struct platform_device *pdev, ++ struct jh71xx_pmu *pmu); ++ int (*pmu_set_state)(struct jh71xx_pmu_dev *pmd, ++ u32 mask, bool on); + }; + + struct jh71xx_pmu { +@@ -79,12 +87,12 @@ static int jh71xx_pmu_get_state(struct j + if (!mask) + return -EINVAL; + +- *is_on = readl(pmu->base + JH71XX_PMU_CURR_POWER_MODE) & mask; ++ *is_on = readl(pmu->base + pmu->match_data->pmu_status) & mask; + + return 0; + } + +-static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) ++static int jh7110_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) + { + struct jh71xx_pmu *pmu = pmd->pmu; + unsigned long flags; +@@ -92,22 +100,8 @@ static int jh71xx_pmu_set_state(struct j + u32 mode; + u32 encourage_lo; + u32 encourage_hi; +- bool is_on; + int ret; + +- ret = jh71xx_pmu_get_state(pmd, mask, &is_on); +- if (ret) { +- dev_dbg(pmu->dev, "unable to get current state for %s\n", +- pmd->genpd.name); +- return ret; +- } +- +- if (is_on == on) { +- dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", +- pmd->genpd.name, on ? "en" : "dis"); +- return 0; +- } +- + spin_lock_irqsave(&pmu->lock, flags); + + /* +@@ -166,6 +160,29 @@ static int jh71xx_pmu_set_state(struct j + return 0; + } + ++static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) ++{ ++ struct jh71xx_pmu *pmu = pmd->pmu; ++ const struct jh71xx_pmu_match_data *match_data = pmu->match_data; ++ bool is_on; ++ int ret; ++ ++ ret = jh71xx_pmu_get_state(pmd, mask, &is_on); ++ if (ret) { ++ dev_dbg(pmu->dev, "unable to get current state for %s\n", ++ pmd->genpd.name); ++ return ret; ++ } ++ ++ if (is_on == on) { ++ dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", ++ pmd->genpd.name, on ? "en" : "dis"); ++ return 0; ++ } ++ ++ return match_data->pmu_set_state(pmd, mask, on); ++} ++ + static int jh71xx_pmu_on(struct generic_pm_domain *genpd) + { + struct jh71xx_pmu_dev *pmd = container_of(genpd, +@@ -226,6 +243,25 @@ static irqreturn_t jh71xx_pmu_interrupt( + return IRQ_HANDLED; + } + ++static int jh7110_pmu_parse_irq(struct platform_device *pdev, struct jh71xx_pmu *pmu) ++{ ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ pmu->irq = platform_get_irq(pdev, 0); ++ if (pmu->irq < 0) ++ return pmu->irq; ++ ++ ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt, ++ 0, pdev->name, pmu); ++ if (ret) ++ dev_err(dev, "failed to request irq\n"); ++ ++ jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); ++ ++ return 0; ++} ++ + static int jh71xx_pmu_init_domain(struct jh71xx_pmu *pmu, int index) + { + struct jh71xx_pmu_dev *pmd; +@@ -275,19 +311,18 @@ static int jh71xx_pmu_probe(struct platf + 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, jh71xx_pmu_interrupt, +- 0, pdev->name, pmu); +- if (ret) +- dev_err(dev, "failed to request irq\n"); ++ spin_lock_init(&pmu->lock); + + match_data = of_device_get_match_data(dev); + if (!match_data) + return -EINVAL; + ++ ret = match_data->pmu_parse_irq(pdev, pmu); ++ if (ret) { ++ dev_err(dev, "failed to parse irq\n"); ++ return ret; ++ } ++ + pmu->genpd = devm_kcalloc(dev, match_data->num_domains, + sizeof(struct generic_pm_domain *), + GFP_KERNEL); +@@ -307,9 +342,6 @@ static int jh71xx_pmu_probe(struct platf + } + } + +- spin_lock_init(&pmu->lock); +- jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); +- + ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data); + if (ret) { + dev_err(dev, "failed to register genpd driver: %d\n", ret); +@@ -357,6 +389,9 @@ static const struct jh71xx_domain_info j + static const struct jh71xx_pmu_match_data jh7110_pmu = { + .num_domains = ARRAY_SIZE(jh7110_power_domains), + .domain_info = jh7110_power_domains, ++ .pmu_status = JH71XX_PMU_CURR_POWER_MODE, ++ .pmu_parse_irq = jh7110_pmu_parse_irq, ++ .pmu_set_state = jh7110_pmu_set_state, + }; + + static const struct of_device_id jh71xx_pmu_of_match[] = { diff --git a/target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch b/target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch new file mode 100644 index 0000000000..2563d5421c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0042-pmdomain-starfive-Add-JH7110-AON-PMU-support.patch @@ -0,0 +1,122 @@ +From 1bf849b606d0b4cae643f96685d3d3981643683d Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 13 Sep 2023 14:54:28 +0100 +Subject: [PATCH 042/116] pmdomain: starfive: Add JH7110 AON PMU support + +Add AON PMU for StarFive JH7110 SoC. It can be used to turn on/off the +dphy rx/tx power switch. + +Reviewed-by: Walker Chen +Signed-off-by: Changhuang Liang +Signed-off-by: Conor Dooley +Link: https://lore.kernel.org/r/20230913-dude-imprecise-fc32622bc947@spud +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/starfive/jh71xx-pmu.c | 57 +++++++++++++++++++++++--- + 1 file changed, 52 insertions(+), 5 deletions(-) + +--- a/drivers/pmdomain/starfive/jh71xx-pmu.c ++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c +@@ -2,7 +2,7 @@ + /* + * StarFive JH71XX PMU (Power Management Unit) Controller Driver + * +- * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ * Copyright (C) 2022-2023 StarFive Technology Co., Ltd. + */ + + #include +@@ -24,6 +24,9 @@ + #define JH71XX_PMU_EVENT_STATUS 0x88 + #define JH71XX_PMU_INT_STATUS 0x8C + ++/* aon pmu register offset */ ++#define JH71XX_AON_PMU_SWITCH 0x00 ++ + /* sw encourage cfg */ + #define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05 + #define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50 +@@ -160,6 +163,26 @@ static int jh7110_pmu_set_state(struct j + return 0; + } + ++static int jh7110_aon_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) ++{ ++ struct jh71xx_pmu *pmu = pmd->pmu; ++ unsigned long flags; ++ u32 val; ++ ++ spin_lock_irqsave(&pmu->lock, flags); ++ val = readl(pmu->base + JH71XX_AON_PMU_SWITCH); ++ ++ if (on) ++ val |= mask; ++ else ++ val &= ~mask; ++ ++ writel(val, pmu->base + JH71XX_AON_PMU_SWITCH); ++ spin_unlock_irqrestore(&pmu->lock, flags); ++ ++ return 0; ++} ++ + static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) + { + struct jh71xx_pmu *pmu = pmd->pmu; +@@ -317,10 +340,12 @@ static int jh71xx_pmu_probe(struct platf + if (!match_data) + return -EINVAL; + +- ret = match_data->pmu_parse_irq(pdev, pmu); +- if (ret) { +- dev_err(dev, "failed to parse irq\n"); +- return ret; ++ if (match_data->pmu_parse_irq) { ++ ret = match_data->pmu_parse_irq(pdev, pmu); ++ if (ret) { ++ dev_err(dev, "failed to parse irq\n"); ++ return ret; ++ } + } + + pmu->genpd = devm_kcalloc(dev, match_data->num_domains, +@@ -394,11 +419,32 @@ static const struct jh71xx_pmu_match_dat + .pmu_set_state = jh7110_pmu_set_state, + }; + ++static const struct jh71xx_domain_info jh7110_aon_power_domains[] = { ++ [JH7110_PD_DPHY_TX] = { ++ .name = "DPHY-TX", ++ .bit = 30, ++ }, ++ [JH7110_PD_DPHY_RX] = { ++ .name = "DPHY-RX", ++ .bit = 31, ++ }, ++}; ++ ++static const struct jh71xx_pmu_match_data jh7110_aon_pmu = { ++ .num_domains = ARRAY_SIZE(jh7110_aon_power_domains), ++ .domain_info = jh7110_aon_power_domains, ++ .pmu_status = JH71XX_AON_PMU_SWITCH, ++ .pmu_set_state = jh7110_aon_pmu_set_state, ++}; ++ + static const struct of_device_id jh71xx_pmu_of_match[] = { + { + .compatible = "starfive,jh7110-pmu", + .data = (void *)&jh7110_pmu, + }, { ++ .compatible = "starfive,jh7110-aon-syscon", ++ .data = (void *)&jh7110_aon_pmu, ++ }, { + /* sentinel */ + } + }; +@@ -414,5 +460,6 @@ static struct platform_driver jh71xx_pmu + builtin_platform_driver(jh71xx_pmu_driver); + + MODULE_AUTHOR("Walker Chen "); ++MODULE_AUTHOR("Changhuang Liang "); + MODULE_DESCRIPTION("StarFive JH71XX PMU Driver"); + MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch b/target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch new file mode 100644 index 0000000000..52be89f29e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0043-pmdomain-Prepare-to-move-Kconfig-files-into-the-pmdo.patch @@ -0,0 +1,36 @@ +From dff6605dcd1fc1e2af1437e59187a6f71ce389cd Mon Sep 17 00:00:00 2001 +From: Ulf Hansson +Date: Mon, 11 Sep 2023 17:22:15 +0200 +Subject: [PATCH 043/116] pmdomain: Prepare to move Kconfig files into the + pmdomain subsystem + +Rather than having the various Kconfig files for the genpd providers +sprinkled across subsystems, let's prepare to move them into the pmdomain +subsystem along with the implementations. + +Reviewed-by: Geert Uytterhoeven +Signed-off-by: Ulf Hansson +--- + drivers/Kconfig | 2 ++ + drivers/pmdomain/Kconfig | 4 ++++ + 2 files changed, 6 insertions(+) + create mode 100644 drivers/pmdomain/Kconfig + +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -175,6 +175,8 @@ source "drivers/soundwire/Kconfig" + + source "drivers/soc/Kconfig" + ++source "drivers/pmdomain/Kconfig" ++ + source "drivers/devfreq/Kconfig" + + source "drivers/extcon/Kconfig" +--- /dev/null ++++ b/drivers/pmdomain/Kconfig +@@ -0,0 +1,4 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++menu "PM Domains" ++ ++endmenu diff --git a/target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch b/target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch new file mode 100644 index 0000000000..909f4cc63a --- /dev/null +++ b/target/linux/starfive/patches-6.6/0044-pmdomain-starfive-Move-Kconfig-file-to-the-pmdomain-.patch @@ -0,0 +1,69 @@ +From de12fe43dbd0ea9fa980ffa05822bd7fd5eed330 Mon Sep 17 00:00:00 2001 +From: Ulf Hansson +Date: Tue, 12 Sep 2023 13:31:44 +0200 +Subject: [PATCH 044/116] pmdomain: starfive: Move Kconfig file to the pmdomain + subsystem + +The Kconfig belongs closer to the corresponding implementation, hence let's +move it from the soc subsystem to the pmdomain subsystem. + +Cc: Walker Chen +Cc: Conor Dooley +Acked-by: Conor Dooley +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/Kconfig | 2 ++ + drivers/{soc => pmdomain}/starfive/Kconfig | 0 + drivers/soc/Kconfig | 1 - + 3 files changed, 2 insertions(+), 1 deletion(-) + rename drivers/{soc => pmdomain}/starfive/Kconfig (100%) + +--- a/drivers/pmdomain/Kconfig ++++ b/drivers/pmdomain/Kconfig +@@ -1,4 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0-only + menu "PM Domains" + ++source "drivers/pmdomain/starfive/Kconfig" ++ + endmenu +--- a/drivers/soc/Kconfig ++++ b/drivers/soc/Kconfig +@@ -24,7 +24,6 @@ source "drivers/soc/renesas/Kconfig" + source "drivers/soc/rockchip/Kconfig" + source "drivers/soc/samsung/Kconfig" + source "drivers/soc/sifive/Kconfig" +-source "drivers/soc/starfive/Kconfig" + source "drivers/soc/sunxi/Kconfig" + source "drivers/soc/tegra/Kconfig" + source "drivers/soc/ti/Kconfig" +--- /dev/null ++++ b/drivers/pmdomain/starfive/Kconfig +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++config JH71XX_PMU ++ bool "Support PMU for StarFive JH71XX Soc" ++ depends on PM ++ depends on ARCH_STARFIVE || COMPILE_TEST ++ default ARCH_STARFIVE ++ select PM_GENERIC_DOMAINS ++ help ++ Say 'y' here to enable support power domain support. ++ In order to meet low power requirements, a Power Management Unit (PMU) ++ is designed for controlling power resources in StarFive JH71XX SoCs. +--- a/drivers/soc/starfive/Kconfig ++++ /dev/null +@@ -1,12 +0,0 @@ +-# SPDX-License-Identifier: GPL-2.0 +- +-config JH71XX_PMU +- bool "Support PMU for StarFive JH71XX Soc" +- depends on PM +- depends on ARCH_STARFIVE || COMPILE_TEST +- default ARCH_STARFIVE +- select PM_GENERIC_DOMAINS +- help +- Say 'y' here to enable support power domain support. +- In order to meet low power requirements, a Power Management Unit (PMU) +- is designed for controlling power resources in StarFive JH71XX SoCs. diff --git a/target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch b/target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch new file mode 100644 index 0000000000..c6e0012528 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0045-dt-bindings-power-Update-prefixes-for-AON-power-doma.patch @@ -0,0 +1,31 @@ +From cac9ce9c7f388a741389b1ec47af65420254db55 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 27 Sep 2023 06:07:33 -0700 +Subject: [PATCH 045/116] dt-bindings: power: Update prefixes for AON power + domain + +Use "JH7110_AON_PD_" prefix for AON power domain for JH7110 SoC. + +Reviewed-by: Walker Chen +Signed-off-by: Changhuang Liang +Acked-by: Conor Dooley +Reviewed-by: Geert Uytterhoeven +Link: https://lore.kernel.org/r/20230927130734.9921-2-changhuang.liang@starfivetech.com +Signed-off-by: Ulf Hansson +--- + include/dt-bindings/power/starfive,jh7110-pmu.h | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/include/dt-bindings/power/starfive,jh7110-pmu.h ++++ b/include/dt-bindings/power/starfive,jh7110-pmu.h +@@ -14,7 +14,8 @@ + #define JH7110_PD_ISP 5 + #define JH7110_PD_VENC 6 + +-#define JH7110_PD_DPHY_TX 0 +-#define JH7110_PD_DPHY_RX 1 ++/* AON Power Domain */ ++#define JH7110_AON_PD_DPHY_TX 0 ++#define JH7110_AON_PD_DPHY_RX 1 + + #endif diff --git a/target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch b/target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch new file mode 100644 index 0000000000..5cb89ae052 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0046-pmdomain-starfive-Update-prefixes-for-AON-power-doma.patch @@ -0,0 +1,32 @@ +From 3ea89ffbd6cc5a15acca6bc2130572f8bd85b9d4 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 27 Sep 2023 06:07:34 -0700 +Subject: [PATCH 046/116] pmdomain: starfive: Update prefixes for AON power + domain + +Use "JH7110_AON_PD_" prefix for AON power doamin for JH7110 SoC. + +Reviewed-by: Walker Chen +Signed-off-by: Changhuang Liang +Link: https://lore.kernel.org/r/20230927130734.9921-3-changhuang.liang@starfivetech.com +Signed-off-by: Ulf Hansson +--- + drivers/pmdomain/starfive/jh71xx-pmu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/pmdomain/starfive/jh71xx-pmu.c ++++ b/drivers/pmdomain/starfive/jh71xx-pmu.c +@@ -420,11 +420,11 @@ static const struct jh71xx_pmu_match_dat + }; + + static const struct jh71xx_domain_info jh7110_aon_power_domains[] = { +- [JH7110_PD_DPHY_TX] = { ++ [JH7110_AON_PD_DPHY_TX] = { + .name = "DPHY-TX", + .bit = 30, + }, +- [JH7110_PD_DPHY_RX] = { ++ [JH7110_AON_PD_DPHY_RX] = { + .name = "DPHY-RX", + .bit = 31, + }, diff --git a/target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch b/target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch new file mode 100644 index 0000000000..782e71a0eb --- /dev/null +++ b/target/linux/starfive/patches-6.6/0047-riscv-dts-starfive-pinfunc-Fix-the-pins-name-of-I2ST.patch @@ -0,0 +1,30 @@ +From e7e3d62b7a470ddf15e30574232b52b2e23ba606 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Mon, 21 Aug 2023 22:41:50 +0800 +Subject: [PATCH 047/116] riscv: dts: starfive: pinfunc: Fix the pins name of + I2STX1 + +These pins are actually I2STX1 clock input, not I2STX0, +so their names should be changed. + +Signed-off-by: Xingyu Wu +Reviewed-by: Walker Chen +Acked-by: Rob Herring +Signed-off-by: Conor Dooley +--- + arch/riscv/boot/dts/starfive/jh7110-pinfunc.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h ++++ b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h +@@ -240,8 +240,8 @@ + #define GPI_SYS_MCLK_EXT 30 + #define GPI_SYS_I2SRX_BCLK 31 + #define GPI_SYS_I2SRX_LRCK 32 +-#define GPI_SYS_I2STX0_BCLK 33 +-#define GPI_SYS_I2STX0_LRCK 34 ++#define GPI_SYS_I2STX1_BCLK 33 ++#define GPI_SYS_I2STX1_LRCK 34 + #define GPI_SYS_TDM_CLK 35 + #define GPI_SYS_TDM_RXD 36 + #define GPI_SYS_TDM_SYNC 37 diff --git a/target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch b/target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch new file mode 100644 index 0000000000..ad6626cd79 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0048-riscv-dts-starfive-Add-full-support-except-VIN-and-V.patch @@ -0,0 +1,549 @@ +From a3d3f611f31fa2dca3deefa7cd443abca02e03fa Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Tue, 11 Apr 2023 16:31:15 +0800 +Subject: [PATCH 048/116] riscv: dts: starfive: Add full support (except VIN + and VOUT) for JH7110 and VisionFive 2 board + +Merge all StarFive dts patches together except VIN and VOUT. + +Signed-off-by: Hal Feng +--- + .../jh7110-starfive-visionfive-2.dtsi | 199 +++++++++++++++ + arch/riscv/boot/dts/starfive/jh7110.dtsi | 233 ++++++++++++++++++ + 2 files changed, 432 insertions(+) + +--- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi ++++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +@@ -19,6 +19,8 @@ + i2c6 = &i2c6; + mmc0 = &mmc0; + mmc1 = &mmc1; ++ pcie0 = &pcie0; ++ pcie1 = &pcie1; + serial0 = &uart0; + }; + +@@ -40,6 +42,33 @@ + gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>; + priority = <224>; + }; ++ ++ pwmdac_codec: pwmdac-codec { ++ compatible = "linux,spdif-dit"; ++ #sound-dai-cells = <0>; ++ }; ++ ++ sound-pwmdac { ++ compatible = "simple-audio-card"; ++ simple-audio-card,name = "StarFive-PWMDAC-Sound-Card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "left_j"; ++ bitclock-master = <&sndcpu0>; ++ frame-master = <&sndcpu0>; ++ ++ sndcpu0: cpu { ++ sound-dai = <&pwmdac>; ++ }; ++ ++ codec { ++ sound-dai = <&pwmdac_codec>; ++ }; ++ }; ++ }; + }; + + &dvp_clk { +@@ -202,8 +231,28 @@ + status = "okay"; + }; + ++&i2srx { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2srx_pins>; ++ status = "okay"; ++}; ++ ++&i2stx0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mclk_ext_pins>; ++ status = "okay"; ++}; ++ ++&i2stx1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2stx1_pins>; ++ status = "okay"; ++}; ++ + &mmc0 { + max-frequency = <100000000>; ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; + bus-width = <8>; + cap-mmc-highspeed; + mmc-ddr-1_8v; +@@ -220,6 +269,8 @@ + + &mmc1 { + max-frequency = <100000000>; ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO1_SDCARD>; ++ assigned-clock-rates = <50000000>; + bus-width = <4>; + no-sdio; + no-mmc; +@@ -231,6 +282,34 @@ + status = "okay"; + }; + ++&pcie0 { ++ perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pcie0_pins>; ++ status = "okay"; ++}; ++ ++&pcie1 { ++ perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy1>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pcie1_pins>; ++ status = "okay"; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_pins>; ++ status = "okay"; ++}; ++ ++&pwmdac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwmdac_pins>; ++ status = "okay"; ++}; ++ + &qspi { + #address-cells = <1>; + #size-cells = <0>; +@@ -336,6 +415,46 @@ + }; + }; + ++ i2srx_pins: i2srx-0 { ++ clk-sd-pins { ++ pinmux = , ++ , ++ , ++ , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ i2stx1_pins: i2stx1-0 { ++ sd-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ }; ++ }; ++ ++ mclk_ext_pins: mclk-ext-0 { ++ mclk-ext-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ + mmc0_pins: mmc0-0 { + rst-pins { + pinmux = ; + }; + }; ++ ++ pcie0_pins: pcie0-0 { ++ clkreq-pins { ++ pinmux = ; ++ bias-pull-down; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ wake-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pcie1_pins: pcie1-0 { ++ clkreq-pins { ++ pinmux = ; ++ bias-pull-down; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ wake-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwm_pins: pwm-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwmdac_pins: pwmdac-0 { ++ pwmdac-pins { ++ pinmux = , ++ ; ++ bias-disable; ++ drive-strength = <2>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ }; + + spi0_pins: spi0-0 { + mosi-pins { +--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi ++++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi +@@ -244,6 +244,7 @@ + clock-output-names = "dvp_clk"; + #clock-cells = <0>; + }; ++ + gmac0_rgmii_rxin: gmac0-rgmii-rxin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac0_rgmii_rxin"; +@@ -512,6 +513,43 @@ + status = "disabled"; + }; + ++ pwmdac: pwmdac@100b0000 { ++ compatible = "starfive,jh7110-pwmdac"; ++ reg = <0x0 0x100b0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_PWMDAC_APB>, ++ <&syscrg JH7110_SYSCLK_PWMDAC_CORE>; ++ clock-names = "apb", "core"; ++ resets = <&syscrg JH7110_SYSRST_PWMDAC_APB>; ++ dmas = <&dma 22>; ++ dma-names = "tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2srx: i2s@100e0000 { ++ compatible = "starfive,jh7110-i2srx"; ++ reg = <0x0 0x100e0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>, ++ <&syscrg JH7110_SYSCLK_I2SRX_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>, ++ <&syscrg JH7110_SYSCLK_I2SRX_BCLK>, ++ <&syscrg JH7110_SYSCLK_I2SRX_LRCK>, ++ <&i2srx_bclk_ext>, ++ <&i2srx_lrck_ext>; ++ clock-names = "i2sclk", "apb", "mclk", ++ "mclk_inner", "mclk_ext", "bclk", ++ "lrck", "bclk_ext", "lrck_ext"; ++ resets = <&syscrg JH7110_SYSRST_I2SRX_APB>, ++ <&syscrg JH7110_SYSRST_I2SRX_BCLK>; ++ dmas = <0>, <&dma 24>; ++ dma-names = "tx", "rx"; ++ starfive,syscon = <&sys_syscon 0x18 0x2>; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ + usb0: usb@10100000 { + compatible = "starfive,jh7110-usb"; + ranges = <0x0 0x0 0x10100000 0x100000>; +@@ -736,6 +774,56 @@ + status = "disabled"; + }; + ++ i2stx0: i2s@120b0000 { ++ compatible = "starfive,jh7110-i2stx0"; ++ reg = <0x0 0x120b0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_I2STX0_BCLK_MST>, ++ <&syscrg JH7110_SYSCLK_I2STX0_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>; ++ clock-names = "i2sclk", "apb", "mclk", ++ "mclk_inner","mclk_ext"; ++ resets = <&syscrg JH7110_SYSRST_I2STX0_APB>, ++ <&syscrg JH7110_SYSRST_I2STX0_BCLK>; ++ dmas = <&dma 47>; ++ dma-names = "tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2stx1: i2s@120c0000 { ++ compatible = "starfive,jh7110-i2stx1"; ++ reg = <0x0 0x120c0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_I2STX1_BCLK_MST>, ++ <&syscrg JH7110_SYSCLK_I2STX1_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>, ++ <&syscrg JH7110_SYSCLK_I2STX1_BCLK>, ++ <&syscrg JH7110_SYSCLK_I2STX1_LRCK>, ++ <&i2stx_bclk_ext>, ++ <&i2stx_lrck_ext>; ++ clock-names = "i2sclk", "apb", "mclk", ++ "mclk_inner", "mclk_ext", "bclk", ++ "lrck", "bclk_ext", "lrck_ext"; ++ resets = <&syscrg JH7110_SYSRST_I2STX1_APB>, ++ <&syscrg JH7110_SYSRST_I2STX1_BCLK>; ++ dmas = <&dma 48>; ++ dma-names = "tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ pwm: pwm@120d0000 { ++ compatible = "starfive,jh7110-pwm", "opencores,pwm-v1"; ++ reg = <0x0 0x120d0000 0x0 0x10000>; ++ clocks = <&syscrg JH7110_SYSCLK_PWM_APB>; ++ resets = <&syscrg JH7110_SYSRST_PWM_APB>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + sfctemp: temperature-sensor@120e0000 { + compatible = "starfive,jh7110-temp"; + reg = <0x0 0x120e0000 0x0 0x10000>; +@@ -811,6 +899,26 @@ + #gpio-cells = <2>; + }; + ++ timer@13050000 { ++ compatible = "starfive,jh7110-timer"; ++ reg = <0x0 0x13050000 0x0 0x10000>; ++ interrupts = <69>, <70>, <71>, <72>; ++ clocks = <&syscrg JH7110_SYSCLK_TIMER_APB>, ++ <&syscrg JH7110_SYSCLK_TIMER0>, ++ <&syscrg JH7110_SYSCLK_TIMER1>, ++ <&syscrg JH7110_SYSCLK_TIMER2>, ++ <&syscrg JH7110_SYSCLK_TIMER3>; ++ clock-names = "apb", "ch0", "ch1", ++ "ch2", "ch3"; ++ resets = <&syscrg JH7110_SYSRST_TIMER_APB>, ++ <&syscrg JH7110_SYSRST_TIMER0>, ++ <&syscrg JH7110_SYSRST_TIMER1>, ++ <&syscrg JH7110_SYSRST_TIMER2>, ++ <&syscrg JH7110_SYSRST_TIMER3>; ++ reset-names = "apb", "ch0", "ch1", ++ "ch2", "ch3"; ++ }; ++ + watchdog@13070000 { + compatible = "starfive,jh7110-wdt"; + reg = <0x0 0x13070000 0x0 0x10000>; +@@ -1011,6 +1119,32 @@ + #power-domain-cells = <1>; + }; + ++ csi2rx: csi-bridge@19800000 { ++ compatible = "starfive,jh7110-csi2rx"; ++ reg = <0x0 0x19800000 0x0 0x10000>; ++ clocks = <&ispcrg JH7110_ISPCLK_VIN_SYS>, ++ <&ispcrg JH7110_ISPCLK_VIN_APB>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>; ++ clock-names = "sys_clk", "p_clk", ++ "pixel_if0_clk", "pixel_if1_clk", ++ "pixel_if2_clk", "pixel_if3_clk"; ++ resets = <&ispcrg JH7110_ISPRST_VIN_SYS>, ++ <&ispcrg JH7110_ISPRST_VIN_APB>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>; ++ reset-names = "sys", "reg_bank", ++ "pixel_if0", "pixel_if1", ++ "pixel_if2", "pixel_if3"; ++ phys = <&csi_phy>; ++ phy-names = "dphy"; ++ status = "disabled"; ++ }; ++ + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x0 0x19810000 0x0 0x10000>; +@@ -1028,6 +1162,19 @@ + power-domains = <&pwrc JH7110_PD_ISP>; + }; + ++ csi_phy: phy@19820000 { ++ compatible = "starfive,jh7110-dphy-rx"; ++ reg = <0x0 0x19820000 0x0 0x10000>; ++ clocks = <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>; ++ clock-names = "cfg", "ref", "tx"; ++ resets = <&ispcrg JH7110_ISPRST_M31DPHY_HW>, ++ <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>; ++ power-domains = <&aon_syscon JH7110_AON_PD_DPHY_RX>; ++ #phy-cells = <0>; ++ }; ++ + voutcrg: clock-controller@295c0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x0 0x295c0000 0x0 0x10000>; +@@ -1045,5 +1192,91 @@ + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_VOUT>; + }; ++ ++ pcie0: pcie@940000000 { ++ compatible = "starfive,jh7110-pcie"; ++ reg = <0x9 0x40000000 0x0 0x1000000>, ++ <0x0 0x2b000000 0x0 0x100000>; ++ reg-names = "cfg", "apb"; ++ linux,pci-domain = <0>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ #interrupt-cells = <1>; ++ ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, ++ <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; ++ interrupts = <56>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>, ++ <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>, ++ <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>, ++ <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>; ++ msi-controller; ++ device_type = "pci"; ++ starfive,stg-syscon = <&stg_syscon>; ++ bus-range = <0x0 0xff>; ++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, ++ <&stgcrg JH7110_STGCLK_PCIE0_TL>, ++ <&stgcrg JH7110_STGCLK_PCIE0_AXI_MST0>, ++ <&stgcrg JH7110_STGCLK_PCIE0_APB>; ++ clock-names = "noc", "tl", "axi_mst0", "apb"; ++ resets = <&stgcrg JH7110_STGRST_PCIE0_AXI_MST0>, ++ <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV0>, ++ <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV>, ++ <&stgcrg JH7110_STGRST_PCIE0_BRG>, ++ <&stgcrg JH7110_STGRST_PCIE0_CORE>, ++ <&stgcrg JH7110_STGRST_PCIE0_APB>; ++ reset-names = "mst0", "slv0", "slv", "brg", ++ "core", "apb"; ++ status = "disabled"; ++ ++ pcie_intc0: interrupt-controller { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ }; ++ }; ++ ++ pcie1: pcie@9c0000000 { ++ compatible = "starfive,jh7110-pcie"; ++ reg = <0x9 0xc0000000 0x0 0x1000000>, ++ <0x0 0x2c000000 0x0 0x100000>; ++ reg-names = "cfg", "apb"; ++ linux,pci-domain = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ #interrupt-cells = <1>; ++ ranges = <0x82000000 0x0 0x38000000 0x0 0x38000000 0x0 0x08000000>, ++ <0xc3000000 0x9 0x80000000 0x9 0x80000000 0x0 0x40000000>; ++ interrupts = <57>; ++ interrupt-map-mask = <0x0 0x0 0x0 0x7>; ++ interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc1 0x1>, ++ <0x0 0x0 0x0 0x2 &pcie_intc1 0x2>, ++ <0x0 0x0 0x0 0x3 &pcie_intc1 0x3>, ++ <0x0 0x0 0x0 0x4 &pcie_intc1 0x4>; ++ msi-controller; ++ device_type = "pci"; ++ starfive,stg-syscon = <&stg_syscon>; ++ bus-range = <0x0 0xff>; ++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, ++ <&stgcrg JH7110_STGCLK_PCIE1_TL>, ++ <&stgcrg JH7110_STGCLK_PCIE1_AXI_MST0>, ++ <&stgcrg JH7110_STGCLK_PCIE1_APB>; ++ clock-names = "noc", "tl", "axi_mst0", "apb"; ++ resets = <&stgcrg JH7110_STGRST_PCIE1_AXI_MST0>, ++ <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV0>, ++ <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV>, ++ <&stgcrg JH7110_STGRST_PCIE1_BRG>, ++ <&stgcrg JH7110_STGRST_PCIE1_CORE>, ++ <&stgcrg JH7110_STGRST_PCIE1_APB>; ++ reset-names = "mst0", "slv0", "slv", "brg", ++ "core", "apb"; ++ status = "disabled"; ++ ++ pcie_intc1: interrupt-controller { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ }; ++ }; + }; + }; diff --git a/target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch b/target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch new file mode 100644 index 0000000000..a7a832dce2 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0049-MAINTAINERS-Update-all-StarFive-entries.patch @@ -0,0 +1,147 @@ +From ae7b57a0c69953f5ec06a378aedeed4c86637998 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Tue, 11 Apr 2023 16:25:57 +0800 +Subject: [PATCH 049/116] MAINTAINERS: Update all StarFive entries + +Merge all StarFive maintainers changes together. + +Signed-off-by: Hal Feng +--- + MAINTAINERS | 61 +++++++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 57 insertions(+), 4 deletions(-) + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -7053,6 +7053,14 @@ T: git git://anongit.freedesktop.org/drm + F: Documentation/devicetree/bindings/display/rockchip/ + F: drivers/gpu/drm/rockchip/ + ++DRM DRIVERS FOR STARFIVE ++M: Keith Zhao ++L: dri-devel@lists.freedesktop.org ++S: Maintained ++T: git git://anongit.freedesktop.org/drm/drm-misc ++F: Documentation/devicetree/bindings/display/starfive/ ++F: drivers/gpu/drm/verisilicon/ ++ + DRM DRIVERS FOR STI + M: Alain Volmat + L: dri-devel@lists.freedesktop.org +@@ -16016,6 +16024,13 @@ F: Documentation/i2c/busses/i2c-ocores.r + F: drivers/i2c/busses/i2c-ocores.c + F: include/linux/platform_data/i2c-ocores.h + ++OPENCORES PWM DRIVER ++M: William Qiu ++M: Hal Feng ++S: Supported ++F: Documentation/devicetree/bindings/pwm/opencores,pwm.yaml ++F: drivers/pwm/pwm-ocores.c ++ + OPENRISC ARCHITECTURE + M: Jonas Bonn + M: Stefan Kristiansson +@@ -16427,6 +16442,14 @@ S: Maintained + F: Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt + F: drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c + ++PCI DRIVER FOR PLDA PCIE IP ++M: Daire McNamara ++M: Kevin Xie ++L: linux-pci@vger.kernel.org ++S: Maintained ++F: Documentation/devicetree/bindings/pci/plda,* ++F: drivers/pci/controller/plda/*plda* ++ + PCI DRIVER FOR RENESAS R-CAR + M: Marek Vasut + M: Yoshihiro Shimoda +@@ -16658,7 +16681,7 @@ M: Daire McNamara +@@ -16682,6 +16705,13 @@ S: Maintained + F: Documentation/devicetree/bindings/pci/socionext,uniphier-pcie* + F: drivers/pci/controller/dwc/pcie-uniphier* + ++PCIE DRIVER FOR STARFIVE JH71x0 ++M: Kevin Xie ++L: linux-pci@vger.kernel.org ++S: Maintained ++F: Documentation/devicetree/bindings/pci/starfive* ++F: drivers/pci/controller/plda/pcie-starfive.c ++ + PCIE DRIVER FOR ST SPEAR13XX + M: Pratyush Anand + L: linux-pci@vger.kernel.org +@@ -18454,7 +18484,7 @@ F: drivers/char/hw_random/mpfs-rng.c + F: drivers/clk/microchip/clk-mpfs*.c + F: drivers/i2c/busses/i2c-microchip-corei2c.c + F: drivers/mailbox/mailbox-mpfs.c +-F: drivers/pci/controller/pcie-microchip-host.c ++F: drivers/pci/controller/plda/pcie-microchip-host.c + F: drivers/pwm/pwm-microchip-core.c + F: drivers/reset/reset-mpfs.c + F: drivers/rtc/rtc-mpfs.c +@@ -20435,6 +20465,15 @@ M: Ion Badulescu + S: Odd Fixes + F: drivers/net/ethernet/adaptec/starfire* + ++STARFIVE CAMERA SUBSYSTEM DRIVER ++M: Jack Zhu ++M: Changhuang Liang ++L: linux-media@vger.kernel.org ++S: Maintained ++F: Documentation/admin-guide/media/starfive_camss.rst ++F: Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml ++F: drivers/staging/media/starfive/camss ++ + STARFIVE CRYPTO DRIVER + M: Jia Jie Ho + M: William Qiu +@@ -20473,6 +20512,13 @@ S: Supported + F: Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml + F: drivers/clk/starfive/clk-starfive-jh7110-pll.c + ++STARFIVE JH7110 PWMDAC DRIVER ++M: Hal Feng ++M: Xingyu Wu ++S: Supported ++F: Documentation/devicetree/bindings/sound/starfive,jh7110-pwmdac.yaml ++F: sound/soc/starfive/jh7110_pwmdac.c ++ + STARFIVE JH7110 SYSCON + M: William Qiu + M: Xingyu Wu +@@ -20520,9 +20566,10 @@ F: drivers/usb/cdns3/cdns3-starfive.c + + STARFIVE JH71XX PMU CONTROLLER DRIVER + M: Walker Chen ++M: Changhuang Liang + S: Supported + F: Documentation/devicetree/bindings/power/starfive* +-F: drivers/pmdomain/starfive/jh71xx-pmu.c ++F: drivers/pmdomain/starfive/ + F: include/dt-bindings/power/starfive,jh7110-pmu.h + + STARFIVE SOC DRIVERS +@@ -20530,7 +20577,13 @@ M: Conor Dooley + S: Maintained + T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ + F: Documentation/devicetree/bindings/soc/starfive/ +-F: drivers/soc/starfive/ ++ ++STARFIVE JH7110 TIMER DRIVER ++M: Samin Guo ++M: Xingyu Wu ++S: Supported ++F: Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml ++F: drivers/clocksource/timer-jh7110.c + + STARFIVE TRNG DRIVER + M: Jia Jie Ho diff --git a/target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch b/target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch new file mode 100644 index 0000000000..a38a885894 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0050-mmc-starfive-Change-the-voltage-to-adapt-to-JH7110-E.patch @@ -0,0 +1,64 @@ +From e394195396995456ef98f52ac123c0cb64687748 Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Mon, 9 Oct 2023 10:59:03 +0800 +Subject: [PATCH 050/116] mmc: starfive: Change the voltage to adapt to JH7110 + EVB + +Change the voltage, so the driver can adapt to JH7110 EVB. + +Signed-off-by: William Qiu +Signed-off-by: Hal Feng +--- + drivers/mmc/host/dw_mmc-starfive.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +--- a/drivers/mmc/host/dw_mmc-starfive.c ++++ b/drivers/mmc/host/dw_mmc-starfive.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -95,10 +96,39 @@ out: + return ret; + } + ++static int dw_mci_starfive_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct dw_mci *host = slot->host; ++ u32 ret; ++ ++ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) ++ ret = gpio_direction_output(25, 0); ++ else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) ++ ret = gpio_direction_output(25, 1); ++ if (ret) ++ return ret; ++ ++ if (!IS_ERR(mmc->supply.vqmmc)) { ++ ret = mmc_regulator_set_vqmmc(mmc, ios); ++ if (ret < 0) { ++ dev_err(host->dev, "Regulator set error %d\n", ret); ++ return ret; ++ } ++ } ++ ++ /* We should delay 20ms wait for timing setting finished. */ ++ mdelay(20); ++ ++ return 0; ++} ++ + static const struct dw_mci_drv_data starfive_data = { + .common_caps = MMC_CAP_CMD23, + .set_ios = dw_mci_starfive_set_ios, + .execute_tuning = dw_mci_starfive_execute_tuning, ++ .switch_voltage = dw_mci_starfive_switch_voltage, + }; + + static const struct of_device_id dw_mci_starfive_match[] = { diff --git a/target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch b/target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch new file mode 100644 index 0000000000..485ab0d9e1 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0051-spi-spl022-Get-and-deassert-reset-in-probe.patch @@ -0,0 +1,60 @@ +From 2cd3e51cb76d49d8db6274ebdc1ba1eb5c872f10 Mon Sep 17 00:00:00 2001 +From: "ziv.xu" +Date: Sun, 4 Feb 2024 10:35:24 +0800 +Subject: [PATCH 051/116] spi: spl022: Get and deassert reset in probe() + +This fix spi1~6 communication time out. + +Signed-off-by: ziv.xu +Signed-off-by: Hal Feng +--- + drivers/spi/spi-pl022.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/drivers/spi/spi-pl022.c ++++ b/drivers/spi/spi-pl022.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + /* + * This macro is used to define some register default values. +@@ -370,6 +371,7 @@ struct pl022 { + resource_size_t phybase; + void __iomem *virtbase; + struct clk *clk; ++ struct reset_control *rst; + struct spi_controller *host; + struct pl022_ssp_controller *host_info; + /* Message per-transfer pump */ +@@ -2181,6 +2183,19 @@ static int pl022_probe(struct amba_devic + goto err_no_clk_en; + } + ++ pl022->rst = devm_reset_control_get(&adev->dev, NULL); ++ if (IS_ERR(pl022->rst)) { ++ status = PTR_ERR(pl022->rst); ++ dev_err(&adev->dev, "could not retrieve SSP/SPI bus reset\n"); ++ goto err_no_rst; ++ } ++ ++ status = reset_control_deassert(pl022->rst); ++ if (status) { ++ dev_err(&adev->dev, "could not deassert SSP/SPI bus reset\n"); ++ goto err_no_rst_de; ++ } ++ + /* Initialize transfer pump */ + tasklet_init(&pl022->pump_transfers, pump_transfers, + (unsigned long)pl022); +@@ -2240,6 +2255,8 @@ static int pl022_probe(struct amba_devic + if (platform_info->enable_dma) + pl022_dma_remove(pl022); + err_no_irq: ++ err_no_rst_de: ++ err_no_rst: + clk_disable_unprepare(pl022->clk); + err_no_clk_en: + err_no_clk: diff --git a/target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch b/target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch new file mode 100644 index 0000000000..e274e84a25 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0052-ASoC-dwc-i2s-Fix-getting-platform-data-error-for-Sta.patch @@ -0,0 +1,30 @@ +From 9cc8de0cdc1600f460f618e342e1f524adad07c4 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Wed, 21 Feb 2024 10:23:48 +0800 +Subject: [PATCH 052/116] ASoC: dwc: i2s: Fix getting platform data error for + StarFive JH7110 + +JH7110 need to use a DT specific function to get the platform data, +otherwise, it fails in probe(). + +Fixes: 9c97790a07dc ("ASoC: dwc: Fix non-DT instantiation") +Signed-off-by: Xingyu Wu +Signed-off-by: Hal Feng +--- + sound/soc/dwc/dwc-i2s.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/sound/soc/dwc/dwc-i2s.c ++++ b/sound/soc/dwc/dwc-i2s.c +@@ -917,7 +917,11 @@ static int jh7110_i2stx0_clk_cfg(struct + + static int dw_i2s_probe(struct platform_device *pdev) + { ++#ifdef CONFIG_OF ++ const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev); ++#else + const struct i2s_platform_data *pdata = pdev->dev.platform_data; ++#endif + struct dw_i2s_dev *dev; + struct resource *res; + int ret, irq; diff --git a/target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch b/target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch new file mode 100644 index 0000000000..8203beca52 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0053-ASoC-dwc-i2s-Add-RX-master-support-for-StarFive-JH71.patch @@ -0,0 +1,67 @@ +From 1be9bd37fdb5f50162dba0158e1fee295ebca9aa Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Tue, 17 Oct 2023 17:22:52 +0800 +Subject: [PATCH 053/116] ASoC: dwc: i2s: Add RX master support for StarFive + JH7110 SoC + +Add JH7110 I2S RX master support, so the PDM can work on JH7110 +EVB board. + +Signed-off-by: Xingyu Wu +Signed-off-by: Hal Feng +--- + sound/soc/dwc/dwc-i2s.c | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) + +--- a/sound/soc/dwc/dwc-i2s.c ++++ b/sound/soc/dwc/dwc-i2s.c +@@ -906,6 +906,27 @@ static int jh7110_i2srx_crg_init(struct + return jh7110_i2s_crg_slave_init(dev); + } + ++/* Special syscon initialization about RX channel with master mode on JH7110 SoC */ ++static int jh7110_i2srx_mst_crg_init(struct dw_i2s_dev *dev) ++{ ++ struct regmap *regmap; ++ unsigned int args[5]; ++ ++ regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node, ++ "starfive,syscon", ++ 5, args); ++ if (IS_ERR(regmap)) ++ return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n"); ++ ++ /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */ ++ regmap_update_bits(regmap, args[0], args[1], args[1]); ++ ++ /* Change I2Srx source (PDM) with syscon register, args[0]: offset, args[1]: mask */ ++ regmap_update_bits(regmap, args[2], args[3], args[4]); ++ ++ return jh7110_i2s_crg_master_init(dev); ++} ++ + static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config) + { + struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config); +@@ -1086,11 +1107,21 @@ static const struct i2s_platform_data jh + .i2s_pd_init = jh7110_i2srx_crg_init, + }; + ++static const struct i2s_platform_data jh7110_i2srx_mst_data = { ++ .cap = DWC_I2S_RECORD | DW_I2S_MASTER, ++ .channel = TWO_CHANNEL_SUPPORT, ++ .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, ++ .snd_rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000, ++ .i2s_clk_cfg = jh7110_i2stx0_clk_cfg, ++ .i2s_pd_init = jh7110_i2srx_mst_crg_init, ++}; ++ + static const struct of_device_id dw_i2s_of_match[] = { + { .compatible = "snps,designware-i2s", }, + { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, }, + { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,}, + { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,}, ++ { .compatible = "starfive,jh7110-i2srx-master", .data = &jh7110_i2srx_mst_data,}, + {}, + }; + diff --git a/target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch b/target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch new file mode 100644 index 0000000000..51976c656e --- /dev/null +++ b/target/linux/starfive/patches-6.6/0054-pinctrl-starfive-jh7110-Unset-.strict-in-pinmux_ops.patch @@ -0,0 +1,24 @@ +From 1ec26ba377d8ae59cd09811ec78623a750a9c150 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Mon, 26 Feb 2024 11:35:44 +0800 +Subject: [PATCH 054/116] pinctrl: starfive: jh7110: Unset .strict in + pinmux_ops + +Allow simultaneous use of the same pin for GPIO and another function. +This feature is used in HDMI hot plug detect. + +Signed-off-by: Hal Feng +--- + drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c ++++ b/drivers/pinctrl/starfive/pinctrl-starfive-jh7110.c +@@ -327,7 +327,6 @@ static const struct pinmux_ops jh7110_pi + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .set_mux = jh7110_set_mux, +- .strict = true, + }; + + static const u8 jh7110_drive_strength_mA[4] = { 2, 4, 8, 12 }; diff --git a/target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch b/target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch new file mode 100644 index 0000000000..39699e8b97 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0055-mm-pgtable-generic.c-Export-symbol-__pte_offset_map.patch @@ -0,0 +1,22 @@ +From 005549a2bd839335b0e3dc4152f00f642b524f07 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Sat, 7 Oct 2023 18:10:20 +0800 +Subject: [PATCH 055/116] mm/pgtable-generic.c: Export symbol __pte_offset_map + +So JH7110 vdec can call pte_offset_map() when it is built as a module. + +Signed-off-by: Hal Feng +--- + mm/pgtable-generic.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/mm/pgtable-generic.c ++++ b/mm/pgtable-generic.c +@@ -304,6 +304,7 @@ nomap: + rcu_read_unlock(); + return NULL; + } ++EXPORT_SYMBOL(__pte_offset_map); + + pte_t *pte_offset_map_nolock(struct mm_struct *mm, pmd_t *pmd, + unsigned long addr, spinlock_t **ptlp) diff --git a/target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch b/target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch new file mode 100644 index 0000000000..7b803d3997 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0056-riscv-dts-starfive-Add-JH7110-EVB-device-tree.patch @@ -0,0 +1,2623 @@ +From 9f46b0d43f8945ff3a8b81ddc6567df370b60911 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Fri, 28 Jul 2023 17:19:12 +0800 +Subject: [PATCH 056/116] riscv: dts: starfive: Add JH7110 EVB device tree + +Add JH7110 evaluation board device tree. +The code is ported from tag JH7110_SDK_6.1_v5.11.3 + +Signed-off-by: Hal Feng +--- + arch/riscv/boot/dts/starfive/Makefile | 3 + + arch/riscv/boot/dts/starfive/jh7110-clk.dtsi | 80 ++ + .../boot/dts/starfive/jh7110-evb-pinctrl.dtsi | 997 ++++++++++++++++++ + arch/riscv/boot/dts/starfive/jh7110-evb.dts | 35 + + arch/riscv/boot/dts/starfive/jh7110-evb.dtsi | 854 +++++++++++++++ + arch/riscv/boot/dts/starfive/jh7110.dtsi | 482 ++++++++- + 6 files changed, 2444 insertions(+), 7 deletions(-) + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-clk.dtsi + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-pinctrl.dtsi + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dtsi + +--- a/arch/riscv/boot/dts/starfive/Makefile ++++ b/arch/riscv/boot/dts/starfive/Makefile +@@ -4,9 +4,12 @@ DTC_FLAGS_jh7100-beaglev-starlight := -@ + DTC_FLAGS_jh7100-starfive-visionfive-v1 := -@ + DTC_FLAGS_jh7110-starfive-visionfive-2-v1.2a := -@ + DTC_FLAGS_jh7110-starfive-visionfive-2-v1.3b := -@ ++DTC_FLAGS_jh7110-evb := -@ + + dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb + dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb + + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb ++ ++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-clk.dtsi +@@ -0,0 +1,80 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ */ ++ ++/ { ++ ac108_mclk: ac108_mclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ }; ++ ++ clk_ext_camera: clk-ext-camera { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ }; ++ ++ wm8960_mclk: wm8960_mclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24576000>; ++ }; ++}; ++ ++&dvp_clk { ++ clock-frequency = <74250000>; ++}; ++ ++&gmac0_rgmii_rxin { ++ clock-frequency = <125000000>; ++}; ++ ++&gmac0_rmii_refin { ++ clock-frequency = <50000000>; ++}; ++ ++&gmac1_rgmii_rxin { ++ clock-frequency = <125000000>; ++}; ++ ++&gmac1_rmii_refin { ++ clock-frequency = <50000000>; ++}; ++ ++&hdmitx0_pixelclk { ++ clock-frequency = <297000000>; ++}; ++ ++&i2srx_bclk_ext { ++ clock-frequency = <12288000>; ++}; ++ ++&i2srx_lrck_ext { ++ clock-frequency = <192000>; ++}; ++ ++&i2stx_bclk_ext { ++ clock-frequency = <12288000>; ++}; ++ ++&i2stx_lrck_ext { ++ clock-frequency = <192000>; ++}; ++ ++&mclk_ext { ++ clock-frequency = <12288000>; ++}; ++ ++&osc { ++ clock-frequency = <24000000>; ++}; ++ ++&rtc_osc { ++ clock-frequency = <32768>; ++}; ++ ++&tdm_ext { ++ clock-frequency = <49152000>; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-pinctrl.dtsi +@@ -0,0 +1,997 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ * Author: Hal Feng ++ */ ++ ++#include "jh7110-pinfunc.h" ++ ++&sysgpio { ++ can0_pins: can0-0 { ++ can-pins { ++ pinmux = , ++ , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ can1_pins: can1-0 { ++ can-pins { ++ pinmux = , ++ , ++ ; ++ drive-strength = <12>; ++ input-enable; ++ }; ++ }; ++ ++ dvp_pins: dvp-0 { ++ dvp-pins{ ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ emmc0_pins: emmc0-0 { ++ emmc-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ drive-strength = <12>; ++ input-enable; ++ slew-rate = <1>; ++ }; ++ }; ++ ++ emmc1_pins: emmc1-0 { ++ emmc-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ gmac0_pins: gmac0-0 { ++ reset-pins { ++ pinmux = ; ++ bias-pull-up; ++ }; ++ }; ++ ++ gmac1_pins: gmac1-0 { ++ mdc-pins { ++ pinmux = ; ++ }; ++ }; ++ ++ hdmi_pins: hdmi-0 { ++ scl-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ }; ++ ++ sda-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ }; ++ ++ cec-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ }; ++ ++ hpd-pins { ++ pinmux = ; ++ bias-disable; /* external pull-up */ ++ input-enable; ++ }; ++ }; ++ ++ i2c0_pins: i2c0-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c1_pins: i2c1-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c2_pins: i2c2-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c3_pins: i2c3-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c4_pins: i2c4-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c5_pins: i2c5-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2c6_pins: i2c6-0 { ++ i2c-pins { ++ pinmux = , ++ ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ }; ++ ++ i2s_clk_pins: i2s-clk-0 { ++ bclk-lrck-pins { ++ pinmux = , ++ , ++ , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ i2srx_clk_pins: i2srx-clk-0 { ++ mclk-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ i2srx_pins: i2srx-0 { ++ i2srx-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ i2stx_pins: i2stx-0 { ++ i2stx-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ mclk_ext_pins: mclk-ext-0 { ++ mclk-ext-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ pdm_pins: pdm-0 { ++ pdm-pins { ++ pinmux = , ++ ; ++ input-enable; ++ }; ++ }; ++ ++ pwm_ch0to3_pins: pwm-ch0to3-0 { ++ pwm-pins { ++ pinmux = , ++ , ++ , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ ++ pwmdac_pins: pwmdac-0 { ++ pwmdac-pins { ++ pinmux = , ++ ; ++ }; ++ }; ++ ++ rgb_pad_pins: rgb-pad-pins { ++ rgb-0-pins { ++ pinmux = ; ++ drive-strength = <12>; ++ input-disable; ++ slew-rate = <1>; ++ }; ++ ++ rgb-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ drive-strength = <12>; ++ input-disable; ++ }; ++ }; ++ ++ sdcard0_pins: sdcard0-0 { ++ sdcard-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ drive-strength = <12>; ++ input-enable; ++ slew-rate = <1>; ++ }; ++ }; ++ ++ sdcard1_pins: sdcard1-0 { ++ sdcard-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ spdif_pins: spdif-0 { ++ spdif-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ ++ spi0_pins: spi0-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi1_pins: spi1-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi2_pins: spi2-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi3_pins: spi3-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi4_pins: spi4-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi5_pins: spi5-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ spi6_pins: spi6-0 { ++ mosi-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ miso-pins { ++ pinmux = ; ++ bias-pull-up; ++ input-enable; ++ input-schmitt-enable; ++ }; ++ ++ sck-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ ++ ss-pins { ++ pinmux = ; ++ bias-disable; ++ input-disable; ++ input-schmitt-disable; ++ }; ++ }; ++ ++ tdm_pins: tdm-0 { ++ tx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ sync-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ pcmclk-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ uart0_pins: uart0-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ uart1_pins: uart1-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ ++ cts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ rts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ uart2_pins: uart2-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ ++ cts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ rts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ uart3_pins: uart3-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ uart4_pins: uart4-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ ++ cts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ rts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ uart5_pins: uart5-0 { ++ tx-pins { ++ pinmux = ; ++ bias-disable; ++ drive-strength = <12>; ++ input-disable; ++ input-schmitt-disable; ++ slew-rate = <0>; ++ }; ++ ++ rx-pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-strength = <2>; ++ input-enable; ++ input-schmitt-enable; ++ slew-rate = <0>; ++ }; ++ ++ cts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ ++ rts-pins { ++ pinmux = ; ++ input-enable; ++ }; ++ }; ++ ++ usb_pins: usb-0 { ++ usb-pins { ++ pinmux = , ++ ; ++ input-enable; ++ }; ++ }; ++}; ++ ++&aongpio { ++ pwm_ch4to5_pins: pwm-ch4to5-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ ++ pwm_ch6to7_pins: pwm-ch6to7-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dts +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ no-sdio; ++ no-mmc; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb.dtsi +@@ -0,0 +1,854 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2023 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110.dtsi" ++#include "jh7110-clk.dtsi" ++#include "jh7110-evb-pinctrl.dtsi" ++#include ++ ++/ { ++ aliases { ++ ethernet0 = &gmac0; ++ ethernet1 = &gmac1; ++ i2c0 = &i2c0; ++ i2c1 = &i2c1; ++ i2c2 = &i2c2; ++ i2c3 = &i2c3; ++ i2c4 = &i2c4; ++ i2c5 = &i2c5; ++ i2c6 = &i2c6; ++ pcie0 = &pcie0; ++ pcie1 = &pcie1; ++ serial0 = &uart0; ++ serial3 = &uart3; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ cpus { ++ timebase-frequency = <4000000>; ++ }; ++ ++ memory@40000000 { ++ device_type = "memory"; ++ reg = <0x0 0x40000000 0x1 0x0>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ linux,cma { ++ compatible = "shared-dma-pool"; ++ reusable; ++ size = <0x0 0x20000000>; ++ alignment = <0x0 0x1000>; ++ alloc-ranges = <0x0 0x70000000 0x0 0x20000000>; ++ linux,cma-default; ++ }; ++ ++ e24_mem: e24@c0000000 { ++ reg = <0x0 0x6ce00000 0x0 0x1600000>; ++ }; ++ ++ xrp_reserved: xrpbuffer@f0000000 { ++ reg = <0x0 0x69c00000 0x0 0x01ffffff ++ 0x0 0x6bc00000 0x0 0x00001000 ++ 0x0 0x6bc01000 0x0 0x00fff000 ++ 0x0 0x6cc00000 0x0 0x00001000>; ++ }; ++ }; ++ ++ /* i2s + hdmi */ ++ sound1: snd-card1 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-HDMI-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "i2s"; ++ bitclock-master = <&sndi2s0>; ++ frame-master = <&sndi2s0>; ++ mclk-fs = <256>; ++ status = "okay"; ++ ++ sndi2s0: cpu { ++ sound-dai = <&i2stx0>; ++ }; ++ ++ codec { ++ sound-dai = <&hdmi>; ++ }; ++ }; ++ }; ++}; ++ ++&U74_1 { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++}; ++ ++&U74_2 { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++}; ++ ++&U74_3 { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++}; ++ ++&U74_4 { ++ /delete-property/ clocks; ++ /delete-property/ clock-names; ++}; ++ ++&can0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&can0_pins>; ++ status = "disabled"; ++}; ++ ++&can1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&can1_pins>; ++ status = "disabled"; ++}; ++ ++&co_process { ++ memory-region = <&e24_mem>; ++ status = "okay"; ++}; ++ ++&dc8200 { ++ status = "okay"; ++ ++ dc_out: port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ dc_out_dpi0: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&hdmi_input0>; ++ }; ++ dc_out_dpi1: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&hdmi_in_lcdc>; ++ }; ++ dc_out_dpi2: endpoint@2 { ++ reg = <2>; ++ remote-endpoint = <&mipi_in>; ++ }; ++ }; ++}; ++ ++&display { ++ ports = <&dc_out_dpi0>; ++ status = "okay"; ++}; ++ ++&dsi_output { ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ mipi_in: endpoint { ++ remote-endpoint = <&dc_out_dpi2>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ mipi_out: endpoint { ++ remote-endpoint = <&dsi_in_port>; ++ }; ++ }; ++ }; ++}; ++ ++&gmac0 { ++ phy-handle = <&phy0>; ++ phy-mode = "rgmii-id"; ++ status = "okay"; ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "snps,dwmac-mdio"; ++ ++ phy0: ethernet-phy@0 { ++ reg = <0>; ++ rx-internal-delay-ps = <1900>; ++ tx-internal-delay-ps = <1650>; ++ }; ++ }; ++}; ++ ++&gmac1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <0>; ++ rxc-skew-ps = <1060>; ++ txc-skew-ps = <1800>; ++ }; ++}; ++ ++&gpu { ++ status = "okay"; ++}; ++ ++&hdmi { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hdmi_pins>; ++ hpd-gpio = <&sysgpio 15 GPIO_ACTIVE_HIGH>; ++ ++ hdmi_in: port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ hdmi_in_lcdc: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&dc_out_dpi1>; ++ }; ++ }; ++}; ++ ++&i2c0 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins>; ++ status = "disabled"; ++ ++ wm8960: codec@1a { ++ compatible = "wlf,wm8960"; ++ reg = <0x1a>; ++ wlf,shared-lrclk; ++ #sound-dai-cells = <0>; ++ }; ++ ++ ac108: ac108@3b { ++ compatible = "x-power,ac108_0"; ++ reg = <0x3b>; ++ #sound-dai-cells = <0>; ++ data-protocol = <0>; ++ }; ++}; ++ ++&i2c1 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ status = "disabled"; ++}; ++ ++&i2c2 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c2_pins>; ++ status = "okay"; ++ ++ tinker_ft5406: tinker_ft5406@38 { ++ compatible = "tinker_ft5406"; ++ reg = <0x38>; ++ }; ++ ++ seeed_plane_i2c@45 { ++ compatible = "seeed_panel"; ++ reg = <0x45>; ++ ++ port { ++ panel_dsi_port: endpoint { ++ remote-endpoint = <&dsi_out_port>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c3 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c3_pins>; ++ status = "disabled"; ++}; ++ ++&i2c4 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c4_pins>; ++ status = "okay"; ++ ++ sc2235: sc2235@30 { ++ compatible = "smartsens,sc2235"; ++ reg = <0x30>; ++ clocks = <&clk_ext_camera>; ++ clock-names = "xclk"; ++ ++ port { ++ /* Parallel bus endpoint */ ++ sc2235_to_parallel: endpoint { ++ remote-endpoint = <¶llel_from_sc2235>; ++ bus-type = <5>; /* Parallel */ ++ bus-width = <8>; ++ data-shift = <2>; /* lines 13:6 are used */ ++ hsync-active = <1>; ++ vsync-active = <1>; ++ pclk-sample = <1>; ++ }; ++ }; ++ }; ++ ++ tda998x@70 { ++ compatible = "nxp,tda998x"; ++ reg = <0x70>; ++ ++ port { ++ tda998x_0_input: endpoint { ++ remote-endpoint = <&hdmi_out>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c5 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c5_pins>; ++ status = "okay"; ++ ++ pmic: jh7110_evb_reg@50 { ++ compatible = "starfive,jh7110-evb-regulator"; ++ reg = <0x50>; ++ ++ regulators { ++ hdmi_1p8: LDO_REG1 { ++ regulator-name = "hdmi_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ mipitx_1p8: LDO_REG2 { ++ regulator-name = "mipitx_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ mipirx_1p8: LDO_REG3 { ++ regulator-name = "mipirx_1p8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ hdmi_0p9: LDO_REG4 { ++ regulator-name = "hdmi_0p9"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ }; ++ mipitx_0p9: LDO_REG5 { ++ regulator-name = "mipitx_0p9"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ }; ++ mipirx_0p9: LDO_REG6 { ++ regulator-name = "mipirx_0p9"; ++ regulator-min-microvolt = <900000>; ++ regulator-max-microvolt = <900000>; ++ }; ++ sdio_vdd: LDO_REG7 { ++ regulator-name = "sdio_vdd"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c6 { ++ clock-frequency = <100000>; ++ i2c-sda-hold-time-ns = <300>; ++ i2c-sda-falling-time-ns = <510>; ++ i2c-scl-falling-time-ns = <510>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c6_pins>; ++ status = "okay"; ++ ++ ov4689: ov4689@36 { ++ compatible = "ovti,ov4689"; ++ reg = <0x36>; ++ clocks = <&clk_ext_camera>; ++ clock-names = "xclk"; ++ //reset-gpio = <&sysgpio 18 0>; ++ rotation = <180>; ++ ++ port { ++ /* Parallel bus endpoint */ ++ ov4689_to_csi2rx0: endpoint { ++ remote-endpoint = <&csi2rx0_from_ov4689>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ }; ++ ++ imx219: imx219@10 { ++ compatible = "sony,imx219"; ++ reg = <0x10>; ++ clocks = <&clk_ext_camera>; ++ clock-names = "xclk"; ++ reset-gpio = <&sysgpio 10 0>; ++ //DOVDD-supply = <&v2v8>; ++ rotation = <0>; ++ orientation = <1>; //CAMERA_ORIENTATION_BACK ++ ++ port { ++ /* CSI2 bus endpoint */ ++ imx219_to_csi2rx0: endpoint { ++ remote-endpoint = <&csi2rx0_from_imx219>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <2 1>; ++ lane-polarities = <1 1 1>; ++ link-frequencies = /bits/ 64 <456000000>; ++ }; ++ }; ++ }; ++ ++ imx708: imx708@1a { ++ compatible = "sony,imx708"; ++ reg = <0x1a>; ++ clocks = <&clk_ext_camera>; ++ reset-gpio = <&sysgpio 10 0>; ++ ++ port { ++ imx708_to_csi2rx0: endpoint { ++ remote-endpoint = <&csi2rx0_from_imx708>; ++ data-lanes = <1 2>; ++ clock-noncontinuous; ++ link-frequencies = /bits/ 64 <450000000>; ++ }; ++ }; ++ }; ++}; ++ ++&i2srx { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_clk_pins &i2srx_pins>; ++}; ++ ++&i2srx_mst { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2srx_clk_pins>; ++}; ++ ++&i2stx0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mclk_ext_pins>; ++ status = "okay"; ++}; ++ ++&i2stx1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2stx_pins>; ++}; ++ ++&jpu { ++ status = "okay"; ++}; ++ ++&mailbox_contrl0 { ++ status = "okay"; ++}; ++ ++&mailbox_client0 { ++ status = "okay"; ++}; ++ ++&mipi_dphy { ++ status = "okay"; ++}; ++ ++&mipi_dsi { ++ status = "okay"; ++ ++ port { ++ dsi_out_port: endpoint@0 { ++ remote-endpoint = <&panel_dsi_port>; ++ }; ++ dsi_in_port: endpoint@1 { ++ remote-endpoint = <&mipi_out>; ++ }; ++ }; ++ ++ mipi_panel: panel@0 { ++ /*compatible = "";*/ ++ status = "okay"; ++ }; ++}; ++ ++&pcie0 { ++ enable-gpios = <&sysgpio 32 GPIO_ACTIVE_HIGH>; ++ perst-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy0>; ++ status = "disabled"; ++}; ++ ++&pcie1 { ++ enable-gpios = <&sysgpio 21 GPIO_ACTIVE_HIGH>; ++ perst-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>; ++ phys = <&pciephy1>; ++ status = "disabled"; ++}; ++ ++&pciephy0 { ++ starfive,sys-syscon = <&sys_syscon 0x18>; ++ starfive,stg-syscon = <&stg_syscon 0x148 0x1f4>; ++}; ++ ++&pdm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pdm_pins>; ++ status = "disabled"; ++}; ++ ++&pwmdac { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwmdac_pins>; ++}; ++ ++&qspi { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ nor_flash: flash@0 { ++ compatible = "jedec,spi-nor"; ++ reg=<0>; ++ cdns,read-delay = <5>; ++ spi-max-frequency = <4687500>; ++ cdns,tshsl-ns = <1>; ++ cdns,tsd2d-ns = <1>; ++ cdns,tchsh-ns = <1>; ++ cdns,tslch-ns = <1>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ spl@0 { ++ reg = <0x0 0x40000>; ++ }; ++ uboot@100000 { ++ reg = <0x100000 0x300000>; ++ }; ++ data@f00000 { ++ reg = <0xf00000 0x100000>; ++ }; ++ }; ++ }; ++}; ++ ++&rgb_output { ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0>; ++ ++ hdmi_input0:endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&dc_out_dpi0>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ ++ hdmi_out:endpoint { ++ remote-endpoint = <&tda998x_0_input>; ++ }; ++ }; ++ }; ++}; ++ ++&spdif { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spdif_pins>; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins>; ++ status = "disabled"; ++ ++ spi_dev0: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi1_pins>; ++ status = "disabled"; ++ ++ spi_dev1: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi2_pins>; ++ status = "disabled"; ++ ++ spi_dev2: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi3_pins>; ++ status = "disabled"; ++ ++ spi_dev3: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi4_pins>; ++ status = "disabled"; ++ ++ spi_dev4: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi5_pins>; ++ status = "disabled"; ++ ++ spi_dev5: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&spi6 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi6_pins>; ++ status = "disabled"; ++ ++ spi_dev6: spi_dev@0 { ++ compatible = "rohm,dh2228fv"; ++ reg = <0>; ++ pl022,com-mode = <1>; ++ spi-max-frequency = <10000000>; ++ }; ++}; ++ ++&tda988x_pin { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rgb_pad_pins>; ++ status = "disabled"; ++}; ++ ++&tdm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&tdm_pins>; ++ status = "disabled"; ++}; ++ ++&uart0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart0_pins>; ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1_pins>; ++ status = "disabled"; ++}; ++ ++&uart2 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart2_pins>; ++ status = "disabled"; ++}; ++ ++&uart3 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart3_pins>; ++ status = "disabled"; ++}; ++ ++&uart4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart4_pins>; ++ status = "disabled"; ++}; ++ ++&uart5 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart5_pins>; ++ status = "disabled"; ++}; ++ ++&usb0 { ++ clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>, ++ <&stgcrg JH7110_STGCLK_USB0_STB>, ++ <&stgcrg JH7110_STGCLK_USB0_APB>, ++ <&stgcrg JH7110_STGCLK_USB0_AXI>, ++ <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>, ++ <&stgcrg JH7110_STGCLK_PCIE0_APB>; ++ clock-names = "lpm", "stb", "apb", "axi", "utmi_apb", "phy"; ++ resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>, ++ <&stgcrg JH7110_STGRST_USB0_APB>, ++ <&stgcrg JH7110_STGRST_USB0_AXI>, ++ <&stgcrg JH7110_STGRST_USB0_UTMI_APB>, ++ <&stgcrg JH7110_STGRST_PCIE0_APB>; ++ reset-names = "pwrup", "apb", "axi", "utmi_apb", "phy"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb_pins>; ++ dr_mode = "host"; /* host or peripheral */ ++ status = "disabled"; ++}; ++ ++&usb_cdns3 { ++ phys = <&usbphy0>, <&pciephy0>; ++ phy-names = "cdns3,usb2-phy", "cdns3,usb3-phy"; ++}; ++ ++&vin_sysctl { ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* Parallel bus endpoint */ ++ parallel_from_sc2235: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&sc2235_to_parallel>; ++ bus-type = <5>; /* Parallel */ ++ bus-width = <8>; ++ data-shift = <2>; /* lines 9:2 are used */ ++ hsync-active = <1>; ++ vsync-active = <0>; ++ pclk-sample = <1>; ++ status = "okay"; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* CSI2 bus endpoint */ ++ csi2rx0_from_ov4689: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&ov4689_to_csi2rx0>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <1 2 3 4>; ++ status = "okay"; ++ }; ++ ++ /* CSI2 bus endpoint */ ++ csi2rx0_from_imx219: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&imx219_to_csi2rx0>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <2 1>; ++ lane-polarities = <1 1 1>; ++ status = "okay"; ++ }; ++ ++ csi2rx0_from_imx708: endpoint@2 { ++ reg = <2>; ++ remote-endpoint = <&imx708_to_csi2rx0>; ++ bus-type = <4>; /* MIPI CSI-2 D-PHY */ ++ clock-lanes = <0>; ++ data-lanes = <2 1>; ++ lane-polarities = <1 1 1>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; ++ ++&vpu_dec { ++ status = "okay"; ++}; ++ ++&vpu_enc { ++ status = "okay"; ++}; ++ ++&xrp { ++ memory-region = <&xrp_reserved>; ++ status = "okay"; ++}; +--- a/arch/riscv/boot/dts/starfive/jh7110.dtsi ++++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi +@@ -196,11 +196,60 @@ + opp-750000000 { + opp-hz = /bits/ 64 <750000000>; + opp-microvolt = <800000>; ++ opp-suspend; + }; + opp-1500000000 { + opp-hz = /bits/ 64 <1500000000>; + opp-microvolt = <1040000>; + }; ++ /* CPU opp table for 1.25GHz */ ++ opp-312500000 { ++ opp-hz = /bits/ 64 <312500000>; ++ opp-microvolt = <800000>; ++ }; ++ opp-417000000 { ++ opp-hz = /bits/ 64 <417000000>; ++ opp-microvolt = <800000>; ++ }; ++ opp-625000000 { ++ opp-hz = /bits/ 64 <625000000>; ++ opp-microvolt = <800000>; ++ opp-suspend; ++ }; ++ opp-1250000000 { ++ opp-hz = /bits/ 64 <1250000000>; ++ opp-microvolt = <1000000>; ++ }; ++ }; ++ ++ display: display-subsystem { ++ compatible = "starfive,jh7110-display","verisilicon,display-subsystem"; ++ status = "disabled"; ++ }; ++ ++ dsi_output: dsi-output { ++ compatible = "starfive,jh7110-display-encoder","verisilicon,dsi-encoder"; ++ status = "disabled"; ++ }; ++ ++ mailbox_client0: mailbox_client { ++ compatible = "starfive,mailbox-test"; ++ mbox-names = "rx", "tx"; ++ mboxes = <&mailbox_contrl0 0 1>,<&mailbox_contrl0 1 0>; ++ status = "disabled"; ++ }; ++ ++ rgb_output: rgb-output { ++ compatible = "starfive,jh7110-rgb_output","verisilicon,rgb-encoder"; ++ //verisilicon,dss-syscon = <&dssctrl>; ++ //verisilicon,mux-mask = <0x70 0x380>; ++ //verisilicon,mux-val = <0x40 0x280>; ++ status = "disabled"; ++ }; ++ ++ tda988x_pin: tda988x_pin { ++ compatible = "starfive,tda998x_rgb_pin"; ++ status = "disabled"; + }; + + thermal-zones { +@@ -349,7 +398,9 @@ + + ccache: cache-controller@2010000 { + compatible = "starfive,jh7110-ccache", "sifive,ccache0", "cache"; +- reg = <0x0 0x2010000 0x0 0x4000>; ++ reg = <0x0 0x2010000 0x0 0x4000>, ++ <0x0 0x8000000 0x0 0x2000000>, ++ <0x0 0xa000000 0x0 0x2000000>; + interrupts = <1>, <3>, <4>, <2>; + cache-block-size = <64>; + cache-level = <2>; +@@ -378,7 +429,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART0_CORE>, + <&syscrg JH7110_SYSCLK_UART0_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART0_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART0_APB>, ++ <&syscrg JH7110_SYSRST_UART0_CORE>; + interrupts = <32>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -391,7 +443,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART1_CORE>, + <&syscrg JH7110_SYSCLK_UART1_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART1_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART1_APB>, ++ <&syscrg JH7110_SYSRST_UART1_CORE>; + interrupts = <33>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -404,7 +457,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART2_CORE>, + <&syscrg JH7110_SYSCLK_UART2_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART2_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART2_APB>, ++ <&syscrg JH7110_SYSRST_UART2_CORE>; + interrupts = <34>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -513,6 +567,25 @@ + status = "disabled"; + }; + ++ spdif: spdif@100a0000 { ++ compatible = "starfive,jh7110-spdif"; ++ reg = <0x0 0x100a0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_SPDIF_APB>, ++ <&syscrg JH7110_SYSCLK_SPDIF_CORE>, ++ <&syscrg JH7110_SYSCLK_AUDIO_ROOT>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>, <&syscrg JH7110_SYSCLK_MCLK>; ++ clock-names = "apb", "core", ++ "audroot", "mclk_inner", ++ "mclk_ext", "mclk"; ++ resets = <&syscrg JH7110_SYSRST_SPDIF_APB>; ++ reset-names = "apb"; ++ interrupts = <84>; ++ interrupt-names = "tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ + pwmdac: pwmdac@100b0000 { + compatible = "starfive,jh7110-pwmdac"; + reg = <0x0 0x100b0000 0x0 0x1000>; +@@ -526,6 +599,42 @@ + status = "disabled"; + }; + ++ pdm: pdm@100d0000 { ++ compatible = "starfive,jh7110-pdm"; ++ reg = <0x0 0x100d0000 0x0 0x1000>; ++ reg-names = "pdm"; ++ clocks = <&syscrg JH7110_SYSCLK_PDM_DMIC>, ++ <&syscrg JH7110_SYSCLK_PDM_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&mclk_ext>; ++ clock-names = "pdm_mclk", "pdm_apb", ++ "clk_mclk", "mclk_ext"; ++ resets = <&syscrg JH7110_SYSRST_PDM_DMIC>, ++ <&syscrg JH7110_SYSRST_PDM_APB>; ++ reset-names = "pdm_dmic", "pdm_apb"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2srx_mst: i2srx_mst@100e0000 { ++ compatible = "starfive,jh7110-i2srx-master"; ++ reg = <0x0 0x100e0000 0x0 0x1000>; ++ clocks = <&syscrg JH7110_SYSCLK_I2SRX_BCLK_MST>, ++ <&syscrg JH7110_SYSCLK_I2SRX_APB>, ++ <&syscrg JH7110_SYSCLK_MCLK>, ++ <&syscrg JH7110_SYSCLK_MCLK_INNER>, ++ <&mclk_ext>; ++ clock-names = "i2sclk", "apb", "mclk", ++ "mclk_inner","mclk_ext"; ++ resets = <&syscrg JH7110_SYSRST_I2SRX_APB>, ++ <&syscrg JH7110_SYSRST_I2SRX_BCLK>; ++ dmas = <&dma 24>; ++ dma-names = "rx"; ++ starfive,syscon = <&sys_syscon 0x18 0x2 0x34 0x3FC00 0x24400>; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ + i2srx: i2s@100e0000 { + compatible = "starfive,jh7110-i2srx"; + reg = <0x0 0x100e0000 0x0 0x1000>; +@@ -622,6 +731,26 @@ + #reset-cells = <1>; + }; + ++ xrp: xrp@10230000 { ++ compatible = "cdns,xrp"; ++ dma-coherent; ++ reg = <0x0 0x10230000 0x0 0x00010000 ++ 0x0 0x10240000 0x0 0x00010000>; ++ clocks = <&stgcrg JH7110_STGCLK_HIFI4_CLK_CORE>; ++ clock-names = "core_clk"; ++ resets = <&stgcrg JH7110_STGRST_HIFI4_CORE>, ++ <&stgcrg JH7110_STGRST_HIFI4_AXI>; ++ reset-names = "rst_core","rst_axi"; ++ starfive,stg-syscon = <&stg_syscon>; ++ firmware-name = "hifi4_elf"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x40000000 0x0 0x20000000 0x040000 ++ 0x69c00000 0x0 0x69c00000 0x03000000>; ++ status = "disabled"; ++ dsp@0 {}; ++ }; ++ + stg_syscon: syscon@10240000 { + compatible = "starfive,jh7110-stg-syscon", "syscon"; + reg = <0x0 0x10240000 0x0 0x1000>; +@@ -633,7 +762,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART3_CORE>, + <&syscrg JH7110_SYSCLK_UART3_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART3_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART3_APB>, ++ <&syscrg JH7110_SYSRST_UART3_CORE>; + interrupts = <45>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -646,7 +776,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART4_CORE>, + <&syscrg JH7110_SYSCLK_UART4_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART4_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART4_APB>, ++ <&syscrg JH7110_SYSRST_UART4_CORE>; + interrupts = <46>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -659,7 +790,8 @@ + clocks = <&syscrg JH7110_SYSCLK_UART5_CORE>, + <&syscrg JH7110_SYSCLK_UART5_APB>; + clock-names = "baudclk", "apb_pclk"; +- resets = <&syscrg JH7110_SYSRST_UART5_APB>; ++ resets = <&syscrg JH7110_SYSRST_UART5_APB>, ++ <&syscrg JH7110_SYSRST_UART5_CORE>; + interrupts = <47>; + reg-io-width = <4>; + reg-shift = <2>; +@@ -919,6 +1051,18 @@ + "ch2", "ch3"; + }; + ++ mailbox_contrl0: mailbox@13060000 { ++ compatible = "starfive,mail_box"; ++ reg = <0x0 0x13060000 0x0 0x0001000>; ++ clocks = <&syscrg JH7110_SYSCLK_MAILBOX_APB>; ++ clock-names = "clk_apb"; ++ resets = <&syscrg JH7110_SYSRST_MAILBOX_APB>; ++ reset-names = "mbx_rre"; ++ interrupts = <26 27>; ++ #mbox-cells = <2>; ++ status = "disabled"; ++ }; ++ + watchdog@13070000 { + compatible = "starfive,jh7110-wdt"; + reg = <0x0 0x13070000 0x0 0x10000>; +@@ -929,6 +1073,112 @@ + <&syscrg JH7110_SYSRST_WDT_CORE>; + }; + ++ jpu: jpu@13090000 { ++ compatible = "starfive,jpu"; ++ dma-coherent; ++ reg = <0x0 0x13090000 0x0 0x300>; ++ interrupts = <14>; ++ clocks = <&syscrg JH7110_SYSCLK_CODAJ12_AXI>, ++ <&syscrg JH7110_SYSCLK_CODAJ12_CORE>, ++ <&syscrg JH7110_SYSCLK_CODAJ12_APB>, ++ <&syscrg JH7110_SYSCLK_NOC_BUS_VDEC_AXI>, ++ <&syscrg JH7110_SYSCLK_VDEC_MAIN>, ++ <&syscrg JH7110_SYSCLK_VDEC_JPG>; ++ clock-names = "axi_clk", "core_clk", "apb_clk", ++ "noc_bus", "main_clk", "dec_clk"; ++ resets = <&syscrg JH7110_SYSRST_CODAJ12_AXI>, ++ <&syscrg JH7110_SYSRST_CODAJ12_CORE>, ++ <&syscrg JH7110_SYSRST_CODAJ12_APB>; ++ reset-names = "rst_axi", "rst_core", "rst_apb"; ++ power-domains = <&pwrc JH7110_PD_VDEC>; ++ status = "disabled"; ++ }; ++ ++ vpu_dec: vpu_dec@130a0000 { ++ compatible = "starfive,vdec"; ++ dma-coherent; ++ reg = <0x0 0x130a0000 0x0 0x10000>; ++ interrupts = <13>; ++ clocks = <&syscrg JH7110_SYSCLK_WAVE511_AXI>, ++ <&syscrg JH7110_SYSCLK_WAVE511_BPU>, ++ <&syscrg JH7110_SYSCLK_WAVE511_VCE>, ++ <&syscrg JH7110_SYSCLK_WAVE511_APB>, ++ <&syscrg JH7110_SYSCLK_NOC_BUS_VDEC_AXI>, ++ <&syscrg JH7110_SYSCLK_VDEC_MAIN>; ++ clock-names = "axi_clk", "bpu_clk", "vce_clk", ++ "apb_clk", "noc_bus", "main_clk"; ++ resets = <&syscrg JH7110_SYSRST_WAVE511_AXI>, ++ <&syscrg JH7110_SYSRST_WAVE511_BPU>, ++ <&syscrg JH7110_SYSRST_WAVE511_VCE>, ++ <&syscrg JH7110_SYSRST_WAVE511_APB>, ++ <&syscrg JH7110_SYSRST_AXIMEM0_AXI>; ++ reset-names = "rst_axi", "rst_bpu", "rst_vce", ++ "rst_apb", "rst_sram"; ++ starfive,vdec_noc_ctrl; ++ power-domains = <&pwrc JH7110_PD_VDEC>; ++ status = "disabled"; ++ }; ++ ++ vpu_enc: vpu_enc@130b0000 { ++ compatible = "starfive,venc"; ++ dma-coherent; ++ reg = <0x0 0x130b0000 0x0 0x10000>; ++ interrupts = <15>; ++ clocks = <&syscrg JH7110_SYSCLK_WAVE420L_AXI>, ++ <&syscrg JH7110_SYSCLK_WAVE420L_BPU>, ++ <&syscrg JH7110_SYSCLK_WAVE420L_VCE>, ++ <&syscrg JH7110_SYSCLK_WAVE420L_APB>, ++ <&syscrg JH7110_SYSCLK_NOC_BUS_VENC_AXI>; ++ clock-names = "axi_clk", "bpu_clk", "vce_clk", ++ "apb_clk", "noc_bus"; ++ resets = <&syscrg JH7110_SYSRST_WAVE420L_AXI>, ++ <&syscrg JH7110_SYSRST_WAVE420L_BPU>, ++ <&syscrg JH7110_SYSRST_WAVE420L_VCE>, ++ <&syscrg JH7110_SYSRST_WAVE420L_APB>, ++ <&syscrg JH7110_SYSRST_AXIMEM1_AXI>; ++ reset-names = "rst_axi", "rst_bpu", "rst_vce", ++ "rst_apb", "rst_sram"; ++ starfive,venc_noc_ctrl; ++ power-domains = <&pwrc JH7110_PD_VENC>; ++ status = "disabled"; ++ }; ++ ++ can0: can@130d0000 { ++ compatible = "starfive,jh7110-can", "ipms,can"; ++ reg = <0x0 0x130d0000 0x0 0x1000>; ++ interrupts = <112>; ++ clocks = <&syscrg JH7110_SYSCLK_CAN0_APB>, ++ <&syscrg JH7110_SYSCLK_CAN0_CAN>, ++ <&syscrg JH7110_SYSCLK_CAN0_TIMER>; ++ clock-names = "apb_clk", "core_clk", "timer_clk"; ++ resets = <&syscrg JH7110_SYSRST_CAN0_APB>, ++ <&syscrg JH7110_SYSRST_CAN0_CORE>, ++ <&syscrg JH7110_SYSRST_CAN0_TIMER>; ++ reset-names = "rst_apb", "rst_core", "rst_timer"; ++ frequency = <40000000>; ++ starfive,sys-syscon = <&sys_syscon 0x10 0x3 0x8>; ++ syscon,can_or_canfd = <0>; ++ status = "disabled"; ++ }; ++ ++ can1: can@130e0000 { ++ compatible = "starfive,jh7110-can", "ipms,can"; ++ reg = <0x0 0x130e0000 0x0 0x1000>; ++ interrupts = <113>; ++ clocks = <&syscrg JH7110_SYSCLK_CAN1_APB>, ++ <&syscrg JH7110_SYSCLK_CAN1_CAN>, ++ <&syscrg JH7110_SYSCLK_CAN1_TIMER>; ++ clock-names = "apb_clk", "core_clk", "timer_clk"; ++ resets = <&syscrg JH7110_SYSRST_CAN1_APB>, ++ <&syscrg JH7110_SYSRST_CAN1_CORE>, ++ <&syscrg JH7110_SYSRST_CAN1_TIMER>; ++ reset-names = "rst_apb", "rst_core", "rst_timer"; ++ frequency = <40000000>; ++ starfive,sys-syscon = <&sys_syscon 0x88 0x12 0x40000>; ++ syscon,can_or_canfd = <0>; ++ status = "disabled"; ++ }; ++ + crypto: crypto@16000000 { + compatible = "starfive,jh7110-crypto"; + reg = <0x0 0x16000000 0x0 0x4000>; +@@ -1119,6 +1369,42 @@ + #power-domain-cells = <1>; + }; + ++ rtc: rtc@17040000 { ++ compatible = "starfive,jh7110-rtc"; ++ reg = <0x0 0x17040000 0x0 0x10000>; ++ interrupts = <10>, <11>, <12>; ++ interrupt-names = "rtc_ms_pulse", "rtc_sec_pulse", "rtc"; ++ clocks = <&aoncrg JH7110_AONCLK_RTC_APB>, ++ <&aoncrg JH7110_AONCLK_RTC_CAL>; ++ clock-names = "pclk", "cal_clk"; ++ resets = <&aoncrg JH7110_AONRST_RTC_32K>, ++ <&aoncrg JH7110_AONRST_RTC_APB>, ++ <&aoncrg JH7110_AONRST_RTC_CAL>; ++ reset-names = "rst_osc", "rst_apb", "rst_cal"; ++ rtc,cal-clock-freq = <1000000>; ++ }; ++ ++ gpu: gpu@18000000 { ++ compatible = "img-gpu"; ++ reg = <0x0 0x18000000 0x0 0x100000>, ++ <0x0 0x130C000 0x0 0x10000>; ++ clocks = <&syscrg JH7110_SYSCLK_GPU_CORE>, ++ <&syscrg JH7110_SYSCLK_GPU_APB>, ++ <&syscrg JH7110_SYSCLK_GPU_RTC_TOGGLE>, ++ <&syscrg JH7110_SYSCLK_GPU_CORE_CLK>, ++ <&syscrg JH7110_SYSCLK_GPU_SYS_CLK>, ++ <&syscrg JH7110_SYSCLK_NOC_BUS_GPU_AXI>; ++ clock-names = "clk_bv", "clk_apb", "clk_rtc", ++ "clk_core", "clk_sys", "clk_axi"; ++ resets = <&syscrg JH7110_SYSRST_GPU_APB>, ++ <&syscrg JH7110_SYSRST_GPU_DOMA>; ++ reset-names = "rst_apb", "rst_doma"; ++ power-domains = <&pwrc JH7110_PD_GPUA>; ++ interrupts = <82>; ++ current-clock = <8000000>; ++ status = "disabled"; ++ }; ++ + csi2rx: csi-bridge@19800000 { + compatible = "starfive,jh7110-csi2rx"; + reg = <0x0 0x19800000 0x0 0x10000>; +@@ -1145,6 +1431,67 @@ + status = "disabled"; + }; + ++ vin_sysctl: vin_sysctl@19800000 { ++ compatible = "starfive,jh7110-vin"; ++ reg = <0x0 0x19800000 0x0 0x10000>, ++ <0x0 0x19810000 0x0 0x10000>, ++ <0x0 0x19820000 0x0 0x10000>, ++ <0x0 0x19840000 0x0 0x10000>, ++ <0x0 0x19870000 0x0 0x30000>, ++ <0x0 0x11840000 0x0 0x10000>, ++ <0x0 0x17030000 0x0 0x10000>, ++ <0x0 0x13020000 0x0 0x10000>; ++ reg-names = "csi2rx", "vclk", "vrst", "sctrl", ++ "isp", "trst", "pmu", "syscrg"; ++ clocks = <&ispcrg JH7110_ISPCLK_DOM4_APB_FUNC>, ++ <&ispcrg JH7110_ISPCLK_VIN_APB>, ++ <&ispcrg JH7110_ISPCLK_VIN_SYS>, ++ <&ispcrg JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C>, ++ <&ispcrg JH7110_ISPCLK_DVP_INV>, ++ <&ispcrg JH7110_ISPCLK_VIN_P_AXI_WR>, ++ <&ispcrg JH7110_ISPCLK_MIPI_RX0_PXL>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF0>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF1>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF2>, ++ <&ispcrg JH7110_ISPCLK_VIN_PIXEL_IF3>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>, ++ <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>, ++ <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, ++ <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>; ++ clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk", ++ "clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr", ++ "clk_mipi_rx0_pxl", "clk_pixel_clk_if0", ++ "clk_pixel_clk_if1", "clk_pixel_clk_if2", ++ "clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in", ++ "clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0", ++ "clk_ispcore_2x", "clk_isp_axi"; ++ resets = <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_P>, ++ <&ispcrg JH7110_ISPRST_ISPV2_TOP_WRAPPER_C>, ++ <&ispcrg JH7110_ISPRST_VIN_APB>, ++ <&ispcrg JH7110_ISPRST_VIN_SYS>, ++ <&ispcrg JH7110_ISPRST_VIN_P_AXI_RD>, ++ <&ispcrg JH7110_ISPRST_VIN_P_AXI_WR>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF0>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF1>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF2>, ++ <&ispcrg JH7110_ISPRST_VIN_PIXEL_IF3>, ++ <&ispcrg JH7110_ISPRST_M31DPHY_HW>, ++ <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>, ++ <&syscrg JH7110_SYSRST_ISP_TOP>, ++ <&syscrg JH7110_SYSRST_ISP_TOP_AXI>; ++ reset-names = "rst_wrapper_p", "rst_wrapper_c", "rst_pclk", ++ "rst_sys_clk", "rst_axird", "rst_axiwr", "rst_pixel_clk_if0", ++ "rst_pixel_clk_if1", "rst_pixel_clk_if2", "rst_pixel_clk_if3", ++ "rst_m31dphy_hw", "rst_m31dphy_b09_always_on", ++ "rst_isp_top_n", "rst_isp_top_axi"; ++ starfive,aon-syscon = <&aon_syscon 0x00>; ++ power-domains = <&pwrc JH7110_PD_ISP>; ++ /* irq nr: vin, isp, isp_csi, isp_scd, isp_csiline */ ++ interrupts = <92 87 88 89 90>; ++ status = "disabled"; ++ }; ++ + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x0 0x19810000 0x0 0x10000>; +@@ -1175,6 +1522,66 @@ + #phy-cells = <0>; + }; + ++ dc8200: dc8200@29400000 { ++ compatible = "starfive,jh7110-dc8200","verisilicon,dc8200"; ++ verisilicon,dss-syscon = <&dssctrl>;//20220624 panel syscon ++ reg = <0x0 0x29400000 0x0 0x100>, ++ <0x0 0x29400800 0x0 0x2000>, ++ <0x0 0x17030000 0x0 0x1000>; ++ interrupts = <95>; ++ clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_DISP_AXI>, ++ <&syscrg JH7110_SYSCLK_VOUT_SRC>, ++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, ++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX0>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX1>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_AXI>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_CORE>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_AHB>, ++ <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, ++ <&voutcrg JH7110_VOUTCLK_DOM_VOUT_TOP_LCD>, ++ <&hdmitx0_pixelclk>, ++ <&voutcrg JH7110_VOUTCLK_DC8200_PIX>; ++ clock-names = "noc_disp","vout_src", ++ "top_vout_axi","top_vout_ahb", ++ "pix_clk","vout_pix1", ++ "axi_clk","core_clk","vout_ahb", ++ "vout_top_axi","vout_top_lcd","hdmitx0_pixelclk","dc8200_pix0"; ++ resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>, ++ <&voutcrg JH7110_VOUTRST_DC8200_AXI>, ++ <&voutcrg JH7110_VOUTRST_DC8200_AHB>, ++ <&voutcrg JH7110_VOUTRST_DC8200_CORE>, ++ <&syscrg JH7110_SYSRST_NOC_BUS_DISP_AXI>; ++ reset-names = "rst_vout_src","rst_axi","rst_ahb","rst_core", ++ "rst_noc_disp"; ++ status = "disabled"; ++ }; ++ ++ hdmi: hdmi@29590000 { ++ compatible = "starfive,jh7110-hdmi","inno,hdmi"; ++ reg = <0x0 0x29590000 0x0 0x4000>; ++ interrupts = <99>; ++ /*interrupts = ;*/ ++ /*clocks = <&cru PCLK_HDMI>;*/ ++ /*clock-names = "pclk";*/ ++ /*pinctrl-names = "default";*/ ++ /*pinctrl-0 = <&hdmi_ctl>;*/ ++ clocks = <&voutcrg JH7110_VOUTCLK_HDMI_TX_SYS>, ++ <&voutcrg JH7110_VOUTCLK_HDMI_TX_MCLK>, ++ <&voutcrg JH7110_VOUTCLK_HDMI_TX_BCLK>, ++ <&hdmitx0_pixelclk>; ++ clock-names = "sysclk", "mclk","bclk","pclk"; ++ resets = <&voutcrg JH7110_VOUTRST_HDMI_TX_HDMI>; ++ reset-names = "hdmi_tx"; ++ #sound-dai-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ dssctrl: dssctrl@295B0000 { ++ compatible = "starfive,jh7110-dssctrl","verisilicon,dss-ctrl", "syscon"; ++ reg = <0 0x295B0000 0 0x90>; ++ }; ++ + voutcrg: clock-controller@295c0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x0 0x295c0000 0x0 0x10000>; +@@ -1193,6 +1600,67 @@ + power-domains = <&pwrc JH7110_PD_VOUT>; + }; + ++ mipi_dsi: mipi@295d0000 { ++ compatible = "starfive,jh7110-mipi_dsi","cdns,dsi"; ++ reg = <0x0 0x295d0000 0x0 0x10000>; ++ interrupts = <98>; ++ reg-names = "dsi"; ++ clocks = <&voutcrg JH7110_VOUTCLK_DSITX_SYS>, ++ <&voutcrg JH7110_VOUTCLK_DSITX_APB>, ++ <&voutcrg JH7110_VOUTCLK_DSITX_TXESC>, ++ <&voutcrg JH7110_VOUTCLK_DSITX_DPI>; ++ clock-names = "dpi", "apb", "txesc", "sys"; ++ resets = <&voutcrg JH7110_VOUTRST_DSITX_DPI>, ++ <&voutcrg JH7110_VOUTRST_DSITX_APB>, ++ <&voutcrg JH7110_VOUTRST_DSITX_RXESC>, ++ <&voutcrg JH7110_VOUTRST_DSITX_SYS>, ++ <&voutcrg JH7110_VOUTRST_DSITX_TXBYTEHS>, ++ <&voutcrg JH7110_VOUTRST_DSITX_TXESC>; ++ reset-names = "dsi_dpi", "dsi_apb", "dsi_rxesc", ++ "dsi_sys", "dsi_txbytehs", "dsi_txesc"; ++ phys = <&mipi_dphy>; ++ phy-names = "dphy"; ++ status = "disabled"; ++ }; ++ ++ mipi_dphy: mipi-dphy@295e0000{ ++ compatible = "starfive,jh7110-mipi-dphy-tx","m31,mipi-dphy-tx"; ++ reg = <0x0 0x295e0000 0x0 0x10000>; ++ clocks = <&voutcrg JH7110_VOUTCLK_MIPITX_DPHY_TXESC>; ++ clock-names = "dphy_txesc"; ++ resets = <&voutcrg JH7110_VOUTRST_MIPITX_DPHY_SYS>, ++ <&voutcrg JH7110_VOUTRST_MIPITX_DPHY_TXBYTEHS>; ++ reset-names = "dphy_sys", "dphy_txbytehs"; ++ #phy-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ co_process: e24@6e210000 { ++ compatible = "starfive,e24"; ++ dma-coherent; ++ reg = <0x0 0x6e210000 0x0 0x00001000>, ++ <0x0 0x6e211000 0x0 0x0003f000>; ++ reg-names = "ecmd", "espace"; ++ clocks = <&stgcrg JH7110_STGCLK_E2_RTC>, ++ <&stgcrg JH7110_STGCLK_E2_CORE>, ++ <&stgcrg JH7110_STGCLK_E2_DBG>; ++ clock-names = "clk_rtc", "clk_core", "clk_dbg"; ++ resets = <&stgcrg JH7110_STGRST_E24_CORE>; ++ reset-names = "e24_core"; ++ starfive,stg-syscon = <&stg_syscon>; ++ interrupt-parent = <&plic>; ++ firmware-name = "e24_elf"; ++ irq-mode = <1>; ++ mbox-names = "tx", "rx"; ++ mboxes = <&mailbox_contrl0 0 2>, ++ <&mailbox_contrl0 2 0>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x6ce00000 0x0 0x6ce00000 0x1600000>; ++ status = "disabled"; ++ dsp@0 {}; ++ }; ++ + pcie0: pcie@940000000 { + compatible = "starfive,jh7110-pcie"; + reg = <0x9 0x40000000 0x0 0x1000000>, diff --git a/target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch b/target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch new file mode 100644 index 0000000000..7a23bf931b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0057-riscv-dts-starfive-Add-JH7110-EVB-expanded-device-tr.patch @@ -0,0 +1,728 @@ +From cae7550054ca0cd940bbc1501ae5611f5d2957e6 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Wed, 20 Sep 2023 14:53:22 +0800 +Subject: [PATCH 057/116] riscv: dts: starfive: Add JH7110 EVB expanded device + tree + +Add JH7110 EVB expanded device tree. +The code is ported from tag JH7110_SDK_6.1_v5.11.3 + +Signed-off-by: Hal Feng +--- + arch/riscv/boot/dts/starfive/Makefile | 11 +- + .../starfive/jh7110-evb-can-pdm-pwmdac.dts | 102 ++++++++++++++++ + .../dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts | 37 ++++++ + .../dts/starfive/jh7110-evb-i2s-ac108.dts | 72 ++++++++++++ + .../dts/starfive/jh7110-evb-pcie-i2s-sd.dts | 111 ++++++++++++++++++ + .../dts/starfive/jh7110-evb-spi-uart2.dts | 65 ++++++++++ + .../starfive/jh7110-evb-uart1-rgb2hdmi.dts | 57 +++++++++ + .../starfive/jh7110-evb-uart4-emmc-spdif.dts | 78 ++++++++++++ + .../starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts | 95 +++++++++++++++ + .../dts/starfive/jh7110-evb-usbdevice.dts | 35 ++++++ + 10 files changed, 662 insertions(+), 1 deletion(-) + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-can-pdm-pwmdac.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-i2s-ac108.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-pcie-i2s-sd.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-spi-uart2.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart1-rgb2hdmi.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart4-emmc-spdif.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts + create mode 100644 arch/riscv/boot/dts/starfive/jh7110-evb-usbdevice.dts + +--- a/arch/riscv/boot/dts/starfive/Makefile ++++ b/arch/riscv/boot/dts/starfive/Makefile +@@ -12,4 +12,13 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-st + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb + +-dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb ++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb \ ++ jh7110-evb-pcie-i2s-sd.dtb \ ++ jh7110-evb-spi-uart2.dtb \ ++ jh7110-evb-uart4-emmc-spdif.dtb \ ++ jh7110-evb-uart5-pwm-i2c-tdm.dtb \ ++ jh7110-evb-dvp-rgb2hdmi.dtb \ ++ jh7110-evb-can-pdm-pwmdac.dtb \ ++ jh7110-evb-i2s-ac108.dtb \ ++ jh7110-evb-usbdevice.dtb \ ++ jh7110-evb-uart1-rgb2hdmi.dtb +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-can-pdm-pwmdac.dts +@@ -0,0 +1,102 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ sound2: snd-card2 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-PDM-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "i2s"; ++ bitclock-master = <&dailink_master>; ++ frame-master = <&dailink_master>; ++ ++ dailink_master:cpu { ++ sound-dai = <&i2srx_mst>; ++ }; ++ ++ dailink_slave:codec { ++ sound-dai = <&pdm>; ++ }; ++ }; ++ }; ++ ++ pwmdac_codec: pwmdac-codec { ++ compatible = "linux,spdif-dit"; ++ #sound-dai-cells = <0>; ++ }; ++ ++ sound3: snd-card3 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-PWMDAC-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "left_j"; ++ bitclock-master = <&sndcpu0>; ++ frame-master = <&sndcpu0>; ++ ++ sndcpu0: cpu { ++ sound-dai = <&pwmdac>; ++ }; ++ ++ codec { ++ sound-dai = <&pwmdac_codec>; ++ }; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&can0 { ++ status = "okay"; ++}; ++ ++&can1 { ++ status = "okay"; ++}; ++ ++&i2srx_mst { ++ status = "okay"; ++}; ++ ++&pwmdac { ++ status = "okay"; ++}; ++ ++&pdm { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-dvp-rgb2hdmi.dts +@@ -0,0 +1,37 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++&vin_sysctl { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dvp_pins>; ++}; ++ ++&rgb_output { ++ status = "okay"; ++}; ++ ++&tda988x_pin { ++ status = "okay"; ++}; ++ ++&dsi_output { ++ status = "disabled"; ++}; ++ ++&mipi_dsi { ++ status = "disabled"; ++}; ++ ++&mipi_dphy { ++ status = "disabled"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-i2s-ac108.dts +@@ -0,0 +1,72 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ /* i2s + ac108 */ ++ sound0: snd-card0 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-AC108-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "i2s"; ++ bitclock-master = <&sndcodec1>; ++ frame-master = <&sndcodec1>; ++ ++ widgets = "Microphone", "Mic Jack", ++ "Line", "Line In", ++ "Line", "Line Out", ++ "Speaker", "Speaker", ++ "Headphone", "Headphone Jack"; ++ routing = "Headphone Jack", "HP_L", ++ "Headphone Jack", "HP_R", ++ "Speaker", "SPK_LP", ++ "Speaker", "SPK_LN", ++ "LINPUT1", "Mic Jack", ++ "LINPUT3", "Mic Jack", ++ "RINPUT1", "Mic Jack", ++ "RINPUT2", "Mic Jack"; ++ ++ cpu { ++ sound-dai = <&i2srx>; ++ }; ++ ++ sndcodec1: codec { ++ sound-dai = <&ac108>; ++ clocks = <&ac108_mclk>; ++ clock-names = "mclk"; ++ }; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&i2c0 { ++ status = "okay"; ++}; ++ ++&i2srx { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-pcie-i2s-sd.dts +@@ -0,0 +1,111 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ /* i2s + wm8960 */ ++ sound6: snd-card6 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-WM8960-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ status = "okay"; ++ format = "i2s"; ++ bitclock-master = <&sndcodec1>; ++ frame-master = <&sndcodec1>; ++ ++ widgets = "Microphone", "Mic Jack", ++ "Line", "Line In", ++ "Line", "Line Out", ++ "Speaker", "Speaker", ++ "Headphone", "Headphone Jack"; ++ routing = "Headphone Jack", "HP_L", ++ "Headphone Jack", "HP_R", ++ "Speaker", "SPK_LP", ++ "Speaker", "SPK_LN", ++ "LINPUT1", "Mic Jack", ++ "LINPUT3", "Mic Jack", ++ "RINPUT1", "Mic Jack", ++ "RINPUT2", "Mic Jack"; ++ cpu0 { ++ sound-dai = <&i2srx>; ++ }; ++ cpu1 { ++ sound-dai = <&i2stx1>; ++ }; ++ ++ sndcodec1:codec { ++ sound-dai = <&wm8960>; ++ clocks = <&wm8960_mclk>; ++ clock-names = "mclk"; ++ }; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&pcie0 { ++ status = "okay"; ++}; ++ ++&uart3 { ++ status = "okay"; ++}; ++ ++&i2c0 { ++ status = "okay"; ++}; ++ ++&usb0 { ++ clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>, ++ <&stgcrg JH7110_STGCLK_USB0_STB>, ++ <&stgcrg JH7110_STGCLK_USB0_APB>, ++ <&stgcrg JH7110_STGCLK_USB0_AXI>, ++ <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>; ++ clock-names = "lpm", "stb", "apb", "axi", "utmi_apb"; ++ resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>, ++ <&stgcrg JH7110_STGRST_USB0_APB>, ++ <&stgcrg JH7110_STGRST_USB0_AXI>, ++ <&stgcrg JH7110_STGRST_USB0_UTMI_APB>; ++ reset-names = "pwrup", "apb", "axi", "utmi_apb"; ++ dr_mode = "host"; /*host or peripheral*/ ++ starfive,usb2-only; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb_pins>; ++ status = "okay"; ++}; ++ ++&i2srx { ++ status = "okay"; ++}; ++ ++&i2stx1 { ++ status = "okay"; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-spi-uart2.dts +@@ -0,0 +1,65 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&uart2 { ++ status = "okay"; ++}; ++ ++&spi0 { ++ status = "okay"; ++}; ++ ++&spi1 { ++ status = "okay"; ++}; ++ ++&spi2 { ++ status = "okay"; ++}; ++ ++&spi3 { ++ status = "okay"; ++}; ++ ++&spi4 { ++ status = "okay"; ++}; ++ ++&spi5 { ++ status = "okay"; ++}; ++ ++&spi6 { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart1-rgb2hdmi.dts +@@ -0,0 +1,57 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&uart1 { ++ status = "okay"; ++}; ++ ++&rgb_output { ++ status = "okay"; ++}; ++ ++&tda988x_pin { ++ status = "okay"; ++}; ++ ++&dsi_output { ++ status = "disabled"; ++}; ++ ++&mipi_dsi { ++ status = "disabled"; ++}; ++ ++&mipi_dphy { ++ status = "disabled"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart4-emmc-spdif.dts +@@ -0,0 +1,78 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ spdif_transmitter: spdif_transmitter { ++ compatible = "linux,spdif-dit"; ++ #sound-dai-cells = <0>; ++ }; ++ ++ sound4: snd-card4 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-SPDIF-Sound-Card"; ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "left_j"; ++ bitclock-master = <&sndcpu0>; ++ frame-master = <&sndcpu0>; ++ ++ sndcpu0: cpu { ++ sound-dai = <&spdif>; ++ }; ++ ++ codec { ++ sound-dai = <&spdif_transmitter>; ++ }; ++ }; ++ }; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&uart4 { ++ status = "okay"; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emmc0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <8>; ++ cap-mmc-highspeed; ++ mmc-hs200-1_8v; ++ non-removable; ++ cap-mmc-hw-reset; ++ board-is-evb; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_ch6to7_pins>; ++ status = "okay"; ++}; ++ ++&spdif { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-uart5-pwm-i2c-tdm.dts +@@ -0,0 +1,95 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++ ++ sound5: snd-card5 { ++ compatible = "simple-audio-card"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ simple-audio-card,name = "StarFive-TDM-Sound-Card"; ++ simple-audio-card,widgets = "Microphone", "Mic Jack", ++ "Line", "Line In", ++ "Line", "Line Out", ++ "Speaker", "Speaker", ++ "Headphone", "Headphone Jack"; ++ simple-audio-card,routing = "Headphone Jack", "HP_L", ++ "Headphone Jack", "HP_R", ++ "Speaker", "SPK_LP", ++ "Speaker", "SPK_LN", ++ "LINPUT1", "Mic Jack", ++ "LINPUT3", "Mic Jack", ++ "RINPUT1", "Mic Jack", ++ "RINPUT2", "Mic Jack"; ++ ++ simple-audio-card,dai-link@0 { ++ reg = <0>; ++ format = "dsp_a"; ++ bitclock-master = <&dailink_master>; ++ frame-master = <&dailink_master>; ++ ++ cpu { ++ sound-dai = <&tdm>; ++ }; ++ dailink_master: codec { ++ sound-dai = <&wm8960>; ++ clocks = <&wm8960_mclk>; ++ }; ++ }; ++ }; ++}; ++ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; ++ ++&uart5 { ++ status = "okay"; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pwm_ch0to3_pins &pwm_ch4to5_pins>; ++ status = "okay"; ++}; ++ ++&tdm { ++ status = "okay"; ++}; ++ ++&i2c0 { ++ status = "okay"; ++}; ++ ++&i2c1 { ++ status = "okay"; ++}; ++ ++&i2c3 { ++ status = "okay"; ++}; +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/jh7110-evb-usbdevice.dts +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: GPL-2.0 OR MIT ++/* ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++/dts-v1/; ++#include "jh7110-evb.dtsi" ++ ++/ { ++ model = "StarFive JH7110 EVB"; ++ compatible = "starfive,jh7110-evb", "starfive,jh7110"; ++}; ++ ++/* default sd card */ ++&mmc0 { ++ assigned-clocks = <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; ++ assigned-clock-rates = <50000000>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&sdcard0_pins>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ broken-cd; ++ post-power-on-delay-ms = <200>; ++ status = "okay"; ++}; ++ ++&usb0 { ++ dr_mode = "peripheral"; /*host or peripheral*/ ++ status = "okay"; ++}; ++ ++&pcie1 { ++ status = "okay"; ++}; diff --git a/target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch b/target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch new file mode 100644 index 0000000000..a75ffc2aff --- /dev/null +++ b/target/linux/starfive/patches-6.6/0058-riscv-dts-starfive-Add-evb-overlay-dtso-subdir.patch @@ -0,0 +1,485 @@ +From e9122ceaf2d8767753e2a126c14b29b78280446d Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Tue, 19 Sep 2023 21:35:39 +0800 +Subject: [PATCH 058/116] riscv: dts: starfive: Add evb-overlay dtso subdir + +Create subdir evb-overlay/ and add overlay .dtso for JH7110 EVB. +The code is ported from tag JH7110_SDK_6.1_v5.11.3 + +Signed-off-by: Hal Feng +--- + arch/riscv/boot/dts/starfive/Makefile | 1 + + .../boot/dts/starfive/evb-overlay/Makefile | 7 + + .../evb-overlay/jh7110-evb-overlay-can.dtso | 24 ++++ + .../jh7110-evb-overlay-rgb2hdmi.dtso | 24 ++++ + .../evb-overlay/jh7110-evb-overlay-sdio.dtso | 78 +++++++++++ + .../evb-overlay/jh7110-evb-overlay-spi.dtso | 72 ++++++++++ + .../jh7110-evb-overlay-uart4-emmc.dtso | 130 ++++++++++++++++++ + .../jh7110-evb-overlay-uart5-pwm.dtso | 92 +++++++++++++ + 8 files changed, 428 insertions(+) + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/Makefile + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-can.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-rgb2hdmi.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-sdio.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart4-emmc.dtso + create mode 100644 arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart5-pwm.dtso + +--- a/arch/riscv/boot/dts/starfive/Makefile ++++ b/arch/riscv/boot/dts/starfive/Makefile +@@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-st + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb + ++subdir-y += evb-overlay + dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb.dtb \ + jh7110-evb-pcie-i2s-sd.dtb \ + jh7110-evb-spi-uart2.dtb \ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/Makefile +@@ -0,0 +1,7 @@ ++# SPDX-License-Identifier: GPL-2.0 ++dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-evb-overlay-can.dtbo \ ++ jh7110-evb-overlay-sdio.dtbo \ ++ jh7110-evb-overlay-spi.dtbo \ ++ jh7110-evb-overlay-uart4-emmc.dtbo \ ++ jh7110-evb-overlay-uart5-pwm.dtbo \ ++ jh7110-evb-overlay-rgb2hdmi.dtbo +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-can.dtso +@@ -0,0 +1,24 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //can0 ++ fragment@0 { ++ target-path = "/soc/can@130d0000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //can1 ++ fragment@1 { ++ target-path = "/soc/can@130e0000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-rgb2hdmi.dtso +@@ -0,0 +1,24 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //hdmi_output ++ fragment@0 { ++ target-path = "/tda988x_pin"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //uart1 ++ fragment@1 { ++ target-path = "/soc/serial@10010000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-sdio.dtso +@@ -0,0 +1,78 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //sysgpio ++ fragment@0 { ++ target-path = "/soc/pinctrl@13040000"; ++ __overlay__ { ++ dt_sdcard1_pins: dt-sdcard1-0 { ++ sdcard-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ }; ++ }; ++ ++ //uart3 ++ fragment@1 { ++ target-path = "/soc/serial@12000000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //i2c0 ++ fragment@2 { ++ target-path = "/soc/i2c@10030000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //mmc1 ++ fragment@3 { ++ target-path = "/soc/mmc@16020000"; ++ __overlay__ { ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <4>; ++ no-sdio; ++ no-mmc; ++ broken-cd; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ sd-uhs-ddr50; ++ cap-sd-highspeed; ++ post-power-on-delay-ms = <200>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dt_sdcard1_pins>; ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-spi.dtso +@@ -0,0 +1,72 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //spi0 ++ fragment@0 { ++ target-path = "/soc/spi@10060000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi1 ++ fragment@1 { ++ target-path = "/soc/spi@10070000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi2 ++ fragment@2 { ++ target-path = "/soc/spi@10080000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi3 ++ fragment@3 { ++ target-path = "/soc/spi@12070000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi4 ++ fragment@4 { ++ target-path = "/soc/spi@12080000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi5 ++ fragment@5 { ++ target-path = "/soc/spi@12090000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //spi6 ++ fragment@6 { ++ target-path = "/soc/spi@120a0000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //uart2 ++ fragment@7 { ++ target-path = "/soc/serial@10020000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart4-emmc.dtso +@@ -0,0 +1,130 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //sysgpio ++ fragment@0 { ++ target-path = "/soc/pinctrl@13040000"; ++ __overlay__ { ++ dt_emmc0_pins: dt-emmc0-0 { ++ emmc-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ drive-strength = <12>; ++ input-enable; ++ slew-rate = <1>; ++ }; ++ }; ++ ++ dt_emmc1_pins: dt-emmc1-0 { ++ emmc-pins { ++ pinmux = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ bias-pull-up; ++ input-enable; ++ }; ++ }; ++ }; ++ }; ++ ++ //aongpio ++ fragment@1 { ++ target-path = "/soc/pinctrl@17020000"; ++ __overlay__ { ++ dt_pwm_ch6to7_pins: dt-pwm-ch6to7-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ }; ++ }; ++ ++ //uart4 ++ fragment@2 { ++ target-path = "/soc/serial@12010000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //mmc1 ++ fragment@3 { ++ target-path = "/soc/mmc@16020000"; ++ __overlay__ { ++ clock-frequency = <102400000>; ++ max-frequency = <100000000>; ++ card-detect-delay = <300>; ++ bus-width = <8>; ++ cap-mmc-hw-reset; ++ non-removable; ++ cap-mmc-highspeed; ++ post-power-on-delay-ms = <200>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dt_emmc1_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ //ptc ++ fragment@4 { ++ target-path = "/soc/pwm@120d0000"; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dt_pwm_ch6to7_pins>; ++ status = "okay"; ++ }; ++ }; ++}; ++ +--- /dev/null ++++ b/arch/riscv/boot/dts/starfive/evb-overlay/jh7110-evb-overlay-uart5-pwm.dtso +@@ -0,0 +1,92 @@ ++/dts-v1/; ++/plugin/; ++#include ++#include "../jh7110-pinfunc.h" ++/ { ++ compatible = "starfive,jh7110"; ++ ++ //sysgpio ++ fragment@0 { ++ target-path = "/soc/pinctrl@13040000"; ++ __overlay__ { ++ dt_pwm_ch0to3_pins: dt-pwm-ch0to3-0 { ++ pwm-pins { ++ pinmux = , ++ , ++ , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ }; ++ }; ++ ++ //aongpio ++ fragment@1 { ++ target-path = "/soc/pinctrl@17020000"; ++ __overlay__ { ++ dt_pwm_ch4to5_pins: dt-pwm-ch4to5-0 { ++ pwm-pins { ++ pinmux = , ++ ; ++ drive-strength = <12>; ++ }; ++ }; ++ }; ++ }; ++ ++ //uart5 ++ fragment@2 { ++ target-path = "/soc/serial@12020000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //ptc ++ fragment@3 { ++ target-path = "/soc/pwm@120d0000"; ++ __overlay__ { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&dt_pwm_ch0to3_pins &dt_pwm_ch4to5_pins>; ++ status = "okay"; ++ }; ++ }; ++ ++ //i2c0 ++ fragment@4 { ++ target-path = "/soc/i2c@10030000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //i2c1 ++ fragment@5 { ++ target-path = "/soc/i2c@10040000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ //i2c3 ++ fragment@6 { ++ target-path = "/soc/i2c@12030000"; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++}; ++ diff --git a/target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch b/target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch new file mode 100644 index 0000000000..ddf72454eb --- /dev/null +++ b/target/linux/starfive/patches-6.6/0059-riscv-configs-Add-starfive_jh7110_defconfig.patch @@ -0,0 +1,385 @@ +From 5c888fa081caf5d9473e733931d1c7b3d4b61e61 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Fri, 28 Jul 2023 18:42:55 +0800 +Subject: [PATCH 059/116] riscv: configs: Add starfive_jh7110_defconfig + +Add starfive_jh7110_defconfig for JH7110 EVB. +The code is ported from tag JH7110_SDK_6.1_v5.11.3 + +Signed-off-by: Hal Feng +--- + arch/riscv/configs/starfive_jh7110_defconfig | 368 +++++++++++++++++++ + 1 file changed, 368 insertions(+) + create mode 100644 arch/riscv/configs/starfive_jh7110_defconfig + +--- /dev/null ++++ b/arch/riscv/configs/starfive_jh7110_defconfig +@@ -0,0 +1,368 @@ ++CONFIG_COMPILE_TEST=y ++# CONFIG_WERROR is not set ++CONFIG_DEFAULT_HOSTNAME="StarFive" ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_USELIB=y ++CONFIG_NO_HZ_IDLE=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BPF_SYSCALL=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_CGROUPS=y ++CONFIG_MEMCG=y ++CONFIG_CGROUP_SCHED=y ++CONFIG_CFS_BANDWIDTH=y ++CONFIG_RT_GROUP_SCHED=y ++CONFIG_CGROUP_PIDS=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_HUGETLB=y ++CONFIG_CPUSETS=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_CGROUP_PERF=y ++CONFIG_CGROUP_BPF=y ++CONFIG_NAMESPACES=y ++CONFIG_USER_NS=y ++CONFIG_CHECKPOINT_RESTORE=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EXPERT=y ++# CONFIG_SYSFS_SYSCALL is not set ++CONFIG_PROFILING=y ++CONFIG_SOC_MICROCHIP_POLARFIRE=y ++CONFIG_SOC_STARFIVE=y ++CONFIG_SOC_VIRT=y ++CONFIG_ERRATA_SIFIVE=y ++CONFIG_NONPORTABLE=y ++CONFIG_SMP=y ++CONFIG_RISCV_SBI_V01=y ++# CONFIG_RISCV_BOOT_SPINWAIT is not set ++CONFIG_HIBERNATION=y ++CONFIG_PM_STD_PARTITION="PARTLABEL=hibernation" ++CONFIG_PM_DEBUG=y ++CONFIG_PM_ADVANCED_DEBUG=y ++CONFIG_PM_TEST_SUSPEND=y ++CONFIG_ENERGY_MODEL=y ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y ++CONFIG_CPUFREQ_DT=y ++CONFIG_VIRTUALIZATION=y ++CONFIG_KVM=m ++CONFIG_JUMP_LABEL=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_BINFMT_MISC=y ++CONFIG_CMA=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_XFRM_USER=m ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_INET_ESP=m ++CONFIG_NETFILTER=y ++CONFIG_BRIDGE_NETFILTER=m ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_FTP=m ++CONFIG_NF_CONNTRACK_TFTP=m ++CONFIG_NETFILTER_XT_MARK=m ++CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m ++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m ++CONFIG_NETFILTER_XT_MATCH_IPVS=m ++CONFIG_IP_VS=m ++CONFIG_IP_VS_PROTO_TCP=y ++CONFIG_IP_VS_PROTO_UDP=y ++CONFIG_IP_VS_RR=m ++CONFIG_IP_VS_NFCT=y ++CONFIG_NF_LOG_ARP=m ++CONFIG_NF_LOG_IPV4=m ++CONFIG_IP_NF_IPTABLES=m ++CONFIG_IP_NF_FILTER=m ++CONFIG_IP_NF_TARGET_REJECT=m ++CONFIG_IP_NF_NAT=m ++CONFIG_IP_NF_TARGET_MASQUERADE=m ++CONFIG_IP_NF_TARGET_REDIRECT=m ++CONFIG_IP_NF_MANGLE=m ++CONFIG_NF_LOG_IPV6=m ++CONFIG_IP6_NF_IPTABLES=m ++CONFIG_IP6_NF_MATCH_IPV6HEADER=m ++CONFIG_IP6_NF_FILTER=m ++CONFIG_IP6_NF_TARGET_REJECT=m ++CONFIG_IP6_NF_MANGLE=m ++CONFIG_BRIDGE=m ++CONFIG_BRIDGE_VLAN_FILTERING=y ++CONFIG_VLAN_8021Q=m ++CONFIG_NET_SCHED=y ++CONFIG_NET_CLS_CGROUP=m ++CONFIG_NETLINK_DIAG=y ++CONFIG_CGROUP_NET_PRIO=y ++CONFIG_CAN=y ++CONFIG_BT=y ++CONFIG_BT_RFCOMM=y ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=y ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HCIUART=y ++CONFIG_BT_HCIUART_H4=y ++CONFIG_CFG80211=y ++CONFIG_MAC80211=y ++CONFIG_RFKILL=y ++CONFIG_NET_9P=y ++CONFIG_NET_9P_VIRTIO=y ++CONFIG_PCI=y ++CONFIG_PCIEPORTBUS=y ++# CONFIG_PCIEASPM is not set ++CONFIG_PCI_HOST_GENERIC=y ++CONFIG_PCIE_FU740=y ++CONFIG_PCIE_STARFIVE_HOST=y ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_MTD=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_SPI_NOR=y ++CONFIG_OF_CONFIGFS=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_VIRTIO_BLK=y ++CONFIG_BLK_DEV_NVME=y ++CONFIG_BLK_DEV_SD=y ++CONFIG_BLK_DEV_SR=y ++CONFIG_SCSI_VIRTIO=y ++CONFIG_ATA=y ++CONFIG_SATA_AHCI=y ++CONFIG_SATA_AHCI_PLATFORM=y ++CONFIG_MD=y ++CONFIG_BLK_DEV_DM=m ++CONFIG_DM_THIN_PROVISIONING=m ++CONFIG_NETDEVICES=y ++CONFIG_DUMMY=m ++CONFIG_MACVLAN=m ++CONFIG_IPVLAN=m ++CONFIG_VXLAN=m ++CONFIG_VETH=m ++CONFIG_VIRTIO_NET=y ++CONFIG_MACB=y ++CONFIG_E1000E=y ++CONFIG_R8169=y ++CONFIG_STMMAC_ETH=y ++CONFIG_DWMAC_DWC_QOS_ETH=y ++# CONFIG_DWMAC_GENERIC is not set ++CONFIG_DWMAC_STARFIVE=y ++CONFIG_MARVELL_PHY=y ++CONFIG_MICREL_PHY=y ++CONFIG_MICROCHIP_PHY=y ++CONFIG_MICROSEMI_PHY=y ++CONFIG_MOTORCOMM_PHY=y ++CONFIG_IPMS_CAN=y ++CONFIG_IWLWIFI=y ++CONFIG_IWLDVM=y ++CONFIG_IWLMVM=y ++CONFIG_INPUT_MOUSEDEV=y ++CONFIG_INPUT_EVDEV=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_TINKER_FT5406=y ++CONFIG_SERIAL_8250=y ++CONFIG_SERIAL_8250_CONSOLE=y ++CONFIG_SERIAL_8250_NR_UARTS=6 ++CONFIG_SERIAL_8250_RUNTIME_UARTS=6 ++CONFIG_SERIAL_8250_EXTENDED=y ++CONFIG_SERIAL_8250_MANY_PORTS=y ++CONFIG_SERIAL_8250_DW=y ++CONFIG_SERIAL_OF_PLATFORM=y ++CONFIG_SERIAL_EARLYCON_RISCV_SBI=y ++CONFIG_TTY_PRINTK=y ++CONFIG_VIRTIO_CONSOLE=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_VIRTIO=y ++CONFIG_HW_RANDOM_JH7110=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_DESIGNWARE_PLATFORM=y ++CONFIG_SPI=y ++CONFIG_SPI_CADENCE_QUADSPI=y ++CONFIG_SPI_PL022=y ++CONFIG_SPI_SIFIVE=y ++CONFIG_SPI_SPIDEV=y ++# CONFIG_PTP_1588_CLOCK is not set ++CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_SIFIVE=y ++CONFIG_SENSORS_SFCTEMP=y ++CONFIG_THERMAL=y ++CONFIG_THERMAL_WRITABLE_TRIPS=y ++CONFIG_CPU_THERMAL=y ++CONFIG_THERMAL_EMULATION=y ++CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_SYSFS=y ++CONFIG_MFD_AXP20X_I2C=y ++CONFIG_REGULATOR=y ++CONFIG_REGULATOR_AXP20X=y ++CONFIG_REGULATOR_STARFIVE_JH7110=y ++# CONFIG_MEDIA_CEC_SUPPORT is not set ++CONFIG_MEDIA_SUPPORT=y ++CONFIG_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=y ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_V4L_MEM2MEM_DRIVERS=y ++CONFIG_VIDEO_WAVE_VPU=m ++CONFIG_VIN_SENSOR_SC2235=y ++CONFIG_VIN_SENSOR_OV4689=y ++CONFIG_VIN_SENSOR_IMX219=y ++CONFIG_VIDEO_STF_VIN=y ++CONFIG_VIDEO_IMX708=y ++CONFIG_DRM_I2C_NXP_TDA998X=y ++CONFIG_DRM_I2C_NXP_TDA9950=y ++CONFIG_DRM_RADEON=m ++CONFIG_DRM_VIRTIO_GPU=m ++CONFIG_DRM_VERISILICON=y ++CONFIG_STARFIVE_INNO_HDMI=y ++CONFIG_STARFIVE_DSI=y ++CONFIG_DRM_IMG_ROGUE=y ++CONFIG_DRM_LEGACY=y ++CONFIG_FB=y ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_USB_AUDIO=y ++CONFIG_SND_SOC=y ++CONFIG_SND_DESIGNWARE_I2S=y ++# CONFIG_SND_SOC_INTEL_SST_TOPLEVEL is not set ++CONFIG_SND_SOC_STARFIVE=y ++CONFIG_SND_SOC_JH7110_PDM=y ++CONFIG_SND_SOC_JH7110_PWMDAC=y ++CONFIG_SND_SOC_JH7110_SPDIF=y ++CONFIG_SND_SOC_JH7110_TDM=y ++CONFIG_SND_SOC_AC108=y ++CONFIG_SND_SOC_WM8960=y ++CONFIG_SND_SIMPLE_CARD=y ++CONFIG_USB=y ++CONFIG_USB_XHCI_HCD=y ++CONFIG_USB_EHCI_HCD=y ++CONFIG_USB_EHCI_HCD_PLATFORM=y ++CONFIG_USB_OHCI_HCD=y ++CONFIG_USB_OHCI_HCD_PLATFORM=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_UAS=y ++CONFIG_USB_CDNS_SUPPORT=y ++CONFIG_USB_CDNS3=y ++CONFIG_USB_CDNS3_GADGET=y ++CONFIG_USB_CDNS3_HOST=y ++CONFIG_USB_CDNS3_STARFIVE=y ++CONFIG_USB_GADGET=y ++CONFIG_USB_CONFIGFS=y ++CONFIG_USB_CONFIGFS_MASS_STORAGE=y ++CONFIG_USB_CONFIGFS_F_FS=y ++CONFIG_MMC=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_CADENCE=y ++CONFIG_MMC_SPI=y ++CONFIG_MMC_DW=y ++CONFIG_MMC_DW_STARFIVE=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_DRV_SPEAR is not set ++CONFIG_RTC_DRV_STARFIVE=y ++CONFIG_DMADEVICES=y ++CONFIG_AMBA_PL08X=y ++CONFIG_DW_AXI_DMAC=y ++# CONFIG_SH_DMAE_BASE is not set ++# CONFIG_TI_EDMA is not set ++# CONFIG_DMA_OMAP is not set ++CONFIG_DMATEST=y ++CONFIG_VIRTIO_PCI=y ++CONFIG_VIRTIO_BALLOON=y ++CONFIG_VIRTIO_INPUT=y ++CONFIG_VIRTIO_MMIO=y ++CONFIG_STAGING=y ++CONFIG_STAGING_MEDIA=y ++CONFIG_CLK_STARFIVE_JH7110_AON=y ++CONFIG_CLK_STARFIVE_JH7110_STG=y ++CONFIG_CLK_STARFIVE_JH7110_ISP=y ++CONFIG_CLK_STARFIVE_JH7110_VOUT=y ++CONFIG_MAILBOX=y ++CONFIG_STARFIVE_MBOX=m ++CONFIG_STARFIVE_MBOX_TEST=m ++CONFIG_RPMSG_CHAR=y ++CONFIG_RPMSG_CTRL=y ++CONFIG_RPMSG_VIRTIO=y ++CONFIG_SIFIVE_CCACHE=y ++CONFIG_PWM=y ++CONFIG_PWM_OCORES=y ++CONFIG_PHY_STARFIVE_JH7110_PCIE=y ++CONFIG_PHY_STARFIVE_JH7110_USB=y ++CONFIG_PHY_M31_DPHY_RX0=y ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_AUTOFS_FS=y ++CONFIG_FUSE_FS=y ++CONFIG_OVERLAY_FS=y ++CONFIG_OVERLAY_FS_INDEX=y ++CONFIG_OVERLAY_FS_XINO_AUTO=y ++CONFIG_OVERLAY_FS_METACOPY=y ++CONFIG_ISO9660_FS=y ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_EXFAT_FS=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_HUGETLBFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_V4_1=y ++CONFIG_NFS_V4_2=y ++CONFIG_ROOT_NFS=y ++CONFIG_9P_FS=y ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_ISO8859_1=m ++CONFIG_SECURITY=y ++CONFIG_SECURITY_SELINUX=y ++CONFIG_SECURITY_APPARMOR=y ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_LSM="" ++CONFIG_INIT_STACK_NONE=y ++CONFIG_CRYPTO_USER=y ++CONFIG_CRYPTO_ZSTD=y ++CONFIG_CRYPTO_USER_API_HASH=y ++CONFIG_CRYPTO_USER_API_SKCIPHER=y ++CONFIG_CRYPTO_USER_API_AEAD=y ++CONFIG_CRYPTO_STATS=y ++CONFIG_CRYPTO_DEV_VIRTIO=y ++CONFIG_CRYPTO_DEV_JH7110=y ++CONFIG_DMA_CMA=y ++CONFIG_PRINTK_TIME=y ++CONFIG_DEBUG_FS=y ++CONFIG_DEBUG_PAGEALLOC=y ++CONFIG_SCHED_STACK_END_CHECK=y ++CONFIG_DEBUG_VM=y ++CONFIG_DEBUG_VM_PGFLAGS=y ++CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_DEBUG_PER_CPU_MAPS=y ++CONFIG_SOFTLOCKUP_DETECTOR=y ++CONFIG_WQ_WATCHDOG=y ++CONFIG_DEBUG_TIMEKEEPING=y ++CONFIG_DEBUG_RT_MUTEXES=y ++CONFIG_DEBUG_SPINLOCK=y ++CONFIG_DEBUG_MUTEXES=y ++CONFIG_DEBUG_RWSEMS=y ++CONFIG_DEBUG_LIST=y ++CONFIG_DEBUG_PLIST=y ++CONFIG_DEBUG_SG=y ++CONFIG_RCU_CPU_STALL_TIMEOUT=60 ++# CONFIG_RCU_TRACE is not set ++CONFIG_RCU_EQS_DEBUG=y ++# CONFIG_FTRACE is not set ++# CONFIG_RUNTIME_TESTING_MENU is not set ++CONFIG_MEMTEST=y diff --git a/target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch b/target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch new file mode 100644 index 0000000000..68deb70c9b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0060-of-configfs-Add-configfs-function.patch @@ -0,0 +1,318 @@ +From 95c702022f5e4cb786719fcf90170334b1e562cc Mon Sep 17 00:00:00 2001 +From: Jianlong Huang +Date: Thu, 16 Jun 2022 17:13:57 +0800 +Subject: [PATCH 060/116] of: configfs: Add configfs function + +Signed-off-by: Jianlong Huang +Signed-off-by: Hal Feng +--- + drivers/of/Kconfig | 7 ++ + drivers/of/Makefile | 1 + + drivers/of/configfs.c | 277 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 285 insertions(+) + create mode 100644 drivers/of/configfs.c + +--- a/drivers/of/Kconfig ++++ b/drivers/of/Kconfig +@@ -102,4 +102,11 @@ config OF_OVERLAY + config OF_NUMA + bool + ++config OF_CONFIGFS ++ bool "Device Tree Overlay ConfigFS interface" ++ select CONFIGFS_FS ++ select OF_OVERLAY ++ help ++ Enable a simple user-space driven DT overlay interface. ++ + endif # OF +--- a/drivers/of/Makefile ++++ b/drivers/of/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o + obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o + obj-$(CONFIG_OF_RESOLVE) += resolver.o + obj-$(CONFIG_OF_OVERLAY) += overlay.o ++obj-$(CONFIG_OF_CONFIGFS) += configfs.o + obj-$(CONFIG_OF_NUMA) += of_numa.o + + ifdef CONFIG_KEXEC_FILE +--- /dev/null ++++ b/drivers/of/configfs.c +@@ -0,0 +1,277 @@ ++/* ++ * Configfs entries for device-tree ++ * ++ * Copyright (C) 2013 - Pantelis Antoniou ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "of_private.h" ++ ++struct cfs_overlay_item { ++ struct config_item item; ++ ++ char path[PATH_MAX]; ++ ++ const struct firmware *fw; ++ struct device_node *overlay; ++ int ov_id; ++ ++ void *dtbo; ++ int dtbo_size; ++}; ++ ++static inline struct cfs_overlay_item *to_cfs_overlay_item( ++ struct config_item *item) ++{ ++ return item ? container_of(item, struct cfs_overlay_item, item) : NULL; ++} ++ ++static ssize_t cfs_overlay_item_path_show(struct config_item *item, ++ char *page) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ return sprintf(page, "%s\n", overlay->path); ++} ++ ++static ssize_t cfs_overlay_item_path_store(struct config_item *item, ++ const char *page, size_t count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ const char *p = page; ++ char *s; ++ int err; ++ ++ /* if it's set do not allow changes */ ++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) ++ return -EPERM; ++ ++ /* copy to path buffer (and make sure it's always zero terminated */ ++ count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); ++ overlay->path[sizeof(overlay->path) - 1] = '\0'; ++ ++ /* strip trailing newlines */ ++ s = overlay->path + strlen(overlay->path); ++ while (s > overlay->path && *--s == '\n') ++ *s = '\0'; ++ ++ pr_debug("%s: path is '%s'\n", __func__, overlay->path); ++ ++ err = request_firmware(&overlay->fw, overlay->path, NULL); ++ if (err != 0) ++ return err; ++ ++ err = of_overlay_fdt_apply((void *)overlay->fw->data, ++ (u32)overlay->fw->size, &overlay->ov_id, NULL); ++ if (err != 0) ++ goto out_err; ++ ++ return count; ++ ++out_err: ++ ++ release_firmware(overlay->fw); ++ overlay->fw = NULL; ++ ++ overlay->path[0] = '\0'; ++ return err; ++} ++ ++static ssize_t cfs_overlay_item_status_show(struct config_item *item, ++ char *page) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ return sprintf(page, "%s\n", ++ overlay->ov_id > 0 ? "applied" : "unapplied"); ++} ++ ++CONFIGFS_ATTR(cfs_overlay_item_, path); ++CONFIGFS_ATTR_RO(cfs_overlay_item_, status); ++ ++static struct configfs_attribute *cfs_overlay_attrs[] = { ++ &cfs_overlay_item_attr_path, ++ &cfs_overlay_item_attr_status, ++ NULL, ++}; ++ ++ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, ++ void *buf, size_t max_count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ pr_debug("%s: buf=%p max_count=%zu\n", __func__, ++ buf, max_count); ++ ++ if (overlay->dtbo == NULL) ++ return 0; ++ ++ /* copy if buffer provided */ ++ if (buf != NULL) { ++ /* the buffer must be large enough */ ++ if (overlay->dtbo_size > max_count) ++ return -ENOSPC; ++ ++ memcpy(buf, overlay->dtbo, overlay->dtbo_size); ++ } ++ ++ return overlay->dtbo_size; ++} ++ ++ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, ++ const void *buf, size_t count) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ int err; ++ ++ /* if it's set do not allow changes */ ++ if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) ++ return -EPERM; ++ ++ /* copy the contents */ ++ overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); ++ if (overlay->dtbo == NULL) ++ return -ENOMEM; ++ ++ overlay->dtbo_size = count; ++ ++ err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size, ++ &overlay->ov_id, NULL); ++ if (err != 0) ++ goto out_err; ++ ++ return count; ++ ++out_err: ++ kfree(overlay->dtbo); ++ overlay->dtbo = NULL; ++ overlay->dtbo_size = 0; ++ overlay->ov_id = 0; ++ ++ return err; ++} ++ ++CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); ++ ++static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { ++ &cfs_overlay_item_attr_dtbo, ++ NULL, ++}; ++ ++static void cfs_overlay_release(struct config_item *item) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ if (overlay->ov_id > 0) ++ of_overlay_remove(&overlay->ov_id); ++ if (overlay->fw) ++ release_firmware(overlay->fw); ++ /* kfree with NULL is safe */ ++ kfree(overlay->dtbo); ++ kfree(overlay); ++} ++ ++static struct configfs_item_operations cfs_overlay_item_ops = { ++ .release = cfs_overlay_release, ++}; ++ ++static struct config_item_type cfs_overlay_type = { ++ .ct_item_ops = &cfs_overlay_item_ops, ++ .ct_attrs = cfs_overlay_attrs, ++ .ct_bin_attrs = cfs_overlay_bin_attrs, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct config_item *cfs_overlay_group_make_item( ++ struct config_group *group, const char *name) ++{ ++ struct cfs_overlay_item *overlay; ++ ++ overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); ++ if (!overlay) ++ return ERR_PTR(-ENOMEM); ++ ++ config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); ++ return &overlay->item; ++} ++ ++static void cfs_overlay_group_drop_item(struct config_group *group, ++ struct config_item *item) ++{ ++ struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); ++ ++ config_item_put(&overlay->item); ++} ++ ++static struct configfs_group_operations overlays_ops = { ++ .make_item = cfs_overlay_group_make_item, ++ .drop_item = cfs_overlay_group_drop_item, ++}; ++ ++static struct config_item_type overlays_type = { ++ .ct_group_ops = &overlays_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++static struct configfs_group_operations of_cfs_ops = { ++ /* empty - we don't allow anything to be created */ ++}; ++ ++static struct config_item_type of_cfs_type = { ++ .ct_group_ops = &of_cfs_ops, ++ .ct_owner = THIS_MODULE, ++}; ++ ++struct config_group of_cfs_overlay_group; ++ ++static struct configfs_subsystem of_cfs_subsys = { ++ .su_group = { ++ .cg_item = { ++ .ci_namebuf = "device-tree", ++ .ci_type = &of_cfs_type, ++ }, ++ }, ++ .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), ++}; ++ ++static int __init of_cfs_init(void) ++{ ++ int ret; ++ ++ pr_info("%s\n", __func__); ++ ++ config_group_init(&of_cfs_subsys.su_group); ++ config_group_init_type_name(&of_cfs_overlay_group, "overlays", ++ &overlays_type); ++ configfs_add_default_group(&of_cfs_overlay_group, ++ &of_cfs_subsys.su_group); ++ ++ ret = configfs_register_subsystem(&of_cfs_subsys); ++ if (ret != 0) { ++ pr_err("%s: failed to register subsys\n", __func__); ++ goto out; ++ } ++ pr_info("%s: OK\n", __func__); ++out: ++ return ret; ++} ++late_initcall(of_cfs_init); diff --git a/target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch b/target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch new file mode 100644 index 0000000000..ade0f467a0 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0061-usr-Add-gen_initramfs_list.sh.patch @@ -0,0 +1,344 @@ +From 7891826f8c2de9ee0f6459cf969f7b082e29b154 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Thu, 1 Jun 2023 23:10:09 -0700 +Subject: [PATCH 061/116] usr: Add gen_initramfs_list.sh + +Add gen_initramfs_list.sh + +Signed-off-by: Hal Feng +--- + usr/gen_initramfs_list.sh | 328 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 328 insertions(+) + create mode 100644 usr/gen_initramfs_list.sh + +--- /dev/null ++++ b/usr/gen_initramfs_list.sh +@@ -0,0 +1,328 @@ ++#!/bin/sh ++# Copyright (C) Martin Schlemmer ++# Copyright (C) 2006 Sam Ravnborg ++# ++# Released under the terms of the GNU GPL ++# ++# Generate a cpio packed initramfs. It uses gen_init_cpio to generate ++# the cpio archive, and then compresses it. ++# The script may also be used to generate the inputfile used for gen_init_cpio ++# This script assumes that gen_init_cpio is located in usr/ directory ++ ++# error out on errors ++set -e ++ ++usage() { ++cat << EOF ++Usage: ++$0 [-o ] [-u ] [-g ] {-d | } ... ++ -o Create compressed initramfs file named using ++ gen_init_cpio and compressor depending on the extension ++ -u User ID to map to user ID 0 (root). ++ is only meaningful if is a ++ directory. "squash" forces all files to uid 0. ++ -g Group ID to map to group ID 0 (root). ++ is only meaningful if is a ++ directory. "squash" forces all files to gid 0. ++ File list or directory for cpio archive. ++ If is a .cpio file it will be used ++ as direct input to initramfs. ++ -d Output the default cpio list. ++ ++All options except -o and -l may be repeated and are interpreted ++sequentially and immediately. -u and -g states are preserved across ++ options so an explicit "-u 0 -g 0" is required ++to reset the root/group mapping. ++EOF ++} ++ ++# awk style field access ++# $1 - field number; rest is argument string ++field() { ++ shift $1 ; echo $1 ++} ++ ++list_default_initramfs() { ++ # echo usr/kinit/kinit ++ : ++} ++ ++default_initramfs() { ++ cat <<-EOF >> ${output} ++ # This is a very simple, default initramfs ++ ++ dir /dev 0755 0 0 ++ nod /dev/console 0600 0 0 c 5 1 ++ dir /root 0700 0 0 ++ # file /kinit usr/kinit/kinit 0755 0 0 ++ # slink /init kinit 0755 0 0 ++ EOF ++} ++ ++filetype() { ++ local argv1="$1" ++ ++ # symlink test must come before file test ++ if [ -L "${argv1}" ]; then ++ echo "slink" ++ elif [ -f "${argv1}" ]; then ++ echo "file" ++ elif [ -d "${argv1}" ]; then ++ echo "dir" ++ elif [ -b "${argv1}" -o -c "${argv1}" ]; then ++ echo "nod" ++ elif [ -p "${argv1}" ]; then ++ echo "pipe" ++ elif [ -S "${argv1}" ]; then ++ echo "sock" ++ else ++ echo "invalid" ++ fi ++ return 0 ++} ++ ++list_print_mtime() { ++ : ++} ++ ++print_mtime() { ++ local my_mtime="0" ++ ++ if [ -e "$1" ]; then ++ my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1) ++ fi ++ ++ echo "# Last modified: ${my_mtime}" >> ${output} ++ echo "" >> ${output} ++} ++ ++list_parse() { ++ if [ -L "$1" ]; then ++ return ++ fi ++ echo "$1" | sed 's/:/\\:/g; s/$/ \\/' ++} ++ ++# for each file print a line in following format ++# ++# for links, devices etc the format differs. See gen_init_cpio for details ++parse() { ++ local location="$1" ++ local name="/${location#${srcdir}}" ++ # change '//' into '/' ++ name=$(echo "$name" | sed -e 's://*:/:g') ++ local mode="$2" ++ local uid="$3" ++ local gid="$4" ++ local ftype=$(filetype "${location}") ++ # remap uid/gid to 0 if necessary ++ [ "$root_uid" = "squash" ] && uid=0 || [ "$uid" -eq "$root_uid" ] && uid=0 ++ [ "$root_gid" = "squash" ] && gid=0 || [ "$gid" -eq "$root_gid" ] && gid=0 ++ local str="${mode} ${uid} ${gid}" ++ ++ [ "${ftype}" = "invalid" ] && return 0 ++ [ "${location}" = "${srcdir}" ] && return 0 ++ ++ case "${ftype}" in ++ "file") ++ str="${ftype} ${name} ${location} ${str}" ++ ;; ++ "nod") ++ local dev=`LC_ALL=C ls -l "${location}"` ++ local maj=`field 5 ${dev}` ++ local min=`field 6 ${dev}` ++ maj=${maj%,} ++ ++ [ -b "${location}" ] && dev="b" || dev="c" ++ ++ str="${ftype} ${name} ${str} ${dev} ${maj} ${min}" ++ ;; ++ "slink") ++ local target=`readlink "${location}"` ++ str="${ftype} ${name} ${target} ${str}" ++ ;; ++ *) ++ str="${ftype} ${name} ${str}" ++ ;; ++ esac ++ ++ echo "${str}" >> ${output} ++ ++ return 0 ++} ++ ++unknown_option() { ++ printf "ERROR: unknown option \"$arg\"\n" >&2 ++ printf "If the filename validly begins with '-', " >&2 ++ printf "then it must be prefixed\n" >&2 ++ printf "by './' so that it won't be interpreted as an option." >&2 ++ printf "\n" >&2 ++ usage >&2 ++ exit 1 ++} ++ ++list_header() { ++ : ++} ++ ++header() { ++ printf "\n#####################\n# $1\n" >> ${output} ++} ++ ++# process one directory (incl sub-directories) ++dir_filelist() { ++ ${dep_list}header "$1" ++ ++ srcdir=$(echo "$1" | sed -e 's://*:/:g') ++ dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" | LANG=C sort) ++ ++ # If $dirlist is only one line, then the directory is empty ++ if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then ++ ${dep_list}print_mtime "$1" ++ ++ echo "${dirlist}" | \ ++ while read x; do ++ ${dep_list}parse ${x} ++ done ++ fi ++} ++ ++# if only one file is specified and it is .cpio file then use it direct as fs ++# if a directory is specified then add all files in given direcotry to fs ++# if a regular file is specified assume it is in gen_initramfs format ++input_file() { ++ source="$1" ++ if [ -f "$1" ]; then ++ ${dep_list}header "$1" ++ is_cpio="$(echo "$1" | sed 's/^.*\.cpio\(\..*\)\{0,1\}/cpio/')" ++ if [ $2 -eq 0 -a ${is_cpio} = "cpio" ]; then ++ cpio_file=$1 ++ echo "$1" | grep -q '^.*\.cpio\..*' && is_cpio_compressed="compressed" ++ [ ! -z ${dep_list} ] && echo "$1" ++ return 0 ++ fi ++ if [ -z ${dep_list} ]; then ++ print_mtime "$1" >> ${output} ++ cat "$1" >> ${output} ++ else ++ echo "$1 \\" ++ cat "$1" | while read type dir file perm ; do ++ if [ "$type" = "file" ]; then ++ echo "$file \\"; ++ fi ++ done ++ fi ++ elif [ -d "$1" ]; then ++ dir_filelist "$1" ++ else ++ echo " ${prog}: Cannot open '$1'" >&2 ++ exit 1 ++ fi ++} ++ ++prog=$0 ++root_uid=0 ++root_gid=0 ++dep_list= ++cpio_file= ++cpio_list= ++output="/dev/stdout" ++output_file="" ++is_cpio_compressed= ++compr="gzip -n -9 -f" ++ ++arg="$1" ++case "$arg" in ++ "-l") # files included in initramfs - used by kbuild ++ dep_list="list_" ++ echo "deps_initramfs := $0 \\" ++ shift ++ ;; ++ "-o") # generate compressed cpio image named $1 ++ shift ++ output_file="$1" ++ cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)" ++ output=${cpio_list} ++ echo "$output_file" | grep -q "\.gz$" \ ++ && [ -x "`which gzip 2> /dev/null`" ] \ ++ && compr="gzip -n -9 -f" ++ echo "$output_file" | grep -q "\.bz2$" \ ++ && [ -x "`which bzip2 2> /dev/null`" ] \ ++ && compr="bzip2 -9 -f" ++ echo "$output_file" | grep -q "\.lzma$" \ ++ && [ -x "`which lzma 2> /dev/null`" ] \ ++ && compr="lzma -9 -f" ++ echo "$output_file" | grep -q "\.xz$" \ ++ && [ -x "`which xz 2> /dev/null`" ] \ ++ && compr="xz --check=crc32 --lzma2=dict=1MiB" ++ echo "$output_file" | grep -q "\.lzo$" \ ++ && [ -x "`which lzop 2> /dev/null`" ] \ ++ && compr="lzop -9 -f" ++ echo "$output_file" | grep -q "\.lz4$" \ ++ && [ -x "`which lz4 2> /dev/null`" ] \ ++ && compr="lz4 -l -9 -f" ++ echo "$output_file" | grep -q "\.cpio$" && compr="cat" ++ shift ++ ;; ++esac ++while [ $# -gt 0 ]; do ++ arg="$1" ++ shift ++ case "$arg" in ++ "-u") # map $1 to uid=0 (root) ++ root_uid="$1" ++ [ "$root_uid" = "-1" ] && root_uid=$(id -u || echo 0) ++ shift ++ ;; ++ "-g") # map $1 to gid=0 (root) ++ root_gid="$1" ++ [ "$root_gid" = "-1" ] && root_gid=$(id -g || echo 0) ++ shift ++ ;; ++ "-d") # display default initramfs list ++ default_list="$arg" ++ ${dep_list}default_initramfs ++ ;; ++ "-h") ++ usage ++ exit 0 ++ ;; ++ *) ++ case "$arg" in ++ "-"*) ++ unknown_option ++ ;; ++ *) # input file/dir - process it ++ input_file "$arg" "$#" ++ ;; ++ esac ++ ;; ++ esac ++done ++ ++# If output_file is set we will generate cpio archive and compress it ++# we are careful to delete tmp files ++if [ ! -z ${output_file} ]; then ++ if [ -z ${cpio_file} ]; then ++ timestamp= ++ if test -n "$KBUILD_BUILD_TIMESTAMP"; then ++ timestamp="$(date -d"$KBUILD_BUILD_TIMESTAMP" +%s || :)" ++ if test -n "$timestamp"; then ++ timestamp="-t $timestamp" ++ fi ++ fi ++ cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)" ++ usr/gen_init_cpio $timestamp ${cpio_list} > ${cpio_tfile} ++ else ++ cpio_tfile=${cpio_file} ++ fi ++ rm ${cpio_list} ++ if [ "${is_cpio_compressed}" = "compressed" ]; then ++ cat ${cpio_tfile} > ${output_file} ++ else ++ (cat ${cpio_tfile} | ${compr} - > ${output_file}) \ ++ || (rm -f ${output_file} ; false) ++ fi ++ [ -z ${cpio_file} ] && rm ${cpio_tfile} ++fi ++exit 0 diff --git a/target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch b/target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch new file mode 100644 index 0000000000..05561f1c51 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0062-i2c-designware-Delete-SMBus-functionalities.patch @@ -0,0 +1,33 @@ +From dcc2827ed6e701a65731c05b0297745559837217 Mon Sep 17 00:00:00 2001 +From: Hal Feng +Date: Fri, 12 May 2023 17:33:20 +0800 +Subject: [PATCH 062/116] i2c: designware: Delete SMBus functionalities + +The driver didn't implement the smbus interface, +so replace the SMBus functionalities with +I2C_FUNC_SMBUS_EMUL. + +Signed-off-by: Hal Feng +--- + drivers/i2c/busses/i2c-designware-core.h | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +--- a/drivers/i2c/busses/i2c-designware-core.h ++++ b/drivers/i2c/busses/i2c-designware-core.h +@@ -18,12 +18,10 @@ + #include + #include + +-#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ +- I2C_FUNC_SMBUS_BYTE | \ +- I2C_FUNC_SMBUS_BYTE_DATA | \ +- I2C_FUNC_SMBUS_WORD_DATA | \ +- I2C_FUNC_SMBUS_BLOCK_DATA | \ +- I2C_FUNC_SMBUS_I2C_BLOCK) ++#define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL \ ++ & ~I2C_FUNC_SMBUS_QUICK \ ++ & ~I2C_FUNC_SMBUS_PROC_CALL \ ++ & ~I2C_FUNC_SMBUS_PEC)) + + #define DW_IC_CON_MASTER BIT(0) + #define DW_IC_CON_SPEED_STD (1 << 1) diff --git a/target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch b/target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch new file mode 100644 index 0000000000..43a7e5ba0d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0063-drivers-mtd-gigadevice-add-gd25lq256d-32M-flash-supp.patch @@ -0,0 +1,26 @@ +From b61cefc6c785aa8a7177a0b535db746fd0047bd8 Mon Sep 17 00:00:00 2001 +From: Ziv Xu +Date: Fri, 19 Jan 2024 15:22:55 +0800 +Subject: [PATCH 063/116] drivers: mtd: gigadevice: add gd25lq256d 32M flash + support + +add gd25lq256d 32M flash support + +Signed-off-by: Ziv Xu +--- + drivers/mtd/spi-nor/gigadevice.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/mtd/spi-nor/gigadevice.c ++++ b/drivers/mtd/spi-nor/gigadevice.c +@@ -66,6 +66,10 @@ static const struct flash_info gigadevic + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, ++ { "gd25lq256d", INFO(0xc86019, 0, 64 * 1024, 512) ++ FLAGS( SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_QUAD_PP) ++ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | ++ SPI_NOR_QUAD_READ) }, + { "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512) + PARSE_SFDP + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6) diff --git a/target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch b/target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch new file mode 100644 index 0000000000..b733110f7d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0064-driver-mailbox-Add-mailbox-driver.patch @@ -0,0 +1,808 @@ +From 76bc13aa12bd111f5da01e107f8d487b20b5a40c Mon Sep 17 00:00:00 2001 +From: "shanlong.li" +Date: Thu, 8 Jun 2023 00:07:15 -0700 +Subject: [PATCH 064/116] driver: mailbox: Add mailbox driver + +Add mailbox driver. + +Signed-off-by: shanlong.li +Signed-off-by: Hal Feng +--- + drivers/mailbox/Kconfig | 13 + + drivers/mailbox/Makefile | 4 + + drivers/mailbox/starfive_mailbox-test.c | 407 ++++++++++++++++++++++++ + drivers/mailbox/starfive_mailbox.c | 347 ++++++++++++++++++++ + 4 files changed, 771 insertions(+) + create mode 100644 drivers/mailbox/starfive_mailbox-test.c + create mode 100644 drivers/mailbox/starfive_mailbox.c + +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -295,4 +295,17 @@ config QCOM_IPCC + acts as an interrupt controller for receiving interrupts from clients. + Say Y here if you want to build this driver. + ++config STARFIVE_MBOX ++ tristate "Platform Starfive Mailbox" ++ depends on OF ++ help ++ Say Y here if you want to build a platform specific variant RISCV ++ controller driver. ++ ++config STARFIVE_MBOX_TEST ++ tristate "Starfive Mailbox Test Client" ++ depends on OF ++ depends on HAS_IOMEM ++ help ++ Test client to help with testing new Controller driver implementations. + endif +--- a/drivers/mailbox/Makefile ++++ b/drivers/mailbox/Makefile +@@ -62,3 +62,7 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox + obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o + + obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o ++ ++obj-$(CONFIG_STARFIVE_MBOX) += starfive_mailbox.o ++ ++obj-$(CONFIG_STARFIVE_MBOX_TEST) += starfive_mailbox-test.o +--- /dev/null ++++ b/drivers/mailbox/starfive_mailbox-test.c +@@ -0,0 +1,407 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (C) 2015 ST Microelectronics ++ * ++ * Author: Lee Jones ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define MBOX_MAX_SIG_LEN 8 ++#define MBOX_MAX_MSG_LEN 16 ++#define MBOX_BYTES_PER_LINE 16 ++#define MBOX_HEXDUMP_LINE_LEN ((MBOX_BYTES_PER_LINE * 4) + 2) ++#define MBOX_HEXDUMP_MAX_LEN (MBOX_HEXDUMP_LINE_LEN * (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) ++ ++static bool mbox_data_ready; ++ ++struct mbox_test_device { ++ struct device *dev; ++ void __iomem *tx_mmio; ++ void __iomem *rx_mmio; ++ struct mbox_chan *tx_channel; ++ struct mbox_chan *rx_channel; ++ char *rx_buffer; ++ char *signal; ++ char *message; ++ spinlock_t lock; ++ wait_queue_head_t waitq; ++ struct fasync_struct *async_queue; ++ struct dentry *root_debugfs_dir; ++}; ++ ++static ssize_t mbox_test_signal_write(struct file *filp, ++ const char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ ++ if (!tdev->tx_channel) { ++ dev_err(tdev->dev, "Channel cannot do Tx\n"); ++ return -EINVAL; ++ } ++ ++ if (count > MBOX_MAX_SIG_LEN) { ++ dev_err(tdev->dev, ++ "Signal length %zd greater than max allowed %d\n", ++ count, MBOX_MAX_SIG_LEN); ++ return -EINVAL; ++ } ++ ++ /* Only allocate memory if we need to */ ++ if (!tdev->signal) { ++ tdev->signal = kzalloc(MBOX_MAX_SIG_LEN, GFP_KERNEL); ++ if (!tdev->signal) ++ return -ENOMEM; ++ } ++ ++ if (copy_from_user(tdev->signal, userbuf, count)) { ++ kfree(tdev->signal); ++ tdev->signal = NULL; ++ return -EFAULT; ++ } ++ ++ return count; ++} ++ ++static const struct file_operations mbox_test_signal_ops = { ++ .write = mbox_test_signal_write, ++ .open = simple_open, ++ .llseek = generic_file_llseek, ++}; ++ ++static int mbox_test_message_fasync(int fd, struct file *filp, int on) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ ++ return fasync_helper(fd, filp, on, &tdev->async_queue); ++} ++ ++static ssize_t mbox_test_message_write(struct file *filp, ++ const char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ void *data; ++ int ret; ++ ++ if (!tdev->tx_channel) { ++ dev_err(tdev->dev, "Channel cannot do Tx\n"); ++ return -EINVAL; ++ } ++ ++ if (count > MBOX_MAX_MSG_LEN) { ++ dev_err(tdev->dev, ++ "Message length %zd greater than max allowed %d\n", ++ count, MBOX_MAX_MSG_LEN); ++ return -EINVAL; ++ } ++ ++ tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL); ++ if (!tdev->message) ++ return -ENOMEM; ++ ++ ret = copy_from_user(tdev->message, userbuf, count); ++ if (ret) { ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ if (tdev->tx_mmio && tdev->signal) { ++ print_hex_dump_bytes("Client: Sending: Signal: ", DUMP_PREFIX_ADDRESS, ++ tdev->signal, MBOX_MAX_SIG_LEN); ++ ++ data = tdev->signal; ++ } else ++ data = tdev->message; ++ ++ print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS, ++ tdev->message, MBOX_MAX_MSG_LEN); ++ ++ ret = mbox_send_message(tdev->tx_channel, data); ++ mbox_chan_txdone(tdev->tx_channel, ret); ++ if (ret < 0) ++ dev_err(tdev->dev, "Failed to send message via mailbox\n"); ++ ++out: ++ kfree(tdev->signal); ++ kfree(tdev->message); ++ tdev->signal = NULL; ++ ++ return ret < 0 ? ret : count; ++} ++ ++static bool mbox_test_message_data_ready(struct mbox_test_device *tdev) ++{ ++ bool data_ready; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tdev->lock, flags); ++ data_ready = mbox_data_ready; ++ spin_unlock_irqrestore(&tdev->lock, flags); ++ ++ return data_ready; ++} ++ ++static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf, ++ size_t count, loff_t *ppos) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ unsigned long flags; ++ char *touser, *ptr; ++ int ret; ++ ++ touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL); ++ if (!touser) ++ return -ENOMEM; ++ ++ if (!tdev->rx_channel) { ++ ret = snprintf(touser, 20, "\n"); ++ ret = simple_read_from_buffer(userbuf, count, ppos, ++ touser, ret); ++ goto kfree_err; ++ } ++ ++ do { ++ if (mbox_test_message_data_ready(tdev)) ++ break; ++ ++ if (filp->f_flags & O_NONBLOCK) { ++ ret = -EAGAIN; ++ goto waitq_err; ++ } ++ ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ goto waitq_err; ++ } ++ schedule(); ++ ++ } while (1); ++ ++ spin_lock_irqsave(&tdev->lock, flags); ++ ++ ptr = tdev->rx_buffer; ++ ++ mbox_data_ready = false; ++ ++ spin_unlock_irqrestore(&tdev->lock, flags); ++ if (copy_to_user((void __user *)userbuf, ptr, 4)) ++ ret = -EFAULT; ++ ++waitq_err: ++ __set_current_state(TASK_RUNNING); ++kfree_err: ++ kfree(touser); ++ return ret; ++} ++ ++static __poll_t ++mbox_test_message_poll(struct file *filp, struct poll_table_struct *wait) ++{ ++ struct mbox_test_device *tdev = filp->private_data; ++ ++ poll_wait(filp, &tdev->waitq, wait); ++ ++ if (mbox_test_message_data_ready(tdev)) ++ return EPOLLIN | EPOLLRDNORM; ++ return 0; ++} ++ ++static const struct file_operations mbox_test_message_ops = { ++ .write = mbox_test_message_write, ++ .read = mbox_test_message_read, ++ .fasync = mbox_test_message_fasync, ++ .poll = mbox_test_message_poll, ++ .open = simple_open, ++ .llseek = generic_file_llseek, ++}; ++ ++static int mbox_test_add_debugfs(struct platform_device *pdev, ++ struct mbox_test_device *tdev) ++{ ++ if (!debugfs_initialized()) ++ return 0; ++ ++ tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL); ++ if (!tdev->root_debugfs_dir) { ++ dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n"); ++ return -EINVAL; ++ } ++ ++ debugfs_create_file("message", 0600, tdev->root_debugfs_dir, ++ tdev, &mbox_test_message_ops); ++ ++ debugfs_create_file("signal", 0200, tdev->root_debugfs_dir, ++ tdev, &mbox_test_signal_ops); ++ ++ return 0; ++} ++ ++static void mbox_test_receive_message(struct mbox_client *client, void *message) ++{ ++ struct mbox_test_device *tdev = dev_get_drvdata(client->dev); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tdev->lock, flags); ++ if (tdev->rx_mmio) { ++ memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN); ++ print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS, ++ tdev->rx_buffer, MBOX_MAX_MSG_LEN); ++ } else if (message) { ++ print_hex_dump_bytes("Client: Received [API]: ", DUMP_PREFIX_ADDRESS, ++ message, MBOX_MAX_MSG_LEN); ++ memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN); ++ } ++ mbox_data_ready = true; ++ spin_unlock_irqrestore(&tdev->lock, flags); ++} ++ ++static void mbox_test_prepare_message(struct mbox_client *client, void *message) ++{ ++ struct mbox_test_device *tdev = dev_get_drvdata(client->dev); ++ ++ if (tdev->tx_mmio) { ++ if (tdev->signal) ++ memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN); ++ else ++ memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN); ++ } ++} ++ ++static struct mbox_chan * ++mbox_test_request_channel(struct platform_device *pdev, const char *name) ++{ ++ struct mbox_client *client; ++ struct mbox_chan *channel; ++ ++ client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL); ++ if (!client) ++ return ERR_PTR(-ENOMEM); ++ ++ client->dev = &pdev->dev; ++ client->rx_callback = mbox_test_receive_message; ++ client->tx_prepare = mbox_test_prepare_message; ++ client->tx_block = false; ++ client->knows_txdone = false; ++ client->tx_tout = 500; ++ ++ channel = mbox_request_channel_byname(client, name); ++ if (IS_ERR(channel)) { ++ dev_warn(&pdev->dev, "Failed to request %s channel\n", name); ++ return NULL; ++ } ++ ++ return channel; ++} ++ ++static int mbox_test_probe(struct platform_device *pdev) ++{ ++ struct mbox_test_device *tdev; ++ struct resource *res; ++ resource_size_t size; ++ int ret; ++ ++ tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); ++ if (!tdev) ++ return -ENOMEM; ++ ++ /* It's okay for MMIO to be NULL */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res); ++ if (PTR_ERR(tdev->tx_mmio) == -EBUSY) { ++ /* if reserved area in SRAM, try just ioremap */ ++ size = resource_size(res); ++ tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size); ++ } else if (IS_ERR(tdev->tx_mmio)) { ++ tdev->tx_mmio = NULL; ++ } ++ ++ /* If specified, second reg entry is Rx MMIO */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res); ++ if (PTR_ERR(tdev->rx_mmio) == -EBUSY) { ++ size = resource_size(res); ++ tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size); ++ } else if (IS_ERR(tdev->rx_mmio)) { ++ tdev->rx_mmio = tdev->tx_mmio; ++ } ++ ++ tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); ++ tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); ++ ++ if (!tdev->tx_channel && !tdev->rx_channel) ++ return -EPROBE_DEFER; ++ ++ /* If Rx is not specified but has Rx MMIO, then Rx = Tx */ ++ if (!tdev->rx_channel && (tdev->rx_mmio != tdev->tx_mmio)) ++ tdev->rx_channel = tdev->tx_channel; ++ ++ tdev->dev = &pdev->dev; ++ platform_set_drvdata(pdev, tdev); ++ ++ spin_lock_init(&tdev->lock); ++ ++ if (tdev->rx_channel) { ++ tdev->rx_buffer = devm_kzalloc(&pdev->dev, ++ MBOX_MAX_MSG_LEN, GFP_KERNEL); ++ if (!tdev->rx_buffer) ++ return -ENOMEM; ++ } ++ ++ ret = mbox_test_add_debugfs(pdev, tdev); ++ if (ret) ++ return ret; ++ ++ dev_info(&pdev->dev, "Successfully registered\n"); ++ ++ return 0; ++} ++ ++static int mbox_test_remove(struct platform_device *pdev) ++{ ++ struct mbox_test_device *tdev = platform_get_drvdata(pdev); ++ ++ debugfs_remove_recursive(tdev->root_debugfs_dir); ++ ++ if (tdev->tx_channel) ++ mbox_free_channel(tdev->tx_channel); ++ if (tdev->rx_channel) ++ mbox_free_channel(tdev->rx_channel); ++ ++ return 0; ++} ++ ++static const struct of_device_id mbox_test_match[] = { ++ { .compatible = "starfive,mailbox-test" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mbox_test_match); ++ ++static struct platform_driver mbox_test_driver = { ++ .driver = { ++ .name = "mailbox_test", ++ .of_match_table = mbox_test_match, ++ }, ++ .probe = mbox_test_probe, ++ .remove = mbox_test_remove, ++}; ++module_platform_driver(mbox_test_driver); ++ ++MODULE_DESCRIPTION("Generic Mailbox Testing Facility"); ++MODULE_AUTHOR("Lee Jones ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mailbox.h" ++ ++#define MBOX_CHAN_MAX 4 ++ ++#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x10)) ++#define MBOX_IRQ_REG 0x00 ++#define MBOX_SET_REG 0x04 ++#define MBOX_CLR_REG 0x08 ++#define MBOX_CMD_REG 0x0c ++#define MBC_PEND_SMRY 0x100 ++ ++typedef enum { ++ MAILBOX_CORE_U7 = 0, ++ MAILBOX_CORE_HIFI4, ++ MAILBOX_CORE_E2, ++ MAILBOX_CORE_RSVD0, ++ MAILBOX_CORE_NUM, ++} mailbox_core_t; ++ ++struct mailbox_irq_name_c{ ++ int id; ++ char name[16]; ++}; ++ ++static const struct mailbox_irq_name_c irq_peer_name[MBOX_CHAN_MAX] = { ++ {MAILBOX_CORE_U7, "u74_core"}, ++ {MAILBOX_CORE_HIFI4, "hifi4_core"}, ++ {MAILBOX_CORE_E2, "e24_core"}, ++ {MAILBOX_CORE_RSVD0, "" }, ++}; ++ ++/** ++ * starfive mailbox channel information ++ * ++ * A channel can be used for TX or RX, it can trigger remote ++ * processor interrupt to notify remote processor and can receive ++ * interrupt if has incoming message. ++ * ++ * @dst_irq: Interrupt vector for remote processor ++ * @core_id: id for remote processor ++ */ ++struct starfive_chan_info { ++ unsigned int dst_irq; ++ mailbox_core_t core_id; ++}; ++ ++/** ++ * starfive mailbox controller data ++ * ++ * Mailbox controller includes 4 channels and can allocate ++ * channel for message transferring. ++ * ++ * @dev: Device to which it is attached ++ * @base: Base address of the register mapping region ++ * @chan: Representation of channels in mailbox controller ++ * @mchan: Representation of channel info ++ * @controller: Representation of a communication channel controller ++ */ ++struct starfive_mbox { ++ struct device *dev; ++ void __iomem *base; ++ struct mbox_chan chan[MBOX_CHAN_MAX]; ++ struct starfive_chan_info mchan[MBOX_CHAN_MAX]; ++ struct mbox_controller controller; ++ struct clk *clk; ++ struct reset_control *rst_rresetn; ++}; ++ ++static struct starfive_mbox *to_starfive_mbox(struct mbox_controller *mbox) ++{ ++ return container_of(mbox, struct starfive_mbox, controller); ++} ++ ++static struct mbox_chan * ++starfive_of_mbox_index_xlate(struct mbox_controller *mbox, ++ const struct of_phandle_args *sp) ++{ ++ struct starfive_mbox *sbox; ++ ++ int ind = sp->args[0]; ++ int core_id = sp->args[1]; ++ ++ if (ind >= mbox->num_chans || core_id >= MAILBOX_CORE_NUM) ++ return ERR_PTR(-EINVAL); ++ ++ sbox = to_starfive_mbox(mbox); ++ ++ sbox->mchan[ind].core_id = core_id; ++ ++ return &mbox->chans[ind]; ++} ++ ++static irqreturn_t starfive_rx_irq_handler(int irq, void *p) ++{ ++ struct mbox_chan *chan = p; ++ unsigned long ch = (unsigned long)chan->con_priv; ++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox); ++ void __iomem *base = MBOX_BASE(mbox, ch); ++ u32 val; ++ ++ val = readl(base + MBOX_CMD_REG); ++ if (!val) ++ return IRQ_NONE; ++ ++ mbox_chan_received_data(chan, (void *)&val); ++ writel(val, base + MBOX_CLR_REG); ++ return IRQ_HANDLED; ++} ++ ++static int starfive_mbox_check_state(struct mbox_chan *chan) ++{ ++ unsigned long ch = (unsigned long)chan->con_priv; ++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox); ++ unsigned long irq_flag = IRQF_SHARED; ++ long ret = 0; ++ ++ pm_runtime_get_sync(mbox->dev); ++ /* MAILBOX should be with IRQF_NO_SUSPEND set */ ++ if (!mbox->dev->pm_domain) ++ irq_flag |= IRQF_NO_SUSPEND; ++ ++ /* Mailbox is idle so directly bail out */ ++ if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch)) ++ return -EBUSY; ++ ++ if (mbox->mchan[ch].dst_irq > 0) { ++ dev_dbg(mbox->dev, "%s: host IRQ = %d, ch:%ld", __func__, mbox->mchan[ch].dst_irq, ch); ++ ret = devm_request_irq(mbox->dev, mbox->mchan[ch].dst_irq, starfive_rx_irq_handler, ++ irq_flag, irq_peer_name[ch].name, chan); ++ if (ret < 0) ++ dev_err(mbox->dev, "request_irq %d failed\n", mbox->mchan[ch].dst_irq); ++ } ++ ++ return ret; ++} ++ ++static int starfive_mbox_startup(struct mbox_chan *chan) ++{ ++ return starfive_mbox_check_state(chan); ++} ++ ++static void starfive_mbox_shutdown(struct mbox_chan *chan) ++{ ++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox); ++ unsigned long ch = (unsigned long)chan->con_priv; ++ void __iomem *base = MBOX_BASE(mbox, ch); ++ ++ writel(0x0, base + MBOX_IRQ_REG); ++ writel(0x0, base + MBOX_CLR_REG); ++ ++ if (mbox->mchan[ch].dst_irq > 0) ++ devm_free_irq(mbox->dev, mbox->mchan[ch].dst_irq, chan); ++ pm_runtime_put_sync(mbox->dev); ++} ++ ++static int starfive_mbox_send_data(struct mbox_chan *chan, void *msg) ++{ ++ unsigned long ch = (unsigned long)chan->con_priv; ++ struct starfive_mbox *mbox = to_starfive_mbox(chan->mbox); ++ struct starfive_chan_info *mchan = &mbox->mchan[ch]; ++ void __iomem *base = MBOX_BASE(mbox, ch); ++ u32 *buf = msg; ++ ++ /* Ensure channel is released */ ++ if (readl(mbox->base + MBC_PEND_SMRY) & BIT(ch)) { ++ pr_debug("%s:%d. busy\n", __func__, __LINE__); ++ return -EBUSY; ++ } ++ ++ /* Clear mask for destination interrupt */ ++ writel(BIT(mchan->core_id), base + MBOX_IRQ_REG); ++ ++ /* Fill message data */ ++ writel(*buf, base + MBOX_SET_REG); ++ return 0; ++} ++ ++static struct mbox_chan_ops starfive_mbox_ops = { ++ .startup = starfive_mbox_startup, ++ .send_data = starfive_mbox_send_data, ++ .shutdown = starfive_mbox_shutdown, ++}; ++ ++static const struct of_device_id starfive_mbox_of_match[] = { ++ { .compatible = "starfive,mail_box",}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, starfive_mbox_of_match); ++ ++void starfive_mailbox_init(struct starfive_mbox *mbox) ++{ ++ mbox->clk = devm_clk_get_optional(mbox->dev, "clk_apb"); ++ if (IS_ERR(mbox->clk)) { ++ dev_err(mbox->dev, "failed to get mailbox\n"); ++ return; ++ } ++ ++ mbox->rst_rresetn = devm_reset_control_get_exclusive(mbox->dev, "mbx_rre"); ++ if (IS_ERR(mbox->rst_rresetn)) { ++ dev_err(mbox->dev, "failed to get mailbox reset\n"); ++ return; ++ } ++ ++ clk_prepare_enable(mbox->clk); ++ reset_control_deassert(mbox->rst_rresetn); ++} ++ ++static int starfive_mbox_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct starfive_mbox *mbox; ++ struct mbox_chan *chan; ++ struct resource *res; ++ unsigned long ch; ++ int err; ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (!mbox) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ mbox->base = devm_ioremap_resource(dev, res); ++ mbox->dev = dev; ++ ++ if (IS_ERR(mbox->base)) ++ return PTR_ERR(mbox->base); ++ ++ starfive_mailbox_init(mbox); ++ ++ mbox->controller.dev = dev; ++ mbox->controller.chans = mbox->chan; ++ mbox->controller.num_chans = MBOX_CHAN_MAX; ++ mbox->controller.ops = &starfive_mbox_ops; ++ mbox->controller.of_xlate = starfive_of_mbox_index_xlate; ++ mbox->controller.txdone_irq = true; ++ mbox->controller.txdone_poll = false; ++ ++ /* Initialize mailbox channel data */ ++ chan = mbox->chan; ++ for (ch = 0; ch < MBOX_CHAN_MAX; ch++) { ++ mbox->mchan[ch].dst_irq = 0; ++ mbox->mchan[ch].core_id = (mailbox_core_t)ch; ++ chan[ch].con_priv = (void *)ch; ++ } ++ mbox->mchan[MAILBOX_CORE_HIFI4].dst_irq = platform_get_irq(pdev, 0); ++ mbox->mchan[MAILBOX_CORE_E2].dst_irq = platform_get_irq(pdev, 1); ++ ++ err = mbox_controller_register(&mbox->controller); ++ if (err) { ++ dev_err(dev, "Failed to register mailbox %d\n", err); ++ return err; ++ } ++ ++ platform_set_drvdata(pdev, mbox); ++ dev_info(dev, "Mailbox enabled\n"); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ return 0; ++} ++ ++static int starfive_mbox_remove(struct platform_device *pdev) ++{ ++ struct starfive_mbox *mbox = platform_get_drvdata(pdev); ++ ++ mbox_controller_unregister(&mbox->controller); ++ devm_clk_put(mbox->dev, mbox->clk); ++ pm_runtime_disable(mbox->dev); ++ ++ return 0; ++} ++ ++static int __maybe_unused starfive_mbox_suspend(struct device *dev) ++{ ++ struct starfive_mbox *mbox = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(mbox->clk); ++ ++ return 0; ++} ++ ++static int __maybe_unused starfive_mbox_resume(struct device *dev) ++{ ++ struct starfive_mbox *mbox = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_prepare_enable(mbox->clk); ++ if (ret) ++ dev_err(dev, "failed to enable clock\n"); ++ ++ return ret; ++} ++ ++static const struct dev_pm_ops starfive_mbox_pm_ops = { ++ .suspend = starfive_mbox_suspend, ++ .resume = starfive_mbox_resume, ++ SET_RUNTIME_PM_OPS(starfive_mbox_suspend, starfive_mbox_resume, NULL) ++}; ++static struct platform_driver starfive_mbox_driver = { ++ .probe = starfive_mbox_probe, ++ .remove = starfive_mbox_remove, ++ .driver = { ++ .name = "mailbox", ++ .of_match_table = starfive_mbox_of_match, ++ .pm = &starfive_mbox_pm_ops, ++ }, ++}; ++ ++static int __init starfive_mbox_init(void) ++{ ++ return platform_driver_register(&starfive_mbox_driver); ++} ++core_initcall(starfive_mbox_init); ++ ++static void __exit starfive_mbox_exit(void) ++{ ++ platform_driver_unregister(&starfive_mbox_driver); ++} ++module_exit(starfive_mbox_exit); ++ ++MODULE_DESCRIPTION("StarFive Mailbox Controller driver"); ++MODULE_AUTHOR("Shanlong Li "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch b/target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch new file mode 100644 index 0000000000..0257cb004b --- /dev/null +++ b/target/linux/starfive/patches-6.6/0065-driver-rtc-Add-StarFive-JH7110-rtc-driver.patch @@ -0,0 +1,789 @@ +From 0f44bd6bec708782f38bba4d03deecf927d1c83d Mon Sep 17 00:00:00 2001 +From: "ziv.xu" +Date: Fri, 9 Jun 2023 15:31:53 +0800 +Subject: [PATCH 065/116] driver: rtc: Add StarFive JH7110 rtc driver + +Add RTC driver and support for StarFive JH7110 SoC. + +Signed-off-by: ziv.xu +Signed-off-by: Hal Feng +--- + drivers/rtc/Kconfig | 8 + + drivers/rtc/Makefile | 1 + + drivers/rtc/rtc-starfive.c | 743 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 752 insertions(+) + create mode 100644 drivers/rtc/rtc-starfive.c + +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1327,6 +1327,14 @@ config RTC_DRV_NTXEC + embedded controller found in certain e-book readers designed by the + original design manufacturer Netronix. + ++config RTC_DRV_STARFIVE ++ tristate "StarFive 32.768k-RTC" ++ depends on ARCH_STARFIVE ++ depends on OF ++ help ++ If you say Y here you will get support for the RTC found on ++ StarFive SOCS. ++ + comment "on-CPU RTC drivers" + + config RTC_DRV_ASM9260 +--- a/drivers/rtc/Makefile ++++ b/drivers/rtc/Makefile +@@ -163,6 +163,7 @@ obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o + obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o + obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o + obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o ++obj-$(CONFIG_RTC_DRV_STARFIVE) += rtc-starfive.o + obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o + obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o + obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o +--- /dev/null ++++ b/drivers/rtc/rtc-starfive.c +@@ -0,0 +1,743 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * RTC driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2021 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Registers */ ++#define SFT_RTC_CFG 0x00 ++#define SFT_RTC_SW_CAL_VALUE 0x04 ++#define SFT_RTC_HW_CAL_CFG 0x08 ++#define SFT_RTC_CMP_CFG 0x0C ++#define SFT_RTC_IRQ_EN 0x10 ++#define SFT_RTC_IRQ_EVEVT 0x14 ++#define SFT_RTC_IRQ_STATUS 0x18 ++#define SFT_RTC_CAL_VALUE 0x24 ++#define SFT_RTC_CFG_TIME 0x28 ++#define SFT_RTC_CFG_DATE 0x2C ++#define SFT_RTC_ACT_TIME 0x34 ++#define SFT_RTC_ACT_DATE 0x38 ++#define SFT_RTC_TIME 0x3C ++#define SFT_RTC_DATE 0x40 ++#define SFT_RTC_TIME_LATCH 0x44 ++#define SFT_RTC_DATE_LATCH 0x48 ++ ++/* RTC_CFG */ ++#define RTC_CFG_ENABLE_SHIFT 0 /* RW: RTC Enable. */ ++#define RTC_CFG_CAL_EN_HW_SHIFT 1 /* RW: Enable of hardware calibretion. */ ++#define RTC_CFG_CAL_SEL_SHIFT 2 /* RW: select the hw/sw calibretion mode.*/ ++#define RTC_CFG_HOUR_MODE_SHIFT 3 /* RW: time hour mode. 24h|12h */ ++ ++/* RTC_SW_CAL_VALUE */ ++#define RTC_SW_CAL_VALUE_MASK GENMASK(15, 0) ++#define RTC_SW_CAL_MAX RTC_SW_CAL_VALUE_MASK ++#define RTC_SW_CAL_MIN 0 ++#define RTC_TICKS_PER_SEC 32768 /* Number of ticks per second */ ++#define RTC_PPB_MULT 1000000000LL /* Multiplier for ppb conversions */ ++ ++/* RTC_HW_CAL_CFG */ ++#define RTC_HW_CAL_REF_SEL_SHIFT 0 ++#define RTC_HW_CAL_FRQ_SEL_SHIFT 1 ++ ++/* IRQ_EN/IRQ_EVEVT/IRQ_STATUS */ ++#define RTC_IRQ_CAL_START BIT(0) ++#define RTC_IRQ_CAL_FINISH BIT(1) ++#define RTC_IRQ_CMP BIT(2) ++#define RTC_IRQ_1SEC BIT(3) ++#define RTC_IRQ_ALAEM BIT(4) ++#define RTC_IRQ_EVT_UPDATE_PSE BIT(31) /* WO: Enable of update time&&date, IRQ_EVEVT only */ ++#define RTC_IRQ_ALL (RTC_IRQ_CAL_START \ ++ | RTC_IRQ_CAL_FINISH \ ++ | RTC_IRQ_CMP \ ++ | RTC_IRQ_1SEC \ ++ | RTC_IRQ_ALAEM) ++ ++/* CAL_VALUE */ ++#define RTC_CAL_VALUE_MASK GENMASK(15, 0) ++ ++/* CFG_TIME/ACT_TIME/RTC_TIME */ ++#define TIME_SEC_MASK GENMASK(6, 0) ++#define TIME_MIN_MASK GENMASK(13, 7) ++#define TIME_HOUR_MASK GENMASK(20, 14) ++ ++/* CFG_DATE/ACT_DATE/RTC_DATE */ ++#define DATE_DAY_MASK GENMASK(5, 0) ++#define DATE_MON_MASK GENMASK(10, 6) ++#define DATE_YEAR_MASK GENMASK(18, 11) ++ ++#define INT_TIMEOUT_US 180 ++ ++enum RTC_HOUR_MODE { ++ RTC_HOUR_MODE_12H = 0, ++ RTC_HOUR_MODE_24H = 1 ++}; ++ ++enum RTC_CAL_MODE { ++ RTC_CAL_MODE_SW = 0, ++ RTC_CAL_MODE_HW = 1 ++}; ++ ++enum RTC_HW_CAL_REF_MODE { ++ RTC_CAL_CLK_REF = 0, ++ RTC_CAL_CLK_MARK = 1 ++}; ++ ++static const unsigned long refclk_list[] = { ++ 1000000, ++ 2000000, ++ 4000000, ++ 5927000, ++ 6000000, ++ 7200000, ++ 8000000, ++ 10250000, ++ 11059200, ++ 12000000, ++ 12288000, ++ 13560000, ++ 16000000, ++ 19200000, ++ 20000000, ++ 22118000, ++ 24000000, ++ 24567000, ++ 25000000, ++ 26000000, ++ 27000000, ++ 30000000, ++ 32000000, ++ 33868800, ++ 36000000, ++ 36860000, ++ 40000000, ++ 44000000, ++ 50000000, ++ 54000000, ++ 28224000, ++ 28000000, ++}; ++ ++struct sft_rtc { ++ struct rtc_device *rtc_dev; ++ struct completion cal_done; ++ struct completion onesec_done; ++ struct clk *pclk; ++ struct clk *cal_clk; ++ struct reset_control *rst_array; ++ int hw_cal_map; ++ void __iomem *regs; ++ int rtc_irq; ++ int ms_pulse_irq; ++ int one_sec_pulse_irq; ++}; ++ ++static inline void sft_rtc_set_enabled(struct sft_rtc *srtc, bool enabled) ++{ ++ u32 val; ++ ++ if (enabled) { ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val |= BIT(RTC_CFG_ENABLE_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_CFG); ++ } else { ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val &= ~BIT(RTC_CFG_ENABLE_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_CFG); ++ } ++} ++ ++static inline bool sft_rtc_get_enabled(struct sft_rtc *srtc) ++{ ++ return !!(readl(srtc->regs + SFT_RTC_CFG) & BIT(RTC_CFG_ENABLE_SHIFT)); ++} ++ ++static inline void sft_rtc_set_mode(struct sft_rtc *srtc, enum RTC_HOUR_MODE mode) ++{ ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val |= mode << RTC_CFG_HOUR_MODE_SHIFT; ++ writel(val, srtc->regs + SFT_RTC_CFG); ++} ++ ++static inline int sft_rtc_irq_enable(struct sft_rtc *srtc, u32 irq, bool enable) ++{ ++ u32 val; ++ ++ if (!(irq & RTC_IRQ_ALL)) ++ return -EINVAL; ++ ++ if (enable) { ++ val = readl(srtc->regs + SFT_RTC_IRQ_EN); ++ val |= irq; ++ writel(val, srtc->regs + SFT_RTC_IRQ_EN); ++ } else { ++ val = readl(srtc->regs + SFT_RTC_IRQ_EN); ++ val &= ~irq; ++ writel(val, srtc->regs + SFT_RTC_IRQ_EN); ++ } ++ return 0; ++} ++ ++static inline void ++sft_rtc_set_cal_hw_enable(struct sft_rtc *srtc, bool enable) ++{ ++ u32 val; ++ ++ if (enable) { ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val |= BIT(RTC_CFG_CAL_EN_HW_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_CFG); ++ } else { ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val &= ~BIT(RTC_CFG_CAL_EN_HW_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_CFG); ++ } ++} ++ ++static inline void ++sft_rtc_set_cal_mode(struct sft_rtc *srtc, enum RTC_CAL_MODE mode) ++{ ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_CFG); ++ val |= mode << RTC_CFG_CAL_SEL_SHIFT; ++ writel(val, srtc->regs + SFT_RTC_CFG); ++} ++ ++static int sft_rtc_get_hw_calclk(struct device *dev, unsigned long freq) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(refclk_list); i++) ++ if (refclk_list[i] == freq) ++ return i; ++ ++ dev_err(dev, "refclk: %ldHz do not support.\n", freq); ++ return -EINVAL; ++} ++ ++static inline void sft_rtc_reg2time(struct rtc_time *tm, u32 reg) ++{ ++ tm->tm_hour = bcd2bin(FIELD_GET(TIME_HOUR_MASK, reg)); ++ tm->tm_min = bcd2bin(FIELD_GET(TIME_MIN_MASK, reg)); ++ tm->tm_sec = bcd2bin(FIELD_GET(TIME_SEC_MASK, reg)); ++} ++ ++static inline void sft_rtc_reg2date(struct rtc_time *tm, u32 reg) ++{ ++ tm->tm_year = bcd2bin(FIELD_GET(DATE_YEAR_MASK, reg)) + 100; ++ tm->tm_mon = bcd2bin(FIELD_GET(DATE_MON_MASK, reg)) - 1; ++ tm->tm_mday = bcd2bin(FIELD_GET(DATE_DAY_MASK, reg)); ++} ++ ++static inline u32 sft_rtc_time2reg(struct rtc_time *tm) ++{ ++ return FIELD_PREP(TIME_HOUR_MASK, bin2bcd(tm->tm_hour)) | ++ FIELD_PREP(TIME_MIN_MASK, bin2bcd(tm->tm_min)) | ++ FIELD_PREP(TIME_SEC_MASK, bin2bcd(tm->tm_sec)); ++} ++ ++static inline u32 sft_rtc_date2reg(struct rtc_time *tm) ++{ ++ return FIELD_PREP(DATE_YEAR_MASK, bin2bcd(tm->tm_year - 100)) | ++ FIELD_PREP(DATE_MON_MASK, bin2bcd(tm->tm_mon + 1)) | ++ FIELD_PREP(DATE_DAY_MASK, bin2bcd(tm->tm_mday)); ++} ++ ++static inline void sft_rtc_update_pulse(struct sft_rtc *srtc) ++{ ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_IRQ_EVEVT); ++ val |= RTC_IRQ_EVT_UPDATE_PSE; ++ writel(val, srtc->regs + SFT_RTC_IRQ_EVEVT); ++} ++ ++static irqreturn_t sft_rtc_irq_handler(int irq, void *data) ++{ ++ struct sft_rtc *srtc = data; ++ struct timerqueue_node *next; ++ u32 irq_flags = 0; ++ u32 irq_mask = 0; ++ u32 val; ++ int ret = 0; ++ ++ val = readl(srtc->regs + SFT_RTC_IRQ_EVEVT); ++ if (val & RTC_IRQ_CAL_START) ++ irq_mask |= RTC_IRQ_CAL_START; ++ ++ if (val & RTC_IRQ_CAL_FINISH) { ++ irq_mask |= RTC_IRQ_CAL_FINISH; ++ complete(&srtc->cal_done); ++ } ++ ++ if (val & RTC_IRQ_CMP) ++ irq_mask |= RTC_IRQ_CMP; ++ ++ if (val & RTC_IRQ_1SEC) { ++ irq_flags |= RTC_PF; ++ irq_mask |= RTC_IRQ_1SEC; ++ complete(&srtc->onesec_done); ++ } ++ ++ if (val & RTC_IRQ_ALAEM) { ++ irq_flags |= RTC_AF; ++ irq_mask |= RTC_IRQ_ALAEM; ++ ++ next = timerqueue_getnext(&srtc->rtc_dev->timerqueue); ++ if (next == &srtc->rtc_dev->aie_timer.node) ++ dev_info(&srtc->rtc_dev->dev, "alarm expires"); ++ } ++ ++ writel(irq_mask, srtc->regs + SFT_RTC_IRQ_EVEVT); ++ ++ /* Wait interrupt flag clear */ ++ ret = readl_poll_timeout_atomic(srtc->regs + SFT_RTC_IRQ_EVEVT, val, ++ (val & irq_mask) == 0, 0, INT_TIMEOUT_US); ++ if (ret) ++ dev_warn(&srtc->rtc_dev->dev, "fail to clear rtc interrupt flag\n"); ++ ++ if (irq_flags) ++ rtc_update_irq(srtc->rtc_dev, 1, irq_flags | RTC_IRQF); ++ ++ return IRQ_HANDLED; ++} ++ ++static int sft_rtc_read_time(struct device *dev, struct rtc_time *tm) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ int irq_1sec_state_start, irq_1sec_state_end; ++ ++ /* If the RTC is disabled, assume the values are invalid */ ++ if (!sft_rtc_get_enabled(srtc)) ++ return -EINVAL; ++ ++ irq_1sec_state_start = ++ (readl(srtc->regs + SFT_RTC_IRQ_STATUS) & RTC_IRQ_1SEC) == 0 ? 0 : 1; ++ ++read_again: ++ val = readl(srtc->regs + SFT_RTC_TIME); ++ sft_rtc_reg2time(tm, val); ++ ++ val = readl(srtc->regs + SFT_RTC_DATE); ++ sft_rtc_reg2date(tm, val); ++ ++ if (irq_1sec_state_start == 0) { ++ irq_1sec_state_end = ++ (readl(srtc->regs + SFT_RTC_IRQ_STATUS) & RTC_IRQ_1SEC) == 0 ? 0 : 1; ++ if (irq_1sec_state_end == 1) { ++ irq_1sec_state_start = 1; ++ goto read_again; ++ } ++ } ++ ++ return 0; ++} ++ ++static int sft_rtc_set_time(struct device *dev, struct rtc_time *tm) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ int ret; ++ ++ val = sft_rtc_time2reg(tm); ++ writel(val, srtc->regs + SFT_RTC_CFG_TIME); ++ ++ val = sft_rtc_date2reg(tm); ++ writel(val, srtc->regs + SFT_RTC_CFG_DATE); ++ ++ /* Update pulse */ ++ sft_rtc_update_pulse(srtc); ++ ++ /* Ensure that data is fully written */ ++ ret = wait_for_completion_interruptible_timeout(&srtc->onesec_done, ++ usecs_to_jiffies(120)); ++ if (ret) { ++ dev_warn(dev, ++ "rtc wait for completion interruptible timeout.\n"); ++ } ++ return 0; ++} ++ ++static int sft_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ ++ return sft_rtc_irq_enable(srtc, RTC_IRQ_ALAEM, enabled); ++} ++ ++static int sft_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_ACT_TIME); ++ sft_rtc_reg2time(&alarm->time, val); ++ ++ val = readl(srtc->regs + SFT_RTC_ACT_DATE); ++ sft_rtc_reg2date(&alarm->time, val); ++ ++ return 0; ++} ++ ++static int sft_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ ++ sft_rtc_alarm_irq_enable(dev, 0); ++ ++ val = sft_rtc_time2reg(&alarm->time); ++ writel(val, srtc->regs + SFT_RTC_ACT_TIME); ++ ++ val = sft_rtc_date2reg(&alarm->time); ++ writel(val, srtc->regs + SFT_RTC_ACT_DATE); ++ ++ sft_rtc_alarm_irq_enable(dev, alarm->enabled); ++ ++ return 0; ++} ++ ++static int sft_rtc_get_offset(struct device *dev, long *offset) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ s64 tmp; ++ u32 val; ++ ++ val = readl(srtc->regs + SFT_RTC_CAL_VALUE) ++ & RTC_SW_CAL_VALUE_MASK; ++ val += 1; ++ /* ++ * the adjust val range is [0x0000-0xffff], ++ * the default val is 0x7fff (32768-1),mapping offset=0 ; ++ */ ++ tmp = (s64)val - RTC_TICKS_PER_SEC; ++ tmp *= RTC_PPB_MULT; ++ tmp = div_s64(tmp, RTC_TICKS_PER_SEC); ++ ++ /* Offset value operates in negative way, so swap sign */ ++ *offset = -tmp; ++ ++ return 0; ++} ++ ++static int sft_rtc_set_offset(struct device *dev, long offset) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ s64 tmp; ++ u32 val; ++ ++ tmp = offset * RTC_TICKS_PER_SEC; ++ tmp = div_s64(tmp, RTC_PPB_MULT); ++ ++ tmp = RTC_TICKS_PER_SEC - tmp; ++ tmp -= 1; ++ if (tmp > RTC_SW_CAL_MAX || tmp < RTC_SW_CAL_MIN) { ++ dev_err(dev, "offset is out of range.\n"); ++ return -EINVAL; ++ } ++ ++ val = tmp & RTC_SW_CAL_VALUE_MASK; ++ /* set software calibration value */ ++ writel(val, srtc->regs + SFT_RTC_SW_CAL_VALUE); ++ ++ /* set CFG_RTC-cal_sel to select calibretion by software. */ ++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_SW); ++ ++ return 0; ++} ++ ++static __maybe_unused int ++sft_rtc_hw_adjustment(struct device *dev, unsigned int enable) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ u32 val; ++ ++ if (srtc->hw_cal_map <= 0) { ++ dev_err(dev, "fail to get cal-clock-freq.\n"); ++ return -EFAULT; ++ } ++ ++ if (enable) { ++ sft_rtc_irq_enable(srtc, RTC_IRQ_CAL_FINISH, true); ++ ++ /* Set reference clock frequency value */ ++ val = readl(srtc->regs + SFT_RTC_HW_CAL_CFG); ++ val |= (srtc->hw_cal_map << RTC_HW_CAL_FRQ_SEL_SHIFT); ++ writel(val, srtc->regs + SFT_RTC_HW_CAL_CFG); ++ ++ /* Set CFG_RTC-cal_sel to select calibretion by hardware. */ ++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_HW); ++ ++ /* Set CFG_RTC-cal_en_hw to launch hardware calibretion.*/ ++ sft_rtc_set_cal_hw_enable(srtc, true); ++ ++ wait_for_completion_interruptible_timeout(&srtc->cal_done, ++ usecs_to_jiffies(100)); ++ ++ sft_rtc_irq_enable(srtc, RTC_IRQ_CAL_FINISH, false); ++ } else { ++ sft_rtc_set_cal_mode(srtc, RTC_CAL_MODE_SW); ++ sft_rtc_set_cal_hw_enable(srtc, false); ++ } ++ ++ return 0; ++} ++ ++static int sft_rtc_get_cal_clk(struct device *dev, struct sft_rtc *srtc) ++{ ++ struct device_node *np = dev->of_node; ++ unsigned long cal_clk_freq; ++ u32 freq; ++ int ret; ++ ++ srtc->cal_clk = devm_clk_get(dev, "cal_clk"); ++ if (IS_ERR(srtc->cal_clk)) ++ return PTR_ERR(srtc->cal_clk); ++ ++ clk_prepare_enable(srtc->cal_clk); ++ ++ cal_clk_freq = clk_get_rate(srtc->cal_clk); ++ if (!cal_clk_freq) { ++ dev_warn(dev, ++ "get rate failed, next try to get from dts.\n"); ++ ret = of_property_read_u32(np, "rtc,cal-clock-freq", &freq); ++ if (!ret) { ++ cal_clk_freq = (u64)freq; ++ } else { ++ dev_err(dev, ++ "Need rtc,cal-clock-freq define in dts.\n"); ++ goto err_disable_cal_clk; ++ } ++ } ++ ++ srtc->hw_cal_map = sft_rtc_get_hw_calclk(dev, cal_clk_freq); ++ if (srtc->hw_cal_map < 0) { ++ ret = srtc->hw_cal_map; ++ goto err_disable_cal_clk; ++ } ++ ++ return 0; ++ ++err_disable_cal_clk: ++ clk_disable_unprepare(srtc->cal_clk); ++ ++ return ret; ++} ++ ++static int sft_rtc_get_irq(struct platform_device *pdev, struct sft_rtc *srtc) ++{ ++ int ret; ++ ++ srtc->rtc_irq = platform_get_irq_byname(pdev, "rtc"); ++ if (srtc->rtc_irq < 0) ++ return -EINVAL; ++ ++ ret = devm_request_irq(&pdev->dev, srtc->rtc_irq, ++ sft_rtc_irq_handler, 0, ++ KBUILD_MODNAME, srtc); ++ if (ret) ++ dev_err(&pdev->dev, "Failed to request interrupt, %d\n", ret); ++ ++ return ret; ++} ++ ++static const struct rtc_class_ops starfive_rtc_ops = { ++ .read_time = sft_rtc_read_time, ++ .set_time = sft_rtc_set_time, ++ .read_alarm = sft_rtc_read_alarm, ++ .set_alarm = sft_rtc_set_alarm, ++ .alarm_irq_enable = sft_rtc_alarm_irq_enable, ++ .set_offset = sft_rtc_set_offset, ++ .read_offset = sft_rtc_get_offset, ++}; ++ ++static int sft_rtc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct sft_rtc *srtc; ++ struct rtc_time tm; ++ struct irq_desc *desc; ++ int ret; ++ ++ srtc = devm_kzalloc(dev, sizeof(*srtc), GFP_KERNEL); ++ if (!srtc) ++ return -ENOMEM; ++ ++ srtc->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(srtc->regs)) ++ return PTR_ERR(srtc->regs); ++ ++ srtc->pclk = devm_clk_get(dev, "pclk"); ++ if (IS_ERR(srtc->pclk)) { ++ ret = PTR_ERR(srtc->pclk); ++ dev_err(dev, ++ "Failed to retrieve the peripheral clock, %d\n", ret); ++ return ret; ++ } ++ ++ srtc->rst_array = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(srtc->rst_array)) { ++ ret = PTR_ERR(srtc->rst_array); ++ dev_err(dev, ++ "Failed to retrieve the rtc reset, %d\n", ret); ++ return ret; ++ } ++ ++ init_completion(&srtc->cal_done); ++ init_completion(&srtc->onesec_done); ++ ++ ret = clk_prepare_enable(srtc->pclk); ++ if (ret) { ++ dev_err(dev, ++ "Failed to enable the peripheral clock, %d\n", ret); ++ return ret; ++ } ++ ++ ret = sft_rtc_get_cal_clk(dev, srtc); ++ if (ret) ++ goto err_disable_pclk; ++ ++ ret = reset_control_deassert(srtc->rst_array); ++ if (ret) { ++ dev_err(dev, ++ "Failed to deassert rtc resets, %d\n", ret); ++ goto err_disable_cal_clk; ++ } ++ ++ ret = sft_rtc_get_irq(pdev, srtc); ++ if (ret) ++ goto err_disable_cal_clk; ++ ++ srtc->rtc_dev = devm_rtc_allocate_device(dev); ++ if (IS_ERR(srtc->rtc_dev)) ++ return PTR_ERR(srtc->rtc_dev); ++ ++ platform_set_drvdata(pdev, srtc); ++ ++ /* The RTC supports 01.01.2001 - 31.12.2099 */ ++ srtc->rtc_dev->range_min = mktime64(2001, 1, 1, 0, 0, 0); ++ srtc->rtc_dev->range_max = mktime64(2099, 12, 31, 23, 59, 59); ++ ++ srtc->rtc_dev->ops = &starfive_rtc_ops; ++ device_init_wakeup(dev, true); ++ ++ desc = irq_to_desc(srtc->rtc_irq); ++ irq_desc_get_chip(desc)->flags = IRQCHIP_SKIP_SET_WAKE; ++ ++ /* Always use 24-hour mode and keep the RTC values */ ++ sft_rtc_set_mode(srtc, RTC_HOUR_MODE_24H); ++ ++ sft_rtc_set_enabled(srtc, true); ++ ++ if (device_property_read_bool(dev, "rtc,hw-adjustment")) ++ sft_rtc_hw_adjustment(dev, true); ++ ++ /* ++ * If rtc time is out of supported range, reset it to the minimum time. ++ * notice that, actual year = 1900 + tm.tm_year ++ * actual month = 1 + tm.tm_mon ++ */ ++ sft_rtc_read_time(dev, &tm); ++ if (tm.tm_year < 101 || tm.tm_year > 199 || tm.tm_mon < 0 || tm.tm_mon > 11 || ++ tm.tm_mday < 1 || tm.tm_mday > 31 || tm.tm_hour < 0 || tm.tm_hour > 23 || ++ tm.tm_min < 0 || tm.tm_min > 59 || tm.tm_sec < 0 || tm.tm_sec > 59) { ++ rtc_time64_to_tm(srtc->rtc_dev->range_min, &tm); ++ sft_rtc_set_time(dev, &tm); ++ } ++ ++ ret = devm_rtc_register_device(srtc->rtc_dev); ++ if (ret) ++ goto err_disable_wakeup; ++ ++ return 0; ++ ++err_disable_wakeup: ++ device_init_wakeup(dev, false); ++ ++err_disable_cal_clk: ++ clk_disable_unprepare(srtc->cal_clk); ++ ++err_disable_pclk: ++ clk_disable_unprepare(srtc->pclk); ++ ++ return ret; ++} ++ ++static int sft_rtc_remove(struct platform_device *pdev) ++{ ++ struct sft_rtc *srtc = platform_get_drvdata(pdev); ++ ++ sft_rtc_alarm_irq_enable(&pdev->dev, 0); ++ device_init_wakeup(&pdev->dev, 0); ++ ++ clk_disable_unprepare(srtc->pclk); ++ clk_disable_unprepare(srtc->cal_clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int sft_rtc_suspend(struct device *dev) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ ++ if (device_may_wakeup(dev)) ++ enable_irq_wake(srtc->rtc_irq); ++ ++ return 0; ++} ++ ++static int sft_rtc_resume(struct device *dev) ++{ ++ struct sft_rtc *srtc = dev_get_drvdata(dev); ++ ++ if (device_may_wakeup(dev)) ++ disable_irq_wake(srtc->rtc_irq); ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(sft_rtc_pm_ops, sft_rtc_suspend, sft_rtc_resume); ++ ++static const struct of_device_id sft_rtc_of_match[] = { ++ { .compatible = "starfive,jh7110-rtc" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, sft_rtc_of_match); ++ ++static struct platform_driver starfive_rtc_driver = { ++ .driver = { ++ .name = "starfive-rtc", ++ .of_match_table = sft_rtc_of_match, ++ .pm = &sft_rtc_pm_ops, ++ }, ++ .probe = sft_rtc_probe, ++ .remove = sft_rtc_remove, ++}; ++module_platform_driver(starfive_rtc_driver); ++ ++MODULE_AUTHOR("Samin Guo "); ++MODULE_AUTHOR("Hal Feng "); ++MODULE_DESCRIPTION("StarFive RTC driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:starfive-rtc"); diff --git a/target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch b/target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch new file mode 100644 index 0000000000..c88008a604 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0066-uart-8250-Add-dw-auto-flow-ctrl-support.patch @@ -0,0 +1,96 @@ +From 552114b8cbbd956ad8466261b5f11b059eba82ca Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Sun, 25 Jun 2023 09:40:29 +0800 +Subject: [PATCH 066/116] uart: 8250: Add dw auto flow ctrl support + +Add designeware 8250 auto flow ctrl support. Enable +it by add auto-flow-control in dts. + +Signed-off-by: Minda Chen +--- + drivers/tty/serial/8250/8250_core.c | 2 ++ + drivers/tty/serial/8250/8250_dw.c | 3 +++ + drivers/tty/serial/8250/8250_port.c | 14 +++++++++++++- + include/linux/serial_8250.h | 1 + + include/uapi/linux/serial_core.h | 2 ++ + 5 files changed, 21 insertions(+), 1 deletion(-) + +--- a/drivers/tty/serial/8250/8250_core.c ++++ b/drivers/tty/serial/8250/8250_core.c +@@ -1129,6 +1129,8 @@ int serial8250_register_8250_port(const + uart->dl_read = up->dl_read; + if (up->dl_write) + uart->dl_write = up->dl_write; ++ if (up->probe) ++ uart->probe = up->probe; + + if (uart->port.type != PORT_8250_CIR) { + if (serial8250_isa_config != NULL) +--- a/drivers/tty/serial/8250/8250_dw.c ++++ b/drivers/tty/serial/8250/8250_dw.c +@@ -612,6 +612,9 @@ static int dw8250_probe(struct platform_ + data->msr_mask_off |= UART_MSR_TERI; + } + ++ if (device_property_read_bool(dev, "auto-flow-control")) ++ up->probe |= UART_PROBE_AFE; ++ + /* If there is separate baudclk, get the rate from it. */ + data->clk = devm_clk_get_optional(dev, "baudclk"); + if (data->clk == NULL) +--- a/drivers/tty/serial/8250/8250_port.c ++++ b/drivers/tty/serial/8250/8250_port.c +@@ -330,6 +330,14 @@ static const struct serial8250_config ua + .rxtrig_bytes = {1, 8, 16, 30}, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, ++ [PORT_16550A_AFE] = { ++ .name = "16550A_AFE", ++ .fifo_size = 16, ++ .tx_loadsz = 16, ++ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, ++ .rxtrig_bytes = {1, 4, 8, 14}, ++ .flags = UART_CAP_FIFO | UART_CAP_AFE, ++ }, + }; + + /* Uart divisor latch read */ +@@ -1143,6 +1151,11 @@ static void autoconfig_16550a(struct uar + up->port.type = PORT_U6_16550A; + up->capabilities |= UART_CAP_AFE; + } ++ ++ if ((up->port.type == PORT_16550A) && (up->probe & UART_PROBE_AFE)) { ++ up->port.type = PORT_16550A_AFE; ++ up->capabilities |= UART_CAP_AFE; ++ } + } + + /* +@@ -2813,7 +2826,6 @@ serial8250_do_set_termios(struct uart_po + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + } +- + /* + * Update the per-port timeout. + */ +--- a/include/linux/serial_8250.h ++++ b/include/linux/serial_8250.h +@@ -141,6 +141,7 @@ struct uart_8250_port { + unsigned char probe; + struct mctrl_gpios *gpios; + #define UART_PROBE_RSA (1 << 0) ++#define UART_PROBE_AFE (1 << 1) + + /* + * Some bits in registers are cleared on a read, so they must +--- a/include/uapi/linux/serial_core.h ++++ b/include/uapi/linux/serial_core.h +@@ -245,4 +245,6 @@ + /* Sunplus UART */ + #define PORT_SUNPLUS 123 + ++#define PORT_16550A_AFE 124 ++ + #endif /* _UAPILINUX_SERIAL_CORE_H */ diff --git a/target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch b/target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch new file mode 100644 index 0000000000..18d735b92d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0067-driver-uart-fix-up-uart-communicate-fail.patch @@ -0,0 +1,29 @@ +From 6edee93a89254f30c3387c88231e7ecec06ba84a Mon Sep 17 00:00:00 2001 +From: "shanlong.li" +Date: Mon, 10 Jul 2023 03:07:57 -0700 +Subject: [PATCH 067/116] driver:uart: fix up uart communicate fail + +fix up uart communicate fail + +Signed-off-by: shanlong.li +--- + drivers/tty/serial/8250/8250_dw.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/tty/serial/8250/8250_dw.c ++++ b/drivers/tty/serial/8250/8250_dw.c +@@ -652,10 +652,10 @@ static int dw8250_probe(struct platform_ + if (err) + return err; + +- data->rst = devm_reset_control_get_optional_exclusive(dev, NULL); +- if (IS_ERR(data->rst)) +- return PTR_ERR(data->rst); +- ++ data->rst = devm_reset_control_array_get_exclusive(dev); ++ if (IS_ERR(data->rst)) { ++ err = PTR_ERR(data->rst); ++ } + reset_control_deassert(data->rst); + + err = devm_add_action_or_reset(dev, dw8250_reset_control_assert, data->rst); diff --git a/target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch b/target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch new file mode 100644 index 0000000000..56efebda6c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0068-uart-8250-add-reset-operation-in-runtime-PM.patch @@ -0,0 +1,32 @@ +From 777d288f03a0b350f6c2d4367b01a80d9f25cd6e Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Wed, 20 Sep 2023 17:19:59 +0800 +Subject: [PATCH 068/116] uart: 8250: add reset operation in runtime PM + +add reset operation in runtime PM + +Signed-off-by: William Qiu +--- + drivers/tty/serial/8250/8250_dw.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/tty/serial/8250/8250_dw.c ++++ b/drivers/tty/serial/8250/8250_dw.c +@@ -745,6 +745,8 @@ static int dw8250_runtime_suspend(struct + { + struct dw8250_data *data = dev_get_drvdata(dev); + ++ reset_control_assert(data->rst); ++ + clk_disable_unprepare(data->clk); + + clk_disable_unprepare(data->pclk); +@@ -760,6 +762,8 @@ static int dw8250_runtime_resume(struct + + clk_prepare_enable(data->clk); + ++ reset_control_deassert(data->rst); ++ + return 0; + } + diff --git a/target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch b/target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch new file mode 100644 index 0000000000..eb46a96314 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0069-dt-bindings-CAN-Add-StarFive-CAN-module.patch @@ -0,0 +1,113 @@ +From 5eda2331a252436756fb40861f01a7a38b1502c7 Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Thu, 15 Jun 2023 20:14:22 +0800 +Subject: [PATCH 069/116] dt-bindings: CAN: Add StarFive CAN module + +Add documentation to describe StarFive CAN engine. + +Signed-off-by: William Qiu +--- + .../devicetree/bindings/net/can/ipms-can.yaml | 97 +++++++++++++++++++ + 1 file changed, 97 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/can/ipms-can.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/can/ipms-can.yaml +@@ -0,0 +1,97 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++#$id: http://devicetree.org/schemas/net/can/ipms-can.yaml# ++#$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: IPMS CAN/CANFD controller Device Tree Bindings ++ ++properties: ++ compatible: ++ const:ipms,can ++ ++ reg: ++ maxItems: 1 ++ items: ++ - description:CAN controller registers ++ ++ interrupts: ++ maxItems: 1 ++ ++ clocks: ++ minItems: 1 ++ items: ++ - description:apb_clk clock ++ - description:core_clk clock ++ - description:timer_clk clock ++ ++ clock-names: ++ minItems: 1 ++ items: ++ - const:apb_clk ++ - const:core_clk ++ - const:timer_clk ++ resets: ++ minItems: 1 ++ items: ++ - description:apb_clk reset ++ - description:core_clk reset ++ - description:timer_clk reset ++ reset-names: ++ minItems: 1 ++ items: ++ - const:rst_apb ++ - const:rst_core ++ - const:rst_timer ++ starfive,sys-syscon: ++ format: ++ starfive,sys-syscon = <&arg0 arg1 arg2 arg3> ++ description: ++ arg0:arg0 is sys_syscon. ++ arg1:arg1 is syscon register offset, used to enable can2.0/canfd function, can0 is 0x10, can1 is 0x88. ++ arg2:arg2 is used to enable the register shift of the can2.0/canfd function, can0 is 0x3, can1 is 0x12. ++ arg3:arg3 is used to enable the register mask of the can2.0/canfd function, can0 is 0x8, can1 is 0x40000 ++ ++ syscon,can_or_canfd: ++ description: ++ IPMS CAN-CTRL core is a serial communications controller that performs serial communication according to the CAN protocol. ++ This CAN bus interface uses the basic CAN principle and meets all constraints of the CAN-specification 2.0B active. ++ Furthermore this CAN core can be configured to meet the specification of CAN with flexible data rate CAN FD. ++ When syscon,can_or_canfd is set to 0, use CAN2.0B. ++ when syscon,can_or_canfd is set to 1, use CAN FD. ++required: ++ - compatible ++ - reg ++ - interrupts ++ - clocks ++ - clock-names ++ - resets ++ - reset-names ++ - starfive,sys-syscon ++ - syscon,can_or_canfd ++additionalProperties:false ++ ++examples: ++ - | ++ can0: can@130d0000{ ++ compatible = "ipms,can"; ++ reg = <0x0 0x130d0000 0x0 0x1000>; ++ interrupts = <112>; ++ interrupt-parent = <&plic>; ++ clocks = <&clkgen JH7110_CAN0_CTRL_CLK_APB>, ++ <&clkgen JH7110_CAN0_CTRL_CLK_CAN>, ++ <&clkgen JH7110_CAN0_CTRL_CLK_TIMER>; ++ clock-names = "apb_clk", ++ "core_clk", ++ "timer_clk"; ++ resets = <&rstgen RSTN_U0_CAN_CTRL_APB>, ++ <&rstgen RSTN_U0_CAN_CTRL_CORE>, ++ <&rstgen RSTN_U0_CAN_CTRL_TIMER>; ++ reset-names = "rst_apb", ++ "rst_core", ++ "rst_timer"; ++ starfive,sys-syscon = <&sys_syscon, 0x10 0x3 0x8>; ++ syscon,can_or_canfd = <0>; ++ }; ++ ++... diff --git a/target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch b/target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch new file mode 100644 index 0000000000..1bb41dd079 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0070-CAN-starfive-Add-CAN-engine-support.patch @@ -0,0 +1,1317 @@ +From b1fbe15b87be654b1b280a76ec1470917d79f720 Mon Sep 17 00:00:00 2001 +From: William Qiu +Date: Thu, 15 Jun 2023 20:15:25 +0800 +Subject: [PATCH 070/116] CAN: starfive - Add CAN engine support + +Adding device probe StarFive CAN module. + +Signed-off-by: William Qiu +Signed-off-by: Hal Feng +--- + drivers/net/can/Kconfig | 5 + + drivers/net/can/Makefile | 1 + + drivers/net/can/ipms_canfd.c | 1275 ++++++++++++++++++++++++++++++++++ + 3 files changed, 1281 insertions(+) + create mode 100644 drivers/net/can/ipms_canfd.c + +--- a/drivers/net/can/Kconfig ++++ b/drivers/net/can/Kconfig +@@ -214,6 +214,11 @@ config CAN_XILINXCAN + Xilinx CAN driver. This driver supports both soft AXI CAN IP and + Zynq CANPS IP. + ++config IPMS_CAN ++ tristate "IPMS CAN" ++ help ++ IPMS CANFD driver. This driver supports IPMS CANFD IP. ++ + source "drivers/net/can/c_can/Kconfig" + source "drivers/net/can/cc770/Kconfig" + source "drivers/net/can/ctucanfd/Kconfig" +--- a/drivers/net/can/Makefile ++++ b/drivers/net/can/Makefile +@@ -31,5 +31,6 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ + obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o + obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o + obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o ++obj-$(CONFIG_IPMS_CAN) += ipms_canfd.o + + subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG +--- /dev/null ++++ b/drivers/net/can/ipms_canfd.c +@@ -0,0 +1,1275 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * StarFive Controller Area Network Host Controller Driver ++ * ++ * Copyright (c) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "ipms_canfd" ++ ++/* CAN registers set */ ++enum canfd_device_reg { ++ CANFD_RUBF_OFFSET = 0x00, /* Receive Buffer Registers 0x00-0x4f */ ++ CANFD_RUBF_ID_OFFSET = 0x00, ++ CANFD_RBUF_CTL_OFFSET = 0x04, ++ CANFD_RBUF_DATA_OFFSET = 0x08, ++ CANFD_TBUF_OFFSET = 0x50, /* Transmit Buffer Registers 0x50-0x97 */ ++ CANFD_TBUF_ID_OFFSET = 0x50, ++ CANFD_TBUF_CTL_OFFSET = 0x54, ++ CANFD_TBUF_DATA_OFFSET = 0x58, ++ CANFD_TTS_OFFSET = 0x98, /* Transmission Time Stamp 0x98-0x9f */ ++ CANFD_CFG_STAT_OFFSET = 0xa0, ++ CANFD_TCMD_OFFSET = 0xa1, ++ CANFD_TCTRL_OFFSET = 0xa2, ++ CANFD_RCTRL_OFFSET = 0xa3, ++ CANFD_RTIE_OFFSET = 0xa4, ++ CANFD_RTIF_OFFSET = 0xa5, ++ CANFD_ERRINT_OFFSET = 0xa6, ++ CANFD_LIMIT_OFFSET = 0xa7, ++ CANFD_S_SEG_1_OFFSET = 0xa8, ++ CANFD_S_SEG_2_OFFSET = 0xa9, ++ CANFD_S_SJW_OFFSET = 0xaa, ++ CANFD_S_PRESC_OFFSET = 0xab, ++ CANFD_F_SEG_1_OFFSET = 0xac, ++ CANFD_F_SEG_2_OFFSET = 0xad, ++ CANFD_F_SJW_OFFSET = 0xae, ++ CANFD_F_PRESC_OFFSET = 0xaf, ++ CANFD_EALCAP_OFFSET = 0xb0, ++ CANFD_RECNT_OFFSET = 0xb2, ++ CANFD_TECNT_OFFSET = 0xb3, ++}; ++ ++enum canfd_reg_bitchange { ++ CAN_FD_SET_RST_MASK = 0x80, /* Set Reset Bit */ ++ CAN_FD_OFF_RST_MASK = 0x7f, /* Reset Off Bit */ ++ CAN_FD_SET_FULLCAN_MASK = 0x10, /* set TTTBM as 1->full TTCAN mode */ ++ CAN_FD_OFF_FULLCAN_MASK = 0xef, /* set TTTBM as 0->separate PTB and STB mode */ ++ CAN_FD_SET_FIFO_MASK = 0x20, /* set TSMODE as 1->FIFO mode */ ++ CAN_FD_OFF_FIFO_MASK = 0xdf, /* set TSMODE as 0->Priority mode */ ++ CAN_FD_SET_TSONE_MASK = 0x04, ++ CAN_FD_OFF_TSONE_MASK = 0xfb, ++ CAN_FD_SET_TSALL_MASK = 0x02, ++ CAN_FD_OFF_TSALL_MASK = 0xfd, ++ CAN_FD_LBMEMOD_MASK = 0x40, /* set loop back mode, external */ ++ CAN_FD_LBMIMOD_MASK = 0x20, /* set loopback internal mode */ ++ CAN_FD_SET_BUSOFF_MASK = 0x01, ++ CAN_FD_OFF_BUSOFF_MASK = 0xfe, ++ CAN_FD_SET_TTSEN_MASK = 0x80, /* set ttsen, tts update enable */ ++ CAN_FD_SET_BRS_MASK = 0x10, /* can fd Bit Rate Switch mask */ ++ CAN_FD_OFF_BRS_MASK = 0xef, ++ CAN_FD_SET_EDL_MASK = 0x20, /* Extended Data Length */ ++ CAN_FD_OFF_EDL_MASK = 0xdf, ++ CAN_FD_SET_DLC_MASK = 0x0f, ++ CAN_FD_SET_TENEXT_MASK = 0x40, ++ CAN_FD_SET_IDE_MASK = 0x80, ++ CAN_FD_OFF_IDE_MASK = 0x7f, ++ CAN_FD_SET_RTR_MASK = 0x40, ++ CAN_FD_OFF_RTR_MASK = 0xbf, ++ CAN_FD_INTR_ALL_MASK = 0xff, /* all interrupts enable mask */ ++ CAN_FD_SET_RIE_MASK = 0x80, ++ CAN_FD_OFF_RIE_MASK = 0x7f, ++ CAN_FD_SET_RFIE_MASK = 0x20, ++ CAN_FD_OFF_RFIE_MASK = 0xdf, ++ CAN_FD_SET_RAFIE_MASK = 0x10, ++ CAN_FD_OFF_RAFIE_MASK = 0xef, ++ CAN_FD_SET_EIE_MASK = 0x02, ++ CAN_FD_OFF_EIE_MASK = 0xfd, ++ CAN_FD_TASCTIVE_MASK = 0x02, ++ CAN_FD_RASCTIVE_MASK = 0x04, ++ CAN_FD_SET_TBSEL_MASK = 0x80, /* message writen in STB */ ++ CAN_FD_OFF_TBSEL_MASK = 0x7f, /* message writen in PTB */ ++ CAN_FD_SET_STBY_MASK = 0x20, ++ CAN_FD_OFF_STBY_MASK = 0xdf, ++ CAN_FD_SET_TPE_MASK = 0x10, /* Transmit primary enable */ ++ CAN_FD_SET_TPA_MASK = 0x08, ++ CAN_FD_SET_SACK_MASK = 0x80, ++ CAN_FD_SET_RREL_MASK = 0x10, ++ CAN_FD_RSTAT_NOT_EMPTY_MASK = 0x03, ++ CAN_FD_SET_RIF_MASK = 0x80, ++ CAN_FD_OFF_RIF_MASK = 0x7f, ++ CAN_FD_SET_RAFIF_MASK = 0x10, ++ CAN_FD_SET_RFIF_MASK = 0x20, ++ CAN_FD_SET_TPIF_MASK = 0x08, /* Transmission Primary Interrupt Flag */ ++ CAN_FD_SET_TSIF_MASK = 0x04, ++ CAN_FD_SET_EIF_MASK = 0x02, ++ CAN_FD_SET_AIF_MASK = 0x01, ++ CAN_FD_SET_EWARN_MASK = 0x80, ++ CAN_FD_SET_EPASS_MASK = 0x40, ++ CAN_FD_SET_EPIE_MASK = 0x20, ++ CAN_FD_SET_EPIF_MASK = 0x10, ++ CAN_FD_SET_ALIE_MASK = 0x08, ++ CAN_FD_SET_ALIF_MASK = 0x04, ++ CAN_FD_SET_BEIE_MASK = 0x02, ++ CAN_FD_SET_BEIF_MASK = 0x01, ++ CAN_FD_OFF_EPIE_MASK = 0xdf, ++ CAN_FD_OFF_BEIE_MASK = 0xfd, ++ CAN_FD_SET_AFWL_MASK = 0x40, ++ CAN_FD_SET_EWL_MASK = 0x0b, ++ CAN_FD_SET_KOER_MASK = 0xe0, ++ CAN_FD_SET_BIT_ERROR_MASK = 0x20, ++ CAN_FD_SET_FORM_ERROR_MASK = 0x40, ++ CAN_FD_SET_STUFF_ERROR_MASK = 0x60, ++ CAN_FD_SET_ACK_ERROR_MASK = 0x80, ++ CAN_FD_SET_CRC_ERROR_MASK = 0xa0, ++ CAN_FD_SET_OTH_ERROR_MASK = 0xc0, ++}; ++ ++/* seg1,seg2,sjw,prescaler all have 8 bits */ ++#define BITS_OF_BITTIMING_REG 8 ++ ++/* in can_bittiming strucure every field has 32 bits---->u32 */ ++#define FBITS_IN_BITTIMING_STR 32 ++#define SEG_1_SHIFT 0 ++#define SEG_2_SHIFT 8 ++#define SJW_SHIFT 16 ++#define PRESC_SHIFT 24 ++ ++/* TTSEN bit used for 32 bit register read or write */ ++#define TTSEN_8_32_SHIFT 24 ++#define RTR_32_8_SHIFT 24 ++ ++/* transmit mode */ ++#define XMIT_FULL 0 ++#define XMIT_SEP_FIFO 1 ++#define XMIT_SEP_PRIO 2 ++#define XMIT_PTB_MODE 3 ++ ++enum IPMS_CAN_TYPE { ++ IPMS_CAN_TYPY_CAN = 0, ++ IPMS_CAN_TYPE_CANFD, ++}; ++ ++struct ipms_canfd_priv { ++ struct can_priv can; ++ struct napi_struct napi; ++ struct device *dev; ++ struct regmap *reg_syscon; ++ void __iomem *reg_base; ++ u32 (*read_reg)(const struct ipms_canfd_priv *priv, enum canfd_device_reg reg); ++ void (*write_reg)(const struct ipms_canfd_priv *priv, enum canfd_device_reg reg, u32 val); ++ struct clk *can_clk; ++ u32 tx_mode; ++ struct reset_control *resets; ++ struct clk_bulk_data *clks; ++ int nr_clks; ++ u32 can_or_canfd; ++}; ++ ++static struct can_bittiming_const canfd_bittiming_const = { ++ .name = DRIVER_NAME, ++ .tseg1_min = 2, ++ .tseg1_max = 16, ++ .tseg2_min = 2, ++ .tseg2_max = 8, ++ .sjw_max = 4, ++ .brp_min = 1, ++ .brp_max = 512, ++ .brp_inc = 1, ++ ++}; ++ ++static struct can_bittiming_const canfd_data_bittiming_const = { ++ .name = DRIVER_NAME, ++ .tseg1_min = 1, ++ .tseg1_max = 16, ++ .tseg2_min = 2, ++ .tseg2_max = 8, ++ .sjw_max = 8, ++ .brp_min = 1, ++ .brp_max = 512, ++ .brp_inc = 1, ++}; ++ ++static void canfd_write_reg_le(const struct ipms_canfd_priv *priv, ++ enum canfd_device_reg reg, u32 val) ++{ ++ iowrite32(val, priv->reg_base + reg); ++} ++ ++static u32 canfd_read_reg_le(const struct ipms_canfd_priv *priv, ++ enum canfd_device_reg reg) ++{ ++ return ioread32(priv->reg_base + reg); ++} ++ ++static inline unsigned char can_ioread8(const void *addr) ++{ ++ void *addr_down; ++ union val { ++ u8 val_8[4]; ++ u32 val_32; ++ } val; ++ u32 offset = 0; ++ ++ addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4); ++ offset = addr - addr_down; ++ val.val_32 = ioread32(addr_down); ++ return val.val_8[offset]; ++} ++ ++static inline void can_iowrite8(unsigned char value, void *addr) ++{ ++ void *addr_down; ++ union val { ++ u8 val_8[4]; ++ u32 val_32; ++ } val; ++ u8 offset = 0; ++ ++ addr_down = (void *)ALIGN_DOWN((unsigned long)addr, 4); ++ offset = addr - addr_down; ++ val.val_32 = ioread32(addr_down); ++ val.val_8[offset] = value; ++ iowrite32(val.val_32, addr_down); ++} ++ ++static void canfd_reigister_set_bit(const struct ipms_canfd_priv *priv, ++ enum canfd_device_reg reg, ++ enum canfd_reg_bitchange set_mask) ++{ ++ void *addr_down; ++ union val { ++ u8 val_8[4]; ++ u32 val_32; ++ } val; ++ u8 offset = 0; ++ ++ addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4); ++ offset = (priv->reg_base + reg) - addr_down; ++ val.val_32 = ioread32(addr_down); ++ val.val_8[offset] |= set_mask; ++ iowrite32(val.val_32, addr_down); ++} ++ ++static void canfd_reigister_off_bit(const struct ipms_canfd_priv *priv, ++ enum canfd_device_reg reg, ++ enum canfd_reg_bitchange set_mask) ++{ ++ void *addr_down; ++ union val { ++ u8 val_8[4]; ++ u32 val_32; ++ } val; ++ u8 offset = 0; ++ ++ addr_down = (void *)ALIGN_DOWN((unsigned long)(priv->reg_base + reg), 4); ++ offset = (priv->reg_base + reg) - addr_down; ++ val.val_32 = ioread32(addr_down); ++ val.val_8[offset] &= set_mask; ++ iowrite32(val.val_32, addr_down); ++} ++ ++static int canfd_device_driver_bittime_configuration(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct can_bittiming *bt = &priv->can.bittiming; ++ struct can_bittiming *dbt = &priv->can.data_bittiming; ++ u32 reset_test, bittiming_temp, dat_bittiming; ++ ++ reset_test = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ if (!(reset_test & CAN_FD_SET_RST_MASK)) { ++ netdev_alert(ndev, "Not in reset mode, cannot set bit timing\n"); ++ return -EPERM; ++ } ++ ++ bittiming_temp = ((bt->phase_seg1 + bt->prop_seg + 1 - 2) << SEG_1_SHIFT) | ++ ((bt->phase_seg2 - 1) << SEG_2_SHIFT) | ++ ((bt->sjw - 1) << SJW_SHIFT) | ++ ((bt->brp - 1) << PRESC_SHIFT); ++ ++ /* Check the bittime parameter */ ++ if ((((int)(bt->phase_seg1 + bt->prop_seg + 1) - 2) < 0) || ++ (((int)(bt->phase_seg2) - 1) < 0) || ++ (((int)(bt->sjw) - 1) < 0) || ++ (((int)(bt->brp) - 1) < 0)) ++ return -EINVAL; ++ ++ priv->write_reg(priv, CANFD_S_SEG_1_OFFSET, bittiming_temp); ++ ++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) { ++ dat_bittiming = ((dbt->phase_seg1 + dbt->prop_seg + 1 - 2) << SEG_1_SHIFT) | ++ ((dbt->phase_seg2 - 1) << SEG_2_SHIFT) | ++ ((dbt->sjw - 1) << SJW_SHIFT) | ++ ((dbt->brp - 1) << PRESC_SHIFT); ++ ++ if ((((int)(dbt->phase_seg1 + dbt->prop_seg + 1) - 2) < 0) || ++ (((int)(dbt->phase_seg2) - 1) < 0) || ++ (((int)(dbt->sjw) - 1) < 0) || ++ (((int)(dbt->brp) - 1) < 0)) ++ return -EINVAL; ++ ++ priv->write_reg(priv, CANFD_F_SEG_1_OFFSET, dat_bittiming); ++ } ++ ++ canfd_reigister_off_bit(priv, CANFD_CFG_STAT_OFFSET, CAN_FD_OFF_RST_MASK); ++ ++ netdev_dbg(ndev, "Slow bit rate: %08x\n", priv->read_reg(priv, CANFD_S_SEG_1_OFFSET)); ++ netdev_dbg(ndev, "Fast bit rate: %08x\n", priv->read_reg(priv, CANFD_F_SEG_1_OFFSET)); ++ ++ return 0; ++} ++ ++int canfd_get_freebuffer(struct ipms_canfd_priv *priv) ++{ ++ /* Get next transmit buffer */ ++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_TENEXT_MASK); ++ ++ if (can_ioread8(priv->reg_base + CANFD_TCTRL_OFFSET) & CAN_FD_SET_TENEXT_MASK) ++ return -1; ++ ++ return 0; ++} ++ ++static void canfd_tx_interrupt(struct net_device *ndev, u8 isr) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ /* wait till transmission of the PTB or STB finished */ ++ while (isr & (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK)) { ++ if (isr & CAN_FD_SET_TPIF_MASK) ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_TPIF_MASK); ++ ++ if (isr & CAN_FD_SET_TSIF_MASK) ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_TSIF_MASK); ++ ++ isr = can_ioread8(priv->reg_base + CANFD_RTIF_OFFSET); ++ } ++ netif_wake_queue(ndev); ++} ++ ++static int can_rx(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct net_device_stats *stats = &ndev->stats; ++ struct can_frame *cf; ++ struct sk_buff *skb; ++ u32 can_id; ++ u8 dlc, control, rx_status; ++ ++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET); ++ ++ if (!(rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK)) ++ return 0; ++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET); ++ can_id = priv->read_reg(priv, CANFD_RUBF_ID_OFFSET); ++ dlc = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET) & CAN_FD_SET_DLC_MASK; ++ ++ skb = alloc_can_skb(ndev, (struct can_frame **)&cf); ++ if (!skb) { ++ stats->rx_dropped++; ++ return 0; ++ } ++ cf->can_dlc = can_cc_dlc2len(dlc); ++ ++ /* change the CANFD id into socketcan id format */ ++ if (control & CAN_FD_SET_IDE_MASK) { ++ cf->can_id = can_id; ++ cf->can_id |= CAN_EFF_FLAG; ++ } else { ++ cf->can_id = can_id; ++ cf->can_id &= (~CAN_EFF_FLAG); ++ } ++ ++ if (control & CAN_FD_SET_RTR_MASK) ++ cf->can_id |= CAN_RTR_FLAG; ++ ++ if (!(control & CAN_FD_SET_RTR_MASK)) { ++ *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET); ++ *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + 4); ++ } ++ ++ canfd_reigister_set_bit(priv, CANFD_RCTRL_OFFSET, CAN_FD_SET_RREL_MASK); ++ stats->rx_bytes += can_fd_dlc2len(cf->can_dlc); ++ stats->rx_packets++; ++ netif_receive_skb(skb); ++ ++ return 1; ++} ++ ++static int canfd_rx(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct net_device_stats *stats = &ndev->stats; ++ struct canfd_frame *cf; ++ struct sk_buff *skb; ++ u32 can_id; ++ u8 dlc, control, rx_status; ++ int i; ++ ++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET); ++ ++ if (!(rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK)) ++ return 0; ++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET); ++ can_id = priv->read_reg(priv, CANFD_RUBF_ID_OFFSET); ++ dlc = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET) & CAN_FD_SET_DLC_MASK; ++ ++ if (control & CAN_FD_SET_EDL_MASK) ++ /* allocate sk_buffer for canfd frame */ ++ skb = alloc_canfd_skb(ndev, &cf); ++ else ++ /* allocate sk_buffer for can frame */ ++ skb = alloc_can_skb(ndev, (struct can_frame **)&cf); ++ ++ if (!skb) { ++ stats->rx_dropped++; ++ return 0; ++ } ++ ++ /* change the CANFD or CAN2.0 data into socketcan data format */ ++ if (control & CAN_FD_SET_EDL_MASK) ++ cf->len = can_fd_dlc2len(dlc); ++ else ++ cf->len = can_cc_dlc2len(dlc); ++ ++ /* change the CANFD id into socketcan id format */ ++ if (control & CAN_FD_SET_EDL_MASK) { ++ cf->can_id = can_id; ++ if (control & CAN_FD_SET_IDE_MASK) ++ cf->can_id |= CAN_EFF_FLAG; ++ else ++ cf->can_id &= (~CAN_EFF_FLAG); ++ } else { ++ cf->can_id = can_id; ++ if (control & CAN_FD_SET_IDE_MASK) ++ cf->can_id |= CAN_EFF_FLAG; ++ else ++ cf->can_id &= (~CAN_EFF_FLAG); ++ ++ if (control & CAN_FD_SET_RTR_MASK) ++ cf->can_id |= CAN_RTR_FLAG; ++ } ++ ++ /* CANFD frames handed over to SKB */ ++ if (control & CAN_FD_SET_EDL_MASK) { ++ for (i = 0; i < cf->len; i += 4) ++ *((u32 *)(cf->data + i)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + i); ++ } else { ++ /* skb reads the received datas, if the RTR bit not set */ ++ if (!(control & CAN_FD_SET_RTR_MASK)) { ++ *((u32 *)(cf->data + 0)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET); ++ *((u32 *)(cf->data + 4)) = priv->read_reg(priv, CANFD_RBUF_DATA_OFFSET + 4); ++ } ++ } ++ ++ canfd_reigister_set_bit(priv, CANFD_RCTRL_OFFSET, CAN_FD_SET_RREL_MASK); ++ ++ stats->rx_bytes += cf->len; ++ stats->rx_packets++; ++ netif_receive_skb(skb); ++ ++ return 1; ++} ++ ++static int canfd_rx_poll(struct napi_struct *napi, int quota) ++{ ++ struct net_device *ndev = napi->dev; ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int work_done = 0; ++ u8 rx_status = 0, control = 0; ++ ++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET); ++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET); ++ ++ /* clear receive interrupt and deal with all the received frames */ ++ while ((rx_status & CAN_FD_RSTAT_NOT_EMPTY_MASK) && (work_done < quota)) { ++ (control & CAN_FD_SET_EDL_MASK) ? (work_done += canfd_rx(ndev)) : (work_done += can_rx(ndev)); ++ ++ control = can_ioread8(priv->reg_base + CANFD_RBUF_CTL_OFFSET); ++ rx_status = can_ioread8(priv->reg_base + CANFD_RCTRL_OFFSET); ++ } ++ napi_complete(napi); ++ canfd_reigister_set_bit(priv, CANFD_RTIE_OFFSET, CAN_FD_SET_RIE_MASK); ++ return work_done; ++} ++ ++static void canfd_rxfull_interrupt(struct net_device *ndev, u8 isr) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ if (isr & CAN_FD_SET_RAFIF_MASK) ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_RAFIF_MASK); ++ ++ if (isr & (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK)) ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, ++ (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK)); ++} ++ ++static int set_canfd_xmit_mode(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ switch (priv->tx_mode) { ++ case XMIT_FULL: ++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_FULLCAN_MASK); ++ break; ++ case XMIT_SEP_FIFO: ++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FULLCAN_MASK); ++ canfd_reigister_set_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_SET_FIFO_MASK); ++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TBSEL_MASK); ++ break; ++ case XMIT_SEP_PRIO: ++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FULLCAN_MASK); ++ canfd_reigister_off_bit(priv, CANFD_TCTRL_OFFSET, CAN_FD_OFF_FIFO_MASK); ++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TBSEL_MASK); ++ break; ++ case XMIT_PTB_MODE: ++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_OFF_TBSEL_MASK); ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static netdev_tx_t canfd_driver_start_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct canfd_frame *cf = (struct canfd_frame *)skb->data; ++ struct net_device_stats *stats = &ndev->stats; ++ u32 ttsen, id, ctl, addr_off; ++ int i; ++ ++ priv->tx_mode = XMIT_PTB_MODE; ++ ++ if (can_dropped_invalid_skb(ndev, skb)) ++ return NETDEV_TX_OK; ++ ++ switch (priv->tx_mode) { ++ case XMIT_FULL: ++ return NETDEV_TX_BUSY; ++ case XMIT_PTB_MODE: ++ set_canfd_xmit_mode(ndev); ++ canfd_reigister_off_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_OFF_STBY_MASK); ++ ++ if (cf->can_id & CAN_EFF_FLAG) { ++ id = (cf->can_id & CAN_EFF_MASK); ++ ttsen = 0 << TTSEN_8_32_SHIFT; ++ id |= ttsen; ++ } else { ++ id = (cf->can_id & CAN_SFF_MASK); ++ ttsen = 0 << TTSEN_8_32_SHIFT; ++ id |= ttsen; ++ } ++ ++ ctl = can_fd_len2dlc(cf->len); ++ ++ /* transmit can fd frame */ ++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) { ++ if (can_is_canfd_skb(skb)) { ++ if (cf->can_id & CAN_EFF_FLAG) ++ ctl |= CAN_FD_SET_IDE_MASK; ++ else ++ ctl &= CAN_FD_OFF_IDE_MASK; ++ ++ if (cf->flags & CANFD_BRS) ++ ctl |= CAN_FD_SET_BRS_MASK; ++ ++ ctl |= CAN_FD_SET_EDL_MASK; ++ ++ addr_off = CANFD_TBUF_DATA_OFFSET; ++ ++ for (i = 0; i < cf->len; i += 4) { ++ priv->write_reg(priv, addr_off, ++ *((u32 *)(cf->data + i))); ++ addr_off += 4; ++ } ++ } else { ++ ctl &= CAN_FD_OFF_EDL_MASK; ++ ctl &= CAN_FD_OFF_BRS_MASK; ++ ++ if (cf->can_id & CAN_EFF_FLAG) ++ ctl |= CAN_FD_SET_IDE_MASK; ++ else ++ ctl &= CAN_FD_OFF_IDE_MASK; ++ ++ if (cf->can_id & CAN_RTR_FLAG) { ++ ctl |= CAN_FD_SET_RTR_MASK; ++ priv->write_reg(priv, ++ CANFD_TBUF_ID_OFFSET, id); ++ priv->write_reg(priv, ++ CANFD_TBUF_CTL_OFFSET, ctl); ++ } else { ++ ctl &= CAN_FD_OFF_RTR_MASK; ++ addr_off = CANFD_TBUF_DATA_OFFSET; ++ priv->write_reg(priv, addr_off, ++ *((u32 *)(cf->data + 0))); ++ priv->write_reg(priv, addr_off + 4, ++ *((u32 *)(cf->data + 4))); ++ } ++ } ++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id); ++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl); ++ addr_off = CANFD_TBUF_DATA_OFFSET; ++ } else { ++ ctl &= CAN_FD_OFF_EDL_MASK; ++ ctl &= CAN_FD_OFF_BRS_MASK; ++ ++ if (cf->can_id & CAN_EFF_FLAG) ++ ctl |= CAN_FD_SET_IDE_MASK; ++ else ++ ctl &= CAN_FD_OFF_IDE_MASK; ++ ++ if (cf->can_id & CAN_RTR_FLAG) { ++ ctl |= CAN_FD_SET_RTR_MASK; ++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id); ++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl); ++ } else { ++ ctl &= CAN_FD_OFF_RTR_MASK; ++ priv->write_reg(priv, CANFD_TBUF_ID_OFFSET, id); ++ priv->write_reg(priv, CANFD_TBUF_CTL_OFFSET, ctl); ++ addr_off = CANFD_TBUF_DATA_OFFSET; ++ priv->write_reg(priv, addr_off, ++ *((u32 *)(cf->data + 0))); ++ priv->write_reg(priv, addr_off + 4, ++ *((u32 *)(cf->data + 4))); ++ } ++ } ++ canfd_reigister_set_bit(priv, CANFD_TCMD_OFFSET, CAN_FD_SET_TPE_MASK); ++ stats->tx_bytes += cf->len; ++ break; ++ default: ++ break; ++ } ++ ++ /*Due to cache blocking, we need call dev_kfree_skb() here to free the socket ++ buffer and return NETDEV_TX_OK */ ++ dev_kfree_skb(skb); ++ ++ return NETDEV_TX_OK; ++} ++ ++static int set_reset_mode(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ u8 ret; ++ ++ ret = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ret |= CAN_FD_SET_RST_MASK; ++ can_iowrite8(ret, priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ return 0; ++} ++ ++static void canfd_driver_stop(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int ret; ++ ++ ret = set_reset_mode(ndev); ++ if (ret) ++ netdev_err(ndev, "Mode Resetting Failed!\n"); ++ ++ priv->can.state = CAN_STATE_STOPPED; ++} ++ ++static int canfd_driver_close(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ netif_stop_queue(ndev); ++ napi_disable(&priv->napi); ++ canfd_driver_stop(ndev); ++ ++ free_irq(ndev->irq, ndev); ++ close_candev(ndev); ++ ++ pm_runtime_put(priv->dev); ++ ++ return 0; ++} ++ ++static enum can_state get_of_chip_status(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ u8 can_stat, eir; ++ ++ can_stat = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ eir = can_ioread8(priv->reg_base + CANFD_ERRINT_OFFSET); ++ ++ if (can_stat & CAN_FD_SET_BUSOFF_MASK) ++ return CAN_STATE_BUS_OFF; ++ ++ if ((eir & CAN_FD_SET_EPASS_MASK) && ~(can_stat & CAN_FD_SET_BUSOFF_MASK)) ++ return CAN_STATE_ERROR_PASSIVE; ++ ++ if (eir & CAN_FD_SET_EWARN_MASK && ~(eir & CAN_FD_SET_EPASS_MASK)) ++ return CAN_STATE_ERROR_WARNING; ++ ++ if (~(eir & CAN_FD_SET_EPASS_MASK)) ++ return CAN_STATE_ERROR_ACTIVE; ++ ++ return CAN_STATE_ERROR_ACTIVE; ++} ++ ++static void canfd_error_interrupt(struct net_device *ndev, u8 isr, u8 eir) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ struct net_device_stats *stats = &ndev->stats; ++ struct can_frame *cf; ++ struct sk_buff *skb; ++ u8 koer, recnt = 0, tecnt = 0, can_stat = 0; ++ ++ skb = alloc_can_err_skb(ndev, &cf); ++ ++ koer = can_ioread8(priv->reg_base + CANFD_EALCAP_OFFSET) & CAN_FD_SET_KOER_MASK; ++ recnt = can_ioread8(priv->reg_base + CANFD_RECNT_OFFSET); ++ tecnt = can_ioread8(priv->reg_base + CANFD_TECNT_OFFSET); ++ ++ /*Read can status*/ ++ can_stat = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ /* Bus off --->active error mode */ ++ if ((isr & CAN_FD_SET_EIF_MASK) && priv->can.state == CAN_STATE_BUS_OFF) ++ priv->can.state = get_of_chip_status(ndev); ++ ++ /* State selection */ ++ if (can_stat & CAN_FD_SET_BUSOFF_MASK) { ++ priv->can.state = get_of_chip_status(ndev); ++ priv->can.can_stats.bus_off++; ++ canfd_reigister_set_bit(priv, CANFD_CFG_STAT_OFFSET, CAN_FD_SET_BUSOFF_MASK); ++ can_bus_off(ndev); ++ if (skb) ++ cf->can_id |= CAN_ERR_BUSOFF; ++ ++ } else if ((eir & CAN_FD_SET_EPASS_MASK) && ~(can_stat & CAN_FD_SET_BUSOFF_MASK)) { ++ priv->can.state = get_of_chip_status(ndev); ++ priv->can.can_stats.error_passive++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_CRTL; ++ cf->data[1] |= (recnt > 127) ? CAN_ERR_CRTL_RX_PASSIVE : 0; ++ cf->data[1] |= (tecnt > 127) ? CAN_ERR_CRTL_TX_PASSIVE : 0; ++ cf->data[6] = tecnt; ++ cf->data[7] = recnt; ++ } ++ } else if (eir & CAN_FD_SET_EWARN_MASK && ~(eir & CAN_FD_SET_EPASS_MASK)) { ++ priv->can.state = get_of_chip_status(ndev); ++ priv->can.can_stats.error_warning++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_CRTL; ++ cf->data[1] |= (recnt > 95) ? CAN_ERR_CRTL_RX_WARNING : 0; ++ cf->data[1] |= (tecnt > 95) ? CAN_ERR_CRTL_TX_WARNING : 0; ++ cf->data[6] = tecnt; ++ cf->data[7] = recnt; ++ } ++ } ++ ++ /* Check for in protocol defined error interrupt */ ++ if (eir & CAN_FD_SET_BEIF_MASK) { ++ if (skb) ++ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; ++ ++ /* bit error interrupt */ ++ if (koer == CAN_FD_SET_BIT_ERROR_MASK) { ++ stats->tx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[2] = CAN_ERR_PROT_BIT; ++ } ++ } ++ /* format error interrupt */ ++ if (koer == CAN_FD_SET_FORM_ERROR_MASK) { ++ stats->rx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[2] = CAN_ERR_PROT_FORM; ++ } ++ } ++ /* stuffing error interrupt */ ++ if (koer == CAN_FD_SET_STUFF_ERROR_MASK) { ++ stats->rx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[3] = CAN_ERR_PROT_STUFF; ++ } ++ } ++ /* ack error interrupt */ ++ if (koer == CAN_FD_SET_ACK_ERROR_MASK) { ++ stats->tx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[2] = CAN_ERR_PROT_LOC_ACK; ++ } ++ } ++ /* crc error interrupt */ ++ if (koer == CAN_FD_SET_CRC_ERROR_MASK) { ++ stats->rx_errors++; ++ if (skb) { ++ cf->can_id |= CAN_ERR_PROT; ++ cf->data[2] = CAN_ERR_PROT_LOC_CRC_SEQ; ++ } ++ } ++ priv->can.can_stats.bus_error++; ++ } ++ if (skb) { ++ stats->rx_packets++; ++ stats->rx_bytes += cf->can_dlc; ++ netif_rx(skb); ++ } ++ ++ netdev_dbg(ndev, "Recnt is 0x%02x", can_ioread8(priv->reg_base + CANFD_RECNT_OFFSET)); ++ netdev_dbg(ndev, "Tecnt is 0x%02x", can_ioread8(priv->reg_base + CANFD_TECNT_OFFSET)); ++} ++ ++static irqreturn_t canfd_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *ndev = (struct net_device *)dev_id; ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ u8 isr, eir; ++ u8 isr_handled = 0, eir_handled = 0; ++ ++ /* read the value of interrupt status register */ ++ isr = can_ioread8(priv->reg_base + CANFD_RTIF_OFFSET); ++ ++ /* read the value of error interrupt register */ ++ eir = can_ioread8(priv->reg_base + CANFD_ERRINT_OFFSET); ++ ++ /* Check for Tx interrupt and Processing it */ ++ if (isr & (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK)) { ++ canfd_tx_interrupt(ndev, isr); ++ isr_handled |= (CAN_FD_SET_TPIF_MASK | CAN_FD_SET_TSIF_MASK); ++ } ++ if (isr & (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK)) { ++ canfd_rxfull_interrupt(ndev, isr); ++ isr_handled |= (CAN_FD_SET_RAFIF_MASK | CAN_FD_SET_RFIF_MASK); ++ } ++ /* Check Rx interrupt and Processing the receive interrupt routine */ ++ if (isr & CAN_FD_SET_RIF_MASK) { ++ canfd_reigister_off_bit(priv, CANFD_RTIE_OFFSET, CAN_FD_OFF_RIE_MASK); ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, CAN_FD_SET_RIF_MASK); ++ ++ napi_schedule(&priv->napi); ++ isr_handled |= CAN_FD_SET_RIF_MASK; ++ } ++ if ((isr & CAN_FD_SET_EIF_MASK) | (eir & (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK))) { ++ /* reset EPIF and BEIF. Reset EIF */ ++ canfd_reigister_set_bit(priv, CANFD_ERRINT_OFFSET, ++ eir & (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK)); ++ canfd_reigister_set_bit(priv, CANFD_RTIF_OFFSET, ++ isr & CAN_FD_SET_EIF_MASK); ++ ++ canfd_error_interrupt(ndev, isr, eir); ++ ++ isr_handled |= CAN_FD_SET_EIF_MASK; ++ eir_handled |= (CAN_FD_SET_EPIF_MASK | CAN_FD_SET_BEIF_MASK); ++ } ++ if ((isr_handled == 0) && (eir_handled == 0)) { ++ netdev_err(ndev, "Unhandled interrupt!\n"); ++ return IRQ_NONE; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int canfd_chip_start(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int err; ++ u8 ret; ++ ++ err = set_reset_mode(ndev); ++ if (err) { ++ netdev_err(ndev, "Mode Resetting Failed!\n"); ++ return err; ++ } ++ ++ err = canfd_device_driver_bittime_configuration(ndev); ++ if (err) { ++ netdev_err(ndev, "Bittime Setting Failed!\n"); ++ return err; ++ } ++ ++ /* Set Almost Full Warning Limit */ ++ canfd_reigister_set_bit(priv, CANFD_LIMIT_OFFSET, CAN_FD_SET_AFWL_MASK); ++ ++ /* Programmable Error Warning Limit = (EWL+1)*8. Set EWL=11->Error Warning=96 */ ++ canfd_reigister_set_bit(priv, CANFD_LIMIT_OFFSET, CAN_FD_SET_EWL_MASK); ++ ++ /* Interrupts enable */ ++ can_iowrite8(CAN_FD_INTR_ALL_MASK, priv->reg_base + CANFD_RTIE_OFFSET); ++ ++ /* Error Interrupts enable(Error Passive and Bus Error) */ ++ canfd_reigister_set_bit(priv, CANFD_ERRINT_OFFSET, CAN_FD_SET_EPIE_MASK); ++ ++ ret = can_ioread8(priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ /* Check whether it is loopback mode or normal mode */ ++ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { ++ ret |= CAN_FD_LBMIMOD_MASK; ++ } else { ++ ret &= ~CAN_FD_LBMEMOD_MASK; ++ ret &= ~CAN_FD_LBMIMOD_MASK; ++ } ++ ++ can_iowrite8(ret, priv->reg_base + CANFD_CFG_STAT_OFFSET); ++ ++ priv->can.state = CAN_STATE_ERROR_ACTIVE; ++ ++ return 0; ++} ++ ++static int canfd_do_set_mode(struct net_device *ndev, enum can_mode mode) ++{ ++ int ret; ++ ++ switch (mode) { ++ case CAN_MODE_START: ++ ret = canfd_chip_start(ndev); ++ if (ret) { ++ netdev_err(ndev, "Could Not Start CAN device !!\n"); ++ return ret; ++ } ++ netif_wake_queue(ndev); ++ break; ++ default: ++ ret = -EOPNOTSUPP; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int canfd_driver_open(struct net_device *ndev) ++{ ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int ret; ++ ++ ret = pm_runtime_get_sync(priv->dev); ++ if (ret < 0) { ++ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", ++ __func__, ret); ++ goto err; ++ } ++ ++ /* Set chip into reset mode */ ++ ret = set_reset_mode(ndev); ++ if (ret) { ++ netdev_err(ndev, "Mode Resetting Failed!\n"); ++ return ret; ++ } ++ ++ /* Common open */ ++ ret = open_candev(ndev); ++ if (ret) ++ return ret; ++ ++ /* Register interrupt handler */ ++ ret = request_irq(ndev->irq, canfd_interrupt, IRQF_SHARED, ndev->name, ndev); ++ if (ret) { ++ netdev_err(ndev, "Request_irq err: %d\n", ret); ++ goto exit_irq; ++ } ++ ++ ret = canfd_chip_start(ndev); ++ if (ret) { ++ netdev_err(ndev, "Could Not Start CAN device !\n"); ++ goto exit_can_start; ++ } ++ ++ napi_enable(&priv->napi); ++ netif_start_queue(ndev); ++ ++ return 0; ++ ++exit_can_start: ++ free_irq(ndev->irq, ndev); ++err: ++ pm_runtime_put(priv->dev); ++exit_irq: ++ close_candev(ndev); ++ return ret; ++} ++ ++static int canfd_control_parse_dt(struct ipms_canfd_priv *priv) ++{ ++ struct of_phandle_args args; ++ u32 syscon_mask, syscon_shift; ++ u32 can_or_canfd; ++ u32 syscon_offset, regval; ++ int ret; ++ ++ ret = of_parse_phandle_with_fixed_args(priv->dev->of_node, ++ "starfive,sys-syscon", 3, 0, &args); ++ if (ret) { ++ dev_err(priv->dev, "Failed to parse starfive,sys-syscon\n"); ++ return -EINVAL; ++ } ++ ++ priv->reg_syscon = syscon_node_to_regmap(args.np); ++ of_node_put(args.np); ++ if (IS_ERR(priv->reg_syscon)) ++ return PTR_ERR(priv->reg_syscon); ++ ++ syscon_offset = args.args[0]; ++ syscon_shift = args.args[1]; ++ syscon_mask = args.args[2]; ++ ++ ret = device_property_read_u32(priv->dev, "syscon,can_or_canfd", &can_or_canfd); ++ if (ret) ++ goto exit_parse; ++ ++ priv->can_or_canfd = can_or_canfd; ++ ++ /* enable can2.0/canfd function */ ++ regval = can_or_canfd << syscon_shift; ++ ret = regmap_update_bits(priv->reg_syscon, syscon_offset, syscon_mask, regval); ++ if (ret) ++ return ret; ++ return 0; ++exit_parse: ++ return ret; ++} ++ ++static const struct net_device_ops canfd_netdev_ops = { ++ .ndo_open = canfd_driver_open, ++ .ndo_stop = canfd_driver_close, ++ .ndo_start_xmit = canfd_driver_start_xmit, ++ .ndo_change_mtu = can_change_mtu, ++}; ++ ++static int canfd_driver_probe(struct platform_device *pdev) ++{ ++ struct net_device *ndev; ++ struct ipms_canfd_priv *priv; ++ void __iomem *addr; ++ int ret; ++ u32 frq; ++ ++ addr = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(addr)) { ++ ret = PTR_ERR(addr); ++ goto exit; ++ } ++ ++ ndev = alloc_candev(sizeof(struct ipms_canfd_priv), 1); ++ if (!ndev) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ priv = netdev_priv(ndev); ++ priv->dev = &pdev->dev; ++ ++ ret = canfd_control_parse_dt(priv); ++ if (ret) ++ goto free_exit; ++ ++ priv->nr_clks = devm_clk_bulk_get_all(priv->dev, &priv->clks); ++ if (priv->nr_clks < 0) { ++ dev_err(priv->dev, "Failed to get can clocks\n"); ++ ret = -ENODEV; ++ goto free_exit; ++ } ++ ++ ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks); ++ if (ret) { ++ dev_err(priv->dev, "Failed to enable clocks\n"); ++ goto free_exit; ++ } ++ ++ priv->resets = devm_reset_control_array_get_exclusive(priv->dev); ++ if (IS_ERR(priv->resets)) { ++ ret = PTR_ERR(priv->resets); ++ dev_err(priv->dev, "Failed to get can resets"); ++ goto clk_exit; ++ } ++ ++ ret = reset_control_deassert(priv->resets); ++ if (ret) ++ goto clk_exit; ++ priv->can.bittiming_const = &canfd_bittiming_const; ++ priv->can.data_bittiming_const = &canfd_data_bittiming_const; ++ priv->can.do_set_mode = canfd_do_set_mode; ++ ++ /* in user space the execution mode can be chosen */ ++ if (priv->can_or_canfd == IPMS_CAN_TYPE_CANFD) ++ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_FD; ++ else ++ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK; ++ priv->reg_base = addr; ++ priv->write_reg = canfd_write_reg_le; ++ priv->read_reg = canfd_read_reg_le; ++ ++ pm_runtime_enable(&pdev->dev); ++ ++ priv->can_clk = devm_clk_get(&pdev->dev, "core_clk"); ++ if (IS_ERR(priv->can_clk)) { ++ dev_err(&pdev->dev, "Device clock not found.\n"); ++ ret = PTR_ERR(priv->can_clk); ++ goto reset_exit; ++ } ++ ++ device_property_read_u32(priv->dev, "frequency", &frq); ++ clk_set_rate(priv->can_clk, frq); ++ ++ priv->can.clock.freq = clk_get_rate(priv->can_clk); ++ ndev->irq = platform_get_irq(pdev, 0); ++ ++ /* we support local echo */ ++ ndev->flags |= IFF_ECHO; ++ ndev->netdev_ops = &canfd_netdev_ops; ++ ++ platform_set_drvdata(pdev, ndev); ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ netif_napi_add(ndev, &priv->napi, canfd_rx_poll); ++ ret = register_candev(ndev); ++ if (ret) { ++ dev_err(&pdev->dev, "Fail to register failed (err=%d)\n", ret); ++ goto reset_exit; ++ } ++ ++ dev_dbg(&pdev->dev, "Driver registered: regs=%p, irp=%d, clock=%d\n", ++ priv->reg_base, ndev->irq, priv->can.clock.freq); ++ ++ return 0; ++ ++reset_exit: ++ reset_control_assert(priv->resets); ++clk_exit: ++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks); ++free_exit: ++ free_candev(ndev); ++exit: ++ return ret; ++} ++ ++static int canfd_driver_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ reset_control_assert(priv->resets); ++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks); ++ pm_runtime_disable(&pdev->dev); ++ ++ unregister_candev(ndev); ++ netif_napi_del(&priv->napi); ++ free_candev(ndev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int __maybe_unused canfd_suspend(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ ++ if (netif_running(ndev)) { ++ netif_stop_queue(ndev); ++ netif_device_detach(ndev); ++ canfd_driver_stop(ndev); ++ } ++ ++ return pm_runtime_force_suspend(dev); ++} ++ ++static int __maybe_unused canfd_resume(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pm_runtime_force_resume(dev); ++ if (ret) { ++ dev_err(dev, "pm_runtime_force_resume failed on resume\n"); ++ return ret; ++ } ++ ++ if (netif_running(ndev)) { ++ ret = canfd_chip_start(ndev); ++ if (ret) { ++ dev_err(dev, "canfd_chip_start failed on resume\n"); ++ return ret; ++ } ++ ++ netif_device_attach(ndev); ++ netif_start_queue(ndev); ++ } ++ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM ++static int canfd_runtime_suspend(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ ++ reset_control_assert(priv->resets); ++ clk_bulk_disable_unprepare(priv->nr_clks, priv->clks); ++ ++ return 0; ++} ++ ++static int canfd_runtime_resume(struct device *dev) ++{ ++ struct net_device *ndev = dev_get_drvdata(dev); ++ struct ipms_canfd_priv *priv = netdev_priv(ndev); ++ int ret; ++ ++ ret = clk_bulk_prepare_enable(priv->nr_clks, priv->clks); ++ if (ret) { ++ dev_err(dev, "Failed to prepare_enable clk\n"); ++ return ret; ++ } ++ ++ ret = reset_control_deassert(priv->resets); ++ if (ret) { ++ dev_err(dev, "Failed to deassert reset\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops canfd_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(canfd_suspend, canfd_resume) ++ SET_RUNTIME_PM_OPS(canfd_runtime_suspend, ++ canfd_runtime_resume, NULL) ++}; ++ ++static const struct of_device_id canfd_of_match[] = { ++ { .compatible = "ipms,can" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, canfd_of_match); ++ ++static struct platform_driver can_driver = { ++ .probe = canfd_driver_probe, ++ .remove = canfd_driver_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .pm = &canfd_pm_ops, ++ .of_match_table = canfd_of_match, ++ }, ++}; ++ ++module_platform_driver(can_driver); ++ ++MODULE_DESCRIPTION("ipms can controller driver for StarFive jh7110 SoC"); ++MODULE_AUTHOR("William Qiu +Date: Fri, 9 Jun 2023 14:57:13 +0800 +Subject: [PATCH 071/116] regulator: starfive-jh7110: Add regulator support for + JH7110 A type EVB. + +Add 7 regulators base on regulator framework for +JH7110 evb HW design. + +Signed-off-by: Kevin.xie +--- + drivers/regulator/Kconfig | 10 ++ + drivers/regulator/Makefile | 1 + + drivers/regulator/starfive-jh7110-regulator.c | 126 ++++++++++++++++++ + include/linux/regulator/jh7110.h | 24 ++++ + 4 files changed, 161 insertions(+) + create mode 100644 drivers/regulator/starfive-jh7110-regulator.c + create mode 100644 include/linux/regulator/jh7110.h + +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -1335,6 +1335,16 @@ config REGULATOR_SM5703 + This driver provides support for voltage regulators of SM5703 + multi-function device. + ++config REGULATOR_STARFIVE_JH7110 ++ tristate "Starfive JH7110 PMIC" ++ depends on I2C ++ select REGMAP_I2C ++ help ++ Say y here to select this option to enable the power regulator of ++ Starfive JH7110 PMIC. ++ This driver supports the control of different power rails of device ++ through regulator interface. ++ + config REGULATOR_STM32_BOOSTER + tristate "STMicroelectronics STM32 BOOSTER" + depends on ARCH_STM32 || COMPILE_TEST +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -156,6 +156,7 @@ obj-$(CONFIG_REGULATOR_SC2731) += sc2731 + obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o + obj-$(CONFIG_REGULATOR_SLG51000) += slg51000-regulator.o + obj-$(CONFIG_REGULATOR_SM5703) += sm5703-regulator.o ++obj-$(CONFIG_REGULATOR_STARFIVE_JH7110) += starfive-jh7110-regulator.o + obj-$(CONFIG_REGULATOR_STM32_BOOSTER) += stm32-booster.o + obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o + obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o +--- /dev/null ++++ b/drivers/regulator/starfive-jh7110-regulator.c +@@ -0,0 +1,126 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2022 Starfive Technology Co., Ltd. ++ * Author: Mason Huo ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define JH7110_PM_POWER_SW_0 0x80 ++#define JH7110_PM_POWER_SW_1 0x81 ++#define ENABLE_MASK(id) BIT(id) ++ ++ ++static const struct regmap_config jh7110_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = JH7110_PM_POWER_SW_1, ++ .cache_type = REGCACHE_FLAT, ++}; ++ ++static const struct regulator_ops jh7110_ldo_ops = { ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .is_enabled = regulator_is_enabled_regmap, ++}; ++ ++#define JH7110_LDO(_id, _name, en_reg, en_mask) \ ++{\ ++ .name = (_name),\ ++ .ops = &jh7110_ldo_ops,\ ++ .of_match = of_match_ptr(_name),\ ++ .regulators_node = of_match_ptr("regulators"),\ ++ .type = REGULATOR_VOLTAGE,\ ++ .id = JH7110_ID_##_id,\ ++ .owner = THIS_MODULE,\ ++ .enable_reg = JH7110_PM_POWER_SW_##en_reg,\ ++ .enable_mask = ENABLE_MASK(en_mask),\ ++} ++ ++static const struct regulator_desc jh7110_regulators[] = { ++ JH7110_LDO(LDO_REG1, "hdmi_1p8", 0, 0), ++ JH7110_LDO(LDO_REG2, "mipitx_1p8", 0, 1), ++ JH7110_LDO(LDO_REG3, "mipirx_1p8", 0, 2), ++ JH7110_LDO(LDO_REG4, "hdmi_0p9", 0, 3), ++ JH7110_LDO(LDO_REG5, "mipitx_0p9", 0, 4), ++ JH7110_LDO(LDO_REG6, "mipirx_0p9", 0, 5), ++ JH7110_LDO(LDO_REG7, "sdio_vdd", 1, 0), ++}; ++ ++static int jh7110_i2c_probe(struct i2c_client *i2c) ++{ ++ struct regulator_config config = { }; ++ struct regulator_dev *rdev; ++ struct regulator_init_data *init_data; ++ struct regmap *regmap; ++ int i, ret; ++ ++ regmap = devm_regmap_init_i2c(i2c, &jh7110_regmap_config); ++ if (IS_ERR(regmap)) { ++ ret = PTR_ERR(regmap); ++ dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ++ ret); ++ return ret; ++ } ++ ++ init_data = of_get_regulator_init_data(&i2c->dev, i2c->dev.of_node, NULL); ++ if (!init_data) ++ return -ENOMEM; ++ config.init_data = init_data; ++ ++ for (i = 0; i < JH7110_MAX_REGULATORS; i++) { ++ config.dev = &i2c->dev; ++ config.regmap = regmap; ++ ++ rdev = devm_regulator_register(&i2c->dev, ++ &jh7110_regulators[i], &config); ++ if (IS_ERR(rdev)) { ++ dev_err(&i2c->dev, ++ "Failed to register JH7110 regulator\n"); ++ return PTR_ERR(rdev); ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct i2c_device_id jh7110_i2c_id[] = { ++ {"jh7110_evb_reg", 0}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, jh7110_i2c_id); ++ ++#ifdef CONFIG_OF ++static const struct of_device_id jh7110_dt_ids[] = { ++ { .compatible = "starfive,jh7110-evb-regulator", ++ .data = &jh7110_i2c_id[0] }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, jh7110_dt_ids); ++#endif ++ ++static struct i2c_driver jh7110_regulator_driver = { ++ .driver = { ++ .name = "jh7110-evb-regulator", ++ .of_match_table = of_match_ptr(jh7110_dt_ids), ++ }, ++ .probe = jh7110_i2c_probe, ++ .id_table = jh7110_i2c_id, ++}; ++ ++module_i2c_driver(jh7110_regulator_driver); ++ ++MODULE_AUTHOR("Mason Huo "); ++MODULE_DESCRIPTION("Regulator device driver for Starfive JH7110"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/regulator/jh7110.h +@@ -0,0 +1,24 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2022 Starfive Technology Co., Ltd. ++ * Author: Mason Huo ++ */ ++ ++#ifndef __LINUX_REGULATOR_JH7110_H ++#define __LINUX_REGULATOR_JH7110_H ++ ++#define JH7110_MAX_REGULATORS 7 ++ ++ ++enum jh7110_reg_id { ++ JH7110_ID_LDO_REG1 = 0, ++ JH7110_ID_LDO_REG2, ++ JH7110_ID_LDO_REG3, ++ JH7110_ID_LDO_REG4, ++ JH7110_ID_LDO_REG5, ++ JH7110_ID_LDO_REG6, ++ JH7110_ID_LDO_REG7, ++}; ++ ++ ++#endif /* __LINUX_REGULATOR_JH7110_H */ diff --git a/target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch b/target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch new file mode 100644 index 0000000000..d602df9e4c --- /dev/null +++ b/target/linux/starfive/patches-6.6/0072-drivers-nvme-Add-precheck-and-delay-for-CQE-pending-.patch @@ -0,0 +1,40 @@ +From f0b4cffe4d1813305f783d208f260747ecc56c50 Mon Sep 17 00:00:00 2001 +From: "Kevin.xie" +Date: Thu, 24 Nov 2022 16:59:12 +0800 +Subject: [PATCH 072/116] drivers: nvme: Add precheck and delay for CQE pending + status. + +To workaroud the NVMe I/O timeout problem in bootup S10udev case +which caused by the CQE update lantancy. + +Signed-off-by: Kevin.xie +--- + drivers/nvme/host/pci.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + #include "trace.h" + #include "nvme.h" +@@ -1062,6 +1063,15 @@ static inline int nvme_poll_cq(struct nv + { + int found = 0; + ++ /* ++ * In some cases, such as udev trigger, cqe status may update ++ * a little bit later than MSI, which cause an irq handle missing. ++ * To workaound, here we will prefetch the status first, and wait ++ * 1us if we get nothing. ++ */ ++ if (!nvme_cqe_pending(nvmeq)) ++ udelay(1); ++ + while (nvme_cqe_pending(nvmeq)) { + found++; + /* diff --git a/target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch b/target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch new file mode 100644 index 0000000000..fff01c4c8f --- /dev/null +++ b/target/linux/starfive/patches-6.6/0073-RISC-V-Create-unique-identification-for-SoC-PMU.patch @@ -0,0 +1,93 @@ +From eb294df4b9fab46bc5dbf676edf51e28e06d1968 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?= + +Date: Tue, 16 Nov 2021 15:48:09 +0000 +Subject: [PATCH 073/116] RISC-V: Create unique identification for SoC PMU +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The SBI PMU platform driver did not provide any identification for +perf events matching. This patch introduces a new sysfs file inside the +platform device (soc:pmu/id) for pmu identification. + +The identification is a 64-bit value generated as: +[63-32]: mvendorid; +[31]: marchid[MSB]; +[30-16]: marchid[15-0]; +[15-0]: mimpid[15MSBs]; + +The CSRs are detailed in the RISC-V privileged spec [1]. +The marchid is split in MSB + 15LSBs, due to the MSB being used for +open-source architecture identification. + +[1] https://github.com/riscv/riscv-isa-manual + +Signed-off-by: João Mário Domingos +--- + drivers/perf/riscv_pmu_sbi.c | 47 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +--- a/drivers/perf/riscv_pmu_sbi.c ++++ b/drivers/perf/riscv_pmu_sbi.c +@@ -1019,6 +1019,46 @@ static struct ctl_table sbi_pmu_sysctl_t + { } + }; + ++static uint64_t pmu_sbi_get_pmu_id(void) ++{ ++ union sbi_pmu_id { ++ uint64_t value; ++ struct { ++ uint16_t imp:16; ++ uint16_t arch:16; ++ uint32_t vendor:32; ++ }; ++ } pmuid; ++ ++ pmuid.value = 0; ++ pmuid.vendor = (uint32_t) sbi_get_mvendorid(); ++ pmuid.arch = (sbi_get_marchid() >> (63 - 15) & (1 << 15)) | (sbi_get_marchid() & 0x7FFF); ++ pmuid.imp = (sbi_get_mimpid() >> 16); ++ ++ return pmuid.value; ++} ++ ++static ssize_t pmu_sbi_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ int len; ++ ++ len = sprintf(buf, "0x%llx\n", pmu_sbi_get_pmu_id()); ++ if (len <= 0) ++ dev_err(dev, "mydrv: Invalid sprintf len: %dn", len); ++ ++ return len; ++} ++ ++static DEVICE_ATTR(id, S_IRUGO | S_IWUSR, pmu_sbi_id_show, 0); ++ ++static struct attribute *pmu_sbi_attrs[] = { ++ &dev_attr_id.attr, ++ NULL ++}; ++ ++ATTRIBUTE_GROUPS(pmu_sbi); ++ + static int pmu_sbi_device_probe(struct platform_device *pdev) + { + struct riscv_pmu *pmu = NULL; +@@ -1067,6 +1107,13 @@ static int pmu_sbi_device_probe(struct p + pmu->event_unmapped = pmu_sbi_event_unmapped; + pmu->csr_index = pmu_sbi_csr_index; + ++ ret = sysfs_create_group(&pdev->dev.kobj, &pmu_sbi_group); ++ if (ret) { ++ dev_err(&pdev->dev, "sysfs creation failed\n"); ++ return ret; ++ } ++ pdev->dev.groups = pmu_sbi_groups; ++ + ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node); + if (ret) + return ret; diff --git a/target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch b/target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch new file mode 100644 index 0000000000..177b3b32d8 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0074-RISC-V-Support-CPUID-for-risc-v-in-perf.patch @@ -0,0 +1,55 @@ +From 1dc069ffadf4ce7817a716f9df2f480254e9b01d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?= + +Date: Tue, 16 Nov 2021 15:48:10 +0000 +Subject: [PATCH 074/116] RISC-V: Support CPUID for risc-v in perf +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch creates the header.c file for the risc-v architecture and introduces support for +PMU identification through sysfs. +It is now possible to configure pmu-events in risc-v. + +Depends on patch [1], that introduces the id sysfs file. + +Signed-off-by: João Mário Domingos +Signed-off-by: minda.chen +--- + drivers/perf/riscv_pmu.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/drivers/perf/riscv_pmu.c ++++ b/drivers/perf/riscv_pmu.c +@@ -18,6 +18,23 @@ + + #include + ++PMU_FORMAT_ATTR(event, "config:0-63"); ++ ++static struct attribute *riscv_arch_formats_attr[] = { ++ &format_attr_event.attr, ++ NULL, ++}; ++ ++static struct attribute_group riscv_pmu_format_group = { ++ .name = "format", ++ .attrs = riscv_arch_formats_attr, ++}; ++ ++static const struct attribute_group *riscv_pmu_attr_groups[] = { ++ &riscv_pmu_format_group, ++ NULL, ++}; ++ + static bool riscv_perf_user_access(struct perf_event *event) + { + return ((event->attr.type == PERF_TYPE_HARDWARE) || +@@ -410,6 +427,7 @@ struct riscv_pmu *riscv_pmu_alloc(void) + cpuc->events[i] = NULL; + } + pmu->pmu = (struct pmu) { ++ .attr_groups = riscv_pmu_attr_groups, + .event_init = riscv_pmu_event_init, + .event_mapped = riscv_pmu_event_mapped, + .event_unmapped = riscv_pmu_event_unmapped, diff --git a/target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch b/target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch new file mode 100644 index 0000000000..01bb4f19ca --- /dev/null +++ b/target/linux/starfive/patches-6.6/0075-RISC-V-Added-generic-pmu-events-mapfile.patch @@ -0,0 +1,42 @@ +From 3e6ea12dda276c01a756764fcafa315b19860c33 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jo=C3=A3o=20M=C3=A1rio=20Domingos?= + +Date: Tue, 16 Nov 2021 15:48:11 +0000 +Subject: [PATCH 075/116] RISC-V: Added generic pmu-events mapfile +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The pmu-events now supports custom events for RISC-V, plus the cycle, +time and instret events were defined. + +Signed-off-by: João Mário Domingos +--- + .../pmu-events/arch/riscv/riscv-generic.json | 20 +++++++++++++++++++ + 1 file changed, 20 insertions(+) + create mode 100644 tools/perf/pmu-events/arch/riscv/riscv-generic.json + +--- /dev/null ++++ b/tools/perf/pmu-events/arch/riscv/riscv-generic.json +@@ -0,0 +1,20 @@ ++[ ++ { ++ "PublicDescription": "CPU Cycles", ++ "EventCode": "0x00", ++ "EventName": "riscv_cycles", ++ "BriefDescription": "CPU cycles RISC-V generic counter" ++ }, ++ { ++ "PublicDescription": "CPU Time", ++ "EventCode": "0x01", ++ "EventName": "riscv_time", ++ "BriefDescription": "CPU time RISC-V generic counter" ++ }, ++ { ++ "PublicDescription": "CPU Instructions", ++ "EventCode": "0x02", ++ "EventName": "riscv_instret", ++ "BriefDescription": "CPU retired instructions RISC-V generic counter" ++ } ++] +\ No newline at end of file diff --git a/target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch b/target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch new file mode 100644 index 0000000000..152a60cfda --- /dev/null +++ b/target/linux/starfive/patches-6.6/0076-perf-sbi-disable-cpu-hotplug-callback.patch @@ -0,0 +1,30 @@ +From 30e0cdcf9e05faa65ecde4ed8b70039568fdb660 Mon Sep 17 00:00:00 2001 +From: Minda Chen +Date: Thu, 2 Mar 2023 17:16:01 +0800 +Subject: [PATCH 076/116] perf: sbi: disable cpu hotplug callback. + +register cpu hotplug callback will cause dhrystone +and coremark benchmark reduce the scores. this CPU +hotplug ops will do in sbi cpu/on and off. So disable +this no side effect. + +Signed-off-by: Minda Chen +Signed-off-by: Hal Feng +--- + drivers/perf/riscv_pmu_sbi.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/perf/riscv_pmu_sbi.c ++++ b/drivers/perf/riscv_pmu_sbi.c +@@ -1114,9 +1114,11 @@ static int pmu_sbi_device_probe(struct p + } + pdev->dev.groups = pmu_sbi_groups; + ++#ifndef CONFIG_ARCH_STARFIVE + ret = cpuhp_state_add_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node); + if (ret) + return ret; ++#endif + + ret = riscv_pm_pmu_register(pmu); + if (ret) diff --git a/target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch b/target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch new file mode 100644 index 0000000000..cdc6e4ec26 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0077-dmaengine-dw-axi-dmac-Drop-unused-print-message.patch @@ -0,0 +1,24 @@ +From fc4b5c7c27e1b56b1f848e50511c4fd081b1b6c5 Mon Sep 17 00:00:00 2001 +From: Walker Chen +Date: Mon, 12 Jun 2023 21:21:45 +0800 +Subject: [PATCH 077/116] dmaengine: dw-axi-dmac: Drop unused print message + +Removed printing information which is not related to StarFive +platform. + +Signed-off-by: Walker Chen +--- + drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c ++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +@@ -523,7 +523,7 @@ static void dw_axi_dma_set_hw_channel(st + unsigned long reg_value, val; + + if (!chip->apb_regs) { +- dev_err(chip->dev, "apb_regs not initialized\n"); ++ dev_dbg(chip->dev, "apb_regs not initialized\n"); + return; + } + diff --git a/target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch b/target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch new file mode 100644 index 0000000000..2a828974ce --- /dev/null +++ b/target/linux/starfive/patches-6.6/0079-ASoC-codecs-Add-AC108-Codec-driver.patch @@ -0,0 +1,4748 @@ +From cd2254c6be9441ebacaa35693ecb5ce116b90622 Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Fri, 16 Jun 2023 16:27:46 +0800 +Subject: [PATCH 079/116] ASoC: codecs: Add AC108 Codec driver + +Add AC108 Codec driver and AC101 driver for AC10x. + +Signed-off-by: Xingyu Wu +Signed-off-by: Hal Feng +--- + sound/soc/codecs/Kconfig | 5 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/ac101.c | 1716 +++++++++++++++++++++++++++++++++ + sound/soc/codecs/ac101_regs.h | 431 +++++++++ + sound/soc/codecs/ac108.c | 1622 +++++++++++++++++++++++++++++++ + sound/soc/codecs/ac108.h | 749 ++++++++++++++ + sound/soc/codecs/ac10x.h | 152 +++ + 7 files changed, 4677 insertions(+) + create mode 100644 sound/soc/codecs/ac101.c + create mode 100644 sound/soc/codecs/ac101_regs.h + create mode 100644 sound/soc/codecs/ac108.c + create mode 100644 sound/soc/codecs/ac108.h + create mode 100644 sound/soc/codecs/ac10x.h + +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -16,6 +16,7 @@ config SND_SOC_ALL_CODECS + depends on COMPILE_TEST + imply SND_SOC_88PM860X + imply SND_SOC_AB8500_CODEC ++ imply SND_SOC_AC108 + imply SND_SOC_AC97_CODEC + imply SND_SOC_AD1836 + imply SND_SOC_AD193X_SPI +@@ -397,6 +398,10 @@ config SND_SOC_AB8500_CODEC + tristate + depends on ABX500_CORE + ++config SND_SOC_AC108 ++ tristate "AC108" ++ depends on I2C ++ + config SND_SOC_AC97_CODEC + tristate "Build generic ASoC AC97 CODEC driver" + select SND_AC97_CODEC +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -2,6 +2,7 @@ + snd-soc-88pm860x-objs := 88pm860x-codec.o + snd-soc-ab8500-codec-objs := ab8500-codec.o + snd-soc-ac97-objs := ac97.o ++snd-soc-ac108-objs := ac108.o ac101.o + snd-soc-ad1836-objs := ad1836.o + snd-soc-ad193x-objs := ad193x.o + snd-soc-ad193x-spi-objs := ad193x-spi.o +@@ -386,6 +387,7 @@ snd-soc-simple-mux-objs := simple-mux.o + + obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o + obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o ++obj-$(CONFIG_SND_SOC_AC108) += snd-soc-ac108.o + obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o + obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o + obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o +--- /dev/null ++++ b/sound/soc/codecs/ac101.c +@@ -0,0 +1,1716 @@ ++/* ++ * ac101.c ++ * ++ * (C) Copyright 2017-2018 ++ * Seeed Technology Co., Ltd. ++ * ++ * PeterYang ++ * ++ * (C) Copyright 2014-2017 ++ * Reuuimlla Technology Co., Ltd. ++ * ++ * huangxin ++ * liushaohua ++ * ++ * X-Powers AC101 codec driver ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ac101_regs.h" ++#include "ac10x.h" ++ ++/* #undef AC101_DEBG ++ * use 'make DEBUG=1' to enable debugging ++ */ ++ ++/* ++ * *** To sync channels *** ++ * ++ * 1. disable clock in codec hw_params() ++ * 2. clear fifo in bcm2835 hw_params() ++ * 3. clear fifo in bcm2385 prepare() ++ * 4. enable RX in bcm2835 trigger() ++ * 5. enable clock in machine trigger() ++ */ ++ ++/*Default initialize configuration*/ ++static bool speaker_double_used = 1; ++static int double_speaker_val = 0x1B; ++static int single_speaker_val = 0x19; ++static int headset_val = 0x3B; ++static int mainmic_val = 0x4; ++static int headsetmic_val = 0x4; ++static bool dmic_used = 0; ++static int adc_digital_val = 0xb0b0; ++static bool drc_used = false; ++ ++#define AC101_RATES (SNDRV_PCM_RATE_8000_96000 & \ ++ ~(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000 | \ ++ SNDRV_PCM_RATE_88200)) ++#define AC101_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_S24_LE |*/ \ ++ SNDRV_PCM_FMTBIT_S32_LE | \ ++ 0) ++ ++static struct ac10x_priv* static_ac10x; ++ ++ ++int ac101_read(struct snd_soc_codec *codec, unsigned reg) { ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int r, v = 0; ++ ++ if ((r = regmap_read(ac10x->regmap101, reg, &v)) < 0) { ++ dev_err(codec->dev, "read reg %02X fail\n", ++ reg); ++ return r; ++ } ++ return v; ++} ++ ++int ac101_write(struct snd_soc_codec *codec, unsigned reg, unsigned val) { ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int v; ++ ++ v = regmap_write(ac10x->regmap101, reg, val); ++ return v; ++} ++ ++int ac101_update_bits(struct snd_soc_codec *codec, unsigned reg, ++ unsigned mask, unsigned value ++) { ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int v; ++ ++ v = regmap_update_bits(ac10x->regmap101, reg, mask, value); ++ return v; ++} ++ ++ ++ ++#ifdef CONFIG_AC101_SWITCH_DETECT ++/******************************************************************************/ ++/********************************switch****************************************/ ++/******************************************************************************/ ++#define KEY_HEADSETHOOK 226 /* key define */ ++#define HEADSET_FILTER_CNT (10) ++ ++/* ++ * switch_hw_config:config the 53 codec register ++ */ ++static void switch_hw_config(struct snd_soc_codec *codec) ++{ ++ int r; ++ ++ AC101_DBG(); ++ ++ /*HMIC/MMIC BIAS voltage level select:2.5v*/ ++ ac101_update_bits(codec, OMIXER_BST1_CTRL, (0xf<state:%d\n", ac10x->state); ++ ++ input_report_switch(ac10x->inpdev, SW_HEADPHONE_INSERT, ac10x->state); ++ input_sync(ac10x->inpdev); ++ return; ++} ++ ++/* ++ * work_cb_clear_irq: clear audiocodec pending and Record the interrupt. ++ */ ++static void work_cb_clear_irq(struct work_struct *work) ++{ ++ int reg_val = 0; ++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_clear_irq); ++ struct snd_soc_codec *codec = ac10x->codec; ++ ++ ac10x->irq_cntr++; ++ ++ reg_val = ac101_read(codec, HMIC_STS); ++ if (BIT(HMIC_PULLOUT_PEND) & reg_val) { ++ ac10x->pullout_cntr++; ++ AC101_DBG("ac10x->pullout_cntr: %d\n", ac10x->pullout_cntr); ++ } ++ ++ reg_val |= HMIC_PEND_ALL; ++ ac101_write(codec, HMIC_STS, reg_val); ++ ++ reg_val = ac101_read(codec, HMIC_STS); ++ if ((reg_val & HMIC_PEND_ALL) != 0){ ++ reg_val |= HMIC_PEND_ALL; ++ ac101_write(codec, HMIC_STS, reg_val); ++ } ++ ++ if (cancel_work_sync(&ac10x->work_switch) != 0) { ++ ac10x->irq_cntr--; ++ } ++ ++ if (0 == schedule_work(&ac10x->work_switch)) { ++ ac10x->irq_cntr--; ++ AC101_DBG("[work_cb_clear_irq] add work struct failed!\n"); ++ } ++} ++ ++enum { ++ HBIAS_LEVEL_1 = 0x02, ++ HBIAS_LEVEL_2 = 0x0B, ++ HBIAS_LEVEL_3 = 0x13, ++ HBIAS_LEVEL_4 = 0x17, ++ HBIAS_LEVEL_5 = 0x19, ++}; ++ ++static int __ac101_get_hmic_data(struct snd_soc_codec *codec) { ++ #ifdef AC101_DEBG ++ static long counter; ++ #endif ++ int r, d; ++ ++ d = GET_HMIC_DATA(ac101_read(codec, HMIC_STS)); ++ ++ r = 0x1 << HMIC_DATA_PEND; ++ ac101_write(codec, HMIC_STS, r); ++ ++ /* prevent i2c accessing too frequently */ ++ usleep_range(1500, 3000); ++ ++ AC101_DBG("HMIC_DATA(%3ld): %02X\n", counter++, d); ++ return d; ++} ++ ++/* ++ * work_cb_earphone_switch: judge the status of the headphone ++ */ ++static void work_cb_earphone_switch(struct work_struct *work) ++{ ++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, work_switch); ++ struct snd_soc_codec *codec = ac10x->codec; ++ ++ static int hook_flag1 = 0, hook_flag2 = 0; ++ static int KEY_VOLUME_FLAG = 0; ++ ++ unsigned filter_buf = 0; ++ int filt_index = 0; ++ int t = 0; ++ ++ ac10x->irq_cntr--; ++ ++ /* read HMIC_DATA */ ++ t = __ac101_get_hmic_data(codec); ++ ++ if ((t >= HBIAS_LEVEL_2) && (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) { ++ t = __ac101_get_hmic_data(codec); ++ ++ if (t >= HBIAS_LEVEL_5){ ++ msleep(150); ++ t = __ac101_get_hmic_data(codec); ++ if (((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1 - 1) || t >= HBIAS_LEVEL_5) ++ && (ac10x->pullout_cntr == 0)) { ++ input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 1); ++ input_sync(ac10x->inpdev); ++ ++ AC101_DBG("KEY_HEADSETHOOK1\n"); ++ ++ if (hook_flag1 != hook_flag2) ++ hook_flag1 = hook_flag2 = 0; ++ hook_flag1++; ++ } ++ if (ac10x->pullout_cntr) ++ ac10x->pullout_cntr--; ++ } else if (t >= HBIAS_LEVEL_4) { ++ msleep(80); ++ t = __ac101_get_hmic_data(codec); ++ if (t < HBIAS_LEVEL_5 && t >= HBIAS_LEVEL_4 && (ac10x->pullout_cntr == 0)) { ++ KEY_VOLUME_FLAG = 1; ++ input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 1); ++ input_sync(ac10x->inpdev); ++ input_report_key(ac10x->inpdev, KEY_VOLUMEUP, 0); ++ input_sync(ac10x->inpdev); ++ ++ AC101_DBG("HMIC_DATA: %d KEY_VOLUMEUP\n", t); ++ } ++ if (ac10x->pullout_cntr) ++ ac10x->pullout_cntr--; ++ } else if (t >= HBIAS_LEVEL_3){ ++ msleep(80); ++ t = __ac101_get_hmic_data(codec); ++ if (t < HBIAS_LEVEL_4 && t >= HBIAS_LEVEL_3 && (ac10x->pullout_cntr == 0)) { ++ KEY_VOLUME_FLAG = 1; ++ input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 1); ++ input_sync(ac10x->inpdev); ++ input_report_key(ac10x->inpdev, KEY_VOLUMEDOWN, 0); ++ input_sync(ac10x->inpdev); ++ AC101_DBG("KEY_VOLUMEDOWN\n"); ++ } ++ if (ac10x->pullout_cntr) ++ ac10x->pullout_cntr--; ++ } ++ } else if ((t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) && ++ (ac10x->mode == FOUR_HEADPHONE_PLUGIN)) { ++ t = __ac101_get_hmic_data(codec); ++ if (t < HBIAS_LEVEL_2 && t >= HBIAS_LEVEL_1) { ++ if (KEY_VOLUME_FLAG) { ++ KEY_VOLUME_FLAG = 0; ++ } ++ if (hook_flag1 == (++hook_flag2)) { ++ hook_flag1 = hook_flag2 = 0; ++ input_report_key(ac10x->inpdev, KEY_HEADSETHOOK, 0); ++ input_sync(ac10x->inpdev); ++ ++ AC101_DBG("KEY_HEADSETHOOK0\n"); ++ } ++ } ++ } else { ++ while (ac10x->irq_cntr == 0 && ac10x->irq != 0) { ++ msleep(20); ++ ++ t = __ac101_get_hmic_data(codec); ++ ++ if (filt_index <= HEADSET_FILTER_CNT) { ++ if (filt_index++ == 0) { ++ filter_buf = t; ++ } else if (filter_buf != t) { ++ filt_index = 0; ++ } ++ continue; ++ } ++ ++ filt_index = 0; ++ if (filter_buf >= HBIAS_LEVEL_2) { ++ ac10x->mode = THREE_HEADPHONE_PLUGIN; ++ ac10x->state = 2; ++ } else if (filter_buf >= HBIAS_LEVEL_1 - 1) { ++ ac10x->mode = FOUR_HEADPHONE_PLUGIN; ++ ac10x->state = 1; ++ } else { ++ ac10x->mode = HEADPHONE_IDLE; ++ ac10x->state = 0; ++ } ++ switch_status_update(ac10x); ++ ac10x->pullout_cntr = 0; ++ break; ++ } ++ } ++} ++ ++/* ++ * audio_hmic_irq: the interrupt handlers ++ */ ++static irqreturn_t audio_hmic_irq(int irq, void *para) ++{ ++ struct ac10x_priv *ac10x = (struct ac10x_priv *)para; ++ if (ac10x == NULL) { ++ return -EINVAL; ++ } ++ ++ if (0 == schedule_work(&ac10x->work_clear_irq)){ ++ AC101_DBG("[audio_hmic_irq] work already in queue_codec_irq, adding failed!\n"); ++ } ++ return IRQ_HANDLED; ++} ++ ++static int ac101_switch_probe(struct ac10x_priv *ac10x) { ++ struct i2c_client *i2c = ac10x->i2c101; ++ long ret; ++ ++ ac10x->gpiod_irq = devm_gpiod_get_optional(&i2c->dev, "switch-irq", GPIOD_IN); ++ if (IS_ERR(ac10x->gpiod_irq)) { ++ ac10x->gpiod_irq = NULL; ++ dev_err(&i2c->dev, "failed get switch-irq in device tree\n"); ++ goto _err_irq; ++ } ++ ++ gpiod_direction_input(ac10x->gpiod_irq); ++ ++ ac10x->irq = gpiod_to_irq(ac10x->gpiod_irq); ++ if (IS_ERR_VALUE(ac10x->irq)) { ++ pr_info("[ac101] map gpio to irq failed, errno = %ld\n", ac10x->irq); ++ ac10x->irq = 0; ++ goto _err_irq; ++ } ++ ++ /* request irq, set irq type to falling edge trigger */ ++ ret = devm_request_irq(ac10x->codec->dev, ac10x->irq, audio_hmic_irq, ++ IRQF_TRIGGER_FALLING, "SWTICH_EINT", ac10x); ++ if (IS_ERR_VALUE(ret)) { ++ pr_info("[ac101] request virq %ld failed, errno = %ld\n", ac10x->irq, ret); ++ goto _err_irq; ++ } ++ ++ ac10x->mode = HEADPHONE_IDLE; ++ ac10x->state = -1; ++ ++ /*use for judge the state of switch*/ ++ INIT_WORK(&ac10x->work_switch, work_cb_earphone_switch); ++ INIT_WORK(&ac10x->work_clear_irq, work_cb_clear_irq); ++ ++ /********************create input device************************/ ++ ac10x->inpdev = devm_input_allocate_device(ac10x->codec->dev); ++ if (!ac10x->inpdev) { ++ AC101_DBG("input_allocate_device: not enough memory for input device\n"); ++ ret = -ENOMEM; ++ goto _err_input_allocate_device; ++ } ++ ++ ac10x->inpdev->name = "seed-voicecard-headset"; ++ ac10x->inpdev->phys = dev_name(ac10x->codec->dev); ++ ac10x->inpdev->id.bustype = BUS_I2C; ++ ac10x->inpdev->dev.parent = ac10x->codec->dev; ++ input_set_drvdata(ac10x->inpdev, ac10x->codec); ++ ++ ac10x->inpdev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SW); ++ ++ set_bit(KEY_HEADSETHOOK, ac10x->inpdev->keybit); ++ set_bit(KEY_VOLUMEUP, ac10x->inpdev->keybit); ++ set_bit(KEY_VOLUMEDOWN, ac10x->inpdev->keybit); ++ input_set_capability(ac10x->inpdev, EV_SW, SW_HEADPHONE_INSERT); ++ ++ ret = input_register_device(ac10x->inpdev); ++ if (ret) { ++ AC101_DBG("input_register_device: input_register_device failed\n"); ++ goto _err_input_register_device; ++ } ++ ++ /* the first headset state checking */ ++ switch_hw_config(ac10x->codec); ++ ac10x->irq_cntr = 1; ++ schedule_work(&ac10x->work_switch); ++ ++ return 0; ++ ++_err_input_register_device: ++_err_input_allocate_device: ++ ++ if (ac10x->irq) { ++ devm_free_irq(&i2c->dev, ac10x->irq, ac10x); ++ ac10x->irq = 0; ++ } ++_err_irq: ++ return ret; ++} ++/******************************************************************************/ ++/********************************switch****************************************/ ++/******************************************************************************/ ++#endif ++ ++ ++ ++void drc_config(struct snd_soc_codec *codec) ++{ ++ int reg_val; ++ reg_val = ac101_read(codec, 0xa3); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= 1<<0; ++ ac101_write(codec, 0xa3, reg_val); ++ ac101_write(codec, 0xa4, 0x2baf); ++ ++ reg_val = ac101_read(codec, 0xa5); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= 1<<0; ++ ac101_write(codec, 0xa5, reg_val); ++ ac101_write(codec, 0xa6, 0x2baf); ++ ++ reg_val = ac101_read(codec, 0xa7); ++ reg_val &= ~(0x7ff<<0); ++ ac101_write(codec, 0xa7, reg_val); ++ ac101_write(codec, 0xa8, 0x44a); ++ ++ reg_val = ac101_read(codec, 0xa9); ++ reg_val &= ~(0x7ff<<0); ++ ac101_write(codec, 0xa9, reg_val); ++ ac101_write(codec, 0xaa, 0x1e06); ++ ++ reg_val = ac101_read(codec, 0xab); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= (0x352<<0); ++ ac101_write(codec, 0xab, reg_val); ++ ac101_write(codec, 0xac, 0x6910); ++ ++ reg_val = ac101_read(codec, 0xad); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= (0x77a<<0); ++ ac101_write(codec, 0xad, reg_val); ++ ac101_write(codec, 0xae, 0xaaaa); ++ ++ reg_val = ac101_read(codec, 0xaf); ++ reg_val &= ~(0x7ff<<0); ++ reg_val |= (0x2de<<0); ++ ac101_write(codec, 0xaf, reg_val); ++ ac101_write(codec, 0xb0, 0xc982); ++ ++ ac101_write(codec, 0x16, 0x9f9f); ++ ++} ++ ++void drc_enable(struct snd_soc_codec *codec,bool on) ++{ ++ int reg_val; ++ if (on) { ++ ac101_write(codec, 0xb5, 0xA080); ++ reg_val = ac101_read(codec, MOD_CLK_ENA); ++ reg_val |= (0x1<<6); ++ ac101_write(codec, MOD_CLK_ENA, reg_val); ++ reg_val = ac101_read(codec, MOD_RST_CTRL); ++ reg_val |= (0x1<<6); ++ ac101_write(codec, MOD_RST_CTRL, reg_val); ++ ++ reg_val = ac101_read(codec, 0xa0); ++ reg_val |= (0x7<<0); ++ ac101_write(codec, 0xa0, reg_val); ++ } else { ++ ac101_write(codec, 0xb5, 0x0); ++ reg_val = ac101_read(codec, MOD_CLK_ENA); ++ reg_val &= ~(0x1<<6); ++ ac101_write(codec, MOD_CLK_ENA, reg_val); ++ reg_val = ac101_read(codec, MOD_RST_CTRL); ++ reg_val &= ~(0x1<<6); ++ ac101_write(codec, MOD_RST_CTRL, reg_val); ++ ++ reg_val = ac101_read(codec, 0xa0); ++ reg_val &= ~(0x7<<0); ++ ac101_write(codec, 0xa0, reg_val); ++ } ++} ++ ++void set_configuration(struct snd_soc_codec *codec) ++{ ++ if (speaker_double_used) { ++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1f<dac_mutex); ++ switch (event) { ++ case SND_SOC_DAPM_PRE_PMU: ++ AC101_DBG(); ++ if (ac10x->dac_enable == 0){ ++ /*enable dac module clk*/ ++ ac101_update_bits(codec, MOD_CLK_ENA, (0x1<dac_enable++; ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ if (ac10x->dac_enable != 0){ ++ ac10x->dac_enable = 0; ++ ++ ac101_update_bits(codec, DAC_DIG_CTRL, (0x1<dac_mutex); ++ return 0; ++} ++ ++static int ac101_headphone_event(struct snd_soc_codec* codec, int event) { ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ /*open*/ ++ AC101_DBG("post:open\n"); ++ ac101_update_bits(codec, OMIXER_DACA_CTRL, (0xf<codec, SYSCLK_CTRL); ++ return (reg_val & (0x1<aif1_clken == 0){ ++ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<aif1_clken++; ++ } ++ } ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ if (ac10x->aif1_clken != 0) { ++ /* disable aif1clk & sysclk */ ++ ret = ac101_update_bits(codec, SYSCLK_CTRL, (0x1<aif1_clken = 0; ++ } ++ break; ++ } ++ } ++ ++ AC101_DBG("event=%d pre_up/%d post_down/%d\n", event, SND_SOC_DAPM_PRE_PMU, ++ SND_SOC_DAPM_POST_PMD); ++ ++ return ret; ++} ++ ++/** ++ * snd_ac101_get_volsw - single mixer get callback ++ * @kcontrol: mixer control ++ * @ucontrol: control element information ++ * ++ * Callback to get the value of a single mixer control, or a double mixer ++ * control that spans 2 registers. ++ * ++ * Returns 0 for success. ++ */ ++static int snd_ac101_get_volsw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol ++){ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ unsigned int val, mask = (1 << fls(mc->max)) - 1; ++ unsigned int invert = mc->invert; ++ int ret; ++ ++ if ((ret = ac101_read(static_ac10x->codec, mc->reg)) < 0) ++ return ret; ++ ++ val = (ret >> mc->shift) & mask; ++ ucontrol->value.integer.value[0] = val - mc->min; ++ if (invert) { ++ ucontrol->value.integer.value[0] = ++ mc->max - ucontrol->value.integer.value[0]; ++ } ++ ++ if (snd_soc_volsw_is_stereo(mc)) { ++ val = (ret >> mc->rshift) & mask; ++ ucontrol->value.integer.value[1] = val - mc->min; ++ if (invert) { ++ ucontrol->value.integer.value[1] = ++ mc->max - ucontrol->value.integer.value[1]; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * snd_ac101_put_volsw - single mixer put callback ++ * @kcontrol: mixer control ++ * @ucontrol: control element information ++ * ++ * Callback to set the value of a single mixer control, or a double mixer ++ * control that spans 2 registers. ++ * ++ * Returns 0 for success. ++ */ ++static int snd_ac101_put_volsw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol ++){ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ unsigned int sign_bit = mc->sign_bit; ++ unsigned int val, mask = (1 << fls(mc->max)) - 1; ++ unsigned int invert = mc->invert; ++ int ret; ++ ++ if (sign_bit) ++ mask = BIT(sign_bit + 1) - 1; ++ ++ val = ((ucontrol->value.integer.value[0] + mc->min) & mask); ++ if (invert) { ++ val = mc->max - val; ++ } ++ ++ ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->shift, val << mc->shift); ++ ++ if (! snd_soc_volsw_is_stereo(mc)) { ++ return ret; ++ } ++ val = ((ucontrol->value.integer.value[1] + mc->min) & mask); ++ if (invert) { ++ val = mc->max - val; ++ } ++ ++ ret = ac101_update_bits(static_ac10x->codec, mc->reg, mask << mc->rshift, ++ val << mc->rshift); ++ return ret; ++} ++ ++ ++static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -11925, 75, 0); ++static const DECLARE_TLV_DB_SCALE(dac_mix_vol_tlv, -600, 600, 0); ++static const DECLARE_TLV_DB_SCALE(dig_vol_tlv, -7308, 116, 0); ++static const DECLARE_TLV_DB_SCALE(speaker_vol_tlv, -4800, 150, 0); ++static const DECLARE_TLV_DB_SCALE(headphone_vol_tlv, -6300, 100, 0); ++ ++static struct snd_kcontrol_new ac101_controls[] = { ++ /*DAC*/ ++ SOC_DOUBLE_TLV("DAC volume", DAC_VOL_CTRL, DAC_VOL_L, DAC_VOL_R, 0xff, 0, dac_vol_tlv), ++ SOC_DOUBLE_TLV("DAC mixer gain", DAC_MXR_GAIN, DACL_MXR_GAIN, DACR_MXR_GAIN, ++ 0xf, 0, dac_mix_vol_tlv), ++ SOC_SINGLE_TLV("digital volume", DAC_DBG_CTRL, DVC, 0x3f, 1, dig_vol_tlv), ++ SOC_SINGLE_TLV("speaker volume", SPKOUT_CTRL, SPK_VOL, 0x1f, 0, speaker_vol_tlv), ++ SOC_SINGLE_TLV("headphone volume", HPOUT_CTRL, HP_VOL, 0x3f, 0, headphone_vol_tlv), ++}; ++ ++/* PLL divisors */ ++struct pll_div { ++ unsigned int pll_in; ++ unsigned int pll_out; ++ int m; ++ int n_i; ++ int n_f; ++}; ++ ++struct aif1_fs { ++ unsigned samp_rate; ++ int bclk_div; ++ int srbit; ++ #define _SERIES_24_576K 0 ++ #define _SERIES_22_579K 1 ++ int series; ++}; ++ ++struct kv_map { ++ int val; ++ int bit; ++}; ++ ++/* ++ * Note : pll code from original tdm/i2s driver. ++ * freq_out = freq_in * N/(M*(2k+1)) , k=1,N=N_i+N_f,N_f=factor*0.2; ++ * N_i[0,1023], N_f_factor[0,7], m[1,64]=REG_VAL[1-63,0] ++ */ ++static const struct pll_div codec_pll_div[] = { ++ {128000, _FREQ_22_579K, 1, 529, 1}, ++ {192000, _FREQ_22_579K, 1, 352, 4}, ++ {256000, _FREQ_22_579K, 1, 264, 3}, ++ {384000, _FREQ_22_579K, 1, 176, 2}, /*((176+2*0.2)*6000000)/(38*(2*1+1))*/ ++ {1411200, _FREQ_22_579K, 1, 48, 0}, ++ {2822400, _FREQ_22_579K, 1, 24, 0}, /* accurate, 11025 * 256 */ ++ {5644800, _FREQ_22_579K, 1, 12, 0}, /* accurate, 22050 * 256 */ ++ {6000000, _FREQ_22_579K, 38, 429, 0}, /*((429+0*0.2)*6000000)/(38*(2*1+1))*/ ++ {11289600, _FREQ_22_579K, 1, 6, 0}, /* accurate, 44100 * 256 */ ++ {13000000, _FREQ_22_579K, 19, 99, 0}, ++ {19200000, _FREQ_22_579K, 25, 88, 1}, ++ {24000000, _FREQ_22_579K, 63, 177, 4}, /* 22577778 Hz */ ++ ++ {128000, _FREQ_24_576K, 1, 576, 0}, ++ {192000, _FREQ_24_576K, 1, 384, 0}, ++ {256000, _FREQ_24_576K, 1, 288, 0}, ++ {384000, _FREQ_24_576K, 1, 192, 0}, ++ {2048000, _FREQ_24_576K, 1, 36, 0}, /* accurate, 8000 * 256 */ ++ {3072000, _FREQ_24_576K, 1, 24, 0}, /* accurate, 12000 * 256 */ ++ {4096000, _FREQ_24_576K, 1, 18, 0}, /* accurate, 16000 * 256 */ ++ {6000000, _FREQ_24_576K, 25, 307, 1}, ++ {6144000, _FREQ_24_576K, 4, 48, 0}, /* accurate, 24000 * 256 */ ++ {12288000, _FREQ_24_576K, 8, 48, 0}, /* accurate, 48000 * 256 */ ++ {13000000, _FREQ_24_576K, 42, 238, 1}, ++ {19200000, _FREQ_24_576K, 25, 96, 0}, ++ {24000000, _FREQ_24_576K, 25, 76, 4}, /* accurate */ ++ ++ {_FREQ_22_579K, _FREQ_22_579K, 8, 24, 0}, /* accurate, 88200 * 256 */ ++ {_FREQ_24_576K, _FREQ_24_576K, 8, 24, 0}, /* accurate, 96000 * 256 */ ++}; ++ ++static const struct aif1_fs codec_aif1_fs[] = { ++ {8000, 12, 0}, ++ {11025, 8, 1, _SERIES_22_579K}, ++ {12000, 8, 2}, ++ {16000, 6, 3}, ++ {22050, 4, 4, _SERIES_22_579K}, ++ {24000, 4, 5}, ++ /* {32000, 3, 6}, dividing by 3 is not support */ ++ {44100, 2, 7, _SERIES_22_579K}, ++ {48000, 2, 8}, ++ {96000, 1, 9}, ++}; ++ ++static const struct kv_map codec_aif1_lrck[] = { ++ {16, 0}, ++ {32, 1}, ++ {64, 2}, ++ {128, 3}, ++ {256, 4}, ++}; ++ ++static const struct kv_map codec_aif1_wsize[] = { ++ {8, 0}, ++ {16, 1}, ++ {20, 2}, ++ {24, 3}, ++ {32, 3}, ++}; ++ ++static const unsigned ac101_bclkdivs[] = { ++ 1, 2, 4, 6, ++ 8, 12, 16, 24, ++ 32, 48, 64, 96, ++ 128, 192, 0, 0, ++}; ++ ++static int ac101_aif_play(struct ac10x_priv* ac10x) { ++ struct snd_soc_codec * codec = ac10x->codec; ++ ++ late_enable_dac(codec, SND_SOC_DAPM_PRE_PMU); ++ ac101_headphone_event(codec, SND_SOC_DAPM_POST_PMU); ++ if (drc_used) { ++ drc_enable(codec, 1); ++ } ++ ++ /* Enable Left & Right Speaker */ ++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), ++ (0x1 << LSPK_EN) | (0x1 << RSPK_EN)); ++ if (ac10x->gpiod_spk_amp_gate) { ++ gpiod_set_value(ac10x->gpiod_spk_amp_gate, 1); ++ } ++ return 0; ++} ++ ++static void ac10x_work_aif_play(struct work_struct *work) { ++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, dlywork.work); ++ ++ ac101_aif_play(ac10x); ++ return; ++} ++ ++int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ AC101_DBG("mute=%d\n", mute); ++ ++ ac101_write(codec, DAC_VOL_CTRL, mute? 0: 0xA0A0); ++ ++ if (!mute) { ++ #if _MASTER_MULTI_CODEC != _MASTER_AC101 ++ /* enable global clock */ ++ ac10x->aif1_clken = 0; ++ ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0); ++ ac101_aif_play(ac10x); ++ #else ++ schedule_delayed_work(&ac10x->dlywork, msecs_to_jiffies(50)); ++ #endif ++ } else { ++ #if _MASTER_MULTI_CODEC == _MASTER_AC101 ++ cancel_delayed_work_sync(&ac10x->dlywork); ++ #endif ++ ++ if (ac10x->gpiod_spk_amp_gate) { ++ gpiod_set_value(ac10x->gpiod_spk_amp_gate, 0); ++ } ++ /* Disable Left & Right Speaker */ ++ ac101_update_bits(codec, SPKOUT_CTRL, (0x1 << LSPK_EN) | (0x1 << RSPK_EN), ++ (0x0 << LSPK_EN) | (0x0 << RSPK_EN)); ++ if (drc_used) { ++ drc_enable(codec, 0); ++ } ++ ac101_headphone_event(codec, SND_SOC_DAPM_PRE_PMD); ++ late_enable_dac(codec, SND_SOC_DAPM_POST_PMD); ++ ++ #if _MASTER_MULTI_CODEC != _MASTER_AC101 ++ ac10x->aif1_clken = 1; ++ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); ++ #endif ++ } ++ return 0; ++} ++ ++void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ AC101_DBG("stream = %s, play: %d, capt: %d, active: %d\n", ++ snd_pcm_stream_str(substream), ++ codec_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, ++ codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active, ++ snd_soc_dai_active(codec_dai)); ++ ++ if (!snd_soc_dai_active(codec_dai)) { ++ ac10x->aif1_clken = 1; ++ ac101_aif1clk(codec, SND_SOC_DAPM_POST_PMD, 0); ++ } else { ++ ac101_aif1clk(codec, SND_SOC_DAPM_PRE_PMU, 0); ++ } ++} ++ ++static int ac101_set_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, ++ unsigned int freq_in, unsigned int freq_out) ++{ ++ struct snd_soc_codec *codec = codec_dai->codec; ++ int i, m, n_i, n_f; ++ ++ AC101_DBG("pll_id:%d\n", pll_id); ++ ++ /* clear volatile reserved bits*/ ++ ac101_update_bits(codec, SYSCLK_CTRL, 0xFF & ~(0x1 << SYSCLK_ENA), 0x0); ++ ++ /* select aif1 clk srouce from mclk1 */ ++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3< _FREQ_24_576K)) { ++ return -EINVAL; ++ } else if ((freq_in == _FREQ_24_576K) || (freq_in == _FREQ_22_579K)) { ++ if (pll_id == AC101_MCLK1) { ++ /*select aif1 clk source from mclk1*/ ++ ac101_update_bits(codec, SYSCLK_CTRL, (0x3<codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int reg_val, freq_out; ++ unsigned channels; ++ ++ AC101_DBG("+++\n"); ++ ++ if (_MASTER_MULTI_CODEC == _MASTER_AC101 && ac101_sysclk_started()) { ++ /* not configure hw_param twice if stream is playback, tell the caller it's started */ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ return 1; ++ } ++ } ++ ++ /* get channels count & slot size */ ++ channels = params_channels(params); ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S24_LE: ++ case SNDRV_PCM_FORMAT_S32_LE: ++ aif1_slot_size = 32; ++ break; ++ case SNDRV_PCM_FORMAT_S16_LE: ++ default: ++ aif1_slot_size = 16; ++ break; ++ } ++ ++ /* set LRCK/BCLK ratio */ ++ aif1_lrck_div = aif1_slot_size * channels; ++ for (i = 0; i < ARRAY_SIZE(codec_aif1_lrck); i++) { ++ if (codec_aif1_lrck[i].val == aif1_lrck_div) { ++ break; ++ } ++ } ++ ac101_update_bits(codec, AIF_CLK_CTRL, (0x7 << AIF1_LRCK_DIV), ++ codec_aif1_lrck[i].bit << AIF1_LRCK_DIV); ++ ++ /* set PLL output freq */ ++ freq_out = _FREQ_24_576K; ++ for (i = 0; i < ARRAY_SIZE(codec_aif1_fs); i++) { ++ if (codec_aif1_fs[i].samp_rate == params_rate(params)) { ++ if (codec_dai->stream[SNDRV_PCM_STREAM_CAPTURE].active && dmic_used && ++ codec_aif1_fs[i].samp_rate == 44100) { ++ ac101_update_bits(codec, AIF_SR_CTRL, (0xf< 2) reg_val = 2; ++ ac101_update_bits(codec, AIF1_ADCDAT_CTRL, 0x3 << AIF1_SLOT_SIZ, reg_val << AIF1_SLOT_SIZ); ++ ++ /* setting pll if it's master mode */ ++ reg_val = ac101_read(codec, AIF_CLK_CTRL); ++ if ((reg_val & (0x1 << AIF1_MSTR_MOD)) == 0) { ++ unsigned bclkdiv; ++ ++ ac101_set_pll(codec_dai, AC101_MCLK1, 0, ac10x->sysclk, freq_out); ++ ++ bclkdiv = freq_out / (aif1_lrck_div * params_rate(params)); ++ for (i = 0; i < ARRAY_SIZE(ac101_bclkdivs) - 1; i++) { ++ if (ac101_bclkdivs[i] >= bclkdiv) { ++ break; ++ } ++ } ++ ac101_update_bits(codec, AIF_CLK_CTRL, (0xf<codec; ++ ++ AC101_DBG(); ++ ++ /* ++ * master or slave selection ++ * 0 = Master mode ++ * 1 = Slave mode ++ */ ++ reg_val = ac101_read(codec, AIF_CLK_CTRL); ++ reg_val &= ~(0x1<codec; ++ ++ AC101_DBG("\n\n\n"); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ++ } ++ return 0; ++} ++ ++int ac101_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int ret = 0; ++ ++ AC101_DBG("stream=%s cmd=%d\n", ++ snd_pcm_stream_str(substream), ++ cmd); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ #if _MASTER_MULTI_CODEC == _MASTER_AC101 ++ if (ac10x->aif1_clken == 0){ ++ /* ++ * enable aif1clk, it' here due to reduce time between 'AC108 Sysclk Enable' and 'AC101 Sysclk Enable' ++ * Or else the two AC108 chips lost the sync. ++ */ ++ ret = 0; ++ ret = ret || ac101_update_bits(codec, MOD_CLK_ENA, ++ (0x1<codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ AC101_DBG("id=%d freq=%d, dir=%d\n", ++ clk_id, freq, dir); ++ ++ ac10x->sysclk = freq; ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops ac101_aif1_dai_ops = { ++ //.startup = ac101_audio_startup, ++ //.shutdown = ac101_aif_shutdown, ++ //.set_sysclk = ac101_set_dai_sysclk, ++ //.set_pll = ac101_set_pll, ++ //.set_fmt = ac101_set_dai_fmt, ++ //.hw_params = ac101_hw_params, ++ //.trigger = ac101_trigger, ++ //.digital_mute = ac101_aif_mute, ++}; ++ ++static struct snd_soc_dai_driver ac101_dai[] = { ++ { ++ .name = "ac10x-aif1", ++ .id = AIF1_CLK, ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = AC101_RATES, ++ .formats = AC101_FORMATS, ++ }, ++ #if 0 ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = 8, ++ .rates = AC101_RATES, ++ .formats = AC101_FORMATS, ++ }, ++ #endif ++ .ops = &ac101_aif1_dai_ops, ++ } ++}; ++#endif ++ ++static void codec_resume_work(struct work_struct *work) ++{ ++ struct ac10x_priv *ac10x = container_of(work, struct ac10x_priv, codec_resume); ++ struct snd_soc_codec *codec = ac10x->codec; ++ ++ AC101_DBG("+++\n"); ++ ++ set_configuration(codec); ++ if (drc_used) { ++ drc_config(codec); ++ } ++ /*enable this bit to prevent leakage from ldoin*/ ++ ac101_update_bits(codec, ADDA_TUNE3, (0x1<bias_level = level; ++ return 0; ++} ++ ++int ac101_codec_probe(struct snd_soc_codec *codec) ++{ ++ int ret = 0; ++ struct ac10x_priv *ac10x; ++ ++ ac10x = dev_get_drvdata(codec->dev); ++ if (ac10x == NULL) { ++ AC101_DBG("not set client data!\n"); ++ return -ENOMEM; ++ } ++ ac10x->codec = codec; ++ ++ INIT_DELAYED_WORK(&ac10x->dlywork, ac10x_work_aif_play); ++ INIT_WORK(&ac10x->codec_resume, codec_resume_work); ++ ac10x->dac_enable = 0; ++ ac10x->aif1_clken = 0; ++ mutex_init(&ac10x->dac_mutex); ++ ++ set_configuration(ac10x->codec); ++ ++ /*enable this bit to prevent leakage from ldoin*/ ++ ac101_update_bits(codec, ADDA_TUNE3, (0x1<get = snd_ac101_get_volsw; ++ skn->put = snd_ac101_put_volsw; ++ } ++ ret = snd_soc_add_codec_controls(codec, ac101_controls, ARRAY_SIZE(ac101_controls)); ++ if (ret) { ++ pr_err("[ac10x] Failed to register audio mode control, " ++ "will continue without it.\n"); ++ } ++ ++ #ifdef CONFIG_AC101_SWITCH_DETECT ++ ret = ac101_switch_probe(ac10x); ++ if (ret) { ++ // not care the switch return value ++ } ++ #endif ++ ++ return 0; ++} ++ ++/* power down chip */ ++int ac101_codec_remove(struct snd_soc_codec *codec) ++{ ++ #ifdef CONFIG_AC101_SWITCH_DETECT ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ if (ac10x->irq) { ++ devm_free_irq(codec->dev, ac10x->irq, ac10x); ++ ac10x->irq = 0; ++ } ++ ++ if (cancel_work_sync(&ac10x->work_switch) != 0) { ++ } ++ ++ if (cancel_work_sync(&ac10x->work_clear_irq) != 0) { ++ } ++ ++ if (ac10x->inpdev) { ++ input_unregister_device(ac10x->inpdev); ++ ac10x->inpdev = NULL; ++ } ++ #endif ++ ++ return 0; ++} ++ ++int ac101_codec_suspend(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ AC101_DBG("[codec]:suspend\n"); ++ regcache_cache_only(ac10x->regmap101, true); ++ return 0; ++} ++ ++int ac101_codec_resume(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int ret; ++ ++ AC101_DBG("[codec]:resume"); ++ ++ /* Sync reg_cache with the hardware */ ++ regcache_cache_only(ac10x->regmap101, false); ++ ret = regcache_sync(ac10x->regmap101); ++ if (ret != 0) { ++ dev_err(codec->dev, "Failed to sync register cache: %d\n", ret); ++ regcache_cache_only(ac10x->regmap101, true); ++ return ret; ++ } ++ ++ #ifdef CONFIG_AC101_SWITCH_DETECT ++ ac10x->mode = HEADPHONE_IDLE; ++ ac10x->state = -1; ++ #endif ++ ++ ac101_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ++ schedule_work(&ac10x->codec_resume); ++ return 0; ++} ++ ++/***************************************************************************/ ++static ssize_t ac101_debug_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct ac10x_priv *ac10x = dev_get_drvdata(dev); ++ int val = 0, flag = 0; ++ u16 value_w, value_r; ++ u8 reg, num, i=0; ++ ++ val = simple_strtol(buf, NULL, 16); ++ flag = (val >> 24) & 0xF; ++ if (flag) { ++ reg = (val >> 16) & 0xFF; ++ value_w = val & 0xFFFF; ++ ac101_write(ac10x->codec, reg, value_w); ++ printk("write 0x%x to reg:0x%x\n", value_w, reg); ++ } else { ++ reg = (val >> 8) & 0xFF; ++ num = val & 0xff; ++ printk("\n"); ++ printk("read:start add:0x%x,count:0x%x\n", reg, num); ++ ++ regcache_cache_bypass(ac10x->regmap101, true); ++ do { ++ value_r = ac101_read(ac10x->codec, reg); ++ printk("0x%x: 0x%04x ", reg++, value_r); ++ if (++i % 4 == 0 || i == num) ++ printk("\n"); ++ } while (i < num); ++ regcache_cache_bypass(ac10x->regmap101, false); ++ } ++ return count; ++} ++static ssize_t ac101_debug_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ printk("echo flag|reg|val > ac10x\n"); ++ printk("eg read star addres=0x06,count 0x10:echo 0610 >ac10x\n"); ++ printk("eg write value:0x13fe to address:0x06 :echo 10613fe > ac10x\n"); ++ return 0; ++} ++static DEVICE_ATTR(ac10x, 0644, ac101_debug_show, ac101_debug_store); ++ ++static struct attribute *audio_debug_attrs[] = { ++ &dev_attr_ac10x.attr, ++ NULL, ++}; ++ ++static struct attribute_group audio_debug_attr_group = { ++ .name = "ac101_debug", ++ .attrs = audio_debug_attrs, ++}; ++/***************************************************************************/ ++ ++/************************************************************/ ++static bool ac101_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case PLL_CTRL2: ++ case HMIC_STS: ++ return true; ++ } ++ return false; ++} ++ ++static const struct regmap_config ac101_regmap = { ++ .reg_bits = 8, ++ .val_bits = 16, ++ .reg_stride = 1, ++ .max_register = 0xB5, ++ .cache_type = REGCACHE_FLAT, ++ .volatile_reg = ac101_volatile_reg, ++}; ++ ++/* Sync reg_cache from the hardware */ ++int ac10x_fill_regcache(struct device* dev, struct regmap* map) { ++ int r, i, n; ++ int v; ++ ++ n = regmap_get_max_register(map); ++ for (i = 0; i < n; i++) { ++ regcache_cache_bypass(map, true); ++ r = regmap_read(map, i, &v); ++ if (r) { ++ dev_dbg(dev, "failed to read register %d\n", i); ++ continue; ++ } ++ regcache_cache_bypass(map, false); ++ ++ regcache_cache_only(map, true); ++ r = regmap_write(map, i, v); ++ regcache_cache_only(map, false); ++ } ++ regcache_cache_bypass(map, false); ++ regcache_cache_only(map, false); ++ ++ return 0; ++} ++ ++int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ++{ ++ struct ac10x_priv *ac10x = i2c_get_clientdata(i2c); ++ int ret = 0; ++ unsigned v = 0; ++ ++ AC101_DBG(); ++ ++ static_ac10x = ac10x; ++ ++ ac10x->regmap101 = devm_regmap_init_i2c(i2c, &ac101_regmap); ++ if (IS_ERR(ac10x->regmap101)) { ++ ret = PTR_ERR(ac10x->regmap101); ++ dev_err(&i2c->dev, "Fail to initialize I/O: %d\n", ret); ++ return ret; ++ } ++ ++ /* Chip reset */ ++ regcache_cache_only(ac10x->regmap101, false); ++ ret = regmap_write(ac10x->regmap101, CHIP_AUDIO_RST, 0); ++ msleep(50); ++ ++ /* sync regcache for FLAT type */ ++ ac10x_fill_regcache(&i2c->dev, ac10x->regmap101); ++ ++ ret = regmap_read(ac10x->regmap101, CHIP_AUDIO_RST, &v); ++ if (ret < 0) { ++ dev_err(&i2c->dev, "failed to read vendor ID: %d\n", ret); ++ return ret; ++ } ++ ++ if (v != AC101_CHIP_ID) { ++ dev_err(&i2c->dev, "chip is not AC101 (%X)\n", v); ++ dev_err(&i2c->dev, "Expected %X\n", AC101_CHIP_ID); ++ return -ENODEV; ++ } ++ ++ ret = sysfs_create_group(&i2c->dev.kobj, &audio_debug_attr_group); ++ if (ret) { ++ pr_err("failed to create attr group\n"); ++ } ++ ++ ac10x->gpiod_spk_amp_gate = devm_gpiod_get_optional(&i2c->dev, "spk-amp-switch", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(ac10x->gpiod_spk_amp_gate)) { ++ ac10x->gpiod_spk_amp_gate = NULL; ++ dev_err(&i2c->dev, "failed get spk-amp-switch in device tree\n"); ++ } ++ ++ return 0; ++} ++ ++void ac101_shutdown(struct i2c_client *i2c) ++{ ++ struct ac10x_priv *ac10x = i2c_get_clientdata(i2c); ++ struct snd_soc_codec *codec = ac10x->codec; ++ int reg_val; ++ ++ if (codec == NULL) { ++ pr_err(": no sound card.\n"); ++ return; ++ } ++ ++ /*set headphone volume to 0*/ ++ reg_val = ac101_read(codec, HPOUT_CTRL); ++ reg_val &= ~(0x3f<dev.kobj, &audio_debug_attr_group); ++ return 0; ++} ++ ++MODULE_DESCRIPTION("ASoC ac10x driver"); ++MODULE_AUTHOR("huangxin,liushaohua"); ++MODULE_AUTHOR("PeterYang"); +--- /dev/null ++++ b/sound/soc/codecs/ac101_regs.h +@@ -0,0 +1,431 @@ ++/* ++ * ac101_regs.h ++ * ++ * (C) Copyright 2017-2018 ++ * Seeed Technology Co., Ltd. ++ * ++ * PeterYang ++ * ++ * (C) Copyright 2010-2017 ++ * Reuuimlla Technology Co., Ltd. ++ * huangxin ++ * ++ * some simple description for this code ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ */ ++#ifndef __AC101_REGS_H__ ++#define __AC101_REGS_H__ ++ ++/*pll source*/ ++#define AC101_MCLK1 1 ++#define AC101_MCLK2 2 ++#define AC101_BCLK1 3 ++#define AC101_BCLK2 4 ++ ++#define AIF1_CLK 1 ++#define AIF2_CLK 2 ++ ++#define CHIP_AUDIO_RST 0x0 ++#define PLL_CTRL1 0x1 ++#define PLL_CTRL2 0x2 ++#define SYSCLK_CTRL 0x3 ++#define MOD_CLK_ENA 0x4 ++#define MOD_RST_CTRL 0x5 ++#define AIF_SR_CTRL 0x6 ++ ++#define AIF1_CLK_CTRL 0x10 ++#define AIF1_ADCDAT_CTRL 0x11 ++#define AIF1_DACDAT_CTRL 0x12 ++#define AIF1_MXR_SRC 0x13 ++#define AIF1_VOL_CTRL1 0x14 ++#define AIF1_VOL_CTRL2 0x15 ++#define AIF1_VOL_CTRL3 0x16 ++#define AIF1_VOL_CTRL4 0x17 ++#define AIF1_MXR_GAIN 0x18 ++#define AIF1_RXD_CTRL 0x19 ++#define ADC_DIG_CTRL 0x40 ++#define ADC_VOL_CTRL 0x41 ++#define ADC_DBG_CTRL 0x42 ++ ++#define HMIC_CTRL1 0x44 ++#define HMIC_CTRL2 0x45 ++#define HMIC_STS 0x46 ++ ++#define DAC_DIG_CTRL 0x48 ++#define DAC_VOL_CTRL 0x49 ++#define DAC_DBG_CTRL 0x4a ++#define DAC_MXR_SRC 0x4c ++#define DAC_MXR_GAIN 0x4d ++ ++#define ADC_APC_CTRL 0x50 ++#define ADC_SRC 0x51 ++#define ADC_SRCBST_CTRL 0x52 ++#define OMIXER_DACA_CTRL 0x53 ++#define OMIXER_SR 0x54 ++#define OMIXER_BST1_CTRL 0x55 ++#define HPOUT_CTRL 0x56 ++#define ESPKOUT_CTRL 0x57 ++#define SPKOUT_CTRL 0x58 ++#define LOUT_CTRL 0x59 ++#define ADDA_TUNE1 0x5a ++#define ADDA_TUNE2 0x5b ++#define ADDA_TUNE3 0x5c ++#define HPOUT_STR 0x5d ++ ++/*CHIP_AUDIO_RST*/ ++#define AC101_CHIP_ID 0x0101 ++ ++/*PLL_CTRL1*/ ++#define DPLL_DAC_BIAS 14 ++#define PLL_POSTDIV_M 8 ++#define CLOSE_LOOP 6 ++#define INT 0 ++ ++/*PLL_CTRL2*/ ++#define PLL_EN 15 ++#define PLL_LOCK_STATUS 14 ++#define PLL_PREDIV_NI 4 ++#define PLL_POSTDIV_NF 0 ++ ++/*SYSCLK_CTRL*/ ++#define PLLCLK_ENA 15 ++#define PLLCLK_SRC 12 ++#define AIF1CLK_ENA 11 ++#define AIF1CLK_SRC 8 ++#define AIF2CLK_ENA 7 ++#define AIF2CLK_SRC 4 ++#define SYSCLK_ENA 3 ++#define SYSCLK_SRC 0 ++ ++/*MOD_CLK_ENA*/ ++#define MOD_CLK_AIF1 15 ++#define MOD_CLK_AIF2 14 ++#define MOD_CLK_AIF3 13 ++#define MOD_CLK_SRC1 11 ++#define MOD_CLK_SRC2 10 ++#define MOD_CLK_HPF_AGC 7 ++#define MOD_CLK_HPF_DRC 6 ++#define MOD_CLK_ADC_DIG 3 ++#define MOD_CLK_DAC_DIG 2 ++ ++/*MOD_RST_CTRL*/ ++#define MOD_RESET_CTL 0 ++#define MOD_RESET_AIF1 15 ++#define MOD_RESET_AIF2 14 ++#define MOD_RESET_AIF3 13 ++#define MOD_RESET_SRC1 11 ++#define MOD_RESET_SRC2 10 ++#define MOD_RESET_HPF_AGC 7 ++#define MOD_RESET_HPF_DRC 6 ++#define MOD_RESET_ADC_DIG 3 ++#define MOD_RESET_DAC_DIG 2 ++ ++/*AIF_SR_CTRL*/ ++#define AIF1_FS 12 //AIF1 Sample Rate ++#define AIF2_FS 8 //AIF2 Sample Rate ++#define SRC1_ENA 3 ++#define SRC1_SRC 2 ++#define SRC2_ENA 1 ++#define SRC2_SRC 0 ++ ++/*AIF1LCK_CTRL*/ ++#define AIF1_MSTR_MOD 15 ++#define AIF1_BCLK_INV 14 ++#define AIF1_LRCK_INV 13 ++#define AIF1_BCLK_DIV 9 ++#define AIF1_LRCK_DIV 6 ++#define AIF1_WORK_SIZ 4 ++#define AIF1_DATA_FMT 2 ++#define DSP_MONO_PCM 1 ++#define AIF1_TDMM_ENA 0 ++ ++/*AIF1_ADCDAT_CTRL*/ ++#define AIF1_AD0L_ENA 15 ++#define AIF1_AD0R_ENA 14 ++#define AIF1_AD1L_ENA 13 ++#define AIF1_AD1R_ENA 12 ++#define AIF1_AD0L_SRC 10 ++#define AIF1_AD0R_SRC 8 ++#define AIF1_AD1L_SRC 6 ++#define AIF1_AD1R_SRC 4 ++#define AIF1_ADCP_ENA 3 ++#define AIF1_ADUL_ENA 2 ++#define AIF1_SLOT_SIZ 0 ++ ++/*AIF1_DACDAT_CTRL*/ ++#define AIF1_DA0L_ENA 15 ++#define AIF1_DA0R_ENA 14 ++#define AIF1_DA1L_ENA 13 ++#define AIF1_DA1R_ENA 12 ++#define AIF1_DA0L_SRC 10 ++#define AIF1_DA0R_SRC 8 ++#define AIF1_DA1L_SRC 6 ++#define AIF1_DA1R_SRC 4 ++#define AIF1_DACP_ENA 3 ++#define AIF1_DAUL_ENA 2 ++#define AIF1_SLOT_SIZ 0 ++ ++/*AIF1_MXR_SRC*/ ++#define AIF1_AD0L_AIF1_DA0L_MXR 15 ++#define AIF1_AD0L_AIF2_DACL_MXR 14 ++#define AIF1_AD0L_ADCL_MXR 13 ++#define AIF1_AD0L_AIF2_DACR_MXR 12 ++#define AIF1_AD0R_AIF1_DA0R_MXR 11 ++#define AIF1_AD0R_AIF2_DACR_MXR 10 ++#define AIF1_AD0R_ADCR_MXR 9 ++#define AIF1_AD0R_AIF2_DACL_MXR 8 ++#define AIF1_AD1L_AIF2_DACL_MXR 7 ++#define AIF1_AD1L_ADCL_MXR 6 ++#define AIF1_AD1L_MXR_SRC 6 ++#define AIF1_AD1R_AIF2_DACR_MXR 3 ++#define AIF1_AD1R_ADCR_MXR 2 ++#define AIF1_AD1R_MXR_SRC 2 ++ ++/*AIF1_VOL_CTRL1*/ ++#define AIF1_AD0L_VOL 8 ++#define AIF1_AD0R_VOL 0 ++ ++/*AIF1_VOL_CTRL2*/ ++#define AIF1_AD1L_VOL 8 ++#define AIF1_AD1R_VOL 0 ++ ++/*AIF1_VOL_CTRL3*/ ++#define AIF1_DA0L_VOL 8 ++#define AIF1_DA0R_VOL 0 ++ ++/*AIF1_VOL_CTRL4*/ ++#define AIF1_DA1L_VOL 8 ++#define AIF1_DA1R_VOL 0 ++ ++/*AIF1_MXR_GAIN*/ ++#define AIF1_AD0L_MXR_GAIN 12 ++#define AIF1_AD0R_MXR_GAIN 8 ++#define AIF1_AD1L_MXR_GAIN 6 ++#define AIF1_AD1R_MXR_GAIN 2 ++ ++/*AIF1_RXD_CTRL*/ ++#define AIF1_N_DATA_DISCARD 8 ++ ++/*ADC_DIG_CTRL*/ ++#define ENAD 15 ++#define ENDM 14 ++#define ADFIR32 13 ++#define ADOUT_DTS 2 ++#define ADOUT_DLY 1 ++ ++/*ADC_VOL_CTRL*/ ++#define ADC_VOL_L 8 ++#define ADC_VOL_R 0 ++ ++/*ADC_DBG_CTRL*/ ++#define ADSW 15 ++#define DMIC_CLK_PIN_CTRL 12 ++ ++/*HMIC_CTRL1*/ ++#define HMIC_M 12 ++#define HMIC_N 8 ++#define HMIC_DATA_IRQ_MODE 7 ++#define HMIC_TH1_HYSTERESIS 5 ++#define HMIC_PULLOUT_IRQ 4 ++#define HMIC_PLUGIN_IRQ 3 ++#define HMIC_KEYUP_IRQ 2 ++#define HMIC_KEYDOWN_IRQ 1 ++#define HMIC_DATA_IRQ_EN 0 ++ ++/*HMIC_CTRL2*/ ++#define HMIC_SAMPLE_SELECT 14 ++#define HMIC_TH2_HYSTERESIS 13 ++#define HMIC_TH2 8 ++#define HMIC_SF 6 ++#define KEYUP_CLEAR 5 ++#define HMIC_TH1 0 ++ ++/*HMIC_STS*/ ++#define HMIC_DATA 8 ++#define GET_HMIC_DATA(r) (((r) >> HMIC_DATA) & 0x1F) ++#define HMIC_PULLOUT_PEND 4 ++#define HMIC_PLUGIN_PEND 3 ++#define HMIC_KEYUP_PEND 2 ++#define HMKC_KEYDOWN_PEND 1 ++#define HMIC_DATA_PEND 0 ++#define HMIC_PEND_ALL (0x1F) ++ ++/*DAC_DIG_CTRL*/ ++#define ENDA 15 ++#define ENHPF 14 ++#define DAFIR32 13 ++#define MODQU 8 ++ ++/*DAC_VOL_CTRL*/ ++#define DAC_VOL_L 8 ++#define DAC_VOL_R 0 ++ ++/*DAC_DBG_CTRL*/ ++#define DASW 15 ++#define ENDWA_N 14 ++#define DAC_MOD_DBG 13 ++#define DAC_PTN_SEL 6 ++#define DVC 0 ++ ++/*DAC_MXR_SRC*/ ++#define DACL_MXR_AIF1_DA0L 15 ++#define DACL_MXR_AIF1_DA1L 14 ++#define DACL_MXR_AIF2_DACL 13 ++#define DACL_MXR_ADCL 12 ++#define DACL_MXR_SRC 12 ++#define DACR_MXR_AIF1_DA0R 11 ++#define DACR_MXR_AIF1_DA1R 10 ++#define DACR_MXR_AIF2_DACR 9 ++#define DACR_MXR_ADCR 8 ++#define DACR_MXR_SRC 8 ++ ++/*DAC_MXR_GAIN*/ ++#define DACL_MXR_GAIN 12 ++#define DACR_MXR_GAIN 8 ++ ++/*ADC_APC_CTRL*/ ++#define ADCREN 15 ++#define ADCRG 12 ++#define ADCLEN 11 ++#define ADCLG 8 ++#define MBIASEN 7 ++#define MMIC_BIAS_CHOP_EN 6 ++#define MMIC_BIAS_CHOP_CKS 4 ++#define HBIASMOD 2 ++#define HBIASEN 1 ++#define HBIASADCEN 0 ++ ++/*ADC_SRC*/ ++#define RADCMIXMUTEMIC1BOOST (13) ++#define RADCMIXMUTEMIC2BOOST (12) ++#define RADCMIXMUTELINEINLR (11) ++#define RADCMIXMUTELINEINR (10) ++#define RADCMIXMUTEAUXINR (9) ++#define RADCMIXMUTEROUTPUT (8) ++#define RADCMIXMUTELOUTPUT (7) ++#define LADCMIXMUTEMIC1BOOST (6) ++#define LADCMIXMUTEMIC2BOOST (5) ++#define LADCMIXMUTELINEINLR (4) ++#define LADCMIXMUTELINEINL (3) ++#define LADCMIXMUTEAUXINL (2) ++#define LADCMIXMUTELOUTPUT (1) ++#define LADCMIXMUTEROUTPUT (0) ++ ++/*ADC_SRCBST_CTRL*/ ++#define MIC1AMPEN 15 ++#define ADC_MIC1G 12 ++#define MIC2AMPEN 11 ++#define ADC_MIC2G 8 ++#define MIC2SLT 7 ++#define LINEIN_PREG 4 ++#define AUXI_PREG 0 ++ ++/*OMIXER_DACA_CTRL*/ ++#define DACAREN 15 ++#define DACALEN 14 ++#define RMIXEN 13 ++#define LMIXEN 12 ++#define HPOUTPUTENABLE 8 ++ ++/*OMIXER_SR*/ ++#define RMIXMUTEMIC1BOOST (13) ++#define RMIXMUTEMIC2BOOST (12) ++#define RMIXMUTELINEINLR (11) ++#define RMIXMUTELINEINR (10) ++#define RMIXMUTEAUXINR (9) ++#define RMIXMUTEDACR (8) ++#define RMIXMUTEDACL (7) ++#define LMIXMUTEMIC1BOOST (6) ++#define LMIXMUTEMIC2BOOST (5) ++#define LMIXMUTELINEINLR (4) ++#define LMIXMUTELINEINL (3) ++#define LMIXMUTEAUXINL (2) ++#define LMIXMUTEDACL (1) ++#define LMIXMUTEDACR (0) ++ ++/*OMIXER_BST1_CTRL*/ ++#define BIASVOLTAGE 12 ++#define AXG 9 ++#define OMIXER_MIC1G 6 ++#define OMIXER_MIC2G 3 ++#define LINEING 0 ++ ++/*HPOUT_CTRL*/ ++#define RHPS 15 ++#define LHPS 14 ++#define RHPPA_MUTE 13 ++#define LHPPA_MUTE 12 ++#define HPPA_EN 11 ++#define HP_VOL 4 ++#define HPPA_DEL 2 ++#define HPPA_IS 0 ++ ++/*ESPKOUT_CTRL*/ ++#define EAR_RAMP_TIME 11 ++#define ESPA_OUT_CURRENT 9 ++#define ESPSR 7 ++#define ESPPA_MUTE 6 ++#define ESPPA_EN 5 ++#define ESP_VOL 0 ++ ++/*SPKOUT_CTRL*/ ++#define HPCALICKS 13 ++#define RSPKS 12 ++#define RSPKINVEN 11 ++#define RSPK_EN 9 ++#define LSPKS 8 ++#define LSPKINVEN 7 ++#define LSPK_EN 5 ++#define SPK_VOL 0 ++ ++/*LOUT_CTRL*/ ++#define LINEOUTG 5 ++#define LINEOUTEN 4 ++#define LINEOUTS0 3 ++#define LINEOUTS1 2 ++#define LINEOUTS2 1 ++#define LINEOUTS3 0 ++ ++/*ADDA_TUNE1*/ ++#define CURRENT_TEST_SELECT 14 ++#define BIHE_CTRL 12 ++#define DITHER 11 ++#define DITHER_CLK 9 ++#define ZERO_CROSSOVER_EN 8 ++#define ZERO_CROSSOVER_TIME 7 ++#define EAR_SPEED_SELECT 6 ++#define REF_CHOPPEN_CKS 4 ++#define OPMIC_BIAS_CUR 0 ++ ++/*ADDA_TUNE2*/ ++#define OPDAC_BIAS_CUR 14 ++#define OPDRV_BIAS_CUR 12 ++#define OPMIX_BIAS_CUR 10 ++#define OPEAR_BIAS_CUR 8 ++#define OPVR_BIAS_CUR 6 ++#define OPAAF_BIAS_CUR 4 ++#define OPADC1_BIAS_CUR 2 ++#define OPADC2_BIAS_CUR 0 ++ ++/*ADDA_TUNE3*/ ++#define LDOEN 15 ++#define LDO_SEL 12 ++#define BIASCALIVERIFY 11 ++#define BIASMODE 10 ++#define BIASCALIDATA 9 ++#define OSCS 1 ++#define OSCEN 0 ++ ++/*HPOUT_STR*/ ++#define HPVL_SOFT_MOD 14 ++#define HPVL_STEP_CTRL 8 ++#define DACA_CHND_ENA 7 ++#define HPPA_MXRD_ENA 6 ++#define HPVL_CTRL_OUT 0 ++ ++#endif//__AC101_REGS_H__ +--- /dev/null ++++ b/sound/soc/codecs/ac108.c +@@ -0,0 +1,1622 @@ ++/* ++ * ac10x.c -- ac10x ALSA SoC Audio driver ++ * ++ * ++ * Author: Baozhu Zuo ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ac108.h" ++#include "ac10x.h" ++ ++#define _USE_CAPTURE 1 ++#define _MASTER_INDEX 0 ++ ++/* #undef DEBUG ++ * use 'make DEBUG=1' to enable debugging ++ */ ++ ++/** ++ * TODO: ++ * 1, add PM API: ac108_suspend,ac108_resume ++ * 2,0x65-0x6a ++ * 3,0x76-0x79 high 4bit ++ */ ++struct pll_div { ++ unsigned int freq_in; ++ unsigned int freq_out; ++ unsigned int m1; ++ unsigned int m2; ++ unsigned int n; ++ unsigned int k1; ++ unsigned int k2; ++}; ++ ++static struct ac10x_priv *ac10x; ++ ++struct real_val_to_reg_val { ++ unsigned int real_val; ++ unsigned int reg_val; ++}; ++ ++static const struct real_val_to_reg_val ac108_sample_rate[] = { ++ { 8000, 0 }, ++ { 11025, 1 }, ++ { 12000, 2 }, ++ { 16000, 3 }, ++ { 22050, 4 }, ++ { 24000, 5 }, ++ { 32000, 6 }, ++ { 44100, 7 }, ++ { 48000, 8 }, ++ { 96000, 9 }, ++}; ++ ++/* Sample resolution */ ++static const struct real_val_to_reg_val ac108_samp_res[] = { ++ { 8, 1 }, ++ { 12, 2 }, ++ { 16, 3 }, ++ { 20, 4 }, ++ { 24, 5 }, ++ { 28, 6 }, ++ { 32, 7 }, ++}; ++ ++static const unsigned int ac108_bclkdivs[] = { ++ 0, 1, 2, 4, ++ 6, 8, 12, 16, ++ 24, 32, 48, 64, ++ 96, 128, 176, 192, ++}; ++ ++/* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ; M1[0,31], M2[0,1], N[0,1023], K1[0,31], K2[0,1] */ ++static const struct pll_div ac108_pll_div_list[] = { ++ { 400000, _FREQ_24_576K, 0, 0, 614, 4, 1 }, ++ { 512000, _FREQ_24_576K, 0, 0, 960, 9, 1 }, /* _FREQ_24_576K/48 */ ++ { 768000, _FREQ_24_576K, 0, 0, 640, 9, 1 }, /* _FREQ_24_576K/32 */ ++ { 800000, _FREQ_24_576K, 0, 0, 614, 9, 1 }, ++ { 1024000, _FREQ_24_576K, 0, 0, 480, 9, 1 }, /* _FREQ_24_576K/24 */ ++ { 1600000, _FREQ_24_576K, 0, 0, 307, 9, 1 }, ++ { 2048000, _FREQ_24_576K, 0, 0, 240, 9, 1 }, /* accurate, 8000 * 256 */ ++ { 3072000, _FREQ_24_576K, 0, 0, 160, 9, 1 }, /* accurate, 12000 * 256 */ ++ { 4096000, _FREQ_24_576K, 2, 0, 360, 9, 1 }, /* accurate, 16000 * 256 */ ++ { 6000000, _FREQ_24_576K, 4, 0, 410, 9, 1 }, ++ { 12000000, _FREQ_24_576K, 9, 0, 410, 9, 1 }, ++ { 13000000, _FREQ_24_576K, 8, 0, 340, 9, 1 }, ++ { 15360000, _FREQ_24_576K, 12, 0, 415, 9, 1 }, ++ { 16000000, _FREQ_24_576K, 12, 0, 400, 9, 1 }, ++ { 19200000, _FREQ_24_576K, 15, 0, 410, 9, 1 }, ++ { 19680000, _FREQ_24_576K, 15, 0, 400, 9, 1 }, ++ { 24000000, _FREQ_24_576K, 9, 0, 256,24, 0 }, /* accurate, 24M -> 24.576M */ ++ ++ { 400000, _FREQ_22_579K, 0, 0, 566, 4, 1 }, ++ { 512000, _FREQ_22_579K, 0, 0, 880, 9, 1 }, ++ { 768000, _FREQ_22_579K, 0, 0, 587, 9, 1 }, ++ { 800000, _FREQ_22_579K, 0, 0, 567, 9, 1 }, ++ { 1024000, _FREQ_22_579K, 0, 0, 440, 9, 1 }, ++ { 1600000, _FREQ_22_579K, 1, 0, 567, 9, 1 }, ++ { 2048000, _FREQ_22_579K, 0, 0, 220, 9, 1 }, ++ { 3072000, _FREQ_22_579K, 0, 0, 148, 9, 1 }, ++ { 4096000, _FREQ_22_579K, 2, 0, 330, 9, 1 }, ++ { 6000000, _FREQ_22_579K, 2, 0, 227, 9, 1 }, ++ { 12000000, _FREQ_22_579K, 8, 0, 340, 9, 1 }, ++ { 13000000, _FREQ_22_579K, 9, 0, 350, 9, 1 }, ++ { 15360000, _FREQ_22_579K, 10, 0, 325, 9, 1 }, ++ { 16000000, _FREQ_22_579K, 11, 0, 340, 9, 1 }, ++ { 19200000, _FREQ_22_579K, 13, 0, 330, 9, 1 }, ++ { 19680000, _FREQ_22_579K, 14, 0, 345, 9, 1 }, ++ { 24000000, _FREQ_22_579K, 24, 0, 588,24, 0 }, /* accurate, 24M -> 22.5792M */ ++ ++ ++ { _FREQ_24_576K / 1, _FREQ_24_576K, 9, 0, 200, 9, 1 }, /* _FREQ_24_576K */ ++ { _FREQ_24_576K / 2, _FREQ_24_576K, 9, 0, 400, 9, 1 }, /* 12288000,accurate, 48000 * 256 */ ++ { _FREQ_24_576K / 4, _FREQ_24_576K, 4, 0, 400, 9, 1 }, /* 6144000, accurate, 24000 * 256 */ ++ { _FREQ_24_576K / 16, _FREQ_24_576K, 0, 0, 320, 9, 1 }, /* 1536000 */ ++ { _FREQ_24_576K / 64, _FREQ_24_576K, 0, 0, 640, 4, 1 }, /* 384000 */ ++ { _FREQ_24_576K / 96, _FREQ_24_576K, 0, 0, 960, 4, 1 }, /* 256000 */ ++ { _FREQ_24_576K / 128, _FREQ_24_576K, 0, 0, 512, 1, 1 }, /* 192000 */ ++ { _FREQ_24_576K / 176, _FREQ_24_576K, 0, 0, 880, 4, 0 }, /* 140000 */ ++ { _FREQ_24_576K / 192, _FREQ_24_576K, 0, 0, 960, 4, 0 }, /* 128000 */ ++ ++ { _FREQ_22_579K / 1, _FREQ_22_579K, 9, 0, 200, 9, 1 }, /* _FREQ_22_579K */ ++ { _FREQ_22_579K / 2, _FREQ_22_579K, 9, 0, 400, 9, 1 }, /* 11289600,accurate, 44100 * 256 */ ++ { _FREQ_22_579K / 4, _FREQ_22_579K, 4, 0, 400, 9, 1 }, /* 5644800, accurate, 22050 * 256 */ ++ { _FREQ_22_579K / 16, _FREQ_22_579K, 0, 0, 320, 9, 1 }, /* 1411200 */ ++ { _FREQ_22_579K / 64, _FREQ_22_579K, 0, 0, 640, 4, 1 }, /* 352800 */ ++ { _FREQ_22_579K / 96, _FREQ_22_579K, 0, 0, 960, 4, 1 }, /* 235200 */ ++ { _FREQ_22_579K / 128, _FREQ_22_579K, 0, 0, 512, 1, 1 }, /* 176400 */ ++ { _FREQ_22_579K / 176, _FREQ_22_579K, 0, 0, 880, 4, 0 }, /* 128290 */ ++ { _FREQ_22_579K / 192, _FREQ_22_579K, 0, 0, 960, 4, 0 }, /* 117600 */ ++ ++ { _FREQ_22_579K / 6, _FREQ_22_579K, 2, 0, 360, 9, 1 }, /* 3763200 */ ++ { _FREQ_22_579K / 8, _FREQ_22_579K, 0, 0, 160, 9, 1 }, /* 2822400, accurate, 11025 * 256 */ ++ { _FREQ_22_579K / 12, _FREQ_22_579K, 0, 0, 240, 9, 1 }, /* 1881600 */ ++ { _FREQ_22_579K / 24, _FREQ_22_579K, 0, 0, 480, 9, 1 }, /* 940800 */ ++ { _FREQ_22_579K / 32, _FREQ_22_579K, 0, 0, 640, 9, 1 }, /* 705600 */ ++ { _FREQ_22_579K / 48, _FREQ_22_579K, 0, 0, 960, 9, 1 }, /* 470400 */ ++}; ++ ++ ++/* AC108 definition */ ++#define AC108_CHANNELS_MAX 8 /* range[1, 16] */ ++#define AC108_RATES (SNDRV_PCM_RATE_8000_96000 & \ ++ ~(SNDRV_PCM_RATE_64000 | \ ++ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)) ++#define AC108_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ ++ /*SNDRV_PCM_FMTBIT_S20_3LE | \ ++ SNDRV_PCM_FMTBIT_S24_LE |*/ \ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static const DECLARE_TLV_DB_SCALE(tlv_adc_pga_gain, 0, 100, 0); ++static const DECLARE_TLV_DB_SCALE(tlv_ch_digital_vol, -11925, 75, 0); ++ ++int ac10x_read(u8 reg, u8* rt_val, struct regmap* i2cm) ++{ ++ int r, v = 0; ++ ++ if ((r = regmap_read(i2cm, reg, &v)) < 0) ++ pr_info("ac10x_read info->[REG-0x%02x]\n", reg); ++ else ++ *rt_val = v; ++ return r; ++} ++ ++int ac10x_write(u8 reg, u8 val, struct regmap* i2cm) ++{ ++ int r; ++ ++ if ((r = regmap_write(i2cm, reg, val)) < 0) ++ pr_info("ac10x_write info->[REG-0x%02x,val-0x%02x]\n", reg, val); ++ return r; ++} ++ ++int ac10x_update_bits(u8 reg, u8 mask, u8 val, struct regmap* i2cm) ++{ ++ int r; ++ ++ if ((r = regmap_update_bits(i2cm, reg, mask, val)) < 0) ++ pr_info("%s() info->[REG-0x%02x,val-0x%02x]\n", __func__, reg, val); ++ return r; ++} ++ ++/** ++ * snd_ac108_get_volsw - single mixer get callback ++ * @kcontrol: mixer control ++ * @ucontrol: control element information ++ * ++ * Callback to get the value of a single mixer control, or a double mixer ++ * control that spans 2 registers. ++ * ++ * Returns 0 for success. ++ */ ++static int snd_ac108_get_volsw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ unsigned int mask = (1 << fls(mc->max)) - 1; ++ unsigned int invert = mc->invert; ++ int ret, chip = mc->autodisable; ++ u8 val; ++ ++ if ((ret = ac10x_read(mc->reg, &val, ac10x->i2cmap[chip])) < 0) ++ return ret; ++ ++ val = ((val >> mc->shift) & mask) - mc->min; ++ if (invert) { ++ val = mc->max - val; ++ } ++ ucontrol->value.integer.value[0] = val; ++ return 0; ++} ++ ++/** ++ * snd_ac108_put_volsw - single mixer put callback ++ * @kcontrol: mixer control ++ * @ucontrol: control element information ++ * ++ * Callback to set the value of a single mixer control, or a double mixer ++ * control that spans 2 registers. ++ * ++ * Returns 0 for success. ++ */ ++static int snd_ac108_put_volsw(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ unsigned int sign_bit = mc->sign_bit; ++ unsigned int val, mask = (1 << fls(mc->max)) - 1; ++ unsigned int invert = mc->invert; ++ int ret, chip = mc->autodisable; ++ ++ if (sign_bit) ++ mask = BIT(sign_bit + 1) - 1; ++ ++ val = ((ucontrol->value.integer.value[0] + mc->min) & mask); ++ if (invert) { ++ val = mc->max - val; ++ } ++ ++ mask = mask << mc->shift; ++ val = val << mc->shift; ++ ++ ret = ac10x_update_bits(mc->reg, mask, val, ac10x->i2cmap[chip]); ++ return ret; ++} ++ ++#define SOC_AC108_SINGLE_TLV(xname, reg, shift, max, invert, chip, tlv_array) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ ++ SNDRV_CTL_ELEM_ACCESS_READWRITE,\ ++ .tlv.p = (tlv_array), \ ++ .info = snd_soc_info_volsw, .get = snd_ac108_get_volsw,\ ++ .put = snd_ac108_put_volsw, \ ++ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, chip) } ++ ++/* single ac108 */ ++static const struct snd_kcontrol_new ac108_snd_controls[] = { ++ /* ### chip 0 ### */ ++ /*0x70: ADC1 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x71: ADC2 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x72: ADC3 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x73: ADC4 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ ++ /*0x90: Analog PGA1 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL, ++ ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x91: Analog PGA2 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL, ++ ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x92: Analog PGA3 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL, ++ ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x93: Analog PGA4 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL, ++ ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++}; ++/* multiple ac108s */ ++static const struct snd_kcontrol_new ac108tdm_snd_controls[] = { ++ /* ### chip 1 ### */ ++ /*0x70: ADC1 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH1 digital volume", ADC1_DVOL_CTRL, ++ 0, 0xff, 0, 1, tlv_ch_digital_vol), ++ /*0x71: ADC2 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH2 digital volume", ADC2_DVOL_CTRL, ++ 0, 0xff, 0, 1, tlv_ch_digital_vol), ++ /*0x72: ADC3 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH3 digital volume", ADC3_DVOL_CTRL, ++ 0, 0xff, 0, 1, tlv_ch_digital_vol), ++ /*0x73: ADC4 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH4 digital volume", ADC4_DVOL_CTRL, ++ 0, 0xff, 0, 1, tlv_ch_digital_vol), ++ ++ /*0x90: Analog PGA1 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC1 PGA gain", ANA_PGA1_CTRL, ++ ADC1_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain), ++ /*0x91: Analog PGA2 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC2 PGA gain", ANA_PGA2_CTRL, ++ ADC2_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain), ++ /*0x92: Analog PGA3 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC3 PGA gain", ANA_PGA3_CTRL, ++ ADC3_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain), ++ /*0x93: Analog PGA4 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC4 PGA gain", ANA_PGA4_CTRL, ++ ADC4_ANALOG_PGA, 0x1f, 0, 1, tlv_adc_pga_gain), ++ ++ /* ### chip 0 ### */ ++ /*0x70: ADC1 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH5 digital volume", ADC1_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x71: ADC2 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH6 digital volume", ADC2_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x72: ADC3 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH7 digital volume", ADC3_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ /*0x73: ADC4 Digital Channel Volume Control Register*/ ++ SOC_AC108_SINGLE_TLV("CH8 digital volume", ADC4_DVOL_CTRL, ++ 0, 0xff, 0, 0, tlv_ch_digital_vol), ++ ++ /*0x90: Analog PGA1 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC5 PGA gain", ANA_PGA1_CTRL, ++ ADC1_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x91: Analog PGA2 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC6 PGA gain", ANA_PGA2_CTRL, ++ ADC2_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x92: Analog PGA3 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC7 PGA gain", ANA_PGA3_CTRL, ++ ADC3_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++ /*0x93: Analog PGA4 Control Register*/ ++ SOC_AC108_SINGLE_TLV("ADC8 PGA gain", ANA_PGA4_CTRL, ++ ADC4_ANALOG_PGA, 0x1f, 0, 0, tlv_adc_pga_gain), ++}; ++ ++ ++static const struct snd_soc_dapm_widget ac108_dapm_widgets[] = { ++ /* input widgets */ ++ SND_SOC_DAPM_INPUT("MIC1P"), ++ SND_SOC_DAPM_INPUT("MIC1N"), ++ ++ SND_SOC_DAPM_INPUT("MIC2P"), ++ SND_SOC_DAPM_INPUT("MIC2N"), ++ ++ SND_SOC_DAPM_INPUT("MIC3P"), ++ SND_SOC_DAPM_INPUT("MIC3N"), ++ ++ SND_SOC_DAPM_INPUT("MIC4P"), ++ SND_SOC_DAPM_INPUT("MIC4N"), ++ ++ SND_SOC_DAPM_INPUT("DMIC1"), ++ SND_SOC_DAPM_INPUT("DMIC2"), ++ ++ /*0xa0: ADC1 Analog Control 1 Register*/ ++ /*0xa1-0xa6:use the defualt value*/ ++ SND_SOC_DAPM_AIF_IN("Channel 1 AAF", "Capture", 0, ANA_ADC1_CTRL1, ADC1_DSM_ENABLE, 1), ++ SND_SOC_DAPM_SUPPLY("Channel 1 EN", ANA_ADC1_CTRL1, ADC1_PGA_ENABLE, 1, NULL, 0), ++ SND_SOC_DAPM_MICBIAS("MIC1BIAS", ANA_ADC1_CTRL1, ADC1_MICBIAS_EN, 1), ++ ++ /*0xa7: ADC2 Analog Control 1 Register*/ ++ /*0xa8-0xad:use the defualt value*/ ++ SND_SOC_DAPM_AIF_IN("Channel 2 AAF", "Capture", 0, ANA_ADC2_CTRL1, ADC2_DSM_ENABLE, 1), ++ SND_SOC_DAPM_SUPPLY("Channel 2 EN", ANA_ADC2_CTRL1, ADC2_PGA_ENABLE, 1, NULL, 0), ++ SND_SOC_DAPM_MICBIAS("MIC2BIAS", ANA_ADC2_CTRL1, ADC2_MICBIAS_EN, 1), ++ ++ /*0xae: ADC3 Analog Control 1 Register*/ ++ /*0xaf-0xb4:use the defualt value*/ ++ SND_SOC_DAPM_AIF_IN("Channel 3 AAF", "Capture", 0, ANA_ADC3_CTRL1, ADC3_DSM_ENABLE, 1), ++ SND_SOC_DAPM_SUPPLY("Channel 3 EN", ANA_ADC3_CTRL1, ADC3_PGA_ENABLE, 1, NULL, 0), ++ SND_SOC_DAPM_MICBIAS("MIC3BIAS", ANA_ADC3_CTRL1, ADC3_MICBIAS_EN, 1), ++ ++ /*0xb5: ADC4 Analog Control 1 Register*/ ++ /*0xb6-0xbb:use the defualt value*/ ++ SND_SOC_DAPM_AIF_IN("Channel 4 AAF", "Capture", 0, ANA_ADC4_CTRL1, ADC4_DSM_ENABLE, 1), ++ SND_SOC_DAPM_SUPPLY("Channel 4 EN", ANA_ADC4_CTRL1, ADC4_PGA_ENABLE, 1, NULL, 0), ++ SND_SOC_DAPM_MICBIAS("MIC4BIAS", ANA_ADC4_CTRL1, ADC4_MICBIAS_EN, 1), ++ ++ ++ /*0x61: ADC Digital Part Enable Register*/ ++ SND_SOC_DAPM_SUPPLY("ADC EN", ADC_DIG_EN, 4, 1, NULL, 0), ++ SND_SOC_DAPM_ADC("ADC1", "Capture", ADC_DIG_EN, 0, 1), ++ SND_SOC_DAPM_ADC("ADC2", "Capture", ADC_DIG_EN, 1, 1), ++ SND_SOC_DAPM_ADC("ADC3", "Capture", ADC_DIG_EN, 2, 1), ++ SND_SOC_DAPM_ADC("ADC4", "Capture", ADC_DIG_EN, 3, 1), ++ ++ SND_SOC_DAPM_SUPPLY("ADC1 CLK", ANA_ADC4_CTRL7, ADC1_CLK_GATING, 1, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("ADC2 CLK", ANA_ADC4_CTRL7, ADC2_CLK_GATING, 1, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("ADC3 CLK", ANA_ADC4_CTRL7, ADC3_CLK_GATING, 1, NULL, 0), ++ SND_SOC_DAPM_SUPPLY("ADC4 CLK", ANA_ADC4_CTRL7, ADC4_CLK_GATING, 1, NULL, 0), ++ ++ SND_SOC_DAPM_SUPPLY("DSM EN", ANA_ADC4_CTRL6, DSM_DEMOFF, 1, NULL, 0), ++ ++ /*0x62:Digital MIC Enable Register*/ ++ SND_SOC_DAPM_MICBIAS("DMIC1 enable", DMIC_EN, 0, 0), ++ SND_SOC_DAPM_MICBIAS("DMIC2 enable", DMIC_EN, 1, 0), ++}; ++ ++static const struct snd_soc_dapm_route ac108_dapm_routes[] = { ++ ++ { "ADC1", NULL, "Channel 1 AAF" }, ++ { "ADC2", NULL, "Channel 2 AAF" }, ++ { "ADC3", NULL, "Channel 3 AAF" }, ++ { "ADC4", NULL, "Channel 4 AAF" }, ++ ++ { "Channel 1 AAF", NULL, "MIC1BIAS" }, ++ { "Channel 2 AAF", NULL, "MIC2BIAS" }, ++ { "Channel 3 AAF", NULL, "MIC3BIAS" }, ++ { "Channel 4 AAF", NULL, "MIC4BIAS" }, ++ ++ { "MIC1BIAS", NULL, "ADC1 CLK" }, ++ { "MIC2BIAS", NULL, "ADC2 CLK" }, ++ { "MIC3BIAS", NULL, "ADC3 CLK" }, ++ { "MIC4BIAS", NULL, "ADC4 CLK" }, ++ ++ ++ { "ADC1 CLK", NULL, "DSM EN" }, ++ { "ADC2 CLK", NULL, "DSM EN" }, ++ { "ADC3 CLK", NULL, "DSM EN" }, ++ { "ADC4 CLK", NULL, "DSM EN" }, ++ ++ ++ { "DSM EN", NULL, "ADC EN" }, ++ ++ { "Channel 1 EN", NULL, "DSM EN" }, ++ { "Channel 2 EN", NULL, "DSM EN" }, ++ { "Channel 3 EN", NULL, "DSM EN" }, ++ { "Channel 4 EN", NULL, "DSM EN" }, ++ ++ ++ { "MIC1P", NULL, "Channel 1 EN" }, ++ { "MIC1N", NULL, "Channel 1 EN" }, ++ ++ { "MIC2P", NULL, "Channel 2 EN" }, ++ { "MIC2N", NULL, "Channel 2 EN" }, ++ ++ { "MIC3P", NULL, "Channel 3 EN" }, ++ { "MIC3N", NULL, "Channel 3 EN" }, ++ ++ { "MIC4P", NULL, "Channel 4 EN" }, ++ { "MIC4N", NULL, "Channel 4 EN" }, ++ ++}; ++ ++static int ac108_multi_write(u8 reg, u8 val, struct ac10x_priv *ac10x) ++{ ++ u8 i; ++ for (i = 0; i < ac10x->codec_cnt; i++) ++ ac10x_write(reg, val, ac10x->i2cmap[i]); ++ return 0; ++} ++ ++static int ac108_multi_update_bits(u8 reg, u8 mask, u8 val, struct ac10x_priv *ac10x) ++{ ++ int r = 0; ++ u8 i; ++ for (i = 0; i < ac10x->codec_cnt; i++) ++ r |= ac10x_update_bits(reg, mask, val, ac10x->i2cmap[i]); ++ return r; ++} ++ ++static unsigned int ac108_codec_read(struct snd_soc_codec *codec, unsigned int reg) ++{ ++ unsigned char val_r; ++ struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev); ++ /*read one chip is fine*/ ++ ac10x_read(reg, &val_r, ac10x->i2cmap[_MASTER_INDEX]); ++ return val_r; ++} ++ ++static int ac108_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) ++{ ++ struct ac10x_priv *ac10x = dev_get_drvdata(codec->dev); ++ ac108_multi_write(reg, val, ac10x); ++ return 0; ++} ++ ++/** ++ * The Power management related registers are Reg01h~Reg09h ++ * 0x01-0x05,0x08,use the default value ++ * @author baozhu (17-6-21) ++ * ++ * @param ac10x ++ */ ++static void ac108_configure_power(struct ac10x_priv *ac10x) ++{ ++ /** ++ * 0x06:Enable Analog LDO ++ */ ++ ac108_multi_update_bits(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE, ac10x); ++ /** ++ * 0x07: ++ * Control VREF output and micbias voltage ? ++ * REF faststart disable, enable Enable VREF (needed for Analog ++ * LDO and MICBIAS) ++ */ ++ ac108_multi_update_bits(PWR_CTRL7, ++ 0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE | ++ 0x01 << VREF_ENABLE, ++ 0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE | ++ 0x01 << VREF_ENABLE, ++ ac10x); ++ /** ++ * 0x09: ++ * Disable fast-start circuit on VREFP ++ * VREFP_RESCTRL=00=1 MOhm ++ * IGEN_TRIM=100=+25% ++ * Enable VREFP (needed by all audio input channels) ++ */ ++ ac108_multi_update_bits(PWR_CTRL9, ++ 0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL | ++ 0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE, ++ 0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL | ++ 0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE, ++ ac10x); ++} ++ ++/** ++ * The clock management related registers are Reg20h~Reg25h ++ * The PLL management related registers are Reg10h~Reg18h. ++ * @author baozhu (17-6-20) ++ * ++ * @param ac10x ++ * @param rate : sample rate ++ * ++ * @return int : fail or success ++ */ ++static int ac108_config_pll(struct ac10x_priv *ac10x, unsigned rate, unsigned lrck_ratio) ++{ ++ unsigned int i = 0; ++ struct pll_div ac108_pll_div = { 0 }; ++ ++ if (ac10x->clk_id == SYSCLK_SRC_PLL) { ++ unsigned pll_src, pll_freq_in; ++ ++ if (lrck_ratio == 0) { ++ /* PLL clock source from MCLK */ ++ pll_freq_in = ac10x->sysclk; ++ pll_src = 0x0; ++ } else { ++ /* PLL clock source from BCLK */ ++ pll_freq_in = rate * lrck_ratio; ++ pll_src = 0x1; ++ } ++ ++ /* FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] */ ++ for (i = 0; i < ARRAY_SIZE(ac108_pll_div_list); i++) { ++ if (ac108_pll_div_list[i].freq_in == pll_freq_in && ++ ac108_pll_div_list[i].freq_out % rate == 0) { ++ ac108_pll_div = ac108_pll_div_list[i]; ++ dev_info(&ac10x->i2c[_MASTER_INDEX]->dev, ++ "AC108 PLL freq_in match:%u, freq_out:%u\n\n", ++ ac108_pll_div.freq_in, ac108_pll_div.freq_out); ++ break; ++ } ++ } ++ /* 0x11,0x12,0x13,0x14: Config PLL DIV param M1/M2/N/K1/K2 */ ++ ac108_multi_update_bits(PLL_CTRL5, ++ 0x1f << PLL_POSTDIV1 | 0x01 << PLL_POSTDIV2, ++ ac108_pll_div.k1 << PLL_POSTDIV1 | ++ ac108_pll_div.k2 << PLL_POSTDIV2, ac10x); ++ ac108_multi_update_bits(PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB, ++ (unsigned char)ac108_pll_div.n << PLL_LOOPDIV_LSB, ac10x); ++ ac108_multi_update_bits(PLL_CTRL3, 0x03 << PLL_LOOPDIV_MSB, ++ (ac108_pll_div.n >> 8) << PLL_LOOPDIV_MSB, ac10x); ++ ac108_multi_update_bits(PLL_CTRL2, 0x1f << PLL_PREDIV1 | 0x01 << PLL_PREDIV2, ++ ac108_pll_div.m1 << PLL_PREDIV1 | ++ ac108_pll_div.m2 << PLL_PREDIV2, ac10x); ++ ++ /*0x18: PLL clk lock enable*/ ++ ac108_multi_update_bits(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN, ++ 0x1 << PLL_LOCK_EN, ac10x); ++ ++ /** ++ * 0x20: enable pll, pll source from mclk/bclk, sysclk source from pll, enable sysclk ++ */ ++ ac108_multi_update_bits(SYSCLK_CTRL, ++ 0x01 << PLLCLK_EN | 0x03 << PLLCLK_SRC | ++ 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ++ 0x01 << PLLCLK_EN | pll_src << PLLCLK_SRC | ++ 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ac10x); ++ ac10x->mclk = ac108_pll_div.freq_out; ++ } ++ if (ac10x->clk_id == SYSCLK_SRC_MCLK) { ++ /** ++ *0x20: sysclk source from mclk, enable sysclk ++ */ ++ ac108_multi_update_bits(SYSCLK_CTRL, ++ 0x01 << PLLCLK_EN | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ++ 0x00 << PLLCLK_EN | 0x00 << SYSCLK_SRC | 0x01 << SYSCLK_EN, ++ ac10x); ++ ac10x->mclk = ac10x->sysclk; ++ } ++ ++ return 0; ++} ++ ++/* ++ * support no more than 16 slots. ++ */ ++static int ac108_multi_chips_slots(struct ac10x_priv *ac, int slots) ++{ ++ int i; ++ ++ /* ++ * codec0 enable slots 2,3,0,1 when 1 codec ++ * ++ * codec0 enable slots 6,7,0,1 when 2 codec ++ * codec1 enable slots 2,3,4,5 ++ * ++ * ... ++ */ ++ for (i = 0; i < ac->codec_cnt; i++) { ++ /* rotate map, due to channels rotated by CPU_DAI */ ++ const unsigned vec_mask[] = { ++ 0x3 << 6 | 0x3, // slots 6,7,0,1 ++ 0xF << 2, // slots 2,3,4,5 ++ 0, ++ 0, ++ }; ++ const unsigned vec_maps[] = { ++ /* ++ * chip 0, ++ * mic 0 sample -> slot 6 ++ * mic 1 sample -> slot 7 ++ * mic 2 sample -> slot 0 ++ * mic 3 sample -> slot 1 ++ */ ++ 0x0 << 12 | 0x1 << 14 | 0x2 << 0 | 0x3 << 2, ++ /* ++ * chip 1, ++ * mic 0 sample -> slot 2 ++ * mic 1 sample -> slot 3 ++ * mic 2 sample -> slot 4 ++ * mic 3 sample -> slot 5 ++ */ ++ 0x0 << 4 | 0x1 << 6 | 0x2 << 8 | 0x3 << 10, ++ 0, ++ 0, ++ }; ++ unsigned vec; ++ ++ /* 0x38-0x3A I2S_TX1_CTRLx */ ++ if (ac->codec_cnt == 1) { ++ vec = 0xFUL; ++ } else { ++ vec = vec_mask[i]; ++ } ++ ac10x_write(I2S_TX1_CTRL1, slots - 1, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CTRL2, (vec >> 0) & 0xFF, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CTRL3, (vec >> 8) & 0xFF, ac->i2cmap[i]); ++ ++ /* 0x3C-0x3F I2S_TX1_CHMP_CTRLx */ ++ if (ac->codec_cnt == 1) { ++ vec = (0x2 << 0 | 0x3 << 2 | 0x0 << 4 | 0x1 << 6); ++ } else if (ac->codec_cnt == 2) { ++ vec = vec_maps[i]; ++ } ++ ++ ac10x_write(I2S_TX1_CHMP_CTRL1, (vec >> 0) & 0xFF, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CHMP_CTRL2, (vec >> 8) & 0xFF, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CHMP_CTRL3, (vec >> 16) & 0xFF, ac->i2cmap[i]); ++ ac10x_write(I2S_TX1_CHMP_CTRL4, (vec >> 24) & 0xFF, ac->i2cmap[i]); ++ } ++ return 0; ++} ++ ++static int ac108_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) ++{ ++ unsigned int i, channels, samp_res, rate; ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ unsigned bclkdiv; ++ int ret = 0; ++ u8 v; ++ ++ dev_dbg(dai->dev, "%s() stream=%s play:%d capt:%d +++\n", __func__, ++ snd_pcm_stream_str(substream), ++ dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active, ++ dai->stream[SNDRV_PCM_STREAM_CAPTURE].active); ++ ++ if (ac10x->i2c101) { ++ ret = ac101_hw_params(substream, params, dai); ++ if (ret > 0) { ++ dev_dbg(dai->dev, "%s() L%d returned\n", __func__, __LINE__); ++ /* not configure hw_param twice */ ++ return 0; ++ } ++ } ++ ++ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE && ++ dai->stream[SNDRV_PCM_STREAM_PLAYBACK].active) ++ || (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ++ dai->stream[SNDRV_PCM_STREAM_CAPTURE].active)) { ++ /* not configure hw_param twice */ ++ /* return 0; */ ++ } ++ ++ channels = params_channels(params); ++ ++ /* Master mode, to clear cpu_dai fifos, output bclk without lrck */ ++ ac10x_read(I2S_CTRL, &v, ac10x->i2cmap[_MASTER_INDEX]); ++ if (v & (0x01 << BCLK_IOEN)) { ++ ac10x_update_bits(I2S_CTRL, 0x1 << LRCK_IOEN, ++ 0x0 << LRCK_IOEN, ac10x->i2cmap[_MASTER_INDEX]); ++ } ++ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S8: ++ samp_res = 0; ++ break; ++ case SNDRV_PCM_FORMAT_S16_LE: ++ samp_res = 2; ++ break; ++ case SNDRV_PCM_FORMAT_S20_3LE: ++ samp_res = 3; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ samp_res = 4; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ samp_res = 6; ++ break; ++ default: ++ dev_err(dai->dev, "AC108 don't supported the sample resolution: %u\n", ++ params_format(params)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(ac108_sample_rate); i++) { ++ if (ac108_sample_rate[i].real_val == params_rate(params) / ++ (ac10x->data_protocol + 1UL)) { ++ rate = i; ++ break; ++ } ++ } ++ if (i >= ARRAY_SIZE(ac108_sample_rate)) { ++ return -EINVAL; ++ } ++ ++ if (channels == 8 && ac108_sample_rate[rate].real_val == 96000) { ++ /* 24.576M bit clock is not support by ac108 */ ++ return -EINVAL; ++ } ++ ++ dev_dbg(dai->dev, "rate: %d , channels: %d , samp_res: %d", ++ ac108_sample_rate[rate].real_val, ++ channels, ++ ac108_samp_res[samp_res].real_val); ++ ++ /** ++ * 0x33: ++ * The 8-Low bit of LRCK period value. It is used to program ++ * the number of BCLKs per channel of sample frame. This value ++ * is interpreted as follow: ++ * The 8-Low bit of LRCK period value. It is used to program ++ * the number of BCLKs per channel of sample frame. This value ++ * is interpreted as follow: PCM mode: Number of BCLKs within ++ * (Left + Right) channel width I2S / Left-Justified / ++ * Right-Justified mode: Number of BCLKs within each individual ++ * channel width (Left or Right) N+1 ++ * For example: ++ * n = 7: 8 BCLK width ++ * … ++ * n = 1023: 1024 BCLKs width ++ * 0X32[0:1]: ++ * The 2-High bit of LRCK period value. ++ */ ++ if (ac10x->i2s_mode != PCM_FORMAT) { ++ if (ac10x->data_protocol) { ++ ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1, ++ ac10x); ++ /*encoding mode, the max LRCK period value < 32,so the 2-High bit is zero*/ ++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x); ++ } else { ++ /*TDM mode or normal mode*/ ++ //ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x); ++ ac108_multi_write(I2S_LRCK_CTRL2, ac108_samp_res[samp_res].real_val - 1, ++ ac10x); ++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, 0x00, ac10x); ++ } ++ } else { ++ unsigned div; ++ ++ /*TDM mode or normal mode*/ ++ div = ac108_samp_res[samp_res].real_val * channels - 1; ++ ac108_multi_write(I2S_LRCK_CTRL2, (div & 0xFF), ac10x); ++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x03 << 0, (div >> 8) << 0, ac10x); ++ } ++ ++ /** ++ * 0x35: ++ * TX Encoding mode will add 4bits to mark channel number ++ * TODO: need a chat to explain this ++ */ ++ ac108_multi_update_bits(I2S_FMT_CTRL2, 0x07 << SAMPLE_RESOLUTION | 0x07 << SLOT_WIDTH_SEL, ++ ac108_samp_res[samp_res].reg_val << SAMPLE_RESOLUTION | ++ ac108_samp_res[samp_res].reg_val << SLOT_WIDTH_SEL, ac10x); ++ ++ /** ++ * 0x60: ++ * ADC Sample Rate synchronised with I2S1 clock zone ++ */ ++ ac108_multi_update_bits(ADC_SPRC, 0x0f << ADC_FS_I2S1, ++ ac108_sample_rate[rate].reg_val << ADC_FS_I2S1, ac10x); ++ ac108_multi_write(HPF_EN, 0x0F, ac10x); ++ ++ if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) { ++ ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, ++ ac108_samp_res[samp_res].real_val * channels); ++ } else { ++ ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, 0); ++ } ++ ++ /* ++ * master mode only ++ */ ++ bclkdiv = ac10x->mclk / (ac108_sample_rate[rate].real_val * channels * ++ ac108_samp_res[samp_res].real_val); ++ for (i = 0; i < ARRAY_SIZE(ac108_bclkdivs) - 1; i++) { ++ if (ac108_bclkdivs[i] >= bclkdiv) { ++ break; ++ } ++ } ++ ac108_multi_update_bits(I2S_BCLK_CTRL, 0x0F << BCLKDIV, i << BCLKDIV, ac10x); ++ ++ /* ++ * slots allocation for each chip ++ */ ++ ac108_multi_chips_slots(ac10x, channels); ++ ++ /*0x21: Module clock enable*/ ++ ac108_multi_write(MOD_CLK_EN, 1 << I2S | 1 << ADC_DIGITAL | ++ 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x); ++ /*0x22: Module reset de-asserted*/ ++ ac108_multi_write(MOD_RST_CTRL, 1 << I2S | 1 << ADC_DIGITAL | ++ 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG, ac10x); ++ ++ ac108_multi_write(I2S_TX1_CHMP_CTRL1, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX1_CHMP_CTRL2, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX1_CHMP_CTRL3, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX1_CHMP_CTRL4, 0xE4, ac10x); ++ ++ ac108_multi_write(I2S_TX2_CHMP_CTRL1, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX2_CHMP_CTRL2, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX2_CHMP_CTRL3, 0xE4, ac10x); ++ ac108_multi_write(I2S_TX2_CHMP_CTRL4, 0xE4, ac10x); ++ ++ dev_dbg(dai->dev, "%s() stream=%s ---\n", __func__, ++ snd_pcm_stream_str(substream)); ++ ++ return 0; ++} ++ ++static int ac108_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) ++{ ++ ++ struct ac10x_priv *ac10x = snd_soc_dai_get_drvdata(dai); ++ ++ freq = 24000000; ++ clk_id = SYSCLK_SRC_PLL; ++ ++ switch (clk_id) { ++ case SYSCLK_SRC_MCLK: ++ ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, ++ SYSCLK_SRC_MCLK << SYSCLK_SRC, ac10x); ++ break; ++ case SYSCLK_SRC_PLL: ++ ac108_multi_update_bits(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, ++ SYSCLK_SRC_PLL << SYSCLK_SRC, ac10x); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ac10x->sysclk = freq; ++ ac10x->clk_id = clk_id; ++ ++ return 0; ++} ++ ++/** ++ * The i2s format management related registers are Reg ++ * 30h~Reg36h ++ * 33h,35h will be set in ac108_hw_params, It's BCLK width and ++ * Sample Resolution. ++ * @author baozhu (17-6-20) ++ * ++ * @param dai ++ * @param fmt ++ * ++ * @return int ++ */ ++static int ac108_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ++{ ++ unsigned char tx_offset, lrck_polarity, brck_polarity; ++ struct ac10x_priv *ac10x = dev_get_drvdata(dai->dev); ++ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: /* AC108 Master */ ++ if (! ac10x->i2c101 || _MASTER_MULTI_CODEC == _MASTER_AC108) { ++ /** ++ * 0x30:chip is master mode ,BCLK & LRCK output ++ */ ++ ac108_multi_update_bits(I2S_CTRL, ++ 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | ++ 0x1 << TXEN | 0x1 << GEN, ++ 0x03 << LRCK_IOEN | 0x01 << SDO1_EN | ++ 0x1 << TXEN | 0x1 << GEN, ac10x); ++ /* multi_chips: only one chip set as Master, and the others also need to set as Slave */ ++ ac10x_update_bits(I2S_CTRL, 0x3 << LRCK_IOEN, 0x01 << BCLK_IOEN, ++ ac10x->i2cmap[_MASTER_INDEX]); ++ } else { ++ /* TODO: Both cpu_dai and codec_dai(AC108) be set as slave in DTS */ ++ dev_err(dai->dev, "used as slave when AC101 is master\n"); ++ } ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: /* AC108 Slave */ ++ /** ++ * 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and ++ * SDO2_EN, Transmitter Block Enable, Globe Enable ++ */ ++ ac108_multi_update_bits(I2S_CTRL, ++ 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | ++ 0x1 << TXEN | 0x1 << GEN, ++ 0x00 << LRCK_IOEN | 0x03 << SDO1_EN | ++ 0x0 << TXEN | 0x0 << GEN, ac10x); ++ break; ++ default: ++ dev_err(dai->dev, "AC108 Master/Slave mode config error:%u\n\n", ++ (fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12); ++ return -EINVAL; ++ } ++ ++ /*AC108 config I2S/LJ/RJ/PCM format*/ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT; ++ tx_offset = 1; ++ break; ++ case SND_SOC_DAIFMT_RIGHT_J: ++ ac10x->i2s_mode = RIGHT_JUSTIFIED_FORMAT; ++ tx_offset = 0; ++ break; ++ case SND_SOC_DAIFMT_LEFT_J: ++ ac10x->i2s_mode = LEFT_JUSTIFIED_FORMAT; ++ tx_offset = 0; ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ ac10x->i2s_mode = PCM_FORMAT; ++ tx_offset = 1; ++ break; ++ case SND_SOC_DAIFMT_DSP_B: ++ ac10x->i2s_mode = PCM_FORMAT; ++ tx_offset = 0; ++ break; ++ default: ++ dev_err(dai->dev, "AC108 I2S format config error:%u\n\n", ++ fmt & SND_SOC_DAIFMT_FORMAT_MASK); ++ return -EINVAL; ++ } ++ ++ /*AC108 config BCLK&LRCK polarity*/ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P; ++ lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P; ++ lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N; ++ lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N; ++ lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH; ++ break; ++ default: ++ dev_err(dai->dev, "AC108 config BCLK/LRCLK polarity error:%u\n\n", ++ (fmt & SND_SOC_DAIFMT_INV_MASK) >> 8); ++ return -EINVAL; ++ } ++ ++ ac108_configure_power(ac10x); ++ ++ /** ++ *0x31: 0: normal mode, negative edge drive and positive edge sample ++ 1: invert mode, positive edge drive and negative edge sample ++ */ ++ ac108_multi_update_bits(I2S_BCLK_CTRL, 0x01 << BCLK_POLARITY, ++ brck_polarity << BCLK_POLARITY, ac10x); ++ /** ++ * 0x32: same as 0x31 ++ */ ++ ac108_multi_update_bits(I2S_LRCK_CTRL1, 0x01 << LRCK_POLARITY, ++ lrck_polarity << LRCK_POLARITY, ac10x); ++ /** ++ * 0x34:Encoding Mode Selection,Mode ++ * Selection,data is offset by 1 BCLKs to LRCK ++ * normal mode for the last half cycle of BCLK in the slot ? ++ * turn to hi-z state (TDM) when not transferring slot ? ++ */ ++ ac108_multi_update_bits(I2S_FMT_CTRL1, ++ 0x01 << ENCD_SEL | 0x03 << MODE_SEL | 0x01 << TX2_OFFSET | ++ 0x01 << TX1_OFFSET | 0x01 << TX_SLOT_HIZ | 0x01 << TX_STATE, ++ ac10x->data_protocol << ENCD_SEL | ac10x->i2s_mode << MODE_SEL | ++ tx_offset << TX2_OFFSET | tx_offset << TX1_OFFSET | ++ 0x00 << TX_SLOT_HIZ | 0x01 << TX_STATE, ac10x); ++ ++ /** ++ * 0x60: ++ * MSB / LSB First Select: This driver only support MSB First Select . ++ * OUT2_MUTE,OUT1_MUTE shoule be set in widget. ++ * LRCK = 1 BCLK width ++ * Linear PCM ++ * ++ * TODO:pcm mode, bit[0:1] and bit[2] is special ++ */ ++ ac108_multi_update_bits(I2S_FMT_CTRL3, ++ 0x01 << TX_MLS | 0x03 << SEXT | ++ 0x01 << LRCK_WIDTH | 0x03 << TX_PDM, ++ 0x00 << TX_MLS | 0x03 << SEXT | ++ 0x00 << LRCK_WIDTH | 0x00 << TX_PDM, ac10x); ++ ++ ac108_multi_write(HPF_EN, 0x00, ac10x); ++ ++ if (ac10x->i2c101) { ++ return ac101_set_dai_fmt(dai, fmt); ++ } ++ return 0; ++} ++ ++/* ++ * due to miss channels order in cpu_dai, we meed defer the clock starting. ++ */ ++static int ac108_set_clock(int y_start_n_stop) ++{ ++ u8 reg; ++ int ret = 0; ++ ++ dev_dbg(ac10x->codec->dev, "%s() L%d cmd:%d\n", __func__, __LINE__, y_start_n_stop); ++ ++ /* spin_lock move to machine trigger */ ++ ++ if (y_start_n_stop && ac10x->sysclk_en == 0) { ++ /* enable lrck clock */ ++ ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]); ++ if (reg & (0x01 << BCLK_IOEN)) { ++ ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, ++ 0x03 << LRCK_IOEN, ++ ac10x->i2cmap[_MASTER_INDEX]); ++ } ++ ++ /*0x10: PLL Common voltage enable, PLL enable */ ++ ret = ret || ac108_multi_update_bits(PLL_CTRL1, ++ 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ++ 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ac10x); ++ /* enable global clock */ ++ ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, ++ 0x1 << TXEN | 0x1 << GEN, ac10x); ++ ++ ac10x->sysclk_en = 1UL; ++ } else if (!y_start_n_stop && ac10x->sysclk_en != 0) { ++ /* disable global clock */ ++ ret = ret || ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, ++ 0x0 << TXEN | 0x0 << GEN, ac10x); ++ ++ /*0x10: PLL Common voltage disable, PLL disable */ ++ ret = ret || ac108_multi_update_bits(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN, ++ 0x00 << PLL_EN | 0x00 << PLL_COM_EN, ac10x); ++ ++ /* disable lrck clock if it's enabled */ ++ ac10x_read(I2S_CTRL, ®, ac10x->i2cmap[_MASTER_INDEX]); ++ if (reg & (0x01 << LRCK_IOEN)) { ++ ret = ret || ac10x_update_bits(I2S_CTRL, 0x03 << LRCK_IOEN, ++ 0x01 << BCLK_IOEN, ++ ac10x->i2cmap[_MASTER_INDEX]); ++ } ++ if (!ret) { ++ ac10x->sysclk_en = 0UL; ++ } ++ } ++ ++ return ret; ++} ++ ++static int ac108_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ u8 r; ++ ++ dev_dbg(dai->dev, "%s() stream=%s\n", ++ __func__, ++ snd_pcm_stream_str(substream)); ++ ++ if (ac10x->i2c101 && _MASTER_MULTI_CODEC == _MASTER_AC101) { ++ ac101_trigger(substream, SNDRV_PCM_TRIGGER_START, dai); ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ return 0; ++ } ++ } ++ ++ ac10x_read(I2S_CTRL, &r, ac10x->i2cmap[_MASTER_INDEX]); ++ if ((r & (0x01 << BCLK_IOEN)) && (r & (0x01 << LRCK_IOEN)) == 0) { ++ /* disable global clock */ ++ ac108_multi_update_bits(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, ++ 0x0 << TXEN | 0x0 << GEN, ac10x); ++ } ++ ++ /* delayed clock starting, move to machine trigger() */ ++ ac108_set_clock(1); ++ ++ return 0; ++} ++ ++int ac108_audio_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ if (ac10x->i2c101) { ++ return ac101_audio_startup(substream, dai); ++ } ++ return 0; ++} ++ ++void ac108_aif_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ ac108_set_clock(0); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ++ /*0x21: Module clock disable */ ++ ac108_multi_write(MOD_CLK_EN, 0x0, ac10x); ++ /*0x22: Module reset asserted */ ++ ac108_multi_write(MOD_RST_CTRL, 0x0, ac10x); ++ } ++ ++ if (ac10x->i2c101) { ++ ac101_aif_shutdown(substream, dai); ++ } ++} ++ ++int ac108_aif_mute(struct snd_soc_dai *dai, int mute, int stream) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ if (ac10x->i2c101) { ++ return ac101_aif_mute(dai, mute); ++ } ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops ac108_dai_ops = { ++ .startup = ac108_audio_startup, ++ .shutdown = ac108_aif_shutdown, ++ ++ /*DAI clocking configuration*/ ++ .set_sysclk = ac108_set_sysclk, ++ ++ /*ALSA PCM audio operations*/ ++ .hw_params = ac108_hw_params, ++ .prepare = ac108_prepare, ++ .mute_stream = ac108_aif_mute, ++ ++ /*DAI format configuration*/ ++ .set_fmt = ac108_set_fmt, ++}; ++ ++static struct snd_soc_dai_driver ac108_dai0 = { ++ .name = "ac10x-codec0", ++ #if _USE_CAPTURE ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = AC108_CHANNELS_MAX, ++ .rates = AC108_RATES, ++ .formats = AC108_FORMATS, ++ }, ++ #endif ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = AC108_CHANNELS_MAX, ++ .rates = AC108_RATES, ++ .formats = AC108_FORMATS, ++ }, ++ .ops = &ac108_dai_ops, ++}; ++ ++static struct snd_soc_dai_driver ac108_dai1 = { ++ .name = "ac10x-codec1", ++ #if _USE_CAPTURE ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = AC108_CHANNELS_MAX, ++ .rates = AC108_RATES, ++ .formats = AC108_FORMATS, ++ }, ++ #endif ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = AC108_CHANNELS_MAX, ++ .rates = AC108_RATES, ++ .formats = AC108_FORMATS, ++ }, ++ .ops = &ac108_dai_ops, ++}; ++ ++static struct snd_soc_dai_driver *ac108_dai[] = { ++ &ac108_dai0, ++ &ac108_dai1, ++}; ++ ++static int ac108_add_widgets(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); ++ const struct snd_kcontrol_new* snd_kcntl = ac108_snd_controls; ++ int ctrl_cnt = ARRAY_SIZE(ac108_snd_controls); ++ ++ /* only register controls correspond to exist chips */ ++ if (ac10x->tdm_chips_cnt >= 2) { ++ snd_kcntl = ac108tdm_snd_controls; ++ ctrl_cnt = ARRAY_SIZE(ac108tdm_snd_controls); ++ } ++ snd_soc_add_codec_controls(codec, snd_kcntl, ctrl_cnt); ++ ++ snd_soc_dapm_new_controls(dapm, ac108_dapm_widgets,ARRAY_SIZE(ac108_dapm_widgets)); ++ snd_soc_dapm_add_routes(dapm, ac108_dapm_routes, ARRAY_SIZE(ac108_dapm_routes)); ++ ++ return 0; ++} ++ ++static int ac108_codec_probe(struct snd_soc_codec *codec) ++{ ++ spin_lock_init(&ac10x->lock); ++ ++ ac10x->codec = codec; ++ dev_set_drvdata(codec->dev, ac10x); ++ ac108_add_widgets(codec); ++ ++ if (ac10x->i2c101) { ++ ac101_codec_probe(codec); ++ } ++ ++ /* change default volume */ ++ ac108_multi_update_bits(ADC1_DVOL_CTRL, 0xff, 0xc8, ac10x); ++ ac108_multi_update_bits(ADC2_DVOL_CTRL, 0xff, 0xc8, ac10x); ++ ac108_multi_update_bits(ADC3_DVOL_CTRL, 0xff, 0xc8, ac10x); ++ ac108_multi_update_bits(ADC4_DVOL_CTRL, 0xff, 0xc8, ac10x); ++ ++ return 0; ++} ++ ++static int ac108_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ dev_dbg(codec->dev, "AC108 level:%d\n", level); ++ ++ switch (level) { ++ case SND_SOC_BIAS_ON: ++ ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN, ++ 0x01 << ADC1_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN, ++ 0x01 << ADC2_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN, ++ 0x01 << ADC3_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN, ++ 0x01 << ADC4_MICBIAS_EN, ac10x); ++ break; ++ ++ case SND_SOC_BIAS_PREPARE: ++ /* Put the MICBIASes into regulating mode */ ++ break; ++ ++ case SND_SOC_BIAS_STANDBY: ++ break; ++ ++ case SND_SOC_BIAS_OFF: ++ ac108_multi_update_bits(ANA_ADC1_CTRL1, 0x01 << ADC1_MICBIAS_EN, ++ 0x00 << ADC1_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC2_CTRL1, 0x01 << ADC2_MICBIAS_EN, ++ 0x00 << ADC2_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC3_CTRL1, 0x01 << ADC3_MICBIAS_EN, ++ 0x00 << ADC3_MICBIAS_EN, ac10x); ++ ac108_multi_update_bits(ANA_ADC4_CTRL1, 0x01 << ADC4_MICBIAS_EN, ++ 0x00 << ADC4_MICBIAS_EN, ac10x); ++ break; ++ } ++ ++ if (ac10x->i2c101) { ++ ac101_set_bias_level(codec, level); ++ } ++ return 0; ++} ++ ++int ac108_codec_remove(struct snd_soc_codec *codec) { ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ ++ if (! ac10x->i2c101) { ++ return 0; ++ } ++ return ac101_codec_remove(codec); ++} ++#if __NO_SND_SOC_CODEC_DRV ++void ac108_codec_remove_void(struct snd_soc_codec *codec) ++{ ++ ac108_codec_remove(codec); ++} ++#define ac108_codec_remove ac108_codec_remove_void ++#endif ++ ++int ac108_codec_suspend(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int i; ++ ++ for (i = 0; i < ac10x->codec_cnt; i++) { ++ regcache_cache_only(ac10x->i2cmap[i], true); ++ } ++ ++ if (! ac10x->i2c101) { ++ return 0; ++ } ++ return ac101_codec_suspend(codec); ++} ++ ++int ac108_codec_resume(struct snd_soc_codec *codec) ++{ ++ struct ac10x_priv *ac10x = snd_soc_codec_get_drvdata(codec); ++ int i, ret; ++ ++ /* Sync reg_cache with the hardware */ ++ for (i = 0; i < ac10x->codec_cnt; i++) { ++ regcache_cache_only(ac10x->i2cmap[i], false); ++ ret = regcache_sync(ac10x->i2cmap[i]); ++ if (ret != 0) { ++ dev_err(codec->dev, "Failed to sync i2cmap%d register cache: %d\n", ++ i, ret); ++ regcache_cache_only(ac10x->i2cmap[i], true); ++ } ++ } ++ ++ if (! ac10x->i2c101) { ++ return 0; ++ } ++ return ac101_codec_resume(codec); ++} ++ ++static struct snd_soc_codec_driver ac10x_soc_codec_driver = { ++ .probe = ac108_codec_probe, ++ .remove = ac108_codec_remove, ++ .suspend = ac108_codec_suspend, ++ .resume = ac108_codec_resume, ++ .set_bias_level = ac108_set_bias_level, ++ .read = ac108_codec_read, ++ .write = ac108_codec_write, ++}; ++ ++static ssize_t ac108_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int val = 0, flag = 0; ++ u8 i = 0, reg, num, value_w, value_r[4]; ++ ++ val = simple_strtol(buf, NULL, 16); ++ flag = (val >> 16) & 0xF; ++ ++ if (flag) { ++ reg = (val >> 8) & 0xFF; ++ value_w = val & 0xFF; ++ ac108_multi_write(reg, value_w, ac10x); ++ dev_info(dev, "Write 0x%02x to REG:0x%02x\n", value_w, reg); ++ } else { ++ int k; ++ ++ reg = (val >> 8) & 0xFF; ++ num = val & 0xff; ++ dev_info(dev, "\nRead: start REG:0x%02x,count:0x%02x\n", reg, num); ++ ++ for (k = 0; k < ac10x->codec_cnt; k++) ++ regcache_cache_bypass(ac10x->i2cmap[k], true); ++ ++ do { ++ memset(value_r, 0, sizeof value_r); ++ ++ for (k = 0; k < ac10x->codec_cnt; k++) ++ ac10x_read(reg, &value_r[k], ac10x->i2cmap[k]); ++ ++ if (ac10x->codec_cnt >= 2) ++ dev_info(dev, "REG[0x%02x]: 0x%02x 0x%02x", reg, ++ value_r[0], value_r[1]); ++ else ++ dev_info(dev, "REG[0x%02x]: 0x%02x", reg, value_r[0]); ++ reg++; ++ ++ if ((++i == num) || (i % 4 == 0)) ++ dev_info(dev, "\n"); ++ } while (i < num); ++ ++ for (k = 0; k < ac10x->codec_cnt; k++) ++ regcache_cache_bypass(ac10x->i2cmap[k], false); ++ } ++ ++ return count; ++} ++ ++static ssize_t ac108_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ dev_info(dev, "echo flag|reg|val > ac108\n"); ++ dev_info(dev, "eg read star addres=0x06,count 0x10:echo 0610 >ac108\n"); ++ dev_info(dev, "eg write value:0xfe to address:0x06 :echo 106fe > ac108\n"); ++ return 0; ++} ++ ++static DEVICE_ATTR(ac108, 0644, ac108_show, ac108_store); ++static struct attribute *ac108_debug_attrs[] = { ++ &dev_attr_ac108.attr, ++ NULL, ++}; ++static struct attribute_group ac108_debug_attr_group = { ++ .name = "ac108_debug", ++ .attrs = ac108_debug_attrs, ++}; ++ ++static const struct i2c_device_id ac108_i2c_id[] = { ++ { "ac108_0", 0 }, ++ { "ac108_1", 1 }, ++ { "ac108_2", 2 }, ++ { "ac108_3", 3 }, ++ { "ac101", AC101_I2C_ID }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ac108_i2c_id); ++ ++static const struct regmap_config ac108_regmap = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .reg_stride = 1, ++ .max_register = 0xDF, ++ .cache_type = REGCACHE_FLAT, ++}; ++static int ac108_i2c_probe(struct i2c_client *i2c) ++{ ++ struct device_node *np = i2c->dev.of_node; ++ const struct i2c_device_id *i2c_id; ++ unsigned int val = 0; ++ int ret = 0, index; ++ ++ if (ac10x == NULL) { ++ ac10x = kzalloc(sizeof(struct ac10x_priv), GFP_KERNEL); ++ if (ac10x == NULL) { ++ dev_err(&i2c->dev, "Unable to allocate ac10x private data\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ i2c_id = i2c_match_id(ac108_i2c_id, i2c); ++ index = (int)i2c_id->driver_data; ++ if (index == AC101_I2C_ID) { ++ ac10x->i2c101 = i2c; ++ i2c_set_clientdata(i2c, ac10x); ++ ret = ac101_probe(i2c, i2c_id); ++ if (ret) { ++ ac10x->i2c101 = NULL; ++ return ret; ++ } ++ goto __ret; ++ } ++ ++ ret = of_property_read_u32(np, "data-protocol", &val); ++ if (ret) { ++ dev_err(&i2c->dev, "Please set data-protocol.\n"); ++ return -EINVAL; ++ } ++ ac10x->data_protocol = val; ++ ++ if (of_property_read_u32(np, "tdm-chips-count", &val)) val = 1; ++ ac10x->tdm_chips_cnt = val; ++ ++ dev_info(&i2c->dev, " ac10x i2c_id number: %d\n", index); ++ dev_info(&i2c->dev, " ac10x data protocol: %d\n", ac10x->data_protocol); ++ ++ ac10x->i2c[index] = i2c; ++ ac10x->i2cmap[index] = devm_regmap_init_i2c(i2c, &ac108_regmap); ++ if (IS_ERR(ac10x->i2cmap[index])) { ++ ret = PTR_ERR(ac10x->i2cmap[index]); ++ dev_err(&i2c->dev, "Fail to initialize i2cmap%d I/O: %d\n", index, ret); ++ return ret; ++ } ++ ++ /* ++ * Writing this register with 0x12 ++ * will resets all register to their default state. ++ */ ++ regcache_cache_only(ac10x->i2cmap[index], false); ++ ++ ret = regmap_write(ac10x->i2cmap[index], CHIP_RST, CHIP_RST_VAL); ++ msleep(1); ++ ++ /* sync regcache for FLAT type */ ++ ac10x_fill_regcache(&i2c->dev, ac10x->i2cmap[index]); ++ ++ ac10x->codec_cnt++; ++ dev_info(&i2c->dev, " ac10x codec count : %d\n", ac10x->codec_cnt); ++ ++ ret = sysfs_create_group(&i2c->dev.kobj, &ac108_debug_attr_group); ++ if (ret) { ++ dev_err(&i2c->dev, "failed to create attr group\n"); ++ } ++ ++__ret: ++ /* It's time to bind codec to i2c[_MASTER_INDEX] when all i2c are ready */ ++ if ((ac10x->codec_cnt != 0 && ac10x->tdm_chips_cnt < 2) ++ || (ac10x->i2c[0] && ac10x->i2c[1] && ac10x->i2c101)) { ++ /* no playback stream */ ++ if (! ac10x->i2c101) { ++ memset(&ac108_dai[_MASTER_INDEX]->playback, '\0', ++ sizeof ac108_dai[_MASTER_INDEX]->playback); ++ } ++ ret = snd_soc_register_codec(&ac10x->i2c[_MASTER_INDEX]->dev, ++ &ac10x_soc_codec_driver, ++ ac108_dai[_MASTER_INDEX], 1); ++ if (ret < 0) { ++ dev_err(&i2c->dev, "Failed to register ac10x codec: %d\n", ret); ++ } ++ } ++ return ret; ++} ++ ++static void ac108_i2c_remove(struct i2c_client *i2c) ++{ ++ if (ac10x->codec != NULL) { ++ snd_soc_unregister_codec(&ac10x->i2c[_MASTER_INDEX]->dev); ++ ac10x->codec = NULL; ++ } ++ if (i2c == ac10x->i2c101) { ++ ac101_remove(ac10x->i2c101); ++ ac10x->i2c101 = NULL; ++ goto __ret; ++ } ++ ++ if (i2c == ac10x->i2c[0]) { ++ ac10x->i2c[0] = NULL; ++ } ++ if (i2c == ac10x->i2c[1]) { ++ ac10x->i2c[1] = NULL; ++ } ++ ++ sysfs_remove_group(&i2c->dev.kobj, &ac108_debug_attr_group); ++ ++__ret: ++ if (!ac10x->i2c[0] && !ac10x->i2c[1] && !ac10x->i2c101) { ++ kfree(ac10x); ++ ac10x = NULL; ++ } ++} ++ ++static const struct of_device_id ac108_of_match[] = { ++ { .compatible = "x-power,ac108_0", }, ++ { .compatible = "x-power,ac108_1", }, ++ { .compatible = "x-power,ac108_2", }, ++ { .compatible = "x-power,ac108_3", }, ++ { .compatible = "x-power,ac101", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, ac108_of_match); ++ ++static struct i2c_driver ac108_i2c_driver = { ++ .driver = { ++ .name = "ac10x-codec", ++ .of_match_table = ac108_of_match, ++ }, ++ .probe = ac108_i2c_probe, ++ .remove = ac108_i2c_remove, ++ .id_table = ac108_i2c_id, ++}; ++ ++module_i2c_driver(ac108_i2c_driver); ++ ++MODULE_DESCRIPTION("ASoC AC108 driver"); ++MODULE_AUTHOR("Baozhu Zuo"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/sound/soc/codecs/ac108.h +@@ -0,0 +1,749 @@ ++/* ++ * ac108.h -- ac108 ALSA Soc Audio driver ++ * ++ * Author: panjunwen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#ifndef _AC108_H ++#define _AC108_H ++ ++/*** AC108 Codec Register Define***/ ++ ++//Chip Reset ++#define CHIP_RST 0x00 ++#define CHIP_RST_VAL 0x12 ++ ++//Power Control ++#define PWR_CTRL1 0x01 ++#define PWR_CTRL2 0x02 ++#define PWR_CTRL3 0x03 ++#define PWR_CTRL4 0x04 ++#define PWR_CTRL5 0x05 ++#define PWR_CTRL6 0x06 ++#define PWR_CTRL7 0x07 ++#define PWR_CTRL8 0x08 ++#define PWR_CTRL9 0x09 ++ ++//PLL Configure Control ++#define PLL_CTRL1 0x10 ++#define PLL_CTRL2 0x11 ++#define PLL_CTRL3 0x12 ++#define PLL_CTRL4 0x13 ++#define PLL_CTRL5 0x14 ++#define PLL_CTRL6 0x16 ++#define PLL_CTRL7 0x17 ++#define PLL_LOCK_CTRL 0x18 ++ ++//System Clock Control ++#define SYSCLK_CTRL 0x20 ++#define MOD_CLK_EN 0x21 ++#define MOD_RST_CTRL 0x22 ++#define DSM_CLK_CTRL 0x25 ++ ++//I2S Common Control ++#define I2S_CTRL 0x30 ++#define I2S_BCLK_CTRL 0x31 ++#define I2S_LRCK_CTRL1 0x32 ++#define I2S_LRCK_CTRL2 0x33 ++#define I2S_FMT_CTRL1 0x34 ++#define I2S_FMT_CTRL2 0x35 ++#define I2S_FMT_CTRL3 0x36 ++ ++//I2S TX1 Control ++#define I2S_TX1_CTRL1 0x38 ++#define I2S_TX1_CTRL2 0x39 ++#define I2S_TX1_CTRL3 0x3A ++#define I2S_TX1_CHMP_CTRL1 0x3C ++#define I2S_TX1_CHMP_CTRL2 0x3D ++#define I2S_TX1_CHMP_CTRL3 0x3E ++#define I2S_TX1_CHMP_CTRL4 0x3F ++ ++//I2S TX2 Control ++#define I2S_TX2_CTRL1 0x40 ++#define I2S_TX2_CTRL2 0x41 ++#define I2S_TX2_CTRL3 0x42 ++#define I2S_TX2_CHMP_CTRL1 0x44 ++#define I2S_TX2_CHMP_CTRL2 0x45 ++#define I2S_TX2_CHMP_CTRL3 0x46 ++#define I2S_TX2_CHMP_CTRL4 0x47 ++ ++//I2S RX1 Control ++#define I2S_RX1_CTRL1 0x50 ++#define I2S_RX1_CHMP_CTRL1 0x54 ++#define I2S_RX1_CHMP_CTRL2 0x55 ++#define I2S_RX1_CHMP_CTRL3 0x56 ++#define I2S_RX1_CHMP_CTRL4 0x57 ++ ++//I2S Loopback Debug ++#define I2S_LPB_DEBUG 0x58 ++ ++//ADC Common Control ++#define ADC_SPRC 0x60 ++#define ADC_DIG_EN 0x61 ++#define DMIC_EN 0x62 ++#define ADC_DSR 0x63 ++#define ADC_FIR 0x64 ++#define ADC_DDT_CTRL 0x65 ++ ++//HPF Control ++#define HPF_EN 0x66 ++#define HPF_COEF_REGH1 0x67 ++#define HPF_COEF_REGH2 0x68 ++#define HPF_COEF_REGL1 0x69 ++#define HPF_COEF_REGL2 0x6A ++#define HPF_GAIN_REGH1 0x6B ++#define HPF_GAIN_REGH2 0x6C ++#define HPF_GAIN_REGL1 0x6D ++#define HPF_GAIN_REGL2 0x6E ++ ++//ADC Digital Channel Volume Control ++#define ADC1_DVOL_CTRL 0x70 ++#define ADC2_DVOL_CTRL 0x71 ++#define ADC3_DVOL_CTRL 0x72 ++#define ADC4_DVOL_CTRL 0x73 ++ ++//ADC Digital Mixer Source and Gain Control ++#define ADC1_DMIX_SRC 0x76 ++#define ADC2_DMIX_SRC 0x77 ++#define ADC3_DMIX_SRC 0x78 ++#define ADC4_DMIX_SRC 0x79 ++ ++//ADC Digital Debug Control ++#define ADC_DIG_DEBUG 0x7F ++ ++//I2S Pad Drive Control ++#define I2S_DAT_PADDRV_CTRL 0x80 ++#define I2S_CLK_PADDRV_CTRL 0x81 ++ ++//Analog PGA Control ++#define ANA_PGA1_CTRL 0x90 ++#define ANA_PGA2_CTRL 0x91 ++#define ANA_PGA3_CTRL 0x92 ++#define ANA_PGA4_CTRL 0x93 ++ ++//MIC Offset Control ++#define MIC_OFFSET_CTRL1 0x96 ++#define MIC_OFFSET_CTRL2 0x97 ++#define MIC1_OFFSET_STATU1 0x98 ++#define MIC1_OFFSET_STATU2 0x99 ++#define MIC2_OFFSET_STATU1 0x9A ++#define MIC2_OFFSET_STATU2 0x9B ++#define MIC3_OFFSET_STATU1 0x9C ++#define MIC3_OFFSET_STATU2 0x9D ++#define MIC4_OFFSET_STATU1 0x9E ++#define MIC4_OFFSET_STATU2 0x9F ++ ++//ADC1 Analog Control ++#define ANA_ADC1_CTRL1 0xA0 ++#define ANA_ADC1_CTRL2 0xA1 ++#define ANA_ADC1_CTRL3 0xA2 ++#define ANA_ADC1_CTRL4 0xA3 ++#define ANA_ADC1_CTRL5 0xA4 ++#define ANA_ADC1_CTRL6 0xA5 ++#define ANA_ADC1_CTRL7 0xA6 ++ ++//ADC2 Analog Control ++#define ANA_ADC2_CTRL1 0xA7 ++#define ANA_ADC2_CTRL2 0xA8 ++#define ANA_ADC2_CTRL3 0xA9 ++#define ANA_ADC2_CTRL4 0xAA ++#define ANA_ADC2_CTRL5 0xAB ++#define ANA_ADC2_CTRL6 0xAC ++#define ANA_ADC2_CTRL7 0xAD ++ ++//ADC3 Analog Control ++#define ANA_ADC3_CTRL1 0xAE ++#define ANA_ADC3_CTRL2 0xAF ++#define ANA_ADC3_CTRL3 0xB0 ++#define ANA_ADC3_CTRL4 0xB1 ++#define ANA_ADC3_CTRL5 0xB2 ++#define ANA_ADC3_CTRL6 0xB3 ++#define ANA_ADC3_CTRL7 0xB4 ++ ++//ADC4 Analog Control ++#define ANA_ADC4_CTRL1 0xB5 ++#define ANA_ADC4_CTRL2 0xB6 ++#define ANA_ADC4_CTRL3 0xB7 ++#define ANA_ADC4_CTRL4 0xB8 ++#define ANA_ADC4_CTRL5 0xB9 ++#define ANA_ADC4_CTRL6 0xBA ++#define ANA_ADC4_CTRL7 0xBB ++ ++//GPIO Configure ++#define GPIO_CFG1 0xC0 ++#define GPIO_CFG2 0xC1 ++#define GPIO_DAT 0xC2 ++#define GPIO_DRV 0xC3 ++#define GPIO_PULL 0xC4 ++#define GPIO_INT_CFG 0xC5 ++#define GPIO_INT_EN 0xC6 ++#define GPIO_INT_STATUS 0xC7 ++ ++//Misc ++#define BGTC_DAT 0xD1 ++#define BGVC_DAT 0xD2 ++#define PRNG_CLK_CTRL 0xDF ++ ++/*** AC108 Codec Register Bit Define***/ ++ ++/*PWR_CTRL1*/ ++#define CP12_CTRL 4 ++#define CP12_SENSE_SELECT 3 ++ ++/*PWR_CTRL2*/ ++#define CP12_SENSE_FILT 6 ++#define CP12_COMP_FF_EN 3 ++#define CP12_FORCE_ENABLE 2 ++#define CP12_FORCE_RSTB 1 ++ ++/*PWR_CTRL3*/ ++#define LDO33DIG_CTRL 0 ++ ++/*PWR_CTRL6*/ ++#define LDO33ANA_2XHDRM 2 ++#define LDO33ANA_ENABLE 0 ++ ++/*PWR_CTRL7*/ ++#define VREF_SEL 3 ++#define VREF_FASTSTART_ENABLE 1 ++#define VREF_ENABLE 0 ++ ++/*PWR_CTRL9*/ ++#define VREFP_FASTSTART_ENABLE 7 ++#define VREFP_RESCTRL 5 ++#define VREFP_LPMODE 4 ++#define IGEN_TRIM 1 ++#define VREFP_ENABLE 0 ++ ++/*PLL_CTRL1*/ ++#define PLL_IBIAS 4 ++#define PLL_NDET 3 ++#define PLL_LOCKED_STATUS 2 ++#define PLL_COM_EN 1 ++#define PLL_EN 0 ++ ++/*PLL_CTRL2*/ ++#define PLL_PREDIV2 5 ++#define PLL_PREDIV1 0 ++ ++/*PLL_CTRL3*/ ++#define PLL_LOOPDIV_MSB 0 ++ ++/*PLL_CTRL4*/ ++#define PLL_LOOPDIV_LSB 0 ++ ++/*PLL_CTRL5*/ ++#define PLL_POSTDIV2 5 ++#define PLL_POSTDIV1 0 ++ ++/*PLL_CTRL6*/ ++#define PLL_LDO 6 ++#define PLL_CP 0 ++ ++/*PLL_CTRL7*/ ++#define PLL_CAP 6 ++#define PLL_RES 4 ++#define PLL_TEST_EN 0 ++ ++/*PLL_LOCK_CTRL*/ ++#define LOCK_LEVEL1 2 ++#define LOCK_LEVEL2 1 ++#define PLL_LOCK_EN 0 ++ ++/*SYSCLK_CTRL*/ ++#define PLLCLK_EN 7 ++#define PLLCLK_SRC 4 ++#define SYSCLK_SRC 3 ++#define SYSCLK_EN 0 ++ ++/*MOD_CLK_EN & MOD_RST_CTRL*/ ++#define I2S 7 ++#define ADC_DIGITAL 4 ++#define MIC_OFFSET_CALIBRATION 1 ++#define ADC_ANALOG 0 ++ ++/*DSM_CLK_CTRL*/ ++#define MIC_OFFSET_DIV 4 ++#define DSM_CLK_SEL 0 ++ ++/*I2S_CTRL*/ ++#define BCLK_IOEN 7 ++#define LRCK_IOEN 6 ++#define SDO2_EN 5 ++#define SDO1_EN 4 ++#define TXEN 2 ++#define RXEN 1 ++#define GEN 0 ++ ++/*I2S_BCLK_CTRL*/ ++#define EDGE_TRANSFER 5 ++#define BCLK_POLARITY 4 ++#define BCLKDIV 0 ++ ++/*I2S_LRCK_CTRL1*/ ++#define LRCK_POLARITY 4 ++#define LRCK_PERIODH 0 ++ ++/*I2S_LRCK_CTRL2*/ ++#define LRCK_PERIODL 0 ++ ++/*I2S_FMT_CTRL1*/ ++#define ENCD_SEL 6 ++#define MODE_SEL 4 ++#define TX2_OFFSET 3 ++#define TX1_OFFSET 2 ++#define TX_SLOT_HIZ 1 ++#define TX_STATE 0 ++ ++/*I2S_FMT_CTRL2*/ ++#define SLOT_WIDTH_SEL 4 ++#define SAMPLE_RESOLUTION 0 ++ ++/*I2S_FMT_CTRL3*/ ++#define TX_MLS 7 ++#define SEXT 5 ++#define OUT2_MUTE 4 ++#define OUT1_MUTE 3 ++#define LRCK_WIDTH 2 ++#define TX_PDM 0 ++ ++/*I2S_TX1_CTRL1*/ ++#define TX1_CHSEL 0 ++ ++/*I2S_TX1_CTRL2*/ ++#define TX1_CH8_EN 7 ++#define TX1_CH7_EN 6 ++#define TX1_CH6_EN 5 ++#define TX1_CH5_EN 4 ++#define TX1_CH4_EN 3 ++#define TX1_CH3_EN 2 ++#define TX1_CH2_EN 1 ++#define TX1_CH1_EN 0 ++ ++/*I2S_TX1_CTRL3*/ ++#define TX1_CH16_EN 7 ++#define TX1_CH15_EN 6 ++#define TX1_CH14_EN 5 ++#define TX1_CH13_EN 4 ++#define TX1_CH12_EN 3 ++#define TX1_CH11_EN 2 ++#define TX1_CH10_EN 1 ++#define TX1_CH9_EN 0 ++ ++/*I2S_TX1_CHMP_CTRL1*/ ++#define TX1_CH4_MAP 6 ++#define TX1_CH3_MAP 4 ++#define TX1_CH2_MAP 2 ++#define TX1_CH1_MAP 0 ++ ++/*I2S_TX1_CHMP_CTRL2*/ ++#define TX1_CH8_MAP 6 ++#define TX1_CH7_MAP 4 ++#define TX1_CH6_MAP 2 ++#define TX1_CH5_MAP 0 ++ ++/*I2S_TX1_CHMP_CTRL3*/ ++#define TX1_CH12_MAP 6 ++#define TX1_CH11_MAP 4 ++#define TX1_CH10_MAP 2 ++#define TX1_CH9_MAP 0 ++ ++/*I2S_TX1_CHMP_CTRL4*/ ++#define TX1_CH16_MAP 6 ++#define TX1_CH15_MAP 4 ++#define TX1_CH14_MAP 2 ++#define TX1_CH13_MAP 0 ++ ++/*I2S_TX2_CTRL1*/ ++#define TX2_CHSEL 0 ++ ++/*I2S_TX2_CHMP_CTRL1*/ ++#define TX2_CH4_MAP 6 ++#define TX2_CH3_MAP 4 ++#define TX2_CH2_MAP 2 ++#define TX2_CH1_MAP 0 ++ ++/*I2S_TX2_CHMP_CTRL2*/ ++#define TX2_CH8_MAP 6 ++#define TX2_CH7_MAP 4 ++#define TX2_CH6_MAP 2 ++#define TX2_CH5_MAP 0 ++ ++/*I2S_TX2_CHMP_CTRL3*/ ++#define TX2_CH12_MAP 6 ++#define TX2_CH11_MAP 4 ++#define TX2_CH10_MAP 2 ++#define TX2_CH9_MAP 0 ++ ++/*I2S_TX2_CHMP_CTRL4*/ ++#define TX2_CH16_MAP 6 ++#define TX2_CH15_MAP 4 ++#define TX2_CH14_MAP 2 ++#define TX2_CH13_MAP 0 ++ ++/*I2S_RX1_CTRL1*/ ++#define RX1_CHSEL 0 ++ ++/*I2S_RX1_CHMP_CTRL1*/ ++#define RX1_CH4_MAP 6 ++#define RX1_CH3_MAP 4 ++#define RX1_CH2_MAP 2 ++#define RX1_CH1_MAP 0 ++ ++/*I2S_RX1_CHMP_CTRL2*/ ++#define RX1_CH8_MAP 6 ++#define RX1_CH7_MAP 4 ++#define RX1_CH6_MAP 2 ++#define RX1_CH5_MAP 0 ++ ++/*I2S_RX1_CHMP_CTRL3*/ ++#define RX1_CH12_MAP 6 ++#define RX1_CH11_MAP 4 ++#define RX1_CH10_MAP 2 ++#define RX1_CH9_MAP 0 ++ ++/*I2S_RX1_CHMP_CTRL4*/ ++#define RX1_CH16_MAP 6 ++#define RX1_CH15_MAP 4 ++#define RX1_CH14_MAP 2 ++#define RX1_CH13_MAP 0 ++ ++/*I2S_LPB_DEBUG*/ ++#define I2S_LPB_DEBUG_EN 0 ++ ++/*ADC_SPRC*/ ++#define ADC_FS_I2S1 0 ++ ++/*ADC_DIG_EN*/ ++#define DG_EN 4 ++#define ENAD4 3 ++#define ENAD3 2 ++#define ENAD2 1 ++#define ENAD1 0 ++ ++/*DMIC_EN*/ ++#define DMIC2_EN 1 ++#define DMIC1_EN 0 ++ ++/*ADC_DSR*/ ++#define DIG_ADC4_SRS 6 ++#define DIG_ADC3_SRS 4 ++#define DIG_ADC2_SRS 2 ++#define DIG_ADC1_SRS 0 ++ ++/*ADC_DDT_CTRL*/ ++#define ADOUT_DLY_EN 2 ++#define ADOUT_DTS 0 ++ ++/*HPF_EN*/ ++#define DIG_ADC4_HPF_EN 3 ++#define DIG_ADC3_HPF_EN 2 ++#define DIG_ADC2_HPF_EN 1 ++#define DIG_ADC1_HPF_EN 0 ++ ++/*ADC1_DMIX_SRC*/ ++#define ADC1_ADC4_DMXL_GC 7 ++#define ADC1_ADC3_DMXL_GC 6 ++#define ADC1_ADC2_DMXL_GC 5 ++#define ADC1_ADC1_DMXL_GC 4 ++#define ADC1_ADC4_DMXL_SRC 3 ++#define ADC1_ADC3_DMXL_SRC 2 ++#define ADC1_ADC2_DMXL_SRC 1 ++#define ADC1_ADC1_DMXL_SRC 0 ++ ++/*ADC2_DMIX_SRC*/ ++#define ADC2_ADC4_DMXL_GC 7 ++#define ADC2_ADC3_DMXL_GC 6 ++#define ADC2_ADC2_DMXL_GC 5 ++#define ADC2_ADC1_DMXL_GC 4 ++#define ADC2_ADC4_DMXL_SRC 3 ++#define ADC2_ADC3_DMXL_SRC 2 ++#define ADC2_ADC2_DMXL_SRC 1 ++#define ADC2_ADC1_DMXL_SRC 0 ++ ++/*ADC3_DMIX_SRC*/ ++#define ADC3_ADC4_DMXL_GC 7 ++#define ADC3_ADC3_DMXL_GC 6 ++#define ADC3_ADC2_DMXL_GC 5 ++#define ADC3_ADC1_DMXL_GC 4 ++#define ADC3_ADC4_DMXL_SRC 3 ++#define ADC3_ADC3_DMXL_SRC 2 ++#define ADC3_ADC2_DMXL_SRC 1 ++#define ADC3_ADC1_DMXL_SRC 0 ++ ++/*ADC4_DMIX_SRC*/ ++#define ADC4_ADC4_DMXL_GC 7 ++#define ADC4_ADC3_DMXL_GC 6 ++#define ADC4_ADC2_DMXL_GC 5 ++#define ADC4_ADC1_DMXL_GC 4 ++#define ADC4_ADC4_DMXL_SRC 3 ++#define ADC4_ADC3_DMXL_SRC 2 ++#define ADC4_ADC2_DMXL_SRC 1 ++#define ADC4_ADC1_DMXL_SRC 0 ++ ++/*ADC_DIG_DEBUG*/ ++#define ADC_PTN_SEL 0 ++ ++/*I2S_DAT_PADDRV_CTRL*/ ++#define TX2_DAT_DRV 4 ++#define TX1_DAT_DRV 0 ++ ++/*I2S_CLK_PADDRV_CTRL*/ ++#define LRCK_DRV 4 ++#define BCLK_DRV 0 ++ ++/*ANA_PGA1_CTRL*/ ++#define ADC1_ANALOG_PGA 1 ++#define ADC1_ANALOG_PGA_STEP 0 ++ ++/*ANA_PGA2_CTRL*/ ++#define ADC2_ANALOG_PGA 1 ++#define ADC2_ANALOG_PGA_STEP 0 ++ ++/*ANA_PGA3_CTRL*/ ++#define ADC3_ANALOG_PGA 1 ++#define ADC3_ANALOG_PGA_STEP 0 ++ ++/*ANA_PGA4_CTRL*/ ++#define ADC4_ANALOG_PGA 1 ++#define ADC4_ANALOG_PGA_STEP 0 ++ ++ ++/*MIC_OFFSET_CTRL1*/ ++#define MIC_OFFSET_CAL_EN4 3 ++#define MIC_OFFSET_CAL_EN3 2 ++#define MIC_OFFSET_CAL_EN2 1 ++#define MIC_OFFSET_CAL_EN1 0 ++ ++/*MIC_OFFSET_CTRL2*/ ++#define MIC_OFFSET_CAL_GAIN 3 ++#define MIC_OFFSET_CAL_CHANNEL 1 ++#define MIC_OFFSET_CAL_EN_ONCE 0 ++ ++/*MIC1_OFFSET_STATU1*/ ++#define MIC1_OFFSET_CAL_DONE 7 ++#define MIC1_OFFSET_CAL_RUN_STA 6 ++#define MIC1_OFFSET_MSB 0 ++ ++/*MIC1_OFFSET_STATU2*/ ++#define MIC1_OFFSET_LSB 0 ++ ++/*MIC2_OFFSET_STATU1*/ ++#define MIC2_OFFSET_CAL_DONE 7 ++#define MIC2_OFFSET_CAL_RUN_STA 6 ++#define MIC2_OFFSET_MSB 0 ++ ++/*MIC2_OFFSET_STATU2*/ ++#define MIC2_OFFSET_LSB 0 ++ ++/*MIC3_OFFSET_STATU1*/ ++#define MIC3_OFFSET_CAL_DONE 7 ++#define MIC3_OFFSET_CAL_RUN_STA 6 ++#define MIC3_OFFSET_MSB 0 ++ ++/*MIC3_OFFSET_STATU2*/ ++#define MIC3_OFFSET_LSB 0 ++ ++/*MIC4_OFFSET_STATU1*/ ++#define MIC4_OFFSET_CAL_DONE 7 ++#define MIC4_OFFSET_CAL_RUN_STA 6 ++#define MIC4_OFFSET_MSB 0 ++ ++/*MIC4_OFFSET_STATU2*/ ++#define MIC4_OFFSET_LSB 0 ++ ++/*ANA_ADC1_CTRL1*/ ++#define ADC1_PGA_BYPASS 7 ++#define ADC1_PGA_BYP_RCM 6 ++#define ADC1_PGA_CTRL_RCM 4 ++#define ADC1_PGA_MUTE 3 ++#define ADC1_DSM_ENABLE 2 ++#define ADC1_PGA_ENABLE 1 ++#define ADC1_MICBIAS_EN 0 ++ ++/*ANA_ADC1_CTRL3*/ ++#define ADC1_ANA_CAL_EN 5 ++#define ADC1_SEL_OUT_EDGE 3 ++#define ADC1_DSM_DISABLE 2 ++#define ADC1_VREFP_DISABLE 1 ++#define ADC1_AAF_DISABLE 0 ++ ++/*ANA_ADC1_CTRL6*/ ++#define PGA_CTRL_TC 6 ++#define PGA_CTRL_RC 4 ++#define PGA_CTRL_I_LIN 2 ++#define PGA_CTRL_I_IN 0 ++ ++/*ANA_ADC1_CTRL7*/ ++#define PGA_CTRL_HI_Z 7 ++#define PGA_CTRL_SHORT_RF 6 ++#define PGA_CTRL_VCM_VG 4 ++#define PGA_CTRL_VCM_IN 0 ++ ++/*ANA_ADC2_CTRL1*/ ++#define ADC2_PGA_BYPASS 7 ++#define ADC2_PGA_BYP_RCM 6 ++#define ADC2_PGA_CTRL_RCM 4 ++#define ADC2_PGA_MUTE 3 ++#define ADC2_DSM_ENABLE 2 ++#define ADC2_PGA_ENABLE 1 ++#define ADC2_MICBIAS_EN 0 ++ ++/*ANA_ADC2_CTRL3*/ ++#define ADC2_ANA_CAL_EN 5 ++#define ADC2_SEL_OUT_EDGE 3 ++#define ADC2_DSM_DISABLE 2 ++#define ADC2_VREFP_DISABLE 1 ++#define ADC2_AAF_DISABLE 0 ++ ++/*ANA_ADC2_CTRL6*/ ++#define PGA_CTRL_IBOOST 7 ++#define PGA_CTRL_IQCTRL 6 ++#define PGA_CTRL_OABIAS 4 ++#define PGA_CTRL_CMLP_DIS 3 ++#define PGA_CTRL_PDB_RIN 2 ++#define PGA_CTRL_PEAKDET 0 ++ ++/*ANA_ADC2_CTRL7*/ ++#define AAF_LPMODE_EN 7 ++#define AAF_STG2_IB_SEL 4 ++#define AAFDSM_IB_DIV2 3 ++#define AAF_STG1_IB_SEL 0 ++ ++/*ANA_ADC3_CTRL1*/ ++#define ADC3_PGA_BYPASS 7 ++#define ADC3_PGA_BYP_RCM 6 ++#define ADC3_PGA_CTRL_RCM 4 ++#define ADC3_PGA_MUTE 3 ++#define ADC3_DSM_ENABLE 2 ++#define ADC3_PGA_ENABLE 1 ++#define ADC3_MICBIAS_EN 0 ++ ++/*ANA_ADC3_CTRL3*/ ++#define ADC3_ANA_CAL_EN 5 ++#define ADC3_INVERT_CLK 4 ++#define ADC3_SEL_OUT_EDGE 3 ++#define ADC3_DSM_DISABLE 2 ++#define ADC3_VREFP_DISABLE 1 ++#define ADC3_AAF_DISABLE 0 ++ ++/*ANA_ADC3_CTRL7*/ ++#define DSM_COMP_IB_SEL 6 ++#define DSM_OTA_CTRL 4 ++#define DSM_LPMODE 3 ++#define DSM_OTA_IB_SEL 0 ++ ++/*ANA_ADC4_CTRL1*/ ++#define ADC4_PGA_BYPASS 7 ++#define ADC4_PGA_BYP_RCM 6 ++#define ADC4_PGA_CTRL_RCM 4 ++#define ADC4_PGA_MUTE 3 ++#define ADC4_DSM_ENABLE 2 ++#define ADC4_PGA_ENABLE 1 ++#define ADC4_MICBIAS_EN 0 ++ ++/*ANA_ADC4_CTRL3*/ ++#define ADC4_ANA_CAL_EN 5 ++#define ADC4_SEL_OUT_EDGE 3 ++#define ADC4_DSM_DISABLE 2 ++#define ADC4_VREFP_DISABLE 1 ++#define ADC4_AAF_DISABLE 0 ++ ++/*ANA_ADC4_CTRL6*/ ++#define DSM_DEMOFF 5 ++#define DSM_EN_DITHER 4 ++#define DSM_VREFP_LPMODE 2 ++#define DSM_VREFP_OUTCTRL 0 ++ ++/*ANA_ADC4_CTRL7*/ ++#define CK8M_EN 5 ++#define OSC_EN 4 ++#define ADC4_CLK_GATING 3 ++#define ADC3_CLK_GATING 2 ++#define ADC2_CLK_GATING 1 ++#define ADC1_CLK_GATING 0 ++ ++/*GPIO_CFG1*/ ++#define GPIO2_SELECT 4 ++#define GPIO1_SELECT 0 ++ ++/*GPIO_CFG2*/ ++#define GPIO4_SELECT 4 ++#define GPIO3_SELECT 0 ++ ++/*GPIO_DAT*///order??? ++#define GPIO4_DAT 3 ++#define GPIO3_DAT 2 ++#define GPIO2_DAT 1 ++#define GPIO1_DAT 0 ++ ++/*GPIO_DRV*/ ++#define GPIO4_DRV 6 ++#define GPIO3_DRV 4 ++#define GPIO2_DRV 2 ++#define GPIO1_DRV 0 ++ ++/*GPIO_PULL*/ ++#define GPIO4_PULL 6 ++#define GPIO3_PULL 4 ++#define GPIO2_PULL 2 ++#define GPIO1_PULL 0 ++ ++/*GPIO_INT_CFG*/ ++#define GPIO4_EINT_CFG 6 ++#define GPIO3_EINT_CFG 4 ++#define GPIO2_EINT_CFG 2 ++#define GPIO1_EINT_CFG 0 ++ ++/*GPIO_INT_EN*///order??? ++#define GPIO4_EINT_EN 3 ++#define GPIO3_EINT_EN 2 ++#define GPIO2_EINT_EN 1 ++#define GPIO1_EINT_EN 0 ++ ++/*GPIO_INT_STATUS*///order??? ++#define GPIO4_EINT_STA 3 ++#define GPIO3_EINT_STA 2 ++#define GPIO2_EINT_STA 1 ++#define GPIO1_EINT_STA 0 ++ ++/*PRNG_CLK_CTRL*/ ++#define PRNG_CLK_EN 1 ++#define PRNG_CLK_POS 0 ++ ++/*** Some Config Value ***/ ++ ++//[SYSCLK_CTRL]: PLLCLK_SRC ++#define PLLCLK_SRC_MCLK 0 ++#define PLLCLK_SRC_BCLK 1 ++#define PLLCLK_SRC_GPIO2 2 ++#define PLLCLK_SRC_GPIO3 3 ++ ++//[SYSCLK_CTRL]: SYSCLK_SRC ++#define SYSCLK_SRC_MCLK 0 ++#define SYSCLK_SRC_PLL 1 ++ ++//I2S BCLK POLARITY Control ++#define BCLK_NORMAL_DRIVE_N_SAMPLE_P 0 ++#define BCLK_INVERT_DRIVE_P_SAMPLE_N 1 ++ ++//I2S LRCK POLARITY Control ++#define LRCK_LEFT_LOW_RIGHT_HIGH 0 ++#define LRCK_LEFT_HIGH_RIGHT_LOW 1 ++ ++//I2S Format Selection ++#define PCM_FORMAT 0 ++#define LEFT_JUSTIFIED_FORMAT 1 ++#define RIGHT_JUSTIFIED_FORMAT 2 ++ ++//I2S data protocol types ++ ++#define IS_ENCODING_MODE 0 ++ ++#endif ++ +--- /dev/null ++++ b/sound/soc/codecs/ac10x.h +@@ -0,0 +1,152 @@ ++/* ++ * ac10x.h ++ * ++ * (C) Copyright 2017-2018 ++ * Seeed Technology Co., Ltd. ++ * ++ * PeterYang ++ * ++ * (C) Copyright 2010-2017 ++ * Reuuimlla Technology Co., Ltd. ++ * huangxin ++ * ++ * some simple description for this code ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ */ ++#ifndef __AC10X_H__ ++#define __AC10X_H__ ++ ++#define AC101_I2C_ID 4 ++#define _MASTER_AC108 0 ++#define _MASTER_AC101 1 ++#define _MASTER_MULTI_CODEC _MASTER_AC101 ++ ++/* enable headset detecting & headset button pressing */ ++#define CONFIG_AC101_SWITCH_DETECT ++ ++/* obsolete */ ++#define CONFIG_AC10X_TRIG_LOCK 0 ++ ++#ifdef AC101_DEBG ++ #define AC101_DBG(format,args...) printk("[AC101] %s() L%d " format, __func__, __LINE__, ##args) ++#else ++ #define AC101_DBG(...) ++#endif ++ ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,18,0) ++#define __NO_SND_SOC_CODEC_DRV 1 ++#else ++#define __NO_SND_SOC_CODEC_DRV 0 ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) ++#if __has_attribute(__fallthrough__) ++# define fallthrough __attribute__((__fallthrough__)) ++#else ++# define fallthrough do {} while (0) /* fallthrough */ ++#endif ++#endif ++ ++#if __NO_SND_SOC_CODEC_DRV ++#define codec component ++#define snd_soc_codec snd_soc_component ++#define snd_soc_codec_driver snd_soc_component_driver ++#define snd_soc_codec_get_drvdata snd_soc_component_get_drvdata ++#define snd_soc_codec_get_dapm snd_soc_component_get_dapm ++#define snd_soc_codec_get_bias_level snd_soc_component_get_bias_level ++#define snd_soc_kcontrol_codec snd_soc_kcontrol_component ++#define snd_soc_read snd_soc_component_read32 ++#define snd_soc_register_codec snd_soc_register_component ++#define snd_soc_unregister_codec snd_soc_unregister_component ++#define snd_soc_update_bits snd_soc_component_update_bits ++#define snd_soc_write snd_soc_component_write ++#define snd_soc_add_codec_controls snd_soc_add_component_controls ++#endif ++ ++ ++#ifdef CONFIG_AC101_SWITCH_DETECT ++enum headphone_mode_u { ++ HEADPHONE_IDLE, ++ FOUR_HEADPHONE_PLUGIN, ++ THREE_HEADPHONE_PLUGIN, ++}; ++#endif ++ ++struct ac10x_priv { ++ struct i2c_client *i2c[4]; ++ struct regmap* i2cmap[4]; ++ int codec_cnt; ++ unsigned sysclk; ++#define _FREQ_24_576K 24576000 ++#define _FREQ_22_579K 22579200 ++ unsigned mclk; /* master clock or aif_clock/aclk */ ++ int clk_id; ++ unsigned char i2s_mode; ++ unsigned char data_protocol; ++ struct delayed_work dlywork; ++ int tdm_chips_cnt; ++ int sysclk_en; ++ ++ /* member for ac101 .begin */ ++ struct snd_soc_codec *codec; ++ struct i2c_client *i2c101; ++ struct regmap* regmap101; ++ ++ struct mutex dac_mutex; ++ u8 dac_enable; ++ spinlock_t lock; ++ u8 aif1_clken; ++ ++ struct work_struct codec_resume; ++ struct gpio_desc* gpiod_spk_amp_gate; ++ ++ #ifdef CONFIG_AC101_SWITCH_DETECT ++ struct gpio_desc* gpiod_irq; ++ long irq; ++ volatile int irq_cntr; ++ volatile int pullout_cntr; ++ volatile int state; ++ ++ enum headphone_mode_u mode; ++ struct work_struct work_switch; ++ struct work_struct work_clear_irq; ++ ++ struct input_dev* inpdev; ++ #endif ++ /* member for ac101 .end */ ++}; ++ ++ ++/* AC101 DAI operations */ ++int ac101_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai); ++void ac101_aif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai); ++int ac101_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt); ++int ac101_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *codec_dai); ++int ac101_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai); ++int ac101_aif_mute(struct snd_soc_dai *codec_dai, int mute); ++ ++/* codec driver specific */ ++int ac101_codec_probe(struct snd_soc_codec *codec); ++int ac101_codec_remove(struct snd_soc_codec *codec); ++int ac101_codec_suspend(struct snd_soc_codec *codec); ++int ac101_codec_resume(struct snd_soc_codec *codec); ++int ac101_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level); ++ ++/* i2c device specific */ ++int ac101_probe(struct i2c_client *i2c, const struct i2c_device_id *id); ++void ac101_shutdown(struct i2c_client *i2c); ++int ac101_remove(struct i2c_client *i2c); ++ ++int ac10x_fill_regcache(struct device* dev, struct regmap* map); ++ ++#endif//__AC10X_H__ diff --git a/target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch b/target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch new file mode 100644 index 0000000000..f2fb206947 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0080-ASoC-starfive-Add-SPDIF-and-PCM-driver.patch @@ -0,0 +1,1169 @@ +From f03b7c834baef87e4f740e10a8bbcbfc57bd985a Mon Sep 17 00:00:00 2001 +From: Xingyu Wu +Date: Thu, 15 Jun 2023 11:32:50 +0800 +Subject: [PATCH 080/116] ASoC: starfive: Add SPDIF and PCM driver + +Add SPDIF and SPDIF-PCM driver for StarFive JH7110. + +Signed-off-by: Xingyu Wu +Signed-off-by: Hal Feng +--- + sound/soc/starfive/Kconfig | 17 + + sound/soc/starfive/Makefile | 5 + + sound/soc/starfive/jh7110_spdif.c | 568 ++++++++++++++++++++++++++ + sound/soc/starfive/jh7110_spdif.h | 196 +++++++++ + sound/soc/starfive/jh7110_spdif_pcm.c | 339 +++++++++++++++ + 5 files changed, 1125 insertions(+) + create mode 100644 sound/soc/starfive/jh7110_spdif.c + create mode 100644 sound/soc/starfive/jh7110_spdif.h + create mode 100644 sound/soc/starfive/jh7110_spdif_pcm.c + +--- a/sound/soc/starfive/Kconfig ++++ b/sound/soc/starfive/Kconfig +@@ -16,6 +16,23 @@ config SND_SOC_JH7110_PWMDAC + Say Y or M if you want to add support for StarFive JH7110 + PWM-DAC driver. + ++config SND_SOC_JH7110_SPDIF ++ tristate "JH7110 SPDIF module" ++ depends on HAVE_CLK && SND_SOC_STARFIVE ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ select REGMAP_MMIO ++ help ++ Say Y or M if you want to add support for SPDIF driver of StarFive ++ JH7110 SoC. ++ ++config SND_SOC_JH7110_SPDIF_PCM ++ bool "PCM PIO extension for JH7110 SPDIF" ++ depends on SND_SOC_JH7110_SPDIF ++ default y if SND_SOC_JH7110_SPDIF ++ help ++ Say Y or N if you want to add a custom ALSA extension that registers ++ a PCM and uses PIO to transfer data. ++ + config SND_SOC_JH7110_TDM + tristate "JH7110 TDM device driver" + depends on HAVE_CLK && SND_SOC_STARFIVE +--- a/sound/soc/starfive/Makefile ++++ b/sound/soc/starfive/Makefile +@@ -1,3 +1,8 @@ + # StarFive Platform Support + obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o ++ ++obj-$(CONFIG_SND_SOC_JH7110_SPDIF) += spdif.o ++spdif-y := jh7110_spdif.o ++spdif-$(CONFIG_SND_SOC_JH7110_SPDIF_PCM) += jh7110_spdif_pcm.o ++ + obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o +--- /dev/null ++++ b/sound/soc/starfive/jh7110_spdif.c +@@ -0,0 +1,568 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * SPDIF driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "jh7110_spdif.h" ++ ++static irqreturn_t spdif_irq_handler(int irq, void *dev_id) ++{ ++ struct sf_spdif_dev *dev = dev_id; ++ bool irq_valid = false; ++ unsigned int intr; ++ unsigned int stat; ++ ++ regmap_read(dev->regmap, SPDIF_INT_REG, &intr); ++ regmap_read(dev->regmap, SPDIF_STAT_REG, &stat); ++ regmap_update_bits(dev->regmap, SPDIF_CTRL, SPDIF_MASK_ENABLE, 0); ++ regmap_update_bits(dev->regmap, SPDIF_INT_REG, SPDIF_INT_REG_BIT, 0); ++ ++ if ((stat & SPDIF_EMPTY_FLAG) || (stat & SPDIF_AEMPTY_FLAG)) { ++ sf_spdif_pcm_push_tx(dev); ++ irq_valid = true; ++ } ++ ++ if ((stat & SPDIF_FULL_FLAG) || (stat & SPDIF_AFULL_FLAG)) { ++ sf_spdif_pcm_pop_rx(dev); ++ irq_valid = true; ++ } ++ ++ if (stat & SPDIF_PARITY_FLAG) ++ irq_valid = true; ++ ++ if (stat & SPDIF_UNDERR_FLAG) ++ irq_valid = true; ++ ++ if (stat & SPDIF_OVRERR_FLAG) ++ irq_valid = true; ++ ++ if (stat & SPDIF_SYNCERR_FLAG) ++ irq_valid = true; ++ ++ if (stat & SPDIF_LOCK_FLAG) ++ irq_valid = true; ++ ++ if (stat & SPDIF_BEGIN_FLAG) ++ irq_valid = true; ++ ++ if (stat & SPDIF_RIGHT_LEFT) ++ irq_valid = true; ++ ++ regmap_update_bits(dev->regmap, SPDIF_CTRL, ++ SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE); ++ ++ if (irq_valid) ++ return IRQ_HANDLED; ++ else ++ return IRQ_NONE; ++} ++ ++static int sf_spdif_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); ++ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; ++ ++ if (tx) { ++ /* tx mode */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_TR_MODE, SPDIF_TR_MODE); ++ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_MASK_FIFO, SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK); ++ } else { ++ /* rx mode */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_TR_MODE, 0); ++ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_MASK_FIFO, SPDIF_FULL_MASK | SPDIF_AFULL_MASK); ++ } ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ /* clock recovery form the SPDIF data stream 0:clk_enable */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CLK_ENABLE, 0); ++ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_ENABLE, SPDIF_ENABLE); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ /* clock recovery form the SPDIF data stream 1:power save mode */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE); ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_ENABLE, 0); ++ break; ++ default: ++ dev_err(dai->dev, "%s L.%d cmd:%d\n", __func__, __LINE__, cmd); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int sf_spdif_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) ++{ ++ struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); ++ unsigned int channels = params_channels(params); ++ unsigned int rate = params_rate(params); ++ unsigned int format = params_format(params); ++ unsigned int tsamplerate; ++ unsigned int mclk; ++ unsigned int audio_root; ++ int ret; ++ ++ switch (channels) { ++ case 1: ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CHANNEL_MODE, SPDIF_CHANNEL_MODE); ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_DUPLICATE, SPDIF_DUPLICATE); ++ spdif->channels = false; ++ break; ++ case 2: ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CHANNEL_MODE, 0); ++ spdif->channels = true; ++ break; ++ default: ++ dev_err(dai->dev, "invalid channels number\n"); ++ return -EINVAL; ++ } ++ ++ switch (format) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ case SNDRV_PCM_FORMAT_S24_LE: ++ case SNDRV_PCM_FORMAT_S24_3LE: ++ case SNDRV_PCM_FORMAT_S32_LE: ++ break; ++ default: ++ dev_err(dai->dev, "invalid format\n"); ++ return -EINVAL; ++ } ++ ++ switch (rate) { ++ case 8000: ++ break; ++ case 11025: ++ audio_root = 148500000; ++ /* 11025 * 512 = 5644800 */ ++ /* But now pll2 is 1188m and mclk should be 5711539 closely. */ ++ mclk = 5711539; ++ break; ++ case 16000: ++ break; ++ case 22050: ++ audio_root = 148500000; ++ mclk = 11423077; ++ break; ++ default: ++ dev_err(dai->dev, "channel:%d sample rate:%d\n", channels, rate); ++ return -EINVAL; ++ } ++ ++ /* use mclk_inner clock from 1188m PLL2 will be better about 11k and 22k*/ ++ if ((rate == 11025) || (rate == 22050)) { ++ ret = clk_set_parent(spdif->mclk, spdif->mclk_inner); ++ if (ret) { ++ dev_err(dai->dev, ++ "failed to set parent to mclk_inner ret=%d\n", ret); ++ goto fail_ext; ++ } ++ ++ ret = clk_set_rate(spdif->audio_root, audio_root); ++ if (ret) { ++ dev_err(dai->dev, "failed to set audio_root rate :%d\n", ret); ++ goto fail_ext; ++ } ++ ++ ret = clk_set_rate(spdif->mclk_inner, mclk); ++ if (ret) { ++ dev_err(dai->dev, "failed to set mclk_inner rate :%d\n", ret); ++ goto fail_ext; ++ } ++ ++ mclk = clk_get_rate(spdif->mclk_inner); ++ } else { ++ ret = clk_set_parent(spdif->mclk, spdif->mclk_ext); ++ if (ret) { ++ dev_err(dai->dev, ++ "failed to set parent to mclk_ext ret=%d\n", ret); ++ goto fail_ext; ++ } ++ ++ mclk = clk_get_rate(spdif->mclk_ext); ++ } ++ ++ /* (FCLK)4096000/128=32000 */ ++ tsamplerate = (mclk / 128 + rate / 2) / rate - 1; ++ if (tsamplerate < 3) ++ tsamplerate = 3; ++ ++ /* transmission sample rate */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, 0xFF, tsamplerate); ++ ++ return 0; ++ ++fail_ext: ++ return ret; ++} ++ ++static int sf_spdif_clks_get(struct platform_device *pdev, ++ struct sf_spdif_dev *spdif) ++{ ++ static struct clk_bulk_data clks[] = { ++ { .id = "apb" }, /* clock-names in dts file */ ++ { .id = "core" }, ++ { .id = "audroot" }, ++ { .id = "mclk_inner"}, ++ { .id = "mclk_ext"}, ++ { .id = "mclk"}, ++ }; ++ int ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks); ++ ++ spdif->spdif_apb = clks[0].clk; ++ spdif->spdif_core = clks[1].clk; ++ spdif->audio_root = clks[2].clk; ++ spdif->mclk_inner = clks[3].clk; ++ spdif->mclk_ext = clks[4].clk; ++ spdif->mclk = clks[5].clk; ++ ++ return ret; ++} ++ ++static int sf_spdif_resets_get(struct platform_device *pdev, ++ struct sf_spdif_dev *spdif) ++{ ++ struct reset_control_bulk_data resets[] = { ++ { .id = "apb" }, ++ }; ++ int ret = devm_reset_control_bulk_get_exclusive(&pdev->dev, ARRAY_SIZE(resets), resets); ++ ++ if (ret) ++ return ret; ++ ++ spdif->rst_apb = resets[0].rstc; ++ ++ return 0; ++} ++ ++static int starfive_spdif_crg_enable(struct sf_spdif_dev *spdif, bool enable) ++{ ++ int ret; ++ ++ dev_dbg(spdif->dev, "starfive_spdif clk&rst %sable.\n", enable ? "en":"dis"); ++ if (enable) { ++ ret = clk_prepare_enable(spdif->spdif_apb); ++ if (ret) { ++ dev_err(spdif->dev, "failed to prepare enable spdif_apb\n"); ++ goto failed_apb_clk; ++ } ++ ++ ret = clk_prepare_enable(spdif->spdif_core); ++ if (ret) { ++ dev_err(spdif->dev, "failed to prepare enable spdif_core\n"); ++ goto failed_core_clk; ++ } ++ ++ ret = reset_control_deassert(spdif->rst_apb); ++ if (ret) { ++ dev_err(spdif->dev, "failed to deassert apb\n"); ++ goto failed_rst; ++ } ++ } else { ++ clk_disable_unprepare(spdif->spdif_core); ++ clk_disable_unprepare(spdif->spdif_apb); ++ } ++ ++ return 0; ++ ++failed_rst: ++ clk_disable_unprepare(spdif->spdif_core); ++failed_core_clk: ++ clk_disable_unprepare(spdif->spdif_apb); ++failed_apb_clk: ++ return ret; ++} ++ ++static int sf_spdif_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct sf_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); ++ ++ pm_runtime_get_sync(spdif->dev); ++ ++ /* reset */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_ENABLE | SPDIF_SFR_ENABLE | SPDIF_FIFO_ENABLE, 0); ++ ++ /* clear irq */ ++ regmap_update_bits(spdif->regmap, SPDIF_INT_REG, ++ SPDIF_INT_REG_BIT, 0); ++ ++ /* power save mode */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE); ++ ++ /* power save mode */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CLK_ENABLE, SPDIF_CLK_ENABLE); ++ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE, ++ SPDIF_PARITCHECK|SPDIF_VALIDITYCHECK|SPDIF_DUPLICATE); ++ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_SETPREAMBB, SPDIF_SETPREAMBB); ++ ++ regmap_update_bits(spdif->regmap, SPDIF_INT_REG, ++ BIT8TO20MASK<regmap, SPDIF_FIFO_CTRL, ++ ALLBITMASK, 0x20|(0x20<regmap, SPDIF_CTRL, ++ SPDIF_PARITYGEN, SPDIF_PARITYGEN); ++ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_MASK_ENABLE, SPDIF_MASK_ENABLE); ++ ++ /* APB access to FIFO enable, disable if use DMA/FIFO */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_USE_FIFO_IF, 0); ++ ++ /* two channel */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ SPDIF_CHANNEL_MODE, 0); ++ ++ pm_runtime_put_sync(spdif->dev); ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops sf_spdif_dai_ops = { ++ .probe = sf_spdif_dai_probe, ++ .trigger = sf_spdif_trigger, ++ .hw_params = sf_spdif_hw_params, ++}; ++ ++#ifdef CONFIG_PM_SLEEP ++static int spdif_system_suspend(struct device *dev) ++{ ++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev); ++ ++ /* save the register value */ ++ regmap_read(spdif->regmap, SPDIF_CTRL, &spdif->reg_spdif_ctrl); ++ regmap_read(spdif->regmap, SPDIF_INT_REG, &spdif->reg_spdif_int); ++ regmap_read(spdif->regmap, SPDIF_FIFO_CTRL, &spdif->reg_spdif_fifo_ctrl); ++ ++ return pm_runtime_force_suspend(dev); ++} ++ ++static int spdif_system_resume(struct device *dev) ++{ ++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev); ++ int ret = pm_runtime_force_resume(dev); ++ ++ if (ret) ++ return ret; ++ ++ /* restore the register value */ ++ regmap_update_bits(spdif->regmap, SPDIF_CTRL, ++ ALLBITMASK, spdif->reg_spdif_ctrl); ++ regmap_update_bits(spdif->regmap, SPDIF_INT_REG, ++ ALLBITMASK, spdif->reg_spdif_int); ++ regmap_update_bits(spdif->regmap, SPDIF_FIFO_CTRL, ++ ALLBITMASK, spdif->reg_spdif_fifo_ctrl); ++ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM ++static int spdif_runtime_suspend(struct device *dev) ++{ ++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev); ++ ++ return starfive_spdif_crg_enable(spdif, false); ++} ++ ++static int spdif_runtime_resume(struct device *dev) ++{ ++ struct sf_spdif_dev *spdif = dev_get_drvdata(dev); ++ ++ return starfive_spdif_crg_enable(spdif, true); ++} ++#endif ++ ++static const struct dev_pm_ops spdif_pm_ops = { ++ SET_RUNTIME_PM_OPS(spdif_runtime_suspend, spdif_runtime_resume, NULL) ++ SET_SYSTEM_SLEEP_PM_OPS(spdif_system_suspend, spdif_system_resume) ++}; ++ ++#define SF_PCM_RATE_44100_192000 (SNDRV_PCM_RATE_44100 | \ ++ SNDRV_PCM_RATE_48000 | \ ++ SNDRV_PCM_RATE_96000 | \ ++ SNDRV_PCM_RATE_192000) ++ ++#define SF_PCM_RATE_8000_22050 (SNDRV_PCM_RATE_8000 | \ ++ SNDRV_PCM_RATE_11025 | \ ++ SNDRV_PCM_RATE_16000 | \ ++ SNDRV_PCM_RATE_22050) ++ ++static struct snd_soc_dai_driver sf_spdif_dai = { ++ .name = "spdif", ++ .id = 0, ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SF_PCM_RATE_8000_22050, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S24_3LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ }, ++ .ops = &sf_spdif_dai_ops, ++ .symmetric_rate = 1, ++}; ++ ++static const struct snd_soc_component_driver sf_spdif_component = { ++ .name = "starfive-spdif", ++}; ++ ++static const struct regmap_config sf_spdif_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x200, ++}; ++ ++static int sf_spdif_probe(struct platform_device *pdev) ++{ ++ struct sf_spdif_dev *spdif; ++ struct resource *res; ++ void __iomem *base; ++ int ret; ++ int irq; ++ ++ spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); ++ if (!spdif) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, spdif); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ spdif->spdif_base = base; ++ spdif->regmap = devm_regmap_init_mmio(&pdev->dev, spdif->spdif_base, ++ &sf_spdif_regmap_config); ++ if (IS_ERR(spdif->regmap)) ++ return PTR_ERR(spdif->regmap); ++ ++ spdif->dev = &pdev->dev; ++ ++ ret = sf_spdif_clks_get(pdev, spdif); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to get audio clock\n"); ++ return ret; ++ } ++ ++ ret = sf_spdif_resets_get(pdev, spdif); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to get audio reset controls\n"); ++ return ret; ++ } ++ ++ ret = starfive_spdif_crg_enable(spdif, true); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable audio clock\n"); ++ return ret; ++ } ++ ++ spdif->fifo_th = 16; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq >= 0) { ++ ret = devm_request_irq(&pdev->dev, irq, spdif_irq_handler, 0, ++ pdev->name, spdif); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "failed to request irq\n"); ++ return ret; ++ } ++ } ++ ++ ret = devm_snd_soc_register_component(&pdev->dev, &sf_spdif_component, ++ &sf_spdif_dai, 1); ++ if (ret) ++ goto err_clk_disable; ++ ++ if (irq >= 0) { ++ ret = sf_spdif_pcm_register(pdev); ++ spdif->use_pio = true; ++ } else { ++ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, ++ 0); ++ spdif->use_pio = false; ++ } ++ ++ if (ret) ++ goto err_clk_disable; ++ ++ starfive_spdif_crg_enable(spdif, false); ++ pm_runtime_enable(&pdev->dev); ++ dev_dbg(&pdev->dev, "spdif register done.\n"); ++ ++ return 0; ++ ++err_clk_disable: ++ return ret; ++} ++ ++static const struct of_device_id sf_spdif_of_match[] = { ++ { .compatible = "starfive,jh7110-spdif", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, sf_spdif_of_match); ++ ++static struct platform_driver sf_spdif_driver = { ++ .driver = { ++ .name = "starfive-spdif", ++ .of_match_table = sf_spdif_of_match, ++ .pm = &spdif_pm_ops, ++ }, ++ .probe = sf_spdif_probe, ++}; ++module_platform_driver(sf_spdif_driver); ++ ++MODULE_AUTHOR("curry.zhang "); ++MODULE_AUTHOR("Xingyu Wu "); ++MODULE_DESCRIPTION("starfive SPDIF driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/sound/soc/starfive/jh7110_spdif.h +@@ -0,0 +1,196 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * SPDIF driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++#ifndef __SND_SOC_JH7110_SPDIF_H ++#define __SND_SOC_JH7110_SPDIF_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SPDIF_CTRL 0x0 ++#define SPDIF_INT_REG 0x4 ++#define SPDIF_FIFO_CTRL 0x8 ++#define SPDIF_STAT_REG 0xC ++ ++#define SPDIF_FIFO_ADDR 0x100 ++#define DMAC_SPDIF_POLLING_LEN 256 ++ ++/* ctrl: sampled on the rising clock edge */ ++#define SPDIF_TSAMPLERATE 0 /* [SRATEW-1:0] */ ++/* 0:SFR reg reset to defualt value; auto set back to '1' after reset */ ++#define SPDIF_SFR_ENABLE (1<<8) ++/* 0:reset of SPDIF block, SRF bits are unchanged; 1:enables SPDIF module */ ++#define SPDIF_ENABLE (1<<9) ++/* 0:FIFO pointers are reset to zero,threshold levels for FIFO are unchaned; auto set back to 1 */ ++#define SPDIF_FIFO_ENABLE (1<<10) ++/* 1:blocked and the modules are in power save mode; 0:block feeds the modules */ ++#define SPDIF_CLK_ENABLE (1<<11) ++#define SPDIF_TR_MODE (1<<12) /* 0:rx; 1:tx */ ++/* 0:party bit rx in a sub-frame is repeated on the parity; 1:check on a parity error */ ++#define SPDIF_PARITCHECK (1<<13) ++/* ++ * 0:parity bit from FIFO is transmitted in sub-frame; ++ * 1:parity bit generated inside the core and added to a transmitted sub-frame ++ */ ++#define SPDIF_PARITYGEN (1<<14) ++/* 0:validity bit in frame isn't checked and all frame are written; 1:validity bit rx is checked */ ++#define SPDIF_VALIDITYCHECK (1<<15) ++#define SPDIF_CHANNEL_MODE (1<<16) /* 0:two-channel; 1:single-channel */ ++/* only tx -single-channel mode; 0:secondary channel; 1: left(primary) channel */ ++#define SPDIF_DUPLICATE (1<<17) ++/* ++ * only tx; ++ * 0:first preamble B after reset tx valid sub-frame; ++ * 1:first preamble B is tx after preambleddel(INT_REG) ++ */ ++#define SPDIF_SETPREAMBB (1<<18) ++/* 0:FIFO disabled ,APB accese FIFO; 1:FIFO enable, APB access to FIFO disable; */ ++#define SPDIF_USE_FIFO_IF (1<<19) ++#define SPDIF_PARITY_MASK (1<<21) ++#define SPDIF_UNDERR_MASK (1<<22) ++#define SPDIF_OVRERR_MASK (1<<23) ++#define SPDIF_EMPTY_MASK (1<<24) ++#define SPDIF_AEMPTY_MASK (1<<25) ++#define SPDIF_FULL_MASK (1<<26) ++#define SPDIF_AFULL_MASK (1<<27) ++#define SPDIF_SYNCERR_MASK (1<<28) ++#define SPDIF_LOCK_MASK (1<<29) ++#define SPDIF_BEGIN_MASK (1<<30) ++#define SPDIF_INTEREQ_MAKS (1<<31) ++ ++#define SPDIF_MASK_ENABLE (SPDIF_PARITY_MASK | SPDIF_UNDERR_MASK | \ ++ SPDIF_OVRERR_MASK | SPDIF_EMPTY_MASK | \ ++ SPDIF_AEMPTY_MASK | SPDIF_FULL_MASK | \ ++ SPDIF_AFULL_MASK | SPDIF_SYNCERR_MASK | \ ++ SPDIF_LOCK_MASK | SPDIF_BEGIN_MASK | \ ++ SPDIF_INTEREQ_MAKS) ++ ++#define SPDIF_MASK_FIFO (SPDIF_EMPTY_MASK | SPDIF_AEMPTY_MASK | \ ++ SPDIF_FULL_MASK | SPDIF_AFULL_MASK) ++ ++/* INT_REG */ ++#define SPDIF_RSAMPLERATE 0 /* [SRATEW-1:0] */ ++#define SPDIF_PREAMBLEDEL 8 /* [PDELAYW+7:8] first B delay */ ++#define SPDIF_PARITYO (1<<21) /* 0:clear parity error */ ++#define SPDIF_TDATA_UNDERR (1<<22) /* tx data underrun error;0:clear */ ++#define SPDIF_RDATA_OVRERR (1<<23) /* rx data overrun error; 0:clear */ ++#define SPDIF_FIFO_EMPTY (1<<24) /* empty; 0:clear */ ++#define SPDIF_FIOF_AEMPTY (1<<25) /* almost empty; 0:clear */ ++#define SPDIF_FIFO_FULL (1<<26) /* FIFO full; 0:clear */ ++#define SPDIF_FIFO_AFULL (1<<27) /* FIFO almost full; 0:clear */ ++#define SPDIF_SYNCERR (1<<28) /* sync error; 0:clear */ ++#define SPDIF_LOCK (1<<29) /* sync; 0:clear */ ++#define SPDIF_BLOCK_BEGIN (1<<30) /* new start block rx data */ ++ ++#define SPDIF_INT_REG_BIT (SPDIF_PARITYO | SPDIF_TDATA_UNDERR | \ ++ SPDIF_RDATA_OVRERR | SPDIF_FIFO_EMPTY | \ ++ SPDIF_FIOF_AEMPTY | SPDIF_FIFO_FULL | \ ++ SPDIF_FIFO_AFULL | SPDIF_SYNCERR | \ ++ SPDIF_LOCK | SPDIF_BLOCK_BEGIN) ++ ++#define SPDIF_ERROR_INT_STATUS (SPDIF_PARITYO | \ ++ SPDIF_TDATA_UNDERR | SPDIF_RDATA_OVRERR) ++#define SPDIF_FIFO_INT_STATUS (SPDIF_FIFO_EMPTY | SPDIF_FIOF_AEMPTY | \ ++ SPDIF_FIFO_FULL | SPDIF_FIFO_AFULL) ++ ++#define SPDIF_INT_PARITY_ERROR (-1) ++#define SPDIF_INT_TDATA_UNDERR (-2) ++#define SPDIF_INT_RDATA_OVRERR (-3) ++#define SPDIF_INT_FIFO_EMPTY 1 ++#define SPDIF_INT_FIFO_AEMPTY 2 ++#define SPDIF_INT_FIFO_FULL 3 ++#define SPDIF_INT_FIFO_AFULL 4 ++#define SPDIF_INT_SYNCERR (-4) ++#define SPDIF_INT_LOCK 5 /* reciever has become synchronized with input data stream */ ++#define SPDIF_INT_BLOCK_BEGIN 6 /* start a new block in recieve data, written into FIFO */ ++ ++/* FIFO_CTRL */ ++#define SPDIF_AEMPTY_THRESHOLD 0 /* [depth-1:0] */ ++#define SPDIF_AFULL_THRESHOLD 16 /* [depth+15:16] */ ++ ++/* STAT_REG */ ++#define SPDIF_FIFO_LEVEL (1<<0) ++#define SPDIF_PARITY_FLAG (1<<21) /* 1:error; 0:repeated */ ++#define SPDIF_UNDERR_FLAG (1<<22) /* 1:error */ ++#define SPDIF_OVRERR_FLAG (1<<23) /* 1:error */ ++#define SPDIF_EMPTY_FLAG (1<<24) /* 1:fifo empty */ ++#define SPDIF_AEMPTY_FLAG (1<<25) /* 1:fifo almost empty */ ++#define SPDIF_FULL_FLAG (1<<26) /* 1:fifo full */ ++#define SPDIF_AFULL_FLAG (1<<27) /* 1:fifo almost full */ ++#define SPDIF_SYNCERR_FLAG (1<<28) /* 1:rx sync error */ ++#define SPDIF_LOCK_FLAG (1<<29) /* 1:RX sync */ ++#define SPDIF_BEGIN_FLAG (1<<30) /* 1:start a new block */ ++/* 1:left channel received and tx into FIFO; 0:right channel received and tx into FIFO */ ++#define SPDIF_RIGHT_LEFT (1<<31) ++ ++#define BIT8TO20MASK 0x1FFF ++#define ALLBITMASK 0xFFFFFFFF ++ ++#define SPDIF_STAT (SPDIF_PARITY_FLAG | SPDIF_UNDERR_FLAG | \ ++ SPDIF_OVRERR_FLAG | SPDIF_EMPTY_FLAG | \ ++ SPDIF_AEMPTY_FLAG | SPDIF_FULL_FLAG | \ ++ SPDIF_AFULL_FLAG | SPDIF_SYNCERR_FLAG | \ ++ SPDIF_LOCK_FLAG | SPDIF_BEGIN_FLAG | \ ++ SPDIF_RIGHT_LEFT) ++struct sf_spdif_dev { ++ void __iomem *spdif_base; ++ struct regmap *regmap; ++ struct device *dev; ++ u32 fifo_th; ++ int active; ++ ++ /* data related to DMA transfers b/w i2s and DMAC */ ++ struct snd_dmaengine_dai_dma_data play_dma_data; ++ struct snd_dmaengine_dai_dma_data capture_dma_data; ++ ++ bool use_pio; ++ struct snd_pcm_substream __rcu *tx_substream; ++ struct snd_pcm_substream __rcu *rx_substream; ++ ++ unsigned int (*tx_fn)(struct sf_spdif_dev *dev, ++ struct snd_pcm_runtime *runtime, unsigned int tx_ptr, ++ bool *period_elapsed, snd_pcm_format_t format); ++ unsigned int (*rx_fn)(struct sf_spdif_dev *dev, ++ struct snd_pcm_runtime *runtime, unsigned int rx_ptr, ++ bool *period_elapsed, snd_pcm_format_t format); ++ ++ snd_pcm_format_t format; ++ bool channels; ++ unsigned int tx_ptr; ++ unsigned int rx_ptr; ++ struct clk *spdif_apb; ++ struct clk *spdif_core; ++ struct clk *audio_root; ++ struct clk *mclk_inner; ++ struct clk *mclk; ++ struct clk *mclk_ext; ++ struct reset_control *rst_apb; ++ unsigned int reg_spdif_ctrl; ++ unsigned int reg_spdif_int; ++ unsigned int reg_spdif_fifo_ctrl; ++ ++ struct snd_dmaengine_dai_dma_data dma_data; ++}; ++ ++#if IS_ENABLED(CONFIG_SND_SOC_JH7110_SPDIF_PCM) ++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev); ++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev); ++int sf_spdif_pcm_register(struct platform_device *pdev); ++#else ++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev) { } ++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev) { } ++int sf_spdif_pcm_register(struct platform_device *pdev) ++{ ++ return -EINVAL; ++} ++#endif ++ ++#endif /* __SND_SOC_JH7110_SPDIF_H */ +--- /dev/null ++++ b/sound/soc/starfive/jh7110_spdif_pcm.c +@@ -0,0 +1,339 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * SPDIF PCM driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "jh7110_spdif.h" ++ ++#define BUFFER_BYTES_MAX (3 * 2 * 8 * PERIOD_BYTES_MIN) ++#define PERIOD_BYTES_MIN 4096 ++#define PERIODS_MIN 2 ++ ++static unsigned int sf_spdif_pcm_tx(struct sf_spdif_dev *dev, ++ struct snd_pcm_runtime *runtime, unsigned int tx_ptr, ++ bool *period_elapsed, snd_pcm_format_t format) ++{ ++ unsigned int period_pos = tx_ptr % runtime->period_size; ++ u32 data[2]; ++ int i; ++ ++ /* two- channel and signal-channel mode */ ++ if (dev->channels) { ++ const u16 (*p16)[2] = (void *)runtime->dma_area; ++ const u32 (*p32)[2] = (void *)runtime->dma_area; ++ ++ for (i = 0; i < dev->fifo_th; i++) { ++ if (format == SNDRV_PCM_FORMAT_S16_LE) { ++ data[0] = p16[tx_ptr][0]; ++ data[0] = data[0]<<8; ++ data[0] &= 0x00ffff00; ++ data[1] = p16[tx_ptr][1]; ++ data[1] = data[1]<<8; ++ data[1] &= 0x00ffff00; ++ } else if (format == SNDRV_PCM_FORMAT_S24_LE) { ++ data[0] = p32[tx_ptr][0]; ++ data[1] = p32[tx_ptr][1]; ++ ++ /* ++ * To adapt S24_3LE and ALSA pass parameter of S24_LE. ++ * operation of S24_LE should be same to S24_3LE. ++ * So it would wrong when playback S24_LE file. ++ * when want to playback S24_LE file, should add in there: ++ * data[0] = data[0]>>8; ++ * data[1] = data[1]>>8; ++ */ ++ ++ data[0] &= 0x00ffffff; ++ data[1] &= 0x00ffffff; ++ } else if (format == SNDRV_PCM_FORMAT_S24_3LE) { ++ data[0] = p32[tx_ptr][0]; ++ data[1] = p32[tx_ptr][1]; ++ data[0] &= 0x00ffffff; ++ data[1] &= 0x00ffffff; ++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) { ++ data[0] = p32[tx_ptr][0]; ++ data[0] = data[0]>>8; ++ data[1] = p32[tx_ptr][1]; ++ data[1] = data[1]>>8; ++ } ++ ++ iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR); ++ iowrite32(data[1], dev->spdif_base + SPDIF_FIFO_ADDR); ++ period_pos++; ++ if (++tx_ptr >= runtime->buffer_size) ++ tx_ptr = 0; ++ } ++ } else { ++ const u16 (*p16) = (void *)runtime->dma_area; ++ const u32 (*p32) = (void *)runtime->dma_area; ++ ++ for (i = 0; i < dev->fifo_th; i++) { ++ if (format == SNDRV_PCM_FORMAT_S16_LE) { ++ data[0] = p16[tx_ptr]; ++ data[0] = data[0]<<8; ++ data[0] &= 0x00ffff00; ++ } else if (format == SNDRV_PCM_FORMAT_S24_LE || ++ format == SNDRV_PCM_FORMAT_S24_3LE) { ++ data[0] = p32[tx_ptr]; ++ data[0] &= 0x00ffffff; ++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) { ++ data[0] = p32[tx_ptr]; ++ data[0] = data[0]>>8; ++ } ++ ++ iowrite32(data[0], dev->spdif_base + SPDIF_FIFO_ADDR); ++ period_pos++; ++ if (++tx_ptr >= runtime->buffer_size) ++ tx_ptr = 0; ++ } ++ } ++ ++ *period_elapsed = period_pos >= runtime->period_size; ++ return tx_ptr; ++} ++ ++static unsigned int sf_spdif_pcm_rx(struct sf_spdif_dev *dev, ++ struct snd_pcm_runtime *runtime, unsigned int rx_ptr, ++ bool *period_elapsed, snd_pcm_format_t format) ++{ ++ u16 (*p16)[2] = (void *)runtime->dma_area; ++ u32 (*p32)[2] = (void *)runtime->dma_area; ++ unsigned int period_pos = rx_ptr % runtime->period_size; ++ u32 data[2]; ++ int i; ++ ++ for (i = 0; i < dev->fifo_th; i++) { ++ data[0] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR); ++ data[1] = ioread32(dev->spdif_base + SPDIF_FIFO_ADDR); ++ if (format == SNDRV_PCM_FORMAT_S16_LE) { ++ p16[rx_ptr][0] = data[0]>>8; ++ p16[rx_ptr][1] = data[1]>>8; ++ } else if (format == SNDRV_PCM_FORMAT_S24_LE) { ++ p32[rx_ptr][0] = data[0]; ++ p32[rx_ptr][1] = data[1]; ++ } else if (format == SNDRV_PCM_FORMAT_S32_LE) { ++ p32[rx_ptr][0] = data[0]<<8; ++ p32[rx_ptr][1] = data[1]<<8; ++ } ++ ++ period_pos++; ++ if (++rx_ptr >= runtime->buffer_size) ++ rx_ptr = 0; ++ } ++ ++ *period_elapsed = period_pos >= runtime->period_size; ++ return rx_ptr; ++} ++ ++static const struct snd_pcm_hardware sf_pcm_hardware = { ++ .info = SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_PAUSE | ++ SNDRV_PCM_INFO_RESUME, ++ .rates = SNDRV_PCM_RATE_8000 | ++ SNDRV_PCM_RATE_11025 | ++ SNDRV_PCM_RATE_16000 | ++ SNDRV_PCM_RATE_22050 | ++ SNDRV_PCM_RATE_32000 | ++ SNDRV_PCM_RATE_44100 | ++ SNDRV_PCM_RATE_48000, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S24_3LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ .channels_min = 1, ++ .channels_max = 2, ++ .buffer_bytes_max = BUFFER_BYTES_MAX, ++ .period_bytes_min = PERIOD_BYTES_MIN, ++ .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, ++ .periods_min = PERIODS_MIN, ++ .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, ++ .fifo_size = 16, ++}; ++ ++static void sf_spdif_pcm_transfer(struct sf_spdif_dev *dev, bool push) ++{ ++ struct snd_pcm_substream *substream; ++ bool active, period_elapsed; ++ ++ rcu_read_lock(); ++ if (push) ++ substream = rcu_dereference(dev->tx_substream); ++ else ++ substream = rcu_dereference(dev->rx_substream); ++ ++ active = substream && snd_pcm_running(substream); ++ if (active) { ++ unsigned int ptr; ++ unsigned int new_ptr; ++ ++ if (push) { ++ ptr = READ_ONCE(dev->tx_ptr); ++ new_ptr = dev->tx_fn(dev, substream->runtime, ptr, ++ &period_elapsed, dev->format); ++ cmpxchg(&dev->tx_ptr, ptr, new_ptr); ++ } else { ++ ptr = READ_ONCE(dev->rx_ptr); ++ new_ptr = dev->rx_fn(dev, substream->runtime, ptr, ++ &period_elapsed, dev->format); ++ cmpxchg(&dev->rx_ptr, ptr, new_ptr); ++ } ++ ++ if (period_elapsed) ++ snd_pcm_period_elapsed(substream); ++ } ++ rcu_read_unlock(); ++} ++ ++void sf_spdif_pcm_push_tx(struct sf_spdif_dev *dev) ++{ ++ sf_spdif_pcm_transfer(dev, true); ++} ++ ++void sf_spdif_pcm_pop_rx(struct sf_spdif_dev *dev) ++{ ++ sf_spdif_pcm_transfer(dev, false); ++} ++ ++static int sf_pcm_open(struct snd_soc_component *component, ++ struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); ++ struct sf_spdif_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); ++ ++ snd_soc_set_runtime_hwparams(substream, &sf_pcm_hardware); ++ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); ++ runtime->private_data = dev; ++ ++ return 0; ++} ++ ++static int sf_pcm_close(struct snd_soc_component *component, ++ struct snd_pcm_substream *substream) ++{ ++ synchronize_rcu(); ++ return 0; ++} ++ ++static int sf_pcm_hw_params(struct snd_soc_component *component, ++ struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct sf_spdif_dev *dev = runtime->private_data; ++ ++ switch (params_channels(hw_params)) { ++ case 1: ++ case 2: ++ break; ++ default: ++ dev_err(dev->dev, "invalid channels number\n"); ++ return -EINVAL; ++ } ++ ++ dev->format = params_format(hw_params); ++ switch (dev->format) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ case SNDRV_PCM_FORMAT_S24_LE: ++ case SNDRV_PCM_FORMAT_S24_3LE: ++ case SNDRV_PCM_FORMAT_S32_LE: ++ break; ++ default: ++ dev_err(dev->dev, "invalid format\n"); ++ return -EINVAL; ++ } ++ ++ dev->tx_fn = sf_spdif_pcm_tx; ++ dev->rx_fn = sf_spdif_pcm_rx; ++ ++ return 0; ++} ++ ++static int sf_pcm_trigger(struct snd_soc_component *component, ++ struct snd_pcm_substream *substream, int cmd) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct sf_spdif_dev *dev = runtime->private_data; ++ int ret = 0; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ WRITE_ONCE(dev->tx_ptr, 0); ++ rcu_assign_pointer(dev->tx_substream, substream); ++ } else { ++ WRITE_ONCE(dev->rx_ptr, 0); ++ rcu_assign_pointer(dev->rx_substream, substream); ++ } ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ rcu_assign_pointer(dev->tx_substream, NULL); ++ else ++ rcu_assign_pointer(dev->rx_substream, NULL); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ return ret; ++} ++ ++static snd_pcm_uframes_t sf_pcm_pointer(struct snd_soc_component *component, ++ struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct sf_spdif_dev *dev = runtime->private_data; ++ snd_pcm_uframes_t pos; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ++ pos = READ_ONCE(dev->tx_ptr); ++ else ++ pos = READ_ONCE(dev->rx_ptr); ++ ++ return pos < runtime->buffer_size ? pos : 0; ++} ++ ++static int sf_pcm_new(struct snd_soc_component *component, ++ struct snd_soc_pcm_runtime *rtd) ++{ ++ size_t size = sf_pcm_hardware.buffer_bytes_max; ++ ++ snd_pcm_set_managed_buffer_all(rtd->pcm, ++ SNDRV_DMA_TYPE_CONTINUOUS, ++ NULL, size, size); ++ ++ return 0; ++} ++ ++static const struct snd_soc_component_driver sf_pcm_component = { ++ .open = sf_pcm_open, ++ .close = sf_pcm_close, ++ .hw_params = sf_pcm_hw_params, ++ .trigger = sf_pcm_trigger, ++ .pointer = sf_pcm_pointer, ++ .pcm_construct = sf_pcm_new, ++}; ++ ++int sf_spdif_pcm_register(struct platform_device *pdev) ++{ ++ return devm_snd_soc_register_component(&pdev->dev, &sf_pcm_component, ++ NULL, 0); ++} diff --git a/target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch b/target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch new file mode 100644 index 0000000000..006760d754 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0081-ASoC-starfive-Add-JH7110-PDM-driver.patch @@ -0,0 +1,537 @@ +From 9c4858f9fe4d8f8fe5cf347b3ca727016b7ba492 Mon Sep 17 00:00:00 2001 +From: Walker Chen +Date: Tue, 20 Jun 2023 15:57:53 +0800 +Subject: [PATCH 081/116] ASoC: starfive: Add JH7110 PDM driver + +Add pdm driver support for the StarFive JH7110 SoC. + +Signed-off-by: Walker Chen +--- + sound/soc/starfive/Kconfig | 8 + + sound/soc/starfive/Makefile | 2 + + sound/soc/starfive/jh7110_pdm.c | 493 ++++++++++++++++++++++++++++++++ + 3 files changed, 503 insertions(+) + create mode 100644 sound/soc/starfive/jh7110_pdm.c + +--- a/sound/soc/starfive/Kconfig ++++ b/sound/soc/starfive/Kconfig +@@ -7,6 +7,14 @@ config SND_SOC_STARFIVE + the Starfive SoCs' Audio interfaces. You will also need to + select the audio interfaces to support below. + ++config SND_SOC_JH7110_PDM ++ tristate "JH7110 PDM device driver" ++ depends on HAVE_CLK && SND_SOC_STARFIVE ++ select SND_SOC_JH7110_I2S ++ select REGMAP_MMIO ++ help ++ Say Y or M if you want to add support for StarFive pdm driver. ++ + config SND_SOC_JH7110_PWMDAC + tristate "JH7110 PWM-DAC device driver" + depends on HAVE_CLK && SND_SOC_STARFIVE +--- a/sound/soc/starfive/Makefile ++++ b/sound/soc/starfive/Makefile +@@ -1,4 +1,6 @@ + # StarFive Platform Support ++obj-$(CONFIG_SND_SOC_JH7110_PDM) += jh7110_pdm.o ++ + obj-$(CONFIG_SND_SOC_JH7110_PWMDAC) += jh7110_pwmdac.o + + obj-$(CONFIG_SND_SOC_JH7110_SPDIF) += spdif.o +--- /dev/null ++++ b/sound/soc/starfive/jh7110_pdm.c +@@ -0,0 +1,493 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * PDM driver for the StarFive JH7110 SoC ++ * ++ * Copyright (C) 2022 StarFive Technology Co., Ltd. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PDM_DMIC_CTRL0 0x00 ++#define PDM_DC_SCALE0 0x04 ++#define PDM_DMIC_CTRL1 0x10 ++#define PDM_DC_SCALE1 0x14 ++ ++/* PDM CTRL OFFSET */ ++#define PDM_DMIC_MSB_SHIFT 1 ++#define PDM_DMIC_MSB_MASK (0x7 << PDM_DMIC_MSB_SHIFT) ++#define PDM_DMIC_VOL_SHIFT 16 ++#define PDM_DMIC_VOL_MASK (0x3f << PDM_DMIC_VOL_SHIFT) ++#define PDM_VOL_DB_MUTE (0x3f << PDM_DMIC_VOL_SHIFT) ++#define PDM_VOL_DB_MAX 0 ++ ++#define PDM_DMIC_RVOL_MASK BIT(22) ++#define PDM_DMIC_LVOL_MASK BIT(23) ++#define PDM_DMIC_I2S_SLAVE BIT(24) ++#define PDM_DMIC_HPF_EN BIT(28) ++#define PDM_DMIC_FASTMODE_MASK BIT(29) ++#define PDM_DMIC_DC_BYPASS_MASK BIT(30) ++#define PDM_SW_RST_MASK BIT(31) ++#define PDM_SW_RST_RELEASE BIT(31) ++ ++/* PDM SCALE OFFSET */ ++#define DMIC_DCOFF3_SHIFT 24 ++#define DMIC_DCOFF2_SHIFT 16 ++#define DMIC_DCOFF1_SHIFT 8 ++ ++#define DMIC_DCOFF3_MASK (0xf << DMIC_DCOFF3_SHIFT) ++#define DMIC_DCOFF3_VAL (0xc << DMIC_DCOFF3_SHIFT) ++#define DMIC_DCOFF1_MASK (0xff << DMIC_DCOFF1_SHIFT) ++#define DMIC_DCOFF1_VAL (0x5 << DMIC_DCOFF1_SHIFT) ++#define DMIC_SCALE_MASK 0x3f ++#define DMIC_SCALE_DEF_VAL 0x8 ++ ++enum PDM_MSB_SHIFT { ++ PDM_MSB_SHIFT_NONE = 0, ++ PDM_MSB_SHIFT_1, ++ PDM_MSB_SHIFT_2, ++ PDM_MSB_SHIFT_3, ++ PDM_MSB_SHIFT_4, ++ PDM_MSB_SHIFT_5, ++ PDM_MSB_SHIFT_6, ++ PDM_MSB_SHIFT_7, ++}; ++ ++struct sf_pdm { ++ struct regmap *pdm_map; ++ struct device *dev; ++ struct clk *clk_pdm_apb; ++ struct clk *clk_pdm_mclk; ++ struct clk *clk_mclk; ++ struct clk *clk_mclk_ext; ++ struct reset_control *rst_pdm_dmic; ++ struct reset_control *rst_pdm_apb; ++ unsigned char flag_first; ++ unsigned int saved_ctrl0; ++ unsigned int saved_scale0; ++}; ++ ++static const DECLARE_TLV_DB_SCALE(volume_tlv, -9450, 150, 0); ++ ++static const struct snd_kcontrol_new sf_pdm_snd_controls[] = { ++ SOC_SINGLE("DC compensation Control", PDM_DMIC_CTRL0, 30, 1, 0), ++ SOC_SINGLE("High Pass Filter Control", PDM_DMIC_CTRL0, 28, 1, 0), ++ SOC_SINGLE("Left Channel Volume Control", PDM_DMIC_CTRL0, 23, 1, 0), ++ SOC_SINGLE("Right Channel Volume Control", PDM_DMIC_CTRL0, 22, 1, 0), ++ SOC_SINGLE_TLV("Volume", PDM_DMIC_CTRL0, 16, 0x3F, 1, volume_tlv), ++ SOC_SINGLE("Data MSB Shift", PDM_DMIC_CTRL0, 1, 7, 0), ++ SOC_SINGLE("SCALE", PDM_DC_SCALE0, 0, 0x3F, 0), ++ SOC_SINGLE("DC offset", PDM_DC_SCALE0, 8, 0xFFFFF, 0), ++}; ++ ++static void sf_pdm_enable(struct regmap *map) ++{ ++ /* Left and Right Channel Volume Control Enable */ ++ regmap_update_bits(map, PDM_DMIC_CTRL0, PDM_DMIC_RVOL_MASK, 0); ++ regmap_update_bits(map, PDM_DMIC_CTRL0, PDM_DMIC_LVOL_MASK, 0); ++} ++ ++static void sf_pdm_disable(struct regmap *map) ++{ ++ /* Left and Right Channel Volume Control Disable */ ++ regmap_update_bits(map, PDM_DMIC_CTRL0, ++ PDM_DMIC_RVOL_MASK, PDM_DMIC_RVOL_MASK); ++ regmap_update_bits(map, PDM_DMIC_CTRL0, ++ PDM_DMIC_LVOL_MASK, PDM_DMIC_LVOL_MASK); ++} ++ ++static int sf_pdm_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ if (priv->flag_first) { ++ priv->flag_first = 0; ++ mdelay(200); ++ } ++ ++ sf_pdm_enable(priv->pdm_map); ++ return 0; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ sf_pdm_disable(priv->pdm_map); ++ return 0; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int sf_pdm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct sf_pdm *priv = snd_soc_dai_get_drvdata(dai); ++ unsigned int sample_rate; ++ unsigned int data_width; ++ int ret; ++ const int pdm_mul = 128; ++ ++ sample_rate = params_rate(params); ++ switch (sample_rate) { ++ case 8000: ++ case 11025: ++ case 16000: ++ break; ++ default: ++ dev_err(priv->dev, "can't support sample rate:%d\n", sample_rate); ++ return -EINVAL; ++ } ++ ++ data_width = params_width(params); ++ switch (data_width) { ++ case 16: ++ case 32: ++ break; ++ default: ++ dev_err(priv->dev, "can't support bit width %d\n", data_width); ++ return -EINVAL; ++ } ++ ++ /* set pdm_mclk, PDM MCLK = 128 * LRCLK */ ++ ret = clk_set_rate(priv->clk_pdm_mclk, pdm_mul * sample_rate); ++ if (ret) { ++ dev_err(priv->dev, "Can't set pdm_mclk: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static const struct snd_soc_dai_ops sf_pdm_dai_ops = { ++ .trigger = sf_pdm_trigger, ++ .hw_params = sf_pdm_hw_params, ++}; ++ ++static void sf_pdm_module_init(struct sf_pdm *priv) ++{ ++ /* Reset */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_SW_RST_MASK, 0x00); ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_SW_RST_MASK, PDM_SW_RST_RELEASE); ++ ++ /* Make sure the device is initially disabled */ ++ sf_pdm_disable(priv->pdm_map); ++ ++ /* MUTE */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_VOL_MASK, PDM_VOL_DB_MUTE); ++ ++ /* UNMUTE */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_VOL_MASK, PDM_VOL_DB_MAX); ++ ++ /* enable high pass filter */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_HPF_EN, PDM_DMIC_HPF_EN); ++ ++ /* PDM work as slave mode */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_I2S_SLAVE, PDM_DMIC_I2S_SLAVE); ++ ++ /* disable fast mode */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_FASTMODE_MASK, 0); ++ ++ /* dmic msb shift 0 */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_MSB_MASK, 0); ++ ++ /* scale: 0x8 */ ++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, ++ DMIC_SCALE_MASK, DMIC_SCALE_DEF_VAL); ++ ++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, ++ DMIC_DCOFF1_MASK, DMIC_DCOFF1_VAL); ++ ++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, ++ DMIC_DCOFF3_MASK, DMIC_DCOFF3_VAL); ++ ++ /* scale: 0x3f */ ++ regmap_update_bits(priv->pdm_map, PDM_DC_SCALE0, ++ DMIC_SCALE_MASK, DMIC_SCALE_MASK); ++ ++ /* dmic msb shift 2 */ ++ regmap_update_bits(priv->pdm_map, PDM_DMIC_CTRL0, ++ PDM_DMIC_MSB_MASK, PDM_MSB_SHIFT_4); ++} ++ ++#define SF_PDM_RATES (SNDRV_PCM_RATE_8000 | \ ++ SNDRV_PCM_RATE_11025 | \ ++ SNDRV_PCM_RATE_16000) ++ ++#define SF_PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_S32_LE) ++ ++static struct snd_soc_dai_driver sf_pdm_dai_drv = { ++ .name = "PDM", ++ .id = 0, ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SF_PDM_RATES, ++ .formats = SF_PDM_FORMATS, ++ }, ++ .ops = &sf_pdm_dai_ops, ++ .symmetric_rate = 1, ++}; ++ ++static int sf_pdm_component_probe(struct snd_soc_component *component) ++{ ++ struct sf_pdm *priv = snd_soc_component_get_drvdata(component); ++ ++ snd_soc_component_init_regmap(component, priv->pdm_map); ++ snd_soc_add_component_controls(component, sf_pdm_snd_controls, ++ ARRAY_SIZE(sf_pdm_snd_controls)); ++ ++ return 0; ++} ++ ++static int sf_pdm_clock_enable(struct sf_pdm *priv) ++{ ++ int ret; ++ ++ ret = clk_prepare_enable(priv->clk_pdm_mclk); ++ if (ret) { ++ dev_err(priv->dev, "failed to prepare enable clk_pdm_mclk\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(priv->clk_pdm_apb); ++ if (ret) { ++ dev_err(priv->dev, "failed to prepare enable clk_pdm_apb\n"); ++ goto disable_pdm_mclk; ++ } ++ ++ ret = reset_control_deassert(priv->rst_pdm_dmic); ++ if (ret) { ++ dev_err(priv->dev, "failed to deassert pdm_dmic\n"); ++ goto disable_pdm_apb; ++ } ++ ++ ret = reset_control_deassert(priv->rst_pdm_apb); ++ if (ret) { ++ dev_err(priv->dev, "failed to deassert pdm_apb\n"); ++ goto disable_pdm_apb; ++ } ++ ++ ret = clk_set_parent(priv->clk_mclk, priv->clk_mclk_ext); ++ if (ret) { ++ dev_err(priv->dev, "failed to set parent clk_mclk ret=%d\n", ret); ++ goto disable_pdm_apb; ++ } ++ ++ return 0; ++ ++disable_pdm_apb: ++ clk_disable_unprepare(priv->clk_pdm_apb); ++disable_pdm_mclk: ++ clk_disable_unprepare(priv->clk_pdm_mclk); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_PM ++static int sf_pdm_runtime_suspend(struct device *dev) ++{ ++ struct sf_pdm *priv = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(priv->clk_pdm_apb); ++ clk_disable_unprepare(priv->clk_pdm_mclk); ++ ++ return 0; ++} ++ ++static int sf_pdm_runtime_resume(struct device *dev) ++{ ++ struct sf_pdm *priv = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = sf_pdm_clock_enable(priv); ++ if (!ret) ++ sf_pdm_module_init(priv); ++ ++ return ret; ++} ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int sf_pdm_suspend(struct snd_soc_component *component) ++{ ++ return pm_runtime_force_suspend(component->dev); ++} ++ ++static int sf_pdm_resume(struct snd_soc_component *component) ++{ ++ return pm_runtime_force_resume(component->dev); ++} ++ ++#else ++#define sf_pdm_suspend NULL ++#define sf_pdm_resume NULL ++#endif ++ ++static const struct snd_soc_component_driver sf_pdm_component_drv = { ++ .name = "jh7110-pdm", ++ .probe = sf_pdm_component_probe, ++ .suspend = sf_pdm_suspend, ++ .resume = sf_pdm_resume, ++}; ++ ++static const struct regmap_config sf_pdm_regmap_cfg = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x20, ++}; ++ ++static int sf_pdm_clock_get(struct platform_device *pdev, struct sf_pdm *priv) ++{ ++ int ret; ++ ++ static struct clk_bulk_data clks[] = { ++ { .id = "pdm_mclk" }, ++ { .id = "pdm_apb" }, ++ { .id = "clk_mclk" }, ++ { .id = "mclk_ext" }, ++ }; ++ ++ ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(clks), clks); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to get pdm clocks\n"); ++ goto exit; ++ } ++ ++ priv->clk_pdm_mclk = clks[0].clk; ++ priv->clk_pdm_apb = clks[1].clk; ++ priv->clk_mclk = clks[2].clk; ++ priv->clk_mclk_ext = clks[3].clk; ++ ++ priv->rst_pdm_dmic = devm_reset_control_get_exclusive(&pdev->dev, "pdm_dmic"); ++ if (IS_ERR(priv->rst_pdm_dmic)) { ++ dev_err(&pdev->dev, "failed to get pdm_dmic reset control\n"); ++ ret = PTR_ERR(priv->rst_pdm_dmic); ++ goto exit; ++ } ++ ++ priv->rst_pdm_apb = devm_reset_control_get_exclusive(&pdev->dev, "pdm_apb"); ++ if (IS_ERR(priv->rst_pdm_apb)) { ++ dev_err(&pdev->dev, "failed to get pdm_apb reset control\n"); ++ ret = PTR_ERR(priv->rst_pdm_apb); ++ goto exit; ++ } ++ ++ /* ++ * pdm clock must always be enabled as hardware issue that ++ * no data in the first 4 seconds of the first recording ++ */ ++ ret = sf_pdm_clock_enable(priv); ++ ++exit: ++ return ret; ++} ++ ++static int sf_pdm_probe(struct platform_device *pdev) ++{ ++ struct sf_pdm *priv; ++ struct resource *res; ++ void __iomem *regs; ++ int ret; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ platform_set_drvdata(pdev, priv); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pdm"); ++ regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ priv->pdm_map = devm_regmap_init_mmio(&pdev->dev, regs, &sf_pdm_regmap_cfg); ++ if (IS_ERR(priv->pdm_map)) { ++ dev_err(&pdev->dev, "failed to init regmap: %ld\n", ++ PTR_ERR(priv->pdm_map)); ++ return PTR_ERR(priv->pdm_map); ++ } ++ ++ priv->dev = &pdev->dev; ++ priv->flag_first = 1; ++ ++ ret = sf_pdm_clock_get(pdev, priv); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable audio-pdm clock\n"); ++ return ret; ++ } ++ ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ ret = devm_snd_soc_register_component(&pdev->dev, &sf_pdm_component_drv, ++ &sf_pdm_dai_drv, 1); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to register pdm dai\n"); ++ return ret; ++ } ++ pm_runtime_enable(&pdev->dev); ++ ++ return 0; ++} ++ ++static int sf_pdm_dev_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ return 0; ++} ++ ++static const struct of_device_id sf_pdm_of_match[] = { ++ {.compatible = "starfive,jh7110-pdm",}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sf_pdm_of_match); ++ ++static const struct dev_pm_ops sf_pdm_pm_ops = { ++ SET_RUNTIME_PM_OPS(sf_pdm_runtime_suspend, ++ sf_pdm_runtime_resume, NULL) ++}; ++ ++static struct platform_driver sf_pdm_driver = { ++ .driver = { ++ .name = "jh7110-pdm", ++ .of_match_table = sf_pdm_of_match, ++ .pm = &sf_pdm_pm_ops, ++ }, ++ .probe = sf_pdm_probe, ++ .remove = sf_pdm_dev_remove, ++}; ++module_platform_driver(sf_pdm_driver); ++ ++MODULE_AUTHOR("Walker Chen "); ++MODULE_DESCRIPTION("Starfive PDM Controller Driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch b/target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch new file mode 100644 index 0000000000..ed8a3af308 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0082-dt-binding-input-Add-tink_ft5406.patch @@ -0,0 +1,55 @@ +From 59cbdfeee0fc1ad382a0bc8f7fa897a9f5d03df0 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Fri, 9 Jun 2023 16:54:36 +0800 +Subject: [PATCH 082/116] dt-binding: input: Add tink_ft5406 + +Add tink_ft5406. + +Signed-off-by: Changhuang Liang +--- + .../bindings/input/tinker_ft5406.yaml | 39 +++++++++++++++++++ + 1 file changed, 39 insertions(+) + create mode 100644 Documentation/devicetree/bindings/input/tinker_ft5406.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/input/tinker_ft5406.yaml +@@ -0,0 +1,39 @@ ++# SPDX-License-Identifier: GPL-2.0 ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/input/touchscreen/goodix.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Tinker FT5406 touchscreen controller Bindings ++ ++maintainers: ++ - Changhuang Liang ++ ++allOf: ++ - $ref: touchscreen.yaml# ++ ++properties: ++ compatible: ++ const: tinker_ft5406 ++ ++ reg: ++ const: 0x38 ++ ++additionalProperties: false ++ ++required: ++ - compatible ++ - reg ++ ++examples: ++ - | ++ i2c { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ tinker_ft5406@38 { ++ compatible = "tinker_ft5406"; ++ reg = <0x38>; ++ }; ++ }; ++ ++... diff --git a/target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch b/target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch new file mode 100644 index 0000000000..f45085f495 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0083-input-touchscreen-Add-tinker_ft5406-driver-support.patch @@ -0,0 +1,444 @@ +From 4800b6e0f2190d991cd4e5352167a9422841f195 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Wed, 21 Dec 2022 16:20:04 +0800 +Subject: [PATCH 083/116] input: touchscreen: Add tinker_ft5406 driver support + +Add tinker_ft5406 driver support + +Signed-off-by: Changhuang Liang +--- + drivers/input/touchscreen/Kconfig | 6 + + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/tinker_ft5406.c | 406 ++++++++++++++++++++++ + 3 files changed, 413 insertions(+) + create mode 100644 drivers/input/touchscreen/tinker_ft5406.c + +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -1399,4 +1399,10 @@ config TOUCHSCREEN_HIMAX_HX83112B + To compile this driver as a module, choose M here: the + module will be called himax_hx83112b. + ++config TOUCHSCREEN_TINKER_FT5406 ++ tristate "tinker ft5406" ++ depends on I2C ++ help ++ Control ft5406 touch ic. ++ + endif +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -118,3 +118,4 @@ obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5 + obj-$(CONFIG_TOUCHSCREEN_IQS7211) += iqs7211.o + obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o + obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) += himax_hx83112b.o ++obj-$(CONFIG_TOUCHSCREEN_TINKER_FT5406) += tinker_ft5406.o +--- /dev/null ++++ b/drivers/input/touchscreen/tinker_ft5406.c +@@ -0,0 +1,406 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * ++ * TINKER BOARD FT5406 touch driver. ++ * ++ * Copyright (c) 2016 ASUSTek Computer Inc. ++ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define RETRY_COUNT 10 ++#define FT_ONE_TCH_LEN 6 ++ ++#define FT_REG_FW_VER 0xA6 ++#define FT_REG_FW_MIN_VER 0xB2 ++#define FT_REG_FW_SUB_MIN_VER 0xB3 ++ ++#define VALID_TD_STATUS_VAL 10 ++#define MAX_TOUCH_POINTS 5 ++ ++#define FT_PRESS 0x7F ++#define FT_MAX_ID 0x0F ++ ++#define FT_TOUCH_X_H 0 ++#define FT_TOUCH_X_L 1 ++#define FT_TOUCH_Y_H 2 ++#define FT_TOUCH_Y_L 3 ++#define FT_TOUCH_EVENT 0 ++#define FT_TOUCH_ID 2 ++ ++#define FT_TOUCH_X_H_REG 3 ++#define FT_TOUCH_X_L_REG 4 ++#define FT_TOUCH_Y_H_REG 5 ++#define FT_TOUCH_Y_L_REG 6 ++#define FT_TD_STATUS_REG 2 ++#define FT_TOUCH_EVENT_REG 3 ++#define FT_TOUCH_ID_REG 5 ++ ++#define FT_TOUCH_DOWN 0 ++#define FT_TOUCH_CONTACT 2 ++ ++struct ts_event { ++ u16 au16_x[MAX_TOUCH_POINTS]; /*x coordinate */ ++ u16 au16_y[MAX_TOUCH_POINTS]; /*y coordinate */ ++ u8 au8_touch_event[MAX_TOUCH_POINTS]; /*touch event: 0:down; 1:up; 2:contact */ ++ u8 au8_finger_id[MAX_TOUCH_POINTS]; /*touch ID */ ++ u16 pressure; ++ u8 touch_point; ++ u8 point_num; ++}; ++ ++struct tinker_ft5406_data { ++ struct device *dev; ++ struct i2c_client *client; ++ struct input_dev *input_dev; ++ struct ts_event event; ++ struct work_struct ft5406_work; ++ ++ int screen_width; ++ int screen_height; ++ int xy_reverse; ++ int known_ids; ++ int retry_count; ++ bool finish_work; ++}; ++ ++struct tinker_ft5406_data *g_ts_data; ++ ++static int fts_i2c_read(struct i2c_client *client, char *writebuf, ++ int writelen, char *readbuf, int readlen) ++{ ++ int ret; ++ ++ if (writelen > 0) { ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = writelen, ++ .buf = writebuf, ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = readlen, ++ .buf = readbuf, ++ }, ++ }; ++ ret = i2c_transfer(client->adapter, msgs, 2); ++ if (ret < 0) ++ dev_err(&client->dev, "i2c read error, %d\n", ret); ++ } else { ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = readlen, ++ .buf = readbuf, ++ }, ++ }; ++ ret = i2c_transfer(client->adapter, msgs, 1); ++ if (ret < 0) ++ dev_err(&client->dev, "i2c read error, %d\n", ret); ++ } ++ ++ return ret; ++} ++ ++static int fts_read_reg(struct i2c_client *client, u8 addr, u8 *val) ++{ ++ return fts_i2c_read(client, &addr, 1, val, 1); ++} ++ ++static int fts_check_fw_ver(struct i2c_client *client) ++{ ++ u8 reg_addr, fw_ver[3]; ++ int ret; ++ ++ reg_addr = FT_REG_FW_VER; ++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[0], 1); ++ if (ret < 0) ++ goto error; ++ ++ reg_addr = FT_REG_FW_MIN_VER; ++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[1], 1); ++ if (ret < 0) ++ goto error; ++ ++ reg_addr = FT_REG_FW_SUB_MIN_VER; ++ ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[2], 1); ++ if (ret < 0) ++ goto error; ++ ++ dev_info(&client->dev, "Firmware version = %d.%d.%d\n", ++ fw_ver[0], fw_ver[1], fw_ver[2]); ++ return 0; ++ ++error: ++ return ret; ++} ++ ++static int fts_read_td_status(struct tinker_ft5406_data *ts_data) ++{ ++ u8 td_status; ++ int ret = -1; ++ ++ ret = fts_read_reg(ts_data->client, FT_TD_STATUS_REG, &td_status); ++ if (ret < 0) { ++ dev_err(&ts_data->client->dev, ++ "Get reg td_status failed, %d\n", ret); ++ return ret; ++ } ++ return (int)td_status; ++} ++ ++static int fts_read_touchdata(struct tinker_ft5406_data *ts_data) ++{ ++ struct ts_event *event = &ts_data->event; ++ int ret = -1, i; ++ u8 buf[FT_ONE_TCH_LEN-2] = { 0 }; ++ u8 reg_addr, pointid = FT_MAX_ID; ++ ++ for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) { ++ reg_addr = FT_TOUCH_X_H_REG + (i * FT_ONE_TCH_LEN); ++ ret = fts_i2c_read(ts_data->client, ®_addr, 1, buf, FT_ONE_TCH_LEN-2); ++ if (ret < 0) { ++ dev_err(&ts_data->client->dev, "Read touchdata failed.\n"); ++ return ret; ++ } ++ ++ pointid = (buf[FT_TOUCH_ID]) >> 4; ++ if (pointid >= MAX_TOUCH_POINTS) ++ break; ++ event->au8_finger_id[i] = pointid; ++ event->au16_x[i] = (s16) (buf[FT_TOUCH_X_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_X_L]; ++ event->au16_y[i] = (s16) (buf[FT_TOUCH_Y_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_Y_L]; ++ event->au8_touch_event[i] = buf[FT_TOUCH_EVENT] >> 6; ++ ++ if (ts_data->xy_reverse) { ++ event->au16_x[i] = ts_data->screen_width - event->au16_x[i] - 1; ++ event->au16_y[i] = ts_data->screen_height - event->au16_y[i] - 1; ++ } ++ } ++ event->pressure = FT_PRESS; ++ ++ return 0; ++} ++ ++static void fts_report_value(struct tinker_ft5406_data *ts_data) ++{ ++ struct ts_event *event = &ts_data->event; ++ int i, modified_ids = 0, released_ids; ++ ++ for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) { ++ if (event->au8_touch_event[i] == FT_TOUCH_DOWN || ++ event->au8_touch_event[i] == FT_TOUCH_CONTACT) { ++ modified_ids |= 1 << event->au8_finger_id[i]; ++ input_mt_slot(ts_data->input_dev, event->au8_finger_id[i]); ++ input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, ++ true); ++ input_report_abs(ts_data->input_dev, ABS_MT_TOUCH_MAJOR, ++ event->pressure); ++ input_report_abs(ts_data->input_dev, ABS_MT_POSITION_X, ++ event->au16_x[i]); ++ input_report_abs(ts_data->input_dev, ABS_MT_POSITION_Y, ++ event->au16_y[i]); ++ ++ if (!((1 << event->au8_finger_id[i]) & ts_data->known_ids)) ++ dev_dbg(&ts_data->client->dev, "Touch id-%d: x = %d, y = %d\n", ++ event->au8_finger_id[i], ++ event->au16_x[i], ++ event->au16_y[i]); ++ } ++ } ++ ++ released_ids = ts_data->known_ids & ~modified_ids; ++ for (i = 0; released_ids && i < MAX_TOUCH_POINTS; i++) { ++ if (released_ids & (1<client->dev, "Release id-%d, known = %x modified = %x\n", ++ i, ts_data->known_ids, modified_ids); ++ input_mt_slot(ts_data->input_dev, i); ++ input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, false); ++ modified_ids &= ~(1 << i); ++ } ++ } ++ ts_data->known_ids = modified_ids; ++ input_mt_report_pointer_emulation(ts_data->input_dev, true); ++ input_sync(ts_data->input_dev); ++} ++ ++static void fts_retry_clear(struct tinker_ft5406_data *ts_data) ++{ ++ if (ts_data->retry_count != 0) ++ ts_data->retry_count = 0; ++} ++ ++static int fts_retry_wait(struct tinker_ft5406_data *ts_data) ++{ ++ if (ts_data->retry_count < RETRY_COUNT) { ++ dev_info(&ts_data->client->dev, ++ "Wait and retry, count = %d\n", ts_data->retry_count); ++ ts_data->retry_count++; ++ msleep(1000); ++ return 1; ++ } ++ dev_err(&ts_data->client->dev, "Attach retry count\n"); ++ return 0; ++} ++ ++static void tinker_ft5406_work(struct work_struct *work) ++{ ++ struct ts_event *event = &g_ts_data->event; ++ int ret = 0, td_status; ++ ++ /* polling 60fps */ ++ while (!g_ts_data->finish_work) { ++ td_status = fts_read_td_status(g_ts_data); ++ if (td_status < 0) { ++ ret = fts_retry_wait(g_ts_data); ++ if (ret == 0) { ++ dev_err(&g_ts_data->client->dev, "Stop touch polling\n"); ++ break; ++ } ++ } else if (td_status < VALID_TD_STATUS_VAL + 1 && ++ (td_status > 0 || g_ts_data->known_ids != 0)) { ++ fts_retry_clear(g_ts_data); ++ memset(event, -1, sizeof(struct ts_event)); ++ event->touch_point = td_status; ++ ret = fts_read_touchdata(g_ts_data); ++ if (ret == 0) ++ fts_report_value(g_ts_data); ++ } ++ msleep_interruptible(17); ++ } ++} ++ ++static int tinker_ft5406_open(struct input_dev *dev) ++{ ++ schedule_work(&g_ts_data->ft5406_work); ++ return 0; ++} ++ ++static void tinker_ft5406_close(struct input_dev *dev) ++{ ++ g_ts_data->finish_work = true; ++ cancel_work_sync(&g_ts_data->ft5406_work); ++ g_ts_data->finish_work = false; ++} ++ ++static int tinker_ft5406_probe(struct i2c_client *client) ++{ ++ struct input_dev *input_dev; ++ int ret = 0; ++ ++ dev_info(&client->dev, "Address = 0x%x\n", client->addr); ++ ++ g_ts_data = kzalloc(sizeof(struct tinker_ft5406_data), GFP_KERNEL); ++ if (g_ts_data == NULL) { ++ dev_err(&client->dev, "No memory for device\n"); ++ return -ENOMEM; ++ } ++ ++ g_ts_data->client = client; ++ i2c_set_clientdata(client, g_ts_data); ++ ++ g_ts_data->screen_width = 800; ++ g_ts_data->screen_height = 480; ++ g_ts_data->xy_reverse = 1; ++ ++ dev_info(&client->dev, "width = %d, height = %d, reverse = %d\n", ++ g_ts_data->screen_width, g_ts_data->screen_height, g_ts_data->xy_reverse); ++ ++ ret = fts_check_fw_ver(g_ts_data->client); ++ if (ret) { ++ dev_err(&client->dev, "Checking touch ic failed\n"); ++ goto check_fw_err; ++ } ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) { ++ dev_err(&client->dev, "Failed to allocate input device\n"); ++ goto input_allocate_failed; ++ } ++ input_dev->name = "fts_ts"; ++ input_dev->id.bustype = BUS_I2C; ++ input_dev->dev.parent = &g_ts_data->client->dev; ++ input_dev->open = tinker_ft5406_open; ++ input_dev->close = tinker_ft5406_close; ++ ++ g_ts_data->input_dev = input_dev; ++ input_set_drvdata(input_dev, g_ts_data); ++ ++ __set_bit(EV_SYN, input_dev->evbit); ++ __set_bit(EV_KEY, input_dev->evbit); ++ __set_bit(EV_ABS, input_dev->evbit); ++ __set_bit(BTN_TOUCH, input_dev->keybit); ++ ++ input_mt_init_slots(input_dev, MAX_TOUCH_POINTS, 0); ++ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, g_ts_data->screen_width, 0, 0); ++ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, g_ts_data->screen_height, 0, 0); ++ ++ ret = input_register_device(input_dev); ++ if (ret) { ++ dev_err(&client->dev, "Input device registration failed\n"); ++ goto input_register_failed; ++ } ++ ++ INIT_WORK(&g_ts_data->ft5406_work, tinker_ft5406_work); ++ ++ return 0; ++ ++input_register_failed: ++ input_free_device(input_dev); ++input_allocate_failed: ++check_fw_err: ++ kfree(g_ts_data); ++ g_ts_data = NULL; ++ return ret; ++} ++ ++static void tinker_ft5406_remove(struct i2c_client *client) ++{ ++ cancel_work_sync(&g_ts_data->ft5406_work); ++ if (g_ts_data->input_dev) { ++ input_unregister_device(g_ts_data->input_dev); ++ input_free_device(g_ts_data->input_dev); ++ } ++ kfree(g_ts_data); ++ g_ts_data = NULL; ++} ++ ++static const struct i2c_device_id tinker_ft5406_id[] = { ++ {"tinker_ft5406", 0}, ++ {}, ++}; ++ ++static struct i2c_driver tinker_ft5406_driver = { ++ .driver = { ++ .name = "tinker_ft5406", ++ }, ++ .probe = tinker_ft5406_probe, ++ .remove = tinker_ft5406_remove, ++ .id_table = tinker_ft5406_id, ++}; ++module_i2c_driver(tinker_ft5406_driver); ++ ++MODULE_DESCRIPTION("TINKER BOARD FT5406 Touch driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch b/target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch new file mode 100644 index 0000000000..69d7c7f494 --- /dev/null +++ b/target/linux/starfive/patches-6.6/0084-dt-binding-media-Add-JH7110-Camera-Subsystem.patch @@ -0,0 +1,246 @@ +From b477a1a53553336edcfeb83be1b35817928daed8 Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Mon, 5 Jun 2023 14:46:16 +0800 +Subject: [PATCH 084/116] dt-binding: media: Add JH7110 Camera Subsystem. + +Add the bindings documentation for Starfive JH7110 Camera Subsystem +which is used for handing image sensor data. + +Signed-off-by: Changhuang Liang +Signed-off-by: Jack Zhu +--- + .../bindings/media/starfive,jh7110-camss.yaml | 228 ++++++++++++++++++ + 1 file changed, 228 insertions(+) + create mode 100644 Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml +@@ -0,0 +1,228 @@ ++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Starfive SoC CAMSS ISP ++ ++maintainers: ++ - Jack Zhu ++ - Changhuang Liang ++ ++description: ++ The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC. It ++ consists of a VIN controller (Video In Controller, a top-level control unit) ++ and an ISP. ++ ++properties: ++ compatible: ++ const: starfive,jh7110-vin ++ ++ reg: ++ maxItems: 8 ++ ++ reg-names: ++ items: ++ - const: csi2rx ++ - const: vclk ++ - const: vrst ++ - const: sctrl ++ - const: isp ++ - const: trst ++ - const: pmu ++ - const: syscrg ++ ++ clocks: ++ maxItems: 16 ++ ++ clock-names: ++ items: ++ - const: clk_apb_func ++ - const: clk_pclk ++ - const: clk_sys_clk ++ - const: clk_wrapper_clk_c ++ - const: clk_dvp_inv ++ - const: clk_axiwr ++ - const: clk_mipi_rx0_pxl ++ - const: clk_pixel_clk_if0 ++ - const: clk_pixel_clk_if1 ++ - const: clk_pixel_clk_if2 ++ - const: clk_pixel_clk_if3 ++ - const: clk_m31dphy_cfgclk_in ++ - const: clk_m31dphy_refclk_in ++ - const: clk_m31dphy_txclkesc_lan0 ++ - const: clk_ispcore_2x ++ - const: clk_isp_axi ++ ++ resets: ++ maxItems: 14 ++ ++ reset-names: ++ items: ++ - const: rst_wrapper_p ++ - const: rst_wrapper_c ++ - const: rst_pclk ++ - const: rst_sys_clk ++ - const: rst_axird ++ - const: rst_axiwr ++ - const: rst_pixel_clk_if0 ++ - const: rst_pixel_clk_if1 ++ - const: rst_pixel_clk_if2 ++ - const: rst_pixel_clk_if3 ++ - const: rst_m31dphy_hw ++ - const: rst_m31dphy_b09_always_on ++ - const: rst_isp_top_n ++ - const: rst_isp_top_axi ++ ++ power-domains: ++ items: ++ - description: JH7110 ISP Power Domain Switch Controller. ++ ++ interrupts: ++ maxItems: 5 ++ ++ ports: ++ $ref: /schemas/graph.yaml#/properties/ports ++ ++ properties: ++ port@0: ++ $ref: /schemas/graph.yaml#/$defs/port-base ++ unevaluatedProperties: false ++ description: Input port for receiving DVP data. ++ ++ properties: ++ endpoint: ++ $ref: video-interfaces.yaml# ++ unevaluatedProperties: false ++ ++ properties: ++ bus-type: ++ enum: [5, 6] ++ ++ bus-width: ++ enum: [8, 10, 12] ++ ++ data-shift: ++ enum: [0, 2] ++ default: 0 ++ ++ hsync-active: ++ enum: [0, 1] ++ default: 1 ++ ++ vsync-active: ++ enum: [0, 1] ++ default: 1 ++ ++ required: ++ - bus-type ++ - bus-width ++ ++ port@1: ++ $ref: /schemas/graph.yaml#/properties/port ++ description: Input port for receiving CSI data. ++ ++ required: ++ - port@0 ++ - port@1 ++ ++required: ++ - compatible ++ - reg ++ - reg-names ++ - clocks ++ - clock-names ++ - resets ++ - reset-names ++ - power-domains ++ - interrupts ++ - ports ++ ++additionalProperties: false ++ ++examples: ++ - | ++ vin_sysctl: vin_sysctl@19800000 { ++ compatible = "starfive,jh7110-vin"; ++ reg = <0x0 0x19800000 0x0 0x10000>, ++ <0x0 0x19810000 0x0 0x10000>, ++ <0x0 0x19820000 0x0 0x10000>, ++ <0x0 0x19840000 0x0 0x10000>, ++ <0x0 0x19870000 0x0 0x30000>, ++ <0x0 0x11840000 0x0 0x10000>, ++ <0x0 0x17030000 0x0 0x10000>, ++ <0x0 0x13020000 0x0 0x10000>; ++ reg-names = "csi2rx", "vclk", "vrst", "sctrl", ++ "isp", "trst", "pmu", "syscrg"; ++ clocks = <&clkisp JH7110_DOM4_APB_FUNC>, ++ <&clkisp JH7110_U0_VIN_PCLK>, ++ <&clkisp JH7110_U0_VIN_SYS_CLK>, ++ <&clkisp JH7110_U0_ISPV2_TOP_WRAPPER_CLK_C>, ++ <&clkisp JH7110_DVP_INV>, ++ <&clkisp JH7110_U0_VIN_CLK_P_AXIWR>, ++ <&clkisp JH7110_MIPI_RX0_PXL>, ++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF0>, ++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF1>, ++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF2>, ++ <&clkisp JH7110_U0_VIN_PIXEL_CLK_IF3>, ++ <&clkisp JH7110_U0_M31DPHY_CFGCLK_IN>, ++ <&clkisp JH7110_U0_M31DPHY_REFCLK_IN>, ++ <&clkisp JH7110_U0_M31DPHY_TXCLKESC_LAN0>, ++ <&clkgen JH7110_ISP_TOP_CLK_ISPCORE_2X>, ++ <&clkgen JH7110_ISP_TOP_CLK_ISP_AXI>; ++ clock-names = "clk_apb_func", "clk_pclk", "clk_sys_clk", ++ "clk_wrapper_clk_c", "clk_dvp_inv", "clk_axiwr", ++ "clk_mipi_rx0_pxl", "clk_pixel_clk_if0", ++ "clk_pixel_clk_if1", "clk_pixel_clk_if2", ++ "clk_pixel_clk_if3", "clk_m31dphy_cfgclk_in", ++ "clk_m31dphy_refclk_in", "clk_m31dphy_txclkesc_lan0", ++ "clk_ispcore_2x", "clk_isp_axi"; ++ resets = <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_P>, ++ <&rstgen RSTN_U0_ISPV2_TOP_WRAPPER_C>, ++ <&rstgen RSTN_U0_VIN_N_PCLK>, ++ <&rstgen RSTN_U0_VIN_N_SYS_CLK>, ++ <&rstgen RSTN_U0_VIN_P_AXIRD>, ++ <&rstgen RSTN_U0_VIN_P_AXIWR>, ++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF0>, ++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF1>, ++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF2>, ++ <&rstgen RSTN_U0_VIN_N_PIXEL_CLK_IF3>, ++ <&rstgen RSTN_U0_M31DPHY_HW>, ++ <&rstgen RSTN_U0_M31DPHY_B09_ALWAYS_ON>, ++ <&rstgen RSTN_U0_DOM_ISP_TOP_N>, ++ <&rstgen RSTN_U0_DOM_ISP_TOP_AXI>; ++ reset-names = "rst_wrapper_p", "rst_wrapper_c", "rst_pclk", ++ "rst_sys_clk", "rst_axird", "rst_axiwr", "rst_pixel_clk_if0", ++ "rst_pixel_clk_if1", "rst_pixel_clk_if2", "rst_pixel_clk_if3", ++ "rst_m31dphy_hw", "rst_m31dphy_b09_always_on", ++ "rst_isp_top_n", "rst_isp_top_axi"; ++ starfive,aon-syscon = <&aon_syscon 0x00>; ++ power-domains = <&pwrc JH7110_PD_ISP>; ++ /* irq nr: vin, isp, isp_csi, isp_scd, isp_csiline */ ++ interrupts = <92>, <87>, <88>, <89>, <90>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ port@0 { ++ reg = <0>; ++ vin_from_sc2235: endpoint { ++ remote-endpoint = <&sc2235_to_vin>; ++ bus-type = <5>; ++ bus-width = <8>; ++ data-shift = <2>; ++ hsync-active = <1>; ++ vsync-active = <0>; ++ pclk-sample = <1>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ vin_from_csi2rx: endpoint { ++ remote-endpoint = <&csi2rx_to_vin>; ++ }; ++ }; ++ }; ++ }; diff --git a/target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch b/target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch new file mode 100644 index 0000000000..b5d5f73f9d --- /dev/null +++ b/target/linux/starfive/patches-6.6/0085-media-starfive-Add-vin-driver-support.patch @@ -0,0 +1,24014 @@ +From 908b10ebc95eb29caae8c4737b23a29af5c6298f Mon Sep 17 00:00:00 2001 +From: Changhuang Liang +Date: Mon, 5 Jun 2023 13:54:16 +0800 +Subject: [PATCH 085/116] media: starfive: Add vin driver support + +Add vin driver support. + +Signed-off-by: Changhuang Liang +--- + drivers/media/platform/Kconfig | 1 + + drivers/media/platform/Makefile | 1 + + drivers/media/platform/starfive/Kconfig | 56 + + drivers/media/platform/starfive/Makefile | 24 + + .../platform/starfive/v4l2_driver/Readme.txt | 11 + + .../starfive/v4l2_driver/imx219_mipi.c | 1583 ++++++++ + .../starfive/v4l2_driver/ov13850_mipi.c | 1921 ++++++++++ + .../starfive/v4l2_driver/ov4689_mipi.c | 2975 +++++++++++++++ + .../platform/starfive/v4l2_driver/ov5640.c | 3227 +++++++++++++++++ + .../platform/starfive/v4l2_driver/sc2235.c | 1914 ++++++++++ + .../starfive/v4l2_driver/stf_common.h | 185 + + .../platform/starfive/v4l2_driver/stf_csi.c | 465 +++ + .../platform/starfive/v4l2_driver/stf_csi.h | 61 + + .../starfive/v4l2_driver/stf_csi_hw_ops.c | 310 ++ + .../starfive/v4l2_driver/stf_csiphy.c | 357 ++ + .../starfive/v4l2_driver/stf_csiphy.h | 188 + + .../starfive/v4l2_driver/stf_csiphy_hw_ops.c | 335 ++ + .../starfive/v4l2_driver/stf_dmabuf.c | 123 + + .../starfive/v4l2_driver/stf_dmabuf.h | 12 + + .../platform/starfive/v4l2_driver/stf_dvp.c | 385 ++ + .../platform/starfive/v4l2_driver/stf_dvp.h | 67 + + .../starfive/v4l2_driver/stf_dvp_hw_ops.c | 187 + + .../platform/starfive/v4l2_driver/stf_event.c | 36 + + .../platform/starfive/v4l2_driver/stf_isp.c | 1521 ++++++++ + .../platform/starfive/v4l2_driver/stf_isp.h | 222 ++ + .../starfive/v4l2_driver/stf_isp_hw_ops.c | 1550 ++++++++ + .../starfive/v4l2_driver/stf_isp_ioctl.h | 133 + + .../platform/starfive/v4l2_driver/stf_video.c | 1552 ++++++++ + .../platform/starfive/v4l2_driver/stf_video.h | 83 + + .../platform/starfive/v4l2_driver/stf_vin.c | 1515 ++++++++ + .../platform/starfive/v4l2_driver/stf_vin.h | 182 + + .../starfive/v4l2_driver/stf_vin_hw_ops.c | 433 +++ + .../platform/starfive/v4l2_driver/stfcamss.c | 1369 +++++++ + .../platform/starfive/v4l2_driver/stfcamss.h | 117 + + include/uapi/linux/jh7110-isp.h | 253 ++ + include/uapi/linux/v4l2-controls.h | 6 + + include/video/stf-vin.h | 443 +++ + 37 files changed, 23803 insertions(+) + create mode 100644 drivers/media/platform/starfive/Kconfig + create mode 100644 drivers/media/platform/starfive/Makefile + create mode 100644 drivers/media/platform/starfive/v4l2_driver/Readme.txt + create mode 100644 drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov13850_mipi.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov4689_mipi.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/ov5640.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/sc2235.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_common.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csi_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_csiphy_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dmabuf.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_dvp_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_event.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_isp_ioctl.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_video.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_video.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin.h + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stfcamss.c + create mode 100644 drivers/media/platform/starfive/v4l2_driver/stfcamss.h + create mode 100644 include/uapi/linux/jh7110-isp.h + create mode 100644 include/video/stf-vin.h + +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -80,6 +80,7 @@ source "drivers/media/platform/renesas/K + source "drivers/media/platform/rockchip/Kconfig" + source "drivers/media/platform/samsung/Kconfig" + source "drivers/media/platform/st/Kconfig" ++source "drivers/media/platform/starfive/Kconfig" + source "drivers/media/platform/sunxi/Kconfig" + source "drivers/media/platform/ti/Kconfig" + source "drivers/media/platform/verisilicon/Kconfig" +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -23,6 +23,7 @@ obj-y += renesas/ + obj-y += rockchip/ + obj-y += samsung/ + obj-y += st/ ++obj-y += starfive/ + obj-y += sunxi/ + obj-y += ti/ + obj-y += verisilicon/ +--- /dev/null ++++ b/drivers/media/platform/starfive/Kconfig +@@ -0,0 +1,56 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++ ++comment "Starfive media platform drivers" ++ ++config VIN_SENSOR_OV5640 ++ tristate "VIN SENSOR support OV5640" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ help ++ Say Y here if you want to have support for VIN sensor OV5640 ++ ++config VIN_SENSOR_SC2235 ++ tristate "VIN SENSOR support SC2235" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ help ++ Say Y here if you want to have support for VIN sensor SC2235 ++ ++config VIN_SENSOR_OV4689 ++ tristate "VIN SENSOR support OV4689" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ ++ help ++ Say Y here if you want to have support for VIN sensor OV4689 ++ ++config VIN_SENSOR_OV13850 ++ bool "VIN SENSOR support OV13850" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ help ++ Say Y here if you want to have support for VIN sensor OV13850 ++ ++config VIN_SENSOR_IMX219 ++ tristate "VIN SENSOR support IMX219" ++ depends on VIDEO_STF_VIN ++ select V4L2_FWNODE ++ default n ++ help ++ Say Y here if you want to have support for VIN sensor IMX219 ++ ++config VIDEO_STF_VIN ++ tristate "starfive VIC video in support" ++ depends on V4L_PLATFORM_DRIVERS ++ depends on VIDEO_DEV ++ select MEDIA_CONTROLLER ++ select VIDEOBUF2_DMA_CONTIG ++ select VIDEO_V4L2_SUBDEV_API ++ select V4L2_FWNODE ++ help ++ To compile this driver as a module, choose M here: the module ++ will be called stf-vin. +--- /dev/null ++++ b/drivers/media/platform/starfive/Makefile +@@ -0,0 +1,24 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++obj-$(CONFIG_VIN_SENSOR_OV5640) += v4l2_driver/ov5640.o ++obj-$(CONFIG_VIN_SENSOR_SC2235) += v4l2_driver/sc2235.o ++obj-$(CONFIG_VIN_SENSOR_OV4689) += v4l2_driver/ov4689_mipi.o ++obj-$(CONFIG_VIN_SENSOR_OV13850) += v4l2_driver/ov13850_mipi.o ++obj-$(CONFIG_VIN_SENSOR_IMX219) += v4l2_driver/imx219_mipi.o ++ ++starfivecamss-objs += v4l2_driver/stfcamss.o \ ++ v4l2_driver/stf_event.o \ ++ v4l2_driver/stf_dvp.o \ ++ v4l2_driver/stf_csi.o \ ++ v4l2_driver/stf_csiphy.o \ ++ v4l2_driver/stf_isp.o \ ++ v4l2_driver/stf_video.o \ ++ v4l2_driver/stf_vin.o \ ++ v4l2_driver/stf_vin_hw_ops.o \ ++ v4l2_driver/stf_csi_hw_ops.o \ ++ v4l2_driver/stf_csiphy_hw_ops.o \ ++ v4l2_driver/stf_isp_hw_ops.o \ ++ v4l2_driver/stf_dvp_hw_ops.o \ ++ v4l2_driver/stf_dmabuf.o ++ ++obj-$(CONFIG_VIDEO_STF_VIN) += starfivecamss.o \ +--- /dev/null ++++ b/drivers/media/platform/starfive/v4l2_driver/Readme.txt +@@ -0,0 +1,11 @@ ++ ++/dev/video0: Output the camera data directly. ++/dev/video1: Output the data of the camera converted by isp. ++ ++ensure linux/arch/riscv/configs/starfive_jh7110_defconfig: ++CONFIG_VIDEO_STF_VIN=y ++CONFIG_VIN_SENSOR_SC2235=y ++CONFIG_VIN_SENSOR_OV4689=y ++ ++Only support the lane0/lane5 of dphy as clock lane, lane1/lane2/lane3/lane4 ++as data lane. +--- /dev/null ++++ b/drivers/media/platform/starfive/v4l2_driver/imx219_mipi.c +@@ -0,0 +1,1583 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * A V4L2 driver for Sony IMX219 cameras. ++ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd ++ * ++ * Based on Sony imx258 camera driver ++ * Copyright (C) 2018 Intel Corporation ++ * ++ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver ++ * Copyright 2018 Qtechnology A/S ++ * ++ * Flip handling taken from the Sony IMX319 driver. ++ * Copyright (C) 2018 Intel Corporation ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define IMX219_REG_VALUE_08BIT 1 ++#define IMX219_REG_VALUE_16BIT 2 ++ ++#define IMX219_REG_MODE_SELECT 0x0100 ++#define IMX219_MODE_STANDBY 0x00 ++#define IMX219_MODE_STREAMING 0x01 ++ ++/* Chip ID */ ++#define IMX219_REG_CHIP_ID 0x0000 ++#define IMX219_CHIP_ID 0x0219 ++ ++/* External clock frequency is 24.0M */ ++#define IMX219_XCLK_FREQ 24000000 ++ ++/* Pixel rate is fixed at 182.4M for all the modes */ ++#define IMX219_PIXEL_RATE 182400000 ++ ++#define IMX219_DEFAULT_LINK_FREQ 456000000 ++ ++/* V_TIMING internal */ ++#define IMX219_REG_VTS 0x0160 ++#define IMX219_VTS_15FPS 0x0dc6 ++#define IMX219_VTS_30FPS_1080P 0x06e3 ++#define IMX219_VTS_30FPS_BINNED 0x06e3 ++#define IMX219_VTS_30FPS_1280x720 0x06e3 ++#define IMX219_VTS_30FPS_640x480 0x06e3 ++#define IMX219_VTS_MAX 0xffff ++ ++#define IMX219_VBLANK_MIN 4 ++ ++/*Frame Length Line*/ ++#define IMX219_FLL_MIN 0x08a6 ++#define IMX219_FLL_MAX 0xffff ++#define IMX219_FLL_STEP 1 ++#define IMX219_FLL_DEFAULT 0x0c98 ++ ++/* HBLANK control - read only */ ++#define IMX219_PPL_DEFAULT 3448 ++ ++/* Exposure control */ ++#define IMX219_REG_EXPOSURE 0x015a ++#define IMX219_EXPOSURE_MIN 4 ++#define IMX219_EXPOSURE_STEP 1 ++#define IMX219_EXPOSURE_DEFAULT 0x640 ++#define IMX219_EXPOSURE_MAX 65535 ++ ++/* Analog gain control */ ++#define IMX219_REG_ANALOG_GAIN 0x0157 ++#define IMX219_ANA_GAIN_MIN 0 ++#define IMX219_ANA_GAIN_MAX 232 ++#define IMX219_ANA_GAIN_STEP 1 ++#define IMX219_ANA_GAIN_DEFAULT 0xd0 ++ ++/* Digital gain control */ ++#define IMX219_REG_DIGITAL_GAIN 0x0158 ++#define IMX219_DGTL_GAIN_MIN 0x0100 ++#define IMX219_DGTL_GAIN_MAX 0x0fff ++#define IMX219_DGTL_GAIN_DEFAULT 0x0100 ++#define IMX219_DGTL_GAIN_STEP 1 ++ ++#define IMX219_REG_ORIENTATION 0x0172 ++ ++/* Test Pattern Control */ ++#define IMX219_REG_TEST_PATTERN 0x0600 ++#define IMX219_TEST_PATTERN_DISABLE 0 ++#define IMX219_TEST_PATTERN_SOLID_COLOR 1 ++#define IMX219_TEST_PATTERN_COLOR_BARS 2 ++#define IMX219_TEST_PATTERN_GREY_COLOR 3 ++#define IMX219_TEST_PATTERN_PN9 4 ++ ++/* Test pattern colour components */ ++#define IMX219_REG_TESTP_RED 0x0602 ++#define IMX219_REG_TESTP_GREENR 0x0604 ++#define IMX219_REG_TESTP_BLUE 0x0606 ++#define IMX219_REG_TESTP_GREENB 0x0608 ++#define IMX219_TESTP_COLOUR_MIN 0 ++#define IMX219_TESTP_COLOUR_MAX 0x03ff ++#define IMX219_TESTP_COLOUR_STEP 1 ++#define IMX219_TESTP_RED_DEFAULT IMX219_TESTP_COLOUR_MAX ++#define IMX219_TESTP_GREENR_DEFAULT 0 ++#define IMX219_TESTP_BLUE_DEFAULT 0 ++#define IMX219_TESTP_GREENB_DEFAULT 0 ++ ++/* IMX219 native and active pixel array size. */ ++#define IMX219_NATIVE_WIDTH 3296U ++#define IMX219_NATIVE_HEIGHT 2480U ++#define IMX219_PIXEL_ARRAY_LEFT 8U ++#define IMX219_PIXEL_ARRAY_TOP 8U ++#define IMX219_PIXEL_ARRAY_WIDTH 3280U ++#define IMX219_PIXEL_ARRAY_HEIGHT 2464U ++ ++struct imx219_reg { ++ u16 address; ++ u8 val; ++}; ++ ++struct imx219_reg_list { ++ unsigned int num_of_regs; ++ const struct imx219_reg *regs; ++}; ++ ++/* Mode : resolution and related config&values */ ++struct imx219_mode { ++ /* Frame width */ ++ unsigned int width; ++ /* Frame height */ ++ unsigned int height; ++ ++ unsigned int fps; ++ ++ /* Analog crop rectangle. */ ++ struct v4l2_rect crop; ++ ++ /* V-timing */ ++ unsigned int vts_def; ++ ++ /* Default register values */ ++ struct imx219_reg_list reg_list; ++}; ++ ++/* ++ * Register sets lifted off the i2C interface from the Raspberry Pi firmware ++ * driver. ++ * 3280x2464 = mode 2, 1920x1080 = mode 1, 1640x1232 = mode 4, 640x480 = mode 7. ++ */ ++ ++static const struct imx219_reg mode_1920_1080_regs[] = { ++ {0x0100, 0x00}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x0c}, ++ {0x300a, 0xff}, ++ {0x300b, 0xff}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x09}, ++ {0x0114, 0x01}, ++ {0x0128, 0x00}, ++ {0x012a, 0x18}, ++ {0x012b, 0x00}, ++ {0x0162, 0x0d}, ++ {0x0163, 0x78}, ++ {0x0164, 0x02}, ++ {0x0165, 0xa8}, ++ {0x0166, 0x0a}, ++ {0x0167, 0x27}, ++ {0x0168, 0x02}, ++ {0x0169, 0xb4}, ++ {0x016a, 0x06}, ++ {0x016b, 0xeb}, ++ {0x016c, 0x07}, ++ {0x016d, 0x80}, ++ {0x016e, 0x04}, ++ {0x016f, 0x38}, ++ {0x0170, 0x01}, ++ {0x0171, 0x01}, ++ {0x0174, 0x00}, ++ {0x0175, 0x00}, ++ {0x0301, 0x05}, ++ {0x0303, 0x01}, ++ {0x0304, 0x03}, ++ {0x0305, 0x03}, ++ {0x0306, 0x00}, ++ {0x0307, 0x39}, ++ {0x030b, 0x01}, ++ {0x030c, 0x00}, ++ {0x030d, 0x72}, ++ {0x0624, 0x07}, ++ {0x0625, 0x80}, ++ {0x0626, 0x04}, ++ {0x0627, 0x38}, ++ {0x455e, 0x00}, ++ {0x471e, 0x4b}, ++ {0x4767, 0x0f}, ++ {0x4750, 0x14}, ++ {0x4540, 0x00}, ++ {0x47b4, 0x14}, ++ {0x4713, 0x30}, ++ {0x478b, 0x10}, ++ {0x478f, 0x10}, ++ {0x4793, 0x10}, ++ {0x4797, 0x0e}, ++ {0x479b, 0x0e}, ++}; ++ ++static const struct imx219_reg mode_1280_720_regs[] = { ++ {0x0100, 0x00}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x0c}, ++ {0x300a, 0xff}, ++ {0x300b, 0xff}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x09}, ++ {0x0114, 0x01}, ++ {0x0128, 0x00}, ++ {0x012a, 0x18}, ++ {0x012b, 0x00}, ++ {0x0162, 0x0d}, ++ {0x0163, 0x78}, ++ {0x0164, 0x01}, ++ {0x0165, 0x68}, ++ {0x0166, 0x0b}, ++ {0x0167, 0x67}, ++ {0x0168, 0x02}, ++ {0x0169, 0x00}, ++ {0x016a, 0x07}, ++ {0x016b, 0x9f}, ++ {0x016c, 0x05}, ++ {0x016d, 0x00}, ++ {0x016e, 0x02}, ++ {0x016f, 0xd0}, ++ {0x0170, 0x01}, ++ {0x0171, 0x01}, ++ {0x0174, 0x01}, ++ {0x0175, 0x01}, ++ {0x0301, 0x05}, ++ {0x0303, 0x01}, ++ {0x0304, 0x03}, ++ {0x0305, 0x03}, ++ {0x0306, 0x00}, ++ {0x0307, 0x39}, ++ {0x030b, 0x01}, ++ {0x030c, 0x00}, ++ {0x030d, 0x72}, ++ {0x0624, 0x06}, ++ {0x0625, 0x68}, ++ {0x0626, 0x04}, ++ {0x0627, 0xd0}, ++ {0x455e, 0x00}, ++ {0x471e, 0x4b}, ++ {0x4767, 0x0f}, ++ {0x4750, 0x14}, ++ {0x4540, 0x00}, ++ {0x47b4, 0x14}, ++ {0x4713, 0x30}, ++ {0x478b, 0x10}, ++ {0x478f, 0x10}, ++ {0x4793, 0x10}, ++ {0x4797, 0x0e}, ++ {0x479b, 0x0e}, ++}; ++ ++static const struct imx219_reg mode_640_480_regs[] = { ++ {0x0100, 0x00}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x0c}, ++ {0x300a, 0xff}, ++ {0x300b, 0xff}, ++ {0x30eb, 0x05}, ++ {0x30eb, 0x09}, ++ {0x0114, 0x01}, ++ {0x0128, 0x00}, ++ {0x012a, 0x18}, ++ {0x012b, 0x00}, ++ {0x0162, 0x0d}, ++ {0x0163, 0x78}, ++ {0x0164, 0x03}, ++ {0x0165, 0xe8}, ++ {0x0166, 0x08}, ++ {0x0167, 0xe7}, ++ {0x0168, 0x02}, ++ {0x0169, 0xf0}, ++ {0x016a, 0x06}, ++ {0x016b, 0xaf}, ++ {0x016c, 0x02}, ++ {0x016d, 0x80}, ++ {0x016e, 0x01}, ++ {0x016f, 0xe0}, ++ {0x0170, 0x01}, ++ {0x0171, 0x01}, ++ {0x0174, 0x03}, ++ {0x0175, 0x03}, ++ {0x0301, 0x05}, ++ {0x0303, 0x01}, ++ {0x0304, 0x03}, ++ {0x0305, 0x03}, ++ {0x0306, 0x00}, ++ {0x0307, 0x39}, ++ {0x030b, 0x01}, ++ {0x030c, 0x00}, ++ {0x030d, 0x72}, ++ {0x0624, 0x06}, ++ {0x0625, 0x68}, ++ {0x0626, 0x04}, ++ {0x0627, 0xd0}, ++ {0x455e, 0x00}, ++ {0x471e, 0x4b}, ++ {0x4767, 0x0f}, ++ {0x4750, 0x14}, ++ {0x4540, 0x00}, ++ {0x47b4, 0x14}, ++ {0x4713, 0x30}, ++ {0x478b, 0x10}, ++ {0x478f, 0x10}, ++ {0x4793, 0x10}, ++ {0x4797, 0x0e}, ++ {0x479b, 0x0e}, ++}; ++ ++static const struct imx219_reg raw8_framefmt_regs[] = { ++ {0x018c, 0x08}, ++ {0x018d, 0x08}, ++ {0x0309, 0x08}, ++}; ++ ++static const struct imx219_reg raw10_framefmt_regs[] = { ++ {0x018c, 0x0a}, ++ {0x018d, 0x0a}, ++ {0x0309, 0x0a}, ++}; ++ ++static const s64 imx219_link_freq_menu[] = { ++ IMX219_DEFAULT_LINK_FREQ, ++}; ++ ++static const char * const imx219_test_pattern_menu[] = { ++ "Disabled", ++ "Color Bars", ++ "Solid Color", ++ "Grey Color Bars", ++ "PN9" ++}; ++ ++static const int imx219_test_pattern_val[] = { ++ IMX219_TEST_PATTERN_DISABLE, ++ IMX219_TEST_PATTERN_COLOR_BARS, ++ IMX219_TEST_PATTERN_SOLID_COLOR, ++ IMX219_TEST_PATTERN_GREY_COLOR, ++ IMX219_TEST_PATTERN_PN9, ++}; ++ ++/* regulator supplies */ ++static const char * const imx219_supply_name[] = { ++ /* Supplies can be enabled in any order */ ++ "VANA", /* Analog (2.8V) supply */ ++ "VDIG", /* Digital Core (1.8V) supply */ ++ "VDDL", /* IF (1.2V) supply */ ++}; ++ ++#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name) ++ ++/* ++ * The supported formats. ++ * This table MUST contain 4 entries per format, to cover the various flip ++ * combinations in the order ++ * - no flip ++ * - h flip ++ * - v flip ++ * - h&v flips ++ */ ++static const u32 codes[] = { ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++ ++ MEDIA_BUS_FMT_SRGGB8_1X8, ++ MEDIA_BUS_FMT_SGRBG8_1X8, ++ MEDIA_BUS_FMT_SGBRG8_1X8, ++ MEDIA_BUS_FMT_SBGGR8_1X8, ++}; ++ ++/* ++ * Initialisation delay between XCLR low->high and the moment when the sensor ++ * can start capture (i.e. can leave software stanby) must be not less than: ++ * t4 + max(t5, t6 +