From c559641978d9bffa6dcf9e7528f9a875449431c1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 24 Jul 2022 17:12:07 -0500 Subject: [PATCH 001/153] dt-bindings: net: bluetooth: realtek: Add RTL8723DS RTL8723DS is another version of the RTL8723 WiFi + Bluetooth chip. It is already supported by the hci_uart/btrtl driver. Document the compatible. Series-to: Marcel Holtmann Series-to: Johan Hedberg Series-to: Luiz Augusto von Dentz Series-to: David S. Miller Series-to: Eric Dumazet Series-to: Jakub Kicinski Series-to: Paolo Abeni Series-cc: linux-bluetooth@vger.kernel.org Signed-off-by: Samuel Holland --- Documentation/devicetree/bindings/net/realtek-bluetooth.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml b/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml index 157d606bf9cb6f..8ac633b7e91735 100644 --- a/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml +++ b/Documentation/devicetree/bindings/net/realtek-bluetooth.yaml @@ -20,6 +20,7 @@ properties: enum: - realtek,rtl8723bs-bt - realtek,rtl8723cs-bt + - realtek,rtl8723ds-bt - realtek,rtl8822cs-bt device-wake-gpios: From 2202d161785165de044276ec643ae7d3749a1bb8 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 13 Nov 2021 11:39:01 -0600 Subject: [PATCH 002/153] bus: sun50i-de2: Prevent driver from being unbound Currently, the driver can be unbound via sysfs. Because it does not call of_platform_depopulate, unbinding the driver will ... Signed-off-by: Samuel Holland --- drivers/bus/sun50i-de2.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/bus/sun50i-de2.c b/drivers/bus/sun50i-de2.c index 414f29cdedf0db..3b070f0304e710 100644 --- a/drivers/bus/sun50i-de2.c +++ b/drivers/bus/sun50i-de2.c @@ -24,12 +24,6 @@ static int sun50i_de2_bus_probe(struct platform_device *pdev) return 0; } -static int sun50i_de2_bus_remove(struct platform_device *pdev) -{ - sunxi_sram_release(&pdev->dev); - return 0; -} - static const struct of_device_id sun50i_de2_bus_of_match[] = { { .compatible = "allwinner,sun50i-a64-de2", }, { /* sentinel */ } @@ -37,9 +31,9 @@ static const struct of_device_id sun50i_de2_bus_of_match[] = { static struct platform_driver sun50i_de2_bus_driver = { .probe = sun50i_de2_bus_probe, - .remove = sun50i_de2_bus_remove, .driver = { .name = "sun50i-de2-bus", + .suppress_bind_attrs = true, .of_match_table = sun50i_de2_bus_of_match, }, }; From d2e2470e978a33a5d3cd69d39e1918f1bfe34f02 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Apr 2022 00:02:08 -0500 Subject: [PATCH 003/153] drm/sun4i: sun8i-hdmi-phy: Use of_device_get_match_data Now that the HDMI PHY is using a platform driver, we can use the usual helper function for getting the variant structure. Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 2 +- drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index 9ad09522947a5e..c480a0514560fe 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -172,7 +172,7 @@ struct sun8i_hdmi_phy { unsigned int rcal; struct regmap *regs; struct reset_control *rst_phy; - struct sun8i_hdmi_phy_variant *variant; + const struct sun8i_hdmi_phy_variant *variant; }; struct sun8i_dw_hdmi_quirks { diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 2860e6bff8b775..4553e04144fe54 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -565,7 +565,7 @@ void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy) void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy, struct dw_hdmi_plat_data *plat_data) { - struct sun8i_hdmi_phy_variant *variant = phy->variant; + const struct sun8i_hdmi_phy_variant *variant = phy->variant; if (variant->is_custom_phy) { plat_data->phy_ops = &sun8i_hdmi_phy_ops; @@ -672,7 +672,6 @@ int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node) static int sun8i_hdmi_phy_probe(struct platform_device *pdev) { - const struct of_device_id *match; struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct sun8i_hdmi_phy *phy; @@ -680,17 +679,11 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev) void __iomem *regs; int ret; - match = of_match_node(sun8i_hdmi_phy_of_table, node); - if (!match) { - dev_err(dev, "Incompatible HDMI PHY\n"); - return -EINVAL; - } - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); if (!phy) return -ENOMEM; - phy->variant = (struct sun8i_hdmi_phy_variant *)match->data; + phy->variant = of_device_get_match_data(dev); phy->dev = dev; ret = of_address_to_resource(node, 0, &res); From 1ae5c7bcc69e757b3bd98b27982f45e58879d975 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Apr 2022 00:04:22 -0500 Subject: [PATCH 004/153] drm/sun4i: sun8i-hdmi-phy: Use devm_platform_ioremap_resource The struct resource is not used for anything else, so we can simplify the code a bit by using the helper function. Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 4553e04144fe54..10504c2de1326e 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -675,7 +675,6 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct sun8i_hdmi_phy *phy; - struct resource res; void __iomem *regs; int ret; @@ -686,13 +685,7 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev) phy->variant = of_device_get_match_data(dev); phy->dev = dev; - ret = of_address_to_resource(node, 0, &res); - if (ret) { - dev_err(dev, "phy: Couldn't get our resources\n"); - return ret; - } - - regs = devm_ioremap_resource(dev, &res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) { dev_err(dev, "Couldn't map the HDMI PHY registers\n"); return PTR_ERR(regs); From 68bc0524784939331181437496fb600847dfb21d Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Apr 2022 11:33:09 -0500 Subject: [PATCH 005/153] drm/sun4i: sun8i-hdmi-phy: Used device-managed clocks/resets Now that the HDMI PHY is using a platform driver, it can use device- managed resources. Use these, as well as the dev_err_probe helper, to simplify the probe function and get rid of the remove function. Series-changes: 2 - Move error handling inside variant checks in probe function Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 90 ++++++++------------------ 1 file changed, 26 insertions(+), 64 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 10504c2de1326e..9086ce547fad89 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -673,10 +673,8 @@ int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node) static int sun8i_hdmi_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; struct sun8i_hdmi_phy *phy; void __iomem *regs; - int ret; phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); if (!phy) @@ -686,88 +684,52 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev) phy->dev = dev; regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(regs)) { - dev_err(dev, "Couldn't map the HDMI PHY registers\n"); - return PTR_ERR(regs); - } + if (IS_ERR(regs)) + return dev_err_probe(dev, PTR_ERR(regs), + "Couldn't map the HDMI PHY registers\n"); phy->regs = devm_regmap_init_mmio(dev, regs, &sun8i_hdmi_phy_regmap_config); - if (IS_ERR(phy->regs)) { - dev_err(dev, "Couldn't create the HDMI PHY regmap\n"); - return PTR_ERR(phy->regs); - } + if (IS_ERR(phy->regs)) + return dev_err_probe(dev, PTR_ERR(phy->regs), + "Couldn't create the HDMI PHY regmap\n"); - phy->clk_bus = of_clk_get_by_name(node, "bus"); - if (IS_ERR(phy->clk_bus)) { - dev_err(dev, "Could not get bus clock\n"); - return PTR_ERR(phy->clk_bus); - } + phy->clk_bus = devm_clk_get(dev, "bus"); + if (IS_ERR(phy->clk_bus)) + return dev_err_probe(dev, PTR_ERR(phy->clk_bus), + "Could not get bus clock\n"); - phy->clk_mod = of_clk_get_by_name(node, "mod"); - if (IS_ERR(phy->clk_mod)) { - dev_err(dev, "Could not get mod clock\n"); - ret = PTR_ERR(phy->clk_mod); - goto err_put_clk_bus; - } + phy->clk_mod = devm_clk_get(dev, "mod"); + if (IS_ERR(phy->clk_mod)) + return dev_err_probe(dev, PTR_ERR(phy->clk_mod), + "Could not get mod clock\n"); if (phy->variant->has_phy_clk) { - phy->clk_pll0 = of_clk_get_by_name(node, "pll-0"); - if (IS_ERR(phy->clk_pll0)) { - dev_err(dev, "Could not get pll-0 clock\n"); - ret = PTR_ERR(phy->clk_pll0); - goto err_put_clk_mod; - } + phy->clk_pll0 = devm_clk_get(dev, "pll-0"); + if (IS_ERR(phy->clk_pll0)) + return dev_err_probe(dev, PTR_ERR(phy->clk_pll0), + "Could not get pll-0 clock\n"); if (phy->variant->has_second_pll) { - phy->clk_pll1 = of_clk_get_by_name(node, "pll-1"); - if (IS_ERR(phy->clk_pll1)) { - dev_err(dev, "Could not get pll-1 clock\n"); - ret = PTR_ERR(phy->clk_pll1); - goto err_put_clk_pll0; - } + phy->clk_pll1 = devm_clk_get(dev, "pll-1"); + if (IS_ERR(phy->clk_pll1)) + return dev_err_probe(dev, PTR_ERR(phy->clk_pll1), + "Could not get pll-1 clock\n"); } } - phy->rst_phy = of_reset_control_get_shared(node, "phy"); - if (IS_ERR(phy->rst_phy)) { - dev_err(dev, "Could not get phy reset control\n"); - ret = PTR_ERR(phy->rst_phy); - goto err_put_clk_pll1; - } + phy->rst_phy = devm_reset_control_get_shared(dev, "phy"); + if (IS_ERR(phy->rst_phy)) + return dev_err_probe(dev, PTR_ERR(phy->rst_phy), + "Could not get phy reset control\n"); platform_set_drvdata(pdev, phy); return 0; - -err_put_clk_pll1: - clk_put(phy->clk_pll1); -err_put_clk_pll0: - clk_put(phy->clk_pll0); -err_put_clk_mod: - clk_put(phy->clk_mod); -err_put_clk_bus: - clk_put(phy->clk_bus); - - return ret; -} - -static int sun8i_hdmi_phy_remove(struct platform_device *pdev) -{ - struct sun8i_hdmi_phy *phy = platform_get_drvdata(pdev); - - reset_control_put(phy->rst_phy); - - clk_put(phy->clk_pll0); - clk_put(phy->clk_pll1); - clk_put(phy->clk_mod); - clk_put(phy->clk_bus); - return 0; } struct platform_driver sun8i_hdmi_phy_driver = { .probe = sun8i_hdmi_phy_probe, - .remove = sun8i_hdmi_phy_remove, .driver = { .name = "sun8i-hdmi-phy", .of_match_table = sun8i_hdmi_phy_of_table, From 058f35a39b4ed0472a4dac90ef67dba0cd1f86a9 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Apr 2022 11:44:56 -0500 Subject: [PATCH 006/153] drm/sun4i: sun8i-hdmi-phy: Support multiple custom PHY ops The D1 SoC comes with a new custom HDMI PHY, which does not share any registers with the existing custom PHY. So it needs a new set of ops. Instead of providing a flag in the variant structure, provide the ops themselves. Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 2 +- drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index c480a0514560fe..1000e86f656f90 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -150,10 +150,10 @@ struct sun8i_hdmi_phy; struct sun8i_hdmi_phy_variant { bool has_phy_clk; bool has_second_pll; - unsigned int is_custom_phy : 1; const struct dw_hdmi_curr_ctrl *cur_ctr; const struct dw_hdmi_mpll_config *mpll_cfg; const struct dw_hdmi_phy_config *phy_cfg; + const struct dw_hdmi_phy_ops *phy_ops; void (*phy_init)(struct sun8i_hdmi_phy *phy); void (*phy_disable)(struct dw_hdmi *hdmi, struct sun8i_hdmi_phy *phy); diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 9086ce547fad89..e6d25bbe3d2f3c 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -567,8 +567,8 @@ void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy, { const struct sun8i_hdmi_phy_variant *variant = phy->variant; - if (variant->is_custom_phy) { - plat_data->phy_ops = &sun8i_hdmi_phy_ops; + if (variant->phy_ops) { + plat_data->phy_ops = variant->phy_ops; plat_data->phy_name = "sun8i_dw_hdmi_phy"; plat_data->phy_data = phy; } else { @@ -587,7 +587,7 @@ static const struct regmap_config sun8i_hdmi_phy_regmap_config = { }; static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = { - .is_custom_phy = true, + .phy_ops = &sun8i_hdmi_phy_ops, .phy_init = &sun8i_hdmi_phy_init_a83t, .phy_disable = &sun8i_hdmi_phy_disable_a83t, .phy_config = &sun8i_hdmi_phy_config_a83t, @@ -595,7 +595,7 @@ static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = { static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = { .has_phy_clk = true, - .is_custom_phy = true, + .phy_ops = &sun8i_hdmi_phy_ops, .phy_init = &sun8i_hdmi_phy_init_h3, .phy_disable = &sun8i_hdmi_phy_disable_h3, .phy_config = &sun8i_hdmi_phy_config_h3, @@ -604,7 +604,7 @@ static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = { static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = { .has_phy_clk = true, .has_second_pll = true, - .is_custom_phy = true, + .phy_ops = &sun8i_hdmi_phy_ops, .phy_init = &sun8i_hdmi_phy_init_h3, .phy_disable = &sun8i_hdmi_phy_disable_h3, .phy_config = &sun8i_hdmi_phy_config_h3, @@ -612,7 +612,7 @@ static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = { static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = { .has_phy_clk = true, - .is_custom_phy = true, + .phy_ops = &sun8i_hdmi_phy_ops, .phy_init = &sun8i_hdmi_phy_init_h3, .phy_disable = &sun8i_hdmi_phy_disable_h3, .phy_config = &sun8i_hdmi_phy_config_h3, From a31176321069d4a55c5ac6a4f2336feed91e17f3 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Apr 2022 15:10:13 -0500 Subject: [PATCH 007/153] drm/sun4i: sun8i-hdmi-phy: Separate A83T and H3 PHY ops Since the driver already needs to support multiple sets of ops, we can drop the mid-layer used by the A83T and H3 PHYs. They share only a small amount of code; factor this out as sun8i_hdmi_phy_set_polarity. For clarity, this commit keeps the existing function order. Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 5 -- drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 89 +++++++++++++------------- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index 1000e86f656f90..ab80d52a70bbee 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -155,11 +155,6 @@ struct sun8i_hdmi_phy_variant { const struct dw_hdmi_phy_config *phy_cfg; const struct dw_hdmi_phy_ops *phy_ops; void (*phy_init)(struct sun8i_hdmi_phy *phy); - void (*phy_disable)(struct dw_hdmi *hdmi, - struct sun8i_hdmi_phy *phy); - int (*phy_config)(struct dw_hdmi *hdmi, - struct sun8i_hdmi_phy *phy, - unsigned int clk_rate); }; struct sun8i_hdmi_phy { diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index e6d25bbe3d2f3c..f94c1ddddbad99 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -123,10 +123,18 @@ static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = { { ~0UL, 0x0000, 0x0000, 0x0000} }; -static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi, - struct sun8i_hdmi_phy *phy, - unsigned int clk_rate) +static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy, + const struct drm_display_mode *mode); + +static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *display, + const struct drm_display_mode *mode) { + unsigned int clk_rate = mode->crtc_clock * 1000; + struct sun8i_hdmi_phy *phy = data; + + sun8i_hdmi_phy_set_polarity(phy, mode); + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN); @@ -185,10 +193,12 @@ static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi, return 0; } -static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi, - struct sun8i_hdmi_phy *phy, - unsigned int clk_rate) +static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *display, + const struct drm_display_mode *mode) { + unsigned int clk_rate = mode->crtc_clock * 1000; + struct sun8i_hdmi_phy *phy = data; u32 pll_cfg1_init; u32 pll_cfg2_init; u32 ana_cfg1_end; @@ -197,6 +207,11 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi, u32 b_offset = 0; u32 val; + if (phy->variant->has_phy_clk) + clk_set_rate(phy->clk_phy, clk_rate); + + sun8i_hdmi_phy_set_polarity(phy, mode); + /* bandwidth / frequency independent settings */ pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN | @@ -333,11 +348,9 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi, return 0; } -static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, - const struct drm_display_info *display, - const struct drm_display_mode *mode) +static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy, + const struct drm_display_mode *mode) { - struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data; u32 val = 0; if (mode->flags & DRM_MODE_FLAG_NHSYNC) @@ -348,16 +361,12 @@ static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val); - - if (phy->variant->has_phy_clk) - clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000); - - return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000); }; -static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi, - struct sun8i_hdmi_phy *phy) +static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) { + struct sun8i_hdmi_phy *phy = data; + dw_hdmi_phy_gen2_txpwron(hdmi, 0); dw_hdmi_phy_gen2_pddq(hdmi, 1); @@ -365,9 +374,10 @@ static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi, SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0); } -static void sun8i_hdmi_phy_disable_h3(struct dw_hdmi *hdmi, - struct sun8i_hdmi_phy *phy) +static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) { + struct sun8i_hdmi_phy *phy = data; + regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, SUN8I_HDMI_PHY_ANA_CFG1_LDOEN | SUN8I_HDMI_PHY_ANA_CFG1_ENVBS | @@ -375,19 +385,20 @@ static void sun8i_hdmi_phy_disable_h3(struct dw_hdmi *hdmi, regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0); } -static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) -{ - struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data; - - phy->variant->phy_disable(hdmi, phy); -} +static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = { + .init = sun8i_a83t_hdmi_phy_config, + .disable = sun8i_a83t_hdmi_phy_disable, + .read_hpd = dw_hdmi_phy_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_phy_setup_hpd, +}; -static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = { - .init = &sun8i_hdmi_phy_config, - .disable = &sun8i_hdmi_phy_disable, - .read_hpd = &dw_hdmi_phy_read_hpd, - .update_hpd = &dw_hdmi_phy_update_hpd, - .setup_hpd = &dw_hdmi_phy_setup_hpd, +static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = { + .init = sun8i_h3_hdmi_phy_config, + .disable = sun8i_h3_hdmi_phy_disable, + .read_hpd = dw_hdmi_phy_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_phy_setup_hpd, }; static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy) @@ -587,35 +598,27 @@ static const struct regmap_config sun8i_hdmi_phy_regmap_config = { }; static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = { - .phy_ops = &sun8i_hdmi_phy_ops, + .phy_ops = &sun8i_a83t_hdmi_phy_ops, .phy_init = &sun8i_hdmi_phy_init_a83t, - .phy_disable = &sun8i_hdmi_phy_disable_a83t, - .phy_config = &sun8i_hdmi_phy_config_a83t, }; static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = { .has_phy_clk = true, - .phy_ops = &sun8i_hdmi_phy_ops, + .phy_ops = &sun8i_h3_hdmi_phy_ops, .phy_init = &sun8i_hdmi_phy_init_h3, - .phy_disable = &sun8i_hdmi_phy_disable_h3, - .phy_config = &sun8i_hdmi_phy_config_h3, }; static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = { .has_phy_clk = true, .has_second_pll = true, - .phy_ops = &sun8i_hdmi_phy_ops, + .phy_ops = &sun8i_h3_hdmi_phy_ops, .phy_init = &sun8i_hdmi_phy_init_h3, - .phy_disable = &sun8i_hdmi_phy_disable_h3, - .phy_config = &sun8i_hdmi_phy_config_h3, }; static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = { .has_phy_clk = true, - .phy_ops = &sun8i_hdmi_phy_ops, + .phy_ops = &sun8i_h3_hdmi_phy_ops, .phy_init = &sun8i_hdmi_phy_init_h3, - .phy_disable = &sun8i_hdmi_phy_disable_h3, - .phy_config = &sun8i_hdmi_phy_config_h3, }; static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = { From 95cd2d843b0735bcb9a340c67ed697bffe127db7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Apr 2022 15:11:51 -0500 Subject: [PATCH 008/153] drm/sun4i: sun8i-hdmi-phy: Group PHY ops functions by generation Now that the PHY ops are separated, sort them topologically, with the common sun8i_hdmi_phy_set_polarity helper at the top. No function contents are changed in this commit. Cover-letter: drm/sun4i: HDMI PHY cleanup/refactoring This series prepares the sun8i HDMI PHY driver for supporting the new custom PHY in the Allwinner D1 SoC. No functional change intended here. This series was tested on D1, H3, and H6. END Series-version: 2 Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Series-to: Maxime Ripard Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 67 ++++++++++++-------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index f94c1ddddbad99..ca53b5e9fffca7 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -124,7 +124,19 @@ static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = { }; static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy, - const struct drm_display_mode *mode); + const struct drm_display_mode *mode) +{ + u32 val = 0; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC; + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC; + + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, + SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val); +}; static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, const struct drm_display_info *display, @@ -193,6 +205,25 @@ static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, return 0; } +static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) +{ + struct sun8i_hdmi_phy *phy = data; + + dw_hdmi_phy_gen2_txpwron(hdmi, 0); + dw_hdmi_phy_gen2_pddq(hdmi, 1); + + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, + SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0); +} + +static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = { + .init = sun8i_a83t_hdmi_phy_config, + .disable = sun8i_a83t_hdmi_phy_disable, + .read_hpd = dw_hdmi_phy_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_phy_setup_hpd, +}; + static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, const struct drm_display_info *display, const struct drm_display_mode *mode) @@ -348,32 +379,6 @@ static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, return 0; } -static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy, - const struct drm_display_mode *mode) -{ - u32 val = 0; - - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC; - - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC; - - regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, - SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val); -}; - -static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) -{ - struct sun8i_hdmi_phy *phy = data; - - dw_hdmi_phy_gen2_txpwron(hdmi, 0); - dw_hdmi_phy_gen2_pddq(hdmi, 1); - - regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, - SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0); -} - static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) { struct sun8i_hdmi_phy *phy = data; @@ -385,14 +390,6 @@ static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0); } -static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = { - .init = sun8i_a83t_hdmi_phy_config, - .disable = sun8i_a83t_hdmi_phy_disable, - .read_hpd = dw_hdmi_phy_read_hpd, - .update_hpd = dw_hdmi_phy_update_hpd, - .setup_hpd = dw_hdmi_phy_setup_hpd, -}; - static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = { .init = sun8i_h3_hdmi_phy_config, .disable = sun8i_h3_hdmi_phy_disable, From 1a556fd9e9021b799fff3278762471915718197b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 31 Mar 2022 23:43:15 -0500 Subject: [PATCH 009/153] dt-bindings: display: Add D1 HDMI compatibles Allwinner D1 contains a HDMI controller with some changes in platform integration, and a new HDMI PHY. Add their compatibles. Signed-off-by: Samuel Holland --- .../bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml | 1 + .../bindings/display/allwinner,sun8i-a83t-hdmi-phy.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml index 4951b5ef5c6af4..7921c864747755 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml @@ -26,6 +26,7 @@ properties: compatible: oneOf: - const: allwinner,sun8i-a83t-dw-hdmi + - const: allwinner,sun20i-d1-dw-hdmi - const: allwinner,sun50i-h6-dw-hdmi - items: diff --git a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-hdmi-phy.yaml b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-hdmi-phy.yaml index a97366aaf924f0..9e444e9f6762b2 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-hdmi-phy.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-hdmi-phy.yaml @@ -19,6 +19,7 @@ properties: - allwinner,sun8i-a83t-hdmi-phy - allwinner,sun8i-h3-hdmi-phy - allwinner,sun8i-r40-hdmi-phy + - allwinner,sun20i-d1-hdmi-phy - allwinner,sun50i-a64-hdmi-phy - allwinner,sun50i-h6-hdmi-phy From fea46222e10e9b9b8955c4621ed9e2f3dd098d60 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Mar 2022 21:24:21 -0500 Subject: [PATCH 010/153] drm/sun4i: Add support for D1 HDMI D1's HDMI controller contains some platform integration changes. It now has two resets (both shared with the PHY) and no external TMDS clock. The controller also supports HDCP without an external clock or reset. While the maximum HDMI frequency is not explicity stated, the BSP PHY driver provides PLL configurations only up to 297 MHz, so use that as the max frequency. Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 28 ++++++++++++++++++++++++--- drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c index 477cb6985b4d27..bd8cf8c87748a2 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c @@ -128,12 +128,17 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, if (encoder->possible_crtcs == 0) return -EPROBE_DEFER; - hdmi->rst_ctrl = devm_reset_control_get(dev, "ctrl"); + hdmi->rst_ctrl = devm_reset_control_get_shared(dev, "ctrl"); if (IS_ERR(hdmi->rst_ctrl)) return dev_err_probe(dev, PTR_ERR(hdmi->rst_ctrl), "Could not get ctrl reset control\n"); - hdmi->clk_tmds = devm_clk_get(dev, "tmds"); + hdmi->rst_sub = devm_reset_control_get_optional_shared(dev, "sub"); + if (IS_ERR(hdmi->rst_sub)) + return dev_err_probe(dev, PTR_ERR(hdmi->rst_sub), + "Could not get sub reset control\n"); + + hdmi->clk_tmds = devm_clk_get_optional(dev, "tmds"); if (IS_ERR(hdmi->clk_tmds)) return dev_err_probe(dev, PTR_ERR(hdmi->clk_tmds), "Couldn't get the tmds clock\n"); @@ -155,10 +160,16 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, goto err_disable_regulator; } + ret = reset_control_deassert(hdmi->rst_sub); + if (ret) { + dev_err(dev, "Could not deassert sub reset control\n"); + goto err_assert_ctrl_reset; + } + ret = clk_prepare_enable(hdmi->clk_tmds); if (ret) { dev_err(dev, "Could not enable tmds clock\n"); - goto err_assert_ctrl_reset; + goto err_assert_sub_reset; } phy_node = of_parse_phandle(dev->of_node, "phys", 0); @@ -205,6 +216,8 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, drm_encoder_cleanup(encoder); err_disable_clk_tmds: clk_disable_unprepare(hdmi->clk_tmds); +err_assert_sub_reset: + reset_control_assert(hdmi->rst_sub); err_assert_ctrl_reset: reset_control_assert(hdmi->rst_ctrl); err_disable_regulator: @@ -246,6 +259,11 @@ static const struct sun8i_dw_hdmi_quirks sun8i_a83t_quirks = { .mode_valid = sun8i_dw_hdmi_mode_valid_a83t, }; +static const struct sun8i_dw_hdmi_quirks sun20i_d1_quirks = { + .mode_valid = sun8i_dw_hdmi_mode_valid_a83t, + .use_drm_infoframe = true, +}; + static const struct sun8i_dw_hdmi_quirks sun50i_h6_quirks = { .mode_valid = sun8i_dw_hdmi_mode_valid_h6, .use_drm_infoframe = true, @@ -256,6 +274,10 @@ static const struct of_device_id sun8i_dw_hdmi_dt_ids[] = { .compatible = "allwinner,sun8i-a83t-dw-hdmi", .data = &sun8i_a83t_quirks, }, + { + .compatible = "allwinner,sun20i-d1-dw-hdmi", + .data = &sun20i_d1_quirks, + }, { .compatible = "allwinner,sun50i-h6-dw-hdmi", .data = &sun50i_h6_quirks, diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index ab80d52a70bbee..9ef335b9ce4a50 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -187,6 +187,7 @@ struct sun8i_dw_hdmi { struct regulator *regulator; const struct sun8i_dw_hdmi_quirks *quirks; struct reset_control *rst_ctrl; + struct reset_control *rst_sub; }; extern struct platform_driver sun8i_hdmi_phy_driver; From 6a5ae4a890d6f10163e7dd84ea14f85b2efd86e7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Apr 2022 15:15:41 -0500 Subject: [PATCH 011/153] drm/sun4i: sun8i-hdmi-phy: Add support for D1 PHY Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 169 +++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 32 +++++ 2 files changed, 201 insertions(+) diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index 9ef335b9ce4a50..371c3d5810244b 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -145,6 +145,175 @@ #define SUN8I_HDMI_PHY_CEC_REG 0x003c +#define SUN20I_HDMI_PHY_CTL0_REG 0x0040 +#define SUN20I_HDMI_PHY_CTL0_PLL_LOCK_MODE_MAN BIT(31) +#define SUN20I_HDMI_PHY_CTL0_PLL_LOCK_MODE BIT(30) +#define SUN20I_HDMI_PHY_CTL0_FIFO_WORKC_EN BIT(29) +#define SUN20I_HDMI_PHY_CTL0_FIFO_AUTOSYNC_DIS BIT(28) +#define SUN20I_HDMI_PHY_CTL0_ENTX GENMASK(27, 24) +#define SUN20I_HDMI_PHY_CTL0_ENBI GENMASK(23, 20) +#define SUN20I_HDMI_PHY_CTL0_ENLDO BIT(18) +#define SUN20I_HDMI_PHY_CTL0_ENLDO_FS BIT(17) +#define SUN20I_HDMI_PHY_CTL0_ENCK BIT(16) +#define SUN20I_HDMI_PHY_CTL0_REG_PLR GENMASK(15, 12) +#define SUN20I_HDMI_PHY_CTL0_REG_DEN GENMASK(11, 8) +#define SUN20I_HDMI_PHY_CTL0_REG_CSMPS GENMASK(7, 6) +#define SUN20I_HDMI_PHY_CTL0_REG_CK_TEST_SEL BIT(5) +#define SUN20I_HDMI_PHY_CTL0_REG_CK_SEL BIT(4) +#define SUN20I_HDMI_PHY_CTL0_HPD_EN BIT(2) +#define SUN20I_HDMI_PHY_CTL0_SCL_EN BIT(1) +#define SUN20I_HDMI_PHY_CTL0_SDA_EN BIT(0) + +#define SUN20I_HDMI_PHY_CTL1_REG 0x0044 +#define SUN20I_HDMI_PHY_CTL1_RXSENSE_MODE_MAN BIT(31) +#define SUN20I_HDMI_PHY_CTL1_RXSENSE_MODE BIT(30) +#define SUN20I_HDMI_PHY_CTL1_RES_S GENMASK(29, 28) +#define SUN20I_HDMI_PHY_CTL1_RES_SCKTMDS BIT(27) +#define SUN20I_HDMI_PHY_CTL1_REG_SWI BIT(26) +#define SUN20I_HDMI_PHY_CTL1_REG_SVR GENMASK(25, 24) +#define SUN20I_HDMI_PHY_CTL1_REG_BST2 GENMASK(21, 20) +#define SUN20I_HDMI_PHY_CTL1_REG_BST1 GENMASK(19, 18) +#define SUN20I_HDMI_PHY_CTL1_REG_BST0 GENMASK(17, 16) +#define SUN20I_HDMI_PHY_CTL1_REG_SP2_3 GENMASK(15, 12) +#define SUN20I_HDMI_PHY_CTL1_REG_SP2_2 GENMASK(11, 8) +#define SUN20I_HDMI_PHY_CTL1_REG_SP2_1 GENMASK(7, 4) +#define SUN20I_HDMI_PHY_CTL1_REG_SP2_0 GENMASK(3, 0) + +#define SUN20I_HDMI_PHY_CTL2_REG 0x0048 +#define SUN20I_HDMI_PHY_CTL2_HPDO_MODE_MAN BIT(31) +#define SUN20I_HDMI_PHY_CTL2_HPDO_MODE BIT(30) +#define SUN20I_HDMI_PHY_CTL2_REG_RESDI GENMASK(29, 24) +#define SUN20I_HDMI_PHY_CTL2_REG_SP1_3 GENMASK(23, 19) +#define SUN20I_HDMI_PHY_CTL2_REG_SP1_2 GENMASK(18, 14) +#define SUN20I_HDMI_PHY_CTL2_REG_SP1_1 GENMASK(13, 9) +#define SUN20I_HDMI_PHY_CTL2_REG_SP1_0 GENMASK(8, 4) +#define SUN20I_HDMI_PHY_CTL2_REG_P2OPT GENMASK(3, 0) + +#define SUN20I_HDMI_PHY_CTL3_REG 0x004c +#define SUN20I_HDMI_PHY_CTL3_REG_P2_3 GENMASK(31, 28) +#define SUN20I_HDMI_PHY_CTL3_REG_P2_2 GENMASK(27, 24) +#define SUN20I_HDMI_PHY_CTL3_REG_P2_1 GENMASK(23, 20) +#define SUN20I_HDMI_PHY_CTL3_REG_P2_0 GENMASK(19, 16) +#define SUN20I_HDMI_PHY_CTL3_REG_MC3 GENMASK(15, 12) +#define SUN20I_HDMI_PHY_CTL3_REG_MC2 GENMASK(11, 8) +#define SUN20I_HDMI_PHY_CTL3_REG_MC1 GENMASK(7, 4) +#define SUN20I_HDMI_PHY_CTL3_REG_MC0 GENMASK(3, 0) + +#define SUN20I_HDMI_PHY_CTL4_REG 0x0050 +#define SUN20I_HDMI_PHY_CTL4_REG_SLV GENMASK(31, 29) +#define SUN20I_HDMI_PHY_CTL4_REG_P1_3 GENMASK(28, 24) +#define SUN20I_HDMI_PHY_CTL4_REG_P1_2 GENMASK(20, 16) +#define SUN20I_HDMI_PHY_CTL4_REG_P1_1 GENMASK(12, 8) +#define SUN20I_HDMI_PHY_CTL4_REG_P1_0 GENMASK(4, 0) + +#define SUN20I_HDMI_PHY_CTL5_REG 0x0054 +#define SUN20I_HDMI_PHY_CTL5_REG_P1OPT GENMASK(19, 16) +#define SUN20I_HDMI_PHY_CTL5_REG_CKPDLYOPT BIT(12) +#define SUN20I_HDMI_PHY_CTL5_REG_CALSW BIT(11) +#define SUN20I_HDMI_PHY_CTL5_ENRESCK BIT(10) +#define SUN20I_HDMI_PHY_CTL5_ENRES BIT(9) +#define SUN20I_HDMI_PHY_CTL5_ENRCAL BIT(8) +#define SUN20I_HDMI_PHY_CTL5_ENP2S GENMASK(7, 4) +#define SUN20I_HDMI_PHY_CTL5_ENIB BIT(1) +#define SUN20I_HDMI_PHY_CTL5_ENCALOG BIT(0) + +#define SUN20I_HDMI_PLL_CTL0_REG 0x0058 +#define SUN20I_HDMI_PLL_CTL0_CKO_SEL GENMASK(31, 30) +#define SUN20I_HDMI_PLL_CTL0_BYPASS_PPLL BIT(29) +#define SUN20I_HDMI_PLL_CTL0_ENVBS BIT(28) +#define SUN20I_HDMI_PLL_CTL0_SLV GENMASK(26, 24) +#define SUN20I_HDMI_PLL_CTL0_BCR BIT(23) +#define SUN20I_HDMI_PLL_CTL0_BYPASS_CLRDPTH BIT(22) +#define SUN20I_HDMI_PLL_CTL0_CLR_DPTH GENMASK(21, 20) +#define SUN20I_HDMI_PLL_CTL0_CUTFB BIT(18) +#define SUN20I_HDMI_PLL_CTL0_DIV2_CKBIT BIT(17) +#define SUN20I_HDMI_PLL_CTL0_DIV2_CKTMDS BIT(16) +#define SUN20I_HDMI_PLL_CTL0_DIV_PRE GENMASK(15, 12) +#define SUN20I_HDMI_PLL_CTL0_DIVX1 BIT(10) +#define SUN20I_HDMI_PLL_CTL0_SDRVEN BIT(9) +#define SUN20I_HDMI_PLL_CTL0_VCORANGE BIT(8) +#define SUN20I_HDMI_PLL_CTL0_N_CNTRL GENMASK(7, 6) +#define SUN20I_HDMI_PLL_CTL0_GMP_CNTRL GENMASK(5, 4) +#define SUN20I_HDMI_PLL_CTL0_PROP_CNTRL GENMASK(2, 0) + +#define SUN20I_HDMI_PLL_CTL1_REG 0x005c +#define SUN20I_HDMI_PLL_CTL1_CTRL_MODLE_CLKSRC BIT(31) +#define SUN20I_HDMI_PLL_CTL1_PCNT_N GENMASK(27, 20) +#define SUN20I_HDMI_PLL_CTL1_PCNT_EN BIT(19) +#define SUN20I_HDMI_PLL_CTL1_SDM_EN BIT(18) +#define SUN20I_HDMI_PLL_CTL1_PIXEL_REP GENMASK(17, 16) +#define SUN20I_HDMI_PLL_CTL1_PWRON BIT(12) +#define SUN20I_HDMI_PLL_CTL1_RESET BIT(11) +#define SUN20I_HDMI_PLL_CTL1_SCKREF BIT(10) +#define SUN20I_HDMI_PLL_CTL1_SCKFB BIT(9) +#define SUN20I_HDMI_PLL_CTL1_DRV_ANA BIT(8) +#define SUN20I_HDMI_PLL_CTL1_FAST_TECH BIT(7) +#define SUN20I_HDMI_PLL_CTL1_GEAR_SHIFT BIT(6) +#define SUN20I_HDMI_PLL_CTL1_REF_CNTRL GENMASK(5, 4) +#define SUN20I_HDMI_PLL_CTL1_INT_CNTRL GENMASK(2, 0) + +#define SUN20I_HDMI_AFIFO_CFG_REG 0x0060 +#define SUN20I_HDMI_AFIFO_CFG_AFIFO_ERROR BIT(0) +#define SUN20I_HDMI_AFIFO_CFG_AFIFO_ERROR_DET BIT(1) + +#define SUN20I_HDMI_MODULATOR_CFG0_REG 0x0064 +#define SUN20I_HDMI_MODULATOR_CFG1_REG 0x0068 + +#define SUN20I_HDMI_INDEB_CTRL_REG 0x006c +#define SUN20I_HDMI_INDEB_CTRL_HPDI_DEBUGMODE BIT(29) +#define SUN20I_HDMI_INDEB_CTRL_HPDI_DEBUG BIT(28) +#define SUN20I_HDMI_INDEB_CTRL_SDAI_DEBUGMODE BIT(25) +#define SUN20I_HDMI_INDEB_CTRL_SDAI_DEBUG BIT(24) +#define SUN20I_HDMI_INDEB_CTRL_SCLI_DEBUGMODE BIT(21) +#define SUN20I_HDMI_INDEB_CTRL_SCLI_DEBUG BIT(20) +#define SUN20I_HDMI_INDEB_CTRL_CECI_DEBUGMODE BIT(17) +#define SUN20I_HDMI_INDEB_CTRL_CECI_DEBUG BIT(16) +#define SUN20I_HDMI_INDEB_CTRL_TXDATA_DEBUGMODE GENMASK(1, 0) + +#define SUN20I_HDMI_INDBG_TXD0_REG 0x0070 +#define SUN20I_HDMI_INDBG_TXD1_REG 0x0074 +#define SUN20I_HDMI_INDBG_TXD2_REG 0x0078 +#define SUN20I_HDMI_INDBG_TXD3_REG 0x007c + +#define SUN20I_HDMI_PLL_STS_REG 0x0080 +#define SUN20I_HDMI_PLL_STS_PHY_CDETPCK_STATUS BIT(31) +#define SUN20I_HDMI_PLL_STS_PHY_CDETP_STATUS GENMASK(30, 28) +#define SUN20I_HDMI_PLL_STS_PHY_CDETNCK_STATUS BIT(27) +#define SUN20I_HDMI_PLL_STS_PHY_CDETN_STATUS GENMASK(26, 24) +#define SUN20I_HDMI_PLL_STS_PHY_HPDO_STATUS BIT(23) +#define SUN20I_HDMI_PLL_STS_PHY_SCLO_STATUS BIT(22) +#define SUN20I_HDMI_PLL_STS_PHY_SDAO_STATUS BIT(21) +#define SUN20I_HDMI_PLL_STS_PHY_CECO_STATUS BIT(20) +#define SUN20I_HDMI_PLL_STS_PHY_COUT2D_STATUS BIT(17) +#define SUN20I_HDMI_PLL_STS_PHY_RCALEND2D_STS BIT(16) +#define SUN20I_HDMI_PLL_STS_PHY_RESDO2D_STATUS GENMASK(13, 8) +#define SUN20I_HDMI_PLL_STS_PLL_LOCK_STATUS BIT(4) +#define SUN20I_HDMI_PLL_STS_RXSENSE_DLY_STATUS BIT(1) +#define SUN20I_HDMI_PLL_STS_TX_READY_DLY_STATUS BIT(0) + +#define SUN20I_HDMI_PRBS_CTL_REG 0x0084 +#define SUN20I_HDMI_PRBS_SEED_GEN_REG 0x0088 +#define SUN20I_HDMI_PRBS_SEED_CHK_REG 0x008c +#define SUN20I_HDMI_PRBS_SEED_NUM_REG 0x0090 +#define SUN20I_HDMI_PRBS_CYCLE_NUM_REG 0x0094 + +#define SUN20I_HDMI_PLL_ODLY_REG 0x0098 +#define SUN20I_HDMI_PLL_ODLY_RXSENSE_DLY_RESET BIT(31) +#define SUN20I_HDMI_PLL_ODLY_RXSENSE_DLY_COUNT GENMASK(30, 16) +#define SUN20I_HDMI_PLL_ODLY_TX_READY_DLY_RESET BIT(15) +#define SUN20I_HDMI_PLL_ODLY_TX_READY_DLY_COUNT GENMASK(14, 0) + +#define SUN20I_HDMI_PHY_CTL6_REG 0x009c +#define SUN20I_HDMI_PHY_CTL6_SWITCH_CLKCH_DATA BIT(31) +#define SUN20I_HDMI_PHY_CTL6_EN_CKDAT BIT(30) +#define SUN20I_HDMI_PHY_CTL6_CLK_GREATE2_340M GENMASK(29, 20) +#define SUN20I_HDMI_PHY_CTL6_CLK_GREATE1_340M GENMASK(19, 10) +#define SUN20I_HDMI_PHY_CTL6_CLK_GREATE0_340M GENMASK(9, 0) + +#define SUN20I_HDMI_PHY_CTL7_REG 0x00a0 +#define SUN20I_HDMI_PHY_CTL7_CLK_LOW_340M GENMASK(21, 12) +#define SUN20I_HDMI_PHY_CTL7_CLK_GREATE3_340M GENMASK(9, 0) + struct sun8i_hdmi_phy; struct sun8i_hdmi_phy_variant { diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index ca53b5e9fffca7..1e7762274c0eaf 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -398,6 +398,28 @@ static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = { .setup_hpd = dw_hdmi_phy_setup_hpd, }; +static int sun20i_d1_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, + const struct drm_display_info *display, + const struct drm_display_mode *mode) +{ + struct sun8i_hdmi_phy *phy = data; + + return 0; +} + +static void sun20i_d1_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) +{ + struct sun8i_hdmi_phy *phy = data; +} + +static const struct dw_hdmi_phy_ops sun20i_d1_hdmi_phy_ops = { + .init = sun20i_d1_hdmi_phy_config, + .disable = sun20i_d1_hdmi_phy_disable, + .read_hpd = dw_hdmi_phy_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_phy_setup_hpd, +}; + static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy) { /* enable read access to HDMI controller */ @@ -576,6 +598,7 @@ void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy, const struct sun8i_hdmi_phy_variant *variant = phy->variant; if (variant->phy_ops) { + plat_data->phy_force_vendor = true; plat_data->phy_ops = variant->phy_ops; plat_data->phy_name = "sun8i_dw_hdmi_phy"; plat_data->phy_data = phy; @@ -612,6 +635,11 @@ static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = { .phy_init = &sun8i_hdmi_phy_init_h3, }; +static const struct sun8i_hdmi_phy_variant sun20i_d1_hdmi_phy = { + .phy_ops = &sun20i_d1_hdmi_phy_ops, + .phy_init = &sun50i_hdmi_phy_init_h6, +}; + static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = { .has_phy_clk = true, .phy_ops = &sun8i_h3_hdmi_phy_ops, @@ -638,6 +666,10 @@ static const struct of_device_id sun8i_hdmi_phy_of_table[] = { .compatible = "allwinner,sun8i-r40-hdmi-phy", .data = &sun8i_r40_hdmi_phy, }, + { + .compatible = "allwinner,sun20i-d1-hdmi-phy", + .data = &sun20i_d1_hdmi_phy, + }, { .compatible = "allwinner,sun50i-a64-hdmi-phy", .data = &sun50i_a64_hdmi_phy, From 38f4bd7453586a90d2d04fcf52d858d6e072139c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Mar 2022 00:46:07 -0500 Subject: [PATCH 012/153] [HACK] drm/sun4i: Copy in BSP code for D1 HDMI PHY Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/aw_phy.h | 411 +++++++++++++++++++++++++ drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 1 + drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 156 ++++++++++ 3 files changed, 568 insertions(+) create mode 100644 drivers/gpu/drm/sun4i/aw_phy.h diff --git a/drivers/gpu/drm/sun4i/aw_phy.h b/drivers/gpu/drm/sun4i/aw_phy.h new file mode 100644 index 00000000000000..8c469126a6fd9d --- /dev/null +++ b/drivers/gpu/drm/sun4i/aw_phy.h @@ -0,0 +1,411 @@ +/* + * Allwinner SoCs hdmi2.0 driver. + * + * Copyright (C) 2016 Allwinner. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef AW_PHY_H_ +#define AW_PHY_H_ + +#define AW_PHY_TIMEOUT 1000 +#define LOCK_TIMEOUT 100 + +/* allwinner phy register offset */ +#define HDMI_PHY_CTL0 0x40 +#define HDMI_PHY_CTL1 0x44 +#define HDMI_PHY_CTL2 0x48 +#define HDMI_PHY_CTL3 0x4C +#define HDMI_PHY_CTL4 0x50 +#define HDMI_PHY_CTL5 0x54 +#define HDMI_PLL_CTL0 0x58 +#define HDMI_PLL_CTL1 0x5C +#define HDMI_AFIFO_CFG 0x60 +#define HDMI_MODULATOR_CFG0 0x64 +#define HDMI_MODULATOR_CFG1 0x68 +#define HDMI_PHY_INDEB_CTRL 0x6C +#define HDMI_PHY_INDBG_TXD0 0x70 +#define HDMI_PHY_INDBG_TXD1 0x74 +#define HDMI_PHY_INDBG_TXD2 0x78 +#define HDMI_PHY_INDBG_TXD3 0x7C +#define HDMI_PHY_PLL_STS 0x80 +#define HDMI_PRBS_CTL 0x84 +#define HDMI_PRBS_SEED_GEN 0x88 +#define HDMI_PRBS_SEED_CHK 0x8C +#define HDMI_PRBS_SEED_NUM 0x90 +#define HDMI_PRBS_CYCLE_NUM 0x94 +#define HDMI_PHY_PLL_ODLY_CFG 0x98 +#define HDMI_PHY_CTL6 0x9C +#define HDMI_PHY_CTL7 0xA0 + +typedef union { + u32 dwval; + struct { + u32 sda_en :1; // Default: 0; + u32 scl_en :1; // Default: 0; + u32 hpd_en :1; // Default: 0; + u32 res0 :1; // Default: 0; + u32 reg_ck_sel :1; // Default: 1; + u32 reg_ck_test_sel :1; // Default: 1; + u32 reg_csmps :2; // Default: 0; + u32 reg_den :4; // Default: F; + u32 reg_plr :4; // Default: 0; + u32 enck :1; // Default: 1; + u32 enldo_fs :1; // Default: 1; + u32 enldo :1; // Default: 1; + u32 res1 :1; // Default: 1; + u32 enbi :4; // Default: F; + u32 entx :4; // Default: F; + u32 async_fifo_autosync_disable :1; // Default: 0; + u32 async_fifo_workc_enable :1; // Default: 1; + u32 phy_pll_lock_mode :1; // Default: 1; + u32 phy_pll_lock_mode_man :1; // Default: 1; + } bits; +} HDMI_PHY_CTL0_t; //=========================== 0x0040 + +typedef union { + u32 dwval; + struct { + u32 reg_sp2_0 : 4 ; // Default: 0; + u32 reg_sp2_1 : 4 ; // Default: 0; + u32 reg_sp2_2 : 4 ; // Default: 0; + u32 reg_sp2_3 : 4 ; // Default: 0; + u32 reg_bst0 : 2 ; // Default: 3; + u32 reg_bst1 : 2 ; // Default: 3; + u32 reg_bst2 : 2 ; // Default: 3; + u32 res0 : 2 ; // Default: 0; + u32 reg_svr : 2 ; // Default: 2; + u32 reg_swi : 1 ; // Default: 0; + u32 res_scktmds : 1 ; // Default: 0; + u32 res_res_s : 2 ; // Default: 3; + u32 phy_rxsense_mode : 1 ; // Default: 0; + u32 res_rxsense_mode_man : 1 ; // Default: 0; + } bits; +} HDMI_PHY_CTL1_t; //===================================================== 0x0044 + +typedef union { + u32 dwval; + struct { + u32 reg_p2opt : 4 ; // Default: 0; + u32 reg_sp1_0 : 5 ; // Default: 0; + u32 reg_sp1_1 : 5 ; // Default: 0; + u32 reg_sp1_2 : 5 ; // Default: 0; + u32 reg_sp1_3 : 5 ; // Default: 0; + u32 reg_resdi : 6 ; // Default: 18; + u32 phy_hpdo_mode : 1 ; // Default: 0; + u32 phy_hpdo_mode_man : 1 ; // Default: 0; + } bits; +} HDMI_PHY_CTL2_t; //===================================================== 0x0048 + + + +typedef union { + u32 dwval; + struct { + u32 reg_mc0 : 4 ; // Default: F; + u32 reg_mc1 : 4 ; // Default: F; + u32 reg_mc2 : 4 ; // Default: F; + u32 reg_mc3 : 4 ; // Default: F; + u32 reg_p2_0 : 4 ; // Default: F; + u32 reg_p2_1 : 4 ; // Default: F; + u32 reg_p2_2 : 4 ; // Default: F; + u32 reg_p2_3 : 4 ; // Default: F; + } bits; +} HDMI_PHY_CTL3_t; //===================================================== 0x004C + + + +typedef union { + u32 dwval; + struct { + u32 reg_p1_0 : 5 ; // Default: 0x10; + u32 res0 : 3 ; // Default: 0; + u32 reg_p1_1 : 5 ; // Default: 0x10; + u32 res1 : 3 ; // Default: 0; + u32 reg_p1_2 : 5 ; // Default: 0x10; + u32 res2 : 3 ; // Default: 0; + u32 reg_p1_3 : 5 ; // Default: 0x10; + u32 reg_slv : 3 ; // Default: 0; + } bits; +} HDMI_PHY_CTL4_t; //===================================================== 0x0050 + +typedef union { + u32 dwval; + struct { + u32 encalog : 1 ; // Default: 0x1; + u32 enib : 1 ; // Default: 0x1; + u32 res0 : 2 ; // Default: 0; + u32 enp2s : 4 ; // Default: 0xF; + u32 enrcal : 1 ; // Default: 0x1; + u32 enres : 1 ; // Default: 1; + u32 enresck : 1 ; // Default: 1; + u32 reg_calsw : 1 ; // Default: 0; + u32 reg_ckpdlyopt : 1 ; // Default: 0; + u32 res1 : 3 ; // Default: 0; + u32 reg_p1opt : 4 ; // Default: 0; + u32 res2 : 12 ; // Default: 0; + } bits; +} HDMI_PHY_CTL5_t; //===================================================== 0x0054 + +typedef union { + u32 dwval; + struct { + u32 prop_cntrl : 3 ; // Default: 0x7; + u32 res0 : 1 ; // Default: 0; + u32 gmp_cntrl : 2 ; // Default: 1; + u32 n_cntrl : 2 ; // Default: 0; + u32 vcorange : 1 ; // Default: 0; + u32 sdrven : 1 ; // Default: 0; + u32 divx1 : 1 ; // Default: 0; + u32 res1 : 1 ; // Default: 0; + u32 div_pre : 4 ; // Default: 0; + u32 div2_cktmds : 1 ; // Default: 1; + u32 div2_ckbit : 1 ; // Default: 1; + u32 cutfb : 1 ; // Default: 0; + u32 res2 : 1 ; // Default: 0; + u32 clr_dpth : 2 ; // Default: 0; + u32 bypass_clrdpth : 1 ; // Default: 0; + u32 bcr : 1 ; // Default: 0; + u32 slv : 3 ; // Default: 4; + u32 res3 : 1 ; // Default: 0; + u32 envbs : 1 ; // Default: 0; + u32 bypass_ppll : 1 ; // Default: 0; + u32 cko_sel : 2 ; // Default: 0; + } bits; +} HDMI_PLL_CTL0_t; //===================================================== 0x0058 + + + +typedef union { + u32 dwval; + struct { + u32 int_cntrl : 3 ; // Default: 0x0; + u32 res0 : 1 ; // Default: 0; + u32 ref_cntrl : 2 ; // Default: 3; + u32 gear_shift : 1 ; // Default: 0; + u32 fast_tech : 1 ; // Default: 0; + u32 drv_ana : 1 ; // Default: 1; + u32 sckfb : 1 ; // Default: 0; + u32 sckref : 1 ; // Default: 0; + u32 reset : 1 ; // Default: 0; + u32 pwron : 1 ; // Default: 0; + u32 res1 : 3 ; // Default: 0; + u32 pixel_rep : 2 ; // Default: 0; + u32 sdm_en : 1 ; // Default: 0; + u32 pcnt_en : 1 ; // Default: 0; + u32 pcnt_n : 8 ; // Default: 0xE; + u32 res2 : 3 ; // Default: 0; + u32 ctrl_modle_clksrc : 1 ; // Default: 0; + } bits; +} HDMI_PLL_CTL1_t; //===================================================== 0x005C + +typedef union { + u32 dwval; + struct { + u32 hdmi_afifo_error : 1 ; // Default: 0x0; + u32 hdmi_afifo_error_det : 1 ; // Default: 0x0; + u32 res0 : 30 ; // Default: 0; + } bits; +} HDMI_AFIFO_CFG_t; //===================================================== 0x0060 + +typedef union { + u32 dwval; + struct { + u32 fnpll_mash_en : 1 ; // Default: 0x0; + u32 fnpll_mash_mod : 2 ; // Default: 0x0; + u32 fnpll_mash_stp : 9 ; // Default: 0x0; + u32 fnpll_mash_m12 : 1 ; // Default: 0x0; + u32 fnpll_mash_frq : 2 ; // Default: 0x0; + u32 fnpll_mash_bot : 17 ; // Default: 0x0; + } bits; +} HDMI_MODULATOR_CFG0_t; //===================================================== 0x0064 + +typedef union { + u32 dwval; + struct { + u32 fnpll_mash_dth : 1 ; // Default: 0x0; + u32 fnpll_mash_fen : 1 ; // Default: 0x0; + u32 fnpll_mash_frc : 17 ; // Default: 0x0; + u32 fnpll_mash_fnv : 8 ; // Default: 0x0; + u32 res0 : 5 ; // Default: 0x0; + } bits; +} HDMI_MODULATOR_CFG1_t; //===================================================== 0x0068 + +typedef union { + u32 dwval; + struct { + u32 txdata_debugmode : 2 ; // Default: 0x0; + u32 res0 : 14 ; // Default: 0x0; + u32 ceci_debug : 1 ; // Default: 0x0; + u32 ceci_debugmode : 1 ; // Default: 0x0; + u32 res1 : 2 ; // Default: 0x0; + u32 sdai_debug : 1 ; // Default: 0x0; + u32 sdai_debugmode : 1 ; // Default: 0x0; + u32 res2 : 2 ; // Default: 0x0; + u32 scli_debug : 1 ; // Default: 0x0; + u32 scli_debugmode : 1 ; // Default: 0x0; + u32 res3 : 2 ; // Default: 0x0; + u32 hpdi_debug : 1 ; // Default: 0x0; + u32 hpdi_debugmode : 1 ; // Default: 0x0; + u32 res4 : 2 ; // Default: 0x0; + } bits; +} HDMI_PHY_INDBG_CTRL_t; //================================================== 0x006C + +typedef union { + u32 dwval; + struct { + u32 txdata0_debug_data : 32 ; // Default: 0x0; + } bits; +} HDMI_PHY_INDBG_TXD0_t; //================================================== 0x0070 + +typedef union { + u32 dwval; + struct { + u32 txdata1_debug_data : 32 ; // Default: 0x0; + } bits; +} HDMI_PHY_INDBG_TXD1_t; //================================================== 0x0074 + +typedef union { + u32 dwval; + struct { + u32 txdata2_debug_data : 32 ; // Default: 0x0; + } bits; +} HDMI_PHY_INDBG_TXD2_t; //================================================== 0x0078 + +typedef union { + u32 dwval; + struct { + u32 txdata3_debug_data : 32 ; // Default: 0x0; + } bits; +} HDMI_PHY_INDBG_TXD3_t; //================================================== 0x007C + +typedef union { + u32 dwval; + struct { + u32 tx_ready_dly_status : 1 ; // Default: 0x0; + u32 rxsense_dly_status : 1 ; // Default: 0x0; + u32 res0 : 2 ; // Default: 0x0; + u32 pll_lock_status : 1 ; // Default: 0x0; + u32 res1 : 3 ; // Default: 0x0; + u32 phy_resdo2d_status : 6 ; // Default: 0x0; + u32 res2 : 2 ; // Default: 0x0; + u32 phy_rcalend2d_status : 1 ; // Default: 0x0; + u32 phy_cout2d_status : 1 ; // Default: 0x0; + u32 res3 : 2 ; // Default: 0x0; + u32 phy_ceco_status : 1 ; // Default: 0x0; + u32 phy_sdao_status : 1 ; // Default: 0x0; + u32 phy_sclo_status : 1 ; // Default: 0x0; + u32 phy_hpdo_status : 1 ; // Default: 0x0; + u32 phy_cdetn_status : 3 ; // Default: 0x0; + u32 phy_cdetnck_status : 1 ; // Default: 0x0; + u32 phy_cdetp_status : 3 ; // Default: 0x0; + u32 phy_cdetpck_status : 1 ; // Default: 0x0; + } bits; +} HDMI_PHY_PLL_STS_t; //===================================================== 0x0080 + +typedef union { + u32 dwval; + struct { + u32 prbs_en : 1 ; // Default: 0x0; + u32 prbs_start : 1 ; // Default: 0x0; + u32 prbs_seq_gen : 1 ; // Default: 0x0; + u32 prbs_seq_chk : 1 ; // Default: 0x0; + u32 prbs_mode : 4 ; // Default: 0x0; + u32 prbs_type : 2 ; // Default: 0x0; + u32 prbs_clk_pol : 1 ; // Default: 0x0; + u32 res0 : 21 ; // Default: 0x0; + } bits; +} HDMI_PRBS_CTL_t; //===================================================== 0x0084 + +typedef union { + u32 dwval; + struct { + u32 prbs_seed_gen : 32 ; // Default: 0x0; + } bits; +} HDMI_PRBS_SEED_GEN_t; //================================================= 0x0088 + +typedef union { + u32 dwval; + struct { + u32 prbs_seed_chk : 32 ; // Default: 0x0; + } bits; +} HDMI_PRBS_SEED_CHK_t; //================================================= 0x008C + +typedef union { + u32 dwval; + struct { + u32 prbs_seed_num : 32 ; // Default: 0x0; + } bits; +} HDMI_PRBS_SEED_NUM_t; //================================================= 0x0090 + +typedef union { + u32 dwval; + struct { + u32 prbs_cycle_num : 32 ; // Default: 0x0; + } bits; +} HDMI_PRBS_CYCLE_NUM_t; //================================================= 0x0094 + +typedef union { + u32 dwval; + struct { + u32 tx_ready_dly_count : 15 ; // Default: 0x0; + u32 tx_ready_dly_reset : 1 ; // Default: 0x0; + u32 rxsense_dly_count : 15 ; // Default: 0x0; + u32 rxsense_dly_reset : 1 ; // Default: 0x0; + } bits; +} HDMI_PHY_PLL_ODLY_CFG_t; //================================================= 0x0098 + +typedef union { + u32 dwval; + struct { + u32 clk_greate0_340m : 10 ; // Default: 0x3FF; + u32 clk_greate1_340m : 10 ; // Default: 0x3FF; + u32 clk_greate2_340m : 10 ; // Default: 0x3FF; + u32 en_ckdat : 1 ; // Default: 0x3FF; + u32 switch_clkch_data_corresponding : 1 ; // Default: 0x3FF; + } bits; +} HDMI_PHY_CTL6_t; //========================================================= 0x009C + +typedef union { + u32 dwval; + struct { + u32 clk_greate3_340m : 10 ; // Default: 0x0; + u32 res0 : 2 ; // Default: 0x3FF; + u32 clk_low_340m : 10 ; // Default: 0x3FF; + u32 res1 : 10 ; // Default: 0x3FF; + } bits; +} HDMI_PHY_CTL7_t; //========================================================= 0x00A0 + +struct __aw_phy_reg_t { + u32 res[16]; /* 0x0 ~ 0x3c */ + HDMI_PHY_CTL0_t phy_ctl0; /* 0x0040 */ + HDMI_PHY_CTL1_t phy_ctl1; /* 0x0044 */ + HDMI_PHY_CTL2_t phy_ctl2; /* 0x0048 */ + HDMI_PHY_CTL3_t phy_ctl3; /* 0x004c */ + HDMI_PHY_CTL4_t phy_ctl4; /* 0x0050 */ + HDMI_PHY_CTL5_t phy_ctl5; /* 0x0054 */ + HDMI_PLL_CTL0_t pll_ctl0; /* 0x0058 */ + HDMI_PLL_CTL1_t pll_ctl1; /* 0x005c */ + HDMI_AFIFO_CFG_t afifo_cfg; /* 0x0060 */ + HDMI_MODULATOR_CFG0_t modulator_cfg0; /* 0x0064 */ + HDMI_MODULATOR_CFG1_t modulator_cfg1; /* 0x0068 */ + HDMI_PHY_INDBG_CTRL_t phy_indbg_ctrl; /* 0x006c */ + HDMI_PHY_INDBG_TXD0_t phy_indbg_txd0; /* 0x0070 */ + HDMI_PHY_INDBG_TXD1_t phy_indbg_txd1; /* 0x0074 */ + HDMI_PHY_INDBG_TXD2_t phy_indbg_txd2; /* 0x0078 */ + HDMI_PHY_INDBG_TXD3_t phy_indbg_txd3; /* 0x007c */ + HDMI_PHY_PLL_STS_t phy_pll_sts; /* 0x0080 */ + HDMI_PRBS_CTL_t prbs_ctl; /* 0x0084 */ + HDMI_PRBS_SEED_GEN_t prbs_seed_gen; /* 0x0088 */ + HDMI_PRBS_SEED_CHK_t prbs_seed_chk; /* 0x008c */ + HDMI_PRBS_SEED_NUM_t prbs_seed_num; /* 0x0090 */ + HDMI_PRBS_CYCLE_NUM_t prbs_cycle_num; /* 0x0094 */ + HDMI_PHY_PLL_ODLY_CFG_t phy_pll_odly_cfg; /* 0x0098 */ + HDMI_PHY_CTL6_t phy_ctl6; /* 0x009c */ + HDMI_PHY_CTL7_t phy_ctl7; /* 0x00A0 */ +}; + +#endif /* AW_PHY_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index 371c3d5810244b..4506312852c22d 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -334,6 +334,7 @@ struct sun8i_hdmi_phy { struct clk *clk_pll1; struct device *dev; unsigned int rcal; + void __iomem *base; struct regmap *regs; struct reset_control *rst_phy; const struct sun8i_hdmi_phy_variant *variant; diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 1e7762274c0eaf..f22ee6c3808ea1 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -9,6 +9,8 @@ #include "sun8i_dw_hdmi.h" +#include "aw_phy.h" + /* * Address can be actually any value. Here is set to same value as * it is set in BSP driver. @@ -398,11 +400,164 @@ static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = { .setup_hpd = dw_hdmi_phy_setup_hpd, }; +static int sun20i_d1_hdmi_phy_enable(volatile struct __aw_phy_reg_t __iomem *phy_base) +{ + int i = 0, status = 0; + + pr_info("enter %s\n", __func__); + + //enib -> enldo -> enrcal -> encalog -> enbi[3:0] -> enck -> enp2s[3:0] -> enres -> enresck -> entx[3:0] + phy_base->phy_ctl4.bits.reg_slv = 4; //low power voltage 1.08V, default is 3, set 4 as well as pll_ctl0 bit [24:26] + phy_base->phy_ctl5.bits.enib = 1; + phy_base->phy_ctl0.bits.enldo = 1; + phy_base->phy_ctl0.bits.enldo_fs = 1; + phy_base->phy_ctl5.bits.enrcal = 1; + + phy_base->phy_ctl5.bits.encalog = 1; + + for (i = 0; i < AW_PHY_TIMEOUT; i++) { + udelay(5); + status = phy_base->phy_pll_sts.bits.phy_rcalend2d_status; + if (status & 0x1) { + pr_info("[%s]:phy_rcalend2d_status\n", __func__); + break; + } + } + if ((i == AW_PHY_TIMEOUT) && !status) { + pr_err("phy_rcalend2d_status Timeout !\n"); + return -1; + } + + phy_base->phy_ctl0.bits.enbi = 0xF; + for (i = 0; i < AW_PHY_TIMEOUT; i++) { + udelay(5); + status = phy_base->phy_pll_sts.bits.pll_lock_status; + if (status & 0x1) { + pr_info("[%s]:pll_lock_status\n", __func__); + break; + } + } + if ((i == AW_PHY_TIMEOUT) && !status) { + pr_err("pll_lock_status Timeout! status = 0x%x\n", status); + return -1; + } + + phy_base->phy_ctl0.bits.enck = 1; + phy_base->phy_ctl5.bits.enp2s = 0xF; + phy_base->phy_ctl5.bits.enres = 1; + phy_base->phy_ctl5.bits.enresck = 1; + phy_base->phy_ctl0.bits.entx = 0xF; + + for (i = 0; i < AW_PHY_TIMEOUT; i++) { + udelay(5); + status = phy_base->phy_pll_sts.bits.tx_ready_dly_status; + if (status & 0x1) { + pr_info("[%s]:tx_ready_status\n", __func__); + break; + } + } + if ((i == AW_PHY_TIMEOUT) && !status) { + pr_err("tx_ready_status Timeout ! status = 0x%x\n", status); + return -1; + } + + return 0; +} + static int sun20i_d1_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, const struct drm_display_info *display, const struct drm_display_mode *mode) { struct sun8i_hdmi_phy *phy = data; + volatile struct __aw_phy_reg_t __iomem *phy_base = phy->base; + int ret; + + pr_info("enter %s\n", __func__); + + /* enable all channel */ + phy_base->phy_ctl5.bits.reg_p1opt = 0xF; + + // phy_reset + phy_base->phy_ctl0.bits.entx = 0; + phy_base->phy_ctl5.bits.enresck = 0; + phy_base->phy_ctl5.bits.enres = 0; + phy_base->phy_ctl5.bits.enp2s = 0; + phy_base->phy_ctl0.bits.enck = 0; + phy_base->phy_ctl0.bits.enbi = 0; + phy_base->phy_ctl5.bits.encalog = 0; + phy_base->phy_ctl5.bits.enrcal = 0; + phy_base->phy_ctl0.bits.enldo_fs = 0; + phy_base->phy_ctl0.bits.enldo = 0; + phy_base->phy_ctl5.bits.enib = 0; + phy_base->pll_ctl1.bits.reset = 1; + phy_base->pll_ctl1.bits.pwron = 0; + phy_base->pll_ctl0.bits.envbs = 0; + + // phy_set_mpll + phy_base->pll_ctl0.bits.cko_sel = 0x3; + phy_base->pll_ctl0.bits.bypass_ppll = 0x1; + phy_base->pll_ctl1.bits.drv_ana = 1; + phy_base->pll_ctl1.bits.ctrl_modle_clksrc = 0x0; //0: PLL_video 1: MPLL + phy_base->pll_ctl1.bits.sdm_en = 0x0; //mpll sdm jitter 很大,暂不使用 + phy_base->pll_ctl1.bits.sckref = 0; //默认值为1 + phy_base->pll_ctl0.bits.slv = 4; + phy_base->pll_ctl0.bits.prop_cntrl = 7; //默认值7 + phy_base->pll_ctl0.bits.gmp_cntrl = 3; //默认值1 + phy_base->pll_ctl1.bits.ref_cntrl = 0; + phy_base->pll_ctl0.bits.vcorange = 1; + + // phy_set_div + phy_base->pll_ctl0.bits.div_pre = 0; //div7 = n+1 + phy_base->pll_ctl1.bits.pcnt_en = 0; + phy_base->pll_ctl1.bits.pcnt_n = 1; //div6 = 1 (pcnt_en=0) [div6 = n (pcnt_en = n 注意部分倍数有问题)] 4-256 + phy_base->pll_ctl1.bits.pixel_rep = 0; //div5 = n+1 + phy_base->pll_ctl0.bits.bypass_clrdpth = 0; + phy_base->pll_ctl0.bits.clr_dpth = 0; //div4 = 1 (bypass_clrdpth = 0) + //00: 2 01: 2.5 10: 3 11: 4 + phy_base->pll_ctl0.bits.n_cntrl = 1; //div + phy_base->pll_ctl0.bits.div2_ckbit = 0; //div1 = n+1 + phy_base->pll_ctl0.bits.div2_cktmds = 0; //div2 = n+1 + phy_base->pll_ctl0.bits.bcr = 0; //div3 0: [1:10] 1: [1:40] + phy_base->pll_ctl1.bits.pwron = 1; + phy_base->pll_ctl1.bits.reset = 0; + + //配置phy + /* config values taken from table */ + phy_base->phy_ctl1.dwval = ((phy_base->phy_ctl1.dwval & 0xFFC0FFFF) | /* config->phy_ctl1 */ 0x0); + phy_base->phy_ctl2.dwval = ((phy_base->phy_ctl2.dwval & 0xFF000000) | /* config->phy_ctl2 */ 0x0); + phy_base->phy_ctl3.dwval = ((phy_base->phy_ctl3.dwval & 0xFFFF0000) | /* config->phy_ctl3 */ 0xFFFF); + phy_base->phy_ctl4.dwval = ((phy_base->phy_ctl4.dwval & 0xE0000000) | /* config->phy_ctl4 */ 0xC0D0D0D); + //phy_base->pll_ctl0.dwval |= config->pll_ctl0; + //phy_base->pll_ctl1.dwval |= config->pll_ctl1; + + // phy_set_clk + phy_base->phy_ctl6.bits.switch_clkch_data_corresponding = 0; + phy_base->phy_ctl6.bits.clk_greate0_340m = 0x3FF; + phy_base->phy_ctl6.bits.clk_greate1_340m = 0x3FF; + phy_base->phy_ctl6.bits.clk_greate2_340m = 0x0; + phy_base->phy_ctl7.bits.clk_greate3_340m = 0x0; + phy_base->phy_ctl7.bits.clk_low_340m = 0x3E0; + phy_base->phy_ctl6.bits.en_ckdat = 1; //默认值为0 + + // phy_base->phy_ctl2.bits.reg_resdi = 0x18; + // phy_base->phy_ctl4.bits.reg_slv = 3; //low power voltage 1.08V, 默认值是3 + + phy_base->phy_ctl1.bits.res_scktmds = 0; // + phy_base->phy_ctl0.bits.reg_csmps = 2; + phy_base->phy_ctl0.bits.reg_ck_test_sel = 0; //? + phy_base->phy_ctl0.bits.reg_ck_sel = 1; + phy_base->phy_indbg_ctrl.bits.txdata_debugmode = 0; + + // phy_enable + ret = sun20i_d1_hdmi_phy_enable(phy_base); + if (ret) + return ret; + + phy_base->phy_ctl0.bits.sda_en = 1; + phy_base->phy_ctl0.bits.scl_en = 1; + phy_base->phy_ctl0.bits.hpd_en = 1; + phy_base->phy_ctl0.bits.reg_den = 0xF; + phy_base->pll_ctl0.bits.envbs = 1; return 0; } @@ -720,6 +875,7 @@ static int sun8i_hdmi_phy_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(regs), "Couldn't map the HDMI PHY registers\n"); + phy->base = regs; phy->regs = devm_regmap_init_mmio(dev, regs, &sun8i_hdmi_phy_regmap_config); if (IS_ERR(phy->regs)) From a74071d40f93af7c1c0509fdca8a52cbcaf41adf Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:42:19 -0500 Subject: [PATCH 013/153] hwspinlock: sun6i: Clarify bank counting logic In some of the most recent datasheets, the register definition was updated in a way that resolves the conflict here: the field is only two bits wide, and a value of "4" really means a bit pattern of "0". Correct the code to reflect this, but leave an updated comment because some datasheets still have incorrect information in them. Fixes: 3c881e05c814 ("hwspinlock: add sun6i hardware spinlock support") Signed-off-by: Samuel Holland --- drivers/hwspinlock/sun6i_hwspinlock.c | 36 +++++++++++---------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/hwspinlock/sun6i_hwspinlock.c b/drivers/hwspinlock/sun6i_hwspinlock.c index c2d314588046eb..21dfbbc7a8ae41 100644 --- a/drivers/hwspinlock/sun6i_hwspinlock.c +++ b/drivers/hwspinlock/sun6i_hwspinlock.c @@ -129,30 +129,22 @@ static int sun6i_hwspinlock_probe(struct platform_device *pdev) } /* - * bit 28 and 29 represents the hwspinlock setup + * Bits 28 and 29 represent the number of available locks. * - * every datasheet (A64, A80, A83T, H3, H5, H6 ...) says the default value is 0x1 and 0x1 - * to 0x4 represent 32, 64, 128 and 256 locks - * but later datasheets (H5, H6) say 00, 01, 10, 11 represent 32, 64, 128 and 256 locks, - * but that would mean H5 and H6 have 64 locks, while their datasheets talk about 32 locks - * all the time, not a single mentioning of 64 locks - * the 0x4 value is also not representable by 2 bits alone, so some datasheets are not - * correct - * one thing have all in common, default value of the sysstatus register is 0x10000000, - * which results in bit 28 being set - * this is the reason 0x1 is considered being 32 locks and bit 30 is taken into account - * verified on H2+ (datasheet 0x1 = 32 locks) and H5 (datasheet 01 = 64 locks) + * The datasheets have two conflicting interpretations for these bits: + * | 00 | 01 | 10 | 11 | + * +-----+----+-----+-----+ + * | 256 | 32 | 64 | 128 | A80, A83T, H3, A64, A50, D1 + * | 32 | 64 | 128 | 256 | H5, H6, R329 + * where some datasheets use "4" instead of "0" for the first column. + * + * Experiments shows that the first interpretation is correct, as all + * known implementations report the value "1" and have 32 spinlocks. */ - num_banks = readl(io_base + SPINLOCK_SYSSTATUS_REG) >> 28; - switch (num_banks) { - case 1 ... 4: - priv->nlocks = 1 << (4 + num_banks); - break; - default: - err = -EINVAL; - dev_err(&pdev->dev, "unsupported hwspinlock setup (%d)\n", num_banks); - goto bank_fail; - } + num_banks = readl(io_base + SPINLOCK_SYSSTATUS_REG) >> 28 & 0x3; + if (!num_banks) + num_banks = 4; + priv->nlocks = 1 << (4 + num_banks); priv->bank = devm_kzalloc(&pdev->dev, struct_size(priv->bank, lock, priv->nlocks), GFP_KERNEL); From 544356f03b790c9d321445e4f68cd5e4b293147f Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:41:44 -0500 Subject: [PATCH 014/153] hwspinlock: sun6i: Fix driver to match binding The binding for this device does not allow using the clock-names and reset-names properties, so the driver should not reference the clock or reset by name. Fixes: 3c881e05c814 ("hwspinlock: add sun6i hardware spinlock support") Signed-off-by: Samuel Holland --- drivers/hwspinlock/sun6i_hwspinlock.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/hwspinlock/sun6i_hwspinlock.c b/drivers/hwspinlock/sun6i_hwspinlock.c index 21dfbbc7a8ae41..11b63ce3bd46c3 100644 --- a/drivers/hwspinlock/sun6i_hwspinlock.c +++ b/drivers/hwspinlock/sun6i_hwspinlock.c @@ -104,14 +104,12 @@ static int sun6i_hwspinlock_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(priv->ahb_clk)) { - err = PTR_ERR(priv->ahb_clk); - dev_err(&pdev->dev, "unable to get AHB clock (%d)\n", err); - return err; - } + priv->ahb_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->ahb_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->ahb_clk), + "unable to get AHB clock\n"); - priv->reset = devm_reset_control_get(&pdev->dev, "ahb"); + priv->reset = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(priv->reset)) return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset), "unable to get reset control\n"); From fb18490609cb5620d992c77c58e1f0ee1ea2e9bb Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 14 Nov 2021 11:37:34 -0600 Subject: [PATCH 015/153] dt-bindings: hwlock: sun6i: Add interrupts property While it was not officially documented until recently (e.g. A50), the hwspinlock block can trigger an interrupt when a lock is unlocked. This capability is used by Allwinner's ARISC firmware, it has been verified to work on A64, and the IRQ numbers are reserved as far back as A31. So most likely this feature has always been available. Even though the Linux hwspinlock framework cannot make use of the IRQ, the capability should still be documented in the device tree. Signed-off-by: Samuel Holland --- .../bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml b/Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml index 10e5a53e447b57..ba47f1ef29bf64 100644 --- a/Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml +++ b/Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml @@ -26,17 +26,22 @@ properties: resets: maxItems: 1 + interrupts: + maxItems: 1 + required: - compatible - reg - clocks - resets + - interrupts additionalProperties: false examples: - | #include + #include #include hwlock@1c18000 { @@ -44,5 +49,6 @@ examples: reg = <0x01c18000 0x1000>; clocks = <&ccu CLK_BUS_SPINLOCK>; resets = <&ccu RST_BUS_SPINLOCK>; + interrupts = ; }; ... From 2de4751444096c41a5598eebff99d5248e7615f5 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 14 Nov 2021 12:36:52 -0600 Subject: [PATCH 016/153] dt-bindings: hwlock: sun6i: Add per-SoC compatibles While all implementations of this hardware appear to be indentical, it is possible that some difference exists. To be safe, add a compatible for each SoC integration, using the A31 compatible only as a fallback. Signed-off-by: Samuel Holland --- .../hwlock/allwinner,sun6i-a31-hwspinlock.yaml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml b/Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml index ba47f1ef29bf64..9fbde6c742b57a 100644 --- a/Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml +++ b/Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml @@ -15,7 +15,21 @@ description: properties: compatible: - const: allwinner,sun6i-a31-hwspinlock + oneOf: + - items: + - enum: + - allwinner,sun8i-a23-hwspinlock + - allwinner,sun8i-a33-hwspinlock + - allwinner,sun8i-a50-hwspinlock + - allwinner,sun8i-a83t-hwspinlock + - allwinner,sun8i-h3-hwspinlock + - allwinner,sun9i-a80-hwspinlock + - allwinner,sun20i-d1-hwspinlock + - allwinner,sun50i-a64-hwspinlock + - allwinner,sun50i-h6-hwspinlock + - allwinner,sun50i-r329-hwspinlock + - const: allwinner,sun6i-a31-hwspinlock + - const: allwinner,sun6i-a31-hwspinlock reg: maxItems: 1 From 0a846a510970dfcfcdbc7072059ec52202c833b4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 6 Jun 2021 10:20:38 -0500 Subject: [PATCH 017/153] dt-bindings: iommu: sun50i: Add compatible for Allwinner D1 D1 contains an IOMMU similar to the one in the H6 SoC, but the D1 variant has no external reset signal. Signed-off-by: Samuel Holland --- .../iommu/allwinner,sun50i-h6-iommu.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml b/Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml index 5e125cf2a88b45..18d3451d4dd57e 100644 --- a/Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml +++ b/Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml @@ -17,7 +17,9 @@ properties: The content of the cell is the master ID. compatible: - const: allwinner,sun50i-h6-iommu + enum: + - allwinner,sun20i-d1-iommu + - allwinner,sun50i-h6-iommu reg: maxItems: 1 @@ -37,7 +39,17 @@ required: - reg - interrupts - clocks - - resets + +if: + properties: + compatible: + contains: + enum: + - allwinner,sun50i-h6-iommu + +then: + required: + - resets additionalProperties: false From 262fbc21e8f9605873bc98055930edebcae89567 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 27 Apr 2022 19:01:57 -0500 Subject: [PATCH 018/153] iommu/sun50i: Support variants without an external reset The IOMMU in the Allwinner D1 SoC does not have an external reset line. Only attempt to get the reset on hardware variants which should have one according to the binding. And switch from the deprecated function to the explicit "exclusive" variant. Signed-off-by: Samuel Holland --- drivers/iommu/sun50i-iommu.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c index c54ab477b8fd01..ec07b60016d3bc 100644 --- a/drivers/iommu/sun50i-iommu.c +++ b/drivers/iommu/sun50i-iommu.c @@ -92,6 +92,10 @@ #define NUM_PT_ENTRIES 256 #define PT_SIZE (NUM_PT_ENTRIES * PT_ENTRY_SIZE) +struct sun50i_iommu_variant { + bool has_reset; +}; + struct sun50i_iommu { struct iommu_device iommu; @@ -905,9 +909,14 @@ static irqreturn_t sun50i_iommu_irq(int irq, void *dev_id) static int sun50i_iommu_probe(struct platform_device *pdev) { + const struct sun50i_iommu_variant *variant; struct sun50i_iommu *iommu; int ret, irq; + variant = of_device_get_match_data(&pdev->dev); + if (!variant) + return -EINVAL; + iommu = devm_kzalloc(&pdev->dev, sizeof(*iommu), GFP_KERNEL); if (!iommu) return -ENOMEM; @@ -947,7 +956,8 @@ static int sun50i_iommu_probe(struct platform_device *pdev) goto err_free_group; } - iommu->reset = devm_reset_control_get(&pdev->dev, NULL); + if (variant->has_reset) + iommu->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(iommu->reset)) { dev_err(&pdev->dev, "Couldn't get our reset line.\n"); ret = PTR_ERR(iommu->reset); @@ -987,8 +997,12 @@ static int sun50i_iommu_probe(struct platform_device *pdev) return ret; } +static const struct sun50i_iommu_variant sun50i_h6_iommu = { + .has_reset = true, +}; + static const struct of_device_id sun50i_iommu_dt[] = { - { .compatible = "allwinner,sun50i-h6-iommu", }, + { .compatible = "allwinner,sun50i-h6-iommu", .data = &sun50i_h6_iommu }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, sun50i_iommu_dt); From 1081a05ceda5e81bb74aeb00d83d8c7ead20763e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 27 Apr 2022 19:06:28 -0500 Subject: [PATCH 019/153] iommu/sun50i: Ensure bypass is disabled The H6 variant of the hardware disables bypass by default. The D1 variant of the hardware enables bypass for all masters by default. Since the driver expects bypass to be disabled, ensure that is the case. Signed-off-by: Samuel Holland --- drivers/iommu/sun50i-iommu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c index ec07b60016d3bc..b9e644b9363784 100644 --- a/drivers/iommu/sun50i-iommu.c +++ b/drivers/iommu/sun50i-iommu.c @@ -374,6 +374,8 @@ static int sun50i_iommu_enable(struct sun50i_iommu *iommu) spin_lock_irqsave(&iommu->iommu_lock, flags); + iommu_write(iommu, IOMMU_BYPASS_REG, 0); + iommu_write(iommu, IOMMU_TTB_REG, sun50i_domain->dt_dma); iommu_write(iommu, IOMMU_TLB_PREFETCH_REG, IOMMU_TLB_PREFETCH_MASTER_ENABLE(0) | From bc835e3aee1e4d0f3a67bc2dc87b5e49a18ecf48 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 27 Apr 2022 19:20:58 -0500 Subject: [PATCH 020/153] iommu/sun50i: Add support for the D1 variant D1 contains an IOMMU similar to the one in the H6 SoC, but the D1 variant has no external reset signal. It also has some register definition changes, but none that affect the current driver. Signed-off-by: Samuel Holland --- drivers/iommu/sun50i-iommu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c index b9e644b9363784..1fb707e37fb395 100644 --- a/drivers/iommu/sun50i-iommu.c +++ b/drivers/iommu/sun50i-iommu.c @@ -999,11 +999,15 @@ static int sun50i_iommu_probe(struct platform_device *pdev) return ret; } +static const struct sun50i_iommu_variant sun20i_d1_iommu = { +}; + static const struct sun50i_iommu_variant sun50i_h6_iommu = { .has_reset = true, }; static const struct of_device_id sun50i_iommu_dt[] = { + { .compatible = "allwinner,sun20i-d1-iommu", .data = &sun20i_d1_iommu }, { .compatible = "allwinner,sun50i-h6-iommu", .data = &sun50i_h6_iommu }, { /* sentinel */ }, }; From 35b995c2d0579894f8f24dab34d079125edba55f Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 27 Apr 2022 19:39:28 -0500 Subject: [PATCH 021/153] iommu/sun50i: Ensure the IOMMU can be used for DMA So far, the driver has relied on arch/arm64/Kconfig to select IOMMU_DMA. Unsurprisingly, this does not work on RISC-V, so the driver must select IOMMU_DMA itself. Cover-letter: iommu/sun50i: Allwinner D1 support D1 is a RISC-V SoC from Allwinner's sunxi family. This series adds IOMMU binding and driver support. One piece is still missing to use the IOMMU for DMA allocations: a call to iommu_setup_dma_ops(). On ARM64 this is handled by the architecture's code. RISC-V does not currently select ARCH_HAS_SETUP_DMA_OPS, but it will once Zicbom support[1] is merged. [1]: https://lore.kernel.org/lkml/20220307224620.1933061-2-heiko@sntech.de/ So I cannot follow virtio-iommu.c and call iommu_setup_dma_ops() when ARCH_HAS_SETUP_DMA_OPS=n. However, if I apply the following patch on top of Heiko's non-coherent DMA series, the display engine successfully uses the IOMMU to allocate its framebuffer: --- a/arch/riscv/mm/dma-noncoherent.c +++ b/arch/riscv/mm/dma-noncoherent.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -53,4 +54,7 @@ { /* If a specific device is dma-coherent, set it here */ dev->dma_coherent = coherent; + + if (iommu) + iommu_setup_dma_ops(dev, dma_base, dma_base + size - 1); } END Series-to: Joerg Roedel Series-to: Will Deacon Series-to: iommu@lists.linux-foundation.org Series-cc: Heiko Stuebner Series-cc: Palmer Dabbelt Series-cc: linux-riscv@lists.infradead.org Signed-off-by: Samuel Holland --- drivers/iommu/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index c79a0df090c083..70a0bfa6d90754 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -223,6 +223,7 @@ config SUN50I_IOMMU depends on ARCH_SUNXI || COMPILE_TEST select ARM_DMA_USE_IOMMU select IOMMU_API + select IOMMU_DMA help Support for the IOMMU introduced in the Allwinner H6 SoCs. From 4af5a80d91e1f591498c5cad2d60d387cb21e4ef Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 2 Jul 2022 08:33:55 -0500 Subject: [PATCH 022/153] mfd: sun6i-prcm: Update Kconfig description This driver is used by a specific range of SoCs between when the PRCM hardware was introduced (A31) and when the PRCM CCU driver was added (A83T). It is unlikely to be extended to additional hardware. Update the description to include the full list of applicable SoCs. Series-to: Lee Jones Series-cc: Chen-Yu Tsai Series-cc: Jernej Skrabec Series-cc: linux-sunxi@lists.linux.dev Signed-off-by: Samuel Holland --- drivers/mfd/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3b59456f55452e..9e44a60e230831 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1357,12 +1357,13 @@ config MFD_STA2X11 select REGMAP_MMIO config MFD_SUN6I_PRCM - bool "Allwinner A31 PRCM controller" + bool "Allwinner A31/A23/A33 PRCM controller" depends on ARCH_SUNXI || COMPILE_TEST select MFD_CORE help Support for the PRCM (Power/Reset/Clock Management) unit available - in A31 SoC. + in the A31, A23, and A33 SoCs. Other Allwinner SoCs contain similar + hardware, but they do not use this driver. config MFD_SYSCON bool "System Controller Register R/W Based on Regmap" From 2bd9251a062a5bef6555d38a026a7868738b0dfb Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 2 Jul 2022 14:20:02 -0500 Subject: [PATCH 023/153] drm/sun4i: Update Kconfig defaults and descriptions Allwinner display drivers are split roughly into two generations. The first generation was found on early 32-bit ARM SoCs and contains DE1.0 and a custom HDMI controller. Clarify that these options only apply to a specific list of SoCs, and limit selecting them to 32-bit ARM, to avoid confusion. The second generation, found in A83T and newer SoCs (both 32-bit and 64-bit), contains a DE2.0 and a DesignWare HDMI controller. Since this is the most widely-used generation, enable it by default. The previous default condition (MACH_SUN8I) was limited to 32-bit SoCs. Also enable the DSI controller by default, which is found on 64-bit SoCs as well. Series-to: Maxime Ripard Series-to: Jernej Skrabec Series-to: Chen-Yu Tsai Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/Kconfig | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig index 3a43c436c74a78..1c2f8909f3cd51 100644 --- a/drivers/gpu/drm/sun4i/Kconfig +++ b/drivers/gpu/drm/sun4i/Kconfig @@ -16,23 +16,25 @@ config DRM_SUN4I if DRM_SUN4I config DRM_SUN4I_HDMI - tristate "Allwinner A10 HDMI Controller Support" + tristate "Allwinner A10/A10s/A20/A31 HDMI Controller Support" + depends on ARM || COMPILE_TEST default DRM_SUN4I help - Choose this option if you have an Allwinner SoC with an HDMI - controller. + Choose this option if you have an Allwinner A10/A10s/A20/A31 + SoC with an HDMI controller. config DRM_SUN4I_HDMI_CEC - bool "Allwinner A10 HDMI CEC Support" + bool "Allwinner A10/A10s/A20/A31 HDMI CEC Support" depends on DRM_SUN4I_HDMI select CEC_CORE select CEC_PIN help - Choose this option if you have an Allwinner SoC with an HDMI - controller and want to use CEC. + Choose this option if you have an Allwinner A10/A10s/A20/A31 + SoC with an HDMI controller and want to use CEC. config DRM_SUN4I_BACKEND tristate "Support for Allwinner A10 Display Engine Backend" + depends on ARM || COMPILE_TEST default DRM_SUN4I help Choose this option if you have an Allwinner SoC with the @@ -41,8 +43,8 @@ config DRM_SUN4I_BACKEND selected the module will be called sun4i-backend. config DRM_SUN6I_DSI - tristate "Allwinner A31 MIPI-DSI Controller Support" - default MACH_SUN8I + tristate "Allwinner A31/A64 MIPI-DSI Controller Support" + default DRM_SUN4I select CRC_CCITT select DRM_MIPI_DSI select RESET_CONTROLLER @@ -55,15 +57,17 @@ config DRM_SUN6I_DSI config DRM_SUN8I_DW_HDMI tristate "Support for Allwinner version of DesignWare HDMI" depends on DRM_SUN4I + default DRM_SUN4I select DRM_DW_HDMI help Choose this option if you have an Allwinner SoC with the - DesignWare HDMI controller with custom HDMI PHY. If M is + DesignWare HDMI controller. SoCs that support HDMI and + have a Display Engine 2.0 contain this controller. If M is selected the module will be called sun8i_dw_hdmi. config DRM_SUN8I_MIXER tristate "Support for Allwinner Display Engine 2.0 Mixer" - default MACH_SUN8I + default DRM_SUN4I help Choose this option if you have an Allwinner SoC with the Allwinner Display Engine 2.0, which has a mixer to do some @@ -75,6 +79,6 @@ config DRM_SUN8I_TCON_TOP default DRM_SUN4I if DRM_SUN8I_MIXER!=n help TCON TOP is responsible for configuring display pipeline for - HTMI, TVE and LCD. + HDMI, TVE and LCD. endif From 94bac43b1df8b4d2030582e6213a439dc172e339 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 4 Aug 2021 21:36:26 -0500 Subject: [PATCH 024/153] dt-bindings: leds: Add Allwinner R329/D1 LED controller The Allwinner R329 and D1 SoCs contain an LED controller designed to drive a series of RGB LED pixels. It supports PIO and DMA transfers, and has configurable timing and pixel format. Series-changes: 2 - Fixed typo leading to duplicate t1h-ns property - Removed "items" layer in definition of dmas/dma-names - Replaced uint32 type reference with maxItems in timing properties Series-changes: 3 - Removed quotes from enumeration values Commit-changes: 3 - Added vendor prefix to timing/format properties - Renamed "format" property to "pixel-format" for clarity - Dropped "vled-supply" as it is unrelated to the controller hardware Series-changes: 4 - Use "default" instead of "maxItems" for timing properties Acked-by: Maxime Ripard Reviewed-by: Rob Herring Signed-off-by: Samuel Holland --- .../leds/allwinner,sun50i-r329-ledc.yaml | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/allwinner,sun50i-r329-ledc.yaml diff --git a/Documentation/devicetree/bindings/leds/allwinner,sun50i-r329-ledc.yaml b/Documentation/devicetree/bindings/leds/allwinner,sun50i-r329-ledc.yaml new file mode 100644 index 00000000000000..3db3fe766e6ab5 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/allwinner,sun50i-r329-ledc.yaml @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/allwinner,sun50i-r329-ledc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner R329 LED Controller Bindings + +maintainers: + - Samuel Holland + +description: + The LED controller found in Allwinner sunxi SoCs uses a one-wire serial + interface to drive up to 1024 RGB LEDs. + +properties: + compatible: + oneOf: + - const: allwinner,sun50i-r329-ledc + - items: + - enum: + - allwinner,sun20i-d1-ledc + - const: allwinner,sun50i-r329-ledc + + reg: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + clocks: + items: + - description: Bus clock + - description: Module clock + + clock-names: + items: + - const: bus + - const: mod + + resets: + maxItems: 1 + + dmas: + maxItems: 1 + description: TX DMA channel + + dma-names: + const: tx + + interrupts: + maxItems: 1 + + allwinner,pixel-format: + description: Pixel format (subpixel transmission order), default is "grb" + enum: + - bgr + - brg + - gbr + - grb + - rbg + - rgb + + allwinner,t0h-ns: + default: 336 + description: Length of high pulse when transmitting a "0" bit + + allwinner,t0l-ns: + default: 840 + description: Length of low pulse when transmitting a "0" bit + + allwinner,t1h-ns: + default: 882 + description: Length of high pulse when transmitting a "1" bit + + allwinner,t1l-ns: + default: 294 + description: Length of low pulse when transmitting a "1" bit + + allwinner,treset-ns: + default: 300000 + description: Minimum delay between transmission frames + +patternProperties: + "^multi-led@[0-9a-f]+$": + type: object + $ref: leds-class-multicolor.yaml# + properties: + reg: + minimum: 0 + maximum: 1023 + description: Index of the LED in the series (must be contiguous) + + required: + - reg + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - dmas + - dma-names + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + + ledc: led-controller@2008000 { + compatible = "allwinner,sun20i-d1-ledc", + "allwinner,sun50i-r329-ledc"; + reg = <0x2008000 0x400>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu 12>, <&ccu 34>; + clock-names = "bus", "mod"; + resets = <&ccu 12>; + dmas = <&dma 42>; + dma-names = "tx"; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH>; + + multi-led@0 { + reg = <0x0>; + color = ; + function = LED_FUNCTION_INDICATOR; + }; + }; + +... From 0bd53e10c98a8c1101a9477d452fdb5dcb9359c4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 26 Jun 2021 11:02:49 -0500 Subject: [PATCH 025/153] leds: sun50i-r329: New driver for the R329/D1 LED controller Some Allwinner sunxi SoCs, starting with the R329, contain an LED controller designed to drive RGB LED pixels. Add a driver for it using the multicolor LED framework, and with LEDs defined in the device tree. Series-version: 4 Series-changes: 2 - Renamed from sunxi-ledc to sun50i-r329-ledc - Added missing "static" to functions/globals as reported by 0day bot Series-changes: 3 - Added vendor prefix to timing/format properties - Renamed "format" property to "pixel-format" for clarity - Dropped "vled-supply" as it is unrelated to the controller hardware - Changed "writesl" to "iowrite32_rep" so the driver builds on hppa Series-changes: 4 - Depend on LEDS_CLASS_MULTICOLOR Series-to: Pavel Machek Series-to: linux-leds@vger.kernel.org Series-cc: devicetree Series-cc: linux-sunxi Series-cc: lakml, lkml Signed-off-by: Samuel Holland --- drivers/leds/Kconfig | 8 + drivers/leds/Makefile | 1 + drivers/leds/leds-sun50i-r329.c | 553 ++++++++++++++++++++++++++++++++ 3 files changed, 562 insertions(+) create mode 100644 drivers/leds/leds-sun50i-r329.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index a49979f41eee76..9c8fb9698739cb 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -281,6 +281,14 @@ config LEDS_COBALT_RAQ help This option enables support for the Cobalt Raq series LEDs. +config LEDS_SUN50I_R329 + tristate "LED support for Allwinner R329 LED controller" + depends on LEDS_CLASS_MULTICOLOR + depends on ARCH_SUNXI || COMPILE_TEST + help + This option enables support for the RGB LED controller + provided in some Allwinner sunxi SoCs, like the R329. + config LEDS_SUNFIRE tristate "LED support for SunFire servers." depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 4fd2f92cd19811..4fd95ad3d4c5bb 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o +obj-$(CONFIG_LEDS_SUN50I_R329) += leds-sun50i-r329.o obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o diff --git a/drivers/leds/leds-sun50i-r329.c b/drivers/leds/leds-sun50i-r329.c new file mode 100644 index 00000000000000..ada6625e2bbc3e --- /dev/null +++ b/drivers/leds/leds-sun50i-r329.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2021 Samuel Holland +// +// Partly based on drivers/leds/leds-turris-omnia.c, which is: +// Copyright (c) 2020 by Marek Behún +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LEDC_CTRL_REG 0x0000 +#define LEDC_CTRL_REG_DATA_LENGTH (0x1fff << 16) +#define LEDC_CTRL_REG_RGB_MODE (0x7 << 6) +#define LEDC_CTRL_REG_LEDC_EN BIT(0) +#define LEDC_T01_TIMING_CTRL_REG 0x0004 +#define LEDC_T01_TIMING_CTRL_REG_T1H (0x3f << 21) +#define LEDC_T01_TIMING_CTRL_REG_T1L (0x1f << 16) +#define LEDC_T01_TIMING_CTRL_REG_T0H (0x1f << 6) +#define LEDC_T01_TIMING_CTRL_REG_T0L (0x3f << 0) +#define LEDC_RESET_TIMING_CTRL_REG 0x000c +#define LEDC_RESET_TIMING_CTRL_REG_LED_NUM (0x3ff << 0) +#define LEDC_DATA_REG 0x0014 +#define LEDC_DMA_CTRL_REG 0x0018 +#define LEDC_DMA_CTRL_REG_FIFO_TRIG_LEVEL (0x1f << 0) +#define LEDC_INT_CTRL_REG 0x001c +#define LEDC_INT_CTRL_REG_GLOBAL_INT_EN BIT(5) +#define LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN BIT(1) +#define LEDC_INT_CTRL_REG_TRANS_FINISH_INT_EN BIT(0) +#define LEDC_INT_STS_REG 0x0020 +#define LEDC_INT_STS_REG_FIFO_CPUREQ_INT BIT(1) +#define LEDC_INT_STS_REG_TRANS_FINISH_INT BIT(0) + +#define LEDC_FIFO_DEPTH 32 +#define LEDC_MAX_LEDS 1024 + +#define LEDS_TO_BYTES(n) ((n) * sizeof(u32)) + +struct sun50i_r329_ledc_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled_info[3]; +}; +#define to_ledc_led(mc) container_of(mc, struct sun50i_r329_ledc_led, mc_cdev) + +struct sun50i_r329_ledc_timing { + u32 t0h_ns; + u32 t0l_ns; + u32 t1h_ns; + u32 t1l_ns; + u32 treset_ns; +}; + +struct sun50i_r329_ledc { + struct device *dev; + void __iomem *base; + struct clk *bus_clk; + struct clk *mod_clk; + struct reset_control *reset; + + u32 *buffer; + struct dma_chan *dma_chan; + dma_addr_t dma_handle; + int pio_length; + int pio_offset; + + spinlock_t lock; + int next_length; + bool xfer_active; + + u32 format; + struct sun50i_r329_ledc_timing timing; + + int num_leds; + struct sun50i_r329_ledc_led leds[]; +}; + +static int sun50i_r329_ledc_dma_xfer(struct sun50i_r329_ledc *priv, int length) +{ + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + + desc = dmaengine_prep_slave_single(priv->dma_chan, priv->dma_handle, + LEDS_TO_BYTES(length), + DMA_MEM_TO_DEV, 0); + if (!desc) + return -ENOMEM; + + cookie = dmaengine_submit(desc); + if (dma_submit_error(cookie)) + return -EIO; + + dma_async_issue_pending(priv->dma_chan); + + return 0; +} + +static void sun50i_r329_ledc_pio_xfer(struct sun50i_r329_ledc *priv, int length) +{ + u32 burst, offset, val; + + if (length) { + /* New transfer (FIFO is empty). */ + offset = 0; + burst = min(length, LEDC_FIFO_DEPTH); + } else { + /* Existing transfer (FIFO is half-full). */ + length = priv->pio_length; + offset = priv->pio_offset; + burst = min(length, LEDC_FIFO_DEPTH / 2); + } + + iowrite32_rep(priv->base + LEDC_DATA_REG, priv->buffer + offset, burst); + + if (burst < length) { + priv->pio_length = length - burst; + priv->pio_offset = offset + burst; + + if (!offset) { + val = readl(priv->base + LEDC_INT_CTRL_REG); + val |= LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN; + writel(val, priv->base + LEDC_INT_CTRL_REG); + } + } else { + /* Disable the request IRQ once all data is written. */ + val = readl(priv->base + LEDC_INT_CTRL_REG); + val &= ~LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN; + writel(val, priv->base + LEDC_INT_CTRL_REG); + } +} + +static void sun50i_r329_ledc_start_xfer(struct sun50i_r329_ledc *priv, + int length) +{ + u32 val; + + dev_dbg(priv->dev, "Updating %d LEDs\n", length); + + val = readl(priv->base + LEDC_CTRL_REG); + val &= ~LEDC_CTRL_REG_DATA_LENGTH; + val |= length << 16 | LEDC_CTRL_REG_LEDC_EN; + writel(val, priv->base + LEDC_CTRL_REG); + + if (length > LEDC_FIFO_DEPTH) { + int ret = sun50i_r329_ledc_dma_xfer(priv, length); + + if (!ret) + return; + + dev_warn(priv->dev, "Failed to set up DMA: %d\n", ret); + } + + sun50i_r329_ledc_pio_xfer(priv, length); +} + +static irqreturn_t sun50i_r329_ledc_irq(int irq, void *dev_id) +{ + struct sun50i_r329_ledc *priv = dev_id; + u32 val; + + val = readl(priv->base + LEDC_INT_STS_REG); + + if (val & LEDC_INT_STS_REG_TRANS_FINISH_INT) { + int next_length; + + /* Start the next transfer if needed. */ + spin_lock(&priv->lock); + next_length = priv->next_length; + if (next_length) + priv->next_length = 0; + else + priv->xfer_active = false; + spin_unlock(&priv->lock); + + if (next_length) + sun50i_r329_ledc_start_xfer(priv, next_length); + } else if (val & LEDC_INT_STS_REG_FIFO_CPUREQ_INT) { + /* Continue the current transfer. */ + sun50i_r329_ledc_pio_xfer(priv, 0); + } + + writel(val, priv->base + LEDC_INT_STS_REG); + + return IRQ_HANDLED; +} + +static void sun50i_r329_ledc_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct sun50i_r329_ledc *priv = dev_get_drvdata(cdev->dev->parent); + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct sun50i_r329_ledc_led *led = to_ledc_led(mc_cdev); + int addr = led - priv->leds; + unsigned long flags; + bool xfer_active; + int next_length; + + led_mc_calc_color_components(mc_cdev, brightness); + + priv->buffer[addr] = led->subled_info[0].brightness << 16 | + led->subled_info[1].brightness << 8 | + led->subled_info[2].brightness; + + dev_dbg(priv->dev, "LED %d -> #%06x\n", addr, priv->buffer[addr]); + + spin_lock_irqsave(&priv->lock, flags); + next_length = max(priv->next_length, addr + 1); + xfer_active = priv->xfer_active; + if (xfer_active) + priv->next_length = next_length; + else + priv->xfer_active = true; + spin_unlock_irqrestore(&priv->lock, flags); + + if (!xfer_active) + sun50i_r329_ledc_start_xfer(priv, next_length); +} + +static const char *const sun50i_r329_ledc_formats[] = { + "rgb", + "rbg", + "grb", + "gbr", + "brg", + "bgr", +}; + +static int sun50i_r329_ledc_parse_format(const struct device_node *np, + struct sun50i_r329_ledc *priv) +{ + const char *format = "grb"; + u32 i; + + of_property_read_string(np, "allwinner,pixel-format", &format); + + for (i = 0; i < ARRAY_SIZE(sun50i_r329_ledc_formats); ++i) { + if (!strcmp(format, sun50i_r329_ledc_formats[i])) { + priv->format = i; + return 0; + } + } + + dev_err(priv->dev, "Bad pixel format '%s'\n", format); + + return -EINVAL; +} + +static void sun50i_r329_ledc_set_format(struct sun50i_r329_ledc *priv) +{ + u32 val; + + val = readl(priv->base + LEDC_CTRL_REG); + val &= ~LEDC_CTRL_REG_RGB_MODE; + val |= priv->format << 6; + writel(val, priv->base + LEDC_CTRL_REG); +} + +static const struct sun50i_r329_ledc_timing sun50i_r329_ledc_default_timing = { + .t0h_ns = 336, + .t0l_ns = 840, + .t1h_ns = 882, + .t1l_ns = 294, + .treset_ns = 300000, +}; + +static int sun50i_r329_ledc_parse_timing(const struct device_node *np, + struct sun50i_r329_ledc *priv) +{ + struct sun50i_r329_ledc_timing *timing = &priv->timing; + + *timing = sun50i_r329_ledc_default_timing; + + of_property_read_u32(np, "allwinner,t0h-ns", &timing->t0h_ns); + of_property_read_u32(np, "allwinner,t0l-ns", &timing->t0l_ns); + of_property_read_u32(np, "allwinner,t1h-ns", &timing->t1h_ns); + of_property_read_u32(np, "allwinner,t1l-ns", &timing->t1l_ns); + of_property_read_u32(np, "allwinner,treset-ns", &timing->treset_ns); + + return 0; +} + +static void sun50i_r329_ledc_set_timing(struct sun50i_r329_ledc *priv) +{ + const struct sun50i_r329_ledc_timing *timing = &priv->timing; + unsigned long mod_freq = clk_get_rate(priv->mod_clk); + u32 cycle_ns = NSEC_PER_SEC / mod_freq; + u32 val; + + val = (timing->t1h_ns / cycle_ns) << 21 | + (timing->t1l_ns / cycle_ns) << 16 | + (timing->t0h_ns / cycle_ns) << 6 | + (timing->t0l_ns / cycle_ns); + writel(val, priv->base + LEDC_T01_TIMING_CTRL_REG); + + val = (timing->treset_ns / cycle_ns) << 16 | + (priv->num_leds - 1); + writel(val, priv->base + LEDC_RESET_TIMING_CTRL_REG); +} + +static int sun50i_r329_ledc_resume(struct device *dev) +{ + struct sun50i_r329_ledc *priv = dev_get_drvdata(dev); + u32 val; + int ret; + + ret = reset_control_deassert(priv->reset); + if (ret) + return ret; + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) + goto err_assert_reset; + + ret = clk_prepare_enable(priv->mod_clk); + if (ret) + goto err_disable_bus_clk; + + sun50i_r329_ledc_set_format(priv); + sun50i_r329_ledc_set_timing(priv); + + /* The trigger level must be at least the burst length. */ + val = readl(priv->base + LEDC_DMA_CTRL_REG); + val &= ~LEDC_DMA_CTRL_REG_FIFO_TRIG_LEVEL; + val |= LEDC_FIFO_DEPTH / 2; + writel(val, priv->base + LEDC_DMA_CTRL_REG); + + val = LEDC_INT_CTRL_REG_GLOBAL_INT_EN | + LEDC_INT_CTRL_REG_TRANS_FINISH_INT_EN; + writel(val, priv->base + LEDC_INT_CTRL_REG); + + return 0; + +err_disable_bus_clk: + clk_disable_unprepare(priv->bus_clk); +err_assert_reset: + reset_control_assert(priv->reset); + + return ret; +} + +static int sun50i_r329_ledc_suspend(struct device *dev) +{ + struct sun50i_r329_ledc *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->mod_clk); + clk_disable_unprepare(priv->bus_clk); + reset_control_assert(priv->reset); + + return 0; +} + +static void sun50i_r329_ledc_dma_cleanup(void *data) +{ + struct sun50i_r329_ledc *priv = data; + struct device *dma_dev = dmaengine_get_dma_device(priv->dma_chan); + + if (priv->buffer) + dma_free_wc(dma_dev, LEDS_TO_BYTES(priv->num_leds), + priv->buffer, priv->dma_handle); + dma_release_channel(priv->dma_chan); +} + +static int sun50i_r329_ledc_probe(struct platform_device *pdev) +{ + const struct device_node *np = pdev->dev.of_node; + struct dma_slave_config dma_cfg = {}; + struct led_init_data init_data = {}; + struct device *dev = &pdev->dev; + struct device_node *child; + struct sun50i_r329_ledc *priv; + struct resource *mem; + int count, irq, ret; + + count = of_get_available_child_count(np); + if (!count) + return -ENODEV; + if (count > LEDC_MAX_LEDS) { + dev_err(dev, "Too many LEDs! (max is %d)\n", LEDC_MAX_LEDS); + return -EINVAL; + } + + priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->num_leds = count; + spin_lock_init(&priv->lock); + dev_set_drvdata(dev, priv); + + ret = sun50i_r329_ledc_parse_format(np, priv); + if (ret) + return ret; + + ret = sun50i_r329_ledc_parse_timing(np, priv); + if (ret) + return ret; + + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(priv->bus_clk)) + return PTR_ERR(priv->bus_clk); + + priv->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(priv->mod_clk)) + return PTR_ERR(priv->mod_clk); + + priv->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + + priv->dma_chan = dma_request_chan(dev, "tx"); + if (IS_ERR(priv->dma_chan)) + return PTR_ERR(priv->dma_chan); + + ret = devm_add_action_or_reset(dev, sun50i_r329_ledc_dma_cleanup, priv); + if (ret) + return ret; + + dma_cfg.dst_addr = mem->start + LEDC_DATA_REG; + dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_cfg.dst_maxburst = LEDC_FIFO_DEPTH / 2; + ret = dmaengine_slave_config(priv->dma_chan, &dma_cfg); + if (ret) + return ret; + + priv->buffer = dma_alloc_wc(dmaengine_get_dma_device(priv->dma_chan), + LEDS_TO_BYTES(priv->num_leds), + &priv->dma_handle, GFP_KERNEL); + if (!priv->buffer) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, sun50i_r329_ledc_irq, + 0, dev_name(dev), priv); + if (ret) + return ret; + + ret = sun50i_r329_ledc_resume(dev); + if (ret) + return ret; + + for_each_available_child_of_node(np, child) { + struct sun50i_r329_ledc_led *led; + struct led_classdev *cdev; + u32 addr, color; + + ret = of_property_read_u32(child, "reg", &addr); + if (ret || addr >= count) { + dev_err(dev, "LED 'reg' values must be from 0 to %d\n", + priv->num_leds - 1); + ret = -EINVAL; + goto err_put_child; + } + + ret = of_property_read_u32(child, "color", &color); + if (ret || color != LED_COLOR_ID_RGB) { + dev_err(dev, "LED 'color' must be LED_COLOR_ID_RGB\n"); + ret = -EINVAL; + goto err_put_child; + } + + led = &priv->leds[addr]; + + led->subled_info[0].color_index = LED_COLOR_ID_RED; + led->subled_info[0].channel = 0; + led->subled_info[1].color_index = LED_COLOR_ID_GREEN; + led->subled_info[1].channel = 1; + led->subled_info[2].color_index = LED_COLOR_ID_BLUE; + led->subled_info[2].channel = 2; + + led->mc_cdev.num_colors = ARRAY_SIZE(led->subled_info); + led->mc_cdev.subled_info = led->subled_info; + + cdev = &led->mc_cdev.led_cdev; + cdev->max_brightness = U8_MAX; + cdev->brightness_set = sun50i_r329_ledc_brightness_set; + + init_data.fwnode = of_fwnode_handle(child); + + ret = devm_led_classdev_multicolor_register_ext(dev, + &led->mc_cdev, + &init_data); + if (ret) { + dev_err(dev, "Failed to register LED %u: %d\n", + addr, ret); + goto err_put_child; + } + } + + dev_info(dev, "Registered %d LEDs\n", priv->num_leds); + + return 0; + +err_put_child: + of_node_put(child); + sun50i_r329_ledc_suspend(&pdev->dev); + + return ret; +} + +static int sun50i_r329_ledc_remove(struct platform_device *pdev) +{ + sun50i_r329_ledc_suspend(&pdev->dev); + + return 0; +} + +static void sun50i_r329_ledc_shutdown(struct platform_device *pdev) +{ + sun50i_r329_ledc_suspend(&pdev->dev); +} + +static const struct of_device_id sun50i_r329_ledc_of_match[] = { + { .compatible = "allwinner,sun50i-r329-ledc" }, + {} +}; +MODULE_DEVICE_TABLE(of, sun50i_r329_ledc_of_match); + +static SIMPLE_DEV_PM_OPS(sun50i_r329_ledc_pm, + sun50i_r329_ledc_suspend, sun50i_r329_ledc_resume); + +static struct platform_driver sun50i_r329_ledc_driver = { + .probe = sun50i_r329_ledc_probe, + .remove = sun50i_r329_ledc_remove, + .shutdown = sun50i_r329_ledc_shutdown, + .driver = { + .name = "sun50i-r329-ledc", + .of_match_table = sun50i_r329_ledc_of_match, + .pm = pm_ptr(&sun50i_r329_ledc_pm), + }, +}; +module_platform_driver(sun50i_r329_ledc_driver); + +MODULE_AUTHOR("Samuel Holland "); +MODULE_DESCRIPTION("Allwinner R329 LED controller driver"); +MODULE_LICENSE("GPL"); From 5e13b09b6e2cc0ef96b64a4ceae300e62e2ad00e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:15:56 -0500 Subject: [PATCH 026/153] mmc: sunxi-mmc: Correct the maximum segment size According to the DMA descriptor documentation, the lowest two bits of the size field are ignored, so the size must be rounded up to a multiple of 4 bytes. Furthermore, 0 is not a valid buffer size; setting the size to 0 will cause that DMA descriptor to be ignored. Together, these restrictions limit the maximum DMA segment size to 4 less than the power-of-two width of the size field. Series-to: Ulf Hansson Series-to: linux-mmc@vger.kernel.org Fixes: 3cbcb16095f9 ("mmc: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs") Signed-off-by: Samuel Holland --- drivers/mmc/host/sunxi-mmc.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index b16e12e62e7222..05c3c3f8488d01 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -214,6 +214,9 @@ #define SDXC_IDMAC_DES0_CES BIT(30) /* card error summary */ #define SDXC_IDMAC_DES0_OWN BIT(31) /* 1-idma owns it, 0-host owns it */ +/* Buffer size must be a multiple of 4 bytes. */ +#define SDXC_IDMAC_SIZE_ALIGN 4 + #define SDXC_CLK_400K 0 #define SDXC_CLK_25M 1 #define SDXC_CLK_50M 2 @@ -361,17 +364,15 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, { struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu; dma_addr_t next_desc = host->sg_dma; - int i, max_len = (1 << host->cfg->idma_des_size_bits); + int i; for (i = 0; i < data->sg_len; i++) { pdes[i].config = cpu_to_le32(SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_DIC); - if (data->sg[i].length == max_len) - pdes[i].buf_size = 0; /* 0 == max_len */ - else - pdes[i].buf_size = cpu_to_le32(data->sg[i].length); + pdes[i].buf_size = cpu_to_le32(ALIGN(data->sg[i].length, + SDXC_IDMAC_SIZE_ALIGN)); next_desc += sizeof(struct sunxi_idma_des); pdes[i].buf_addr_ptr1 = @@ -1421,7 +1422,8 @@ static int sunxi_mmc_probe(struct platform_device *pdev) mmc->max_blk_count = 8192; mmc->max_blk_size = 4096; mmc->max_segs = PAGE_SIZE / sizeof(struct sunxi_idma_des); - mmc->max_seg_size = (1 << host->cfg->idma_des_size_bits); + mmc->max_seg_size = (1 << host->cfg->idma_des_size_bits) - + SDXC_IDMAC_SIZE_ALIGN; mmc->max_req_size = mmc->max_seg_size * mmc->max_segs; /* 400kHz ~ 52MHz */ mmc->f_min = 400000; From 5b17c8f11e645f7fdd2c6a50fd73598773e41882 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 30 Jul 2022 00:33:54 -0500 Subject: [PATCH 027/153] nvmem: sunxi_sid: Always use 32-bit MMIO reads The SID SRAM on at least some SoCs (A64 and D1) returns different values when read with bus cycles narrower than 32 bits. This is not immediately obvious, because memcpy_fromio() uses word-size accesses as long as enough data is being copied. The vendor driver always uses 32-bit MMIO reads, so let's do the same here. This removes the need for the slower register-based readout method on A64, and it fixes readout on D1 where the SRAM method was being used. The special case for the last word is needed to maintain .word_size == 1 for sysfs ABI compatibility, as noted previously in commit de2a3eaea552 ("nvmem: sunxi_sid: Optimize register read-out method"). Fixes: 07ae4fde9efa ("nvmem: sunxi_sid: Add support for D1 variant") Signed-off-by: Samuel Holland --- drivers/nvmem/sunxi_sid.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 5750e1f4bcdbbf..92dfe4cb10e386 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -41,8 +41,21 @@ static int sunxi_sid_read(void *context, unsigned int offset, void *val, size_t bytes) { struct sunxi_sid *sid = context; + u32 word; + + /* .stride = 4 so offset is guaranteed to be aligned */ + __ioread32_copy(val, sid->base + sid->value_offset + offset, bytes / 4); - memcpy_fromio(val, sid->base + sid->value_offset + offset, bytes); + val += round_down(bytes, 4); + offset += round_down(bytes, 4); + bytes = bytes % 4; + + if (!bytes) + return 0; + + /* Handle any trailing bytes */ + word = readl_relaxed(sid->base + sid->value_offset + offset); + memcpy(val, &word, bytes); return 0; } From 452b7bcdf0dfda2d60c9ad87d2b2a1328c79f677 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 31 Jul 2022 20:34:20 -0500 Subject: [PATCH 028/153] nvmem: sunxi_sid: Drop the workaround on A64 Now that the SRAM readout code is fixed, and it returns the same values as register readout, the A64 variant can switch back to SRAM readout. This makes the D1 variant structure redundant, so remove it. Signed-off-by: Samuel Holland --- drivers/nvmem/sunxi_sid.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 92dfe4cb10e386..a970f1741cc63d 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -197,15 +197,9 @@ static const struct sunxi_sid_cfg sun8i_h3_cfg = { .need_register_readout = true, }; -static const struct sunxi_sid_cfg sun20i_d1_cfg = { - .value_offset = 0x200, - .size = 0x100, -}; - static const struct sunxi_sid_cfg sun50i_a64_cfg = { .value_offset = 0x200, .size = 0x100, - .need_register_readout = true, }; static const struct sunxi_sid_cfg sun50i_h6_cfg = { @@ -218,7 +212,7 @@ static const struct of_device_id sunxi_sid_of_match[] = { { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, - { .compatible = "allwinner,sun20i-d1-sid", .data = &sun20i_d1_cfg }, + { .compatible = "allwinner,sun20i-d1-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg }, From 48059753f593124dd05b63c990aa0244559b39df Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 31 Jul 2022 20:58:17 -0500 Subject: [PATCH 029/153] nvmem: core: Support reading cells with >= 8 bit offsets For NVMEM devices with .stride > 1, some cell values may not be aligned to the device's stride. In this case, it is necessary to use bit_offset to access the cell. For example, to access the third byte of an NVMEM device with .stride == 4, we need "bits = <16 8>;" in the devicetree. Implement this on the read side. The write side implementation would be more complicated, and it is not necessary for read-only NVMEM devices. For now, reject writes for these cells to avoid any incorrect behavior. Signed-off-by: Samuel Holland --- drivers/nvmem/core.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 1e3c754efd0d8d..f883b4d9a95ae1 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1373,63 +1373,67 @@ void nvmem_cell_put(struct nvmem_cell *cell) } EXPORT_SYMBOL_GPL(nvmem_cell_put); -static void nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void *buf) +static int nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void *buf) { u8 *p, *b; - int i, extra, bit_offset = cell->bit_offset; + int i, bit_offset = cell->bit_offset, bytes = cell->bytes; p = b = buf; if (bit_offset) { + int byte_offset = bit_offset / BITS_PER_BYTE; + + b += byte_offset; + bytes -= byte_offset; + bit_offset %= BITS_PER_BYTE; + /* First shift */ - *b++ >>= bit_offset; + *p = *b++ >> bit_offset; /* setup rest of the bytes if any */ - for (i = 1; i < cell->bytes; i++) { + for (i = 1; i < bytes; i++) { /* Get bits from next byte and shift them towards msb */ - *p |= *b << (BITS_PER_BYTE - bit_offset); - - p = b; - *b++ >>= bit_offset; + *p++ |= *b << (BITS_PER_BYTE - bit_offset); + *p = *b++ >> bit_offset; } - } else { - /* point to the msb */ - p += cell->bytes - 1; } /* result fits in less bytes */ - extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE); - while (--extra >= 0) - *p-- = 0; + bytes = DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE); + p = buf + bytes; + memset(p, 0, cell->bytes - bytes); /* clear msb bits if any leftover in the last byte */ if (cell->nbits % BITS_PER_BYTE) - *p &= GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0); + p[-1] &= GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0); + + return bytes; } static int __nvmem_cell_read(struct nvmem_device *nvmem, struct nvmem_cell_entry *cell, void *buf, size_t *len, const char *id) { + int bytes = cell->bytes; int rc; - rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes); + rc = nvmem_reg_read(nvmem, cell->offset, buf, bytes); if (rc) return rc; /* shift bits in-place */ if (cell->bit_offset || cell->nbits) - nvmem_shift_read_buffer_in_place(cell, buf); + bytes = nvmem_shift_read_buffer_in_place(cell, buf); if (nvmem->cell_post_process) { rc = nvmem->cell_post_process(nvmem->priv, id, - cell->offset, buf, cell->bytes); + cell->offset, buf, bytes); if (rc) return rc; } if (len) - *len = cell->bytes; + *len = bytes; return 0; } @@ -1526,6 +1530,7 @@ static int __nvmem_cell_entry_write(struct nvmem_cell_entry *cell, void *buf, si int rc; if (!nvmem || nvmem->read_only || + cell->bit_offset >= BITS_PER_BYTE || (cell->bit_offset == 0 && len != cell->bytes)) return -EINVAL; From 7754a6d1171a702e39670fa538519cfb5fbfc8fd Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 13 Nov 2021 10:08:41 -0600 Subject: [PATCH 030/153] ASoC: dt-bindings: sun4i-spdif: Require resets for H6 The H6 variant has a module reset, and it is used by the driver. So the resets property should be required in the binding for this variant. Fixes: b20453031472 ("dt-bindings: sound: sun4i-spdif: Add Allwinner H6 compatible") Signed-off-by: Samuel Holland --- .../devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml index 444a432912bb4e..1118930980f3e5 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml @@ -61,6 +61,7 @@ allOf: enum: - allwinner,sun6i-a31-spdif - allwinner,sun8i-h3-spdif + - allwinner,sun50i-h6-spdif then: required: From 8f09432d5b356aaed5b9a4bf5620a40281846a9c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 13 Nov 2021 10:48:24 -0600 Subject: [PATCH 031/153] ASoC: dt-bindings: sun4i-spdif: Add compatible for D1 D1 mostly keeps the existing register layout, but it separates the module clock into separate clocks for the RX block and the TX block. Signed-off-by: Samuel Holland --- .../sound/allwinner,sun4i-a10-spdif.yaml | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml index 1118930980f3e5..3af7fec163a24a 100644 --- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml +++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml @@ -18,10 +18,12 @@ properties: compatible: oneOf: - - const: allwinner,sun4i-a10-spdif - - const: allwinner,sun6i-a31-spdif - - const: allwinner,sun8i-h3-spdif - - const: allwinner,sun50i-h6-spdif + - enum: + - allwinner,sun4i-a10-spdif + - allwinner,sun6i-a31-spdif + - allwinner,sun8i-h3-spdif + - allwinner,sun20i-d1-spdif + - allwinner,sun50i-h6-spdif - items: - const: allwinner,sun8i-a83t-spdif - const: allwinner,sun8i-h3-spdif @@ -36,14 +38,12 @@ properties: maxItems: 1 clocks: - items: - - description: Bus Clock - - description: Module Clock + minItems: 2 + maxItems: 3 clock-names: - items: - - const: apb - - const: spdif + minItems: 2 + maxItems: 3 # Even though it only applies to subschemas under the conditionals, # not listing them here will trigger a warning because of the @@ -54,6 +54,39 @@ properties: maxItems: 1 allOf: + - if: + properties: + compatible: + contains: + enum: + - allwinner,sun20i-d1-spdif + + then: + properties: + clocks: + items: + - description: Bus Clock + - description: RX Module Clock + - description: TX Module Clock + + clock-names: + items: + - const: apb + - const: rx + - const: tx + + else: + properties: + clocks: + items: + - description: Bus Clock + - description: Module Clock + + clock-names: + items: + - const: apb + - const: spdif + - if: properties: compatible: @@ -61,6 +94,7 @@ allOf: enum: - allwinner,sun6i-a31-spdif - allwinner,sun8i-h3-spdif + - allwinner,sun20i-d1-spdif - allwinner,sun50i-h6-spdif then: From 0f4f3f9bf155f4cf2d43eb168615645327f9ffe1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 13 Nov 2021 11:12:14 -0600 Subject: [PATCH 032/153] ASoC: sun4i-spdif: Assert reset when removing the device This completes reversing the process done in the probe function. Signed-off-by: Samuel Holland --- sound/soc/sunxi/sun4i-spdif.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index bcceebca915ac8..221b7b9993fda2 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -705,10 +705,14 @@ static int sun4i_spdif_probe(struct platform_device *pdev) static int sun4i_spdif_remove(struct platform_device *pdev) { + struct sun4i_spdif_dev *host = dev_get_drvdata(&pdev->dev); + pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) sun4i_spdif_runtime_suspend(&pdev->dev); + reset_control_assert(host->rst); + return 0; } From 170a7c45f4721920ca15501dcea127ce96d4f5e8 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 13 Nov 2021 11:14:30 -0600 Subject: [PATCH 033/153] ASoC: sun4i-spdif: Simplify code around optional resets The driver does not need to care about which variants have a reset; the devicetree binding already enforces that the necessary resources are provided. Simplify the logic by always calling the optional getter, which will return NULL if no reset reference is found. Also clean up the error handling, which should not print a misleading error in the EPROBE_DEFER case. Signed-off-by: Samuel Holland --- sound/soc/sunxi/sun4i-spdif.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index 221b7b9993fda2..f0fae7766292b4 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -170,12 +170,10 @@ * struct sun4i_spdif_quirks - Differences between SoC variants. * * @reg_dac_txdata: TX FIFO offset for DMA config. - * @has_reset: SoC needs reset deasserted. * @val_fctl_ftx: TX FIFO flush bitmask. */ struct sun4i_spdif_quirks { unsigned int reg_dac_txdata; - bool has_reset; unsigned int val_fctl_ftx; }; @@ -546,19 +544,16 @@ static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, - .has_reset = true, }; static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, - .has_reset = true, }; static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, - .has_reset = true, }; static const struct of_device_id sun4i_spdif_of_match[] = { @@ -667,17 +662,12 @@ static int sun4i_spdif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); - if (quirks->has_reset) { - host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, - NULL); - if (PTR_ERR(host->rst) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); - return ret; - } - if (!IS_ERR(host->rst)) - reset_control_deassert(host->rst); - } + host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(host->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->rst), + "Failed to get reset\n"); + + reset_control_deassert(host->rst); ret = devm_snd_soc_register_component(&pdev->dev, &sun4i_spdif_component, &sun4i_spdif_dai, 1); From 991dab1ee34e6cb9b9e997e279152a720f2bc313 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:53:16 -0500 Subject: [PATCH 034/153] ASoC: sun4i-spdif: Add support for separate RX/TX clocks On older variants of the hardware, the RX and TX blocks share a single module clock, named "spdif" in the DT binding. The D1 variant has separate RX and TX clocks, so the TX module clock is named "tx" in the binding. To support this, supply the clock name in the quirks structure. Since the driver supports only TX, only the TX clock name is needed. Signed-off-by: Samuel Holland --- sound/soc/sunxi/sun4i-spdif.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index f0fae7766292b4..06fbac9e7b1738 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -169,18 +169,20 @@ /** * struct sun4i_spdif_quirks - Differences between SoC variants. * + * @tx_clk_name: firmware name for the TX clock reference. * @reg_dac_txdata: TX FIFO offset for DMA config. * @val_fctl_ftx: TX FIFO flush bitmask. */ struct sun4i_spdif_quirks { + const char *tx_clk_name; unsigned int reg_dac_txdata; unsigned int val_fctl_ftx; }; struct sun4i_spdif_dev { struct platform_device *pdev; - struct clk *spdif_clk; struct clk *apb_clk; + struct clk *tx_clk; struct reset_control *rst; struct snd_soc_dai_driver cpu_dai_drv; struct regmap *regmap; @@ -313,7 +315,7 @@ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - ret = clk_set_rate(host->spdif_clk, mclk); + ret = clk_set_rate(host->tx_clk, mclk); if (ret < 0) { dev_err(&pdev->dev, "Setting SPDIF clock rate for %d Hz failed!\n", mclk); @@ -537,21 +539,25 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = { }; static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { + .tx_clk_name = "spdif", .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, }; static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { + .tx_clk_name = "spdif", .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, }; static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { + .tx_clk_name = "spdif", .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, }; static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { + .tx_clk_name = "spdif", .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, }; @@ -586,7 +592,7 @@ static int sun4i_spdif_runtime_suspend(struct device *dev) { struct sun4i_spdif_dev *host = dev_get_drvdata(dev); - clk_disable_unprepare(host->spdif_clk); + clk_disable_unprepare(host->tx_clk); clk_disable_unprepare(host->apb_clk); return 0; @@ -597,12 +603,12 @@ static int sun4i_spdif_runtime_resume(struct device *dev) struct sun4i_spdif_dev *host = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(host->spdif_clk); + ret = clk_prepare_enable(host->tx_clk); if (ret) return ret; ret = clk_prepare_enable(host->apb_clk); if (ret) - clk_disable_unprepare(host->spdif_clk); + clk_disable_unprepare(host->tx_clk); return ret; } @@ -650,10 +656,10 @@ static int sun4i_spdif_probe(struct platform_device *pdev) return PTR_ERR(host->apb_clk); } - host->spdif_clk = devm_clk_get(&pdev->dev, "spdif"); - if (IS_ERR(host->spdif_clk)) { - dev_err(&pdev->dev, "failed to get a spdif clock.\n"); - return PTR_ERR(host->spdif_clk); + host->tx_clk = devm_clk_get(&pdev->dev, quirks->tx_clk_name); + if (IS_ERR(host->tx_clk)) { + dev_err(&pdev->dev, "failed to get TX module clock.\n"); + return PTR_ERR(host->tx_clk); } host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; From 97fead77e17d9186c41dd977dbd62257a2f30699 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:53:26 -0500 Subject: [PATCH 035/153] ASoC: sun4i-spdif: Add support for the D1 variant The D1 variant is similar to the H6 variant, except for its clock setup. The clock tree changes impact some register fields on the RX side, but those are not yet relevant, because RX is not supported by this driver. Signed-off-by: Samuel Holland --- sound/soc/sunxi/sun4i-spdif.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index 06fbac9e7b1738..98da5386d4d7f9 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -556,6 +556,12 @@ static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, }; +static const struct sun4i_spdif_quirks sun20i_d1_spdif_quirks = { + .tx_clk_name = "tx", + .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, + .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, +}; + static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { .tx_clk_name = "spdif", .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, @@ -575,6 +581,10 @@ static const struct of_device_id sun4i_spdif_of_match[] = { .compatible = "allwinner,sun8i-h3-spdif", .data = &sun8i_h3_spdif_quirks, }, + { + .compatible = "allwinner,sun20i-d1-spdif", + .data = &sun20i_d1_spdif_quirks, + }, { .compatible = "allwinner,sun50i-h6-spdif", .data = &sun50i_h6_spdif_quirks, From 906956182ee60bab7beb32e7092778b6bbec1bb8 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 16 Jul 2022 13:48:41 -0500 Subject: [PATCH 036/153] dt-bindings: sram: sunxi-sram: Add D1 compatible string D1 needs to export a register for managing some LDO regulators, so it needs a unique compatible. Signed-off-by: Samuel Holland --- .../bindings/sram/allwinner,sun4i-a10-system-control.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml b/Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml index 1c426c211e3668..ab6e95a97c9f1b 100644 --- a/Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml +++ b/Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml @@ -39,6 +39,7 @@ properties: - items: - const: allwinner,sun8i-r40-system-control - const: allwinner,sun4i-a10-system-control + - const: allwinner,sun20i-d1-system-control - const: allwinner,sun50i-a64-sram-controller deprecated: true - const: allwinner,sun50i-a64-system-control From fab0e28a41fb9cfe8694cc86026f4169d2bd2192 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 19 Jan 2020 15:52:21 -0600 Subject: [PATCH 037/153] soc: sunxi: sram: Actually claim SRAM regions sunxi_sram_claim() checks the sram_desc->claimed flag before updating the register, with the intent that only one device can claim a region. However, this was ineffective because the flag was never set. Fixes: 4af34b572a85 ("drivers: soc: sunxi: Introduce SoC driver to map SRAMs") Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index a8f3876963a087..f3d3f9259df95b 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -254,6 +254,7 @@ int sunxi_sram_claim(struct device *dev) writel(val | ((device << sram_data->offset) & mask), base + sram_data->reg); + sram_desc->claimed = true; spin_unlock(&sram_lock); return 0; From 59b93b3e60d90ff4e0cf4ea27a5fae1ee9b763b3 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 29 Jul 2022 22:01:15 -0500 Subject: [PATCH 038/153] soc: sunxi: sram: Prevent the driver from being unbound This driver exports a regmap tied to the platform device (as opposed to a syscon, which exports a regmap tied to the OF node). Because of this, the driver can never be unbound, as that would destroy the regmap. Use builtin_platform_driver_probe() to enforce this limitation. Fixes: 5828729bebbb ("soc: sunxi: export a regmap for EMAC clock reg on A64") Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index f3d3f9259df95b..a858a37fcdd402 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -330,7 +330,7 @@ static struct regmap_config sunxi_sram_emac_clock_regmap = { .writeable_reg = sunxi_sram_regmap_accessible_reg, }; -static int sunxi_sram_probe(struct platform_device *pdev) +static int __init sunxi_sram_probe(struct platform_device *pdev) { struct dentry *d; struct regmap *emac_clock; @@ -410,9 +410,8 @@ static struct platform_driver sunxi_sram_driver = { .name = "sunxi-sram", .of_match_table = sunxi_sram_dt_match, }, - .probe = sunxi_sram_probe, }; -module_platform_driver(sunxi_sram_driver); +builtin_platform_driver_probe(sunxi_sram_driver, sunxi_sram_probe); MODULE_AUTHOR("Maxime Ripard "); MODULE_DESCRIPTION("Allwinner sunXi SRAM Controller Driver"); From 454a2fd1e47ed6d184cd8236f1a55389973782f6 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 29 Jul 2022 21:49:05 -0500 Subject: [PATCH 039/153] soc: sunxi: sram: Fix probe function ordering issues Errors from debugfs are intended to be non-fatal, and should not prevent the driver from probing. Since debugfs file creation is treated as infallible, move it below the parts of the probe function that can fail. This prevents an error elsewhere in the probe function from causing the file to leak. Do the same for the call to of_platform_populate(). Finally, checkpatch suggests an octal literal for the file permissions. Fixes: 4af34b572a85 ("drivers: soc: sunxi: Introduce SoC driver to map SRAMs") Fixes: 5828729bebbb ("soc: sunxi: export a regmap for EMAC clock reg on A64") Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index a858a37fcdd402..52d07bed7664e9 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -332,9 +332,9 @@ static struct regmap_config sunxi_sram_emac_clock_regmap = { static int __init sunxi_sram_probe(struct platform_device *pdev) { - struct dentry *d; struct regmap *emac_clock; const struct sunxi_sramc_variant *variant; + struct device *dev = &pdev->dev; sram_dev = &pdev->dev; @@ -346,13 +346,6 @@ static int __init sunxi_sram_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); - - d = debugfs_create_file("sram", S_IRUGO, NULL, NULL, - &sunxi_sram_fops); - if (!d) - return -ENOMEM; - if (variant->num_emac_clocks > 0) { emac_clock = devm_regmap_init_mmio(&pdev->dev, base, &sunxi_sram_emac_clock_regmap); @@ -361,6 +354,10 @@ static int __init sunxi_sram_probe(struct platform_device *pdev) return PTR_ERR(emac_clock); } + of_platform_populate(dev->of_node, NULL, NULL, dev); + + debugfs_create_file("sram", 0444, NULL, NULL, &sunxi_sram_fops); + return 0; } From 85d0862b5f63087f840b2393329d9bf8ffafb230 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:46:04 -0500 Subject: [PATCH 040/153] soc: sunxi: sram: Fix debugfs info for A64 SRAM C The labels were backward with respect to the register values. The SRAM is mapped to the CPU when the register value is 1. Fixes: 5e4fb6429761 ("drivers: soc: sunxi: add support for A64 and its SRAM C") Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 52d07bed7664e9..09754cd1d57dca 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -78,8 +78,8 @@ static struct sunxi_sram_desc sun4i_a10_sram_d = { static struct sunxi_sram_desc sun50i_a64_sram_c = { .data = SUNXI_SRAM_DATA("C", 0x4, 24, 1, - SUNXI_SRAM_MAP(0, 1, "cpu"), - SUNXI_SRAM_MAP(1, 0, "de2")), + SUNXI_SRAM_MAP(1, 0, "cpu"), + SUNXI_SRAM_MAP(0, 1, "de2")), }; static const struct of_device_id sunxi_sram_dt_ids[] = { From f81efcc9a729a3b49ff8521861b1e87fef51a42d Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 16 Jul 2022 12:21:57 -0500 Subject: [PATCH 041/153] soc: sunxi: sram: Return void from the release function There is no point in returning an error here, as the caller can do nothing about it. In fact, all callers already ignore the return value. Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 8 +++----- include/linux/soc/sunxi/sunxi_sram.h | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 09754cd1d57dca..9622fd45f5e59d 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -261,25 +261,23 @@ int sunxi_sram_claim(struct device *dev) } EXPORT_SYMBOL(sunxi_sram_claim); -int sunxi_sram_release(struct device *dev) +void sunxi_sram_release(struct device *dev) { const struct sunxi_sram_data *sram_data; struct sunxi_sram_desc *sram_desc; if (!dev || !dev->of_node) - return -EINVAL; + return; sram_data = sunxi_sram_of_parse(dev->of_node, NULL); if (IS_ERR(sram_data)) - return -EINVAL; + return; sram_desc = to_sram_desc(sram_data); spin_lock(&sram_lock); sram_desc->claimed = false; spin_unlock(&sram_lock); - - return 0; } EXPORT_SYMBOL(sunxi_sram_release); diff --git a/include/linux/soc/sunxi/sunxi_sram.h b/include/linux/soc/sunxi/sunxi_sram.h index c5f663bba9c289..60e274d1b821b7 100644 --- a/include/linux/soc/sunxi/sunxi_sram.h +++ b/include/linux/soc/sunxi/sunxi_sram.h @@ -14,6 +14,6 @@ #define _SUNXI_SRAM_H_ int sunxi_sram_claim(struct device *dev); -int sunxi_sram_release(struct device *dev); +void sunxi_sram_release(struct device *dev); #endif /* _SUNXI_SRAM_H_ */ From 728c6d050d943871d7a07f589c6089b4f4f958f8 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 16 Jul 2022 11:24:51 -0500 Subject: [PATCH 042/153] soc: sunxi: sram: Save a pointer to the OF match data It is inefficient to match the compatible string every time the regmap is accessed. Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 9622fd45f5e59d..7c6fb17cfe7f93 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -305,9 +305,7 @@ static const struct sunxi_sramc_variant sun50i_h616_sramc_variant = { static bool sunxi_sram_regmap_accessible_reg(struct device *dev, unsigned int reg) { - const struct sunxi_sramc_variant *variant; - - variant = of_device_get_match_data(dev); + const struct sunxi_sramc_variant *variant = dev_get_drvdata(dev); if (reg < SUNXI_SRAM_EMAC_CLOCK_REG) return false; @@ -340,6 +338,8 @@ static int __init sunxi_sram_probe(struct platform_device *pdev) if (!variant) return -EINVAL; + dev_set_drvdata(dev, (struct sunxi_sramc_variant *)variant); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); From bc0071cb9dc35963ba03f0a75d3c100d334182de Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 16 Jul 2022 12:59:14 -0500 Subject: [PATCH 043/153] soc: sunxi: sram: Export the LDO control register Some newer Allwinner SoCs contain internal LDOs managed by a register in the system control MMIO space. Export this from the regmap in addtion to the EMAC register. Use generic names now that the regmap is no longer EMAC-specific. Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 7c6fb17cfe7f93..7e8dab0f0ec428 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -283,6 +283,7 @@ EXPORT_SYMBOL(sunxi_sram_release); struct sunxi_sramc_variant { int num_emac_clocks; + bool has_ldo_ctrl; }; static const struct sunxi_sramc_variant sun4i_a10_sramc_variant = { @@ -302,25 +303,28 @@ static const struct sunxi_sramc_variant sun50i_h616_sramc_variant = { }; #define SUNXI_SRAM_EMAC_CLOCK_REG 0x30 +#define SUNXI_SYS_LDO_CTRL_REG 0x150 + static bool sunxi_sram_regmap_accessible_reg(struct device *dev, unsigned int reg) { const struct sunxi_sramc_variant *variant = dev_get_drvdata(dev); - if (reg < SUNXI_SRAM_EMAC_CLOCK_REG) - return false; - if (reg > SUNXI_SRAM_EMAC_CLOCK_REG + variant->num_emac_clocks * 4) - return false; + if (reg >= SUNXI_SRAM_EMAC_CLOCK_REG && + reg < SUNXI_SRAM_EMAC_CLOCK_REG + variant->num_emac_clocks * 4) + return true; + if (reg == SUNXI_SYS_LDO_CTRL_REG && variant->has_ldo_ctrl) + return true; - return true; + return false; } -static struct regmap_config sunxi_sram_emac_clock_regmap = { +static struct regmap_config sunxi_sram_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, /* last defined register */ - .max_register = SUNXI_SRAM_EMAC_CLOCK_REG + 4, + .max_register = SUNXI_SYS_LDO_CTRL_REG, /* other devices have no business accessing other registers */ .readable_reg = sunxi_sram_regmap_accessible_reg, .writeable_reg = sunxi_sram_regmap_accessible_reg, @@ -328,9 +332,9 @@ static struct regmap_config sunxi_sram_emac_clock_regmap = { static int __init sunxi_sram_probe(struct platform_device *pdev) { - struct regmap *emac_clock; const struct sunxi_sramc_variant *variant; struct device *dev = &pdev->dev; + struct regmap *regmap; sram_dev = &pdev->dev; @@ -344,12 +348,10 @@ static int __init sunxi_sram_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - if (variant->num_emac_clocks > 0) { - emac_clock = devm_regmap_init_mmio(&pdev->dev, base, - &sunxi_sram_emac_clock_regmap); - - if (IS_ERR(emac_clock)) - return PTR_ERR(emac_clock); + if (variant->num_emac_clocks || variant->has_ldo_ctrl) { + regmap = devm_regmap_init_mmio(dev, base, &sunxi_sram_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); } of_platform_populate(dev->of_node, NULL, NULL, dev); From 6d7aba595e7801148d370f5c23db70d01cc84a65 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 6 Jun 2021 10:03:12 -0500 Subject: [PATCH 044/153] dt-bindings: thermal: sun8i: Add compatible for D1 D1 contains a thermal sensor similar to other Allwinner SoCs. Like the H3 variant, it contains only one channel. D1's thermal sensor gets a reference voltage from AVCC. This may always have been the case; it is explicitly documented in the SoC user manuals since at least H616. However, it was not as important on earlier SoCs, because those reference designs foreced AVCC always-on by connecting it to the PLL power supply. Now, since D1 only uses AVCC for other optional peripherals, this supply could be turned off at runtime, so it must be made explicit in the DTS. Signed-off-by: Samuel Holland --- .../thermal/allwinner,sun8i-a83t-ths.yaml | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml index 6e0b110153b01f..f72ceef424bdba 100644 --- a/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml +++ b/Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml @@ -16,6 +16,7 @@ properties: - allwinner,sun8i-a83t-ths - allwinner,sun8i-h3-ths - allwinner,sun8i-r40-ths + - allwinner,sun20i-d1-ths - allwinner,sun50i-a64-ths - allwinner,sun50i-a100-ths - allwinner,sun50i-h5-ths @@ -55,6 +56,10 @@ properties: - 0 - 1 + vref-supply: + description: + Regulator for the analog reference voltage + allOf: - if: properties: @@ -84,7 +89,9 @@ allOf: properties: compatible: contains: - const: allwinner,sun8i-h3-ths + enum: + - allwinner,sun8i-h3-ths + - allwinner,sun20i-d1-ths then: properties: @@ -103,6 +110,7 @@ allOf: enum: - allwinner,sun8i-h3-ths - allwinner,sun8i-r40-ths + - allwinner,sun20i-d1-ths - allwinner,sun50i-a64-ths - allwinner,sun50i-a100-ths - allwinner,sun50i-h5-ths @@ -114,6 +122,17 @@ allOf: - clock-names - resets + - if: + properties: + compatible: + contains: + enum: + - allwinner,sun20i-d1-ths + + then: + required: + - vref-supply + required: - compatible - reg From 93c3b50a700a91819c24d32667d129bddc11d54d Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 6 Jun 2021 10:04:52 -0500 Subject: [PATCH 045/153] thermal: sun8i: Document the unknown field The H616 and newer user manuals document this field as the ADC acquisition time. Update the defintion and the timing calculations using the diagram in the manual. Signed-off-by: Samuel Holland --- drivers/thermal/sun8i_thermal.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index d9cd23cbb6717e..f05e70e280dd9a 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -50,7 +50,8 @@ #define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16) #define SUN8I_THS_DATA_IRQ_STS(x) BIT(x + 8) -#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16) +#define SUN50I_THS_CTRL0_FS_DIV(x) ((GENMASK(15, 0) & (x)) << 16) +#define SUN50I_THS_CTRL0_T_ACQ(x) (GENMASK(15, 0) & (x)) #define SUN50I_THS_FILTER_EN BIT(2) #define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x)) #define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12) @@ -417,25 +418,22 @@ static int sun8i_h3_thermal_init(struct ths_device *tmdev) return 0; } -/* - * Without this undocumented value, the returned temperatures would - * be higher than real ones by about 20C. - */ -#define SUN50I_H6_CTRL0_UNK 0x0000002f - static int sun50i_h6_thermal_init(struct ths_device *tmdev) { int val; /* - * T_acq = 20us + * T_sample = 20us > T_acq + T_conv + * T_acq = 2us + * T_conv = 14 cycles * clkin = 24MHz * - * x = T_acq * clkin - 1 + * x = T_sample * clkin - 1 * = 479 */ regmap_write(tmdev->regmap, SUN50I_THS_CTRL0, - SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479)); + SUN50I_THS_CTRL0_FS_DIV(479) | + SUN50I_THS_CTRL0_T_ACQ(47)); /* average over 4 samples */ regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC, SUN50I_THS_FILTER_EN | From 6793d1d2035d03b25e768eec559d3ebadf585ab5 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 23 Jun 2021 19:29:37 -0500 Subject: [PATCH 046/153] thermal: sun8i: Set the event type for new samples Currently, an IRQ is only configured to trigger when a new sample is available. So report this event type to the thermal core. Signed-off-by: Samuel Holland --- drivers/thermal/sun8i_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index f05e70e280dd9a..ddd21ef4e3eb5a 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -192,7 +192,7 @@ static irqreturn_t sun8i_irq_thread(int irq, void *data) for_each_set_bit(i, &irq_bitmap, tmdev->chip->sensor_num) { thermal_zone_device_update(tmdev->sensor[i].tzd, - THERMAL_EVENT_UNSPECIFIED); + THERMAL_EVENT_TEMP_SAMPLE); } return IRQ_HANDLED; From d6bb4ec519b556cd3835ccc4e6bb2239d3815f93 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 23 Jun 2021 19:30:10 -0500 Subject: [PATCH 047/153] thermal: sun8i: Use optional clock/reset getters The driver does not need to care about what variants have which clocks and resets; the devicetree binding already enforces that the necessary resources are provided. Simplify the logic by always calling the optional version of the getters, to pick up whatever references exist. Signed-off-by: Samuel Holland --- drivers/thermal/sun8i_thermal.c | 34 +++++++++------------------------ 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index ddd21ef4e3eb5a..be8064e8990692 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -66,8 +66,6 @@ struct tsensor { }; struct ths_thermal_chip { - bool has_mod_clk; - bool has_bus_clk_reset; int sensor_num; int offset; int scale; @@ -335,21 +333,17 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev) if (IS_ERR(tmdev->regmap)) return PTR_ERR(tmdev->regmap); - if (tmdev->chip->has_bus_clk_reset) { - tmdev->reset = devm_reset_control_get(dev, NULL); - if (IS_ERR(tmdev->reset)) - return PTR_ERR(tmdev->reset); + tmdev->reset = devm_reset_control_get_optional(dev, NULL); + if (IS_ERR(tmdev->reset)) + return PTR_ERR(tmdev->reset); - tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus"); - if (IS_ERR(tmdev->bus_clk)) - return PTR_ERR(tmdev->bus_clk); - } + tmdev->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); + if (IS_ERR(tmdev->bus_clk)) + return PTR_ERR(tmdev->bus_clk); - if (tmdev->chip->has_mod_clk) { - tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod"); - if (IS_ERR(tmdev->mod_clk)) - return PTR_ERR(tmdev->mod_clk); - } + tmdev->mod_clk = devm_clk_get_optional(&pdev->dev, "mod"); + if (IS_ERR(tmdev->mod_clk)) + return PTR_ERR(tmdev->mod_clk); ret = reset_control_deassert(tmdev->reset); if (ret) @@ -554,8 +548,6 @@ static const struct ths_thermal_chip sun8i_h3_ths = { .sensor_num = 1, .scale = 1211, .offset = 217000, - .has_mod_clk = true, - .has_bus_clk_reset = true, .temp_data_base = SUN8I_THS_TEMP_DATA, .calibrate = sun8i_h3_ths_calibrate, .init = sun8i_h3_thermal_init, @@ -567,8 +559,6 @@ static const struct ths_thermal_chip sun8i_r40_ths = { .sensor_num = 2, .offset = 251086, .scale = 1130, - .has_mod_clk = true, - .has_bus_clk_reset = true, .temp_data_base = SUN8I_THS_TEMP_DATA, .calibrate = sun8i_h3_ths_calibrate, .init = sun8i_h3_thermal_init, @@ -580,8 +570,6 @@ static const struct ths_thermal_chip sun50i_a64_ths = { .sensor_num = 3, .offset = 260890, .scale = 1170, - .has_mod_clk = true, - .has_bus_clk_reset = true, .temp_data_base = SUN8I_THS_TEMP_DATA, .calibrate = sun8i_h3_ths_calibrate, .init = sun8i_h3_thermal_init, @@ -591,7 +579,6 @@ static const struct ths_thermal_chip sun50i_a64_ths = { static const struct ths_thermal_chip sun50i_a100_ths = { .sensor_num = 3, - .has_bus_clk_reset = true, .ft_deviation = 8000, .offset = 187744, .scale = 672, @@ -604,8 +591,6 @@ static const struct ths_thermal_chip sun50i_a100_ths = { static const struct ths_thermal_chip sun50i_h5_ths = { .sensor_num = 2, - .has_mod_clk = true, - .has_bus_clk_reset = true, .temp_data_base = SUN8I_THS_TEMP_DATA, .calibrate = sun8i_h3_ths_calibrate, .init = sun8i_h3_thermal_init, @@ -615,7 +600,6 @@ static const struct ths_thermal_chip sun50i_h5_ths = { static const struct ths_thermal_chip sun50i_h6_ths = { .sensor_num = 2, - .has_bus_clk_reset = true, .ft_deviation = 7000, .offset = 187744, .scale = 672, From a6957f0dbfc5507390e1a2bd1048b538651f766b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 23 Jun 2021 20:17:46 -0500 Subject: [PATCH 048/153] thermal: sun8i: Ensure vref is powered On some boards, the supply for the reference voltage (AVCC) is not always on. For the thermal sensor to work, that supply must be powered. So add the necessary regulator consumer. Signed-off-by: Samuel Holland --- drivers/thermal/sun8i_thermal.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index be8064e8990692..fa88fd91f997b5 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,7 @@ struct ths_device { struct device *dev; struct regmap *regmap; struct reset_control *reset; + struct regulator *vref_supply; struct clk *bus_clk; struct clk *mod_clk; struct tsensor sensor[MAX_SENSOR_NUM]; @@ -333,6 +335,10 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev) if (IS_ERR(tmdev->regmap)) return PTR_ERR(tmdev->regmap); + tmdev->vref_supply = devm_regulator_get(dev, "vref"); + if (IS_ERR(tmdev->vref_supply)) + return PTR_ERR(tmdev->vref_supply); + tmdev->reset = devm_reset_control_get_optional(dev, NULL); if (IS_ERR(tmdev->reset)) return PTR_ERR(tmdev->reset); @@ -345,10 +351,14 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev) if (IS_ERR(tmdev->mod_clk)) return PTR_ERR(tmdev->mod_clk); - ret = reset_control_deassert(tmdev->reset); + ret = regulator_enable(tmdev->vref_supply); if (ret) return ret; + ret = reset_control_deassert(tmdev->reset); + if (ret) + goto disable_vref_supply; + ret = clk_prepare_enable(tmdev->bus_clk); if (ret) goto assert_reset; @@ -373,6 +383,8 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev) clk_disable_unprepare(tmdev->bus_clk); assert_reset: reset_control_assert(tmdev->reset); +disable_vref_supply: + regulator_disable(tmdev->vref_supply); return ret; } @@ -529,6 +541,7 @@ static int sun8i_ths_remove(struct platform_device *pdev) clk_disable_unprepare(tmdev->mod_clk); clk_disable_unprepare(tmdev->bus_clk); reset_control_assert(tmdev->reset); + regulator_disable(tmdev->vref_supply); return 0; } From 480adbed8628bfae1a156bb03ad8e2384f80d85a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 6 Jun 2021 10:05:08 -0500 Subject: [PATCH 049/153] thermal: sun8i: Add support for the D1 variant D1 has a thermal sensor block like other SoCs in the H6 generation. It has a single sensor channel and a unique temperature formula. Signed-off-by: Samuel Holland --- drivers/thermal/sun8i_thermal.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index fa88fd91f997b5..1994e503bf1515 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -579,6 +579,17 @@ static const struct ths_thermal_chip sun8i_r40_ths = { .calc_temp = sun8i_ths_calc_temp, }; +static const struct ths_thermal_chip sun20i_d1_ths = { + .sensor_num = 1, + .offset = 188147, + .scale = 672, + .temp_data_base = SUN50I_H6_THS_TEMP_DATA, + .calibrate = sun50i_h6_ths_calibrate, + .init = sun50i_h6_thermal_init, + .irq_ack = sun50i_h6_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + static const struct ths_thermal_chip sun50i_a64_ths = { .sensor_num = 3, .offset = 260890, @@ -627,6 +638,7 @@ static const struct of_device_id of_ths_match[] = { { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths }, { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths }, { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths }, + { .compatible = "allwinner,sun20i-d1-ths", .data = &sun20i_d1_ths }, { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths }, { .compatible = "allwinner,sun50i-a100-ths", .data = &sun50i_a100_ths }, { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths }, From 58632f0fa808ca63e5183ef2def0263a2b86c1cb Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 24 Jul 2022 15:27:54 -0500 Subject: [PATCH 050/153] dt-bindings: timer: allwinner,sun4i-a10-timer: Add D1 compatible Allwinner D1 contains the usual sun4i MMIO timer device. It contains two timers like other recent SoCs, so it is compatible with the A23 variant. Series-to: Daniel Lezcano Series-to: Thomas Gleixner Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Signed-off-by: Samuel Holland --- .../devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml index 53fd24bdc34e3c..3711872b6b99d2 100644 --- a/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml +++ b/Documentation/devicetree/bindings/timer/allwinner,sun4i-a10-timer.yaml @@ -20,6 +20,7 @@ properties: - allwinner,suniv-f1c100s-timer - items: - enum: + - allwinner,sun20i-d1-timer - allwinner,sun50i-a64-timer - allwinner,sun50i-h6-timer - allwinner,sun50i-h616-timer From 3168a59cb1277d49c879ad68e16e8b48ae547ae2 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Jul 2022 01:15:33 +0200 Subject: [PATCH 051/153] of: also handle dma-noncoherent in of_dma_is_coherent() of_dma_is_coherent() currently expects the architecture to be non-coherent and some devices being coherent getting marked as such with the dma-coherent devicetree property. For PowerPC CONFIG_OF_DMA_DEFAULT_COHERENT was added which currently makes of_dma_is_coherent() always return true but doesn't handle the case of the architecture being coherent but some devices not. So modify the function to also check for dma-noncoherent and set a suitable default return value. If CONFIG_OF_DMA_DEFAULT_COHERENT is set the value starts with true and finding dma-noncoherent will set it to false and without CONFIG_OF_DMA_DEFAULT_COHERENT, the behaviour is reversed. Reviewed-by: Christoph Hellwig Reviewed-by: Rob Herring Reviewed-by: Guo Ren Signed-off-by: Heiko Stuebner --- drivers/of/address.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/of/address.c b/drivers/of/address.c index 94f017d808c44c..96f0a12e507cd1 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -1045,26 +1045,29 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) * * It returns true if "dma-coherent" property was found * for this device in the DT, or if DMA is coherent by - * default for OF devices on the current platform. + * default for OF devices on the current platform and no + * "dma-noncoherent" property was found for this device. */ bool of_dma_is_coherent(struct device_node *np) { struct device_node *node; - - if (IS_ENABLED(CONFIG_OF_DMA_DEFAULT_COHERENT)) - return true; + bool is_coherent = IS_ENABLED(CONFIG_OF_DMA_DEFAULT_COHERENT); node = of_node_get(np); while (node) { if (of_property_read_bool(node, "dma-coherent")) { - of_node_put(node); - return true; + is_coherent = true; + break; + } + if (of_property_read_bool(node, "dma-noncoherent")) { + is_coherent = false; + break; } node = of_get_next_dma_parent(node); } of_node_put(node); - return false; + return is_coherent; } EXPORT_SYMBOL_GPL(of_dma_is_coherent); From d1a6403969fecb5dfbe6993b611decbbd7a7432a Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Jul 2022 01:15:34 +0200 Subject: [PATCH 052/153] dt-bindings: riscv: document cbom-block-size The Zicbom operates on a block-size defined for the cpu-core, which does not necessarily match other cache-sizes used. So add the necessary property for the system to know the core's block-size. Reviewed-by: Anup Patel Reviewed-by: Guo Ren Acked-by: Rob Herring Signed-off-by: Heiko Stuebner --- Documentation/devicetree/bindings/riscv/cpus.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index d632ac76532e9b..873dd12f6e896d 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -63,6 +63,11 @@ properties: - riscv,sv48 - riscv,none + riscv,cbom-block-size: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The blocksize in bytes for the Zicbom cache operations. + riscv,isa: description: Identifies the specific RISC-V instruction set architecture From 7260feda07f3ca492deb6cc60ede723d8404d051 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Jul 2022 01:15:35 +0200 Subject: [PATCH 053/153] riscv: Add support for non-coherent devices using zicbom extension The Zicbom ISA-extension was ratified in november 2021 and introduces instructions for dcache invalidate, clean and flush operations. Implement cache management operations for non-coherent devices based on them. Of course not all cores will support this, so implement an alternative-based mechanism that replaces empty instructions with ones done around Zicbom instructions. As discussed in previous versions, assume the platform being coherent by default so that non-coherent devices need to get marked accordingly by firmware. Reviewed-by: Christoph Hellwig Signed-off-by: Heiko Stuebner Cc: Christoph Hellwig Cc: Atish Patra Cc: Guo Ren Cc: Anup Patel Reviewed-by: Guo Ren --- arch/riscv/Kconfig | 31 ++++++++ arch/riscv/Makefile | 4 + arch/riscv/include/asm/cache.h | 4 + arch/riscv/include/asm/cacheflush.h | 10 +++ arch/riscv/include/asm/errata_list.h | 19 ++++- arch/riscv/include/asm/hwcap.h | 1 + arch/riscv/kernel/cpu.c | 1 + arch/riscv/kernel/cpufeature.c | 24 ++++++ arch/riscv/kernel/setup.c | 2 + arch/riscv/mm/Makefile | 1 + arch/riscv/mm/dma-noncoherent.c | 112 +++++++++++++++++++++++++++ 11 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/mm/dma-noncoherent.c diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 45c60864a75f3e..cfecba28299e24 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -113,6 +113,7 @@ config RISCV select MODULES_USE_ELF_RELA if MODULES select MODULE_SECTIONS if MODULES select OF + select OF_DMA_DEFAULT_COHERENT select OF_EARLY_FLATTREE select OF_IRQ select PCI_DOMAINS_GENERIC if PCI @@ -218,6 +219,14 @@ config PGTABLE_LEVELS config LOCKDEP_SUPPORT def_bool y +config RISCV_DMA_NONCOHERENT + bool + select ARCH_HAS_DMA_PREP_COHERENT + select ARCH_HAS_SYNC_DMA_FOR_DEVICE + select ARCH_HAS_SYNC_DMA_FOR_CPU + select ARCH_HAS_SETUP_DMA_OPS + select DMA_DIRECT_REMAP + source "arch/riscv/Kconfig.socs" source "arch/riscv/Kconfig.erratas" @@ -392,6 +401,28 @@ config RISCV_ISA_SVPBMT If you don't know what to do here, say Y. +config CC_HAS_ZICBOM + bool + default y if 64BIT && $(cc-option,-mabi=lp64 -march=rv64ima_zicbom) + default y if 32BIT && $(cc-option,-mabi=ilp32 -march=rv32ima_zicbom) + +config RISCV_ISA_ZICBOM + bool "Zicbom extension support for non-coherent DMA operation" + depends on CC_HAS_ZICBOM + depends on !XIP_KERNEL + select RISCV_DMA_NONCOHERENT + select RISCV_ALTERNATIVE + default y + help + Adds support to dynamically detect the presence of the ZICBOM + extension (Cache Block Management Operations) and enable its + usage. + + The Zicbom extension can be used to handle for example + non-coherent DMA support on devices that need it. + + If you don't know what to do here, say Y. + config FPU bool "FPU support" default y diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 81029d40a6727a..42d7ff8730aa64 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -56,6 +56,10 @@ riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c toolchain-need-zicsr-zifencei := $(call cc-option-yn, -march=$(riscv-march-y)_zicsr_zifencei) riscv-march-$(toolchain-need-zicsr-zifencei) := $(riscv-march-y)_zicsr_zifencei +# Check if the toolchain supports Zicbom extension +toolchain-supports-zicbom := $(call cc-option-yn, -march=$(riscv-march-y)_zicbom) +riscv-march-$(toolchain-supports-zicbom) := $(riscv-march-y)_zicbom + KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y)) KBUILD_AFLAGS += -march=$(riscv-march-y) diff --git a/arch/riscv/include/asm/cache.h b/arch/riscv/include/asm/cache.h index 9b58b104559ee8..d3036df23ccbba 100644 --- a/arch/riscv/include/asm/cache.h +++ b/arch/riscv/include/asm/cache.h @@ -11,6 +11,10 @@ #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) +#ifdef CONFIG_RISCV_DMA_NONCOHERENT +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES +#endif + /* * RISC-V requires the stack pointer to be 16-byte aligned, so ensure that * the flat loader aligns it accordingly. diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 23ff7035099261..a60acaecfedab6 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -42,6 +42,16 @@ void flush_icache_mm(struct mm_struct *mm, bool local); #endif /* CONFIG_SMP */ +#ifdef CONFIG_RISCV_ISA_ZICBOM +void riscv_init_cbom_blocksize(void); +#else +static inline void riscv_init_cbom_blocksize(void) { } +#endif + +#ifdef CONFIG_RISCV_DMA_NONCOHERENT +void riscv_noncoherent_supported(void); +#endif + /* * Bits in sys_riscv_flush_icache()'s flags argument. */ diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index 398e351e7002e7..79d89aeeaa6c96 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -20,7 +20,8 @@ #endif #define CPUFEATURE_SVPBMT 0 -#define CPUFEATURE_NUMBER 1 +#define CPUFEATURE_ZICBOM 1 +#define CPUFEATURE_NUMBER 2 #ifdef __ASSEMBLY__ @@ -87,6 +88,22 @@ asm volatile(ALTERNATIVE( \ #define ALT_THEAD_PMA(_val) #endif +#define ALT_CMO_OP(_op, _start, _size, _cachesize) \ +asm volatile(ALTERNATIVE( \ + __nops(5), \ + "mv a0, %1\n\t" \ + "j 2f\n\t" \ + "3:\n\t" \ + "cbo." __stringify(_op) " (a0)\n\t" \ + "add a0, a0, %0\n\t" \ + "2:\n\t" \ + "bltu a0, %2, 3b\n\t", 0, \ + CPUFEATURE_ZICBOM, CONFIG_RISCV_ISA_ZICBOM) \ + : : "r"(_cachesize), \ + "r"((unsigned long)(_start) & ~((_cachesize) - 1UL)), \ + "r"((unsigned long)(_start) + (_size)) \ + : "a0") + #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index e48eebdd26315e..ed4045e70f7a9e 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -54,6 +54,7 @@ extern unsigned long elf_hwcap; enum riscv_isa_ext_id { RISCV_ISA_EXT_SSCOFPMF = RISCV_ISA_EXT_BASE, RISCV_ISA_EXT_SVPBMT, + RISCV_ISA_EXT_ZICBOM, RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX, }; diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index 022fd186199297..76a2a225e3d995 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -93,6 +93,7 @@ int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid) static struct riscv_isa_ext_data isa_ext_arr[] = { __RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF), __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT), + __RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM), __RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX), }; diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index e233fe154c96c8..f914e8da157a81 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -200,6 +201,7 @@ void __init riscv_fill_hwcap(void) } else { SET_ISA_EXT_MAP("sscofpmf", RISCV_ISA_EXT_SSCOFPMF); SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT); + SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM); } #undef SET_ISA_EXT_MAP } @@ -261,6 +263,25 @@ static bool __init_or_module cpufeature_probe_svpbmt(unsigned int stage) return false; } +static bool __init_or_module cpufeature_probe_zicbom(unsigned int stage) +{ +#ifdef CONFIG_RISCV_ISA_ZICBOM + switch (stage) { + case RISCV_ALTERNATIVES_EARLY_BOOT: + return false; + default: + if (riscv_isa_extension_available(NULL, ZICBOM)) { + riscv_noncoherent_supported(); + return true; + } else { + return false; + } + } +#endif + + return false; +} + /* * Probe presence of individual extensions. * @@ -275,6 +296,9 @@ static u32 __init_or_module cpufeature_probe(unsigned int stage) if (cpufeature_probe_svpbmt(stage)) cpu_req_feature |= (1U << CPUFEATURE_SVPBMT); + if (cpufeature_probe_zicbom(stage)) + cpu_req_feature |= (1U << CPUFEATURE_ZICBOM); + return cpu_req_feature; } diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index f0f36a4a0e9b81..95ef6e2bf45c02 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -296,6 +297,7 @@ void __init setup_arch(char **cmdline_p) #endif riscv_fill_hwcap(); + riscv_init_cbom_blocksize(); apply_boot_alternatives(); } diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile index ac7a25298a04af..d76aabf4b94d6a 100644 --- a/arch/riscv/mm/Makefile +++ b/arch/riscv/mm/Makefile @@ -30,3 +30,4 @@ endif endif obj-$(CONFIG_DEBUG_VIRTUAL) += physaddr.o +obj-$(CONFIG_RISCV_DMA_NONCOHERENT) += dma-noncoherent.o diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c new file mode 100644 index 00000000000000..a8dc0bd9078d7e --- /dev/null +++ b/arch/riscv/mm/dma-noncoherent.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RISC-V specific functions to support DMA for non-coherent devices + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + */ + +#include +#include +#include +#include +#include +#include + +static unsigned int riscv_cbom_block_size = L1_CACHE_BYTES; +static bool noncoherent_supported; + +void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ + void *vaddr = phys_to_virt(paddr); + + switch (dir) { + case DMA_TO_DEVICE: + ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); + break; + case DMA_FROM_DEVICE: + ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); + break; + case DMA_BIDIRECTIONAL: + ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); + break; + default: + break; + } +} + +void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ + void *vaddr = phys_to_virt(paddr); + + switch (dir) { + case DMA_TO_DEVICE: + break; + case DMA_FROM_DEVICE: + case DMA_BIDIRECTIONAL: + ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); + break; + default: + break; + } +} + +void arch_dma_prep_coherent(struct page *page, size_t size) +{ + void *flush_addr = page_address(page); + + ALT_CMO_OP(flush, flush_addr, size, riscv_cbom_block_size); +} + +void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, + const struct iommu_ops *iommu, bool coherent) +{ + WARN_TAINT(!coherent && riscv_cbom_block_size > ARCH_DMA_MINALIGN, + TAINT_CPU_OUT_OF_SPEC, + "%s %s: ARCH_DMA_MINALIGN smaller than riscv,cbom-block-size (%d < %d)", + dev_driver_string(dev), dev_name(dev), + ARCH_DMA_MINALIGN, riscv_cbom_block_size); + + WARN_TAINT(!coherent && !noncoherent_supported, TAINT_CPU_OUT_OF_SPEC, + "%s %s: device non-coherent but no non-coherent operations supported", + dev_driver_string(dev), dev_name(dev)); + + dev->dma_coherent = coherent; +} + +#ifdef CONFIG_RISCV_ISA_ZICBOM +void riscv_init_cbom_blocksize(void) +{ + struct device_node *node; + int ret; + u32 val; + + for_each_of_cpu_node(node) { + int hartid = riscv_of_processor_hartid(node); + int cbom_hartid; + + if (hartid < 0) + continue; + + /* set block-size for cbom extension if available */ + ret = of_property_read_u32(node, "riscv,cbom-block-size", &val); + if (ret) + continue; + + if (!riscv_cbom_block_size) { + riscv_cbom_block_size = val; + cbom_hartid = hartid; + } else { + if (riscv_cbom_block_size != val) + pr_warn("cbom-block-size mismatched between harts %d and %d\n", + cbom_hartid, hartid); + } + } +} +#endif + +void riscv_noncoherent_supported(void) +{ + noncoherent_supported = true; +} From 864e0c306e9a4ea179747a02ef7d5f57cf9da1e1 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Jul 2022 01:15:36 +0200 Subject: [PATCH 054/153] riscv: implement cache-management errata for T-Head SoCs The T-Head C906 and C910 implement a scheme for handling cache operations different from the generic Zicbom extension. Add an errata for it next to the generic dma coherency ops. Reviewed-by: Samuel Holland Tested-by: Samuel Holland Reviewed-by: Guo Ren Signed-off-by: Heiko Stuebner --- arch/riscv/Kconfig.erratas | 11 +++++++ arch/riscv/errata/thead/errata.c | 20 ++++++++++++ arch/riscv/include/asm/errata_list.h | 48 +++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/arch/riscv/Kconfig.erratas b/arch/riscv/Kconfig.erratas index f62b62807e853a..6850e938993024 100644 --- a/arch/riscv/Kconfig.erratas +++ b/arch/riscv/Kconfig.erratas @@ -55,4 +55,15 @@ config ERRATA_THEAD_PBMT If you don't know what to do here, say "Y". +config ERRATA_THEAD_CMO + bool "Apply T-Head cache management errata" + depends on ERRATA_THEAD + select RISCV_DMA_NONCOHERENT + default y + help + This will apply the cache management errata to handle the + non-standard handling on non-coherent operations on T-Head SoCs. + + If you don't know what to do here, say "Y". + endmenu # "CPU errata selection" diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c index b37b6fedd53bc5..202c83f677b2ed 100644 --- a/arch/riscv/errata/thead/errata.c +++ b/arch/riscv/errata/thead/errata.c @@ -27,6 +27,23 @@ static bool errata_probe_pbmt(unsigned int stage, return false; } +static bool errata_probe_cmo(unsigned int stage, + unsigned long arch_id, unsigned long impid) +{ +#ifdef CONFIG_ERRATA_THEAD_CMO + if (arch_id != 0 || impid != 0) + return false; + + if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) + return false; + + riscv_noncoherent_supported(); + return true; +#else + return false; +#endif +} + static u32 thead_errata_probe(unsigned int stage, unsigned long archid, unsigned long impid) { @@ -35,6 +52,9 @@ static u32 thead_errata_probe(unsigned int stage, if (errata_probe_pbmt(stage, archid, impid)) cpu_req_errata |= (1U << ERRATA_THEAD_PBMT); + if (errata_probe_cmo(stage, archid, impid)) + cpu_req_errata |= (1U << ERRATA_THEAD_CMO); + return cpu_req_errata; } diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index 79d89aeeaa6c96..19a771085781a6 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -16,7 +16,8 @@ #ifdef CONFIG_ERRATA_THEAD #define ERRATA_THEAD_PBMT 0 -#define ERRATA_THEAD_NUMBER 1 +#define ERRATA_THEAD_CMO 1 +#define ERRATA_THEAD_NUMBER 2 #endif #define CPUFEATURE_SVPBMT 0 @@ -88,17 +89,54 @@ asm volatile(ALTERNATIVE( \ #define ALT_THEAD_PMA(_val) #endif +/* + * dcache.ipa rs1 (invalidate, physical address) + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000001 01010 rs1 000 00000 0001011 + * dache.iva rs1 (invalida, virtual address) + * 0000001 00110 rs1 000 00000 0001011 + * + * dcache.cpa rs1 (clean, physical address) + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000001 01001 rs1 000 00000 0001011 + * dcache.cva rs1 (clean, virtual address) + * 0000001 00100 rs1 000 00000 0001011 + * + * dcache.cipa rs1 (clean then invalidate, physical address) + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000001 01011 rs1 000 00000 0001011 + * dcache.civa rs1 (... virtual address) + * 0000001 00111 rs1 000 00000 0001011 + * + * sync.s (make sure all cache operations finished) + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000000 11001 00000 000 00000 0001011 + */ +#define THEAD_inval_A0 ".long 0x0265000b" +#define THEAD_clean_A0 ".long 0x0245000b" +#define THEAD_flush_A0 ".long 0x0275000b" +#define THEAD_SYNC_S ".long 0x0190000b" + #define ALT_CMO_OP(_op, _start, _size, _cachesize) \ -asm volatile(ALTERNATIVE( \ - __nops(5), \ +asm volatile(ALTERNATIVE_2( \ + __nops(6), \ "mv a0, %1\n\t" \ "j 2f\n\t" \ "3:\n\t" \ "cbo." __stringify(_op) " (a0)\n\t" \ "add a0, a0, %0\n\t" \ "2:\n\t" \ - "bltu a0, %2, 3b\n\t", 0, \ - CPUFEATURE_ZICBOM, CONFIG_RISCV_ISA_ZICBOM) \ + "bltu a0, %2, 3b\n\t" \ + "nop", 0, CPUFEATURE_ZICBOM, CONFIG_RISCV_ISA_ZICBOM, \ + "mv a0, %1\n\t" \ + "j 2f\n\t" \ + "3:\n\t" \ + THEAD_##_op##_A0 "\n\t" \ + "add a0, a0, %0\n\t" \ + "2:\n\t" \ + "bltu a0, %2, 3b\n\t" \ + THEAD_SYNC_S, THEAD_VENDOR_ID, \ + ERRATA_THEAD_CMO, CONFIG_ERRATA_THEAD_CMO) \ : : "r"(_cachesize), \ "r"((unsigned long)(_start) & ~((_cachesize) - 1UL)), \ "r"((unsigned long)(_start) + (_size)) \ From 3d01159a4fc399fd138c48706cc514b7e6401568 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 17 Jan 2022 14:10:34 +0530 Subject: [PATCH 055/153] RISC-V: Clear SIP bit only when using SBI IPI operations The software interrupt pending (i.e. [M|S]SIP) bit is writeable for S-mode but read-only for M-mode so we clear this bit only when using SBI IPI operations. Signed-off-by: Anup Patel Reviewed-by: Bin Meng --- arch/riscv/kernel/sbi.c | 8 +++++++- arch/riscv/kernel/smp.c | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c index 775d3322b422fe..fc614650a2e37d 100644 --- a/arch/riscv/kernel/sbi.c +++ b/arch/riscv/kernel/sbi.c @@ -643,8 +643,14 @@ static void sbi_send_cpumask_ipi(const struct cpumask *target) sbi_send_ipi(target); } +static void sbi_ipi_clear(void) +{ + csr_clear(CSR_IP, IE_SIE); +} + static const struct riscv_ipi_ops sbi_ipi_ops = { - .ipi_inject = sbi_send_cpumask_ipi + .ipi_inject = sbi_send_cpumask_ipi, + .ipi_clear = sbi_ipi_clear }; void __init sbi_init(void) diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index 018e7dc45df6c6..5724234f972d83 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -89,8 +89,6 @@ void riscv_clear_ipi(void) { if (ipi_ops && ipi_ops->ipi_clear) ipi_ops->ipi_clear(); - - csr_clear(CSR_IP, IE_SIE); } EXPORT_SYMBOL_GPL(riscv_clear_ipi); From 2d2f8bfa2d835e9b0cd78dd8e889cb50dc8a6d98 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Thu, 27 Jan 2022 18:56:55 +0530 Subject: [PATCH 056/153] irqchip/riscv-intc: Allow drivers to directly discover INTC hwnode Various RISC-V drivers (such as SBI IPI, SBI Timer, SBI PMU, and KVM RISC-V) don't have associated DT node but these drivers need standard per-CPU (local) interrupts defined by the RISC-V privileged specification. We add riscv_get_intc_hwnode() in arch/riscv which allows RISC-V drivers not having DT node to discover INTC hwnode which in-turn helps these drivers to map per-CPU (local) interrupts provided by the INTC driver. Signed-off-by: Anup Patel --- arch/riscv/include/asm/irq.h | 4 ++++ arch/riscv/kernel/irq.c | 18 ++++++++++++++++++ drivers/irqchip/irq-riscv-intc.c | 7 +++++++ 3 files changed, 29 insertions(+) diff --git a/arch/riscv/include/asm/irq.h b/arch/riscv/include/asm/irq.h index e4c435509983e5..43b9ebfbd94364 100644 --- a/arch/riscv/include/asm/irq.h +++ b/arch/riscv/include/asm/irq.h @@ -12,6 +12,10 @@ #include +void riscv_set_intc_hwnode_fn(struct fwnode_handle *(*fn)(void)); + +struct fwnode_handle *riscv_get_intc_hwnode(void); + extern void __init init_IRQ(void); #endif /* _ASM_RISCV_IRQ_H */ diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index 7207fa08d78f86..96d3171f0ca11d 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -7,9 +7,27 @@ #include #include +#include +#include #include #include +static struct fwnode_handle *(*__get_intc_node)(void); + +void riscv_set_intc_hwnode_fn(struct fwnode_handle *(*fn)(void)) +{ + __get_intc_node = fn; +} + +struct fwnode_handle *riscv_get_intc_hwnode(void) +{ + if (__get_intc_node) + return __get_intc_node(); + + return NULL; +} +EXPORT_SYMBOL_GPL(riscv_get_intc_hwnode); + int arch_show_interrupts(struct seq_file *p, int prec) { show_ipi_stats(p, prec); diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 499e5f81b3fe33..9066467e99e4e6 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -92,6 +92,11 @@ static const struct irq_domain_ops riscv_intc_domain_ops = { .xlate = irq_domain_xlate_onecell, }; +static struct fwnode_handle *riscv_intc_hwnode(void) +{ + return intc_domain->fwnode; +} + static int __init riscv_intc_init(struct device_node *node, struct device_node *parent) { @@ -126,6 +131,8 @@ static int __init riscv_intc_init(struct device_node *node, return rc; } + riscv_set_intc_hwnode_fn(riscv_intc_hwnode); + cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_STARTING, "irqchip/riscv/intc:starting", riscv_intc_cpu_starting, From 5f0ee105b4615e960ffb6eea5f93fccb6f3a3c7d Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Thu, 24 Mar 2022 16:12:31 +0530 Subject: [PATCH 057/153] genirq: Add mechanism to multiplex a single HW IPI All RISC-V platforms have a single HW IPI provided by the INTC local interrupt controller. The HW method to trigger INTC IPI can be through external irqchip (e.g. RISC-V AIA), through platform specific device (e.g. SiFive CLINT timer), or through firmware (e.g. SBI IPI call). To support multiple IPIs on RISC-V, we add a generic IPI multiplexing mechanism which help us create multiple virtual IPIs using a single HW IPI. This generic IPI multiplexing is shared among various RISC-V irqchip drivers. Signed-off-by: Anup Patel --- include/linux/irq.h | 16 ++++ kernel/irq/Kconfig | 4 + kernel/irq/Makefile | 1 + kernel/irq/ipi-mux.c | 199 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 kernel/irq/ipi-mux.c diff --git a/include/linux/irq.h b/include/linux/irq.h index c3eb89606c2b17..4d3532d8853f96 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -1266,6 +1266,22 @@ int __ipi_send_mask(struct irq_desc *desc, const struct cpumask *dest); int ipi_send_single(unsigned int virq, unsigned int cpu); int ipi_send_mask(unsigned int virq, const struct cpumask *dest); +/** + * struct ipi_mux_ops - IPI multiplex operations + * + * @ipi_mux_clear: Optional function to clear parent IPI + * @ipi_mux_send: Trigger parent IPI on target CPUs + */ +struct ipi_mux_ops { + void (*ipi_mux_clear)(unsigned int parent_virq); + void (*ipi_mux_send)(unsigned int parent_virq, + const struct cpumask *mask); +}; + +void ipi_mux_process(void); +int ipi_mux_create(unsigned int parent_virq, unsigned int nr_ipi, + const struct ipi_mux_ops *ops); + #ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER /* * Registers a generic IRQ handling function as the top-level IRQ handler in diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index db3d174c53d484..ca76c21ccc8551 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -86,6 +86,10 @@ config GENERIC_IRQ_IPI depends on SMP select IRQ_DOMAIN_HIERARCHY +# Generic IRQ IPI Mux support +config GENERIC_IRQ_IPI_MUX + bool + # Generic MSI interrupt support config GENERIC_MSI_IRQ bool diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile index b4f53717d1434f..f19d3080bf11aa 100644 --- a/kernel/irq/Makefile +++ b/kernel/irq/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_GENERIC_IRQ_MIGRATION) += cpuhotplug.o obj-$(CONFIG_PM_SLEEP) += pm.o obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o obj-$(CONFIG_GENERIC_IRQ_IPI) += ipi.o +obj-$(CONFIG_GENERIC_IRQ_IPI_MUX) += ipi-mux.o obj-$(CONFIG_SMP) += affinity.o obj-$(CONFIG_GENERIC_IRQ_DEBUGFS) += debugfs.o obj-$(CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR) += matrix.o diff --git a/kernel/irq/ipi-mux.c b/kernel/irq/ipi-mux.c new file mode 100644 index 00000000000000..bd6b31ca588b34 --- /dev/null +++ b/kernel/irq/ipi-mux.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Multiplex several virtual IPIs over a single HW IPI. + * + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#define pr_fmt(fmt) "ipi-mux: " fmt +#include +#include +#include +#include +#include +#include +#include + +static unsigned int ipi_mux_nr; +static unsigned int ipi_mux_parent_virq; +static struct irq_domain *ipi_mux_domain; +static const struct ipi_mux_ops *ipi_mux_ops; +static DEFINE_PER_CPU(unsigned long, ipi_mux_bits); + +static void ipi_mux_send_mask(struct irq_data *d, const struct cpumask *mask) +{ + int cpu; + + /* Barrier before doing atomic bit update to IPI bits */ + smp_mb__before_atomic(); + + for_each_cpu(cpu, mask) + set_bit(d->hwirq, per_cpu_ptr(&ipi_mux_bits, cpu)); + + /* Barrier after doing atomic bit update to IPI bits */ + smp_mb__after_atomic(); + + /* Trigger the parent IPI */ + ipi_mux_ops->ipi_mux_send(ipi_mux_parent_virq, mask); +} + +static const struct irq_chip ipi_mux_chip = { + .name = "IPI Mux", + .ipi_send_mask = ipi_mux_send_mask, +}; + +static int ipi_mux_domain_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct irq_fwspec *fwspec = arg; + irq_hw_number_t hwirq; + unsigned int type; + int i, ret; + + ret = irq_domain_translate_onecell(d, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) { + irq_set_percpu_devid(virq + i); + irq_domain_set_info(d, virq + i, hwirq + i, + &ipi_mux_chip, d->host_data, + handle_percpu_devid_irq, NULL, NULL); + } + + return 0; +} + +static const struct irq_domain_ops ipi_mux_domain_ops = { + .alloc = ipi_mux_domain_alloc, + .free = irq_domain_free_irqs_top, +}; + +/** + * ipi_mux_process - Process multiplexed virtual IPIs + */ +void ipi_mux_process(void) +{ + unsigned long irqs, *bits = this_cpu_ptr(&ipi_mux_bits); + irq_hw_number_t hwirq; + int err; + + /* Clear the parent IPI */ + if (ipi_mux_ops->ipi_mux_clear) + ipi_mux_ops->ipi_mux_clear(ipi_mux_parent_virq); + + /* + * Barrier for IPI bits paired with smp_mb__before_atomic() + * and smp_mb__after_atomic() used in ipi_mux_send_mask(). + */ + smp_mb(); + + irqs = xchg(bits, 0); + if (!irqs) + return; + + for_each_set_bit(hwirq, &irqs, ipi_mux_nr) { + err = generic_handle_domain_irq(ipi_mux_domain, + hwirq); + if (unlikely(err)) + pr_warn_ratelimited( + "can't find mapping for hwirq %lu\n", + hwirq); + } +} + +static void ipi_mux_handler(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + ipi_mux_process(); + chained_irq_exit(chip, desc); +} + +static int ipi_mux_dying_cpu(unsigned int cpu) +{ + disable_percpu_irq(ipi_mux_parent_virq); + return 0; +} + +static int ipi_mux_starting_cpu(unsigned int cpu) +{ + enable_percpu_irq(ipi_mux_parent_virq, + irq_get_trigger_type(ipi_mux_parent_virq)); + return 0; +} + +/** + * ipi_mux_create - Create virtual IPIs multiplexed on top of a single + * parent IPI. + * @parent_virq: virq of the parent per-CPU IRQ + * @nr_ipi: number of virtual IPIs to create. This should + * be <= BITS_PER_LONG + * @ops: multiplexing operations for the parent IPI + * + * If the parent IPI > 0 then ipi_mux_process() will be automatically + * called via chained handler. + * + * If the parent IPI <= 0 then it is responsibility of irqchip drivers + * to explicitly call ipi_mux_process() for processing muxed IPIs. + * + * Returns first virq of the newly created virtual IPIs upon success + * or <=0 upon failure + */ +int ipi_mux_create(unsigned int parent_virq, unsigned int nr_ipi, + const struct ipi_mux_ops *ops) +{ + struct fwnode_handle *fwnode; + struct irq_domain *domain; + struct irq_fwspec ipi; + int virq; + + if (ipi_mux_domain || BITS_PER_LONG < nr_ipi || + !ops || !ops->ipi_mux_send) + return -EINVAL; + + if (parent_virq && + !irqd_is_per_cpu(irq_desc_get_irq_data(irq_to_desc(parent_virq)))) + return -EINVAL; + + fwnode = irq_domain_alloc_named_fwnode("IPI-Mux"); + if (!fwnode) { + pr_err("unable to create IPI Mux fwnode\n"); + return -ENOMEM; + } + + domain = irq_domain_create_simple(fwnode, nr_ipi, 0, + &ipi_mux_domain_ops, NULL); + if (!domain) { + pr_err("unable to add IPI Mux domain\n"); + irq_domain_free_fwnode(fwnode); + return -ENOMEM; + } + + ipi.fwnode = domain->fwnode; + ipi.param_count = 1; + ipi.param[0] = 0; + virq = __irq_domain_alloc_irqs(domain, -1, nr_ipi, + NUMA_NO_NODE, &ipi, false, NULL); + if (virq <= 0) { + pr_err("unable to alloc IRQs from IPI Mux domain\n"); + irq_domain_remove(domain); + irq_domain_free_fwnode(fwnode); + return virq; + } + + ipi_mux_domain = domain; + ipi_mux_nr = nr_ipi; + ipi_mux_parent_virq = parent_virq; + ipi_mux_ops = ops; + + if (parent_virq > 0) { + irq_set_chained_handler(parent_virq, ipi_mux_handler); + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "irqchip/ipi-mux:starting", + ipi_mux_starting_cpu, ipi_mux_dying_cpu); + } + + return virq; +} From 25bac58f0765534ee487902f4e40c579e1a74001 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 17 Jan 2022 14:10:38 +0530 Subject: [PATCH 058/153] RISC-V: Treat IPIs as normal Linux IRQs Currently, the RISC-V kernel provides arch specific hooks (i.e. struct riscv_ipi_ops) to register IPI handling methods. The stats gathering of IPIs is also arch specific in the RISC-V kernel. Other architectures (such as ARM, ARM64, and MIPS) have moved away from custom arch specific IPI handling methods. Currently, these architectures have Linux irqchip drivers providing a range of Linux IRQ numbers to be used as IPIs and IPI triggering is done using generic IPI APIs. This approach allows architectures to treat IPIs as normal Linux IRQs and IPI stats gathering is done by the generic Linux IRQ subsystem. We extend the RISC-V IPI handling as-per above approach so that arch specific IPI handling methods (struct riscv_ipi_ops) can be removed and the IPI handling is done through the Linux IRQ subsystem. Signed-off-by: Anup Patel --- arch/riscv/Kconfig | 2 + arch/riscv/include/asm/sbi.h | 2 + arch/riscv/include/asm/smp.h | 36 ++++--- arch/riscv/kernel/Makefile | 1 + arch/riscv/kernel/cpu-hotplug.c | 3 +- arch/riscv/kernel/irq.c | 3 +- arch/riscv/kernel/sbi-ipi.c | 60 ++++++++++++ arch/riscv/kernel/sbi.c | 17 ---- arch/riscv/kernel/smp.c | 155 +++++++++++++++--------------- arch/riscv/kernel/smpboot.c | 5 +- drivers/clocksource/timer-clint.c | 41 +++++--- drivers/irqchip/irq-riscv-intc.c | 55 +++++------ 12 files changed, 225 insertions(+), 155 deletions(-) create mode 100644 arch/riscv/kernel/sbi-ipi.c diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 45c60864a75f3e..8965f0826e0bc5 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -59,6 +59,8 @@ config RISCV select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO select GENERIC_IDLE_POLL_SETUP select GENERIC_IOREMAP if MMU + select GENERIC_IRQ_IPI + select GENERIC_IRQ_IPI_MUX select GENERIC_IRQ_MULTI_HANDLER select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW_LEVEL diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h index 9e3c2cf1edafe4..72a5426364ab0f 100644 --- a/arch/riscv/include/asm/sbi.h +++ b/arch/riscv/include/asm/sbi.h @@ -228,6 +228,7 @@ struct sbiret { }; void sbi_init(void); +void sbi_ipi_init(void); struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, @@ -297,6 +298,7 @@ static inline unsigned long sbi_mk_version(unsigned long major, int sbi_err_map_linux_errno(int err); #else /* CONFIG_RISCV_SBI */ static inline int sbi_remote_fence_i(const struct cpumask *cpu_mask) { return -1; } +static inline void sbi_ipi_init(void) { } static inline void sbi_init(void) {} #endif /* CONFIG_RISCV_SBI */ #endif /* _ASM_RISCV_SBI_H */ diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h index d3443be7eedc5f..6b96e2e04aad24 100644 --- a/arch/riscv/include/asm/smp.h +++ b/arch/riscv/include/asm/smp.h @@ -15,11 +15,6 @@ struct seq_file; extern unsigned long boot_cpu_hartid; -struct riscv_ipi_ops { - void (*ipi_inject)(const struct cpumask *target); - void (*ipi_clear)(void); -}; - #ifdef CONFIG_SMP /* * Mapping between linux logical cpu index and hartid. @@ -33,9 +28,6 @@ void show_ipi_stats(struct seq_file *p, int prec); /* SMP initialization hook for setup_arch */ void __init setup_smp(void); -/* Called from C code, this handles an IPI. */ -void handle_IPI(struct pt_regs *regs); - /* Hook for the generic smp_call_function_many() routine. */ void arch_send_call_function_ipi_mask(struct cpumask *mask); @@ -44,11 +36,17 @@ void arch_send_call_function_single_ipi(int cpu); int riscv_hartid_to_cpuid(unsigned long hartid); -/* Set custom IPI operations */ -void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops); +/* Enable IPI for CPU hotplug */ +void riscv_ipi_enable(void); + +/* Disable IPI for CPU hotplug */ +void riscv_ipi_disable(void); -/* Clear IPI for current CPU */ -void riscv_clear_ipi(void); +/* Check if IPI interrupt numbers are available */ +bool riscv_ipi_have_virq_range(void); + +/* Set the IPI interrupt numbers for arch (called by irqchip drivers) */ +void riscv_ipi_set_virq_range(int virq, int nr_irqs, bool percpu_enable); /* Secondary hart entry */ asmlinkage void smp_callin(void); @@ -82,11 +80,21 @@ static inline unsigned long cpuid_to_hartid_map(int cpu) return boot_cpu_hartid; } -static inline void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops) +static inline void riscv_ipi_enable(void) { } -static inline void riscv_clear_ipi(void) +static inline void riscv_ipi_disable(void) +{ +} + +static inline bool riscv_ipi_have_virq_range(void) +{ + return false; +} + +static inline void riscv_ipi_set_virq_range(int virq, int nr, + bool percpu_enable) { } diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 33bb60a354cd20..d85d038dd723a4 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_TRACE_IRQFLAGS) += trace_irq.o obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o obj-$(CONFIG_RISCV_SBI) += sbi.o +obj-$(CONFIG_RISCV_SBI) += sbi-ipi.o ifeq ($(CONFIG_RISCV_SBI), y) obj-$(CONFIG_SMP) += cpu_ops_sbi.o endif diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c index f7a832e3a1d1d4..39235cf5065221 100644 --- a/arch/riscv/kernel/cpu-hotplug.c +++ b/arch/riscv/kernel/cpu-hotplug.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include bool cpu_has_hotplug(unsigned int cpu) { @@ -43,6 +43,7 @@ int __cpu_disable(void) remove_cpu_topology(cpu); numa_remove_cpu(cpu); set_cpu_online(cpu, false); + riscv_ipi_disable(); irq_migrate_all_off_this_cpu(); return ret; diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index 96d3171f0ca11d..eb9a68a539e662 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include static struct fwnode_handle *(*__get_intc_node)(void); @@ -39,4 +39,5 @@ void __init init_IRQ(void) irqchip_init(); if (!handle_arch_irq) panic("No interrupt controller found."); + sbi_ipi_init(); } diff --git a/arch/riscv/kernel/sbi-ipi.c b/arch/riscv/kernel/sbi-ipi.c new file mode 100644 index 00000000000000..5be545f6914cad --- /dev/null +++ b/arch/riscv/kernel/sbi-ipi.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Multiplex several IPIs over a single HW IPI. + * + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#define pr_fmt(fmt) "riscv: " fmt +#include +#include +#include +#include + +static void sbi_send_cpumask_ipi(unsigned int parent_virq, + const struct cpumask *target) +{ + sbi_send_ipi(target); +} + +static void sbi_ipi_clear(unsigned int parent_virq) +{ + csr_clear(CSR_IP, IE_SIE); +} + +static struct ipi_mux_ops sbi_ipi_ops = { + .ipi_mux_clear = sbi_ipi_clear, + .ipi_mux_send = sbi_send_cpumask_ipi, +}; + +void __init sbi_ipi_init(void) +{ + int virq, parent_virq; + struct irq_domain *domain; + + if (riscv_ipi_have_virq_range()) + return; + + domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), + DOMAIN_BUS_ANY); + if (!domain) { + pr_err("unable to find INTC IRQ domain\n"); + return; + } + + parent_virq = irq_create_mapping(domain, RV_IRQ_SOFT); + if (!parent_virq) { + pr_err("unable to create INTC IRQ mapping\n"); + return; + } + + virq = ipi_mux_create(parent_virq, BITS_PER_LONG, &sbi_ipi_ops); + if (virq <= 0) { + pr_err("unable to create muxed IPIs\n"); + irq_dispose_mapping(parent_virq); + return; + } + + riscv_ipi_set_virq_range(virq, BITS_PER_LONG, false); + pr_info("providing IPIs using SBI IPI extension\n"); +} diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c index fc614650a2e37d..e9f04eba0e0967 100644 --- a/arch/riscv/kernel/sbi.c +++ b/arch/riscv/kernel/sbi.c @@ -638,21 +638,6 @@ long sbi_get_mimpid(void) return __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID); } -static void sbi_send_cpumask_ipi(const struct cpumask *target) -{ - sbi_send_ipi(target); -} - -static void sbi_ipi_clear(void) -{ - csr_clear(CSR_IP, IE_SIE); -} - -static const struct riscv_ipi_ops sbi_ipi_ops = { - .ipi_inject = sbi_send_cpumask_ipi, - .ipi_clear = sbi_ipi_clear -}; - void __init sbi_init(void) { int ret; @@ -699,6 +684,4 @@ void __init sbi_init(void) __sbi_send_ipi = __sbi_send_ipi_v01; __sbi_rfence = __sbi_rfence_v01; } - - riscv_set_ipi_ops(&sbi_ipi_ops); } diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index 5724234f972d83..afaf94d422086f 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -17,9 +17,9 @@ #include #include #include +#include #include -#include #include #include @@ -41,11 +41,10 @@ void __init smp_setup_processor_id(void) cpuid_to_hartid_map(0) = boot_cpu_hartid; } -/* A collection of single bit ipi messages. */ -static struct { - unsigned long stats[IPI_MAX] ____cacheline_aligned; - unsigned long bits ____cacheline_aligned; -} ipi_data[NR_CPUS] __cacheline_aligned; +static int ipi_virq_base __ro_after_init; +static int nr_ipi __ro_after_init = IPI_MAX; +static bool ipi_percpu_enable __ro_after_init; +static struct irq_desc *ipi_desc[IPI_MAX] __read_mostly; int riscv_hartid_to_cpuid(unsigned long hartid) { @@ -77,46 +76,14 @@ static void ipi_stop(void) wait_for_interrupt(); } -static const struct riscv_ipi_ops *ipi_ops __ro_after_init; - -void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops) -{ - ipi_ops = ops; -} -EXPORT_SYMBOL_GPL(riscv_set_ipi_ops); - -void riscv_clear_ipi(void) -{ - if (ipi_ops && ipi_ops->ipi_clear) - ipi_ops->ipi_clear(); -} -EXPORT_SYMBOL_GPL(riscv_clear_ipi); - static void send_ipi_mask(const struct cpumask *mask, enum ipi_message_type op) { - int cpu; - - smp_mb__before_atomic(); - for_each_cpu(cpu, mask) - set_bit(op, &ipi_data[cpu].bits); - smp_mb__after_atomic(); - - if (ipi_ops && ipi_ops->ipi_inject) - ipi_ops->ipi_inject(mask); - else - pr_warn("SMP: IPI inject method not available\n"); + __ipi_send_mask(ipi_desc[op], mask); } static void send_ipi_single(int cpu, enum ipi_message_type op) { - smp_mb__before_atomic(); - set_bit(op, &ipi_data[cpu].bits); - smp_mb__after_atomic(); - - if (ipi_ops && ipi_ops->ipi_inject) - ipi_ops->ipi_inject(cpumask_of(cpu)); - else - pr_warn("SMP: IPI inject method not available\n"); + __ipi_send_mask(ipi_desc[op], cpumask_of(cpu)); } #ifdef CONFIG_IRQ_WORK @@ -126,55 +93,89 @@ void arch_irq_work_raise(void) } #endif -void handle_IPI(struct pt_regs *regs) +static irqreturn_t handle_IPI(int irq, void *data) +{ + int ipi = irq - ipi_virq_base; + + switch (ipi) { + case IPI_RESCHEDULE: + scheduler_ipi(); + break; + case IPI_CALL_FUNC: + generic_smp_call_function_interrupt(); + break; + case IPI_CPU_STOP: + ipi_stop(); + break; + case IPI_IRQ_WORK: + irq_work_run(); + break; +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST + case IPI_TIMER: + tick_receive_broadcast(); + break; +#endif + default: + pr_warn("CPU%d: unhandled IPI%d\n", smp_processor_id(), ipi); + break; + }; + + return IRQ_HANDLED; +} + +void riscv_ipi_enable(void) { - unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits; - unsigned long *stats = ipi_data[smp_processor_id()].stats; + int i; - riscv_clear_ipi(); + if (WARN_ON_ONCE(!ipi_virq_base)) + return; - while (true) { - unsigned long ops; + for (i = 0; i < nr_ipi && ipi_percpu_enable; i++) + enable_percpu_irq(ipi_virq_base + i, 0); +} - /* Order bit clearing and data access. */ - mb(); +void riscv_ipi_disable(void) +{ + int i; - ops = xchg(pending_ipis, 0); - if (ops == 0) - return; + if (WARN_ON_ONCE(!ipi_virq_base)) + return; - if (ops & (1 << IPI_RESCHEDULE)) { - stats[IPI_RESCHEDULE]++; - scheduler_ipi(); - } + for (i = 0; i < nr_ipi && ipi_percpu_enable; i++) + disable_percpu_irq(ipi_virq_base + i); +} - if (ops & (1 << IPI_CALL_FUNC)) { - stats[IPI_CALL_FUNC]++; - generic_smp_call_function_interrupt(); - } +bool riscv_ipi_have_virq_range(void) +{ + return (ipi_virq_base) ? true : false; +} - if (ops & (1 << IPI_CPU_STOP)) { - stats[IPI_CPU_STOP]++; - ipi_stop(); - } +void riscv_ipi_set_virq_range(int virq, int nr, bool percpu_enable) +{ + int i, err; - if (ops & (1 << IPI_IRQ_WORK)) { - stats[IPI_IRQ_WORK]++; - irq_work_run(); - } + if (WARN_ON(ipi_virq_base)) + return; -#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST - if (ops & (1 << IPI_TIMER)) { - stats[IPI_TIMER]++; - tick_receive_broadcast(); - } -#endif - BUG_ON((ops >> IPI_MAX) != 0); + WARN_ON(nr < IPI_MAX); + nr_ipi = min(nr, IPI_MAX); + ipi_virq_base = virq; + ipi_percpu_enable = percpu_enable; + + /* Request IPIs */ + for (i = 0; i < nr_ipi; i++) { + err = request_percpu_irq(ipi_virq_base + i, handle_IPI, + "IPI", &ipi_virq_base); + WARN_ON(err); - /* Order data access and bit testing. */ - mb(); + ipi_desc[i] = irq_to_desc(ipi_virq_base + i); + irq_set_status_flags(ipi_virq_base + i, IRQ_HIDDEN); } + + /* Enabled IPIs for boot CPU immediately */ + riscv_ipi_enable(); } +EXPORT_SYMBOL_GPL(riscv_ipi_set_virq_range); static const char * const ipi_names[] = { [IPI_RESCHEDULE] = "Rescheduling interrupts", @@ -192,7 +193,7 @@ void show_ipi_stats(struct seq_file *p, int prec) seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : ""); for_each_online_cpu(cpu) - seq_printf(p, "%10lu ", ipi_data[cpu].stats[i]); + seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu)); seq_printf(p, " %s\n", ipi_names[i]); } } diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index a752c7b416838a..0e41c550d3ff37 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include "head.h" @@ -156,12 +155,12 @@ asmlinkage __visible void smp_callin(void) struct mm_struct *mm = &init_mm; unsigned int curr_cpuid = smp_processor_id(); - riscv_clear_ipi(); - /* All kernel threads share the same mm context. */ mmgrab(mm); current->active_mm = mm; + riscv_ipi_enable(); + notify_cpu_starting(curr_cpuid); numa_add_cpu(curr_cpuid); update_siblings_masks(curr_cpuid); diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c index 6cfe2ab73eb0cc..69f8d2ac191644 100644 --- a/drivers/clocksource/timer-clint.c +++ b/drivers/clocksource/timer-clint.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,6 +33,7 @@ /* CLINT manages IPI and Timer for RISC-V M-mode */ static u32 __iomem *clint_ipi_base; +static unsigned int clint_ipi_irq; static u64 __iomem *clint_timer_cmp; static u64 __iomem *clint_timer_val; static unsigned long clint_timer_freq; @@ -41,7 +44,8 @@ u64 __iomem *clint_time_val; EXPORT_SYMBOL(clint_time_val); #endif -static void clint_send_ipi(const struct cpumask *target) +static void clint_send_ipi(unsigned int parent_virq, + const struct cpumask *target) { unsigned int cpu; @@ -49,14 +53,14 @@ static void clint_send_ipi(const struct cpumask *target) writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu)); } -static void clint_clear_ipi(void) +static void clint_clear_ipi(unsigned int parent_virq) { writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id())); } -static struct riscv_ipi_ops clint_ipi_ops = { - .ipi_inject = clint_send_ipi, - .ipi_clear = clint_clear_ipi, +static struct ipi_mux_ops clint_ipi_ops = { + .ipi_mux_clear = clint_clear_ipi, + .ipi_mux_send = clint_send_ipi, }; #ifdef CONFIG_64BIT @@ -146,7 +150,7 @@ static irqreturn_t clint_timer_interrupt(int irq, void *dev_id) static int __init clint_timer_init_dt(struct device_node *np) { - int rc; + int rc, virq; u32 i, nr_irqs; void __iomem *base; struct of_phandle_args oirq; @@ -170,6 +174,12 @@ static int __init clint_timer_init_dt(struct device_node *np) return -ENODEV; } + /* Find parent irq domain and map ipi irq */ + if (!clint_ipi_irq && + oirq.args[0] == RV_IRQ_SOFT && + irq_find_host(oirq.np)) + clint_ipi_irq = irq_of_parse_and_map(np, i); + /* Find parent irq domain and map timer irq */ if (!clint_timer_irq && oirq.args[0] == RV_IRQ_TIMER && @@ -177,9 +187,9 @@ static int __init clint_timer_init_dt(struct device_node *np) clint_timer_irq = irq_of_parse_and_map(np, i); } - /* If CLINT timer irq not found then fail */ - if (!clint_timer_irq) { - pr_err("%pOFP: timer irq not found\n", np); + /* If CLINT ipi or timer irq not found then fail */ + if (!clint_ipi_irq || !clint_timer_irq) { + pr_err("%pOFP: ipi/timer irq not found\n", np); return -ENODEV; } @@ -228,11 +238,20 @@ static int __init clint_timer_init_dt(struct device_node *np) goto fail_free_irq; } - riscv_set_ipi_ops(&clint_ipi_ops); - clint_clear_ipi(); + virq = ipi_mux_create(clint_ipi_irq, BITS_PER_LONG, &clint_ipi_ops); + if (virq <= 0) { + pr_err("unable to create muxed IPIs\n"); + rc = (virq < 0) ? virq : -ENODEV; + goto fail_remove_cpuhp; + } + + riscv_ipi_set_virq_range(virq, BITS_PER_LONG, false); + clint_clear_ipi(clint_ipi_irq); return 0; +fail_remove_cpuhp: + cpuhp_remove_state(CPUHP_AP_CLINT_TIMER_STARTING); fail_free_irq: free_irq(clint_timer_irq, &clint_clock_event); fail_iounmap: diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 9066467e99e4e6..784d256457048b 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -26,20 +26,7 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs) if (unlikely(cause >= BITS_PER_LONG)) panic("unexpected interrupt cause"); - switch (cause) { -#ifdef CONFIG_SMP - case RV_IRQ_SOFT: - /* - * We only use software interrupts to pass IPIs, so if a - * non-SMP system gets one, then we don't know what to do. - */ - handle_IPI(regs); - break; -#endif - default: - generic_handle_domain_irq(intc_domain, cause); - break; - } + generic_handle_domain_irq(intc_domain, cause); } /* @@ -59,18 +46,6 @@ static void riscv_intc_irq_unmask(struct irq_data *d) csr_set(CSR_IE, BIT(d->hwirq)); } -static int riscv_intc_cpu_starting(unsigned int cpu) -{ - csr_set(CSR_IE, BIT(RV_IRQ_SOFT)); - return 0; -} - -static int riscv_intc_cpu_dying(unsigned int cpu) -{ - csr_clear(CSR_IE, BIT(RV_IRQ_SOFT)); - return 0; -} - static struct irq_chip riscv_intc_chip = { .name = "RISC-V INTC", .irq_mask = riscv_intc_irq_mask, @@ -87,9 +62,32 @@ static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq, return 0; } +static int riscv_intc_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *arg) +{ + int i, ret; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + struct irq_fwspec *fwspec = arg; + + ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) { + ret = riscv_intc_domain_map(domain, virq + i, hwirq + i); + if (ret) + return ret; + } + + return 0; +} + static const struct irq_domain_ops riscv_intc_domain_ops = { .map = riscv_intc_domain_map, .xlate = irq_domain_xlate_onecell, + .alloc = riscv_intc_domain_alloc }; static struct fwnode_handle *riscv_intc_hwnode(void) @@ -133,11 +131,6 @@ static int __init riscv_intc_init(struct device_node *node, riscv_set_intc_hwnode_fn(riscv_intc_hwnode); - cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_STARTING, - "irqchip/riscv/intc:starting", - riscv_intc_cpu_starting, - riscv_intc_cpu_dying); - pr_info("%d local interrupts mapped\n", BITS_PER_LONG); return 0; From 992a34ba2048eb69f7686b9085f0aa8736e702ae Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 24 Jul 2022 15:34:48 -0500 Subject: [PATCH 059/153] fixup! RISC-V: Treat IPIs as normal Linux IRQs Signed-off-by: Samuel Holland --- arch/riscv/Kconfig | 4 ++-- arch/riscv/kernel/irq.c | 3 ++- arch/riscv/kernel/sbi-ipi.c | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 8965f0826e0bc5..132a4061a10735 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -59,8 +59,8 @@ config RISCV select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO select GENERIC_IDLE_POLL_SETUP select GENERIC_IOREMAP if MMU - select GENERIC_IRQ_IPI - select GENERIC_IRQ_IPI_MUX + select GENERIC_IRQ_IPI if SMP + select GENERIC_IRQ_IPI_MUX if SMP select GENERIC_IRQ_MULTI_HANDLER select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW_LEVEL diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index eb9a68a539e662..9297710ff8fd10 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -39,5 +39,6 @@ void __init init_IRQ(void) irqchip_init(); if (!handle_arch_irq) panic("No interrupt controller found."); - sbi_ipi_init(); + if (IS_ENABLED(CONFIG_SMP)) + sbi_ipi_init(); } diff --git a/arch/riscv/kernel/sbi-ipi.c b/arch/riscv/kernel/sbi-ipi.c index 5be545f6914cad..7bc5249b09d4af 100644 --- a/arch/riscv/kernel/sbi-ipi.c +++ b/arch/riscv/kernel/sbi-ipi.c @@ -11,6 +11,8 @@ #include #include +#ifdef CONFIG_SMP + static void sbi_send_cpumask_ipi(unsigned int parent_virq, const struct cpumask *target) { @@ -58,3 +60,5 @@ void __init sbi_ipi_init(void) riscv_ipi_set_virq_range(virq, BITS_PER_LONG, false); pr_info("providing IPIs using SBI IPI extension\n"); } + +#endif From 6d144b1d721953ca2ba7e2c7f3ab2fdcce18e961 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 17 Jan 2022 14:10:40 +0530 Subject: [PATCH 060/153] RISC-V: Allow marking IPIs as suitable for remote FENCEs To do remote FENCEs (i.e. remote TLB flushes) using IPI calls on the RISC-V kernel, we need hardware mechanism to directly inject IPI from the supervisor mode (i.e. RISC-V kernel) instead of using SBI calls. The upcoming AIA IMSIC devices allow direct IPI injection from the supervisor mode (i.e. RISC-V kernel). To support this, we extend the riscv_ipi_set_virq_range() function so that IPI provider (i.e. irqchip drivers can mark IPIs as suitable for remote FENCEs. Signed-off-by: Anup Patel --- arch/riscv/include/asm/smp.h | 19 +++++++++++++++++-- arch/riscv/kernel/sbi-ipi.c | 2 +- arch/riscv/kernel/smp.c | 12 +++++++++++- drivers/clocksource/timer-clint.c | 2 +- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h index 6b96e2e04aad24..6387c92042eae8 100644 --- a/arch/riscv/include/asm/smp.h +++ b/arch/riscv/include/asm/smp.h @@ -16,6 +16,9 @@ struct seq_file; extern unsigned long boot_cpu_hartid; #ifdef CONFIG_SMP + +#include + /* * Mapping between linux logical cpu index and hartid. */ @@ -46,7 +49,13 @@ void riscv_ipi_disable(void); bool riscv_ipi_have_virq_range(void); /* Set the IPI interrupt numbers for arch (called by irqchip drivers) */ -void riscv_ipi_set_virq_range(int virq, int nr_irqs, bool percpu_enable); +void riscv_ipi_set_virq_range(int virq, int nr_irqs, bool percpu_enable, + bool use_for_rfence); + +/* Check if we can use IPIs for remote FENCEs */ +DECLARE_STATIC_KEY_FALSE(riscv_ipi_for_rfence); +#define riscv_use_ipi_for_rfence() \ + static_branch_unlikely(&riscv_ipi_for_rfence) /* Secondary hart entry */ asmlinkage void smp_callin(void); @@ -94,10 +103,16 @@ static inline bool riscv_ipi_have_virq_range(void) } static inline void riscv_ipi_set_virq_range(int virq, int nr, - bool percpu_enable) + bool percpu_enable, + bool use_for_rfence) { } +static inline bool riscv_use_ipi_for_rfence(void) +{ + return false; +} + #endif /* CONFIG_SMP */ #if defined(CONFIG_HOTPLUG_CPU) && (CONFIG_SMP) diff --git a/arch/riscv/kernel/sbi-ipi.c b/arch/riscv/kernel/sbi-ipi.c index 7bc5249b09d4af..40fb09acc780df 100644 --- a/arch/riscv/kernel/sbi-ipi.c +++ b/arch/riscv/kernel/sbi-ipi.c @@ -57,7 +57,7 @@ void __init sbi_ipi_init(void) return; } - riscv_ipi_set_virq_range(virq, BITS_PER_LONG, false); + riscv_ipi_set_virq_range(virq, BITS_PER_LONG, false, false); pr_info("providing IPIs using SBI IPI extension\n"); } diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index afaf94d422086f..10b21ec4d77efc 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -150,7 +150,11 @@ bool riscv_ipi_have_virq_range(void) return (ipi_virq_base) ? true : false; } -void riscv_ipi_set_virq_range(int virq, int nr, bool percpu_enable) +DEFINE_STATIC_KEY_FALSE(riscv_ipi_for_rfence); +EXPORT_SYMBOL_GPL(riscv_ipi_for_rfence); + +void riscv_ipi_set_virq_range(int virq, int nr, bool percpu_enable, + bool use_for_rfence) { int i, err; @@ -174,6 +178,12 @@ void riscv_ipi_set_virq_range(int virq, int nr, bool percpu_enable) /* Enabled IPIs for boot CPU immediately */ riscv_ipi_enable(); + + /* Update RFENCE static key */ + if (use_for_rfence) + static_branch_enable(&riscv_ipi_for_rfence); + else + static_branch_disable(&riscv_ipi_for_rfence); } EXPORT_SYMBOL_GPL(riscv_ipi_set_virq_range); diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c index 69f8d2ac191644..3a90b6b3ca4876 100644 --- a/drivers/clocksource/timer-clint.c +++ b/drivers/clocksource/timer-clint.c @@ -245,7 +245,7 @@ static int __init clint_timer_init_dt(struct device_node *np) goto fail_remove_cpuhp; } - riscv_ipi_set_virq_range(virq, BITS_PER_LONG, false); + riscv_ipi_set_virq_range(virq, BITS_PER_LONG, false, true); clint_clear_ipi(clint_ipi_irq); return 0; From d9ad365fdcd5ac8d847570ed94c04ea3a3fa26b4 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 17 Jan 2022 14:10:42 +0530 Subject: [PATCH 061/153] RISC-V: Use IPIs for remote TLB flush when possible If we have specialized interrupt controller (such as AIA IMSIC) which allows supervisor mode to directly inject IPIs without any assistance from M-mode or HS-mode then using such specialized interrupt controller, we can do remote TLB flushes directly from supervisor mode instead of using the SBI RFENCE calls. This patch extends remote TLB flush functions to use supervisor mode IPIs whenever direct supervisor mode IPIs.are supported by interrupt controller. Signed-off-by: Anup Patel --- arch/riscv/mm/tlbflush.c | 93 +++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/arch/riscv/mm/tlbflush.c b/arch/riscv/mm/tlbflush.c index 37ed760d007c3e..27a7db8eb2c4dd 100644 --- a/arch/riscv/mm/tlbflush.c +++ b/arch/riscv/mm/tlbflush.c @@ -23,14 +23,62 @@ static inline void local_flush_tlb_page_asid(unsigned long addr, : "memory"); } +static inline void local_flush_tlb_range(unsigned long start, + unsigned long size, unsigned long stride) +{ + if (size <= stride) + local_flush_tlb_page(start); + else + local_flush_tlb_all(); +} + +static inline void local_flush_tlb_range_asid(unsigned long start, + unsigned long size, unsigned long stride, unsigned long asid) +{ + if (size <= stride) + local_flush_tlb_page_asid(start, asid); + else + local_flush_tlb_all_asid(asid); +} + +static void __ipi_flush_tlb_all(void *info) +{ + local_flush_tlb_all(); +} + void flush_tlb_all(void) { - sbi_remote_sfence_vma(NULL, 0, -1); + if (riscv_use_ipi_for_rfence()) + on_each_cpu(__ipi_flush_tlb_all, NULL, 1); + else + sbi_remote_sfence_vma(NULL, 0, -1); +} + +struct flush_tlb_range_data { + unsigned long asid; + unsigned long start; + unsigned long size; + unsigned long stride; +}; + +static void __ipi_flush_tlb_range_asid(void *info) +{ + struct flush_tlb_range_data *d = info; + + local_flush_tlb_range_asid(d->start, d->size, d->stride, d->asid); +} + +static void __ipi_flush_tlb_range(void *info) +{ + struct flush_tlb_range_data *d = info; + + local_flush_tlb_range(d->start, d->size, d->stride); } -static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start, - unsigned long size, unsigned long stride) +static void __flush_tlb_range(struct mm_struct *mm, unsigned long start, + unsigned long size, unsigned long stride) { + struct flush_tlb_range_data ftd; struct cpumask *cmask = mm_cpumask(mm); unsigned int cpuid; bool broadcast; @@ -45,19 +93,34 @@ static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start, unsigned long asid = atomic_long_read(&mm->context.id); if (broadcast) { - sbi_remote_sfence_vma_asid(cmask, start, size, asid); - } else if (size <= stride) { - local_flush_tlb_page_asid(start, asid); + if (riscv_use_ipi_for_rfence()) { + ftd.asid = asid; + ftd.start = start; + ftd.size = size; + ftd.stride = stride; + on_each_cpu_mask(cmask, + __ipi_flush_tlb_range_asid, + &ftd, 1); + } else + sbi_remote_sfence_vma_asid(cmask, + start, size, asid); } else { - local_flush_tlb_all_asid(asid); + local_flush_tlb_range_asid(start, size, stride, asid); } } else { if (broadcast) { - sbi_remote_sfence_vma(cmask, start, size); - } else if (size <= stride) { - local_flush_tlb_page(start); + if (riscv_use_ipi_for_rfence()) { + ftd.asid = 0; + ftd.start = start; + ftd.size = size; + ftd.stride = stride; + on_each_cpu_mask(cmask, + __ipi_flush_tlb_range, + &ftd, 1); + } else + sbi_remote_sfence_vma(cmask, start, size); } else { - local_flush_tlb_all(); + local_flush_tlb_range(start, size, stride); } } @@ -66,23 +129,23 @@ static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start, void flush_tlb_mm(struct mm_struct *mm) { - __sbi_tlb_flush_range(mm, 0, -1, PAGE_SIZE); + __flush_tlb_range(mm, 0, -1, PAGE_SIZE); } void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { - __sbi_tlb_flush_range(vma->vm_mm, addr, PAGE_SIZE, PAGE_SIZE); + __flush_tlb_range(vma->vm_mm, addr, PAGE_SIZE, PAGE_SIZE); } void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { - __sbi_tlb_flush_range(vma->vm_mm, start, end - start, PAGE_SIZE); + __flush_tlb_range(vma->vm_mm, start, end - start, PAGE_SIZE); } #ifdef CONFIG_TRANSPARENT_HUGEPAGE void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { - __sbi_tlb_flush_range(vma->vm_mm, start, end - start, PMD_SIZE); + __flush_tlb_range(vma->vm_mm, start, end - start, PMD_SIZE); } #endif From ac056bc110dad9942e744a76d959183ba2da3231 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Tue, 25 Jan 2022 09:48:38 +0530 Subject: [PATCH 062/153] RISC-V: Use IPIs for remote icache flush when possible If we have specialized interrupt controller (such as AIA IMSIC) which allows supervisor mode to directly inject IPIs without any assistance from M-mode or HS-mode then using such specialized interrupt controller, we can do remote icache flushe directly from supervisor mode instead of using the SBI RFENCE calls. This patch extends remote icache flush functions to use supervisor mode IPIs whenever direct supervisor mode IPIs.are supported by interrupt controller. Signed-off-by: Anup Patel --- arch/riscv/mm/cacheflush.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/riscv/mm/cacheflush.c b/arch/riscv/mm/cacheflush.c index 6cb7d96ad9c7bc..7c7e44aaf79161 100644 --- a/arch/riscv/mm/cacheflush.c +++ b/arch/riscv/mm/cacheflush.c @@ -18,7 +18,7 @@ void flush_icache_all(void) { local_flush_icache_all(); - if (IS_ENABLED(CONFIG_RISCV_SBI)) + if (IS_ENABLED(CONFIG_RISCV_SBI) && !riscv_use_ipi_for_rfence()) sbi_remote_fence_i(NULL); else on_each_cpu(ipi_remote_fence_i, NULL, 1); @@ -66,7 +66,8 @@ void flush_icache_mm(struct mm_struct *mm, bool local) * with flush_icache_deferred(). */ smp_mb(); - } else if (IS_ENABLED(CONFIG_RISCV_SBI)) { + } else if (IS_ENABLED(CONFIG_RISCV_SBI) && + !riscv_use_ipi_for_rfence()) { sbi_remote_fence_i(&others); } else { on_each_cpu_mask(&others, ipi_remote_fence_i, NULL, 1); From 847a91ea16f87eb72517efb44acce766e2557406 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Fri, 22 Apr 2022 23:41:07 +0800 Subject: [PATCH 063/153] rtc: sun6i: add support for R329 RTC Allwinner R329 has a RTC with a similar time storage with H616 but a slightly different clock part. As we have already handled the R329 RTC clocks in the CCU driver, add a compatible string to RTC driver to allow probing of the RTC. Commit-notes: Resending this patch separately from Icenowy's R329 series[1] because it is also needed for D1 (which has R329 as its fallback compatible[2]), so I would like to get it in to 5.20. [1]: https://lore.kernel.org/lkml/BYAPR20MB2472C608678F3FAEDA7B7541BCF79@BYAPR20MB2472.namprd20.prod.outlook.com/ [2]: https://lore.kernel.org/lkml/20220203021736.13434-3-samuel@sholland.org/ END Series-prefix: RESEND Series-to: Alessandro Zummo Series-to: Alexandre Belloni Signed-off-by: Icenowy Zheng Signed-off-by: Samuel Holland --- drivers/rtc/rtc-sun6i.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 57540727ce1c14..ed5516089e9a05 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -875,6 +875,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = { { .compatible = "allwinner,sun50i-h6-rtc" }, { .compatible = "allwinner,sun50i-h616-rtc", .data = (void *)RTC_LINEAR_DAY }, + { .compatible = "allwinner,sun50i-r329-rtc", + .data = (void *)RTC_LINEAR_DAY }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); From f52b554d30a279a1384a4aa810a96ad500f500fc Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:46:45 -0500 Subject: [PATCH 064/153] soc: sunxi: sram: Add support for the D1 system control D1 has a single EMAC and some LDOs that need to be exported. Cover-letter: soc: sunxi: sram: Fixes and D1 support This series cleans up a few issues in the system controller driver, and then expands the exported regmap to support one of the pairs of LDOs built in to the D1 SoC. Eventually, we will need to update the SRAM region claiming API so ownership can be swapped back and forth by consumer drivers. This is necessary for uploading firmware to the R329/D1 DSPs, but it is not needed for initial bringup. END Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 7e8dab0f0ec428..92f9186c1c4248 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -294,6 +294,11 @@ static const struct sunxi_sramc_variant sun8i_h3_sramc_variant = { .num_emac_clocks = 1, }; +static const struct sunxi_sramc_variant sun20i_d1_sramc_variant = { + .num_emac_clocks = 1, + .has_ldo_ctrl = true, +}; + static const struct sunxi_sramc_variant sun50i_a64_sramc_variant = { .num_emac_clocks = 1, }; @@ -382,6 +387,10 @@ static const struct of_device_id sunxi_sram_dt_match[] = { .compatible = "allwinner,sun8i-h3-system-control", .data = &sun8i_h3_sramc_variant, }, + { + .compatible = "allwinner,sun20i-d1-system-control", + .data = &sun20i_d1_sramc_variant, + }, { .compatible = "allwinner,sun50i-a64-sram-controller", .data = &sun50i_a64_sramc_variant, From c24701cbd5509f1c9a1c8ec013a97498604df99c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 2 Aug 2022 00:29:32 -0500 Subject: [PATCH 065/153] dt-bindings: nvmem: Allow bit offsets greater than a byte Signed-off-by: Samuel Holland --- Documentation/devicetree/bindings/nvmem/nvmem.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/nvmem/nvmem.yaml b/Documentation/devicetree/bindings/nvmem/nvmem.yaml index 3bb349c634cbcc..4f440ab6a13c86 100644 --- a/Documentation/devicetree/bindings/nvmem/nvmem.yaml +++ b/Documentation/devicetree/bindings/nvmem/nvmem.yaml @@ -53,7 +53,7 @@ patternProperties: $ref: /schemas/types.yaml#/definitions/uint32-array items: - minimum: 0 - maximum: 7 + maximum: 63 description: Offset in bit within the address range specified by reg. - minimum: 1 From fe9080dc6cda063585c3a0d4997f7f1d58d66333 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 3 Aug 2022 23:22:14 -0500 Subject: [PATCH 066/153] dt-bindings: mfd: x-powers,axp152: Document the AXP228 variant AXP228 is a PMIC used on boards such as the Clockwork ClockworkPi and DevTerm. Its register map appears to be identical to the AXP221 variant. The only known difference is in the default values for regulator on/off states and voltages. Series-to: Chen-Yu Tsai Series-to: Lee Jones Signed-off-by: Samuel Holland --- Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml b/Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml index 3a53bae611bcd1..cfbf221789bb88 100644 --- a/Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml +++ b/Documentation/devicetree/bindings/mfd/x-powers,axp152.yaml @@ -92,6 +92,9 @@ properties: - x-powers,axp806 - x-powers,axp809 - x-powers,axp813 + - items: + - const: x-powers,axp228 + - const: x-powers,axp221 - items: - const: x-powers,axp805 - const: x-powers,axp806 From b7a706313eedf0fc81919477b66187f01d11b311 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 7 Aug 2022 20:08:49 -0500 Subject: [PATCH 067/153] clk: sunxi-ng: mp: Avoid computing the rate twice ccu_mp_find_best() already computes a best_rate at the same time as the best m and p factors. Return it so the caller does not need to duplicate the division. Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Signed-off-by: Samuel Holland --- drivers/clk/sunxi-ng/ccu_mp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c index 57cf2d615148ca..cc94a694cb676c 100644 --- a/drivers/clk/sunxi-ng/ccu_mp.c +++ b/drivers/clk/sunxi-ng/ccu_mp.c @@ -10,9 +10,9 @@ #include "ccu_gate.h" #include "ccu_mp.h" -static void ccu_mp_find_best(unsigned long parent, unsigned long rate, - unsigned int max_m, unsigned int max_p, - unsigned int *m, unsigned int *p) +static unsigned long ccu_mp_find_best(unsigned long parent, unsigned long rate, + unsigned int max_m, unsigned int max_p, + unsigned int *m, unsigned int *p) { unsigned long best_rate = 0; unsigned int best_m = 0, best_p = 0; @@ -35,6 +35,8 @@ static void ccu_mp_find_best(unsigned long parent, unsigned long rate, *m = best_m; *p = best_p; + + return best_rate; } static unsigned long ccu_mp_find_best_with_parent_adj(struct clk_hw *hw, @@ -109,8 +111,7 @@ static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux, max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); if (!clk_hw_can_set_rate_parent(&cmp->common.hw)) { - ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p); - rate = *parent_rate / p / m; + rate = ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p); } else { rate = ccu_mp_find_best_with_parent_adj(hw, parent_rate, rate, max_m, max_p); From 96419302b44a38b53ba831ccbd3b20b9fb2a068e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 6 Aug 2022 17:18:55 -0500 Subject: [PATCH 068/153] mmc: sunxi-mmc: Fix clock refcount imbalance during unbind If the controller is suspended by runtime PM, the clock is already disabled, so do not try to disable it again during removal. Use pm_runtime_disable() to flush any pending runtime PM transitions. Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Series-to: Ulf Hansson Series-to: Wolfram Sang Fixes: 9a8e1e8cc2c0 ("mmc: sunxi: Add runtime_pm support") Signed-off-by: Samuel Holland --- drivers/mmc/host/sunxi-mmc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index b16e12e62e7222..3db9f32d6a7b9f 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1492,9 +1492,11 @@ static int sunxi_mmc_remove(struct platform_device *pdev) struct sunxi_mmc_host *host = mmc_priv(mmc); mmc_remove_host(mmc); - pm_runtime_force_suspend(&pdev->dev); - disable_irq(host->irq); - sunxi_mmc_disable(host); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) { + disable_irq(host->irq); + sunxi_mmc_disable(host); + } dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); mmc_free_host(mmc); From 159d1492c7a11622c83bc2b5473bf7ecbcd7f734 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 7 Aug 2022 10:47:36 -0500 Subject: [PATCH 069/153] dt-bindings: display: sun4i: Add D1 TCONs to conditionals When adding the D1 TCON bindings, I missed the conditional blocks that restrict the binding for TCON LCD vs TCON TV hardware. Add the D1 TCON variants to the appropriate blocks for DE2 TCON LCDs and TCON TVs. Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Series-to: Maxime Ripard Fixes: ae5a5d26c15c ("dt-bindings: display: Add D1 display engine compatibles") Signed-off-by: Samuel Holland --- .../devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml index 4a92a4c7dcd70e..f8168986a0a9ea 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml @@ -233,6 +233,7 @@ allOf: - allwinner,sun8i-a83t-tcon-lcd - allwinner,sun8i-v3s-tcon - allwinner,sun9i-a80-tcon-lcd + - allwinner,sun20i-d1-tcon-lcd then: properties: @@ -252,6 +253,7 @@ allOf: - allwinner,sun8i-a83t-tcon-tv - allwinner,sun8i-r40-tcon-tv - allwinner,sun9i-a80-tcon-tv + - allwinner,sun20i-d1-tcon-tv then: properties: @@ -278,6 +280,7 @@ allOf: - allwinner,sun9i-a80-tcon-lcd - allwinner,sun4i-a10-tcon - allwinner,sun8i-a83t-tcon-lcd + - allwinner,sun20i-d1-tcon-lcd then: required: @@ -294,6 +297,7 @@ allOf: - allwinner,sun8i-a23-tcon - allwinner,sun8i-a33-tcon - allwinner,sun8i-a83t-tcon-lcd + - allwinner,sun20i-d1-tcon-lcd then: properties: From 36e6cb9519380b60a84ee6d8f0e82eb0eb73d86b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 7 Aug 2022 10:49:30 -0500 Subject: [PATCH 070/153] dt-bindings: display: sun6i-dsi: Fix clock conditional The A64 case should have limited maxItems, instead of duplicating the minItems value from the main binding. While here, simplify the binding by making this an "else" case of the two-clock conditional block. Fixes: fe5040f2843a ("dt-bindings: sun6i-dsi: Document A64 MIPI-DSI controller") Signed-off-by: Samuel Holland --- .../bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml index bf0bdf54e5f9e4..ae55ef3fb1fed1 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-mipi-dsi.yaml @@ -78,16 +78,10 @@ allOf: required: - clock-names - - if: - properties: - compatible: - contains: - const: allwinner,sun50i-a64-mipi-dsi - - then: + else: properties: clocks: - minItems: 1 + maxItems: 1 unevaluatedProperties: false From b605a3e0854b5616d9cd87927ddb13cd2f5578b6 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 7 May 2022 18:34:25 -0500 Subject: [PATCH 071/153] genirq: Add support for oneshot-safe threaded EOIs irqchips can use the combination of flags IRQCHIP_ONESHOT_SAFE | IRQCHIP_EOI_THREADED to elide mask operations. Signed-off-by: Samuel Holland --- kernel/irq/chip.c | 36 +++++++++++++++++------------------- kernel/irq/internals.h | 2 +- kernel/irq/manage.c | 12 ++++++------ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 8ac37e8e738a3b..0e2a0875edace4 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -439,16 +439,6 @@ void unmask_irq(struct irq_desc *desc) } } -void unmask_threaded_irq(struct irq_desc *desc) -{ - struct irq_chip *chip = desc->irq_data.chip; - - if (chip->flags & IRQCHIP_EOI_THREADED) - chip->irq_eoi(&desc->irq_data); - - unmask_irq(desc); -} - /* * handle_nested_irq - Handle a nested irq from a irq thread * @irq: the interrupt number @@ -656,25 +646,33 @@ void handle_level_irq(struct irq_desc *desc) } EXPORT_SYMBOL_GPL(handle_level_irq); -static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip) +void unmask_eoi_threaded_irq(struct irq_desc *desc) { - if (!(desc->istate & IRQS_ONESHOT)) { + struct irq_chip *chip = desc->irq_data.chip; + + if (desc->istate & IRQS_ONESHOT) + unmask_irq(desc); + + if (chip->flags & IRQCHIP_EOI_THREADED) chip->irq_eoi(&desc->irq_data); +} + +static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip) +{ + /* Do not send EOI if the thread will do it for us. */ + if ((chip->flags & IRQCHIP_EOI_THREADED) && desc->threads_oneshot) return; - } + /* * We need to unmask in the following cases: * - Oneshot irq which did not wake the thread (caused by a * spurious interrupt or a primary handler handling it * completely). */ - if (!irqd_irq_disabled(&desc->irq_data) && - irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) { - chip->irq_eoi(&desc->irq_data); + if ((desc->istate & IRQS_ONESHOT) && !desc->threads_oneshot) unmask_irq(desc); - } else if (!(chip->flags & IRQCHIP_EOI_THREADED)) { - chip->irq_eoi(&desc->irq_data); - } + + chip->irq_eoi(&desc->irq_data); } /** diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index f09c60393e559f..b530741c11b1e4 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -91,7 +91,7 @@ extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu); extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu); extern void mask_irq(struct irq_desc *desc); extern void unmask_irq(struct irq_desc *desc); -extern void unmask_threaded_irq(struct irq_desc *desc); +extern void unmask_eoi_threaded_irq(struct irq_desc *desc); #ifdef CONFIG_SPARSE_IRQ static inline void irq_mark_irq(unsigned int irq) { } diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 40fe7806cc8c99..f7ec12f26f1a90 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1074,9 +1074,9 @@ static int irq_wait_for_interrupt(struct irqaction *action) static void irq_finalize_oneshot(struct irq_desc *desc, struct irqaction *action) { - if (!(desc->istate & IRQS_ONESHOT) || - action->handler == irq_forced_secondary_handler) + if (action->handler == irq_forced_secondary_handler) return; + again: chip_bus_lock(desc); raw_spin_lock_irq(&desc->lock); @@ -1112,9 +1112,8 @@ static void irq_finalize_oneshot(struct irq_desc *desc, desc->threads_oneshot &= ~action->thread_mask; - if (!desc->threads_oneshot && !irqd_irq_disabled(&desc->irq_data) && - irqd_irq_masked(&desc->irq_data)) - unmask_threaded_irq(desc); + if (!desc->threads_oneshot) + unmask_eoi_threaded_irq(desc); out_unlock: raw_spin_unlock_irq(&desc->lock); @@ -1662,7 +1661,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) * !ONESHOT irqs the thread mask is 0 so we can avoid a * conditional in irq_wake_thread(). */ - if (new->flags & IRQF_ONESHOT) { + if ((new->flags & IRQF_ONESHOT) || + (desc->irq_data.chip->flags & (IRQCHIP_ONESHOT_SAFE | IRQCHIP_EOI_THREADED)) == (IRQCHIP_ONESHOT_SAFE | IRQCHIP_EOI_THREADED)) { /* * Unlikely to have 32 resp 64 irqs sharing one line, * but who knows. From 17ad6d9fb898e02257756362362fbcde2570ae06 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 7 May 2022 18:38:34 -0500 Subject: [PATCH 072/153] irqchip/sifive-plic: Enable oneshot-safe threaded EOIs Signed-off-by: Samuel Holland --- drivers/irqchip/irq-sifive-plic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 2f4784860df5df..24c7ffa6f282df 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -201,7 +201,9 @@ static struct irq_chip plic_chip = { .irq_set_affinity = plic_set_affinity, #endif .irq_set_type = plic_irq_set_type, - .flags = IRQCHIP_AFFINITY_PRE_STARTUP, + .flags = IRQCHIP_ONESHOT_SAFE | + IRQCHIP_EOI_THREADED | + IRQCHIP_AFFINITY_PRE_STARTUP, }; static int plic_irq_set_type(struct irq_data *d, unsigned int type) From 161cb8d1d4a7b0c027e92000b7dfc4584e8979ff Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 16 May 2021 14:05:17 -0500 Subject: [PATCH 073/153] dt-bindings: riscv: Add T-HEAD C906 and C910 compatibles The C906 and C910 are RISC-V CPU cores from T-HEAD Semiconductor. Notably, the C906 core is used in the Allwinner D1 SoC. Signed-off-by: Samuel Holland --- Documentation/devicetree/bindings/riscv/cpus.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index d632ac76532e9b..f7bfda2911edda 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -38,6 +38,8 @@ properties: - sifive,u5 - sifive,u7 - canaan,k210 + - thead,c906 + - thead,c910 - const: riscv - items: - enum: From cd1c52e4acbc0f16dc486bb10dc2cd9a4c7a1bfb Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 17 Jul 2022 14:42:05 -0500 Subject: [PATCH 074/153] dt-bindings: vendor-prefixes: Add Allwinner D1 board vendors Some boards using the Allwinner D1 SoC are made by vendors not previously documented. Clockwork Tech LLC (https://www.clockworkpi.com/) manufactures the ClockworkPi family of boards. Beijing Widora Technology Co., Ltd. (https://mangopi.cc/) manufactures the MangoPi family of boards. Signed-off-by: Samuel Holland --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index f0db7320969419..6c333a3c3a30b7 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -254,6 +254,8 @@ patternProperties: description: Cirrus Logic, Inc. "^cisco,.*": description: Cisco Systems, Inc. + "^clockwork,.*": + description: Clockwork Tech LLC "^cloudengines,.*": description: Cloud Engines, Inc. "^cnm,.*": @@ -1392,6 +1394,8 @@ patternProperties: description: Shenzhen whwave Electronics, Inc. "^wi2wi,.*": description: Wi2Wi, Inc. + "^widora,.*": + description: Beijing Widora Technology Co., Ltd. "^wiligear,.*": description: Wiligear, Ltd. "^willsemi,.*": From c21fab1e02fb72f6fda829b47bee31886840750b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 29 Jun 2022 00:26:39 -0500 Subject: [PATCH 075/153] dt-bindings: riscv: Add Allwinner D1 board compatibles Several SoMs and boards are available that feature the Allwinner D1 SoC. Document their compatible strings. Signed-off-by: Samuel Holland --- .../devicetree/bindings/riscv/allwinner.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/riscv/allwinner.yaml diff --git a/Documentation/devicetree/bindings/riscv/allwinner.yaml b/Documentation/devicetree/bindings/riscv/allwinner.yaml new file mode 100644 index 00000000000000..b8bbad6cf0ad43 --- /dev/null +++ b/Documentation/devicetree/bindings/riscv/allwinner.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/riscv/allwinner.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner RISC-V SoC-based boards + +maintainers: + - Samuel Holland + +description: + Allwinner RISC-V SoC-based boards + +properties: + $nodename: + const: '/' + compatible: + oneOf: + - description: Dongshan Nezha STU SoM + items: + - const: 100ask,dongshan-nezha-stu + - const: allwinner,sun20i-d1 + + - description: D1 Nezha board + items: + - const: allwinner,d1-nezha + - const: allwinner,sun20i-d1 + + - description: ClockworkPi R-01 SoM and v3.14 board + items: + - const: clockwork,r-01-clockworkpi-v3.14 + - const: allwinner,sun20i-d1 + + - description: ClockworkPi R-01 SoM, v3.14 board, and DevTerm expansion + items: + - const: clockwork,r-01-devterm-v3.14 + - const: clockwork,r-01-clockworkpi-v3.14 + - const: allwinner,sun20i-d1 + + - description: Lichee RV SoM + items: + - const: sipeed,lichee-rv + - const: allwinner,sun20i-d1 + + - description: Carrier boards for the Lichee RV SoM + items: + - enum: + - sipeed,lichee-rv-86-panel-480p + - sipeed,lichee-rv-86-panel-720p + - sipeed,lichee-rv-dock + - const: sipeed,lichee-rv + - const: allwinner,sun20i-d1 + + - description: MangoPi MQ Pro board + items: + - const: widora,mangopi-mq-pro + - const: allwinner,sun20i-d1 + +additionalProperties: true + +... From 9a41ddb36a4ec75d7a11164850f741f3b33d5671 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 16 May 2021 14:17:45 -0500 Subject: [PATCH 076/153] riscv: Add Allwinner SoC family Kconfig option Allwinner manufactures the sunxi family of application processors. This includes the "sun8i" series of ARMv7 SoCs, the "sun50i" series of ARMv8 SoCs, and now the "sun20i" series of 64-bit RISC-V SoCs. The first SoC in the sun20i series is D1, containing a single T-HEAD C906 core. D1s is a low-pin-count variant of D1 with co-packaged DRAM. Most peripherals are shared across the entire chip family. In fact, the ARMv7 T113 SoC is pin-compatible and almost entirely register-compatible with the D1s. This means many existing device drivers can be reused. To facilitate this reuse, name the symbol ARCH_SUNXI, since that is what the existing drivers have as their dependency. Signed-off-by: Samuel Holland --- arch/riscv/Kconfig.socs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 69774bb362d6a7..1caacbfac1a513 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -1,5 +1,14 @@ menu "SoC selection" +config ARCH_SUNXI + bool "Allwinner sun20i SoCs" + select ERRATA_THEAD if MMU && !XIP_KERNEL + select SIFIVE_PLIC + select SUN4I_TIMER + help + This enables support for Allwinner sun20i platform hardware, + including boards based on the D1 and D1s SoCs. + config SOC_MICROCHIP_POLARFIRE bool "Microchip PolarFire SoCs" select MCHP_CLK_MPFS From 1cbb9e2b25732344b18a9ec658aa23cf35da5086 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 9 Aug 2022 20:17:01 -0500 Subject: [PATCH 077/153] drm/sun4i: dsi: Prevent underflow when computing packet sizes Currently, the packet overhead is subtracted using unsigned arithmetic. With a short sync pulse, this could underflow and wrap around to near the maximal u16 value. Fix this by using signed subtraction. The call to max() will correctly handle any negative numbers that are produced. Apply the same fix to the other timings, even though those subtractions are less likely to underflow. Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Series-to: Maxime Ripard Fixes: 133add5b5ad4 ("drm/sun4i: Add Allwinner A31 MIPI-DSI controller support") Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index b4dfa166eccdfa..34234a144e87da 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -531,7 +531,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, struct drm_display_mode *mode) { struct mipi_dsi_device *device = dsi->device; - unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; + int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0; u32 basic_ctl = 0; size_t bytes; @@ -555,7 +555,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, * (4 bytes). Its minimal size is therefore 10 bytes */ #define HSA_PACKET_OVERHEAD 10 - hsa = max((unsigned int)HSA_PACKET_OVERHEAD, + hsa = max(HSA_PACKET_OVERHEAD, (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); /* @@ -564,7 +564,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, * therefore 6 bytes */ #define HBP_PACKET_OVERHEAD 6 - hbp = max((unsigned int)HBP_PACKET_OVERHEAD, + hbp = max(HBP_PACKET_OVERHEAD, (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD); /* @@ -574,7 +574,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, * 16 bytes */ #define HFP_PACKET_OVERHEAD 16 - hfp = max((unsigned int)HFP_PACKET_OVERHEAD, + hfp = max(HFP_PACKET_OVERHEAD, (mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD); /* @@ -583,7 +583,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, * bytes). Its minimal size is therefore 10 bytes. */ #define HBLK_PACKET_OVERHEAD 10 - hblk = max((unsigned int)HBLK_PACKET_OVERHEAD, + hblk = max(HBLK_PACKET_OVERHEAD, (mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp - HBLK_PACKET_OVERHEAD); From 4590057bb73522be99737995b6563adafd664b92 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 28 May 2022 19:04:56 -0500 Subject: [PATCH 078/153] irqchip/sifive-plic: Support wake IRQs Signed-off-by: Samuel Holland --- drivers/irqchip/irq-sifive-plic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 24c7ffa6f282df..d8fc3354b38c2d 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -187,7 +187,8 @@ static struct irq_chip plic_edge_chip = { .irq_set_affinity = plic_set_affinity, #endif .irq_set_type = plic_irq_set_type, - .flags = IRQCHIP_AFFINITY_PRE_STARTUP, + .flags = IRQCHIP_SKIP_SET_WAKE | + IRQCHIP_AFFINITY_PRE_STARTUP, }; static struct irq_chip plic_chip = { @@ -201,7 +202,8 @@ static struct irq_chip plic_chip = { .irq_set_affinity = plic_set_affinity, #endif .irq_set_type = plic_irq_set_type, - .flags = IRQCHIP_ONESHOT_SAFE | + .flags = IRQCHIP_SKIP_SET_WAKE | + IRQCHIP_ONESHOT_SAFE | IRQCHIP_EOI_THREADED | IRQCHIP_AFFINITY_PRE_STARTUP, }; From 26aa629b88034e33dd9782b3f76eeb7a30f3e801 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 27 Apr 2022 18:47:53 -0500 Subject: [PATCH 079/153] riscv: mm: Use IOMMU for DMA when available Signed-off-by: Samuel Holland --- arch/riscv/mm/dma-noncoherent.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c index a8dc0bd9078d7e..d85b4cd094b060 100644 --- a/arch/riscv/mm/dma-noncoherent.c +++ b/arch/riscv/mm/dma-noncoherent.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -73,6 +74,9 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, dev_driver_string(dev), dev_name(dev)); dev->dma_coherent = coherent; + + if (iommu) + iommu_setup_dma_ops(dev, dma_base, dma_base + size - 1); } #ifdef CONFIG_RISCV_ISA_ZICBOM From 7526dae288bc8162f15ca7f7add1860c1151c2c9 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 28 Jun 2022 23:20:33 -0500 Subject: [PATCH 080/153] riscv: dts: allwinner: Add the D1 SoC base devicetree D1 is a SoC containing a single-core T-HEAD Xuantie C906 CPU, as well as one HiFi 4 DSP. The SoC is based on a design that additionally contained a pair of Cortex A7's. For that reason, some peripherals are duplicated. This devicetree includes all of the peripherals that already have a documented binding. Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/Makefile | 1 + arch/riscv/boot/dts/allwinner/Makefile | 1 + arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 959 +++++++++++++++++++ 3 files changed, 961 insertions(+) create mode 100644 arch/riscv/boot/dts/allwinner/Makefile create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile index ff174996cdfd0a..f292e31bdb2c8d 100644 --- a/arch/riscv/boot/dts/Makefile +++ b/arch/riscv/boot/dts/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +subdir-y += allwinner subdir-y += sifive subdir-y += starfive subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile new file mode 100644 index 00000000000000..f66554cd5c4518 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/Makefile @@ -0,0 +1 @@ +# SPDX-License-Identifier: GPL-2.0 diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi new file mode 100644 index 00000000000000..115c3b90b1f730 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -0,0 +1,959 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2021-2022 Samuel Holland + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + + cpus { + timebase-frequency = <24000000>; + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "thead,c906", "riscv"; + device_type = "cpu"; + reg = <0>; + clocks = <&ccu CLK_RISCV>; + clock-frequency = <24000000>; + d-cache-block-size = <64>; + d-cache-sets = <256>; + d-cache-size = <32768>; + i-cache-block-size = <64>; + i-cache-sets = <128>; + i-cache-size = <32768>; + mmu-type = "riscv,sv39"; + riscv,isa = "rv64imafdc"; + #cooling-cells = <2>; + + cpu0_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + }; + + de: display-engine { + compatible = "allwinner,sun20i-d1-display-engine"; + allwinner,pipelines = <&mixer0>, <&mixer1>; + status = "disabled"; + }; + + osc24M: osc24M-clk { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "osc24M"; + #clock-cells = <0>; + }; + + thermal-zones { + cpu-thermal { + polling-delay = <0>; + polling-delay-passive = <0>; + thermal-sensors = <&ths>; + + trips { + cpu_target: cpu-target { + hysteresis = <3000>; + temperature = <85000>; + type = "passive"; + }; + + cpu-crit { + hysteresis = <0>; + temperature = <110000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_target>; + cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + }; + + soc { + compatible = "simple-bus"; + ranges; + interrupt-parent = <&plic>; + dma-noncoherent; + #address-cells = <1>; + #size-cells = <1>; + + dsp_wdt: watchdog@1700400 { + compatible = "allwinner,sun20i-d1-wdt"; + reg = <0x1700400 0x20>; + interrupts = <138 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&osc24M>, <&rtc CLK_OSC32K>; + clock-names = "hosc", "losc"; + status = "reserved"; + }; + + pio: pinctrl@2000000 { + compatible = "allwinner,sun20i-d1-pinctrl"; + reg = <0x2000000 0x800>; + interrupts = <85 IRQ_TYPE_LEVEL_HIGH>, + <87 IRQ_TYPE_LEVEL_HIGH>, + <89 IRQ_TYPE_LEVEL_HIGH>, + <91 IRQ_TYPE_LEVEL_HIGH>, + <93 IRQ_TYPE_LEVEL_HIGH>, + <95 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_APB0>, + <&osc24M>, + <&rtc CLK_OSC32K>; + clock-names = "apb", "hosc", "losc"; + gpio-controller; + interrupt-controller; + #gpio-cells = <3>; + #interrupt-cells = <3>; + + /omit-if-no-ref/ + i2c0_pb10_pins: i2c0-pb10-pins { + pins = "PB10", "PB11"; + function = "i2c0"; + }; + + /omit-if-no-ref/ + i2c2_pb0_pins: i2c2-pb0-pins { + pins = "PB0", "PB1"; + function = "i2c2"; + }; + + /omit-if-no-ref/ + lcd_rgb666_pins: lcd-rgb666-pins { + pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", + "PD6", "PD7", "PD8", "PD9", "PD10", "PD11", + "PD12", "PD13", "PD14", "PD15", "PD16", "PD17", + "PD18", "PD19", "PD20", "PD21"; + function = "lcd0"; + }; + + /omit-if-no-ref/ + mmc0_pins: mmc0-pins { + pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; + function = "mmc0"; + }; + + /omit-if-no-ref/ + mmc1_pins: mmc1-pins { + pins = "PG0", "PG1", "PG2", "PG3", "PG4", "PG5"; + function = "mmc1"; + }; + + /omit-if-no-ref/ + mmc2_pins: mmc2-pins { + pins = "PC2", "PC3", "PC4", "PC5", "PC6", "PC7"; + function = "mmc2"; + }; + + /omit-if-no-ref/ + rgmii_pe_pins: rgmii-pe-pins { + pins = "PE0", "PE1", "PE2", "PE3", "PE4", + "PE5", "PE6", "PE7", "PE8", "PE9", + "PE11", "PE12", "PE13", "PE14", "PE15"; + function = "emac"; + }; + + /omit-if-no-ref/ + rmii_pe_pins: rmii-pe-pins { + pins = "PE0", "PE1", "PE2", "PE3", "PE4", + "PE5", "PE6", "PE7", "PE8", "PE9"; + function = "emac"; + }; + + /omit-if-no-ref/ + uart0_pb8_pins: uart0-pb8-pins { + pins = "PB8", "PB9"; + function = "uart0"; + }; + + /omit-if-no-ref/ + uart1_pg6_pins: uart1-pg6-pins { + pins = "PG6", "PG7"; + function = "uart1"; + }; + + /omit-if-no-ref/ + uart1_pg8_rts_cts_pins: uart1-pg8-rts-cts-pins { + pins = "PG8", "PG9"; + function = "uart1"; + }; + }; + + ccu: clock-controller@2001000 { + compatible = "allwinner,sun20i-d1-ccu"; + reg = <0x2001000 0x1000>; + clocks = <&osc24M>, + <&rtc CLK_OSC32K>, + <&rtc CLK_IOSC>; + clock-names = "hosc", "losc", "iosc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + ths: temperature-sensor@2009400 { + compatible = "allwinner,sun20i-d1-ths"; + reg = <0x2009400 0x400>; + interrupts = <74 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_THS>, <&osc24M>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_THS>; + nvmem-cells = <&ths_calib>; + nvmem-cell-names = "calibration"; + #thermal-sensor-cells = <0>; + }; + + lradc: keys@2009800 { + compatible = "allwinner,sun20i-d1-lradc", + "allwinner,sun50i-r329-lradc"; + reg = <0x2009800 0x400>; + interrupts = <77 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_LRADC>; + resets = <&ccu RST_BUS_LRADC>; + status = "disabled"; + }; + + iommu: iommu@2010000 { + compatible = "allwinner,sun20i-d1-iommu"; + reg = <0x2010000 0x10000>; + interrupts = <80 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_IOMMU>; + #iommu-cells = <1>; + }; + + codec: audio-codec@2030000 { + compatible = "simple-mfd", "syscon"; + reg = <0x2030000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + regulators@2030348 { + compatible = "allwinner,sun20i-d1-analog-ldos"; + reg = <0x2030348 0x4>; + nvmem-cells = <&bg_trim>; + nvmem-cell-names = "bg_trim"; + + reg_aldo: aldo { + }; + + reg_hpldo: hpldo { + }; + }; + }; + + i2s0: i2s@2032000 { + compatible = "allwinner,sun20i-d1-i2s", + "allwinner,sun50i-r329-i2s"; + reg = <0x2032000 0x1000>; + interrupts = <42 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_I2S0>, + <&ccu CLK_I2S0>; + clock-names = "apb", "mod"; + resets = <&ccu RST_BUS_I2S0>; + dmas = <&dma 3>, <&dma 3>; + dma-names = "rx", "tx"; + status = "disabled"; + #sound-dai-cells = <0>; + }; + + i2s1: i2s@2033000 { + compatible = "allwinner,sun20i-d1-i2s", + "allwinner,sun50i-r329-i2s"; + reg = <0x2033000 0x1000>; + interrupts = <43 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_I2S1>, + <&ccu CLK_I2S1>; + clock-names = "apb", "mod"; + resets = <&ccu RST_BUS_I2S1>; + dmas = <&dma 4>, <&dma 4>; + dma-names = "rx", "tx"; + status = "disabled"; + #sound-dai-cells = <0>; + }; + + i2s2: i2s@2034000 { + compatible = "allwinner,sun20i-d1-i2s", + "allwinner,sun50i-r329-i2s"; + reg = <0x2034000 0x1000>; + interrupts = <44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_I2S2>, + <&ccu CLK_I2S2>; + clock-names = "apb", "mod"; + resets = <&ccu RST_BUS_I2S2>; + dmas = <&dma 5>, <&dma 5>; + dma-names = "rx", "tx"; + status = "disabled"; + #sound-dai-cells = <0>; + }; + + timer: timer@2050000 { + compatible = "allwinner,sun20i-d1-timer", + "allwinner,sun8i-a23-timer"; + reg = <0x2050000 0xa0>; + interrupts = <75 IRQ_TYPE_LEVEL_HIGH>, + <76 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&osc24M>; + }; + + wdt: watchdog@20500a0 { + compatible = "allwinner,sun20i-d1-wdt-reset", + "allwinner,sun20i-d1-wdt"; + reg = <0x20500a0 0x20>; + interrupts = <79 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&osc24M>, <&rtc CLK_OSC32K>; + clock-names = "hosc", "losc"; + status = "reserved"; + }; + + uart0: serial@2500000 { + compatible = "snps,dw-apb-uart"; + reg = <0x2500000 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = <18 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_UART0>; + resets = <&ccu RST_BUS_UART0>; + dmas = <&dma 14>, <&dma 14>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart1: serial@2500400 { + compatible = "snps,dw-apb-uart"; + reg = <0x2500400 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = <19 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_UART1>; + resets = <&ccu RST_BUS_UART1>; + dmas = <&dma 15>, <&dma 15>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart2: serial@2500800 { + compatible = "snps,dw-apb-uart"; + reg = <0x2500800 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_UART2>; + resets = <&ccu RST_BUS_UART2>; + dmas = <&dma 16>, <&dma 16>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart3: serial@2500c00 { + compatible = "snps,dw-apb-uart"; + reg = <0x2500c00 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = <21 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_UART3>; + resets = <&ccu RST_BUS_UART3>; + dmas = <&dma 17>, <&dma 17>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart4: serial@2501000 { + compatible = "snps,dw-apb-uart"; + reg = <0x2501000 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_UART4>; + resets = <&ccu RST_BUS_UART4>; + dmas = <&dma 18>, <&dma 18>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart5: serial@2501400 { + compatible = "snps,dw-apb-uart"; + reg = <0x2501400 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupts = <23 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_UART5>; + resets = <&ccu RST_BUS_UART5>; + dmas = <&dma 19>, <&dma 19>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + i2c0: i2c@2502000 { + compatible = "allwinner,sun20i-d1-i2c", + "allwinner,sun8i-v536-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x2502000 0x400>; + interrupts = <25 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_I2C0>; + resets = <&ccu RST_BUS_I2C0>; + dmas = <&dma 43>, <&dma 43>; + dma-names = "rx", "tx"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c1: i2c@2502400 { + compatible = "allwinner,sun20i-d1-i2c", + "allwinner,sun8i-v536-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x2502400 0x400>; + interrupts = <26 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_I2C1>; + resets = <&ccu RST_BUS_I2C1>; + dmas = <&dma 44>, <&dma 44>; + dma-names = "rx", "tx"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c2: i2c@2502800 { + compatible = "allwinner,sun20i-d1-i2c", + "allwinner,sun8i-v536-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x2502800 0x400>; + interrupts = <27 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_I2C2>; + resets = <&ccu RST_BUS_I2C2>; + dmas = <&dma 45>, <&dma 45>; + dma-names = "rx", "tx"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + i2c3: i2c@2502c00 { + compatible = "allwinner,sun20i-d1-i2c", + "allwinner,sun8i-v536-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x2502c00 0x400>; + interrupts = <28 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_I2C3>; + resets = <&ccu RST_BUS_I2C3>; + dmas = <&dma 46>, <&dma 46>; + dma-names = "rx", "tx"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + syscon: syscon@3000000 { + compatible = "allwinner,sun20i-d1-system-control"; + reg = <0x3000000 0x1000>; + ranges; + #address-cells = <1>; + #size-cells = <1>; + + regulators@3000150 { + compatible = "allwinner,sun20i-d1-system-ldos"; + reg = <0x3000150 0x4>; + + reg_ldoa: ldoa { + }; + + reg_ldob: ldob { + }; + }; + }; + + dma: dma-controller@3002000 { + compatible = "allwinner,sun20i-d1-dma"; + reg = <0x3002000 0x1000>; + interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_DMA>, <&ccu CLK_MBUS_DMA>; + clock-names = "bus", "mbus"; + resets = <&ccu RST_BUS_DMA>; + dma-channels = <16>; + dma-requests = <48>; + #dma-cells = <1>; + }; + + sid: efuse@3006000 { + compatible = "allwinner,sun20i-d1-sid"; + reg = <0x3006000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + ths_calib: ths-calib@14 { + reg = <0x14 0x4>; + }; + + bg_trim: bg-trim@28 { + reg = <0x28 0x4>; + bits = <16 8>; + }; + }; + + mbus: dram-controller@3102000 { + compatible = "allwinner,sun20i-d1-mbus"; + reg = <0x3102000 0x1000>, + <0x3103000 0x1000>; + reg-names = "mbus", "dram"; + interrupts = <59 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_MBUS>, + <&ccu CLK_DRAM>, + <&ccu CLK_BUS_DRAM>; + clock-names = "mbus", "dram", "bus"; + dma-ranges = <0 0x40000000 0x80000000>; + #address-cells = <1>; + #size-cells = <1>; + #interconnect-cells = <1>; + }; + + mmc0: mmc@4020000 { + compatible = "allwinner,sun20i-d1-mmc"; + reg = <0x4020000 0x1000>; + interrupts = <56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC0>; + reset-names = "ahb"; + cap-sd-highspeed; + max-frequency = <150000000>; + no-mmc; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + mmc1: mmc@4021000 { + compatible = "allwinner,sun20i-d1-mmc"; + reg = <0x4021000 0x1000>; + interrupts = <57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC1>; + reset-names = "ahb"; + cap-sd-highspeed; + max-frequency = <150000000>; + no-mmc; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + mmc2: mmc@4022000 { + compatible = "allwinner,sun20i-d1-emmc", + "allwinner,sun50i-a100-emmc"; + reg = <0x4022000 0x1000>; + interrupts = <58 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC2>; + reset-names = "ahb"; + cap-mmc-highspeed; + max-frequency = <150000000>; + mmc-ddr-1_8v; + mmc-ddr-3_3v; + no-sd; + no-sdio; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + usb_otg: usb@4100000 { + compatible = "allwinner,sun20i-d1-musb", + "allwinner,sun8i-a33-musb"; + reg = <0x4100000 0x400>; + interrupts = <45 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "mc"; + clocks = <&ccu CLK_BUS_OTG>; + resets = <&ccu RST_BUS_OTG>; + extcon = <&usbphy 0>; + phys = <&usbphy 0>; + phy-names = "usb"; + status = "disabled"; + }; + + usbphy: phy@4100400 { + compatible = "allwinner,sun20i-d1-usb-phy"; + reg = <0x4100400 0x100>, + <0x4101800 0x100>, + <0x4200800 0x100>; + reg-names = "phy_ctrl", + "pmu0", + "pmu1"; + clocks = <&osc24M>, + <&osc24M>; + clock-names = "usb0_phy", + "usb1_phy"; + resets = <&ccu RST_USB_PHY0>, + <&ccu RST_USB_PHY1>; + reset-names = "usb0_reset", + "usb1_reset"; + status = "disabled"; + #phy-cells = <1>; + }; + + ehci0: usb@4101000 { + compatible = "allwinner,sun20i-d1-ehci", + "generic-ehci"; + reg = <0x4101000 0x100>; + interrupts = <46 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_OHCI0>, + <&ccu CLK_BUS_EHCI0>, + <&ccu CLK_USB_OHCI0>; + resets = <&ccu RST_BUS_OHCI0>, + <&ccu RST_BUS_EHCI0>; + phys = <&usbphy 0>; + phy-names = "usb"; + status = "disabled"; + }; + + ohci0: usb@4101400 { + compatible = "allwinner,sun20i-d1-ohci", + "generic-ohci"; + reg = <0x4101400 0x100>; + interrupts = <47 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_OHCI0>, + <&ccu CLK_USB_OHCI0>; + resets = <&ccu RST_BUS_OHCI0>; + phys = <&usbphy 0>; + phy-names = "usb"; + status = "disabled"; + }; + + ehci1: usb@4200000 { + compatible = "allwinner,sun20i-d1-ehci", + "generic-ehci"; + reg = <0x4200000 0x100>; + interrupts = <49 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_OHCI1>, + <&ccu CLK_BUS_EHCI1>, + <&ccu CLK_USB_OHCI1>; + resets = <&ccu RST_BUS_OHCI1>, + <&ccu RST_BUS_EHCI1>; + phys = <&usbphy 1>; + phy-names = "usb"; + status = "disabled"; + }; + + ohci1: usb@4200400 { + compatible = "allwinner,sun20i-d1-ohci", + "generic-ohci"; + reg = <0x4200400 0x100>; + interrupts = <50 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_OHCI1>, + <&ccu CLK_USB_OHCI1>; + resets = <&ccu RST_BUS_OHCI1>; + phys = <&usbphy 1>; + phy-names = "usb"; + status = "disabled"; + }; + + emac: ethernet@4500000 { + compatible = "allwinner,sun20i-d1-emac", + "allwinner,sun50i-a64-emac"; + reg = <0x4500000 0x10000>; + interrupts = <62 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + clocks = <&ccu CLK_BUS_EMAC>; + clock-names = "stmmaceth"; + resets = <&ccu RST_BUS_EMAC>; + reset-names = "stmmaceth"; + syscon = <&syscon>; + status = "disabled"; + + mdio: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + display_clocks: clock-controller@5000000 { + compatible = "allwinner,sun20i-d1-de2-clk", + "allwinner,sun50i-h5-de2-clk"; + reg = <0x5000000 0x10000>; + clocks = <&ccu CLK_BUS_DE>, + <&ccu CLK_DE>; + clock-names = "bus", + "mod"; + resets = <&ccu RST_BUS_DE>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + mixer0: mixer@5100000 { + compatible = "allwinner,sun20i-d1-de2-mixer-0"; + reg = <0x5100000 0x100000>; + clocks = <&display_clocks CLK_BUS_MIXER0>, + <&display_clocks CLK_MIXER0>; + clock-names = "bus", + "mod"; + resets = <&display_clocks RST_MIXER0>; + iommus = <&iommu 2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + mixer0_out: port@1 { + reg = <1>; + + mixer0_out_tcon_top_mixer0: endpoint { + remote-endpoint = <&tcon_top_mixer0_in_mixer0>; + }; + }; + }; + }; + + mixer1: mixer@5200000 { + compatible = "allwinner,sun20i-d1-de2-mixer-1"; + reg = <0x5200000 0x100000>; + clocks = <&display_clocks CLK_BUS_MIXER1>, + <&display_clocks CLK_MIXER1>; + clock-names = "bus", + "mod"; + resets = <&display_clocks RST_MIXER1>; + iommus = <&iommu 2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + mixer1_out: port@1 { + reg = <1>; + + mixer1_out_tcon_top_mixer1: endpoint { + remote-endpoint = <&tcon_top_mixer1_in_mixer1>; + }; + }; + }; + }; + + tcon_top: tcon-top@5460000 { + compatible = "allwinner,sun20i-d1-tcon-top"; + reg = <0x5460000 0x1000>; + clocks = <&ccu CLK_BUS_DPSS_TOP>, + <&ccu CLK_TCON_TV>, + <&ccu CLK_TVE>, + <&ccu CLK_MIPI_DSI>; + clock-names = "bus", + "tcon-tv0", + "tve0", + "dsi"; + clock-output-names = "tcon-top-tv0", + "tcon-top-dsi"; + resets = <&ccu RST_BUS_DPSS_TOP>; + #clock-cells = <1>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + tcon_top_mixer0_in: port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + tcon_top_mixer0_in_mixer0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mixer0_out_tcon_top_mixer0>; + }; + }; + + tcon_top_mixer0_out: port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + tcon_top_mixer0_out_tcon_lcd0: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon_lcd0_in_tcon_top_mixer0>; + }; + + tcon_top_mixer0_out_tcon_tv0: endpoint@2 { + reg = <2>; + remote-endpoint = <&tcon_tv0_in_tcon_top_mixer0>; + }; + }; + + tcon_top_mixer1_in: port@2 { + reg = <2>; + #address-cells = <1>; + #size-cells = <0>; + + tcon_top_mixer1_in_mixer1: endpoint@1 { + reg = <1>; + remote-endpoint = <&mixer1_out_tcon_top_mixer1>; + }; + }; + + tcon_top_mixer1_out: port@3 { + reg = <3>; + #address-cells = <1>; + #size-cells = <0>; + + tcon_top_mixer1_out_tcon_lcd0: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon_lcd0_in_tcon_top_mixer1>; + }; + + tcon_top_mixer1_out_tcon_tv0: endpoint@2 { + reg = <2>; + remote-endpoint = <&tcon_tv0_in_tcon_top_mixer1>; + }; + }; + + tcon_top_hdmi_in: port@4 { + reg = <4>; + + tcon_top_hdmi_in_tcon_tv0: endpoint { + remote-endpoint = <&tcon_tv0_out_tcon_top_hdmi>; + }; + }; + + tcon_top_hdmi_out: port@5 { + reg = <5>; + }; + }; + }; + + tcon_lcd0: lcd-controller@5461000 { + compatible = "allwinner,sun20i-d1-tcon-lcd"; + reg = <0x5461000 0x1000>; + interrupts = <106 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_TCON_LCD0>, + <&ccu CLK_TCON_LCD0>; + clock-names = "ahb", "tcon-ch0"; + clock-output-names = "tcon-pixel-clock"; + resets = <&ccu RST_BUS_TCON_LCD0>, + <&ccu RST_BUS_LVDS0>; + reset-names = "lcd", "lvds"; + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + tcon_lcd0_in: port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + tcon_lcd0_in_tcon_top_mixer0: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon_top_mixer0_out_tcon_lcd0>; + }; + + tcon_lcd0_in_tcon_top_mixer1: endpoint@1 { + reg = <1>; + remote-endpoint = <&tcon_top_mixer1_out_tcon_lcd0>; + }; + }; + + tcon_lcd0_out: port@1 { + reg = <1>; + }; + }; + }; + + tcon_tv0: lcd-controller@5470000 { + compatible = "allwinner,sun20i-d1-tcon-tv"; + reg = <0x5470000 0x1000>; + interrupts = <107 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_TCON_TV>, + <&tcon_top CLK_TCON_TOP_TV0>; + clock-names = "ahb", "tcon-ch1"; + resets = <&ccu RST_BUS_TCON_TV>; + reset-names = "lcd"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + tcon_tv0_in: port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + tcon_tv0_in_tcon_top_mixer0: endpoint@0 { + reg = <0>; + remote-endpoint = <&tcon_top_mixer0_out_tcon_tv0>; + }; + + tcon_tv0_in_tcon_top_mixer1: endpoint@1 { + reg = <1>; + remote-endpoint = <&tcon_top_mixer1_out_tcon_tv0>; + }; + }; + + tcon_tv0_out: port@1 { + reg = <1>; + + tcon_tv0_out_tcon_top_hdmi: endpoint { + remote-endpoint = <&tcon_top_hdmi_in_tcon_tv0>; + }; + }; + }; + }; + + riscv_wdt: watchdog@6011000 { + compatible = "allwinner,sun20i-d1-wdt"; + reg = <0x6011000 0x20>; + interrupts = <147 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&osc24M>, <&rtc CLK_OSC32K>; + clock-names = "hosc", "losc"; + }; + + r_ccu: clock-controller@7010000 { + compatible = "allwinner,sun20i-d1-r-ccu"; + reg = <0x7010000 0x400>; + clocks = <&osc24M>, + <&rtc CLK_OSC32K>, + <&rtc CLK_IOSC>, + <&ccu CLK_PLL_PERIPH0_DIV3>; + clock-names = "hosc", "losc", "iosc", "pll-periph"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + rtc: rtc@7090000 { + compatible = "allwinner,sun20i-d1-rtc", + "allwinner,sun50i-r329-rtc"; + reg = <0x7090000 0x400>; + interrupts = <160 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&r_ccu CLK_BUS_R_RTC>, + <&osc24M>, + <&r_ccu CLK_R_AHB>; + clock-names = "bus", "hosc", "ahb"; + #clock-cells = <1>; + }; + + plic: interrupt-controller@10000000 { + compatible = "allwinner,sun20i-d1-plic", + "thead,c900-plic"; + reg = <0x10000000 0x4000000>; + interrupts-extended = <&cpu0_intc 11>, + <&cpu0_intc 9>; + interrupt-controller; + riscv,ndev = <176>; + #address-cells = <0>; + #interrupt-cells = <2>; + }; + }; +}; From c7a4bd105e8c1542ddc460e8c4e7b3ccdebee03f Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 28 Jun 2022 23:31:16 -0500 Subject: [PATCH 081/153] riscv: dts: allwinner: Add Allwinner D1 Nezha devicetree "D1 Nezha" is Allwinner's first-party development board for the D1 SoC. It was shipped with 512M, 1G, or 2G of DDR3. It supports onboard audio, HDMI, gigabit Ethernet, WiFi and Bluetooth, USB 2.0 host and OTG ports, plus low-speed I/O from the SoC and a GPIO expander chip. Most other D1 boards copied the Nezha's power tree, with the 1.8V rail powered by the SoCs internal LDOA, analog domains powered by ALDO, and the rest of the board powered by always-on fixed regulators. Some (but not all) boards also copied the PWM CPU regulator. To avoid duplication, factor out the out the regulator references that are common across all known boards. Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/Makefile | 1 + .../sun20i-d1-common-regulators.dtsi | 55 ++++++ .../boot/dts/allwinner/sun20i-d1-nezha.dts | 171 ++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile index f66554cd5c4518..b0a15e8c8d825b 100644 --- a/arch/riscv/boot/dts/allwinner/Makefile +++ b/arch/riscv/boot/dts/allwinner/Makefile @@ -1 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-nezha.dtb diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi new file mode 100644 index 00000000000000..11bdc81e38d5b2 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2021-2022 Samuel Holland + +/ { + reg_vcc: vcc { + compatible = "regulator-fixed"; + regulator-name = "vcc"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_vcc_3v3: vcc-3v3 { + compatible = "regulator-fixed"; + regulator-name = "vcc-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <®_vcc>; + }; +}; + +&lradc { + vref-supply = <®_aldo>; +}; + +&pio { + vcc-pb-supply = <®_vcc_3v3>; + vcc-pc-supply = <®_vcc_3v3>; + vcc-pd-supply = <®_vcc_3v3>; + vcc-pe-supply = <®_vcc_3v3>; + vcc-pf-supply = <®_vcc_3v3>; + vcc-pg-supply = <®_vcc_3v3>; +}; + +®_aldo { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vdd33-supply = <®_vcc_3v3>; +}; + +®_hpldo { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + hpldoin-supply = <®_vcc_3v3>; +}; + +®_ldoa { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + ldo-in-supply = <®_vcc_3v3>; +}; + +&ths { + vref-supply = <®_aldo>; +}; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts new file mode 100644 index 00000000000000..df865ee15fcfa8 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2021-2022 Samuel Holland + +/dts-v1/; + +#include +#include + +#include "sun20i-d1.dtsi" +#include "sun20i-d1-common-regulators.dtsi" + +/ { + model = "Allwinner D1 Nezha"; + compatible = "allwinner,d1-nezha", "allwinner,sun20i-d1"; + + aliases { + ethernet0 = &emac; + ethernet1 = &xr829; + mmc0 = &mmc0; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + reg_usbvbus: usbvbus { + compatible = "regulator-fixed"; + regulator-name = "usbvbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&pio 3 19 GPIO_ACTIVE_HIGH>; /* PD19 */ + enable-active-high; + vin-supply = <®_vcc>; + }; + + /* + * This regulator is PWM-controlled, but the PWM controller is not + * yet supported, so fix the regulator to its default voltage. + */ + reg_vdd_cpu: vdd-cpu { + compatible = "regulator-fixed"; + regulator-name = "vdd-cpu"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + vin-supply = <®_vcc>; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pio 6 12 GPIO_ACTIVE_LOW>; /* PG12 */ + }; +}; + +&cpu0 { + cpu-supply = <®_vdd_cpu>; +}; + +&ehci0 { + status = "okay"; +}; + +&ehci1 { + status = "okay"; +}; + +&emac { + pinctrl-0 = <&rgmii_pe_pins>; + pinctrl-names = "default"; + phy-handle = <&ext_rgmii_phy>; + phy-mode = "rgmii-id"; + phy-supply = <®_vcc_3v3>; + status = "okay"; +}; + +&i2c2 { + pinctrl-0 = <&i2c2_pb0_pins>; + pinctrl-names = "default"; + status = "okay"; + + pcf8574a: gpio@38 { + compatible = "nxp,pcf8574a"; + reg = <0x38>; + interrupt-parent = <&pio>; + interrupts = <1 2 IRQ_TYPE_LEVEL_LOW>; /* PB2 */ + interrupt-controller; + gpio-controller; + #gpio-cells = <2>; + #interrupt-cells = <2>; + }; +}; + +&lradc { + status = "okay"; + + button-160 { + label = "OK"; + linux,code = ; + channel = <0>; + voltage = <160000>; + }; +}; + +&mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; +}; + +&mmc0 { + bus-width = <4>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ + disable-wp; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&mmc1 { + bus-width = <4>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc1_pins>; + pinctrl-names = "default"; + status = "okay"; + + xr829: wifi@1 { + reg = <1>; + }; +}; + +&ohci0 { + status = "okay"; +}; + +&ohci1 { + status = "okay"; +}; + +&uart0 { + pinctrl-0 = <&uart0_pb8_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&uart1 { + uart-has-rtscts; + pinctrl-0 = <&uart1_pg6_pins>, <&uart1_pg8_rts_cts_pins>; + pinctrl-names = "default"; + status = "okay"; + + /* XR829 bluetooth is connected here */ +}; + +&usb_otg { + dr_mode = "otg"; + status = "okay"; +}; + +&usbphy { + usb0_id_det-gpios = <&pio 3 21 GPIO_ACTIVE_HIGH>; /* PD21 */ + usb0_vbus_det-gpios = <&pio 3 20 GPIO_ACTIVE_HIGH>; /* PD20 */ + usb0_vbus-supply = <®_usbvbus>; + usb1_vbus-supply = <®_vcc>; + status = "okay"; +}; From d69bfa18104328825a5a4556e40684db1c5f262a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 29 Jun 2022 00:13:50 -0500 Subject: [PATCH 082/153] riscv: dts: allwinner: Add Sipeed Lichee RV devicetrees Sipeed manufactures a "Lichee RV" system-on-module, which provides a minimal working system on its own, as well as a few carrier boards. The "Dock" board provides audio, USB, and WiFi. The "86 Panel" additionally provides 100M Ethernet and a built-in display panel. The 86 Panel repurposes the USB ID and VBUS detection GPIOs for its RGB panel interface, since the USB OTG port is inacessible inside the case. Co-developed-by: Jisheng Zhang Signed-off-by: Jisheng Zhang Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/Makefile | 4 + .../sun20i-d1-lichee-rv-86-panel-480p.dts | 29 ++++++ .../sun20i-d1-lichee-rv-86-panel-720p.dts | 10 +++ .../sun20i-d1-lichee-rv-86-panel.dtsi | 88 +++++++++++++++++++ .../allwinner/sun20i-d1-lichee-rv-dock.dts | 74 ++++++++++++++++ .../dts/allwinner/sun20i-d1-lichee-rv.dts | 83 +++++++++++++++++ 6 files changed, 288 insertions(+) create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-480p.dts create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-720p.dts create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel.dtsi create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv.dts diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile index b0a15e8c8d825b..300ada20c73558 100644 --- a/arch/riscv/boot/dts/allwinner/Makefile +++ b/arch/riscv/boot/dts/allwinner/Makefile @@ -1,2 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-86-panel-480p.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-86-panel-720p.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-dock.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-nezha.dtb diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-480p.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-480p.dts new file mode 100644 index 00000000000000..4df8ffb715618b --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-480p.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Samuel Holland + +#include "sun20i-d1-lichee-rv-86-panel.dtsi" + +/ { + model = "Sipeed Lichee RV 86 Panel (480p)"; + compatible = "sipeed,lichee-rv-86-panel-480p", "sipeed,lichee-rv", + "allwinner,sun20i-d1"; +}; + +&i2c2 { + pinctrl-0 = <&i2c2_pb0_pins>; + pinctrl-names = "default"; + status = "okay"; + + touchscreen@48 { + compatible = "focaltech,ft6236"; + reg = <0x48>; + interrupt-parent = <&pio>; + interrupts = <6 14 IRQ_TYPE_LEVEL_LOW>; /* PG14 */ + iovcc-supply = <®_vcc_3v3>; + reset-gpios = <&pio 6 15 GPIO_ACTIVE_LOW>; /* PG15 */ + touchscreen-size-x = <480>; + touchscreen-size-y = <480>; + vcc-supply = <®_vcc_3v3>; + wakeup-source; + }; +}; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-720p.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-720p.dts new file mode 100644 index 00000000000000..1874fc05359f44 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-720p.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Samuel Holland + +#include "sun20i-d1-lichee-rv-86-panel.dtsi" + +/ { + model = "Sipeed Lichee RV 86 Panel (720p)"; + compatible = "sipeed,lichee-rv-86-panel-720p", "sipeed,lichee-rv", + "allwinner,sun20i-d1"; +}; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel.dtsi new file mode 100644 index 00000000000000..ace2db106a64ee --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel.dtsi @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Samuel Holland + +#include "sun20i-d1-lichee-rv.dts" + +/ { + aliases { + ethernet0 = &emac; + ethernet1 = &xr829; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&ccu CLK_FANOUT1>; + clock-names = "ext_clock"; + reset-gpios = <&pio 6 12 GPIO_ACTIVE_LOW>; /* PG12 */ + assigned-clocks = <&ccu CLK_FANOUT1>; + assigned-clock-rates = <32768>; + pinctrl-0 = <&clk_pg11_pin>; + pinctrl-names = "default"; + }; +}; + +&ehci1 { + status = "okay"; +}; + +&emac { + pinctrl-0 = <&rmii_pe_pins>; + pinctrl-names = "default"; + phy-handle = <&ext_rmii_phy>; + phy-mode = "rmii"; + phy-supply = <®_vcc_3v3>; + status = "okay"; +}; + +&mdio { + ext_rmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + reset-gpios = <&pio 4 16 GPIO_ACTIVE_LOW>; /* PE16 */ + }; +}; + +&mmc1 { + bus-width = <4>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc1_pins>; + pinctrl-names = "default"; + status = "okay"; + + xr829: wifi@1 { + reg = <1>; + }; +}; + +&ohci1 { + status = "okay"; +}; + +&pio { + clk_pg11_pin: clk-pg11-pin { + pins = "PG11"; + function = "clk"; + }; +}; + +&uart1 { + uart-has-rtscts; + pinctrl-0 = <&uart1_pg6_pins>, <&uart1_pg8_rts_cts_pins>; + pinctrl-names = "default"; + status = "okay"; + + /* XR829 bluetooth is connected here */ +}; + +&usb_otg { + status = "disabled"; +}; + +&usbphy { + /delete-property/ usb0_id_det-gpios; + /delete-property/ usb0_vbus_det-gpios; + usb1_vbus-supply = <®_vcc>; +}; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts new file mode 100644 index 00000000000000..ca36a5d75a7f24 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Jisheng Zhang +// Copyright (C) 2022 Samuel Holland + +#include + +#include "sun20i-d1-lichee-rv.dts" + +/ { + model = "Sipeed Lichee RV Dock"; + compatible = "sipeed,lichee-rv-dock", "sipeed,lichee-rv", + "allwinner,sun20i-d1"; + + aliases { + ethernet1 = &rtl8723ds; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pio 6 12 GPIO_ACTIVE_LOW>; /* PG12 */ + }; +}; + +&ehci1 { + status = "okay"; +}; + +&lradc { + status = "okay"; + + button-220 { + label = "OK"; + linux,code = ; + channel = <0>; + voltage = <220000>; + }; +}; + +&mmc1 { + bus-width = <4>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc1_pins>; + pinctrl-names = "default"; + status = "okay"; + + rtl8723ds: wifi@1 { + reg = <1>; + }; +}; + +&ohci1 { + status = "okay"; +}; + +&uart1 { + uart-has-rtscts; + pinctrl-0 = <&uart1_pg6_pins>, <&uart1_pg8_rts_cts_pins>; + pinctrl-names = "default"; + status = "okay"; + + bluetooth { + compatible = "realtek,rtl8723ds-bt"; + device-wake-gpios = <&pio 6 15 GPIO_ACTIVE_HIGH>; /* PG16 */ + enable-gpios = <&pio 6 18 GPIO_ACTIVE_HIGH>; /* PG18 */ + host-wake-gpios = <&pio 6 17 GPIO_ACTIVE_HIGH>; /* PG17 */ + }; +}; + +&usbphy { + usb1_vbus-supply = <®_vcc>; +}; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv.dts new file mode 100644 index 00000000000000..2cc0b63a273de4 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv.dts @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Jisheng Zhang +// Copyright (C) 2022 Samuel Holland + +/dts-v1/; + +#include +#include + +#include "sun20i-d1.dtsi" +#include "sun20i-d1-common-regulators.dtsi" + +/ { + model = "Sipeed Lichee RV"; + compatible = "sipeed,lichee-rv", "allwinner,sun20i-d1"; + + aliases { + mmc0 = &mmc0; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + function = LED_FUNCTION_STATUS; + gpios = <&pio 2 1 GPIO_ACTIVE_HIGH>; /* PC1 */ + }; + }; + + reg_vdd_cpu: vdd-cpu { + compatible = "regulator-fixed"; + regulator-name = "vdd-cpu"; + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <900000>; + vin-supply = <®_vcc>; + }; +}; + +&cpu0 { + cpu-supply = <®_vdd_cpu>; +}; + +&ehci0 { + status = "okay"; +}; + +&mmc0 { + broken-cd; + bus-width = <4>; + disable-wp; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&ohci0 { + status = "okay"; +}; + +&uart0 { + pinctrl-0 = <&uart0_pb8_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&usb_otg { + dr_mode = "otg"; + status = "okay"; +}; + +&usbphy { + usb0_id_det-gpios = <&pio 3 21 GPIO_ACTIVE_HIGH>; /* PD21 */ + usb0_vbus_det-gpios = <&pio 3 20 GPIO_ACTIVE_HIGH>; /* PD20 */ + usb0_vbus-supply = <®_vcc>; + status = "okay"; +}; From 2194c0677227f21c0174555c588df959337203d3 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 9 Jul 2022 17:43:17 -0500 Subject: [PATCH 083/153] riscv: dts: allwinner: Add MangoPi MQ Pro devicetree The MangoPi MQ Pro is a tiny SBC with a layout compatible to the Raspberry Pi Zero. It includes the Allwinner D1 SoC, 512M or 1G of DDR3, and an RTL8723DS-based WiFi/Bluetooth module. The board also exposes GPIO Port E via a connector on the end of the board, which can support either a camera or an RMII Ethernet PHY. The additional regulators supply that connector. Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/Makefile | 1 + .../allwinner/sun20i-d1-mangopi-mq-pro.dts | 128 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile index 300ada20c73558..bcc3041757532d 100644 --- a/arch/riscv/boot/dts/allwinner/Makefile +++ b/arch/riscv/boot/dts/allwinner/Makefile @@ -3,4 +3,5 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-86-panel-480p.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-86-panel-720p.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-dock.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-mangopi-mq-pro.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-nezha.dtb diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts new file mode 100644 index 00000000000000..61a26d3db521ff --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Samuel Holland + +/dts-v1/; + +#include + +#include "sun20i-d1.dtsi" +#include "sun20i-d1-common-regulators.dtsi" + +/ { + model = "MangoPi MQ Pro"; + compatible = "widora,mangopi-mq-pro", "allwinner,sun20i-d1"; + + aliases { + ethernet0 = &rtl8723ds; + mmc0 = &mmc0; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + reg_avdd2v8: avdd2v8 { + compatible = "regulator-fixed"; + regulator-name = "avdd2v8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + vin-supply = <®_vcc_3v3>; + }; + + reg_dvdd: dvdd { + compatible = "regulator-fixed"; + regulator-name = "dvdd"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + vin-supply = <®_vcc_3v3>; + }; + + reg_vdd_cpu: vdd-cpu { + compatible = "regulator-fixed"; + regulator-name = "vdd-cpu"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + vin-supply = <®_vcc>; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pio 6 17 GPIO_ACTIVE_LOW>; /* PG17 */ + }; +}; + +&cpu0 { + cpu-supply = <®_vdd_cpu>; +}; + +&ehci1 { + status = "okay"; +}; + +&mmc0 { + bus-width = <4>; + cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ + disable-wp; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&mmc1 { + bus-width = <4>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc1_pins>; + pinctrl-names = "default"; + status = "okay"; + + rtl8723ds: wifi@1 { + reg = <1>; + interrupt-parent = <&pio>; + interrupts = <6 10 IRQ_TYPE_LEVEL_LOW>; /* PG10 */ + interrupt-names = "host-wake"; + }; +}; + +&ohci1 { + status = "okay"; +}; + +&pio { + vcc-pe-supply = <®_avdd2v8>; +}; + +&uart0 { + pinctrl-0 = <&uart0_pb8_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&uart1 { + uart-has-rtscts; + pinctrl-0 = <&uart1_pg6_pins>, <&uart1_pg8_rts_cts_pins>; + pinctrl-names = "default"; + status = "okay"; + + bluetooth { + compatible = "realtek,rtl8723ds-bt"; + device-wake-gpios = <&pio 6 18 GPIO_ACTIVE_HIGH>; /* PG18 */ + enable-gpios = <&pio 6 15 GPIO_ACTIVE_HIGH>; /* PG15 */ + host-wake-gpios = <&pio 6 14 GPIO_ACTIVE_HIGH>; /* PG14 */ + }; +}; + +&usb_otg { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usbphy { + usb0_vbus-supply = <®_vcc>; + status = "okay"; +}; From cb8b412f0c649d822c509157054c7224492693fd Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 10 Jul 2022 11:24:42 -0500 Subject: [PATCH 084/153] riscv: dts: allwinner: Add Dongshan Nezha STU devicetree The 100ask Dongshan Nezha STU is a system-on-module that can be used standalone or with a carrier board. The SoM provides gigabit Ethernet, HDMI, a USB peripheral port, and WiFi/Bluetooth via an RTL8723DS chip. The "DIY" carrier board exposes almost every pin from the D1 SoC to 0.1" headers, but contains no digital circuitry, so it does not have its own devicetree. Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/Makefile | 1 + .../sun20i-d1-dongshan-nezha-stu.dts | 114 ++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile index bcc3041757532d..530ef8adb8b0ad 100644 --- a/arch/riscv/boot/dts/allwinner/Makefile +++ b/arch/riscv/boot/dts/allwinner/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-dongshan-nezha-stu.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-86-panel-480p.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-86-panel-720p.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-dock.dtb diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts new file mode 100644 index 00000000000000..c3d06dfaa7c3c6 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Samuel Holland + +/dts-v1/; + +#include +#include + +#include "sun20i-d1.dtsi" +#include "sun20i-d1-common-regulators.dtsi" + +/ { + model = "Dongshan Nezha STU"; + compatible = "100ask,dongshan-nezha-stu", "allwinner,sun20i-d1"; + + aliases { + ethernet0 = &emac; + mmc0 = &mmc0; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&pio 2 1 GPIO_ACTIVE_HIGH>; /* PC1 */ + }; + }; + + reg_usbvbus: usbvbus { + compatible = "regulator-fixed"; + regulator-name = "usbvbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&pio 3 19 GPIO_ACTIVE_HIGH>; /* PD19 */ + enable-active-high; + vin-supply = <®_vcc>; + }; + + /* + * This regulator is PWM-controlled, but the PWM controller is not + * yet supported, so fix the regulator to its default voltage. + */ + reg_vdd_cpu: vdd-cpu { + compatible = "regulator-fixed"; + regulator-name = "vdd-cpu"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + vin-supply = <®_vcc>; + }; +}; + +&cpu0 { + cpu-supply = <®_vdd_cpu>; +}; + +&ehci0 { + status = "okay"; +}; + +&emac { + pinctrl-0 = <&rgmii_pe_pins>; + pinctrl-names = "default"; + phy-handle = <&ext_rgmii_phy>; + phy-mode = "rgmii-id"; + phy-supply = <®_vcc_3v3>; + status = "okay"; +}; + +&mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; +}; + +&mmc0 { + broken-cd; + bus-width = <4>; + disable-wp; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&ohci0 { + status = "okay"; +}; + +&uart0 { + pinctrl-0 = <&uart0_pb8_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&usb_otg { + dr_mode = "otg"; + status = "okay"; +}; + +&usbphy { + usb0_id_det-gpios = <&pio 3 21 GPIO_ACTIVE_HIGH>; /* PD21 */ + usb0_vbus_det-gpios = <&pio 3 20 GPIO_ACTIVE_HIGH>; /* PD20 */ + usb0_vbus-supply = <®_usbvbus>; + status = "okay"; +}; From 3b04fedde118d7b3594b0f4e9e16c8b5e47baf09 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 17 Jul 2022 20:33:40 -0500 Subject: [PATCH 085/153] regulator: dt-bindings: Add Allwinner D1 LDOs The Allwinner D1 SoC contains two pairs of in-package LDOs. One pair is for general purpose use. LDOA generally powers the board's 1.8 V rail. LDOB generally powers the in-package DRAM, where applicable. The other pair of LDOs powers the analog power domains inside the SoC, including the audio codec, thermal sensor, and ADCs. These LDOs require a 0.9 V bandgap voltage reference. The calibration value for the voltage reference is stored in an eFuse, accessed via an NVMEM cell. Neither LDO control register is in its own MMIO range; instead, each regulator device relies on a regmap/syscon exported by its parent. Series-changes: 2 - Remove syscon property from bindings - Update binding examples to fix warnings and provide context Series-changes: 3 - Add "reg" property Signed-off-by: Samuel Holland --- .../allwinner,sun20i-d1-analog-ldos.yaml | 70 +++++++++++++++++++ .../allwinner,sun20i-d1-system-ldos.yaml | 36 ++++++++++ 2 files changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/allwinner,sun20i-d1-analog-ldos.yaml create mode 100644 Documentation/devicetree/bindings/regulator/allwinner,sun20i-d1-system-ldos.yaml diff --git a/Documentation/devicetree/bindings/regulator/allwinner,sun20i-d1-analog-ldos.yaml b/Documentation/devicetree/bindings/regulator/allwinner,sun20i-d1-analog-ldos.yaml new file mode 100644 index 00000000000000..1e24a9de5019cc --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/allwinner,sun20i-d1-analog-ldos.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/allwinner,sun20i-d1-analog-ldos.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner D1 Analog LDOs + +description: + Allwinner D1 contains a set of LDOs which are designed to supply analog power + inside and outside the SoC. They are controlled by a register within the audio + codec MMIO space, but which is not part of the audio codec clock/reset domain. + +maintainers: + - Samuel Holland + +properties: + compatible: + enum: + - allwinner,sun20i-d1-analog-ldos + + reg: + maxItems: 1 + + nvmem-cells: + items: + - description: NVMEM cell for the calibrated bandgap reference trim value + + nvmem-cell-names: + items: + - const: bg_trim + +patternProperties: + "^(aldo|hpldo)$": + type: object + $ref: regulator.yaml# + +required: + - compatible + - reg + - nvmem-cells + - nvmem-cell-names + +unevaluatedProperties: false + +examples: + - | + audio-codec@2030000 { + compatible = "simple-mfd", "syscon"; + reg = <0x2030000 0x1000>; + + regulators@2030348 { + compatible = "allwinner,sun20i-d1-analog-ldos"; + reg = <0x2030348 0x4>; + nvmem-cells = <&bg_trim>; + nvmem-cell-names = "bg_trim"; + + reg_aldo: aldo { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_hpldo: hpldo { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/regulator/allwinner,sun20i-d1-system-ldos.yaml b/Documentation/devicetree/bindings/regulator/allwinner,sun20i-d1-system-ldos.yaml new file mode 100644 index 00000000000000..c862f794d111a5 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/allwinner,sun20i-d1-system-ldos.yaml @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/allwinner,sun20i-d1-system-ldos.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner D1 System LDOs + +description: + Allwinner D1 contains a pair of general-purpose LDOs which are designed to + supply power inside and outside the SoC. They are controlled by a register + within the system control MMIO space. + +maintainers: + - Samuel Holland + +properties: + compatible: + enum: + - allwinner,sun20i-d1-system-ldos + + reg: + maxItems: 1 + +patternProperties: + "^(ldoa|ldob)$": + type: object + $ref: regulator.yaml# + +required: + - compatible + - reg + +unevaluatedProperties: false + +... From 23fbc71b3ab2f321fb04033e1c8051a984afc251 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 17 Jul 2022 11:46:52 -0500 Subject: [PATCH 086/153] regulator: sun20i: Add support for Allwinner D1 LDOs D1 contains two pairs of LDOs. Since they have similar bindings, and they always exist together, put them in a single driver. The analog LDOs are relatively boring, with a single linear range. Their one quirk is that a bandgap reference must be calibrated for them to produce the correct voltage. The system LDOs have the complication that their voltage step is not an integer, so a custom .list_voltage is needed to get the rounding right. Series-changes: 2 - Use decimal numbers for .n_voltages instead of field widths - Get the regmap from the parent device instead of a property/phandle Signed-off-by: Samuel Holland --- drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/sun20i-regulator.c | 232 +++++++++++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 drivers/regulator/sun20i-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index cbe0f96ca342bb..20a22f900bb299 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1234,6 +1234,14 @@ config REGULATOR_STW481X_VMMC This driver supports the internal VMMC regulator in the STw481x PMIC chips. +config REGULATOR_SUN20I + tristate "Allwinner D1 internal LDOs" + depends on ARCH_SUNXI || COMPILE_TEST + depends on MFD_SYSCON && NVMEM + default ARCH_SUNXI + help + This driver supports the internal LDOs in the Allwinner D1 SoC. + config REGULATOR_SY7636A tristate "Silergy SY7636A voltage regulator" help diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 8d3ee8b6d41d8f..cb3ac9290fc351 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -145,6 +145,7 @@ obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o +obj-$(CONFIG_REGULATOR_SUN20I) += sun20i-regulator.o obj-$(CONFIG_REGULATOR_SY7636A) += sy7636a-regulator.o obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o obj-$(CONFIG_REGULATOR_SY8824X) += sy8824x.o diff --git a/drivers/regulator/sun20i-regulator.c b/drivers/regulator/sun20i-regulator.c new file mode 100644 index 00000000000000..789a68829630cb --- /dev/null +++ b/drivers/regulator/sun20i-regulator.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (c) 2021-2022 Samuel Holland +// + +#include +#include +#include +#include +#include +#include +#include + +#define SUN20I_POWER_REG 0x348 + +#define SUN20I_SYS_LDO_CTRL_REG 0x150 + +struct sun20i_regulator_data { + int (*init)(struct device *dev, + struct regmap *regmap); + const struct regulator_desc *descs; + unsigned int ndescs; +}; + +static int sun20i_d1_analog_ldos_init(struct device *dev, struct regmap *regmap) +{ + u8 bg_trim; + int ret; + + ret = nvmem_cell_read_u8(dev, "bg_trim", &bg_trim); + if (ret) + return dev_err_probe(dev, ret, "Failed to get bg_trim value\n"); + + /* The default value corresponds to 900 mV. */ + if (!bg_trim) + bg_trim = 0x19; + + return regmap_update_bits(regmap, SUN20I_POWER_REG, + GENMASK(7, 0), bg_trim); +} + +static const struct regulator_ops sun20i_d1_analog_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_desc sun20i_d1_analog_ldo_descs[] = { + { + .name = "aldo", + .supply_name = "vdd33", + .of_match = "aldo", + .ops = &sun20i_d1_analog_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = 8, + .min_uV = 1650000, + .uV_step = 50000, + .vsel_reg = SUN20I_POWER_REG, + .vsel_mask = GENMASK(14, 12), + .enable_reg = SUN20I_POWER_REG, + .enable_mask = BIT(31), + }, + { + .name = "hpldo", + .supply_name = "hpldoin", + .of_match = "hpldo", + .ops = &sun20i_d1_analog_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = 8, + .min_uV = 1650000, + .uV_step = 50000, + .vsel_reg = SUN20I_POWER_REG, + .vsel_mask = GENMASK(10, 8), + .enable_reg = SUN20I_POWER_REG, + .enable_mask = BIT(30), + }, +}; + +static const struct sun20i_regulator_data sun20i_d1_analog_ldos = { + .init = sun20i_d1_analog_ldos_init, + .descs = sun20i_d1_analog_ldo_descs, + .ndescs = ARRAY_SIZE(sun20i_d1_analog_ldo_descs), +}; + +/* regulator_list_voltage_linear() modified for the non-integral uV_step. */ +static int sun20i_d1_system_ldo_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct regulator_desc *desc = rdev->desc; + unsigned int uV; + + if (selector >= desc->n_voltages) + return -EINVAL; + + uV = desc->min_uV + (desc->uV_step * selector); + + /* Produce correctly-rounded absolute voltages. */ + return uV + ((selector + 1 + (desc->min_uV % 4)) / 3); +} + +static const struct regulator_ops sun20i_d1_system_ldo_ops = { + .list_voltage = sun20i_d1_system_ldo_list_voltage, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_desc sun20i_d1_system_ldo_descs[] = { + { + .name = "ldoa", + .supply_name = "ldo-in", + .of_match = "ldoa", + .ops = &sun20i_d1_system_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = 32, + .min_uV = 1600000, + .uV_step = 13333, /* repeating */ + .vsel_reg = SUN20I_SYS_LDO_CTRL_REG, + .vsel_mask = GENMASK(7, 0), + }, + { + .name = "ldob", + .supply_name = "ldo-in", + .of_match = "ldob", + .ops = &sun20i_d1_system_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = 64, + .min_uV = 1166666, + .uV_step = 13333, /* repeating */ + .vsel_reg = SUN20I_SYS_LDO_CTRL_REG, + .vsel_mask = GENMASK(15, 8), + }, +}; + +static const struct sun20i_regulator_data sun20i_d1_system_ldos = { + .descs = sun20i_d1_system_ldo_descs, + .ndescs = ARRAY_SIZE(sun20i_d1_system_ldo_descs), +}; + +static const struct of_device_id sun20i_regulator_of_match[] = { + { + .compatible = "allwinner,sun20i-d1-analog-ldos", + .data = &sun20i_d1_analog_ldos, + }, + { + .compatible = "allwinner,sun20i-d1-system-ldos", + .data = &sun20i_d1_system_ldos, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sun20i_regulator_of_match); + +static struct regmap *sun20i_regulator_get_regmap(struct device *dev) +{ + struct regmap *regmap; + + /* + * First try the syscon interface. The system control device is not + * compatible with "syscon", so fall back to getting the regmap from + * its platform device. This is ugly, but required for devicetree + * backward compatibility. + */ + regmap = syscon_node_to_regmap(dev->parent->of_node); + if (!IS_ERR(regmap)) + return regmap; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return ERR_PTR(-EPROBE_DEFER); + + return regmap; +} + +static int sun20i_regulator_probe(struct platform_device *pdev) +{ + const struct sun20i_regulator_data *data; + struct device *dev = &pdev->dev; + struct regulator_config config; + struct regmap *regmap; + int ret; + + data = of_device_get_match_data(dev); + if (!data) + return -EINVAL; + + regmap = sun20i_regulator_get_regmap(dev); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to get regmap\n"); + + if (data->init) { + ret = data->init(dev, regmap); + if (ret) + return ret; + } + + config = (struct regulator_config) { + .dev = dev, + .regmap = regmap, + }; + + for (unsigned int i = 0; i < data->ndescs; ++i) { + const struct regulator_desc *desc = &data->descs[i]; + struct regulator_dev *rdev; + + rdev = devm_regulator_register(dev, desc, &config); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + } + + return 0; +} + +static struct platform_driver sun20i_regulator_driver = { + .probe = sun20i_regulator_probe, + .driver = { + .name = "sun20i-regulator", + .of_match_table = sun20i_regulator_of_match, + }, +}; +module_platform_driver(sun20i_regulator_driver); + +MODULE_AUTHOR("Samuel Holland "); +MODULE_DESCRIPTION("Allwinner D1 internal LDO driver"); +MODULE_LICENSE("GPL"); From 1d6bdabd62c70bc13597428a8d06aeba497dda37 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 1 Aug 2022 23:57:19 -0500 Subject: [PATCH 087/153] dt-bindings: sram: sunxi-sram: Add optional regulators child Some sunxi SoCs have in-package regulators controlled by a register in the system control MMIO block. Allow a child node for these regulators in addition to SRAM child nodes. Commit-changes: 2 - New patch for v2 Series-changes: 3 - Reference regulator schema from SRAM controller schema - Move system LDOs example to SRAM controller schema Signed-off-by: Samuel Holland --- .../allwinner,sun4i-a10-system-control.yaml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml b/Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml index 1c426c211e3668..7d9baf53c10cc2 100644 --- a/Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml +++ b/Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml @@ -57,6 +57,9 @@ properties: ranges: true patternProperties: + "^regulators@[a-z0-9]+": + $ref: /schemas/regulator/allwinner,sun20i-d1-system-ldos.yaml# + "^sram@[a-z0-9]+": type: object @@ -148,3 +151,28 @@ examples: }; }; }; + + - | + syscon@3000000 { + compatible = "allwinner,sun20i-d1-system-control"; + reg = <0x3000000 0x1000>; + ranges; + #address-cells = <1>; + #size-cells = <1>; + + regulators@3000150 { + compatible = "allwinner,sun20i-d1-system-ldos"; + reg = <0x3000150 0x4>; + + reg_ldoa: ldoa { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_ldob: ldob { + regulator-name = "vcc-dram"; + regulator-min-microvolt = <1500000>; + regulator-max-microvolt = <1500000>; + }; + }; + }; From 7b2d4257c40247804e7f619dc03bfbdc4d3393aa Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 2 Aug 2022 00:01:21 -0500 Subject: [PATCH 088/153] soc: sunxi: sram: Only iterate over SRAM children Now that a "regulators" child is accepted by the controller binding, the debugfs show routine must be explicitly limited to "sram" children. Otherwise, it will crash because the "regulators" node has no address. Series-to: Liam Girdwood Series-to: Mark Brown Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Series-to: Krzysztof Kozlowski Series-to: Rob Herring Commit-changes: 2 - New patch for v2 Series-version: 3 Cover-letter: regulator: Add support for Allwinner D1 LDOs This series adds bindings and a driver for the two pairs of LDOs inside the Allwinner D1 SoC. A preparatory binding and driver change is required for the SRAM controller, so the regulators device can be its child node. END Signed-off-by: Samuel Holland --- drivers/soc/sunxi/sunxi_sram.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index a8f3876963a087..b3016b9698fbec 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -120,6 +120,9 @@ static int sunxi_sram_show(struct seq_file *s, void *data) seq_puts(s, "--------------------\n\n"); for_each_child_of_node(sram_dev->of_node, sram_node) { + if (!of_node_name_eq(sram_node, "sram")) + continue; + sram_addr_p = of_get_address(sram_node, 0, NULL, NULL); seq_printf(s, "sram@%08x\n", From d7c04eec05107a563a049e4ca78dbff8c58b2339 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 10 Jul 2022 23:43:49 -0500 Subject: [PATCH 089/153] riscv: dts: allwinner: Add ClockworkPi and DevTerm devicetrees Clockwork Tech manufactures several SoMs for their RasPi CM3-compatible "ClockworkPi" mainboard. Their R-01 SoM features the Allwinner D1 SoC. The R-01 contains only the CPU, DRAM, and always-on voltage regulation; it does not merit a separate devicetree. The ClockworkPi mainboard features analog audio, a MIPI-DSI panel, USB host and peripheral ports, an Ampak AP6256 WiFi/Bluetooth module, and an X-Powers AXP228 PMIC for managing a Li-ion battery. The DevTerm is a complete system which extends the ClockworkPi mainboard with a pair of expansion boards. These expansion boards provide a fan, a keyboard, speakers, and a thermal printer. Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/Makefile | 2 + .../allwinner/sun20i-d1-clockworkpi-v3.14.dts | 242 ++++++++++++++++++ .../dts/allwinner/sun20i-d1-devterm-v3.14.dts | 37 +++ 3 files changed, 281 insertions(+) create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts create mode 100644 arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts diff --git a/arch/riscv/boot/dts/allwinner/Makefile b/arch/riscv/boot/dts/allwinner/Makefile index 530ef8adb8b0ad..25097da6fdb938 100644 --- a/arch/riscv/boot/dts/allwinner/Makefile +++ b/arch/riscv/boot/dts/allwinner/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-clockworkpi-v3.14.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-devterm-v3.14.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-dongshan-nezha-stu.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-86-panel-480p.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-lichee-rv-86-panel-720p.dtb diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts new file mode 100644 index 00000000000000..74b4b6d8363ab4 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Samuel Holland + +/dts-v1/; + +#include + +#include "sun20i-d1.dtsi" +#include "sun20i-d1-common-regulators.dtsi" + +/ { + model = "ClockworkPi v3.14 (R-01)"; + compatible = "clockwork,r-01-clockworkpi-v3.14", "allwinner,sun20i-d1"; + + aliases { + ethernet0 = &ap6256; + mmc0 = &mmc0; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + /* + * This regulator is PWM-controlled, but the PWM controller is not + * yet supported, so fix the regulator to its default voltage. + */ + reg_vdd_cpu: vdd-cpu { + compatible = "regulator-fixed"; + regulator-name = "vdd-cpu"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + vin-supply = <®_vcc>; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&pio 6 11 GPIO_ACTIVE_LOW>; /* PG11/GPIO3 */ + }; +}; + +&cpu0 { + cpu-supply = <®_vdd_cpu>; +}; + +&ehci1 { + status = "okay"; +}; + +&i2c0 { + pinctrl-0 = <&i2c0_pb10_pins>; + pinctrl-names = "default"; + status = "okay"; + + axp221: pmic@34 { + compatible = "x-powers,axp228", "x-powers,axp221"; + reg = <0x34>; + interrupt-parent = <&pio>; + interrupts = <4 9 IRQ_TYPE_LEVEL_LOW>; /* PE9/GPIO2 */ + interrupt-controller; + #interrupt-cells = <1>; + + ac_power_supply: ac-power { + compatible = "x-powers,axp221-ac-power-supply"; + }; + + axp_adc: adc { + compatible = "x-powers,axp221-adc"; + #io-channel-cells = <1>; + }; + + battery_power_supply: battery-power { + compatible = "x-powers,axp221-battery-power-supply"; + }; + + regulators { + x-powers,dcdc-freq = <3000>; + + reg_dcdc1: dcdc1 { + regulator-name = "sys-3v3"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_dcdc3: dcdc3 { + regulator-name = "sys-1v8"; + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + reg_aldo1: aldo1 { + regulator-name = "aud-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_aldo2: aldo2 { + regulator-name = "disp-3v3"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_aldo3: aldo3 { + regulator-name = "vdd-wifi"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + /* DLDO1 and ELDO1-3 are connected in parallel. */ + reg_dldo1: dldo1 { + regulator-name = "vbat-wifi-a"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + /* DLDO2-DLDO4 are connected in parallel. */ + reg_dldo2: dldo2 { + regulator-name = "vcc-3v3-ext-a"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_dldo3: dldo3 { + regulator-name = "vcc-3v3-ext-b"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_dldo4: dldo4 { + regulator-name = "vcc-3v3-ext-c"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_eldo1: eldo1 { + regulator-name = "vbat-wifi-b"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_eldo2: eldo2 { + regulator-name = "vbat-wifi-c"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_eldo3: eldo3 { + regulator-name = "vbat-wifi-d"; + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + }; + + usb_power_supply: usb-power { + compatible = "x-powers,axp221-usb-power-supply"; + status = "disabled"; + }; + }; +}; + +&mmc0 { + broken-cd; + bus-width = <4>; + disable-wp; + vmmc-supply = <®_dcdc1>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&mmc1 { + bus-width = <4>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + vmmc-supply = <®_dldo1>; + vqmmc-supply = <®_aldo3>; + pinctrl-0 = <&mmc1_pins>; + pinctrl-names = "default"; + status = "okay"; + + ap6256: wifi@1 { + compatible = "brcm,bcm43456-fmac", "brcm,bcm4329-fmac"; + reg = <1>; + interrupt-parent = <&pio>; + interrupts = <6 10 IRQ_TYPE_LEVEL_LOW>; /* PG10/GPIO4 */ + interrupt-names = "host-wake"; + }; +}; + +&ohci1 { + status = "okay"; +}; + +&pio { + vcc-pg-supply = <®_ldoa>; +}; + +&uart0 { + pinctrl-0 = <&uart0_pb8_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&uart1 { + uart-has-rtscts; + pinctrl-0 = <&uart1_pg6_pins>, <&uart1_pg8_rts_cts_pins>; + pinctrl-names = "default"; + status = "okay"; + + bluetooth { + compatible = "brcm,bcm4345c5"; + interrupt-parent = <&pio>; + interrupts = <6 17 IRQ_TYPE_LEVEL_HIGH>; /* PG17/GPIO6 */ + device-wakeup-gpios = <&pio 6 16 GPIO_ACTIVE_HIGH>; /* PG16/GPIO7 */ + shutdown-gpios = <&pio 6 18 GPIO_ACTIVE_HIGH>; /* PG18/GPIO5 */ + max-speed = <1500000>; + vbat-supply = <®_dldo1>; + vddio-supply = <®_aldo3>; + }; +}; + +&usb_otg { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usbphy { + usb0_vbus_power-supply = <&ac_power_supply>; + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts new file mode 100644 index 00000000000000..690bfa35a54838 --- /dev/null +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2022 Samuel Holland + +/dts-v1/; + +#include "sun20i-d1-clockworkpi-v3.14.dts" + +/ { + model = "Clockwork DevTerm (R-01)"; + compatible = "clockwork,r-01-devterm-v3.14", + "clockwork,r-01-clockworkpi-v3.14", + "allwinner,sun20i-d1"; + + fan { + compatible = "gpio-fan"; + gpios = <&pio 3 10 GPIO_ACTIVE_HIGH>; /* PD10/GPIO41 */ + gpio-fan,speed-map = <0 0>, + <6000 1>; + #cooling-cells = <2>; + }; + + i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&pio 3 14 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; /* PD14/GPIO44 */ + scl-gpios = <&pio 3 15 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; /* PD15/GPIO45 */ + #address-cells = <1>; + #size-cells = <0>; + + adc@54 { + compatible = "ti,adc101c"; + reg = <0x54>; + interrupt-parent = <&pio>; + interrupts = <4 12 IRQ_TYPE_LEVEL_LOW>; /* PE12/GPIO35 */ + vref-supply = <®_dldo2>; + }; + }; +}; From fc05adc896cfbd7ba8e5dd58bdbe29489a163922 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Mon, 27 Jun 2022 01:33:05 -0500 Subject: [PATCH 090/153] riscv: defconfig: Enable the Allwinner D1 platorm and drivers Now that several D1-based boards are supported, enable the platform in our defconfig. Build in the drivers which are necessary to boot, such as the pinctrl, RTC (which provides critical clocks), SPI (for boot flash), and watchdog (which may be left enabled by the bootloader). Other common peripherals are enabled as modules. Cover-letter: riscv: Allwinner D1 platform support This series adds the Kconfig/defconfig plumbing and devicetrees for a range of Allwinner D1-based boards. Many features are already enabled, including USB, Ethernet, and WiFi. Notable exceptions include: - Analog audio - HDMI - PWM (for CPU voltage regulators) - SPI (for NOR/NAND flash and display panels) The SoC devicetree contains one small hack to avoid a dependency on the audio codec binding, since that is not ready yet: the codec node uses a bare "syscon" compatible. The SoC devicetree uses bindings from the following series which have not yet been merged: - [IOMMU] - [LDOs] - [syscon] - [thermal sensor] - [timer] END Series-to: Chen-Yu Tsai Series-to: Jernej Skrabec Series-to: linux-sunxi@lists.linux.dev Series-to: Palmer Dabbelt Series-to: Paul Walmsley Series-to: Albert Ou Series-to: linux-riscv@lists.infradead.org Series-to: Rob Herring Series-to: Krzysztof Kozlowski Series-to: devicetree@vger.kernel.org Series-cc: linux-kernel@vger.kernel.org Signed-off-by: Samuel Holland --- arch/riscv/configs/defconfig | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index aed332a9d4ea21..57d251032bebc1 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -25,6 +25,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_EXPERT=y # CONFIG_SYSFS_SYSCALL is not set CONFIG_PROFILING=y +CONFIG_ARCH_SUNXI=y CONFIG_SOC_MICROCHIP_POLARFIRE=y CONFIG_SOC_SIFIVE=y CONFIG_SOC_STARFIVE=y @@ -120,20 +121,26 @@ CONFIG_E1000E=y CONFIG_R8169=y CONFIG_MICROSEMI_PHY=y CONFIG_INPUT_MOUSEDEV=y +CONFIG_KEYBOARD_SUN4I_LRADC=m CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_VIRTIO_CONSOLE=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_I2C_MV64XXX=m CONFIG_SPI=y CONFIG_SPI_SIFIVE=y +CONFIG_SPI_SUN6I=y # CONFIG_PTP_1588_CLOCK is not set -CONFIG_GPIOLIB=y +CONFIG_PINCTRL=y CONFIG_GPIO_SIFIVE=y +CONFIG_WATCHDOG=y +CONFIG_SUNXI_WATCHDOG=y CONFIG_DRM=m CONFIG_DRM_RADEON=m CONFIG_DRM_NOUVEAU=m +CONFIG_DRM_SUN4I=m CONFIG_DRM_VIRTIO_GPU=m CONFIG_FB=y CONFIG_FRAMEBUFFER_CONSOLE=y @@ -152,13 +159,19 @@ CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_CADENCE=y CONFIG_MMC_SPI=y CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_SUN6I=y CONFIG_VIRTIO_PCI=y CONFIG_VIRTIO_BALLOON=y CONFIG_VIRTIO_INPUT=y CONFIG_VIRTIO_MMIO=y +CONFIG_SUN8I_DE2_CCU=m +CONFIG_SUN50I_IOMMU=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_CTRL=y CONFIG_RPMSG_VIRTIO=y +CONFIG_EXTCON=m +CONFIG_PHY_SUN4I_USB=m +CONFIG_NVMEM_SUNXI_SID=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y From a3964b0961a225e242258d754a5a063e937054c4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 14 Nov 2021 09:04:29 -0600 Subject: [PATCH 091/153] dt-bindings: crypto: sun8i-ce: Add compatible for D1 D1 has a crypto engine similar to the one in other Allwinner SoCs. Like H6, it has a separate MBUS clock gate. It also requires the internal RC oscillator to be enabled for the TRNG to return data. This is likely the case for earlier variants as well, but the clock drivers for earlier SoCs did not allow disabling the RC oscillator. Series-changes: 2 - Add TRNG clock Signed-off-by: Samuel Holland --- .../bindings/crypto/allwinner,sun8i-ce.yaml | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml b/Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml index 026a9f9e1aeb50..9fe3d7f8981bc3 100644 --- a/Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml +++ b/Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml @@ -14,6 +14,7 @@ properties: enum: - allwinner,sun8i-h3-crypto - allwinner,sun8i-r40-crypto + - allwinner,sun20i-d1-crypto - allwinner,sun50i-a64-crypto - allwinner,sun50i-h5-crypto - allwinner,sun50i-h6-crypto @@ -29,6 +30,7 @@ properties: - description: Bus clock - description: Module clock - description: MBus clock + - description: TRNG clock (RC oscillator) minItems: 2 clock-names: @@ -36,6 +38,7 @@ properties: - const: bus - const: mod - const: ram + - const: trng minItems: 2 resets: @@ -44,19 +47,31 @@ properties: if: properties: compatible: - const: allwinner,sun50i-h6-crypto + enum: + - allwinner,sun20i-d1-crypto then: properties: clocks: - minItems: 3 + minItems: 4 clock-names: - minItems: 3 + minItems: 4 else: - properties: - clocks: - maxItems: 2 - clock-names: - maxItems: 2 + if: + properties: + compatible: + const: allwinner,sun50i-h6-crypto + then: + properties: + clocks: + minItems: 3 + clock-names: + minItems: 3 + else: + properties: + clocks: + maxItems: 2 + clock-names: + maxItems: 2 required: - compatible From 00909d15eb1ba45ba9dbe1d99edaa41faab81fef Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 23:02:43 -0500 Subject: [PATCH 092/153] riscv: dts: allwinner: d1: Add LED controller Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi index 115c3b90b1f730..2730f1554c42b8 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -144,6 +144,12 @@ function = "lcd0"; }; + /omit-if-no-ref/ + ledc_pc0_pin: ledc-pc0-pin { + pins = "PC0"; + function = "ledc"; + }; + /omit-if-no-ref/ mmc0_pins: mmc0-pins { pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; @@ -207,6 +213,21 @@ #reset-cells = <1>; }; + ledc: led-controller@2008000 { + compatible = "allwinner,sun20i-d1-ledc", + "allwinner,sun50i-r329-ledc"; + reg = <0x2008000 0x400>; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_LEDC>, <&ccu CLK_LEDC>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_LEDC>; + dmas = <&dma 42>; + dma-names = "tx"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + ths: temperature-sensor@2009400 { compatible = "allwinner,sun20i-d1-ths"; reg = <0x2009400 0x400>; From 9447683f3afc7e703dbbff21072d3e96081bc2df Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 1 Feb 2022 21:50:16 -0600 Subject: [PATCH 093/153] crypto: sun8i-ce - Add TRNG clock to D1 variant At least the D1 variant requires a separate clock for the TRNG. Without this clock enabled, reading from /dev/hwrng reports: sun8i-ce 3040000.crypto: DMA timeout for TRNG (tm=96) on flow 3 Experimentation shows that the necessary clock is the SoC's internal RC oscillator. This makes sense, as the oscillator's frequency variations can be used as a source of randomness. Since D1 does not yet have a device tree, we can update this variant without breaking anything. Series-changes: 2 - New patch Signed-off-by: Samuel Holland --- drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 1 + drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c index 9f659469983544..a6865ff4d400c4 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c @@ -118,6 +118,7 @@ static const struct ce_variant ce_d1_variant = { { "bus", 0, 200000000 }, { "mod", 300000000, 0 }, { "ram", 0, 400000000 }, + { "trng", 0, 0 }, }, .esr = ESR_D1, .prng = CE_ALG_PRNG, diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h index 8177aaba443498..27029fb77e293a 100644 --- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h +++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h @@ -105,7 +105,7 @@ #define MAX_SG 8 -#define CE_MAX_CLOCKS 3 +#define CE_MAX_CLOCKS 4 #define MAXFLOW 4 From 075cc1fa5da3d874ae9241f8eea09a7035c0a3be Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 23:03:01 -0500 Subject: [PATCH 094/153] riscv: dts: allwinner: d1: Add RGB LEDs to boards Signed-off-by: Samuel Holland --- .../boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts | 12 ++++++++++++ arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts index ca36a5d75a7f24..02d13e987e0222 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts @@ -25,6 +25,18 @@ status = "okay"; }; +&ledc { + pinctrl-0 = <&ledc_pc0_pin>; + pinctrl-names = "default"; + status = "okay"; + + multi-led@0 { + reg = <0x0>; + color = ; + function = LED_FUNCTION_STATUS; + }; +}; + &lradc { status = "okay"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts index df865ee15fcfa8..726fe434abc96e 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts @@ -5,6 +5,7 @@ #include #include +#include #include "sun20i-d1.dtsi" #include "sun20i-d1-common-regulators.dtsi" @@ -90,6 +91,18 @@ }; }; +&ledc { + pinctrl-0 = <&ledc_pc0_pin>; + pinctrl-names = "default"; + status = "okay"; + + multi-led@0 { + reg = <0x0>; + color = ; + function = LED_FUNCTION_INDICATOR; + }; +}; + &lradc { status = "okay"; From a8eb1cb250a2af2ea848de84757ec5112d5dc8c5 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 22:20:31 -0500 Subject: [PATCH 095/153] riscv: dts: allwinner: d1: Add crypto engine support Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi index 2730f1554c42b8..c4b4b4fb8f517f 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -527,6 +527,18 @@ }; }; + crypto: crypto@3040000 { + compatible = "allwinner,sun20i-d1-crypto"; + reg = <0x3040000 0x800>; + interrupts = <68 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_CE>, + <&ccu CLK_CE>, + <&ccu CLK_MBUS_CE>, + <&rtc CLK_IOSC>; + clock-names = "bus", "mod", "ram", "trng"; + resets = <&ccu RST_BUS_CE>; + }; + mbus: dram-controller@3102000 { compatible = "allwinner,sun20i-d1-mbus"; reg = <0x3102000 0x1000>, From d562baf12c8122daeb789e8e2beb5cf285427460 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 22:23:05 -0500 Subject: [PATCH 096/153] dt-bindings: spi: sun6i: Add R329 variant Signed-off-by: Samuel Holland --- .../devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml index ca4c95345a49b2..6b6e19f5608d83 100644 --- a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml +++ b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml @@ -21,6 +21,7 @@ properties: oneOf: - const: allwinner,sun6i-a31-spi - const: allwinner,sun8i-h3-spi + - const: allwinner,sun50i-r329-spi - items: - enum: - allwinner,sun8i-r40-spi @@ -28,6 +29,9 @@ properties: - allwinner,sun50i-h616-spi - allwinner,suniv-f1c100s-spi - const: allwinner,sun8i-h3-spi + - items: + - const: allwinner,sun20i-d1-spi + - const: allwinner,sun50i-r329-spi reg: maxItems: 1 From aa4be4d52683c0f2b164d71f9b0552a7831fa88c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 16 Jul 2021 21:33:16 -0500 Subject: [PATCH 097/153] spi: spi-sun6i: Use a struct for quirks Signed-off-by: Samuel Holland --- drivers/spi/spi-sun6i.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 23ad052528dbe7..125fd3052db5ad 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -85,7 +85,12 @@ #define SUN6I_TXDATA_REG 0x200 #define SUN6I_RXDATA_REG 0x300 +struct sun6i_spi_quirks { + unsigned long fifo_depth; +}; + struct sun6i_spi { + const struct sun6i_spi_quirks *quirks; struct spi_master *master; void __iomem *base_addr; dma_addr_t dma_addr_rx; @@ -99,7 +104,6 @@ struct sun6i_spi { const u8 *tx_buf; u8 *rx_buf; int len; - unsigned long fifo_depth; }; static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg) @@ -156,7 +160,7 @@ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi) u8 byte; /* See how much data we can fit */ - cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi); + cnt = sspi->quirks->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi); len = min((int)cnt, sspi->len); @@ -289,14 +293,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master, * the hardcoded value used in old generation of Allwinner * SPI controller. (See spi-sun4i.c) */ - trig_level = sspi->fifo_depth / 4 * 3; + trig_level = sspi->quirks->fifo_depth / 4 * 3; } else { /* * Setup FIFO DMA request trigger level * We choose 1/2 of the full fifo depth, that value will * be used as DMA burst length. */ - trig_level = sspi->fifo_depth / 2; + trig_level = sspi->quirks->fifo_depth / 2; if (tfr->tx_buf) reg |= SUN6I_FIFO_CTL_TF_DRQ_EN; @@ -410,9 +414,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master, reg = SUN6I_INT_CTL_TC; if (!use_dma) { - if (rx_len > sspi->fifo_depth) + if (rx_len > sspi->quirks->fifo_depth) reg |= SUN6I_INT_CTL_RF_RDY; - if (tx_len > sspi->fifo_depth) + if (tx_len > sspi->quirks->fifo_depth) reg |= SUN6I_INT_CTL_TF_ERQ; } @@ -543,7 +547,7 @@ static bool sun6i_spi_can_dma(struct spi_master *master, * the fifo length we can just fill the fifo and wait for a single * irq, so don't bother setting up dma */ - return xfer->len > sspi->fifo_depth; + return xfer->len > sspi->quirks->fifo_depth; } static int sun6i_spi_probe(struct platform_device *pdev) @@ -582,7 +586,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) } sspi->master = master; - sspi->fifo_depth = (unsigned long)of_device_get_match_data(&pdev->dev); + sspi->quirks = of_device_get_match_data(&pdev->dev); master->max_speed_hz = 100 * 1000 * 1000; master->min_speed_hz = 3 * 1000; @@ -696,9 +700,17 @@ static int sun6i_spi_remove(struct platform_device *pdev) return 0; } +static const struct sun6i_spi_quirks sun6i_a31_spi_quirks = { + .fifo_depth = SUN6I_FIFO_DEPTH, +}; + +static const struct sun6i_spi_quirks sun8i_h3_spi_quirks = { + .fifo_depth = SUN8I_FIFO_DEPTH, +}; + static const struct of_device_id sun6i_spi_match[] = { - { .compatible = "allwinner,sun6i-a31-spi", .data = (void *)SUN6I_FIFO_DEPTH }, - { .compatible = "allwinner,sun8i-h3-spi", .data = (void *)SUN8I_FIFO_DEPTH }, + { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_quirks }, + { .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_quirks }, {} }; MODULE_DEVICE_TABLE(of, sun6i_spi_match); From 273a3658fa9d67c0533162b0bc93b0ad43b130c2 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 16 Jul 2021 21:46:31 -0500 Subject: [PATCH 098/153] spi: spi-sun6i: Add Allwinner R329 support Signed-off-by: Samuel Holland --- drivers/spi/spi-sun6i.c | 78 ++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 125fd3052db5ad..77a9e0646f3f3a 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -30,6 +30,7 @@ #define SUN6I_GBL_CTL_REG 0x04 #define SUN6I_GBL_CTL_BUS_ENABLE BIT(0) #define SUN6I_GBL_CTL_MASTER BIT(1) +#define SUN6I_GBL_CTL_SAMPLE_MODE BIT(2) #define SUN6I_GBL_CTL_TP BIT(7) #define SUN6I_GBL_CTL_RST BIT(31) @@ -87,6 +88,8 @@ struct sun6i_spi_quirks { unsigned long fifo_depth; + bool has_divider : 1; + bool has_new_sample_mode : 1; }; struct sun6i_spi { @@ -351,38 +354,44 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); /* Ensure that we have a parent clock fast enough */ - mclk_rate = clk_get_rate(sspi->mclk); - if (mclk_rate < (2 * tfr->speed_hz)) { - clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); + if (sspi->quirks->has_divider) { mclk_rate = clk_get_rate(sspi->mclk); - } + if (mclk_rate < (2 * tfr->speed_hz)) { + clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + } - /* - * Setup clock divider. - * - * We have two choices there. Either we can use the clock - * divide rate 1, which is calculated thanks to this formula: - * SPI_CLK = MOD_CLK / (2 ^ cdr) - * Or we can use CDR2, which is calculated with the formula: - * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) - * Wether we use the former or the latter is set through the - * DRS bit. - * - * First try CDR2, and if we can't reach the expected - * frequency, fall back to CDR1. - */ - div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz); - div_cdr2 = DIV_ROUND_UP(div_cdr1, 2); - if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { - reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS; - tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2); + /* + * Setup clock divider. + * + * We have two choices there. Either we can use the clock + * divide rate 1, which is calculated thanks to this formula: + * SPI_CLK = MOD_CLK / (2 ^ cdr) + * Or we can use CDR2, which is calculated with the formula: + * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) + * Wether we use the former or the latter is set through the + * DRS bit. + * + * First try CDR2, and if we can't reach the expected + * frequency, fall back to CDR1. + */ + div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz); + div_cdr2 = DIV_ROUND_UP(div_cdr1, 2); + if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { + reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS; + tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2); + } else { + div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1)); + reg = SUN6I_CLK_CTL_CDR1(div); + tfr->effective_speed_hz = mclk_rate / (1 << div); + } + sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); } else { - div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1)); - reg = SUN6I_CLK_CTL_CDR1(div); - tfr->effective_speed_hz = mclk_rate / (1 << div); + clk_set_rate(sspi->mclk, tfr->speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + tfr->effective_speed_hz = mclk_rate; } - sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); /* Finally enable the bus - doing so before might raise SCK to HIGH */ reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG); reg |= SUN6I_GBL_CTL_BUS_ENABLE; @@ -492,6 +501,7 @@ static int sun6i_spi_runtime_resume(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct sun6i_spi *sspi = spi_master_get_devdata(master); int ret; + u32 reg; ret = clk_prepare_enable(sspi->hclk); if (ret) { @@ -511,8 +521,10 @@ static int sun6i_spi_runtime_resume(struct device *dev) goto err2; } - sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, - SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP); + reg = SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP; + if (sspi->quirks->has_new_sample_mode) + reg |= SUN6I_GBL_CTL_SAMPLE_MODE; + sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, reg); return 0; @@ -702,15 +714,23 @@ static int sun6i_spi_remove(struct platform_device *pdev) static const struct sun6i_spi_quirks sun6i_a31_spi_quirks = { .fifo_depth = SUN6I_FIFO_DEPTH, + .has_divider = true, }; static const struct sun6i_spi_quirks sun8i_h3_spi_quirks = { .fifo_depth = SUN8I_FIFO_DEPTH, + .has_divider = true, +}; + +static const struct sun6i_spi_quirks sun50i_r329_spi_quirks = { + .fifo_depth = SUN8I_FIFO_DEPTH, + .has_new_sample_mode = true, }; static const struct of_device_id sun6i_spi_match[] = { { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_quirks }, { .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_quirks }, + { .compatible = "allwinner,sun50i-r329-spi", .data = &sun50i_r329_spi_quirks }, {} }; MODULE_DEVICE_TABLE(of, sun6i_spi_match); From edde2ef160060a7931caec0a09ad214a127a104c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 17 Jul 2021 11:19:29 -0500 Subject: [PATCH 099/153] [WIP] spi: spi-sun6i: Dual/Quad RX Support Signed-off-by: Samuel Holland --- drivers/spi/spi-sun6i.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 77a9e0646f3f3a..a80ccf0973a8c2 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -82,6 +82,8 @@ #define SUN6I_XMIT_CNT_REG 0x34 #define SUN6I_BURST_CTL_CNT_REG 0x38 +#define SUN6I_BURST_CTL_CNT_QUAD_EN BIT(29) +#define SUN6I_BURST_CTL_CNT_DUAL_EN BIT(28) #define SUN6I_TXDATA_REG 0x200 #define SUN6I_RXDATA_REG 0x300 @@ -404,7 +406,17 @@ static int sun6i_spi_transfer_one(struct spi_master *master, /* Setup the counters */ sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, tfr->len); sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len); - sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len); + + reg = tx_len; + switch (tfr->rx_nbits) { + case SPI_NBITS_QUAD: + reg |= SUN6I_BURST_CTL_CNT_QUAD_EN; + break; + case SPI_NBITS_DUAL: + reg |= SUN6I_BURST_CTL_CNT_DUAL_EN; + break; + } + sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, reg); if (!use_dma) { /* Fill the TX FIFO */ @@ -606,7 +618,8 @@ static int sun6i_spi_probe(struct platform_device *pdev) master->set_cs = sun6i_spi_set_cs; master->transfer_one = sun6i_spi_transfer_one; master->num_chipselect = 4; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST + | SPI_RX_DUAL | SPI_RX_QUAD; master->bits_per_word_mask = SPI_BPW_MASK(8); master->dev.of_node = pdev->dev.of_node; master->auto_runtime_pm = true; From 3483fe31750f2de546e520cc72de2fc4ee03fa3a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 00:54:01 -0500 Subject: [PATCH 100/153] riscv: dts: allwinner: Add SPI support Signed-off-by: Samuel Holland --- .../dts/allwinner/sun20i-d1-lichee-rv.dts | 6 +++ .../boot/dts/allwinner/sun20i-d1-nezha.dts | 44 ++++++++++++++++ arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 51 +++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv.dts index 2cc0b63a273de4..52ed6145b4077d 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv.dts @@ -64,6 +64,12 @@ status = "okay"; }; +&spi0 { + pinctrl-0 = <&spi0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + &uart0 { pinctrl-0 = <&uart0_pb8_pins>; pinctrl-names = "default"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts index 726fe434abc96e..21c0a7e67e76c9 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts @@ -19,6 +19,7 @@ ethernet1 = &xr829; mmc0 = &mmc0; serial0 = &uart0; + spi0 = &spi0; }; chosen { @@ -155,6 +156,49 @@ status = "okay"; }; +&spi0 { + pinctrl-0 = <&spi0_pins>; + pinctrl-names = "default"; + status = "okay"; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "boot0"; + reg = <0x00000000 0x00100000>; + }; + + partition@100000 { + label = "uboot"; + reg = <0x00100000 0x00300000>; + }; + + partition@400000 { + label = "secure_storage"; + reg = <0x00400000 0x00100000>; + }; + + partition@500000 { + label = "sys"; + reg = <0x00500000 0x0fb00000>; + }; + }; + }; +}; + +&spi1 { + pinctrl-0 = <&spi1_pd_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + &uart0 { pinctrl-0 = <&uart0_pb8_pins>; pinctrl-names = "default"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi index c4b4b4fb8f517f..af5fbf79dd17cc 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -183,6 +183,24 @@ function = "emac"; }; + /omit-if-no-ref/ + spi0_pins: spi0-pins { + pins = "PC2", "PC3", "PC4", "PC5", "PC6", "PC7"; + function = "spi0"; + }; + + /omit-if-no-ref/ + spi1_pb_pins: spi1-pb-pins { + pins = "PB0", "PB8", "PB9", "PB10", "PB11", "PB12"; + function = "spi1"; + }; + + /omit-if-no-ref/ + spi1_pd_pins: spi1-pd-pins { + pins = "PD10", "PD11", "PD12", "PD13", "PD14", "PD15"; + function = "spi1"; + }; + /omit-if-no-ref/ uart0_pb8_pins: uart0-pb8-pins { pins = "PB8", "PB9"; @@ -607,6 +625,39 @@ #size-cells = <0>; }; + spi0: spi@4025000 { + compatible = "allwinner,sun20i-d1-spi", + "allwinner,sun50i-r329-spi"; + reg = <0x4025000 0x1000>; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; + clock-names = "ahb", "mod"; + resets = <&ccu RST_BUS_SPI0>; + dmas = <&dma 22>, <&dma 22>; + dma-names = "rx", "tx"; + num-cs = <1>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + + spi1: spi@4026000 { + compatible = "allwinner,sun20i-d1-spi-dbi", + "allwinner,sun50i-r329-spi-dbi", + "allwinner,sun50i-r329-spi"; + reg = <0x4026000 0x1000>; + interrupts = <32 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>; + clock-names = "ahb", "mod"; + resets = <&ccu RST_BUS_SPI1>; + dmas = <&dma 23>, <&dma 23>; + dma-names = "rx", "tx"; + num-cs = <1>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + usb_otg: usb@4100000 { compatible = "allwinner,sun20i-d1-musb", "allwinner,sun8i-a33-musb"; From dc351adf5a7a57e76c66976b25c2ec2c24d14543 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 12 Aug 2022 01:29:34 -0500 Subject: [PATCH 101/153] fixup! dt-bindings: spi: sun6i: Add R329 variant --- .../devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml index 6b6e19f5608d83..c700a25b9b7b79 100644 --- a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml +++ b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml @@ -32,6 +32,10 @@ properties: - items: - const: allwinner,sun20i-d1-spi - const: allwinner,sun50i-r329-spi + - items: + - const: allwinner,sun20i-d1-spi-dbi + - const: allwinner,sun50i-r329-spi-dbi + - const: allwinner,sun50i-r329-spi reg: maxItems: 1 From a406045366b4314ccd788474ea45ec8f32402be0 Mon Sep 17 00:00:00 2001 From: Ban Tao Date: Tue, 2 Mar 2021 20:40:23 +0800 Subject: [PATCH 102/153] pwm: sun8i-v536: document device tree bindings This adds binding documentation for sun8i-v536 SoC PWM driver. Signed-off-by: Ban Tao --- .../bindings/pwm/pwm-sun8i-v536.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt diff --git a/Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt b/Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt new file mode 100644 index 00000000000000..ab3f4fe0560a25 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt @@ -0,0 +1,24 @@ +Allwinner sun8i-v536 SoC PWM controller + +Required properties: + - compatible: should be "allwinner,-pwm" + "allwinner,sun8i-v833-pwm" + "allwinner,sun8i-v536-pwm" + "allwinner,sun50i-r818-pwm" + "allwinner,sun50i-a133-pwm" + "allwinner,sun50i-r329-pwm" + - reg: physical base address and length of the controller's registers + - #pwm-cells: should be 3. See pwm.txt in this directory for a description of + the cells format. + - clocks: From common clock binding, handle to the parent clock. + - resets: From reset clock binding, handle to the parent clock. + +Example: + + pwm: pwm@300a0000 { + compatible = "allwinner,sun50i-r818-pwm"; + reg = <0x0300a000 0x3ff>; + clocks = <&ccu CLK_BUS_PWM>; + resets = <&ccu RST_BUS_PWM>; + #pwm-cells = <3>; + }; From 333186fc5773a94ee0d68d9e7d6ebda3a649aedd Mon Sep 17 00:00:00 2001 From: Ban Tao Date: Tue, 2 Mar 2021 20:37:37 +0800 Subject: [PATCH 103/153] pwm: sunxi: Add Allwinner SoC PWM controller driver The Allwinner R818, A133, R329, V536 and V833 has a new PWM controller IP compared to the older Allwinner SoCs. Signed-off-by: Ban Tao --- MAINTAINERS | 6 + drivers/pwm/Kconfig | 11 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-sun8i-v536.c | 401 +++++++++++++++++++++++++++++++++++ 4 files changed, 419 insertions(+) create mode 100644 drivers/pwm/pwm-sun8i-v536.c diff --git a/MAINTAINERS b/MAINTAINERS index 62cad03aedb480..54ed65f9c22909 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -796,6 +796,12 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/staging/media/sunxi/cedrus/ +ALLWINNER PWM DRIVER +M: Ban Tao +L: linux-pwm@vger.kernel.org +S: Maintained +F: drivers/pwm/pwm-sun8i-v536.c + ALPHA PORT M: Richard Henderson M: Ivan Kokshaysky diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 904de8d61828a6..584e63f26fb095 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -572,6 +572,17 @@ config PWM_SUN4I To compile this driver as a module, choose M here: the module will be called pwm-sun4i. +config PWM_SUN8I_V536 + tristate "Allwinner SUN8I_V536 PWM support" + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM && COMMON_CLK + help + Enhanced PWM framework driver for Allwinner R818, A133, R329, + V536 and V833 SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-sun8i-v536. + config PWM_SUNPLUS tristate "Sunplus PWM support" depends on ARCH_SUNPLUS || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 5c08bdb817b4c5..c6542469b2edc3 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o +obj-$(CONFIG_PWM_SUN8I_V536) += pwm-sun8i-v536.o obj-$(CONFIG_PWM_SUNPLUS) += pwm-sunplus.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o diff --git a/drivers/pwm/pwm-sun8i-v536.c b/drivers/pwm/pwm-sun8i-v536.c new file mode 100644 index 00000000000000..52101df6bd41b1 --- /dev/null +++ b/drivers/pwm/pwm-sun8i-v536.c @@ -0,0 +1,401 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Allwinner sun8i-v536 Pulse Width Modulation Controller + * + * Copyright (C) 2021 Ban Tao + * + * + * Limitations: + * - When PWM is disabled, the output is driven to inactive. + * - If the register is reconfigured while PWM is running, + * it does not complete the currently running period. + * - If the user input duty is beyond acceptible limits, + * -EINVAL is returned. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PWM_GET_CLK_OFFSET(chan) (0x20 + ((chan >> 1) * 0x4)) +#define PWM_CLK_APB_SCR BIT(7) +#define PWM_DIV_M 0 +#define PWM_DIV_M_MASK GENMASK(3, PWM_DIV_M) + +#define PWM_CLK_REG 0x40 +#define PWM_CLK_GATING BIT(0) + +#define PWM_ENABLE_REG 0x80 +#define PWM_EN BIT(0) + +#define PWM_CTL_REG(chan) (0x100 + 0x20 * chan) +#define PWM_ACT_STA BIT(8) +#define PWM_PRESCAL_K 0 +#define PWM_PRESCAL_K_MASK GENMASK(7, PWM_PRESCAL_K) + +#define PWM_PERIOD_REG(chan) (0x104 + 0x20 * chan) +#define PWM_ENTIRE_CYCLE 16 +#define PWM_ENTIRE_CYCLE_MASK GENMASK(31, PWM_ENTIRE_CYCLE) +#define PWM_ACT_CYCLE 0 +#define PWM_ACT_CYCLE_MASK GENMASK(15, PWM_ACT_CYCLE) + +#define BIT_CH(bit, chan) ((bit) << (chan)) +#define SET_BITS(shift, mask, reg, val) \ + (((reg) & ~mask) | (val << (shift))) + +#define PWM_OSC_CLK 24000000 +#define PWM_PRESCALER_MAX 256 +#define PWM_CLK_DIV_M__MAX 9 +#define PWM_ENTIRE_CYCLE_MAX 65536 + +struct sun8i_pwm_data { + unsigned int npwm; +}; + +struct sun8i_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + struct reset_control *rst_clk; + void __iomem *base; + const struct sun8i_pwm_data *data; +}; + +static inline struct sun8i_pwm_chip *to_sun8i_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct sun8i_pwm_chip, chip); +} + +static inline u32 sun8i_pwm_readl(struct sun8i_pwm_chip *chip, + unsigned long offset) +{ + return readl(chip->base + offset); +} + +static inline void sun8i_pwm_writel(struct sun8i_pwm_chip *chip, + u32 val, unsigned long offset) +{ + writel(val, chip->base + offset); +} + +static void sun8i_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) +{ + struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip); + u64 clk_rate; + u32 tmp, entire_cycles, active_cycles; + unsigned int prescaler, div_m; + + tmp = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + if (tmp & PWM_CLK_APB_SCR) + clk_rate = clk_get_rate(pc->clk); + else + clk_rate = PWM_OSC_CLK; + + tmp = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + div_m = 0x1 << (tmp & PWM_DIV_M_MASK); + + tmp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm)); + prescaler = (tmp & PWM_PRESCAL_K_MASK) + 1; + + tmp = sun8i_pwm_readl(pc, PWM_PERIOD_REG(pwm->hwpwm)); + entire_cycles = (tmp >> PWM_ENTIRE_CYCLE) + 1; + active_cycles = (tmp & PWM_ACT_CYCLE_MASK); + + /* (clk / div_m / prescaler) / entire_cycles = NSEC_PER_SEC / period_ns. */ + state->period = DIV_ROUND_CLOSEST_ULL(entire_cycles * NSEC_PER_SEC, + clk_rate) * div_m * prescaler; + /* duty_ns / period_ns = active_cycles / entire_cycles. */ + state->duty_cycle = DIV_ROUND_CLOSEST_ULL(active_cycles * state->period, + entire_cycles); + + /* parsing polarity */ + tmp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm)); + if (tmp & PWM_ACT_STA) + state->polarity = PWM_POLARITY_NORMAL; + else + state->polarity = PWM_POLARITY_INVERSED; + + /* parsing enabled */ + tmp = sun8i_pwm_readl(pc, PWM_ENABLE_REG); + if (tmp & BIT_CH(PWM_EN, pwm->hwpwm)) + state->enabled = true; + else + state->enabled = false; + + dev_dbg(chip->dev, "duty_ns=%lld period_ns=%lld polarity=%s enabled=%s.\n", + state->duty_cycle, state->period, + state->polarity ? "inversed":"normal", + state->enabled ? "true":"false"); +} + +static void sun8i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip); + u32 temp; + + temp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm)); + + if (polarity == PWM_POLARITY_NORMAL) + temp |= PWM_ACT_STA; + else + temp &= ~PWM_ACT_STA; + + sun8i_pwm_writel(pc, temp, PWM_CTL_REG(pwm->hwpwm)); +} + +static int sun8i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip); + unsigned long long c; + unsigned long entire_cycles, active_cycles; + unsigned int div_m, prescaler; + u64 duty_ns = state->duty_cycle, period_ns = state->period; + u32 config; + int ret = 0; + + if (period_ns > 334) { + /* if freq < 3M, then select 24M clock */ + c = PWM_OSC_CLK; + config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + config &= ~PWM_CLK_APB_SCR; + sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + } else { + /* if freq > 3M, then select APB as clock */ + c = clk_get_rate(pc->clk); + config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + config |= PWM_CLK_APB_SCR; + sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + } + + dev_dbg(chip->dev, "duty_ns=%lld period_ns=%lld c =%llu.\n", + duty_ns, period_ns, c); + + /* + * (clk / div_m / prescaler) / entire_cycles = NSEC_PER_SEC / period_ns. + * So, entire_cycles = clk * period_ns / NSEC_PER_SEC / div_m / prescaler. + */ + c = c * period_ns; + c = DIV_ROUND_CLOSEST_ULL(c, NSEC_PER_SEC); + for (div_m = 0; div_m < PWM_CLK_DIV_M__MAX; div_m++) { + for (prescaler = 0; prescaler < PWM_PRESCALER_MAX; prescaler++) { + /* + * actual prescaler = prescaler(reg value) + 1. + * actual div_m = 0x1 << div_m(reg value). + */ + entire_cycles = ((unsigned long)c >> div_m)/(prescaler + 1); + if (entire_cycles <= PWM_ENTIRE_CYCLE_MAX) + goto calc_end; + } + } + ret = -EINVAL; + goto exit; + +calc_end: + /* + * duty_ns / period_ns = active_cycles / entire_cycles. + * So, active_cycles = entire_cycles * duty_ns / period_ns. + */ + c = (unsigned long long)entire_cycles * duty_ns; + c = DIV_ROUND_CLOSEST_ULL(c, period_ns); + active_cycles = c; + if (entire_cycles == 0) + entire_cycles++; + + /* config clk div_m*/ + config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + config = SET_BITS(PWM_DIV_M, PWM_DIV_M_MASK, config, div_m); + sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + + /* config prescaler */ + config = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm)); + config = SET_BITS(PWM_PRESCAL_K, PWM_PRESCAL_K_MASK, config, prescaler); + sun8i_pwm_writel(pc, config, PWM_CTL_REG(pwm->hwpwm)); + + /* config active and period cycles */ + config = sun8i_pwm_readl(pc, PWM_PERIOD_REG(pwm->hwpwm)); + config = SET_BITS(PWM_ACT_CYCLE, PWM_ACT_CYCLE_MASK, config, active_cycles); + config = SET_BITS(PWM_ENTIRE_CYCLE, PWM_ENTIRE_CYCLE_MASK, + config, (entire_cycles - 1)); + sun8i_pwm_writel(pc, config, PWM_PERIOD_REG(pwm->hwpwm)); + + dev_dbg(chip->dev, "active_cycles=%lu entire_cycles=%lu prescaler=%u div_m=%u\n", + active_cycles, entire_cycles, prescaler, div_m); + +exit: + return ret; +} + +static void sun8i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, + bool enable) +{ + struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip); + u32 clk, pwm_en; + + clk = sun8i_pwm_readl(pc, PWM_CLK_REG); + pwm_en = sun8i_pwm_readl(pc, PWM_ENABLE_REG); + + if (enable) { + clk |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + sun8i_pwm_writel(pc, clk, PWM_CLK_REG); + + pwm_en |= BIT_CH(PWM_EN, pwm->hwpwm); + sun8i_pwm_writel(pc, pwm_en, PWM_ENABLE_REG); + } else { + pwm_en &= ~BIT_CH(PWM_EN, pwm->hwpwm); + sun8i_pwm_writel(pc, pwm_en, PWM_ENABLE_REG); + + clk &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + sun8i_pwm_writel(pc, clk, PWM_CLK_REG); + } +} + +static int sun8i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct pwm_state curstate; + int ret; + + pwm_get_state(pwm, &curstate); + + ret = sun8i_pwm_config(chip, pwm, state); + + if (state->polarity != curstate.polarity) + sun8i_pwm_set_polarity(chip, pwm, state->polarity); + + if (state->enabled != curstate.enabled) + sun8i_pwm_enable(chip, pwm, state->enabled); + + return ret; +} + +static const struct pwm_ops sun8i_pwm_ops = { + .get_state = sun8i_pwm_get_state, + .apply = sun8i_pwm_apply, + .owner = THIS_MODULE, +}; + +static const struct sun8i_pwm_data sun8i_pwm_data_c9 = { + .npwm = 9, +}; + +static const struct sun8i_pwm_data sun50i_pwm_data_c16 = { + .npwm = 16, +}; + +static const struct of_device_id sun8i_pwm_dt_ids[] = { + { + .compatible = "allwinner,sun8i-v536-pwm", + .data = &sun8i_pwm_data_c9, + }, { + .compatible = "allwinner,sun50i-r818-pwm", + .data = &sun50i_pwm_data_c16, + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, sun8i_pwm_dt_ids); + +static int sun8i_pwm_probe(struct platform_device *pdev) +{ + struct sun8i_pwm_chip *pc; + int ret; + + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (!pc) + return dev_err_probe(&pdev->dev, -ENOMEM, + "memory allocation failed\n"); + + pc->data = of_device_get_match_data(&pdev->dev); + if (!pc->data) + return dev_err_probe(&pdev->dev, -ENODEV, + "can't get match data\n"); + + pc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pc->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(pc->base), + "can't remap pwm resource\n"); + + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk), + "get clock failed\n"); + + pc->rst_clk = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(pc->rst_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pc->rst_clk), + "get reset failed\n"); + + /* Deassert reset */ + ret = reset_control_deassert(pc->rst_clk); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "cannot deassert reset control\n"); + + ret = clk_prepare_enable(pc->clk); + if (ret) { + dev_err(&pdev->dev, "cannot prepare and enable clk %pe\n", + ERR_PTR(ret)); + goto err_clk; + } + + pc->chip.dev = &pdev->dev; + pc->chip.ops = &sun8i_pwm_ops; + pc->chip.npwm = pc->data->npwm; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.base = -1; + pc->chip.of_pwm_n_cells = 3; + + ret = pwmchip_add(&pc->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + goto err_pwm_add; + } + + platform_set_drvdata(pdev, pc); + + return 0; + +err_pwm_add: + clk_disable_unprepare(pc->clk); +err_clk: + reset_control_assert(pc->rst_clk); + + return ret; +} + +static int sun8i_pwm_remove(struct platform_device *pdev) +{ + struct sun8i_pwm_chip *pc = platform_get_drvdata(pdev); + int ret; + + ret = pwmchip_remove(&pc->chip); + if (ret) + return ret; + + clk_disable_unprepare(pc->clk); + reset_control_assert(pc->rst_clk); + + return 0; +} + +static struct platform_driver sun8i_pwm_driver = { + .driver = { + .name = "sun8i-pwm-v536", + .of_match_table = sun8i_pwm_dt_ids, + }, + .probe = sun8i_pwm_probe, + .remove = sun8i_pwm_remove, +}; +module_platform_driver(sun8i_pwm_driver); + +MODULE_ALIAS("platform:sun8i-v536-pwm"); +MODULE_AUTHOR("Ban Tao "); +MODULE_DESCRIPTION("Allwinner sun8i-v536 PWM driver"); +MODULE_LICENSE("GPL v2"); From 2c4e357077ea310df44d48f0d177d1631dceba2b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 6 Jun 2021 11:05:20 -0500 Subject: [PATCH 104/153] squash? pwm: sunxi: Add Allwinner SoC PWM controller driver --- drivers/pwm/Kconfig | 4 ++-- drivers/pwm/pwm-sun8i-v536.c | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 584e63f26fb095..8fb65d24584803 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -573,11 +573,11 @@ config PWM_SUN4I will be called pwm-sun4i. config PWM_SUN8I_V536 - tristate "Allwinner SUN8I_V536 PWM support" + tristate "Allwinner SUN8I V536 enhanced PWM support" depends on ARCH_SUNXI || COMPILE_TEST depends on HAS_IOMEM && COMMON_CLK help - Enhanced PWM framework driver for Allwinner R818, A133, R329, + Enhanced PWM framework driver for Allwinner A133, D1, R329, R818, V536 and V833 SoCs. To compile this driver as a module, choose M here: the module diff --git a/drivers/pwm/pwm-sun8i-v536.c b/drivers/pwm/pwm-sun8i-v536.c index 52101df6bd41b1..ab302c6cb4ee6c 100644 --- a/drivers/pwm/pwm-sun8i-v536.c +++ b/drivers/pwm/pwm-sun8i-v536.c @@ -373,12 +373,8 @@ static int sun8i_pwm_probe(struct platform_device *pdev) static int sun8i_pwm_remove(struct platform_device *pdev) { struct sun8i_pwm_chip *pc = platform_get_drvdata(pdev); - int ret; - - ret = pwmchip_remove(&pc->chip); - if (ret) - return ret; + pwmchip_remove(&pc->chip); clk_disable_unprepare(pc->clk); reset_control_assert(pc->rst_clk); From 73e652c37f72dfe2578ee800de5acf04f9098919 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 6 Jun 2021 10:56:25 -0500 Subject: [PATCH 105/153] pwm: sun8i-v536: Add support for the Allwinner D1 Signed-off-by: Samuel Holland --- drivers/pwm/pwm-sun8i-v536.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pwm/pwm-sun8i-v536.c b/drivers/pwm/pwm-sun8i-v536.c index ab302c6cb4ee6c..732f15c88b676c 100644 --- a/drivers/pwm/pwm-sun8i-v536.c +++ b/drivers/pwm/pwm-sun8i-v536.c @@ -285,6 +285,10 @@ static const struct sun8i_pwm_data sun8i_pwm_data_c9 = { .npwm = 9, }; +static const struct sun8i_pwm_data sun20i_pwm_data_c8 = { + .npwm = 8, +}; + static const struct sun8i_pwm_data sun50i_pwm_data_c16 = { .npwm = 16, }; @@ -293,6 +297,9 @@ static const struct of_device_id sun8i_pwm_dt_ids[] = { { .compatible = "allwinner,sun8i-v536-pwm", .data = &sun8i_pwm_data_c9, + }, { + .compatible = "allwinner,sun20i-d1-pwm", + .data = &sun20i_pwm_data_c8, }, { .compatible = "allwinner,sun50i-r818-pwm", .data = &sun50i_pwm_data_c16, From 30218b7b665c24eac1acd79575364a1ec9d9599e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 22:24:52 -0500 Subject: [PATCH 106/153] riscv: dts: allwinner: d1: Add PWM support Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 35 ++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi index af5fbf79dd17cc..66b2548c72e97a 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -183,6 +183,30 @@ function = "emac"; }; + /omit-if-no-ref/ + pwm0_pd16_pin: pwm0-pd16-pin { + pins = "PD16"; + function = "pwm0"; + }; + + /omit-if-no-ref/ + pwm2_pd18_pin: pwm2-pd18-pin { + pins = "PD18"; + function = "pwm2"; + }; + + /omit-if-no-ref/ + pwm4_pd20_pin: pwm4-pd20-pin { + pins = "PD20"; + function = "pwm4"; + }; + + /omit-if-no-ref/ + pwm7_pd22_pin: pwm7-pd22-pin { + pins = "PD22"; + function = "pwm7"; + }; + /omit-if-no-ref/ spi0_pins: spi0-pins { pins = "PC2", "PC3", "PC4", "PC5", "PC6", "PC7"; @@ -220,6 +244,17 @@ }; }; + pwm: pwm@2000c00 { + compatible = "allwinner,sun20i-d1-pwm"; + reg = <0x2000c00 0x400>; + interrupts = <34 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_PWM>, <&osc24M>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_PWM>; + status = "disabled"; + #pwm-cells = <3>; + }; + ccu: clock-controller@2001000 { compatible = "allwinner,sun20i-d1-ccu"; reg = <0x2001000 0x1000>; From 3facd167644de86060df3b9b934f8aca357f2317 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 22:25:40 -0500 Subject: [PATCH 107/153] riscv: dts: allwinner: d1: Hook up PWM-controlled CPU voltage regulators Signed-off-by: Samuel Holland --- .../allwinner/sun20i-d1-clockworkpi-v3.14.dts | 19 +++++++++++-------- .../sun20i-d1-dongshan-nezha-stu.dts | 19 +++++++++++-------- .../boot/dts/allwinner/sun20i-d1-nezha.dts | 19 +++++++++++-------- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts index 74b4b6d8363ab4..e43ba03a28a7e0 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts @@ -22,16 +22,13 @@ stdout-path = "serial0:115200n8"; }; - /* - * This regulator is PWM-controlled, but the PWM controller is not - * yet supported, so fix the regulator to its default voltage. - */ reg_vdd_cpu: vdd-cpu { - compatible = "regulator-fixed"; + compatible = "pwm-regulator"; + pwms = <&pwm 0 50000 0>; + pwm-supply = <®_vcc>; regulator-name = "vdd-cpu"; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - vin-supply = <®_vcc>; + regulator-min-microvolt = <810000>; + regulator-max-microvolt = <1160000>; }; wifi_pwrseq: wifi-pwrseq { @@ -207,6 +204,12 @@ vcc-pg-supply = <®_ldoa>; }; +&pwm { + pinctrl-0 = <&pwm0_pd16_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + &uart0 { pinctrl-0 = <&uart0_pb8_pins>; pinctrl-names = "default"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts index c3d06dfaa7c3c6..460255b6341840 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts @@ -43,16 +43,13 @@ vin-supply = <®_vcc>; }; - /* - * This regulator is PWM-controlled, but the PWM controller is not - * yet supported, so fix the regulator to its default voltage. - */ reg_vdd_cpu: vdd-cpu { - compatible = "regulator-fixed"; + compatible = "pwm-regulator"; + pwms = <&pwm 0 50000 0>; + pwm-supply = <®_vcc>; regulator-name = "vdd-cpu"; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - vin-supply = <®_vcc>; + regulator-min-microvolt = <810000>; + regulator-max-microvolt = <1160000>; }; }; @@ -95,6 +92,12 @@ status = "okay"; }; +&pwm { + pinctrl-0 = <&pwm0_pd16_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + &uart0 { pinctrl-0 = <&uart0_pb8_pins>; pinctrl-names = "default"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts index 21c0a7e67e76c9..e60d76f88d82cf 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts @@ -36,16 +36,13 @@ vin-supply = <®_vcc>; }; - /* - * This regulator is PWM-controlled, but the PWM controller is not - * yet supported, so fix the regulator to its default voltage. - */ reg_vdd_cpu: vdd-cpu { - compatible = "regulator-fixed"; + compatible = "pwm-regulator"; + pwms = <&pwm 0 50000 0>; + pwm-supply = <®_vcc>; regulator-name = "vdd-cpu"; - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1100000>; - vin-supply = <®_vcc>; + regulator-min-microvolt = <810000>; + regulator-max-microvolt = <1160000>; }; wifi_pwrseq: wifi-pwrseq { @@ -156,6 +153,12 @@ status = "okay"; }; +&pwm { + pinctrl-0 = <&pwm0_pd16_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + &spi0 { pinctrl-0 = <&spi0_pins>; pinctrl-names = "default"; From 3c8eb8286593221b5e0491771f27ac605c2ad43f Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 22:57:13 -0500 Subject: [PATCH 108/153] riscv: dts: allwinner: mangopi-mq-pro: Add PWM LED Signed-off-by: Samuel Holland --- .../boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts index 61a26d3db521ff..9364829f486fd7 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts @@ -4,6 +4,7 @@ /dts-v1/; #include +#include #include "sun20i-d1.dtsi" #include "sun20i-d1-common-regulators.dtsi" @@ -22,6 +23,16 @@ stdout-path = "serial0:115200n8"; }; + leds { + compatible = "pwm-leds"; + + led { + function = LED_FUNCTION_STATUS; + max-brightness = <255>; + pwms = <&pwm 2 50000 0>; + }; + }; + reg_avdd2v8: avdd2v8 { compatible = "regulator-fixed"; regulator-name = "avdd2v8"; From cdcc35d4ebd326619292c627c8f42eddc0c53a4e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 7 Aug 2022 10:46:43 -0500 Subject: [PATCH 109/153] drm/sun4i: dsi: Allow panel attach before card registration Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index b4dfa166eccdfa..d4a9e8750dc6aa 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -967,13 +967,12 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host, if (IS_ERR(panel)) return PTR_ERR(panel); - if (!dsi->drm || !dsi->drm->registered) - return -EPROBE_DEFER; dsi->panel = panel; dsi->device = device; - drm_kms_helper_hotplug_event(dsi->drm); + if (dsi->drm && dsi->drm->registered) + drm_kms_helper_hotplug_event(dsi->drm); dev_info(host->dev, "Attached device %s\n", device->name); @@ -988,7 +987,8 @@ static int sun6i_dsi_detach(struct mipi_dsi_host *host, dsi->panel = NULL; dsi->device = NULL; - drm_kms_helper_hotplug_event(dsi->drm); + if (dsi->drm && dsi->drm->registered) + drm_kms_helper_hotplug_event(dsi->drm); return 0; } From debfc8ee5256a25a61f4b3ac081568e0ae1f5e9b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 27 Apr 2022 18:50:01 -0500 Subject: [PATCH 110/153] drm/sun4i: mixer: Remove unused CMA headers Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_mixer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index 875a1156c04ea5..bf4206619f6ad7 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -16,9 +16,8 @@ #include #include -#include -#include #include +#include #include #include "sun4i_drv.h" From 1053a12d442dc23dd3e3b9655d34a939e098f9ac Mon Sep 17 00:00:00 2001 From: Roman Beranek Date: Wed, 25 Nov 2020 13:07:35 +0100 Subject: [PATCH 111/153] drm/sun4i: decouple TCON_DCLK_DIV value from pll_mipi/dotclock ratio Observations showed that an actual refresh rate differs from the intended. Specifically, in case of 4-lane panels it was reduced by 1/3, and in case of 2-lane panels by 2/3. BSP code apparently distinguishes between a `dsi_div` and a 'tcon inner div'. While this 'inner' divider is under DSI always 4, the `dsi_div` is defined as a number of bits per pixel over a number of DSI lanes. This value is then involved in setting the rate of PLL_MIPI. I couldn't really figure out how to fit this into the dotclock driver, so I opted for this hack where the requested rate is adjusted in such a way that the sun4i_dotclock driver can remain untouched. Signed-off-by: Roman Beranek Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 44 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 2ee158aaeb9e9d..0a31fd34748dc2 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -290,18 +290,6 @@ static int sun4i_tcon_get_clk_delay(const struct drm_display_mode *mode, return delay; } -static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon, - const struct drm_display_mode *mode) -{ - /* Configure the dot clock */ - clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); - - /* Set the resolution */ - regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, - SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | - SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); -} - static void sun4i_tcon0_mode_set_dithering(struct sun4i_tcon *tcon, const struct drm_connector *connector) { @@ -364,12 +352,18 @@ static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon, u8 bpp = mipi_dsi_pixel_format_to_bpp(device->format); u8 lanes = device->lanes; u32 block_space, start_delay; - u32 tcon_div; tcon->dclk_min_div = SUN6I_DSI_TCON_DIV; tcon->dclk_max_div = SUN6I_DSI_TCON_DIV; - sun4i_tcon0_mode_set_common(tcon, mode); + /* Configure the dot clock */ + clk_set_rate(tcon->dclk, mode->crtc_clock * 1000 + * bpp / (lanes * SUN6I_DSI_TCON_DIV)); + + /* Set the resolution */ + regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, + SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | + SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); /* Set dithering if needed */ sun4i_tcon0_mode_set_dithering(tcon, sun4i_tcon_get_connector(encoder)); @@ -393,9 +387,7 @@ static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon, * The datasheet says that this should be set higher than 20 * * pixel cycle, but it's not clear what a pixel cycle is. */ - regmap_read(tcon->regs, SUN4I_TCON0_DCLK_REG, &tcon_div); - tcon_div &= GENMASK(6, 0); - block_space = mode->htotal * bpp / (tcon_div * lanes); + block_space = mode->htotal * bpp / (SUN6I_DSI_TCON_DIV * lanes); block_space -= mode->hdisplay + 40; regmap_write(tcon->regs, SUN4I_TCON0_CPU_TRI0_REG, @@ -437,7 +429,14 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon, tcon->dclk_min_div = 7; tcon->dclk_max_div = 7; - sun4i_tcon0_mode_set_common(tcon, mode); + + /* Configure the dot clock */ + clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); + + /* Set the resolution */ + regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, + SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | + SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); /* Set dithering if needed */ sun4i_tcon0_mode_set_dithering(tcon, sun4i_tcon_get_connector(encoder)); @@ -514,7 +513,14 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, tcon->dclk_min_div = tcon->quirks->dclk_min_div; tcon->dclk_max_div = 127; - sun4i_tcon0_mode_set_common(tcon, mode); + + /* Configure the dot clock */ + clk_set_rate(tcon->dclk, mode->crtc_clock * 1000); + + /* Set the resolution */ + regmap_write(tcon->regs, SUN4I_TCON0_BASIC0_REG, + SUN4I_TCON0_BASIC0_X(mode->crtc_hdisplay) | + SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay)); /* Set dithering if needed */ sun4i_tcon0_mode_set_dithering(tcon, connector); From 168c6a16d77614304b9c8e8aa0fa84f6f9fcde42 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 7 Aug 2022 21:09:39 -0500 Subject: [PATCH 112/153] drm/sun4i: tcon: Always protect the LCD dotclock rate This handles the case where multiple CRTCs get their .mode_set function called during the same atomic commit, before rate protection is applied by enabling the CRTC. Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun4i_dotclock.c | 4 ++++ drivers/gpu/drm/sun4i/sun4i_tcon.c | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_dotclock.c b/drivers/gpu/drm/sun4i/sun4i_dotclock.c index 417ade3d2565d9..c1305edb9553aa 100644 --- a/drivers/gpu/drm/sun4i/sun4i_dotclock.c +++ b/drivers/gpu/drm/sun4i/sun4i_dotclock.c @@ -6,6 +6,7 @@ * Maxime Ripard */ +#include #include #include @@ -194,12 +195,15 @@ int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon) if (IS_ERR(tcon->dclk)) return PTR_ERR(tcon->dclk); + clk_rate_exclusive_get(tcon->dclk); + return 0; } EXPORT_SYMBOL(sun4i_dclk_create); int sun4i_dclk_free(struct sun4i_tcon *tcon) { + clk_rate_exclusive_put(tcon->dclk); clk_unregister(tcon->dclk); return 0; } diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 0a31fd34748dc2..c64e3a875462d0 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -107,9 +107,11 @@ static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel, if (enabled) { clk_prepare_enable(clk); - clk_rate_exclusive_get(clk); + if (clk != tcon->dclk) + clk_rate_exclusive_get(clk); } else { - clk_rate_exclusive_put(clk); + if (clk != tcon->dclk) + clk_rate_exclusive_put(clk); clk_disable_unprepare(clk); } } From fcd9f470d2844ec7de47061e98429f42f42e36da Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Tue, 31 Dec 2019 18:35:24 +0530 Subject: [PATCH 113/153] drm/sun4i: tcon_top: Register reset, clock gates in probe TCON TOP is processing clock gates and reset control for TV0, TV1 and DSI channels during bind and release the same during unbind component ops. The usual DSI initialization would setup all controller clocks along with DPHY clocking during probe. Since the actual clock gates (along with DSI clock gate) are initialized during ton top bind, the DPHY is failed to get the DSI clock during that time. To solve, this circular dependency move the reset control, clock gate registration from bind to probe and release the same from unbind to remove. This eventually give a chance DPHY to initialize the DSI clock gate. Signed-off-by: Jagan Teki Signed-off-by: Samuel Holland --- drivers/gpu/drm/sun4i/sun8i_tcon_top.c | 42 ++++++++++++++------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c index da97682b683514..0ab1ddaffc5535 100644 --- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c +++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c @@ -124,14 +124,29 @@ static struct clk_hw *sun8i_tcon_top_register_gate(struct device *dev, static int sun8i_tcon_top_bind(struct device *dev, struct device *master, void *data) { - struct platform_device *pdev = to_platform_device(dev); + return 0; +} + +static void sun8i_tcon_top_unbind(struct device *dev, struct device *master, + void *data) +{ +} + +static const struct component_ops sun8i_tcon_top_ops = { + .bind = sun8i_tcon_top_bind, + .unbind = sun8i_tcon_top_unbind, +}; + +static int sun8i_tcon_top_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; struct clk_hw_onecell_data *clk_data; struct sun8i_tcon_top *tcon_top; const struct sun8i_tcon_top_quirks *quirks; void __iomem *regs; int ret, i; - quirks = of_device_get_match_data(&pdev->dev); + quirks = of_device_get_match_data(dev); tcon_top = devm_kzalloc(dev, sizeof(*tcon_top), GFP_KERNEL); if (!tcon_top) @@ -222,7 +237,7 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, dev_set_drvdata(dev, tcon_top); - return 0; + return component_add(dev, &sun8i_tcon_top_ops); err_unregister_gates: for (i = 0; i < CLK_NUM; i++) @@ -235,13 +250,15 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, return ret; } -static void sun8i_tcon_top_unbind(struct device *dev, struct device *master, - void *data) +static int sun8i_tcon_top_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev); struct clk_hw_onecell_data *clk_data = tcon_top->clk_data; int i; + component_del(dev, &sun8i_tcon_top_ops); + of_clk_del_provider(dev->of_node); for (i = 0; i < CLK_NUM; i++) if (clk_data->hws[i]) @@ -249,21 +266,6 @@ static void sun8i_tcon_top_unbind(struct device *dev, struct device *master, clk_disable_unprepare(tcon_top->bus); reset_control_assert(tcon_top->rst); -} - -static const struct component_ops sun8i_tcon_top_ops = { - .bind = sun8i_tcon_top_bind, - .unbind = sun8i_tcon_top_unbind, -}; - -static int sun8i_tcon_top_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &sun8i_tcon_top_ops); -} - -static int sun8i_tcon_top_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &sun8i_tcon_top_ops); return 0; } From 7cda25fada1891e8f2da38aad507731c1592effe Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 22:46:28 -0500 Subject: [PATCH 114/153] riscv: dts: allwinner: lichee-rv-86-panel-480p: Add panel Signed-off-by: Samuel Holland --- .../sun20i-d1-lichee-rv-86-panel-480p.dts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-480p.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-480p.dts index 4df8ffb715618b..33d79489e48b1b 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-480p.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel-480p.dts @@ -7,6 +7,36 @@ model = "Sipeed Lichee RV 86 Panel (480p)"; compatible = "sipeed,lichee-rv-86-panel-480p", "sipeed,lichee-rv", "allwinner,sun20i-d1"; + + backlight: backlight { + compatible = "pwm-backlight"; + power-supply = <®_vcc>; + pwms = <&pwm 7 50000 0>; + }; + + spi { + compatible = "spi-gpio"; + cs-gpios = <&pio 4 14 GPIO_ACTIVE_LOW>; /* PE14 */ + mosi-gpios = <&pio 4 12 GPIO_ACTIVE_HIGH>; /* PE12 */ + sck-gpios = <&pio 4 15 GPIO_ACTIVE_HIGH>; /* PE15 */ + num-chipselects = <1>; + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "sitronix,st7701s"; + reg = <0>; + backlight = <&backlight>; + reset-gpios = <&pio 6 13 GPIO_ACTIVE_LOW>; /* PG13 */ + spi-3wire; + + port { + panel_in_tcon_lcd0: endpoint { + remote-endpoint = <&tcon_lcd0_out_panel>; + }; + }; + }; + }; }; &i2c2 { @@ -27,3 +57,20 @@ wakeup-source; }; }; + +&pwm { + pinctrl-0 = <&pwm7_pd22_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + +&tcon_lcd0 { + pinctrl-0 = <&lcd_rgb666_pins>; + pinctrl-names = "default"; +}; + +&tcon_lcd0_out { + tcon_lcd0_out_panel: endpoint { + remote-endpoint = <&panel_in_tcon_lcd0>; + }; +}; From ecd29173f84064722709c9ed5567c142007f4c72 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 7 Aug 2022 10:59:29 -0500 Subject: [PATCH 115/153] riscv: dts: allwinner: d1: Add DSI pipeline Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 51 +++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi index 66b2548c72e97a..55fdff18edac86 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -123,6 +123,14 @@ #gpio-cells = <3>; #interrupt-cells = <3>; + /omit-if-no-ref/ + dsi_4lane_pins: dsi-4lane-pins { + pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5", + "PD6", "PD7", "PD8", "PD9"; + drive-strength = <30>; + function = "dsi"; + }; + /omit-if-no-ref/ i2c0_pb10_pins: i2c0-pb10-pins { pins = "PB10", "PB11"; @@ -864,13 +872,47 @@ }; }; + dsi: dsi@5450000 { + compatible = "allwinner,sun20i-d1-mipi-dsi", + "allwinner,sun50i-a100-mipi-dsi"; + reg = <0x5450000 0x1000>; + interrupts = <108 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_MIPI_DSI>, + <&tcon_top CLK_TCON_TOP_DSI>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_MIPI_DSI>; + phys = <&dphy>; + phy-names = "dphy"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + + port { + dsi_in_tcon_lcd0: endpoint { + remote-endpoint = <&tcon_lcd0_out_dsi>; + }; + }; + }; + + dphy: phy@5451000 { + compatible = "allwinner,sun20i-d1-mipi-dphy", + "allwinner,sun50i-a100-mipi-dphy"; + reg = <0x5451000 0x1000>; + interrupts = <108 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_MIPI_DSI>, + <&ccu CLK_MIPI_DSI>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_MIPI_DSI>; + #phy-cells = <0>; + }; + tcon_top: tcon-top@5460000 { compatible = "allwinner,sun20i-d1-tcon-top"; reg = <0x5460000 0x1000>; clocks = <&ccu CLK_BUS_DPSS_TOP>, <&ccu CLK_TCON_TV>, <&ccu CLK_TVE>, - <&ccu CLK_MIPI_DSI>; + <&ccu CLK_TCON_LCD0>; clock-names = "bus", "tcon-tv0", "tve0", @@ -987,6 +1029,13 @@ tcon_lcd0_out: port@1 { reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + tcon_lcd0_out_dsi: endpoint@1 { + reg = <1>; + remote-endpoint = <&dsi_in_tcon_lcd0>; + }; }; }; }; From fb87f8c5c6aaf4e8f57d17b2107f7af1a98e8a35 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 22:29:03 -0500 Subject: [PATCH 116/153] riscv: dts: allwinner: devterm: Add DSI panel and backlight Signed-off-by: Samuel Holland --- .../allwinner/sun20i-d1-clockworkpi-v3.14.dts | 8 +++++++- .../dts/allwinner/sun20i-d1-devterm-v3.14.dts | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts index e43ba03a28a7e0..3d641a641bab9c 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts @@ -22,6 +22,12 @@ stdout-path = "serial0:115200n8"; }; + backlight: backlight { + compatible = "pwm-backlight"; + power-supply = <®_vcc>; + pwms = <&pwm 4 50000 0>; /* PD20/GPIO9 */ + }; + reg_vdd_cpu: vdd-cpu { compatible = "pwm-regulator"; pwms = <&pwm 0 50000 0>; @@ -205,7 +211,7 @@ }; &pwm { - pinctrl-0 = <&pwm0_pd16_pin>; + pinctrl-0 = <&pwm0_pd16_pin>, <&pwm4_pd20_pin>; pinctrl-names = "default"; status = "okay"; }; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts index 690bfa35a54838..3896a31b7fd34b 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts @@ -35,3 +35,23 @@ }; }; }; + +&de { + status = "okay"; +}; + +&dsi { + pinctrl-0 = <&dsi_4lane_pins>; + pinctrl-names = "default"; + status = "okay"; + + panel@0 { + compatible = "clockwork,cwd686"; + reg = <0>; + backlight = <&backlight>; + reset-gpios = <&pio 3 19 GPIO_ACTIVE_LOW>; /* PD19/GPIO8 */ + rotation = <90>; + iovcc-supply = <®_dcdc3>; + vci-supply = <®_aldo2>; + }; +}; From 04acecdfd9cb73db2bd5afba2be8a5d251fd097f Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 6 Aug 2022 12:55:57 -0500 Subject: [PATCH 117/153] riscv: dts: allwinner: d1: Add HDMI pipeline Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi index 55fdff18edac86..a3235d428241d3 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -990,6 +990,10 @@ tcon_top_hdmi_out: port@5 { reg = <5>; + + tcon_top_hdmi_out_hdmi: endpoint { + remote-endpoint = <&hdmi_in_tcon_top>; + }; }; }; }; @@ -1080,6 +1084,51 @@ }; }; + hdmi: hdmi@5500000 { + compatible = "allwinner,sun20i-d1-dw-hdmi"; + reg = <0x5500000 0x10000>; + reg-io-width = <1>; + interrupts = <109 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_HDMI>, + <&ccu CLK_HDMI_24M>, + <&tcon_top CLK_TCON_TOP_TV0>, + <&ccu CLK_HDMI_CEC>; + clock-names = "iahb", "isfr", "tmds", "cec"; + resets = <&ccu RST_BUS_HDMI_MAIN>, <&ccu RST_BUS_HDMI_SUB>; + reset-names = "ctrl", "sub"; + phys = <&hdmi_phy>; + phy-names = "phy"; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi_in_tcon_top: endpoint { + remote-endpoint = <&tcon_top_hdmi_out_hdmi>; + }; + }; + + hdmi_out: port@1 { + reg = <1>; + }; + }; + }; + + hdmi_phy: phy@5510000 { + compatible = "allwinner,sun20i-d1-hdmi-phy"; + reg = <0x5510000 0x10000>; + clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI_24M>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_HDMI_MAIN>; + reset-names = "phy"; + status = "disabled"; + #phy-cells = <0>; + }; + riscv_wdt: watchdog@6011000 { compatible = "allwinner,sun20i-d1-wdt"; reg = <0x6011000 0x20>; From 2a49fc31300d12916a13fd1d33228ce2da6c53be Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:50:57 -0500 Subject: [PATCH 118/153] ASoC: sun4i-i2s: Also set capture DMA width Signed-off-by: Samuel Holland --- sound/soc/sunxi/sun4i-i2s.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 6028871825baea..b0f7edf56532f6 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -633,6 +633,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, params_physical_width(params)); return -EINVAL; } + i2s->capture_dma_data.addr_width = width; i2s->playback_dma_data.addr_width = width; sr = i2s->variant->get_sr(word_size); From d64d3327fc4177d40275b44dc8dfcbc5db1a91da Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 6 Aug 2022 12:56:35 -0500 Subject: [PATCH 119/153] riscv: dts: allwinner: d1: Enable HDMI on supported boards Signed-off-by: Samuel Holland --- .../allwinner/sun20i-d1-clockworkpi-v3.14.dts | 29 +++++++++++++++++++ .../sun20i-d1-common-regulators.dtsi | 4 +++ .../dts/allwinner/sun20i-d1-devterm-v3.14.dts | 4 --- .../sun20i-d1-dongshan-nezha-stu.dts | 29 +++++++++++++++++++ .../allwinner/sun20i-d1-lichee-rv-dock.dts | 29 +++++++++++++++++++ .../allwinner/sun20i-d1-mangopi-mq-pro.dts | 29 +++++++++++++++++++ .../boot/dts/allwinner/sun20i-d1-nezha.dts | 29 +++++++++++++++++++ 7 files changed, 149 insertions(+), 4 deletions(-) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts index 3d641a641bab9c..54f913e4b5473b 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts @@ -28,6 +28,17 @@ pwms = <&pwm 4 50000 0>; /* PD20/GPIO9 */ }; + hdmi_connector: connector { + compatible = "hdmi-connector"; + type = "d"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_out_connector>; + }; + }; + }; + reg_vdd_cpu: vdd-cpu { compatible = "pwm-regulator"; pwms = <&pwm 0 50000 0>; @@ -47,10 +58,28 @@ cpu-supply = <®_vdd_cpu>; }; +&de { + status = "okay"; +}; + &ehci1 { status = "okay"; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_connector: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; +}; + +&hdmi_phy { + status = "okay"; +}; + &i2c0 { pinctrl-0 = <&i2c0_pb10_pins>; pinctrl-names = "default"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi index 11bdc81e38d5b2..fdd7ba44dee1aa 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi @@ -18,6 +18,10 @@ }; }; +&hdmi { + hvcc-supply = <®_ldoa>; +}; + &lradc { vref-supply = <®_aldo>; }; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts index 3896a31b7fd34b..a926e1023b9971 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-devterm-v3.14.dts @@ -36,10 +36,6 @@ }; }; -&de { - status = "okay"; -}; - &dsi { pinctrl-0 = <&dsi_4lane_pins>; pinctrl-names = "default"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts index 460255b6341840..b03f29395c429e 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-dongshan-nezha-stu.dts @@ -23,6 +23,17 @@ stdout-path = "serial0:115200n8"; }; + hdmi_connector: connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_out_connector>; + }; + }; + }; + leds { compatible = "gpio-leds"; @@ -57,6 +68,10 @@ cpu-supply = <®_vdd_cpu>; }; +&de { + status = "okay"; +}; + &ehci0 { status = "okay"; }; @@ -70,6 +85,20 @@ status = "okay"; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_connector: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; +}; + +&hdmi_phy { + status = "okay"; +}; + &mdio { ext_rgmii_phy: ethernet-phy@1 { compatible = "ethernet-phy-ieee802.3-c22"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts index 02d13e987e0222..934a0db3ae64c7 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts @@ -15,16 +15,45 @@ ethernet1 = &rtl8723ds; }; + hdmi_connector: connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_out_connector>; + }; + }; + }; + wifi_pwrseq: wifi-pwrseq { compatible = "mmc-pwrseq-simple"; reset-gpios = <&pio 6 12 GPIO_ACTIVE_LOW>; /* PG12 */ }; }; +&de { + status = "okay"; +}; + &ehci1 { status = "okay"; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_connector: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; +}; + +&hdmi_phy { + status = "okay"; +}; + &ledc { pinctrl-0 = <&ledc_pc0_pin>; pinctrl-names = "default"; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts index 9364829f486fd7..d1b4cc844c9fd4 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-mangopi-mq-pro.dts @@ -23,6 +23,17 @@ stdout-path = "serial0:115200n8"; }; + hdmi_connector: connector { + compatible = "hdmi-connector"; + type = "c"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_out_connector>; + }; + }; + }; + leds { compatible = "pwm-leds"; @@ -67,10 +78,28 @@ cpu-supply = <®_vdd_cpu>; }; +&de { + status = "okay"; +}; + &ehci1 { status = "okay"; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_connector: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; +}; + +&hdmi_phy { + status = "okay"; +}; + &mmc0 { bus-width = <4>; cd-gpios = <&pio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts index e60d76f88d82cf..260a60eec02597 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts @@ -26,6 +26,17 @@ stdout-path = "serial0:115200n8"; }; + hdmi_connector: connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_out_connector>; + }; + }; + }; + reg_usbvbus: usbvbus { compatible = "regulator-fixed"; regulator-name = "usbvbus"; @@ -55,6 +66,10 @@ cpu-supply = <®_vdd_cpu>; }; +&de { + status = "okay"; +}; + &ehci0 { status = "okay"; }; @@ -72,6 +87,20 @@ status = "okay"; }; +&hdmi { + status = "okay"; +}; + +&hdmi_out { + hdmi_out_connector: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; +}; + +&hdmi_phy { + status = "okay"; +}; + &i2c2 { pinctrl-0 = <&i2c2_pb0_pins>; pinctrl-names = "default"; From c63aedc13a2981b5a61a1e7550b5539c63241922 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 13 Jun 2021 23:52:47 -0500 Subject: [PATCH 120/153] [WIP] ASoC: sun4i-spdif: Add support for separate resets Signed-off-by: Samuel Holland --- sound/soc/sunxi/sun4i-spdif.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index bcceebca915ac8..72ed6b9a7f6983 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -28,10 +28,11 @@ #include #define SUN4I_SPDIF_CTL (0x00) + #define SUN4I_SPDIF_CTL_RST_RX BIT(12) #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */ #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2) #define SUN4I_SPDIF_CTL_GEN BIT(1) - #define SUN4I_SPDIF_CTL_RESET BIT(0) + #define SUN4I_SPDIF_CTL_RST_TX BIT(0) #define SUN4I_SPDIF_TXCFG (0x04) #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31) @@ -196,7 +197,7 @@ static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) const struct sun4i_spdif_quirks *quirks = host->quirks; /* soft reset SPDIF */ - regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET); + regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RST_TX); /* flush TX FIFO */ regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, From c28401360f4923ca142b18c33a4b69fdb8ec0c1b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 7 Aug 2022 10:58:02 -0500 Subject: [PATCH 121/153] dt-bindings: display: sun4i-tcon: Add external LVDS PHY A100 and D1 use the same "combo" PHY for LVDS0 and DSI. Signed-off-by: Samuel Holland --- .../bindings/display/allwinner,sun4i-a10-tcon.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml index 4a92a4c7dcd70e..9803c05e744e56 100644 --- a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml +++ b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-tcon.yaml @@ -80,6 +80,13 @@ properties: dmas: maxItems: 1 + phys: + maxItems: 1 + + phy-names: + items: + - const: "lvds0" + resets: anyOf: - items: From 819ebc2b7a6d4f8aacd8981cd2273643e2a89bf1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 12 Jun 2021 23:42:48 -0500 Subject: [PATCH 122/153] ASoC: sun20i-codec: New driver for D1 internal codec Signed-off-by: Samuel Holland --- sound/soc/sunxi/Kconfig | 6 + sound/soc/sunxi/Makefile | 1 + sound/soc/sunxi/sun20i-codec.c | 942 +++++++++++++++++++++++++++++++++ 3 files changed, 949 insertions(+) create mode 100644 sound/soc/sunxi/sun20i-codec.c diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig index ddcaaa98d3cb33..c4ef1f41b2449c 100644 --- a/sound/soc/sunxi/Kconfig +++ b/sound/soc/sunxi/Kconfig @@ -30,6 +30,12 @@ config SND_SUN8I_CODEC_ANALOG Say Y or M if you want to add support for the analog controls for the codec embedded in newer Allwinner SoCs. +config SND_SUN20I_CODEC + tristate "Allwinner D1 (sun20i) Audio Codec" + depends on ARCH_SUNXI || COMPILE_TEST + help + Say Y or M to add support for the audio codec in Allwinner D1 SoC. + config SND_SUN50I_CODEC_ANALOG tristate "Allwinner sun50i Codec Analog Controls Support" depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile index a86be340a07610..7bbe2526e16ee3 100644 --- a/sound/soc/sunxi/Makefile +++ b/sound/soc/sunxi/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o +obj-$(CONFIG_SND_SUN20I_CODEC) += sun20i-codec.o obj-$(CONFIG_SND_SUN50I_CODEC_ANALOG) += sun50i-codec-analog.o obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o diff --git a/sound/soc/sunxi/sun20i-codec.c b/sound/soc/sunxi/sun20i-codec.c new file mode 100644 index 00000000000000..c58dde3eba7cd0 --- /dev/null +++ b/sound/soc/sunxi/sun20i-codec.c @@ -0,0 +1,942 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define SUN20I_CODEC_DAC_DPC 0x0000 +#define SUN20I_CODEC_DAC_DPC_EN_DA 31 +#define SUN20I_CODEC_DAC_DPC_HPF_EN 18 +#define SUN20I_CODEC_DAC_DPC_DVOL 12 +#define SUN20I_CODEC_DAC_VOL_CTRL 0x0004 +#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL 16 +#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_L 8 +#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_R 0 +#define SUN20I_CODEC_DAC_FIFOC 0x0010 +#define SUN20I_CODEC_DAC_FIFOC_FS 29 +#define SUN20I_CODEC_DAC_FIFOC_FIFO_MODE 24 +#define SUN20I_CODEC_DAC_FIFOC_DRQ_CLR_CNT 21 +#define SUN20I_CODEC_DAC_FIFOC_TRIG_LEVEL 8 +#define SUN20I_CODEC_DAC_FIFOC_MONO_EN 6 +#define SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS 5 +#define SUN20I_CODEC_DAC_FIFOC_DRQ_EN 4 +#define SUN20I_CODEC_DAC_FIFOC_FIFO_FLUSH 0 +#define SUN20I_CODEC_DAC_TXDATA 0x0020 +#define SUN20I_CODEC_DAC_DEBUG 0x0028 +#define SUN20I_CODEC_DAC_DEBUG_DA_SWP 6 +#define SUN20I_CODEC_DAC_ADDA_LOOP_MODE 0 + +#define SUN20I_CODEC_ADC_FIFOC 0x0030 +#define SUN20I_CODEC_ADC_FIFOC_FS 29 +#define SUN20I_CODEC_ADC_FIFOC_EN_AD 28 +#define SUN20I_CODEC_ADC_FIFOC_FIFO_MODE 24 +#define SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS 16 +#define SUN20I_CODEC_ADC_FIFOC_TRIG_LEVEL 4 +#define SUN20I_CODEC_ADC_FIFOC_DRQ_EN 3 +#define SUN20I_CODEC_ADC_FIFOC_FIFO_FLUSH 0 +#define SUN20I_CODEC_ADC_VOL_CTRL 0x0034 +#define SUN20I_CODEC_ADC_VOL_CTRL_ADC3_VOL 16 +#define SUN20I_CODEC_ADC_VOL_CTRL_ADC2_VOL 8 +#define SUN20I_CODEC_ADC_VOL_CTRL_ADC1_VOL 0 +#define SUN20I_CODEC_ADC_RXDATA 0x0040 +#define SUN20I_CODEC_ADC_DEBUG 0x004c +#define SUN20I_CODEC_ADC_DEBUG_AD_SWP1 24 +#define SUN20I_CODEC_ADC_DIG_CTRL 0x0050 +#define SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN 16 +#define SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN 0 + +#define SUN20I_CODEC_DAC_DAP_CTRL 0x00f0 +#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_EN 31 +#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_DRC_EN 29 +#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_HPF_EN 28 + +#define SUN20I_CODEC_ADC_DAP_CTRL 0x00f8 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_EN 31 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_DRC_EN 29 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_HPF_EN 28 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_EN 27 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_DRC_EN 25 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_HPF_EN 24 + +#define SUN20I_CODEC_ADC1 0x0300 +#define SUN20I_CODEC_ADC1_ADC1_EN 31 +#define SUN20I_CODEC_ADC1_MICIN1_PGA_EN 30 +#define SUN20I_CODEC_ADC1_ADC1_DITHER_EN 29 +#define SUN20I_CODEC_ADC1_MICIN1_SIN_EN 28 +#define SUN20I_CODEC_ADC1_FMINL_EN 27 +#define SUN20I_CODEC_ADC1_FMINL_GAIN 26 +#define SUN20I_CODEC_ADC1_DITHER_LEVEL 24 +#define SUN20I_CODEC_ADC1_LINEINL_EN 23 +#define SUN20I_CODEC_ADC1_LINEINL_GAIN 22 +#define SUN20I_CODEC_ADC1_ADC1_PGA_GAIN 8 +#define SUN20I_CODEC_ADC2 0x0304 +#define SUN20I_CODEC_ADC2_ADC2_EN 31 +#define SUN20I_CODEC_ADC2_MICIN2_PGA_EN 30 +#define SUN20I_CODEC_ADC2_ADC2_DITHER_EN 29 +#define SUN20I_CODEC_ADC2_MICIN2_SIN_EN 28 +#define SUN20I_CODEC_ADC2_FMINR_EN 27 +#define SUN20I_CODEC_ADC2_FMINR_GAIN 26 +#define SUN20I_CODEC_ADC2_DITHER_LEVEL 24 +#define SUN20I_CODEC_ADC2_LINEINR_EN 23 +#define SUN20I_CODEC_ADC2_LINEINR_GAIN 22 +#define SUN20I_CODEC_ADC2_ADC2_PGA_GAIN 8 +#define SUN20I_CODEC_ADC3 0x0308 +#define SUN20I_CODEC_ADC3_ADC3_EN 31 +#define SUN20I_CODEC_ADC3_MICIN3_PGA_EN 30 +#define SUN20I_CODEC_ADC3_ADC3_DITHER_EN 29 +#define SUN20I_CODEC_ADC3_MICIN3_SIN_EN 28 +#define SUN20I_CODEC_ADC3_DITHER_LEVEL 24 +#define SUN20I_CODEC_ADC3_ADC3_PGA_GAIN 8 + +#define SUN20I_CODEC_DAC 0x0310 +#define SUN20I_CODEC_DAC_DACL_EN 15 +#define SUN20I_CODEC_DAC_DACR_EN 14 +#define SUN20I_CODEC_DAC_LINEOUTL_EN 13 +#define SUN20I_CODEC_DAC_LMUTE 12 +#define SUN20I_CODEC_DAC_LINEOUTR_EN 11 +#define SUN20I_CODEC_DAC_RMUTE 10 +#define SUN20I_CODEC_DAC_LINEOUTL_DIFFEN 6 +#define SUN20I_CODEC_DAC_LINEOUTR_DIFFEN 5 +#define SUN20I_CODEC_DAC_LINEOUT_VOL_CTRL 0 + +#define SUN20I_CODEC_MICBIAS 0x0318 +#define SUN20I_CODEC_MICBIAS_SELDETADCFS 28 +#define SUN20I_CODEC_MICBIAS_SELDETADCDB 26 +#define SUN20I_CODEC_MICBIAS_SELDETADCBF 24 +#define SUN20I_CODEC_MICBIAS_JACKDETEN 23 +#define SUN20I_CODEC_MICBIAS_SELDETADCDY 21 +#define SUN20I_CODEC_MICBIAS_MICADCEN 20 +#define SUN20I_CODEC_MICBIAS_POPFREE 19 +#define SUN20I_CODEC_MICBIAS_DET_MODE 18 +#define SUN20I_CODEC_MICBIAS_AUTOPLEN 17 +#define SUN20I_CODEC_MICBIAS_MICDETPL 16 +#define SUN20I_CODEC_MICBIAS_HMICBIASEN 15 +#define SUN20I_CODEC_MICBIAS_HMICBIASSEL 13 +#define SUN20I_CODEC_MICBIAS_HMIC_CHOPPER_EN 12 +#define SUN20I_CODEC_MICBIAS_HMIC_CHOPPER_CLK 10 +#define SUN20I_CODEC_MICBIAS_MMICBIASEN 7 +#define SUN20I_CODEC_MICBIAS_MMICBIASSEL 5 +#define SUN20I_CODEC_MICBIAS_MMIC_CHOPPER_EN 4 +#define SUN20I_CODEC_MICBIAS_MMIC_CHOPPER_CLK 2 + +/* TODO */ +#define SUN20I_CODEC_RAMP 0x031c +#define SUN20I_CODEC_RAMP_HP_PULL_OUT_EN 15 + +#define SUN20I_CODEC_HMIC_CTRL 0x0328 +#define SUN20I_CODEC_HMIC_CTRL_SAMPLE_SELECT 21 +#define SUN20I_CODEC_HMIC_CTRL_MDATA_THRESHOLD 16 +#define SUN20I_CODEC_HMIC_CTRL_SF 14 +#define SUN20I_CODEC_HMIC_CTRL_M 10 +#define SUN20I_CODEC_HMIC_CTRL_N 6 +#define SUN20I_CODEC_HMIC_CTRL_THRESH_DEBOUNCE 3 +#define SUN20I_CODEC_HMIC_CTRL_JACK_OUT_IRQ_EN 2 +#define SUN20I_CODEC_HMIC_CTRL_JACK_IN_IRQ_EN 1 +#define SUN20I_CODEC_HMIC_CTRL_MIC_DET_IRQ_EN 0 +#define SUN20I_CODEC_HMIC_STS 0x032c +#define SUN20I_CODEC_HMIC_STS_MDATA_DISCARD 13 +#define SUN20I_CODEC_HMIC_STS_HMIC_DATA 8 +#define SUN20I_CODEC_HMIC_STS_JACK_OUT_IRQ 4 +#define SUN20I_CODEC_HMIC_STS_JACK_IN_IRQ 3 +#define SUN20I_CODEC_HMIC_STS_MIC_DET_IRQ 0 + +#define SUN20I_CODEC_HP2 0x0340 +#define SUN20I_CODEC_HP2_HPFB_BUF_EN 31 +#define SUN20I_CODEC_HP2_HEADPHONE_GAIN 28 +#define SUN20I_CODEC_HP2_HPFB_RES 26 +#define SUN20I_CODEC_HP2_HP_DRVEN 21 +#define SUN20I_CODEC_HP2_HP_DRVOUTEN 20 +#define SUN20I_CODEC_HP2_RSWITCH 19 +#define SUN20I_CODEC_HP2_RAMPEN 18 +#define SUN20I_CODEC_HP2_HPFB_IN_EN 17 +#define SUN20I_CODEC_HP2_RAMP_FINAL_CONTROL 16 +#define SUN20I_CODEC_HP2_RAMP_OUT_EN 15 +#define SUN20I_CODEC_HP2_RAMP_FINAL_STATE_RES 13 + +/* Not affected by codec bus clock/reset */ +#define SUN20I_CODEC_POWER 0x0348 +#define SUN20I_CODEC_POWER_ALDO_EN_MASK BIT(31) +#define SUN20I_CODEC_POWER_HPLDO_EN_MASK BIT(30) +#define SUN20I_CODEC_POWER_ALDO_VOLTAGE_MASK GENMASK(14, 12) +#define SUN20I_CODEC_POWER_HPLDO_VOLTAGE_MASK GENMASK(10, 8) + +#define SUN20I_CODEC_ADC_CUR 0x034c + +#define SUN20I_CODEC_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE|\ + SNDRV_PCM_FMTBIT_S20_LE|\ + SNDRV_PCM_FMTBIT_S32_LE) + +#define DRIVER_NAME "sun20i-codec" + +/* snd_soc_register_card() takes over drvdata, so the card must be first! */ +struct sun20i_codec { + struct snd_soc_card card; + struct snd_soc_dai_link dai_link; + struct snd_soc_dai_link_component dlcs[3]; + struct snd_dmaengine_dai_dma_data dma_data[2]; + + struct clk *bus_clk; + struct clk *adc_clk; + struct clk *dac_clk; + struct reset_control *reset; +}; + +static int sun20i_codec_dai_probe(struct snd_soc_dai *dai) +{ + struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, + &codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK], + &codec->dma_data[SNDRV_PCM_STREAM_CAPTURE]); + + return 0; +} + +static struct clk *sun20i_codec_get_clk(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai); + + return substream->stream == SNDRV_PCM_STREAM_CAPTURE ? + codec->adc_clk : codec->dac_clk; +} + +static const unsigned int sun20i_codec_rates[] = { + 7350, 8000, 11025, 12000, 14700, 16000, 22050, 24000, + 29400, 32000, 44100, 48000, 88200, 96000, 176400, 192000, +}; + +static const struct snd_pcm_hw_constraint_list sun20i_codec_rate_lists[] = { + [SNDRV_PCM_STREAM_PLAYBACK] = { + .list = sun20i_codec_rates, + .count = ARRAY_SIZE(sun20i_codec_rates), + }, + [SNDRV_PCM_STREAM_CAPTURE] = { + .list = sun20i_codec_rates, + .count = ARRAY_SIZE(sun20i_codec_rates) - 4, /* max 48 kHz */ + }, +}; + +static int sun20i_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + const struct snd_pcm_hw_constraint_list *list; + int ret; + + list = &sun20i_codec_rate_lists[substream->stream]; + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, list); + if (ret) + return ret; + + ret = clk_prepare_enable(sun20i_codec_get_clk(substream, dai)); + if (ret) + return ret; + + return 0; +} + +static void sun20i_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + clk_disable_unprepare(sun20i_codec_get_clk(substream, dai)); +} + +static unsigned int sun20i_codec_get_clk_rate(unsigned int sample_rate) +{ + return (sample_rate % 4000) ? 22579200 : 24576000; +} + +static const unsigned short sun20i_codec_divisors[] = { + 512, 1024, 2048, 128, + 768, 1536, 3072, 256, +}; + +static int sun20i_codec_get_fs(unsigned int clk_rate, unsigned int sample_rate) +{ + unsigned int divisor = clk_rate / sample_rate; + int i; + + for (i = 0; i < ARRAY_SIZE(sun20i_codec_divisors); ++i) + if (sun20i_codec_divisors[i] == divisor) + return i; + + return -EINVAL; +} + +static int sun20i_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *component = dai->component; + unsigned int channels = params_channels(params); + unsigned int sample_bits = params_width(params); + unsigned int sample_rate = params_rate(params); + unsigned int clk_rate = sun20i_codec_get_clk_rate(sample_rate); + enum dma_slave_buswidth dma_width; + unsigned int reg; + int ret, val; + + switch (params_physical_width(params)) { + case 16: + dma_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 32: + dma_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(dai->dev, "Unsupported physical sample width: %d\n", + params_physical_width(params)); + return -EINVAL; + } + codec->dma_data[substream->stream].addr_width = dma_width; + + ret = clk_set_rate(sun20i_codec_get_clk(substream, dai), + sun20i_codec_get_clk_rate(sample_rate)); + if (ret) + return ret; + + reg = substream->stream == SNDRV_PCM_STREAM_CAPTURE ? + SUN20I_CODEC_ADC_FIFOC : SUN20I_CODEC_DAC_FIFOC; + + val = sun20i_codec_get_fs(clk_rate, sample_rate); + if (val < 0) + return val; + snd_soc_component_update_bits(component, reg, + 0x7 << SUN20I_CODEC_DAC_FIFOC_FS, + val << SUN20I_CODEC_DAC_FIFOC_FS); + + /* Data is at MSB for full 4-byte samples, otherwise at LSB. */ + val = sample_bits != 32; + snd_soc_component_update_bits(component, reg, + 0x1 << SUN20I_CODEC_DAC_FIFOC_FIFO_MODE, + val << SUN20I_CODEC_DAC_FIFOC_FIFO_MODE); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + val = sample_bits > 16; + snd_soc_component_update_bits(component, reg, + 0x1 << SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS, + val << SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS); + + val = BIT(channels) - 1; + snd_soc_component_update_bits(component, SUN20I_CODEC_ADC_DIG_CTRL, + 0xf << SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN, + val << SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN); + } else { + val = sample_bits > 16; + snd_soc_component_update_bits(component, reg, + 0x1 << SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS, + val << SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS); + + val = channels == 1; + snd_soc_component_update_bits(component, reg, + 0x1 << SUN20I_CODEC_DAC_FIFOC_MONO_EN, + val << SUN20I_CODEC_DAC_FIFOC_MONO_EN); + } + + return 0; +} + +static int sun20i_codec_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg, mask; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + reg = SUN20I_CODEC_ADC_FIFOC; + mask = BIT(SUN20I_CODEC_ADC_FIFOC_DRQ_EN); + } else { + reg = SUN20I_CODEC_DAC_FIFOC; + mask = BIT(SUN20I_CODEC_DAC_FIFOC_DRQ_EN); + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + mask |= BIT(SUN20I_CODEC_DAC_FIFOC_FIFO_FLUSH); + snd_soc_component_update_bits(component, reg, mask, mask); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + snd_soc_component_update_bits(component, reg, mask, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops sun20i_codec_dai_ops = { + .startup = sun20i_codec_startup, + .shutdown = sun20i_codec_shutdown, + .hw_params = sun20i_codec_hw_params, + .trigger = sun20i_codec_trigger, +}; + +static struct snd_soc_dai_driver sun20i_codec_dai = { + .name = DRIVER_NAME, + .probe = sun20i_codec_dai_probe, + .ops = &sun20i_codec_dai_ops, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 3, /* ??? */ + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SUN20I_CODEC_PCM_FORMATS, + .sig_bits = 20, + }, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SUN20I_CODEC_PCM_FORMATS, + .sig_bits = 20, + }, +}; + +static const DECLARE_TLV_DB_SCALE(sun20i_codec_boost_vol_scale, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(sun20i_codec_digital_vol_scale, -12000, 75, 1); +static const DECLARE_TLV_DB_SCALE(sun20i_codec_headphone_vol_scale, -4200, 600, 0); +/* FIXME */ +static const DECLARE_TLV_DB_SCALE(sun20i_codec_line_out_vol_scale, -4650, 150, 1); +/* FIXME */ +static const DECLARE_TLV_DB_SCALE(sun20i_codec_pga_vol_scale, 500, 100, 0); + +static const char *const sun20i_codec_line_out_mode_enum_text[] = { + "Single-Ended", "Differential" +}; + +static const SOC_ENUM_DOUBLE_DECL(sun20i_codec_line_out_mode_enum, + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LINEOUTL_DIFFEN, + SUN20I_CODEC_DAC_LINEOUTR_DIFFEN, + sun20i_codec_line_out_mode_enum_text); + +static const struct snd_kcontrol_new sun20i_codec_controls[] = { + /* Digital Controls */ + SOC_DOUBLE_TLV("DAC Playback Volume", + SUN20I_CODEC_DAC_VOL_CTRL, + SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_L, + SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_R, + 0xc0, 0, sun20i_codec_digital_vol_scale), + SOC_SINGLE_TLV("ADC3 Capture Volume", + SUN20I_CODEC_ADC_VOL_CTRL, + SUN20I_CODEC_ADC_VOL_CTRL_ADC3_VOL, + 0xc0, 0, sun20i_codec_digital_vol_scale), + SOC_SINGLE_TLV("ADC2 Capture Volume", + SUN20I_CODEC_ADC_VOL_CTRL, + SUN20I_CODEC_ADC_VOL_CTRL_ADC2_VOL, + 0xc0, 0, sun20i_codec_digital_vol_scale), + SOC_SINGLE_TLV("ADC1 Capture Volume", + SUN20I_CODEC_ADC_VOL_CTRL, + SUN20I_CODEC_ADC_VOL_CTRL_ADC1_VOL, + 0xc0, 0, sun20i_codec_digital_vol_scale), + + /* Analog Controls */ + SOC_DOUBLE_R_TLV("FM Capture Volume", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC1_FMINL_GAIN, + 0x1, 0, sun20i_codec_boost_vol_scale), + SOC_DOUBLE_R_TLV("Line In Capture Volume", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC1_LINEINL_GAIN, + 0x1, 0, sun20i_codec_boost_vol_scale), + SOC_ENUM("Line Out Mode Playback Enum", + sun20i_codec_line_out_mode_enum), + SOC_SINGLE_TLV("Line Out Playback Volume", + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LINEOUT_VOL_CTRL, + 0x1f, 0, sun20i_codec_line_out_vol_scale), + SOC_SINGLE_TLV("Headphone Playback Volume", + SUN20I_CODEC_HP2, + SUN20I_CODEC_HP2_HEADPHONE_GAIN, + 0x7, 1, sun20i_codec_headphone_vol_scale), +}; + +static const struct snd_kcontrol_new sun20i_codec_line_out_switch = + SOC_DAPM_DOUBLE("Line Out Playback Switch", + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LMUTE, + SUN20I_CODEC_DAC_RMUTE, 1, 1); + +static const struct snd_kcontrol_new sun20i_codec_hp_switch = + SOC_DAPM_SINGLE("Headphone Playback Switch", + SUN20I_CODEC_HP2, + SUN20I_CODEC_HP2_HP_DRVOUTEN, 1, 0); + +static const struct snd_kcontrol_new sun20i_codec_adc12_mixer_controls[] = { + /* ADC1 Only */ + SOC_DAPM_SINGLE("Mic1 Capture Switch", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC1_MICIN1_SIN_EN, 1, 0), + /* Shared */ + SOC_DAPM_DOUBLE_R("FM Capture Switch", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC1_FMINL_EN, 1, 0), + /* Shared */ + SOC_DAPM_DOUBLE_R("Line In Capture Switch", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC1_LINEINL_EN, 1, 0), + /* ADC2 Only */ + SOC_DAPM_SINGLE("Mic2 Capture Switch", + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC2_MICIN2_SIN_EN, 1, 0), +}; + +static const struct snd_kcontrol_new sun20i_codec_adc3_mixer_controls[] = { + SOC_DAPM_SINGLE("Mic3 Capture Switch", + SUN20I_CODEC_ADC3, + SUN20I_CODEC_ADC3_MICIN3_SIN_EN, 1, 0), +}; + +static const struct snd_kcontrol_new sun20i_codec_mic1_volume = + SOC_DAPM_SINGLE_TLV("Capture Volume", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC1_ADC1_PGA_GAIN, + 0x1f, 0, sun20i_codec_pga_vol_scale); + +static const struct snd_kcontrol_new sun20i_codec_mic2_volume = + SOC_DAPM_SINGLE_TLV("Capture Volume", + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC2_ADC2_PGA_GAIN, + 0x1f, 0, sun20i_codec_pga_vol_scale); + +static const struct snd_kcontrol_new sun20i_codec_mic3_volume = + SOC_DAPM_SINGLE_TLV("Capture Volume", + SUN20I_CODEC_ADC3, + SUN20I_CODEC_ADC3_ADC3_PGA_GAIN, + 0x1f, 0, sun20i_codec_pga_vol_scale); + +static const struct snd_soc_dapm_widget sun20i_codec_widgets[] = { + /* Playback */ + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), + + SND_SOC_DAPM_SWITCH("LINEOUTL Switch", + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LINEOUTL_EN, 0, + &sun20i_codec_line_out_switch), + SND_SOC_DAPM_SWITCH("LINEOUTR Switch", + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LINEOUTR_EN, 0, + &sun20i_codec_line_out_switch), + + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + + SND_SOC_DAPM_SWITCH("HPOUTL Switch", + SND_SOC_NOPM, 0, 0, &sun20i_codec_hp_switch), + SND_SOC_DAPM_SWITCH("HPOUTR Switch", + SND_SOC_NOPM, 0, 0, &sun20i_codec_hp_switch), + SND_SOC_DAPM_SUPPLY("Headphone Driver", + SUN20I_CODEC_HP2, + SUN20I_CODEC_HP2_HP_DRVEN, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DACL", NULL, + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_DACL_EN, 0), + SND_SOC_DAPM_DAC("DACR", NULL, + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_DACR_EN, 0), + SND_SOC_DAPM_SUPPLY("DAC", + SUN20I_CODEC_DAC_DPC, + SUN20I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0), + + SND_SOC_DAPM_AIF_IN("DACL FIFO", "Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DACR FIFO", "Playback", 1, + SND_SOC_NOPM, 0, 0), + + /* Capture */ + SND_SOC_DAPM_AIF_OUT("ADC1 FIFO", "Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ADC2 FIFO", "Capture", 1, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ADC3 FIFO", "Capture", 2, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_ADC("ADC1", NULL, + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC1_ADC1_EN, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC2_ADC2_EN, 0), + SND_SOC_DAPM_ADC("ADC3", NULL, + SUN20I_CODEC_ADC3, + SUN20I_CODEC_ADC3_ADC3_EN, 0), + SND_SOC_DAPM_SUPPLY("ADC", + SUN20I_CODEC_ADC_FIFOC, + SUN20I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_NAMED_CTL("ADC1 Mixer", SND_SOC_NOPM, 0, 0, + sun20i_codec_adc12_mixer_controls, 3), + SND_SOC_DAPM_MIXER_NAMED_CTL("ADC2 Mixer", SND_SOC_NOPM, 0, 0, + sun20i_codec_adc12_mixer_controls + 1, 3), + SND_SOC_DAPM_MIXER_NAMED_CTL("ADC3 Mixer", SND_SOC_NOPM, 0, 0, + sun20i_codec_adc3_mixer_controls, + ARRAY_SIZE(sun20i_codec_adc3_mixer_controls)), + + SND_SOC_DAPM_PGA("Mic1", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC1_MICIN1_PGA_EN, 0, + &sun20i_codec_mic1_volume, 1), + SND_SOC_DAPM_PGA("Mic2", + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC2_MICIN2_PGA_EN, 0, + &sun20i_codec_mic2_volume, 1), + SND_SOC_DAPM_PGA("Mic3", + SUN20I_CODEC_ADC3, + SUN20I_CODEC_ADC3_MICIN3_PGA_EN, 0, + &sun20i_codec_mic3_volume, 1), + + SND_SOC_DAPM_INPUT("MICIN1"), + SND_SOC_DAPM_INPUT("MICIN2"), + SND_SOC_DAPM_INPUT("MICIN3"), + + SND_SOC_DAPM_INPUT("FMINL"), + SND_SOC_DAPM_INPUT("FMINR"), + + SND_SOC_DAPM_INPUT("LINEINL"), + SND_SOC_DAPM_INPUT("LINEINR"), + + SND_SOC_DAPM_SUPPLY("HBIAS", + SUN20I_CODEC_MICBIAS, + SUN20I_CODEC_MICBIAS_HMICBIASEN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", + SUN20I_CODEC_MICBIAS, + SUN20I_CODEC_MICBIAS_MMICBIASEN, 0, NULL, 0), + + SND_SOC_DAPM_REGULATOR_SUPPLY("avcc", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("hpvcc", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd33", 0, 0), +}; + +static const struct snd_soc_dapm_route sun20i_codec_routes[] = { + /* Playback */ + { "LINEOUTL", NULL, "LINEOUTL Switch" }, + { "LINEOUTR", NULL, "LINEOUTR Switch" }, + + { "LINEOUTL Switch", "Line Out Playback Switch", "DACL" }, + { "LINEOUTR Switch", "Line Out Playback Switch", "DACR" }, + + { "HPOUTL", NULL, "HPOUTL Switch" }, + { "HPOUTR", NULL, "HPOUTR Switch" }, + + { "HPOUTL Switch", "Headphone Playback Switch", "DACL" }, + { "HPOUTR Switch", "Headphone Playback Switch", "DACR" }, + { "HPOUTL Switch", NULL, "Headphone Driver" }, + { "HPOUTR Switch", NULL, "Headphone Driver" }, + { "Headphone Driver", NULL, "hpvcc" }, + + { "DACL", NULL, "DACL FIFO" }, + { "DACR", NULL, "DACR FIFO" }, + { "DACL", NULL, "DAC" }, + { "DACR", NULL, "DAC" }, + { "DACL", NULL, "avcc" }, + { "DACR", NULL, "avcc" }, + + /* Capture */ + { "ADC1 FIFO", NULL, "ADC1" }, + { "ADC2 FIFO", NULL, "ADC2" }, + { "ADC3 FIFO", NULL, "ADC3" }, + + { "ADC1", NULL, "ADC1 Mixer" }, + { "ADC2", NULL, "ADC2 Mixer" }, + { "ADC3", NULL, "ADC3 Mixer" }, + { "ADC1", NULL, "ADC" }, + { "ADC2", NULL, "ADC" }, + { "ADC3", NULL, "ADC" }, + { "ADC1", NULL, "avcc" }, + { "ADC2", NULL, "avcc" }, + { "ADC3", NULL, "avcc" }, + + { "ADC1 Mixer", "Mic1 Capture Switch", "Mic1" }, + { "ADC2 Mixer", "Mic2 Capture Switch", "Mic2" }, + { "ADC3 Mixer", "Mic3 Capture Switch", "Mic3" }, + { "ADC1 Mixer", "FM Capture Switch", "FMINL" }, + { "ADC2 Mixer", "FM Capture Switch", "FMINR" }, + { "ADC1 Mixer", "Line In Capture Switch", "LINEINL" }, + { "ADC2 Mixer", "Line In Capture Switch", "LINEINR" }, + + { "Mic1", NULL, "MICIN1" }, + { "Mic2", NULL, "MICIN2" }, + { "Mic3", NULL, "MICIN3" }, + + { "HBIAS", NULL, "vdd33" }, + { "MBIAS", NULL, "vdd33" }, +}; + +static int sun20i_codec_component_probe(struct snd_soc_component *component) +{ + struct sun20i_codec *codec = snd_soc_component_get_drvdata(component); + int ret; + + ret = reset_control_deassert(codec->reset); + if (ret) + return ret; + + ret = clk_prepare_enable(codec->bus_clk); + if (ret) + goto err_assert_reset; + + /* Enable digital volume control. */ + snd_soc_component_update_bits(component, SUN20I_CODEC_DAC_VOL_CTRL, + 0x1 << SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL, + 0x1 << SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL); + snd_soc_component_update_bits(component, SUN20I_CODEC_ADC_DIG_CTRL, + 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN, + 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN); + + return 0; + +err_assert_reset: + reset_control_assert(codec->reset); + + return ret; +} + +static void sun20i_codec_component_remove(struct snd_soc_component *component) +{ + struct sun20i_codec *codec = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(codec->bus_clk); + reset_control_assert(codec->reset); +} + +static const struct snd_soc_component_driver sun20i_codec_component = { + .controls = sun20i_codec_controls, + .num_controls = ARRAY_SIZE(sun20i_codec_controls), + .dapm_widgets = sun20i_codec_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun20i_codec_widgets), + .dapm_routes = sun20i_codec_routes, + .num_dapm_routes = ARRAY_SIZE(sun20i_codec_routes), + .probe = sun20i_codec_component_probe, + .remove = sun20i_codec_component_remove, +}; + +static int sun20i_codec_init_card(struct device *dev, + struct sun20i_codec *codec) +{ + struct snd_soc_dai_link *dai_link = &codec->dai_link; + struct snd_soc_card *card = &codec->card; + int ret; + + codec->dlcs[0].of_node = dev->of_node; + codec->dlcs[0].dai_name = DRIVER_NAME; + codec->dlcs[1].name = "snd-soc-dummy"; + codec->dlcs[1].dai_name = "snd-soc-dummy-dai"; + codec->dlcs[2].of_node = dev->of_node; + + dai_link->name = DRIVER_NAME; + dai_link->stream_name = DRIVER_NAME; + dai_link->cpus = &codec->dlcs[0]; + dai_link->num_cpus = 1; + dai_link->codecs = &codec->dlcs[1]; + dai_link->num_codecs = 1; + dai_link->platforms = &codec->dlcs[2]; + dai_link->num_platforms = 1; + + card->name = DRIVER_NAME; + card->dev = dev; + card->owner = THIS_MODULE; + card->dai_link = dai_link; + card->num_links = 1; + card->fully_routed = true; + + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); + if (ret) + return ret; + + ret = snd_soc_of_parse_pin_switches(card, "pin-switches"); + if (ret) + return ret; + + ret = snd_soc_of_parse_audio_routing(card, "routing"); + if (ret) + return ret; + + ret = snd_soc_of_parse_audio_simple_widgets(card, "widgets"); + if (ret) + return ret; + + return 0; +} + +static const struct regmap_config sun20i_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN20I_CODEC_ADC_CUR, +}; + +static const struct regulator_ops sun20i_codec_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_desc sun20i_codec_ldos[] = { + { + .name = "aldo", + .supply_name = "vdd33", + .of_match = "aldo", + .regulators_node = "regulators", + .ops = &sun20i_codec_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = BIT(3), + .min_uV = 1650000, + .uV_step = 50000, + .vsel_reg = SUN20I_CODEC_POWER, + .vsel_mask = SUN20I_CODEC_POWER_ALDO_VOLTAGE_MASK, + .enable_reg = SUN20I_CODEC_POWER, + .enable_mask = SUN20I_CODEC_POWER_ALDO_EN_MASK, + }, + { + .name = "hpldo", + .supply_name = "hpldoin", + .of_match = "hpldo", + .regulators_node = "regulators", + .ops = &sun20i_codec_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = BIT(3), + .min_uV = 1650000, + .uV_step = 50000, + .vsel_reg = SUN20I_CODEC_POWER, + .vsel_mask = SUN20I_CODEC_POWER_HPLDO_VOLTAGE_MASK, + .enable_reg = SUN20I_CODEC_POWER, + .enable_mask = SUN20I_CODEC_POWER_HPLDO_EN_MASK, + }, +}; + +static int sun20i_codec_probe(struct platform_device *pdev) +{ + struct regulator_config config = { .dev = &pdev->dev }; + struct device *dev = &pdev->dev; + struct sun20i_codec *codec; + struct regulator_dev *rdev; + struct regmap *regmap; + struct resource *res; + void __iomem *base; + int i, ret; + + codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + dev_set_drvdata(dev, codec); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), + "Failed to map registers\n"); + + regmap = devm_regmap_init_mmio(dev, base, + &sun20i_codec_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to create regmap\n"); + + codec->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(codec->bus_clk)) + return dev_err_probe(dev, PTR_ERR(codec->bus_clk), + "Failed to get bus clock\n"); + + codec->adc_clk = devm_clk_get(dev, "adc"); + if (IS_ERR(codec->adc_clk)) + return dev_err_probe(dev, PTR_ERR(codec->adc_clk), + "Failed to get ADC clock\n"); + + codec->dac_clk = devm_clk_get(dev, "dac"); + if (IS_ERR(codec->dac_clk)) + return dev_err_probe(dev, PTR_ERR(codec->dac_clk), + "Failed to get DAC clock\n"); + + codec->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(codec->reset)) + return dev_err_probe(dev, PTR_ERR(codec->reset), + "Failed to get reset\n"); + + for (i = 0; i < ARRAY_SIZE(sun20i_codec_ldos); ++i) { + const struct regulator_desc *desc = &sun20i_codec_ldos[i]; + + rdev = devm_regulator_register(dev, desc, &config); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + } + + ret = devm_snd_soc_register_component(dev, &sun20i_codec_component, + &sun20i_codec_dai, 1); + if (ret) + return dev_err_probe(dev, ret, "Failed to register component\n"); + + codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = + res->start + SUN20I_CODEC_DAC_TXDATA; + codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 8; + codec->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = + res->start + SUN20I_CODEC_ADC_RXDATA; + codec->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 8; + + ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to register PCM\n"); + + ret = sun20i_codec_init_card(dev, codec); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize card\n"); + + ret = devm_snd_soc_register_card(dev, &codec->card); + if (ret) + return dev_err_probe(dev, ret, "Failed to register card\n"); + + return 0; +} + +static const struct of_device_id sun20i_codec_of_match[] = { + { .compatible = "allwinner,sun20i-d1-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, sun20i_codec_of_match); + +static struct platform_driver sun20i_codec_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = sun20i_codec_of_match, + }, + .probe = sun20i_codec_probe, +}; +module_platform_driver(sun20i_codec_driver); + +MODULE_DESCRIPTION("Allwinner D1 (sun20i) codec driver"); +MODULE_AUTHOR("Samuel Holland "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sun20i-codec"); From 9a031568d5f137ed39168d1da77907a2ac670f28 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 7 Aug 2022 10:58:57 -0500 Subject: [PATCH 123/153] riscv: dts: allwinner: d1: Add LVDS0 PHY Signed-off-by: Samuel Holland --- arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi index a3235d428241d3..5dc3c5a8da6ac9 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -1009,6 +1009,8 @@ resets = <&ccu RST_BUS_TCON_LCD0>, <&ccu RST_BUS_LVDS0>; reset-names = "lcd", "lvds"; + phys = <&dphy>; + phy-names = "lvds0"; #clock-cells = <0>; ports { From 23858f912f2dc6709cc25f0dac4b5397a3f8cf96 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 23 Jun 2021 21:18:47 -0500 Subject: [PATCH 124/153] [WIP] ASoC: sun20i-codec: What is this ramp thing? Signed-off-by: Samuel Holland --- sound/soc/sunxi/sun20i-codec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sunxi/sun20i-codec.c b/sound/soc/sunxi/sun20i-codec.c index c58dde3eba7cd0..377bef42611ea4 100644 --- a/sound/soc/sunxi/sun20i-codec.c +++ b/sound/soc/sunxi/sun20i-codec.c @@ -710,6 +710,10 @@ static int sun20i_codec_component_probe(struct snd_soc_component *component) 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN, 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN); + /* Maaagic... */ + snd_soc_component_update_bits(component, SUN20I_CODEC_RAMP, + BIT(1) | BIT(0), BIT(0)); + return 0; err_assert_reset: From d623962bc80a9b2bbbef296d902437b1d8b10a95 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 11 Aug 2022 00:39:42 -0500 Subject: [PATCH 125/153] riscv: dts: allwinner: d1: Add sound cards to boards Signed-off-by: Samuel Holland --- .../allwinner/sun20i-d1-clockworkpi-v3.14.dts | 77 +++++++++++++++++++ .../sun20i-d1-common-regulators.dtsi | 5 ++ .../sun20i-d1-lichee-rv-86-panel.dtsi | 63 +++++++++++++++ .../allwinner/sun20i-d1-lichee-rv-dock.dts | 56 ++++++++++++++ .../boot/dts/allwinner/sun20i-d1-nezha.dts | 12 +++ arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi | 46 ++++++++++- 6 files changed, 258 insertions(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts index 54f913e4b5473b..da48ceb0c499d5 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-clockworkpi-v3.14.dts @@ -22,12 +22,49 @@ stdout-path = "serial0:115200n8"; }; + audio_amplifier: audio-amplifier { + compatible = "simple-audio-amplifier"; + enable-gpios = <&pio 4 11 GPIO_ACTIVE_HIGH>; /* PE11/GPIO11 */ + sound-name-prefix = "Amplifier"; + VCC-supply = <®_vcc>; + }; + + /* + * FIXME: This is not really an amplifier, but the amplifier binding + * has the needed properties and behavior. + */ + audio_switch: audio-switch { + compatible = "simple-audio-amplifier"; + enable-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2/AUD_SWITCH */ + sound-name-prefix = "Switch"; + VCC-supply = <®_aldo1>; + }; + backlight: backlight { compatible = "pwm-backlight"; power-supply = <®_vcc>; pwms = <&pwm 4 50000 0>; /* PD20/GPIO9 */ }; + bt_sco_codec: bt-sco-codec { + #sound-dai-cells = <0>; + compatible = "linux,bt-sco"; + }; + + bt-sound { + compatible = "simple-audio-card"; + + simple-audio-card,dai-link { + cpu { + sound-dai = <&i2s1>; + }; + + codec { + sound-dai = <&bt_sco_codec>; + }; + }; + }; + hdmi_connector: connector { compatible = "hdmi-connector"; type = "d"; @@ -54,6 +91,25 @@ }; }; +&codec { + aux-devs = <&audio_amplifier>, <&audio_switch>; + hp-det-gpio = <&pio 1 12 GPIO_ACTIVE_HIGH>; /* PB12/GPIO10 */ + pin-switches = "Internal Speakers"; + routing = "Internal Speakers", "Amplifier OUTL", + "Internal Speakers", "Amplifier OUTR", + "Amplifier INL", "Switch OUTL", + "Amplifier INR", "Switch OUTR", + "Headphone Jack", "Switch OUTL", + "Headphone Jack", "Switch OUTR", + "Switch INL", "HPOUTL", + "Switch INR", "HPOUTR", + "MICIN3", "Headset Microphone", + "Headset Microphone", "HBIAS"; + widgets = "Microphone", "Headset Microphone", + "Headphone", "Headphone Jack", + "Speaker", "Internal Speakers"; +}; + &cpu0 { cpu-supply = <®_vdd_cpu>; }; @@ -201,6 +257,12 @@ }; }; +&i2s1 { + pinctrl-0 = <&i2s1_clk_pins>, <&i2s1_din_pin>, <&i2s1_dout_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + &mmc0 { broken-cd; bus-width = <4>; @@ -237,6 +299,21 @@ &pio { vcc-pg-supply = <®_ldoa>; + + i2s1_clk_pins: i2s1-clk-pins { + pins = "PG12", "PG13"; + function = "i2s1"; + }; + + i2s1_din_pin: i2s1-din-pin { + pins = "PG14"; + function = "i2s1_din"; + }; + + i2s1_dout_pin: i2s1-dout-pin { + pins = "PG15"; + function = "i2s1_dout"; + }; }; &pwm { diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi index fdd7ba44dee1aa..9cbc1725fdb84e 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-common-regulators.dtsi @@ -18,6 +18,11 @@ }; }; +&codec { + avcc-supply = <®_aldo>; + hpvcc-supply = <®_hpldo>; +}; + &hdmi { hvcc-supply = <®_ldoa>; }; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel.dtsi index ace2db106a64ee..396cb0d25c90aa 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-86-panel.dtsi @@ -9,6 +9,38 @@ ethernet1 = &xr829; }; + audio_amplifier: audio-amplifier { + compatible = "simple-audio-amplifier"; + enable-gpios = <&pio 1 10 GPIO_ACTIVE_HIGH>; /* PB10 */ + sound-name-prefix = "Amplifier"; + }; + + dmic_codec: dmic-codec { + compatible = "dmic-codec"; + num-channels = <1>; + #sound-dai-cells = <0>; + }; + + dmic-sound { + compatible = "simple-audio-card"; + #address-cells = <1>; + #size-cells = <0>; + + simple-audio-card,dai-link@0 { + format = "pdm"; + frame-master = <&link0_cpu>; + bitclock-master = <&link0_cpu>; + + link0_cpu: cpu { + sound-dai = <&dmic>; + }; + + link0_codec: codec { + sound-dai = <&dmic_codec>; + }; + }; + }; + wifi_pwrseq: wifi-pwrseq { compatible = "mmc-pwrseq-simple"; clocks = <&ccu CLK_FANOUT1>; @@ -21,6 +53,27 @@ }; }; +&codec { + aux-devs = <&audio_amplifier>; + routing = "Internal Speaker", "Amplifier OUTL", + "Internal Speaker", "Amplifier OUTR", + "Amplifier INL", "HPOUTL", + "Amplifier INR", "HPOUTR", + "LINEINL", "HPOUTL", + "LINEINR", "HPOUTR", + "MICIN3", "Internal Microphone", + "Internal Microphone", "HBIAS"; + widgets = "Microphone", "Internal Microphone", + "Speaker", "Internal Speaker"; + status = "okay"; +}; + +&dmic { + pinctrl-0 = <&dmic_pb11_d0_pin>, <&dmic_pe17_clk_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + &ehci1 { status = "okay"; }; @@ -66,6 +119,16 @@ pins = "PG11"; function = "clk"; }; + + dmic_pb11_d0_pin: dmic-pb11-d0-pin { + pins = "PB11"; + function = "dmic"; + }; + + dmic_pe17_clk_pin: dmic-pe17-clk-pin { + pins = "PE17"; + function = "dmic"; + }; }; &uart1 { diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts index 934a0db3ae64c7..7fcfc99f6bacad 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-lichee-rv-dock.dts @@ -15,6 +15,32 @@ ethernet1 = &rtl8723ds; }; + dmic_codec: dmic-codec { + compatible = "dmic-codec"; + num-channels = <2>; + #sound-dai-cells = <0>; + }; + + dmic-sound { + compatible = "simple-audio-card"; + #address-cells = <1>; + #size-cells = <0>; + + simple-audio-card,dai-link@0 { + format = "pdm"; + frame-master = <&link0_cpu>; + bitclock-master = <&link0_cpu>; + + link0_cpu: cpu { + sound-dai = <&dmic>; + }; + + link0_codec: codec { + sound-dai = <&dmic_codec>; + }; + }; + }; + hdmi_connector: connector { compatible = "hdmi-connector"; type = "a"; @@ -32,10 +58,28 @@ }; }; +&codec { + routing = "Internal Speaker", "HPOUTL", + "Internal Speaker", "HPOUTR", + "LINEINL", "HPOUTL", + "LINEINR", "HPOUTR", + "MICIN3", "Internal Microphone", + "Internal Microphone", "HBIAS"; + widgets = "Microphone", "Internal Microphone", + "Speaker", "Internal Speaker"; + status = "okay"; +}; + &de { status = "okay"; }; +&dmic { + pinctrl-0 = <&dmic_pb11_d0_pin>, <&dmic_pe17_clk_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + &ehci1 { status = "okay"; }; @@ -96,6 +140,18 @@ status = "okay"; }; +&pio { + dmic_pb11_d0_pin: dmic-pb11-d0-pin { + pins = "PB11"; + function = "dmic"; + }; + + dmic_pe17_clk_pin: dmic-pe17-clk-pin { + pins = "PE17"; + function = "dmic"; + }; +}; + &uart1 { uart-has-rtscts; pinctrl-0 = <&uart1_pg6_pins>, <&uart1_pg8_rts_cts_pins>; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts index 260a60eec02597..a645eb5e34754a 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts @@ -62,6 +62,18 @@ }; }; +&codec { + routing = "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "LINEINL", "HPOUTL", + "LINEINR", "HPOUTR", + "MICIN3", "Headset Microphone", + "Headset Microphone", "HBIAS"; + widgets = "Microphone", "Headset Microphone", + "Headphone", "Headphone Jack"; + status = "okay"; +}; + &cpu0 { cpu-supply = <®_vdd_cpu>; }; diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi index 5dc3c5a8da6ac9..dce3e38d0d0b8f 100644 --- a/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi +++ b/arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi @@ -320,10 +320,22 @@ }; codec: audio-codec@2030000 { - compatible = "simple-mfd", "syscon"; + compatible = "allwinner,sun20i-d1-codec", "simple-mfd", "syscon"; reg = <0x2030000 0x1000>; + interrupts = <41 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_AUDIO>, + <&ccu CLK_AUDIO_ADC>, + <&ccu CLK_AUDIO_DAC>, + <&osc24M>, + <&rtc CLK_OSC32K>; + clock-names = "bus", "adc", "dac", "hosc", "losc"; + resets = <&ccu RST_BUS_AUDIO>; + dmas = <&dma 7>, <&dma 7>; + dma-names = "rx", "tx"; + status = "disabled"; #address-cells = <1>; #size-cells = <1>; + #sound-dai-cells = <0>; regulators@2030348 { compatible = "allwinner,sun20i-d1-analog-ldos"; @@ -339,6 +351,21 @@ }; }; + // TODO: try the posted driver. + dmic: dmic@2031000 { + compatible = "allwinner,sun20i-d1-dmic"; + reg = <0x2031000 0x400>; + interrupts = <40 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_DMIC>, + <&ccu CLK_DMIC>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_DMIC>; + dmas = <&dma 8>; + dma-names = "rx"; + status = "disabled"; + #sound-dai-cells = <0>; + }; + i2s0: i2s@2032000 { compatible = "allwinner,sun20i-d1-i2s", "allwinner,sun50i-r329-i2s"; @@ -369,6 +396,7 @@ #sound-dai-cells = <0>; }; + // TODO: how to integrate ASRC? same or separate node? i2s2: i2s@2034000 { compatible = "allwinner,sun20i-d1-i2s", "allwinner,sun50i-r329-i2s"; @@ -384,6 +412,22 @@ #sound-dai-cells = <0>; }; + // TODO: add receive functionality + spdif: spdif@2036000 { + compatible = "allwinner,sun20i-d1-spdif"; + reg = <0x2036000 0x400>; + interrupts = <39 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_SPDIF>, + <&ccu CLK_SPDIF_RX>, + <&ccu CLK_SPDIF_TX>; + clock-names = "apb", "rx", "tx"; + resets = <&ccu RST_BUS_SPDIF>; + dmas = <&dma 2>, <&dma 2>; + dma-names = "rx", "tx"; + status = "disabled"; + #sound-dai-cells = <0>; + }; + timer: timer@2050000 { compatible = "allwinner,sun20i-d1-timer", "allwinner,sun8i-a23-timer"; From 1828fc8218b882e870e6b2d8583478a08cfde861 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 12 Aug 2022 01:59:35 -0500 Subject: [PATCH 126/153] dt-bindings: display: Add Sitronix ST7701s panel binding Signed-off-by: Samuel Holland --- .../display/panel/sitronix,st7701s.yaml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/sitronix,st7701s.yaml diff --git a/Documentation/devicetree/bindings/display/panel/sitronix,st7701s.yaml b/Documentation/devicetree/bindings/display/panel/sitronix,st7701s.yaml new file mode 100644 index 00000000000000..ec3623046479a5 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/sitronix,st7701s.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/sitronix,st7701s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sitronix ST7701 based LCD panels + +maintainers: + - Samuel Holland + +description: | + Panel used on Lichee RV 86 Panel + +allOf: + - $ref: panel-common.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + items: + - const: sitronix,st7701s + + backlight: true + + reset-gpios: true + +required: + - compatible + - reset-gpios + +unevaluatedProperties: false From 651dc493116ea41561a4d92086afc4ee0577ba3a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 29 Mar 2022 22:47:57 -0500 Subject: [PATCH 127/153] drm/panel: Add driver for ST7701s DPI LCD panel Signed-off-by: Samuel Holland --- drivers/gpu/drm/panel/Kconfig | 8 + drivers/gpu/drm/panel/Makefile | 1 + .../gpu/drm/panel/panel-sitronix-st7701s.c | 444 ++++++++++++++++++ 3 files changed, 453 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-sitronix-st7701s.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 38799effd00ad8..58c91fa92d0dc4 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -595,6 +595,14 @@ config DRM_PANEL_SITRONIX_ST7701 ST7701 controller for 480X864 LCD panels with MIPI/RGB/SPI system interfaces. +config DRM_PANEL_SITRONIX_ST7701S + tristate "Sitronix ST7701s panel driver" + depends on OF + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for the Sitronix + ST7701s controller with a SPI interface. + config DRM_PANEL_SITRONIX_ST7703 tristate "Sitronix ST7703 based MIPI touchscreen panels" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 42a7ab54234b0b..73cfef8b9f608a 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS060T1SX01) += panel-sharp-ls060t1sx01.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o +obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701S) += panel-sitronix-st7701s.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701s.c b/drivers/gpu/drm/panel/panel-sitronix-st7701s.c new file mode 100644 index 00000000000000..b579203908e4cb --- /dev/null +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701s.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2017 Free Electrons + */ + +#include +#include +#include +#include + +#include