diff options
10 files changed, 220 insertions, 383 deletions
diff --git a/include/kernel-version.mk b/include/kernel-version.mk index 746f75d212..29be92246d 100644 --- a/include/kernel-version.mk +++ b/include/kernel-version.mk @@ -6,10 +6,10 @@ ifdef CONFIG_TESTING_KERNEL KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER) endif -LINUX_VERSION-5.4 = .111 +LINUX_VERSION-5.4 = .112 LINUX_VERSION-5.10 = .31 -LINUX_KERNEL_HASH-5.4.111 = 21626132658dc34cb41b7aa7b80ecf83751890a71ac1a63d77aea9d488271a03 +LINUX_KERNEL_HASH-5.4.112 = b8361d461a4254c86d4c68aa4ceab1debaa3b2ccc6785542d026837a4fc20ca4 LINUX_KERNEL_HASH-5.10.31 = 54eef1a4d29a2582281375e028ac73c2c5d90dfa21500fa8c3b00e529a2b510d remove_uri_prefix=$(subst git://,,$(subst http://,,$(subst https://,,$(1)))) diff --git a/target/linux/generic/hack-5.4/902-debloat_proc.patch b/target/linux/generic/hack-5.4/902-debloat_proc.patch index d9febbada8..7d6c2b2d6e 100644 --- a/target/linux/generic/hack-5.4/902-debloat_proc.patch +++ b/target/linux/generic/hack-5.4/902-debloat_proc.patch @@ -330,7 +330,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name> --- a/net/core/sock.c +++ b/net/core/sock.c -@@ -3629,6 +3629,8 @@ static __net_initdata struct pernet_oper +@@ -3623,6 +3623,8 @@ static __net_initdata struct pernet_oper static int __init proto_init(void) { diff --git a/target/linux/generic/pending-5.4/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch b/target/linux/generic/pending-5.4/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch index 3590557a30..90f4199eab 100644 --- a/target/linux/generic/pending-5.4/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch +++ b/target/linux/generic/pending-5.4/670-ipv6-allow-rejecting-with-source-address-failed-policy.patch @@ -185,7 +185,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org> cfg->fc_flags |= RTF_REJECT; if (rtm->rtm_type == RTN_LOCAL) -@@ -6019,6 +6050,8 @@ static int ip6_route_dev_notify(struct n +@@ -6021,6 +6052,8 @@ static int ip6_route_dev_notify(struct n #ifdef CONFIG_IPV6_MULTIPLE_TABLES net->ipv6.ip6_prohibit_entry->dst.dev = dev; net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); @@ -194,7 +194,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org> net->ipv6.ip6_blk_hole_entry->dst.dev = dev; net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); #endif -@@ -6030,6 +6063,7 @@ static int ip6_route_dev_notify(struct n +@@ -6032,6 +6065,7 @@ static int ip6_route_dev_notify(struct n in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); #ifdef CONFIG_IPV6_MULTIPLE_TABLES in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); @@ -202,7 +202,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org> in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); #endif } -@@ -6222,6 +6256,8 @@ static int __net_init ip6_route_net_init +@@ -6224,6 +6258,8 @@ static int __net_init ip6_route_net_init #ifdef CONFIG_IPV6_MULTIPLE_TABLES net->ipv6.fib6_has_custom_rules = false; @@ -211,7 +211,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org> net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, sizeof(*net->ipv6.ip6_prohibit_entry), GFP_KERNEL); -@@ -6232,11 +6268,21 @@ static int __net_init ip6_route_net_init +@@ -6234,11 +6270,21 @@ static int __net_init ip6_route_net_init ip6_template_metrics, true); INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached); @@ -234,7 +234,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org> net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, ip6_template_metrics, true); -@@ -6260,6 +6306,8 @@ out: +@@ -6262,6 +6308,8 @@ out: return ret; #ifdef CONFIG_IPV6_MULTIPLE_TABLES @@ -243,7 +243,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org> out_ip6_prohibit_entry: kfree(net->ipv6.ip6_prohibit_entry); out_ip6_null_entry: -@@ -6279,6 +6327,7 @@ static void __net_exit ip6_route_net_exi +@@ -6281,6 +6329,7 @@ static void __net_exit ip6_route_net_exi kfree(net->ipv6.ip6_null_entry); #ifdef CONFIG_IPV6_MULTIPLE_TABLES kfree(net->ipv6.ip6_prohibit_entry); @@ -251,7 +251,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org> kfree(net->ipv6.ip6_blk_hole_entry); #endif dst_entries_destroy(&net->ipv6.ip6_dst_ops); -@@ -6356,6 +6405,9 @@ void __init ip6_route_init_special_entri +@@ -6358,6 +6407,9 @@ void __init ip6_route_init_special_entri init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); diff --git a/target/linux/mediatek/patches-5.4/0601-net-dsa-propagate-resolved-link-config-via-mac_link_.patch b/target/linux/mediatek/patches-5.4/0601-net-dsa-propagate-resolved-link-config-via-mac_link_.patch index 4c939e8017..7938751124 100644 --- a/target/linux/mediatek/patches-5.4/0601-net-dsa-propagate-resolved-link-config-via-mac_link_.patch +++ b/target/linux/mediatek/patches-5.4/0601-net-dsa-propagate-resolved-link-config-via-mac_link_.patch @@ -51,7 +51,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net> struct ethtool_eee *p = &priv->dev->ports[port].eee; --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c -@@ -1508,7 +1508,9 @@ static void gswip_phylink_mac_link_down( +@@ -1657,7 +1657,9 @@ static void gswip_phylink_mac_link_down( static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, diff --git a/target/linux/mediatek/patches-5.4/115-dts-bpi64-add-snand-support.patch b/target/linux/mediatek/patches-5.4/115-dts-bpi64-add-snand-support.patch index aa0e347115..4d369cefef 100644 --- a/target/linux/mediatek/patches-5.4/115-dts-bpi64-add-snand-support.patch +++ b/target/linux/mediatek/patches-5.4/115-dts-bpi64-add-snand-support.patch @@ -1,6 +1,6 @@ ---- a/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts 2021-03-13 13:46:37.057936814 +0200 -+++ b/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts 2021-03-13 13:49:26.957481217 +0200 -@@ -114,7 +114,7 @@ +--- a/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts ++++ b/arch/arm64/boot/dts/mediatek/mt7622-bananapi-bpi-r64.dts +@@ -108,7 +108,7 @@ }; &bch { @@ -9,7 +9,7 @@ }; &btif { -@@ -259,14 +259,40 @@ +@@ -283,14 +283,40 @@ status = "disabled"; }; diff --git a/target/linux/mvebu/patches-5.4/319-ARM-dts-turris-omnia-configure-LED-2--INTn-pin-as-interrupt-pin.patch b/target/linux/mvebu/patches-5.4/319-ARM-dts-turris-omnia-configure-LED-2--INTn-pin-as-interrupt-pin.patch deleted file mode 100644 index 1fbbe8b506..0000000000 --- a/target/linux/mvebu/patches-5.4/319-ARM-dts-turris-omnia-configure-LED-2--INTn-pin-as-interrupt-pin.patch +++ /dev/null @@ -1,64 +0,0 @@ -From: "Marek Behún" <kabel@kernel.org> -To: Gregory CLEMENT <gregory.clement@bootlin.com> -Cc: "Marek Behún" <kabel@kernel.org>, Rui Salvaterra <rsalvaterra@gmail.com>, "Uwe Kleine-König" <uwe@kleine-koenig.org>, linux-arm-kernel@lists.infradead.org, Andrew Lunn <andrew@lunn.ch>, stable@vger.kernel.org -Subject: [PATCH mvebu-dt] ARM: dts: turris-omnia: configure LED[2]/INTn pin as interrupt pin -Date: Sun, 21 Feb 2021 00:11:44 +0100 -Message-Id: <20210220231144.32325-1-kabel@kernel.org> -X-Mailer: git-send-email 2.26.2 -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Use the `marvell,reg-init` DT property to configure the LED[2]/INTn pin -of the Marvell 88E1514 ethernet PHY on Turris Omnia into interrupt mode. - -Without this the pin is by default in LED[2] mode, and the Marvell PHY -driver configures LED[2] into "On - Link, Blink - Activity" mode. - -This fixes the issue where the pca9538 GPIO/interrupt controller (which -can't mask interrupts in HW) received too many interrupts and after a -time started ignoring the interrupt with error message: - IRQ 71: nobody cared - -There is a work in progress to have the Marvell PHY driver support -parsing PHY LED nodes from OF and registering the LEDs as Linux LED -class devices. Once this is done the PHY driver can also automatically -set the pin into INTn mode if it does not find LED[2] in OF. - -Until then, though, we fix this via `marvell,reg-init` DT property. - -Signed-off-by: Marek Behún <kabel@kernel.org> -Reported-by: Rui Salvaterra <rsalvaterra@gmail.com> -Fixes: 26ca8b52d6e1 ("ARM: dts: add support for Turris Omnia") -Cc: Uwe Kleine-König <uwe@kleine-koenig.org> -Cc: linux-arm-kernel@lists.infradead.org -Cc: Andrew Lunn <andrew@lunn.ch> -Cc: Gregory CLEMENT <gregory.clement@bootlin.com> -Cc: <stable@vger.kernel.org> - ---- - -This patch fixes bug introduced with the commit that added Turris -Omnia's DTS (26ca8b52d6e1), but will not apply cleanly because there is -commit 8ee4a5f4f40d which changed node name and node compatible -property and this commit did not go into stable. - -So either commit 8ee4a5f4f40d has also to go into stable before this, or -this patch has to be fixed a little in order to apply to 4.14+. - -Please let me know how should I handle this. - ---- - arch/arm/boot/dts/armada-385-turris-omnia.dts | 1 + - 1 file changed, 1 insertion(+) - ---- a/arch/arm/boot/dts/armada-385-turris-omnia.dts -+++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts -@@ -254,6 +254,7 @@ - status = "okay"; - compatible = "ethernet-phy-id0141.0DD1", "ethernet-phy-ieee802.3-c22"; - reg = <1>; -+ marvell,reg-init = <3 18 0 0x4985>; - - /* irq is connected to &pcawan pin 7 */ - }; diff --git a/target/linux/sunxi/patches-5.4/440-add-h6-pwm.patch b/target/linux/sunxi/patches-5.4/440-add-h6-pwm.patch index 64ffd719ac..eb254ee74d 100644 --- a/target/linux/sunxi/patches-5.4/440-add-h6-pwm.patch +++ b/target/linux/sunxi/patches-5.4/440-add-h6-pwm.patch @@ -1,8 +1,17 @@ -diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c -index 581d23287333..487899d4cc3f 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c -@@ -16,6 +16,7 @@ +@@ -3,6 +3,10 @@ + * Driver for Allwinner sun4i Pulse Width Modulation Controller + * + * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com> ++ * ++ * Limitations: ++ * - When outputing the source clock directly, the PWM logic will be bypassed ++ * and the currently running period is not guaranteed to be completed + */ + + #include <linux/bitops.h> +@@ -16,6 +20,7 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -10,182 +19,70 @@ index 581d23287333..487899d4cc3f 100644 #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/time.h> -@@ -78,6 +79,7 @@ struct sun4i_pwm_data { +@@ -72,12 +77,15 @@ static const u32 prescaler_table[] = { + + struct sun4i_pwm_data { + bool has_prescaler_bypass; ++ bool has_direct_mod_clk_output; + unsigned int npwm; + }; + struct sun4i_pwm_chip { struct pwm_chip chip; ++ struct clk *bus_clk; struct clk *clk; + struct reset_control *rst; void __iomem *base; spinlock_t ctrl_lock; const struct sun4i_pwm_data *data; -@@ -364,6 +366,22 @@ static int sun4i_pwm_probe(struct platform_device *pdev) - if (IS_ERR(pwm->clk)) - return PTR_ERR(pwm->clk); - -+ pwm->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); -+ if (IS_ERR(pwm->rst)) { -+ if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) -+ dev_err(&pdev->dev, "get reset failed %pe\n", -+ pwm->rst); -+ return PTR_ERR(pwm->rst); -+ } -+ -+ /* Deassert reset */ -+ ret = reset_control_deassert(pwm->rst); -+ if (ret) { -+ dev_err(&pdev->dev, "cannot deassert reset control: %pe\n", -+ ERR_PTR(ret)); -+ return ret; -+ } -+ - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &sun4i_pwm_ops; - pwm->chip.base = -1; -@@ -376,19 +394,31 @@ static int sun4i_pwm_probe(struct platform_device *pdev) - ret = pwmchip_add(&pwm->chip); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); -- return ret; -+ goto err_pwm_add; - } - - platform_set_drvdata(pdev, pwm); - - return 0; -+ -+err_pwm_add: -+ reset_control_assert(pwm->rst); -+ -+ return ret; - } - - static int sun4i_pwm_remove(struct platform_device *pdev) - { - struct sun4i_pwm_chip *pwm = platform_get_drvdata(pdev); -+ int ret; -+ -+ ret = pwmchip_remove(&pwm->chip); -+ if (ret) -+ return ret; -+ -+ reset_control_assert(pwm->rst); +@@ -115,6 +123,20 @@ static void sun4i_pwm_get_state(struct p -- return pwmchip_remove(&pwm->chip); -+ return 0; - } - - static struct platform_driver sun4i_pwm_driver = { - -diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c -index 487899d4cc3f..80026167044b 100644 ---- a/drivers/pwm/pwm-sun4i.c -+++ b/drivers/pwm/pwm-sun4i.c -@@ -362,9 +362,34 @@ static int sun4i_pwm_probe(struct platform_device *pdev) - if (IS_ERR(pwm->base)) - return PTR_ERR(pwm->base); + val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); -- pwm->clk = devm_clk_get(&pdev->dev, NULL); -- if (IS_ERR(pwm->clk)) + /* -+ * All hardware variants need a source clock that is divided and -+ * then feeds the counter that defines the output wave form. In the -+ * device tree this clock is either unnamed or called "mod". -+ * Some variants (e.g. H6) need another clock to access the -+ * hardware registers; this is called "bus". -+ * So we request "mod" first (and ignore the corner case that a -+ * parent provides a "mod" clock while the right one would be the -+ * unnamed one of the PWM device) and if this is not found we fall -+ * back to the first clock of the PWM. ++ * PWM chapter in H6 manual has a diagram which explains that if bypass ++ * bit is set, no other setting has any meaning. Even more, experiment ++ * proved that also enable bit is ignored in this case. + */ -+ pwm->clk = devm_clk_get_optional(&pdev->dev, "mod"); -+ if (IS_ERR(pwm->clk)) { -+ if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) -+ dev_err(&pdev->dev, "get mod clock failed %pe\n", -+ pwm->clk); - return PTR_ERR(pwm->clk); ++ if ((val & BIT_CH(PWM_BYPASS, pwm->hwpwm)) && ++ sun4i_pwm->data->has_direct_mod_clk_output) { ++ state->period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, clk_rate); ++ state->duty_cycle = DIV_ROUND_UP_ULL(state->period, 2); ++ state->polarity = PWM_POLARITY_NORMAL; ++ state->enabled = true; ++ return; + } + -+ if (!pwm->clk) { -+ pwm->clk = devm_clk_get(&pdev->dev, NULL); -+ if (IS_ERR(pwm->clk)) { -+ if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) -+ dev_err(&pdev->dev, "get unnamed clock failed %pe\n", -+ pwm->clk); -+ return PTR_ERR(pwm->clk); -+ } -+ } + if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) && + sun4i_pwm->data->has_prescaler_bypass) + prescaler = 1; +@@ -146,13 +168,24 @@ static void sun4i_pwm_get_state(struct p - pwm->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); - if (IS_ERR(pwm->rst)) { - -diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c -index 80026167044b..a6727dd89e28 100644 ---- a/drivers/pwm/pwm-sun4i.c -+++ b/drivers/pwm/pwm-sun4i.c -@@ -78,6 +78,7 @@ struct sun4i_pwm_data { + static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm, + const struct pwm_state *state, +- u32 *dty, u32 *prd, unsigned int *prsclr) ++ u32 *dty, u32 *prd, unsigned int *prsclr, ++ bool *bypass) + { + u64 clk_rate, div = 0; + unsigned int pval, prescaler = 0; - struct sun4i_pwm_chip { - struct pwm_chip chip; -+ struct clk *bus_clk; - struct clk *clk; - struct reset_control *rst; - void __iomem *base; -@@ -391,6 +392,14 @@ static int sun4i_pwm_probe(struct platform_device *pdev) - } - } + clk_rate = clk_get_rate(sun4i_pwm->clk); -+ pwm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); -+ if (IS_ERR(pwm->bus_clk)) { -+ if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) -+ dev_err(&pdev->dev, "get bus clock failed %pe\n", -+ pwm->bus_clk); -+ return PTR_ERR(pwm->bus_clk); -+ } ++ *bypass = sun4i_pwm->data->has_direct_mod_clk_output && ++ state->enabled && ++ (state->period * clk_rate >= NSEC_PER_SEC) && ++ (state->period * clk_rate < 2 * NSEC_PER_SEC) && ++ (state->duty_cycle * clk_rate * 2 >= NSEC_PER_SEC); + - pwm->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); - if (IS_ERR(pwm->rst)) { - if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) -@@ -407,6 +416,17 @@ static int sun4i_pwm_probe(struct platform_device *pdev) - return ret; - } - -+ /* -+ * We're keeping the bus clock on for the sake of simplicity. -+ * Actually it only needs to be on for hardware register accesses. -+ */ -+ ret = clk_prepare_enable(pwm->bus_clk); -+ if (ret) { -+ dev_err(&pdev->dev, "cannot prepare and enable bus_clk %pe\n", -+ ERR_PTR(ret)); -+ goto err_bus; -+ } ++ /* Skip calculation of other parameters if we bypass them */ ++ if (*bypass) ++ return 0; + - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &sun4i_pwm_ops; - pwm->chip.base = -1; -@@ -427,6 +447,8 @@ static int sun4i_pwm_probe(struct platform_device *pdev) - return 0; - - err_pwm_add: -+ clk_disable_unprepare(pwm->bus_clk); -+err_bus: - reset_control_assert(pwm->rst); - - return ret; -@@ -441,6 +463,7 @@ static int sun4i_pwm_remove(struct platform_device *pdev) - if (ret) - return ret; - -+ clk_disable_unprepare(pwm->bus_clk); - reset_control_assert(pwm->rst); - - return 0; - -diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c -index a6727dd89e28..e369b5a398f4 100644 ---- a/drivers/pwm/pwm-sun4i.c -+++ b/drivers/pwm/pwm-sun4i.c -@@ -202,9 +202,9 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + if (sun4i_pwm->data->has_prescaler_bypass) { + /* First, test without any prescaler when available */ + prescaler = PWM_PRESCAL_MASK; +@@ -200,10 +233,11 @@ static int sun4i_pwm_apply(struct pwm_ch { struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); struct pwm_state cstate; @@ -195,9 +92,11 @@ index a6727dd89e28..e369b5a398f4 100644 - unsigned int delay_us; + unsigned int delay_us, prescaler; unsigned long now; ++ bool bypass; pwm_get_state(pwm, &cstate); -@@ -220,43 +220,37 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + +@@ -218,43 +252,50 @@ static int sun4i_pwm_apply(struct pwm_ch spin_lock(&sun4i_pwm->ctrl_lock); ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); @@ -205,30 +104,40 @@ index a6727dd89e28..e369b5a398f4 100644 - (cstate.duty_cycle != state->duty_cycle)) { - u32 period, duty, val; - unsigned int prescaler; -- ++ ret = sun4i_pwm_calculate(sun4i_pwm, state, &duty, &period, &prescaler, ++ &bypass); ++ if (ret) { ++ dev_err(chip->dev, "period exceeds the maximum value\n"); ++ spin_unlock(&sun4i_pwm->ctrl_lock); ++ if (!cstate.enabled) ++ clk_disable_unprepare(sun4i_pwm->clk); ++ return ret; ++ } + - ret = sun4i_pwm_calculate(sun4i_pwm, state, - &duty, &period, &prescaler); - if (ret) { - dev_err(chip->dev, "period exceeds the maximum value\n"); -- spin_unlock(&sun4i_pwm->ctrl_lock); ++ if (sun4i_pwm->data->has_direct_mod_clk_output) { ++ if (bypass) { ++ ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm); ++ /* We can skip other parameter */ ++ sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); + spin_unlock(&sun4i_pwm->ctrl_lock); - if (!cstate.enabled) - clk_disable_unprepare(sun4i_pwm->clk); - return ret; -- } -- ++ return 0; ++ } else { ++ ctrl &= ~BIT_CH(PWM_BYPASS, pwm->hwpwm); + } ++ } + - if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) { - /* Prescaler changed, the clock has to be gated */ - ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); - sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); -+ ret = sun4i_pwm_calculate(sun4i_pwm, state, &duty, &period, &prescaler); -+ if (ret) { -+ dev_err(chip->dev, "period exceeds the maximum value\n"); -+ spin_unlock(&sun4i_pwm->ctrl_lock); -+ if (!cstate.enabled) -+ clk_disable_unprepare(sun4i_pwm->clk); -+ return ret; -+ } - +- - ctrl &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm); - ctrl |= BIT_CH(prescaler, pwm->hwpwm); - } @@ -262,120 +171,7 @@ index a6727dd89e28..e369b5a398f4 100644 if (state->enabled) { ctrl |= BIT_CH(PWM_EN, pwm->hwpwm); } else if (!sun4i_pwm->needs_delay[pwm->hwpwm]) { - -diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c -index e369b5a398f4..07bf7be6074b 100644 ---- a/drivers/pwm/pwm-sun4i.c -+++ b/drivers/pwm/pwm-sun4i.c -@@ -3,6 +3,10 @@ - * Driver for Allwinner sun4i Pulse Width Modulation Controller - * - * Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com> -+ * -+ * Limitations: -+ * - When outputing the source clock directly, the PWM logic will be bypassed -+ * and the currently running period is not guaranteed to be completed - */ - - #include <linux/bitops.h> -@@ -73,6 +77,7 @@ static const u32 prescaler_table[] = { - - struct sun4i_pwm_data { - bool has_prescaler_bypass; -+ bool has_direct_mod_clk_output; - unsigned int npwm; - }; - -@@ -118,6 +123,20 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, - - val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - -+ /* -+ * PWM chapter in H6 manual has a diagram which explains that if bypass -+ * bit is set, no other setting has any meaning. Even more, experiment -+ * proved that also enable bit is ignored in this case. -+ */ -+ if ((val & BIT_CH(PWM_BYPASS, pwm->hwpwm)) && -+ sun4i_pwm->data->has_direct_mod_clk_output) { -+ state->period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, clk_rate); -+ state->duty_cycle = DIV_ROUND_UP_ULL(state->period, 2); -+ state->polarity = PWM_POLARITY_NORMAL; -+ state->enabled = true; -+ return; -+ } -+ - if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) && - sun4i_pwm->data->has_prescaler_bypass) - prescaler = 1; -@@ -149,13 +168,24 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip, - - static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm, - const struct pwm_state *state, -- u32 *dty, u32 *prd, unsigned int *prsclr) -+ u32 *dty, u32 *prd, unsigned int *prsclr, -+ bool *bypass) - { - u64 clk_rate, div = 0; - unsigned int pval, prescaler = 0; - - clk_rate = clk_get_rate(sun4i_pwm->clk); - -+ *bypass = sun4i_pwm->data->has_direct_mod_clk_output && -+ state->enabled && -+ (state->period * clk_rate >= NSEC_PER_SEC) && -+ (state->period * clk_rate < 2 * NSEC_PER_SEC) && -+ (state->duty_cycle * clk_rate * 2 >= NSEC_PER_SEC); -+ -+ /* Skip calculation of other parameters if we bypass them */ -+ if (*bypass) -+ return 0; -+ - if (sun4i_pwm->data->has_prescaler_bypass) { - /* First, test without any prescaler when available */ - prescaler = PWM_PRESCAL_MASK; -@@ -206,6 +236,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - int ret; - unsigned int delay_us, prescaler; - unsigned long now; -+ bool bypass; - - pwm_get_state(pwm, &cstate); - -@@ -220,7 +251,8 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - spin_lock(&sun4i_pwm->ctrl_lock); - ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - -- ret = sun4i_pwm_calculate(sun4i_pwm, state, &duty, &period, &prescaler); -+ ret = sun4i_pwm_calculate(sun4i_pwm, state, &duty, &period, &prescaler, -+ &bypass); - if (ret) { - dev_err(chip->dev, "period exceeds the maximum value\n"); - spin_unlock(&sun4i_pwm->ctrl_lock); -@@ -229,6 +261,18 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - return ret; - } - -+ if (sun4i_pwm->data->has_direct_mod_clk_output) { -+ if (bypass) { -+ ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm); -+ /* We can skip other parameter */ -+ sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG); -+ spin_unlock(&sun4i_pwm->ctrl_lock); -+ return 0; -+ } else { -+ ctrl &= ~BIT_CH(PWM_BYPASS, pwm->hwpwm); -+ } -+ } -+ - if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) { - /* Prescaler changed, the clock has to be gated */ - ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); - -diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c -index 07bf7be6074b..c394878c7e5d 100644 ---- a/drivers/pwm/pwm-sun4i.c -+++ b/drivers/pwm/pwm-sun4i.c -@@ -360,6 +360,12 @@ static const struct sun4i_pwm_data sun4i_pwm_single_bypass = { +@@ -320,6 +361,12 @@ static const struct sun4i_pwm_data sun4i .npwm = 1, }; @@ -388,13 +184,122 @@ index 07bf7be6074b..c394878c7e5d 100644 static const struct of_device_id sun4i_pwm_dt_ids[] = { { .compatible = "allwinner,sun4i-a10-pwm", -@@ -376,6 +382,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = { - }, { +@@ -337,6 +384,9 @@ static const struct of_device_id sun4i_p .compatible = "allwinner,sun8i-h3-pwm", .data = &sun4i_pwm_single_bypass, -+ }, { + }, { + .compatible = "allwinner,sun50i-h6-pwm", + .data = &sun50i_h6_pwm_data, - }, { ++ }, { /* sentinel */ }, + }; +@@ -361,9 +411,69 @@ static int sun4i_pwm_probe(struct platfo + if (IS_ERR(pwm->base)) + return PTR_ERR(pwm->base); + +- pwm->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(pwm->clk)) ++ /* ++ * All hardware variants need a source clock that is divided and ++ * then feeds the counter that defines the output wave form. In the ++ * device tree this clock is either unnamed or called "mod". ++ * Some variants (e.g. H6) need another clock to access the ++ * hardware registers; this is called "bus". ++ * So we request "mod" first (and ignore the corner case that a ++ * parent provides a "mod" clock while the right one would be the ++ * unnamed one of the PWM device) and if this is not found we fall ++ * back to the first clock of the PWM. ++ */ ++ pwm->clk = devm_clk_get_optional(&pdev->dev, "mod"); ++ if (IS_ERR(pwm->clk)) { ++ if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "get mod clock failed %pe\n", ++ pwm->clk); + return PTR_ERR(pwm->clk); ++ } ++ ++ if (!pwm->clk) { ++ pwm->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(pwm->clk)) { ++ if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "get unnamed clock failed %pe\n", ++ pwm->clk); ++ return PTR_ERR(pwm->clk); ++ } ++ } ++ ++ pwm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); ++ if (IS_ERR(pwm->bus_clk)) { ++ if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "get bus clock failed %pe\n", ++ pwm->bus_clk); ++ return PTR_ERR(pwm->bus_clk); ++ } ++ ++ pwm->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); ++ if (IS_ERR(pwm->rst)) { ++ if (PTR_ERR(pwm->rst) != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "get reset failed %pe\n", ++ pwm->rst); ++ return PTR_ERR(pwm->rst); ++ } ++ ++ /* Deassert reset */ ++ ret = reset_control_deassert(pwm->rst); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot deassert reset control: %pe\n", ++ ERR_PTR(ret)); ++ return ret; ++ } ++ ++ /* ++ * We're keeping the bus clock on for the sake of simplicity. ++ * Actually it only needs to be on for hardware register accesses. ++ */ ++ ret = clk_prepare_enable(pwm->bus_clk); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot prepare and enable bus_clk %pe\n", ++ ERR_PTR(ret)); ++ goto err_bus; ++ } + + pwm->chip.dev = &pdev->dev; + pwm->chip.ops = &sun4i_pwm_ops; +@@ -377,19 +487,34 @@ static int sun4i_pwm_probe(struct platfo + ret = pwmchip_add(&pwm->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); +- return ret; ++ goto err_pwm_add; + } + + platform_set_drvdata(pdev, pwm); + + return 0; ++ ++err_pwm_add: ++ clk_disable_unprepare(pwm->bus_clk); ++err_bus: ++ reset_control_assert(pwm->rst); ++ ++ return ret; + } + + static int sun4i_pwm_remove(struct platform_device *pdev) + { + struct sun4i_pwm_chip *pwm = platform_get_drvdata(pdev); ++ int ret; ++ ++ ret = pwmchip_remove(&pwm->chip); ++ if (ret) ++ return ret; + +- return pwmchip_remove(&pwm->chip); ++ clk_disable_unprepare(pwm->bus_clk); ++ reset_control_assert(pwm->rst); ++ ++ return 0; + } + + static struct platform_driver sun4i_pwm_driver = { diff --git a/target/linux/sunxi/patches-5.4/441-arm64-dts-add-PWM-node.patch b/target/linux/sunxi/patches-5.4/441-arm64-dts-add-PWM-node.patch index 60e653dcba..c14e729539 100644 --- a/target/linux/sunxi/patches-5.4/441-arm64-dts-add-PWM-node.patch +++ b/target/linux/sunxi/patches-5.4/441-arm64-dts-add-PWM-node.patch @@ -14,11 +14,9 @@ Signed-off-by: Clément Péron <peron.clem@gmail.com> arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -index 29824081b43b..6d4bde488f15 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -@@ -245,6 +245,16 @@ +@@ -231,6 +231,16 @@ status = "disabled"; }; diff --git a/target/linux/sunxi/patches-5.4/442-arm64-dts-orangepi-one-plus-enable-PWM.patch b/target/linux/sunxi/patches-5.4/442-arm64-dts-orangepi-one-plus-enable-PWM.patch index d95c3c8987..8ebbe3fad8 100644 --- a/target/linux/sunxi/patches-5.4/442-arm64-dts-orangepi-one-plus-enable-PWM.patch +++ b/target/linux/sunxi/patches-5.4/442-arm64-dts-orangepi-one-plus-enable-PWM.patch @@ -1,10 +1,8 @@ -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-one-plus.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-one-plus.dts -index f335f7482a73..cf684bc7374d 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-one-plus.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-one-plus.dts -@@ -45,3 +45,7 @@ - reg = <1>; - }; +@@ -10,3 +10,7 @@ + model = "OrangePi One Plus"; + compatible = "xunlong,orangepi-one-plus", "allwinner,sun50i-h6"; }; + +&pwm { diff --git a/target/linux/sunxi/patches-5.4/443-board-h6-orangepioneplus-fix-missing-ethernet.patch b/target/linux/sunxi/patches-5.4/443-board-h6-orangepioneplus-fix-missing-ethernet.patch index 5f7974c609..88c466d9b9 100644 --- a/target/linux/sunxi/patches-5.4/443-board-h6-orangepioneplus-fix-missing-ethernet.patch +++ b/target/linux/sunxi/patches-5.4/443-board-h6-orangepioneplus-fix-missing-ethernet.patch @@ -1,8 +1,6 @@ -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-one-plus.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-one-plus.dts -index 12e1756..79139f3 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-one-plus.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-one-plus.dts -@@ -9,4 +9,39 @@ +@@ -9,6 +9,41 @@ / { model = "OrangePi One Plus"; compatible = "xunlong,orangepi-one-plus", "allwinner,sun50i-h6"; @@ -42,3 +40,5 @@ index 12e1756..79139f3 100644 + reg = <1>; + }; }; + + &pwm { |