sun6i MIPI DSI driver EoTp
Hi, Maxime, according to MIPI DSI spec chapter 8.8.2 the EoTp packet can be enabled or disabled on the protocol level. It seems like that it might be required for the panel I am working on (Himax HX8379A-based panel). Here is some crappy driver I found on github showing that it might be an issue [1] May I ask you if this feature supported by sun6i MIPI DSI driver and where ? As there is no datasheet available, could you please help to support this feature (if not yet) ? The second point is that the panel requires NON-BURST-WITH-SYNC-EVENTS mode. Could it be a problem with the current driver? [1] https://github.com/lnfamous/Kernel_Htc_Pico_CyanogenMod12.1/blob/ff5eed4b86f5c9e653ebde4d89fe969c6abf611a/drivers/video/msm/mipi_hx8379a_video_wvga_pt.c#L107 [2] https://github.com/lnfamous/Kernel_Htc_Pico_CyanogenMod12.1/blob/ff5eed4b86f5c9e653ebde4d89fe969c6abf611a/drivers/video/msm/mipi_hx8379a_video_wvga_pt.c#L92 Thank you, Sergey
Compilation issue with Linaro ARM toolchain
Hi, guys, I am having an issue builduing kernel 5.11 (rc4) with Linaro ARM toolchain. The issue seems to be related to CC plugins sources. Here is my build log: https://pastebin.com/DTn7Szax. I have never seen this before with versions 5.10 and below. Thank you, Sergey
Re: Compilation issue with Linaro ARM toolchain
Hi, Maxim, thank you for the reply. Installing libmpc-dev helped. On 18.01.2021 19:04, Maxim Kuvyrkov wrote: On 18 Jan 2021, at 18:59, Mathieu Poirier wrote: Adding the Linaro toolchain group mailing list. On Mon, Jan 18, 2021 at 05:49:39PM +0300, Sergey Suloev wrote: Hi, guys, I am having an issue builduing kernel 5.11 (rc4) with Linaro ARM toolchain. The issue seems to be related to CC plugins sources. Here is my build log: https://pastebin.com/DTn7Szax. I have never seen this before with versions 5.10 and below. Thanks, Mathieu. Hi Sergey, MPC (along with GMP and MPFR) are libraries that GCC and its plugins depend on. I think, installing libmpc-dev or equivalent should fix the problem; installing an equivalent of “apt build-dep gcc” might be more robust. Linux kernel has recently relaxed the way it detects support for compiler plugins, so it’s now enabling them more often than before. Hope this helps, -- Maxim Kuvyrkov https://www.linaro.org Thanks, Sergey
Re: [PATCH v2 12/26] drm/sun4i: Add support for multiple DW HDMI PHY clock parents
Hi, guys, On 05/18/2018 05:46 PM, Jernej Škrabec wrote: Hi, Dne petek, 18. maj 2018 ob 12:01:16 CEST je Maxime Ripard napisal(a): On Fri, May 18, 2018 at 03:15:22PM +0530, Jagan Teki wrote: From: Jernej Skrabec Some SoCs with DW HDMI have multiple possible clock parents, like A64 and R40. Expand HDMI PHY clock driver to support second clock parent. Signed-off-by: Jernej Skrabec Signed-off-by: Jagan Teki --- Changes for v2: - new patch drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 9 ++- drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 33 --- drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c | 89 ++ 3 files changed, 96 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index 79154f0f674a..303189d6602c 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -98,7 +98,8 @@ #define SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN BIT(29) #define SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN BIT(28) #define SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 BIT(27) -#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL BIT(26) +#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK BIT(26) +#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_SHIFT 26 #define SUN8I_HDMI_PHY_PLL_CFG1_PLLEN BIT(25) #define SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(x) ((x) << 22) #define SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(x)((x) << 20) @@ -146,7 +147,7 @@ struct sun8i_hdmi_phy; struct sun8i_hdmi_phy_variant { - bool has_phy_clk; + int phy_clk_num; void (*phy_init)(struct sun8i_hdmi_phy *phy); void (*phy_disable)(struct dw_hdmi *hdmi, struct sun8i_hdmi_phy *phy); @@ -160,6 +161,7 @@ struct sun8i_hdmi_phy { struct clk *clk_mod; struct clk *clk_phy; struct clk *clk_pll0; + struct clk *clk_pll1; unsigned intrcal; struct regmap *regs; struct reset_control*rst_phy; @@ -188,6 +190,7 @@ void sun8i_hdmi_phy_remove(struct sun8i_dw_hdmi *hdmi); void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy); const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void); -int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev); +int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev, +int clk_num); #endif /* _SUN8I_DW_HDMI_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 5a52fc489a9d..0eadf087fc46 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -183,7 +183,13 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,> regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0); - regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, pll_cfg1_init); + /* +* NOTE: We have to be careful not to overwrite PHY parent +* clock selection bit and clock divider. +*/ + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, + (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, + pll_cfg1_init); regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG, (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK, pll_cfg2_init); @@ -232,7 +238,7 @@ 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) + if (phy->variant->phy_clk_num) clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000); return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000); @@ -393,7 +399,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, + .phy_clk_num = 1, .phy_init = &sun8i_hdmi_phy_init_h3, .phy_disable = &sun8i_hdmi_phy_disable_h3, .phy_config = &sun8i_hdmi_phy_config_h3, @@ -464,7 +470,7 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node)> goto err_put_clk_bus; } - if (phy->variant->has_phy_clk) { + if (phy->variant->phy_clk_num) { 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"); @@ -472,7 +478,16 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node)>
Re: [PATCH v2 12/26] drm/sun4i: Add support for multiple DW HDMI PHY clock parents
Hi, Jernej, On 05/18/2018 06:15 PM, Jernej Škrabec wrote: Hi, Dne petek, 18. maj 2018 ob 17:09:40 CEST je Sergey Suloev napisal(a): Hi, guys, On 05/18/2018 05:46 PM, Jernej Škrabec wrote: Hi, Dne petek, 18. maj 2018 ob 12:01:16 CEST je Maxime Ripard napisal(a): On Fri, May 18, 2018 at 03:15:22PM +0530, Jagan Teki wrote: From: Jernej Skrabec Some SoCs with DW HDMI have multiple possible clock parents, like A64 and R40. Expand HDMI PHY clock driver to support second clock parent. Signed-off-by: Jernej Skrabec Signed-off-by: Jagan Teki --- Changes for v2: - new patch drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 9 ++- drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 33 --- drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c | 89 ++ 3 files changed, 96 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h index 79154f0f674a..303189d6602c 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h @@ -98,7 +98,8 @@ #define SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN BIT(29) #define SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN BIT(28) #define SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 BIT(27) -#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL BIT(26) +#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK BIT(26) +#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_SHIFT 26 #define SUN8I_HDMI_PHY_PLL_CFG1_PLLENBIT(25) #define SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(x) ((x) << 22) #define SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(x) ((x) << 20) @@ -146,7 +147,7 @@ struct sun8i_hdmi_phy; struct sun8i_hdmi_phy_variant { - bool has_phy_clk; + int phy_clk_num; void (*phy_init)(struct sun8i_hdmi_phy *phy); void (*phy_disable)(struct dw_hdmi *hdmi, struct sun8i_hdmi_phy *phy); @@ -160,6 +161,7 @@ struct sun8i_hdmi_phy { struct clk *clk_mod; struct clk *clk_phy; struct clk *clk_pll0; + struct clk *clk_pll1; unsigned intrcal; struct regmap *regs; struct reset_control*rst_phy; @@ -188,6 +190,7 @@ void sun8i_hdmi_phy_remove(struct sun8i_dw_hdmi *hdmi); void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy); const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void); -int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev); +int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev, +int clk_num); #endif /* _SUN8I_DW_HDMI_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 5a52fc489a9d..0eadf087fc46 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -183,7 +183,13 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,> regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0); - regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, pll_cfg1_init); + /* +* NOTE: We have to be careful not to overwrite PHY parent +* clock selection bit and clock divider. +*/ + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, + (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, + pll_cfg1_init); regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG, (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK, pll_cfg2_init); @@ -232,7 +238,7 @@ 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) + if (phy->variant->phy_clk_num) clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000); return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000); @@ -393,7 +399,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, + .phy_clk_num = 1, .phy_init = &sun8i_hdmi_phy_init_h3, .phy_disable = &sun8i_hdmi_phy_disable_h3, .phy_config = &sun8i_hdmi_phy_config_h3, @@ -464,7 +470,7 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node)> goto err_put_clk_bus; } - if (phy->variant->has_phy_clk) { + if (phy->variant->phy_clk_num) { p
Re: [PATCH 7/7] ARM: dts: sun7i: Add dts file for the A20-linova1-7 HMI
Hi, Giulio, On 05/05/2018 12:52 AM, Giulio Benetti wrote: Hi Maxime! Il 04/05/2018 10:06, Maxime Ripard ha scritto: Hi, On Wed, May 02, 2018 at 06:41:34PM +0200, Giulio Benetti wrote: You don't have to handcode the fragments anymore with the new syntax, and U-Boot makes it really trivial to use if you use the FIT image format to have multiple overlays bundled in the same image. You can choose to apply them dynamically, for example based on an EEPROM or some other metric to see which combination you have. Ah, this is interesting. I'm going to experiment with that. I'm struggling against this, I don't really know how to proceed, except keeping monolithic dts files including other dtsi files. About dt-overlays I've tried to look around lot of time, but the only thing I've found is this: https://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git/tree/arch/arm/boot/dts?h=topic/renesas-overlays where they use .dtso tagging them as "/plugin/;" and compile all .dtso found in dts folder. Then they obtain .dtbo files that should be the dt-overlays we have spoken about right? Yes. You don't have to do that though, you can just rely on dtc to compile them, outside of the linux build system. What I can't understand is if there's a real standard at this time to follow, because on renesas-driver they use their way to handle all .dtso files, but on mainline there seems to be nothing about that. I'm not sure what you mean here. It's just fragments of device tree, that have to be compiled using dtc, that's it. You can use the Linux build system infrastructure to do that, or you can build your own simpler one. That's really up to you. See for example https://github.com/NextThingCo/CHIP-dt-overlays/blob/master/Makefile (even though the overlays themselves use the legacy syntax and shouldn't really be used an examples) Everything works now! Thank you very much! I've setted up a Repo on Github to give an example on how make it work with no pain: https://github.com/micronovasrl/linova-dtoverlays At the moment it's a mess all around, but it's working and give an idea on how to make it work. Though I'm going to clean it up well as a base for linova dtoverlays. Ah, btw, can you confirm me that base dts file must be compiled outside kernel with: dtc -@ Otherwise as in-tree dts with make dtbs "-@" argument is not passed. Right? Thank you a lot for your help and time again! Best regards! I'd highly recommend you to look at Armbian overlay support: it is easy and elegant. https://github.com/armbian/build/blob/master/patch/kernel/sunxi-next/add-overlay-compilation-support.patch https://github.com/armbian/build/blob/master/patch/kernel/sunxi-next/add-sunxi-overlays.patch Sergey
Re: [PATCH 0/3] Fix broken bananapi m2 devicetree/regulators
On 02/09/2018 08:52 PM, Philipp Rossak wrote: This patchseries fixes the bananapi m1 devicetree, to be able to boot again. The first two patches update/improve the devicetree and the last patch adds all missing regulators. Regards, Philipp Philipp Rossak (3): arm: dts: sun6i: a31s: bpi-m2: update mmc supply nodes arm: dts: sun6i: a31s: bpi-m2: improve pmic properties arm: dts: sun6i: a31s: fix: bpi-m2: add missing regulators arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 70 +++- 1 file changed, 67 insertions(+), 3 deletions(-) patches are not working Thanks
Re: [PATCH 0/3] Fix broken bananapi m2 devicetree/regulators
On 02/11/2018 12:01 AM, Philipp Rossak wrote: Hey Sergey, Thanks for mentioning, but I think the problem has nothing to do with those patches. I tested them with the v4.15.0 Kernel since this is the last stable release and we are right now in the merging window. I tested the latest mainline, without those patches and the kernel is not booting (I can't see any uart output). Thanks, Philipp On 10.02.2018 14:56, Sergey Suloev wrote: On 02/09/2018 08:52 PM, Philipp Rossak wrote: This patchseries fixes the bananapi m1 devicetree, to be able to boot again. The first two patches update/improve the devicetree and the last patch adds all missing regulators. Regards, Philipp Philipp Rossak (3): arm: dts: sun6i: a31s: bpi-m2: update mmc supply nodes arm: dts: sun6i: a31s: bpi-m2: improve pmic properties arm: dts: sun6i: a31s: fix: bpi-m2: add missing regulators arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 70 +++- 1 file changed, 67 insertions(+), 3 deletions(-) patches are not working Thanks same problem, but after applying the patches my device is till hanging.
Re: [PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
On 04/05/2018 12:19 PM, Maxime Ripard wrote: On Wed, Apr 04, 2018 at 02:35:14PM +0300, Sergey Suloev wrote: On 04/04/2018 09:50 AM, Maxime Ripard wrote: On Tue, Apr 03, 2018 at 06:44:46PM +0300, Sergey Suloev wrote: There is no need to handle 3/4 empty interrupt as the maximum supported transfer length in PIO mode is equal to FIFO depth, i.e. 128 bytes for sun6i and 64 bytes for sun8i SoCs. Changes in v3: 1) Restored processing of 3/4 FIFO full interrupt. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 41 + 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 78acc1f..c09ad10 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -207,7 +207,10 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) { - return SUN6I_MAX_XFER_SIZE - 1; + struct spi_master *master = spi->master; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + return sspi->fifo_depth; Doesn't that effectively revert 3288d5cb40c0 ? Why do you need to do so? Need what? Why do you need to revert that change. Change is supposed to restrict max transfer size for PIO mode otherwise it will fail. The maximum transfer length = FIFO depth for PIO mode. The point of that patch was precisely to allow to send more data than the FIFO. You're breaking that behaviour without any justification, and this is not ok. I am sorry, but you can't. That's a hardware limitation. } static int sun6i_spi_prepare_message(struct spi_master *master, @@ -255,8 +258,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master, int ret = 0; u32 reg; - if (tfr->len > SUN6I_MAX_XFER_SIZE) - return -EINVAL; + /* A zero length transfer never finishes if programmed + in the hardware */ Improper comment style here. Please make sure to run checkpatch before sending your patches. ok + if (!tfr->len) + return 0; Can that case even happen? Not sure, just for safety. + /* Don't support transfer larger than the FIFO */ + if (tfr->len > sspi->fifo_depth) + return -EMSGSIZE; You're changing the return type, why? As a more appropriate one The fact that it's more appropriate should be justified. reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; @@ -278,8 +287,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, */ trig_level = sspi->fifo_depth / 4 * 3; sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, - (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | - (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); + (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS)); reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); @@ -343,11 +351,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); /* Enable the interrupts */ - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC | SUN6I_INT_CTL_RF_RDY); - if (tx_len > sspi->fifo_depth) - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); This would also need to be explained in your commit log. What exactly and in what way ? You should explain, at least: A) What is the current behaviour B) Why that is a problem, or what problem does it cause C) What solution you implement and why you think it's justified /* Start the transfer */ reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); @@ -376,7 +381,9 @@ out: static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) { struct sun6i_spi *sspi = dev_id; - u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); + u32 status; + + status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); Why is this change needed? A minor one, for readability. That's arguable, and you should have a single logical change per patch. So this doesn't belong in this one. Maxime ___ linux-arm-kernel mailing list linux-arm-ker...@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
On 04/05/2018 04:17 PM, Mark Brown wrote: On Thu, Apr 05, 2018 at 12:59:35PM +0300, Sergey Suloev wrote: On 04/05/2018 12:19 PM, Maxime Ripard wrote: The point of that patch was precisely to allow to send more data than the FIFO. You're breaking that behaviour without any justification, and this is not ok. I am sorry, but you can't. That's a hardware limitation. Are you positive about that? Normally you can add things to hardware FIFOs while they're being drained so so long as you can keep data flowing in at least as fast as it's being consumed. Well, normally yes, but this is not the case with the hardware that I own. My a20 (BPiM1+) and a31 (BPiM2) boards behaves differently. With a transfer larger than FIFO then TC interrupt never happens.
Re: [PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
On 04/06/2018 10:34 AM, Maxime Ripard wrote: On Thu, Apr 05, 2018 at 04:44:16PM +0300, Sergey Suloev wrote: On 04/05/2018 04:17 PM, Mark Brown wrote: On Thu, Apr 05, 2018 at 12:59:35PM +0300, Sergey Suloev wrote: On 04/05/2018 12:19 PM, Maxime Ripard wrote: The point of that patch was precisely to allow to send more data than the FIFO. You're breaking that behaviour without any justification, and this is not ok. I am sorry, but you can't. That's a hardware limitation. Are you positive about that? Normally you can add things to hardware FIFOs while they're being drained so so long as you can keep data flowing in at least as fast as it's being consumed. Well, normally yes, but this is not the case with the hardware that I own. My a20 (BPiM1+) and a31 (BPiM2) boards behaves differently. With a transfer larger than FIFO then TC interrupt never happens. Because you're not supposed to have a transfer larger than the FIFO, but to have to setup at first a transfer the size of the FIFO, and then when it's (or starts to be) depleted, fill it up again. According to what you said the driver must implement "transfer_one_message" instead of "transfer_one" That's the point of the patch you're reverting, and if it doesn't work, you should make it work and not simply revert it. Maxime
Re: [PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
On 04/06/2018 10:34 AM, Maxime Ripard wrote: On Thu, Apr 05, 2018 at 04:44:16PM +0300, Sergey Suloev wrote: On 04/05/2018 04:17 PM, Mark Brown wrote: On Thu, Apr 05, 2018 at 12:59:35PM +0300, Sergey Suloev wrote: On 04/05/2018 12:19 PM, Maxime Ripard wrote: The point of that patch was precisely to allow to send more data than the FIFO. You're breaking that behaviour without any justification, and this is not ok. I am sorry, but you can't. That's a hardware limitation. Are you positive about that? Normally you can add things to hardware FIFOs while they're being drained so so long as you can keep data flowing in at least as fast as it's being consumed. Well, normally yes, but this is not the case with the hardware that I own. My a20 (BPiM1+) and a31 (BPiM2) boards behaves differently. With a transfer larger than FIFO then TC interrupt never happens. Because you're not supposed to have a transfer larger than the FIFO, but to have to setup at first a transfer the size of the FIFO, and then when it's (or starts to be) depleted, fill it up again. According to what you said the driver must implement "transfer_one_message" instead of "transfer_one", because the maximum transfer length is 64 bytes (for sun4i) and we shouldn't allow "transfer_one" handle more than 64 bytes. Otherwise it breaks the concept. That's the point of the patch you're reverting, and if it doesn't work, you should make it work and not simply revert it. Maxime
Re: [PATCH 2/6] spi: sun4i: restrict transfer length in PIO-mode
On 04/03/2018 11:10 AM, Maxime Ripard wrote: On Thu, Mar 29, 2018 at 09:59:03PM +0300, Sergey Suloev wrote: There is no need to handle 3/4 empty/full interrupts as the maximum supported transfer length in PIO mode is 64 bytes for sun4i-family SoCs. That assumes that you'll be able to treat the FIFO full interrupt and drain the FIFO before we have the next byte coming in. This would require a real time system, and we're not in one of them. Maxime ___ linux-arm-kernel mailing list linux-arm-ker...@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel so you think we should still handle 3/4 FIFO full ?
Re: [PATCH 5/6] spi: sun4i: introduce register set/unset helpers
On 04/03/2018 11:14 AM, Maxime Ripard wrote: On Thu, Mar 29, 2018 at 09:59:06PM +0300, Sergey Suloev wrote: Two helper functions were added in order to update registers easily. Signed-off-by: Sergey Suloev I'm not really sure what's easier about this one. Maxime ___ linux-arm-kernel mailing list linux-arm-ker...@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel well, just seems more readable vs doing "read, or, write" every time
Re: [PATCH 2/6] spi: sun4i: restrict transfer length in PIO-mode
On 04/03/2018 11:10 AM, Maxime Ripard wrote: On Thu, Mar 29, 2018 at 09:59:03PM +0300, Sergey Suloev wrote: There is no need to handle 3/4 empty/full interrupts as the maximum supported transfer length in PIO mode is 64 bytes for sun4i-family SoCs. That assumes that you'll be able to treat the FIFO full interrupt and drain the FIFO before we have the next byte coming in. This would require a real time system, and we're not in one of them. Maxime AFAIK in SPI protocol we send and receive at the same time. As soon as the transfer length is <= FIFO depth then it means that at the moment we get TC interrupt all data for this transfer sent/received already. Is your point here that draining FIFO might be a long operation and we can lose next portion of data ?
Re: [PATCH 2/6] spi: sun4i: restrict transfer length in PIO-mode
On 04/03/2018 02:40 PM, Maxime Ripard wrote: On Tue, Apr 03, 2018 at 02:08:43PM +0300, Sergey Suloev wrote: On 04/03/2018 11:10 AM, Maxime Ripard wrote: On Thu, Mar 29, 2018 at 09:59:03PM +0300, Sergey Suloev wrote: There is no need to handle 3/4 empty/full interrupts as the maximum supported transfer length in PIO mode is 64 bytes for sun4i-family SoCs. That assumes that you'll be able to treat the FIFO full interrupt and drain the FIFO before we have the next byte coming in. This would require a real time system, and we're not in one of them. AFAIK in SPI protocol we send and receive at the same time. It depends. The protocol allows it yes, but most devices I've seen can only operate in half duplex. But it's not really the point. As soon as the transfer length is <= FIFO depth then it means that at the moment we get TC interrupt all data for this transfer sent/received already. Is your point here that draining FIFO might be a long operation and we can lose next portion of data ? My point is that, if you get another interrupt(s) right before the FIFO full interrupt, that interrupt is going to be masked for as long as it is needed for the previous handler(s) to execute. If you're having another byte received while the interrupt is masked, you're losing data. Maxime ___ linux-arm-kernel mailing list linux-arm-ker...@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel ok, I am going to put back 3/4 full handler then.
Re: [PATCH 6/6] spi: sun4i: add DMA transfers support
On 04/03/2018 11:17 AM, Maxime Ripard wrote: On Thu, Mar 29, 2018 at 09:59:07PM +0300, Sergey Suloev wrote: +static int sun4i_spi_dma_setup(struct device *dev, + struct resource *res) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct dma_slave_config dma_sconf; + int ret; + + master->dma_tx = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(master->dma_tx)) { + dev_err(dev, "Unable to acquire DMA TX channel\n"); + ret = PTR_ERR(master->dma_tx); + goto out; + } + + dma_sconf.direction = DMA_MEM_TO_DEV; + dma_sconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; I guess that would depend on the size of the transfer, right? no "this is the width in bytes of the source (RX)register where DMA data shall be read. If the sourceis memory this may be ignored depending on architecture." AFAIK is should be 1 byte for SPI side and seems to be ignored for memory side, but as soon as I don't know what should be correct value for memory side I just put 1 there too. + dma_sconf.dst_addr = res->start + SUN4I_TXDATA_REG; + dma_sconf.dst_maxburst = 1; + dma_sconf.src_maxburst = 1; And a burst of 1 seems sub-optimal here. I did some tests before with 3/4 FIFO size but it didn't work and I got stuck with 1 byte. It seems like 1 byte is the correct value because in SPI protocol we can only send 1 byte in 1 burst. + ret = sun4i_spi_dma_setup(&pdev->dev, res); + if (ret) { + if (ret == -EPROBE_DEFER) { + /* wait for the dma driver to load */ + goto err_free_master; + } + dev_warn(&pdev->dev, "DMA transfer not supported\n"); Saying why it's not supported would be great. I can put more info in this log but there is already a message printed from sun4_spi_dma_setup() if any error occurs Maxime
[PATCH v2 0/6] spi: Add support for DMA transfers in sun4i SPI driver
The following patchset provides corrections for PIO-mode and support for DMA transfers in sun4i SPI driver. Changes in v2: 1) Restored processing of 3/4 FIFO full interrupt. 2) Debug log enhancements. Sergey Suloev (6): spi: core: handle timeout error from transfer_one() spi: sun4i: restrict transfer length in PIO-mode spi: sun4i: coding style/readability improvements spi: sun4i: use completion provided by SPI core driver spi: sun4i: introduce register set/unset helpers spi: sun4i: add DMA transfers support drivers/spi/spi-sun4i.c | 442 +--- drivers/spi/spi.c | 5 +- 2 files changed, 347 insertions(+), 100 deletions(-) -- 2.16.2
[PATCH v2 2/6] spi: sun4i: restrict transfer length in PIO-mode
There is no need to handle the 3/4 FIFO empty interrupt as the maximum supported transfer length in PIO mode is 64 bytes. As long as a problem was reported previously with filling FIFO on A10s we want to stick with 63 bytes depth. Changes in v2: 1) Restored processing of 3/4 FIFO full interrupt. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 37 ++--- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 4141003..08fd007 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -22,7 +22,12 @@ #include -#define SUN4I_FIFO_DEPTH 64 +/* + * FIFO length is 64 bytes + * But filling the FIFO fully might cause a timeout + * on some devices, for example on spi2 on A10s + */ +#define SUN4I_FIFO_DEPTH 63 #define SUN4I_RXDATA_REG 0x00 @@ -202,7 +207,7 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun4i_spi_max_transfer_size(struct spi_device *spi) { - return SUN4I_FIFO_DEPTH - 1; + return SUN4I_FIFO_DEPTH; } static int sun4i_spi_transfer_one(struct spi_master *master, @@ -216,11 +221,8 @@ static int sun4i_spi_transfer_one(struct spi_master *master, int ret = 0; u32 reg; - /* We don't support transfer larger than the FIFO */ - if (tfr->len > SUN4I_MAX_XFER_SIZE) - return -EMSGSIZE; - - if (tfr->tx_buf && tfr->len >= SUN4I_MAX_XFER_SIZE) + /* We don't support transfers larger than FIFO depth */ + if (tfr->len > SUN4I_FIFO_DEPTH) return -EMSGSIZE; reinit_completion(&sspi->done); @@ -313,17 +315,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master, /* * Fill the TX FIFO -* Filling the FIFO fully causes timeout for some reason -* at least on spi2 on A10s */ - sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1); + sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); /* Enable the interrupts */ sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC | SUN4I_INT_CTL_RF_F34); - /* Only enable Tx FIFO interrupt if we really need it */ - if (tx_len > SUN4I_FIFO_DEPTH) - sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34); /* Start the transfer */ reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); @@ -371,20 +368,6 @@ static irqreturn_t sun4i_spi_handler(int irq, void *dev_id) return IRQ_HANDLED; } - /* Transmit FIFO 3/4 empty */ - if (status & SUN4I_INT_CTL_TF_E34) { - sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); - - if (!sspi->len) - /* nothing left to transmit */ - sun4i_spi_disable_interrupt(sspi, SUN4I_INT_CTL_TF_E34); - - /* Only clear the interrupt _after_ re-seeding the FIFO */ - sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TF_E34); - - return IRQ_HANDLED; - } - return IRQ_NONE; } -- 2.16.2
[PATCH v2 6/6] spi: sun4i: add DMA transfers support
DMA transfers are now available for sun4i-family SoCs. The DMA mode is used automatically as soon as requested transfer length is more than FIFO length. Changes in v2: 1) Debug log enhancements. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 299 1 file changed, 277 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index d81d31c..dda7922 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,6 +41,7 @@ #define SUN4I_CTL_CPHA BIT(2) #define SUN4I_CTL_CPOL BIT(3) #define SUN4I_CTL_CS_ACTIVE_LOWBIT(4) +#define SUN4I_CTL_DMA_DEDICATEDBIT(5) #define SUN4I_CTL_LMTF BIT(6) #define SUN4I_CTL_TF_RST BIT(8) #define SUN4I_CTL_RF_RST BIT(9) @@ -58,6 +61,8 @@ #define SUN4I_INT_STA_REG 0x10 #define SUN4I_DMA_CTL_REG 0x14 +#define SUN4I_CTL_DMA_RF_READY BIT(0) +#define SUN4I_CTL_DMA_TF_NOT_FULL BIT(10) #define SUN4I_WAIT_REG 0x18 @@ -169,6 +174,13 @@ static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) } } +static bool sun4i_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + return tfr->len > SUN4I_FIFO_DEPTH; +} + static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) { struct sun4i_spi *sspi = spi_master_get_devdata(spi->master); @@ -208,6 +220,11 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun4i_spi_max_transfer_size(struct spi_device *spi) { + struct spi_master *master = spi->master; + + if (master->can_dma) + return SUN4I_MAX_XFER_SIZE; + return SUN4I_FIFO_DEPTH; } @@ -235,6 +252,164 @@ static int sun4i_spi_wait_for_transfer(struct spi_device *spi, return 0; } +static void sun4i_spi_dma_callback(void *param) +{ + struct spi_master *master = param; + + dev_dbg(&master->dev, "DMA transfer complete\n"); + spi_finalize_current_transfer(master); +} + +static int sun4i_spi_dmap_prep_tx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_tx, + tfr->tx_sg.sgl, tfr->tx_sg.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare TX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun4i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_tx); + + return 0; +} + +static int sun4i_spi_dmap_prep_rx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_rx, + tfr->rx_sg.sgl, tfr->rx_sg.nents, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare RX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun4i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_rx); + + return 0; +} + +static int sun4i_spi_transfer_one_dma(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + struct sun4i_spi *sspi = spi_master_get_devdata(master); + dma_cookie_t tx_cookie = 0, rx_cookie = 0; + enum dma_status status; + int ret; + u32 reg = 0; + + dev_dbg(&master->dev, "Using DMA mode for transfer\n"); + + /* Disable interrupts */ + sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0); + + if (sspi->tx_buf) { + ret = sun4i_spi_dmap_prep_tx(master, tfr, &tx_cookie); +
[PATCH v2 5/6] spi: sun4i: introduce register set/unset helpers
Two helper functions were added in order to set/unset specified flags in registers. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 40 +++- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 9d1bc20..d81d31c 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -107,29 +107,29 @@ static inline void sun4i_spi_write(struct sun4i_spi *sspi, u32 reg, u32 value) writel(value, sspi->base_addr + reg); } -static inline u32 sun4i_spi_get_tx_fifo_count(struct sun4i_spi *sspi) +static inline void sun4i_spi_set(struct sun4i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG); - - reg >>= SUN4I_FIFO_STA_TF_CNT_BITS; + u32 reg = sun4i_spi_read(sspi, addr); - return reg & SUN4I_FIFO_STA_TF_CNT_MASK; + reg |= val; + sun4i_spi_write(sspi, addr, reg); } -static inline void sun4i_spi_enable_interrupt(struct sun4i_spi *sspi, u32 mask) +static inline void sun4i_spi_unset(struct sun4i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG); + u32 reg = sun4i_spi_read(sspi, addr); - reg |= mask; - sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg); + reg &= ~val; + sun4i_spi_write(sspi, addr, reg); } -static inline void sun4i_spi_disable_interrupt(struct sun4i_spi *sspi, u32 mask) +static inline u32 sun4i_spi_get_tx_fifo_count(struct sun4i_spi *sspi) { - u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG); + u32 reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG); - reg &= ~mask; - sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg); + reg >>= SUN4I_FIFO_STA_TF_CNT_BITS; + + return reg & SUN4I_FIFO_STA_TF_CNT_MASK; } static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) @@ -256,13 +256,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master, /* Clear pending interrupts */ sun4i_spi_write(sspi, SUN4I_INT_STA_REG, ~0); + /* Reset FIFOs */ + sun4i_spi_set(sspi, SUN4I_CTL_REG, + SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST); reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); - /* Reset FIFOs */ - sun4i_spi_write(sspi, SUN4I_CTL_REG, - reg | SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST); - /* * Setup the transfer control register: Chip Select, * polarities, etc. @@ -342,12 +341,11 @@ static int sun4i_spi_transfer_one(struct spi_master *master, sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); /* Enable the interrupts */ - sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC | -SUN4I_INT_CTL_RF_F34); + sun4i_spi_set(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC | + SUN4I_INT_CTL_RF_F34); /* Start the transfer */ - reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); - sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH); + sun4i_spi_set(sspi, SUN4I_CTL_REG, SUN4I_CTL_XCH); ret = sun4i_spi_wait_for_transfer(spi, tfr); -- 2.16.2
[PATCH v2 4/6] spi: sun4i: use completion provided by SPI core driver
As long as the completion already provided by the SPI core then there is no need to waste extra-memory on this. Also a waiting function was added to avoid code duplication. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 62 - 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 899e956..9d1bc20 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -92,8 +92,6 @@ struct sun4i_spi { struct clk *hclk; struct clk *mclk; - struct completion done; - const u8*tx_buf; u8 *rx_buf; int len; @@ -213,13 +211,36 @@ static size_t sun4i_spi_max_transfer_size(struct spi_device *spi) return SUN4I_FIFO_DEPTH; } +static int sun4i_spi_wait_for_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + unsigned int start, end, tx_time; + unsigned int timeout; + + /* calc required timeout from given speed & len values */ + tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); + start = jiffies; + timeout = wait_for_completion_timeout(&master->xfer_completion, + msecs_to_jiffies(tx_time)); + end = jiffies; + if (!timeout) { + dev_warn(&master->dev, +"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", +dev_name(&spi->dev), tfr->len, tfr->speed_hz, +jiffies_to_msecs(end - start), tx_time); + return -ETIMEDOUT; + } + + return 0; +} + static int sun4i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) { struct sun4i_spi *sspi = spi_master_get_devdata(master); - unsigned int mclk_rate, div, timeout; - unsigned int start, end, tx_time; + unsigned int mclk_rate, div; unsigned int tx_len = 0; int ret = 0; u32 reg; @@ -228,7 +249,6 @@ static int sun4i_spi_transfer_one(struct spi_master *master, if (tfr->len > SUN4I_FIFO_DEPTH) return -EMSGSIZE; - reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; sspi->rx_buf = tfr->rx_buf; sspi->len = tfr->len; @@ -329,22 +349,8 @@ static int sun4i_spi_transfer_one(struct spi_master *master, reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH); - tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); - start = jiffies; - timeout = wait_for_completion_timeout(&sspi->done, - msecs_to_jiffies(tx_time)); - end = jiffies; - if (!timeout) { - dev_warn(&master->dev, -"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", -dev_name(&spi->dev), tfr->len, tfr->speed_hz, -jiffies_to_msecs(end - start), tx_time); - ret = -ETIMEDOUT; - goto out; - } - + ret = sun4i_spi_wait_for_transfer(spi, tfr); -out: sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0); return ret; @@ -352,14 +358,18 @@ out: static irqreturn_t sun4i_spi_handler(int irq, void *dev_id) { - struct sun4i_spi *sspi = dev_id; - u32 status = sun4i_spi_read(sspi, SUN4I_INT_STA_REG); + struct spi_master *master = dev_id; + struct sun4i_spi *sspi = spi_master_get_devdata(master); + u32 status; + + status = sun4i_spi_read(sspi, SUN4I_INT_STA_REG); /* Transfer complete */ if (status & SUN4I_INT_CTL_TC) { - sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC); + sun4i_spi_write(sspi, SUN4I_INT_STA_REG, + SUN4I_INT_CTL_TC); sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); - complete(&sspi->done); + spi_finalize_current_transfer(master); return IRQ_HANDLED; } @@ -456,7 +466,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler, - 0, dev_name(&pdev->dev), sspi); + 0, dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); goto err_free_master; @@ -476,8 +486,6 @@ static int sun4i_spi_probe(struct
[PATCH v2 3/6] spi: sun4i: coding style/readability improvements
Minor changes to fulfill the coding style and improve the readability. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 32 +--- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 08fd007..899e956 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -83,8 +83,11 @@ #define SUN4I_FIFO_STA_TF_CNT_MASK 0x7f #define SUN4I_FIFO_STA_TF_CNT_BITS 16 +#define SUN4I_SPI_MAX_SPEED_HZ 100 * 1000 * 1000 +#define SUN4I_SPI_MIN_SPEED_HZ 3 * 1000 +#define SUN4I_SPI_MODE_BITS(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST) + struct sun4i_spi { - struct spi_master *master; void __iomem*base_addr; struct clk *hclk; struct clk *mclk; @@ -418,12 +421,23 @@ static int sun4i_spi_probe(struct platform_device *pdev) struct resource *res; int ret = 0, irq; - master = spi_alloc_master(&pdev->dev, sizeof(struct sun4i_spi)); + master = spi_alloc_master(&pdev->dev, sizeof(*sspi)); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); return -ENOMEM; } + master->max_speed_hz = SUN4I_SPI_MAX_SPEED_HZ; + master->min_speed_hz = SUN4I_SPI_MIN_SPEED_HZ; + master->num_chipselect = 4; + master->mode_bits = SUN4I_SPI_MODE_BITS; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->set_cs = sun4i_spi_set_cs; + master->transfer_one = sun4i_spi_transfer_one; + master->max_transfer_size = sun4i_spi_max_transfer_size; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; + platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); @@ -442,24 +456,12 @@ static int sun4i_spi_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler, - 0, "sun4i-spi", sspi); + 0, dev_name(&pdev->dev), sspi); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); goto err_free_master; } - sspi->master = master; - master->max_speed_hz = 100 * 1000 * 1000; - master->min_speed_hz = 3 * 1000; - master->set_cs = sun4i_spi_set_cs; - master->transfer_one = sun4i_spi_transfer_one; - master->num_chipselect = 4; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; - master->bits_per_word_mask = SPI_BPW_MASK(8); - master->dev.of_node = pdev->dev.of_node; - master->auto_runtime_pm = true; - master->max_transfer_size = sun4i_spi_max_transfer_size; - sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sspi->hclk)) { dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); -- 2.16.2
[PATCH v2 1/6] spi: core: handle timeout error from transfer_one()
As long as sun4i/sun6i SPI drivers have overriden the default "wait for completion" procedure then we need to properly handle -ETIMEDOUT error from transfer_one(). Signed-off-by: Sergey Suloev --- drivers/spi/spi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b33a727..2dcd4f6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1028,7 +1028,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, reinit_completion(&ctlr->xfer_completion); ret = ctlr->transfer_one(ctlr, msg->spi, xfer); - if (ret < 0) { + if (ret < 0 && ret != -ETIMEDOUT) { SPI_STATISTICS_INCREMENT_FIELD(statm, errors); SPI_STATISTICS_INCREMENT_FIELD(stats, @@ -1051,7 +1051,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, msecs_to_jiffies(ms)); } - if (ms == 0) { + if (ms == 0 || ret == -ETIMEDOUT) { SPI_STATISTICS_INCREMENT_FIELD(statm, timedout); SPI_STATISTICS_INCREMENT_FIELD(stats, @@ -1059,6 +1059,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, dev_err(&msg->spi->dev, "SPI transfer timed out\n"); msg->status = -ETIMEDOUT; + ret = 0; } } else { if (xfer->len) -- 2.16.2
[PATCH v3 6/6] spi: sun6i: add DMA transfers support
DMA transfers are now available for sun6i and sun8i SoCs. The DMA mode is used automatically as soon as requested transfer length is more than FIFO length. Changes in v3: 1) Debug log enhancements. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 331 ++-- 1 file changed, 294 insertions(+), 37 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 2fa9d6e..7f41871 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -55,17 +57,20 @@ #define SUN6I_FIFO_CTL_REG 0x18 #define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_MASK 0xff -#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS 0 +#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_POS 0 +#define SUN6I_FIFO_CTL_RF_DRQ_EN BIT(8) #define SUN6I_FIFO_CTL_RF_RST BIT(15) #define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_MASK 0xff -#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS 16 +#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_POS 16 +#define SUN6I_FIFO_CTL_TF_DRQ_EN BIT(24) #define SUN6I_FIFO_CTL_TF_RST BIT(31) +#define SUN6I_FIFO_CTL_DMA_DEDICATEBIT(9)|BIT(25) #define SUN6I_FIFO_STA_REG 0x1c #define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f -#define SUN6I_FIFO_STA_RF_CNT_BITS 0 +#define SUN6I_FIFO_STA_RF_CNT_POS 0 #define SUN6I_FIFO_STA_TF_CNT_MASK 0x7f -#define SUN6I_FIFO_STA_TF_CNT_BITS 16 +#define SUN6I_FIFO_STA_TF_CNT_POS 16 #define SUN6I_CLK_CTL_REG 0x24 #define SUN6I_CLK_CTL_CDR2_MASK0xff @@ -135,7 +140,7 @@ static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi) { u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); - reg >>= SUN6I_FIFO_STA_TF_CNT_BITS; + reg >>= SUN6I_FIFO_STA_TF_CNT_POS; return reg & SUN6I_FIFO_STA_TF_CNT_MASK; } @@ -148,7 +153,7 @@ static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len) /* See how much data is available */ reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); reg &= SUN6I_FIFO_STA_RF_CNT_MASK; - cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS; + cnt = reg >> SUN6I_FIFO_STA_RF_CNT_POS; if (len > cnt) len = cnt; @@ -177,6 +182,15 @@ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len) } } +static bool sun6i_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + return tfr->len > sspi->fifo_depth; +} + static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) { struct sun6i_spi *sspi = spi_master_get_devdata(spi->master); @@ -208,6 +222,9 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) struct spi_master *master = spi->master; struct sun6i_spi *sspi = spi_master_get_devdata(master); + if (master->can_dma) + return SUN6I_MAX_XFER_SIZE; + return sspi->fifo_depth; } @@ -268,16 +285,187 @@ static int sun6i_spi_wait_for_transfer(struct spi_device *spi, return 0; } +static void sun6i_spi_dma_callback(void *param) +{ + struct spi_master *master = param; + + dev_dbg(&master->dev, "DMA transfer complete\n"); + spi_finalize_current_transfer(master); +} + +static int sun6i_spi_dmap_prep_tx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_tx, + tfr->tx_sg.sgl, tfr->tx_sg.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare TX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun6i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_tx); + + return 0; +} + +static int sun6i_spi_dmap_prep_rx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_rx, +
[PATCH v3 5/6] spi: sun6i: introduce register set/unset helpers
Two helper functions were added in order to set/unset specified flags in registers. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 37 - 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 0912404..2fa9d6e 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -115,29 +115,29 @@ static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value) writel(value, sspi->base_addr + reg); } -static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi) +static inline void sun6i_spi_set(struct sun6i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); - - reg >>= SUN6I_FIFO_STA_TF_CNT_BITS; + u32 reg = sun6i_spi_read(sspi, addr); - return reg & SUN6I_FIFO_STA_TF_CNT_MASK; + reg |= val; + sun6i_spi_write(sspi, addr, reg); } -static inline void sun6i_spi_enable_interrupt(struct sun6i_spi *sspi, u32 mask) +static inline void sun6i_spi_unset(struct sun6i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG); + u32 reg = sun6i_spi_read(sspi, addr); - reg |= mask; - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); + reg &= ~val; + sun6i_spi_write(sspi, addr, reg); } -static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask) +static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi) { - u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG); + u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); - reg &= ~mask; - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); + reg >>= SUN6I_FIFO_STA_TF_CNT_BITS; + + return reg & SUN6I_FIFO_STA_TF_CNT_MASK; } static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len) @@ -310,18 +310,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS)); - - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); /* * If it's a TX only transfer, we don't want to fill the RX * FIFO with bogus data */ if (sspi->rx_buf) - reg &= ~SUN6I_TFR_CTL_DHB; + sun6i_spi_unset(sspi, SUN6I_TFR_CTL_REG, SUN6I_TFR_CTL_DHB); else - reg |= SUN6I_TFR_CTL_DHB; - - sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); + sun6i_spi_set(sspi, SUN6I_TFR_CTL_REG, SUN6I_TFR_CTL_DHB); /* Ensure that we have a parent clock fast enough */ @@ -376,8 +372,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, SUN6I_INT_CTL_RF_RDY); /* Start the transfer */ - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); - sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); + sun6i_spi_set(sspi, SUN6I_TFR_CTL_REG, SUN6I_TFR_CTL_XCH); /* Wait for completion */ ret = sun6i_spi_wait_for_transfer(spi, tfr); -- 2.16.2
[PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
There is no need to handle 3/4 empty interrupt as the maximum supported transfer length in PIO mode is equal to FIFO depth, i.e. 128 bytes for sun6i and 64 bytes for sun8i SoCs. Changes in v3: 1) Restored processing of 3/4 FIFO full interrupt. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 41 + 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 78acc1f..c09ad10 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -207,7 +207,10 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) { - return SUN6I_MAX_XFER_SIZE - 1; + struct spi_master *master = spi->master; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + return sspi->fifo_depth; } static int sun6i_spi_prepare_message(struct spi_master *master, @@ -255,8 +258,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master, int ret = 0; u32 reg; - if (tfr->len > SUN6I_MAX_XFER_SIZE) - return -EINVAL; + /* A zero length transfer never finishes if programmed + in the hardware */ + if (!tfr->len) + return 0; + + /* Don't support transfer larger than the FIFO */ + if (tfr->len > sspi->fifo_depth) + return -EMSGSIZE; reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; @@ -278,8 +287,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, */ trig_level = sspi->fifo_depth / 4 * 3; sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, - (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | - (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); + (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS)); reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); @@ -343,11 +351,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); /* Enable the interrupts */ - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC | SUN6I_INT_CTL_RF_RDY); - if (tx_len > sspi->fifo_depth) - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); /* Start the transfer */ reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); @@ -376,7 +381,9 @@ out: static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) { struct sun6i_spi *sspi = dev_id; - u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); + u32 status; + + status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); /* Transfer complete */ if (status & SUN6I_INT_CTL_TC) { @@ -388,26 +395,12 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) /* Receive FIFO 3/4 full */ if (status & SUN6I_INT_CTL_RF_RDY) { - sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH); + sun6i_spi_drain_fifo(sspi, sspi->fifo_depth); /* Only clear the interrupt _after_ draining the FIFO */ sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_RF_RDY); return IRQ_HANDLED; } - /* Transmit FIFO 3/4 empty */ - if (status & SUN6I_INT_CTL_TF_ERQ) { - sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH); - - if (!sspi->len) - /* nothing left to transmit */ - sun6i_spi_disable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); - - /* Only clear the interrupt _after_ re-seeding the FIFO */ - sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TF_ERQ); - - return IRQ_HANDLED; - } - return IRQ_NONE; } -- 2.16.2
[PATCH v3 0/6] spi: Add support for DMA transfers in sun6i SPI driver
The following patchset provides corrections for PIO-mode and support for DMA transfers in sun6i SPI driver. Changes in v2: 1) Fixed issue with misplacing a piece of code that requires access to the transfer structure into sun6i_spi_prepare_message() function where the transfer structure is not available. 2) Fixed issue with passing an invalid argument into devm_request_irq() function. Changes in v3: 1) Restored processing of 3/4 FIFO full interrupt. 2) Debug log enhancements. Sergey Suloev (6): spi: sun6i: coding style/readability improvements spi: sun6i: handle chip select polarity flag spi: sun6i: restrict transfer length in PIO-mode spi: sun6i: use completion provided by SPI core spi: sun6i: introduce register set/unset helpers spi: sun6i: add DMA transfers support drivers/spi/spi-sun6i.c | 526 1 file changed, 402 insertions(+), 124 deletions(-) -- 2.16.2
[PATCH v3 4/6] spi: sun6i: use completion provided by SPI core
As long as the completion is already provided by the SPI core then there is no need to waste extra-memory on this. Also a waiting function was added to avoid code duplication. Changes in v2: 1) Fixed issue with passing an invalid argument into devm_request_irq() function. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 52 - 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index c09ad10..0912404 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -99,8 +99,6 @@ struct sun6i_spi { struct clk *mclk; struct reset_control*rstc; - struct completion done; - const u8*tx_buf; u8 *rx_buf; int len; @@ -246,6 +244,30 @@ static int sun6i_spi_prepare_message(struct spi_master *master, return 0; } +static int sun6i_spi_wait_for_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + unsigned int start, end, tx_time; + unsigned int timeout; + + /* smart wait for completion */ + tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); + start = jiffies; + timeout = wait_for_completion_timeout(&master->xfer_completion, + msecs_to_jiffies(tx_time)); + end = jiffies; + if (!timeout) { + dev_warn(&master->dev, +"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", +dev_name(&spi->dev), tfr->len, tfr->speed_hz, +jiffies_to_msecs(end - start), tx_time); + return -ETIMEDOUT; + } + + return 0; +} + static int sun6i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) @@ -267,7 +289,6 @@ static int sun6i_spi_transfer_one(struct spi_master *master, if (tfr->len > sspi->fifo_depth) return -EMSGSIZE; - reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; sspi->rx_buf = tfr->rx_buf; sspi->len = tfr->len; @@ -358,21 +379,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master, reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); - tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); - start = jiffies; - timeout = wait_for_completion_timeout(&sspi->done, - msecs_to_jiffies(tx_time)); - end = jiffies; - if (!timeout) { - dev_warn(&master->dev, -"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", -dev_name(&spi->dev), tfr->len, tfr->speed_hz, -jiffies_to_msecs(end - start), tx_time); - ret = -ETIMEDOUT; - goto out; - } + /* Wait for completion */ + ret = sun6i_spi_wait_for_transfer(spi, tfr); -out: sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); return ret; @@ -380,7 +389,8 @@ out: static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) { - struct sun6i_spi *sspi = dev_id; + struct spi_master *master = dev_id; + struct sun6i_spi *sspi = spi_master_get_devdata(master); u32 status; status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); @@ -389,7 +399,7 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) if (status & SUN6I_INT_CTL_TC) { sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); sun6i_spi_drain_fifo(sspi, sspi->fifo_depth); - complete(&sspi->done); + spi_finalize_current_transfer(master); return IRQ_HANDLED; } @@ -496,7 +506,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler, - 0, dev_name(&pdev->dev), sspi); + 0, dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); goto err_free_master; @@ -518,8 +528,6 @@ static int sun6i_spi_probe(struct platform_device *pdev) goto err_free_master; } - init_completion(&sspi->done); - sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(sspi->rstc)) { dev_err(&pdev->dev, "Couldn't get reset controller\n"); -- 2.16.2
[PATCH v3 2/6] spi: sun6i: handle chip select polarity flag
The chip select polarity flag is declared as supported but is not handled in the code. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 88ad45e..78acc1f 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -193,6 +193,12 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) else reg &= ~SUN6I_TFR_CTL_CS_LEVEL; + /* Handle chip select "reverse" polarity */ + if (spi->mode & SPI_CS_HIGH) + reg &= ~SUN6I_TFR_CTL_SPOL; + else + reg |= SUN6I_TFR_CTL_SPOL; + /* We want to control the chip select manually */ reg |= SUN6I_TFR_CTL_CS_MANUAL; -- 2.16.2
[PATCH v3 1/6] spi: sun6i: coding style/readability improvements
Minor changes to fulfill the coding style and improve the readability of the code. Changes in v2: 1) Fixed issue with misplacing a piece of code that requires access to the transfer structure into sun6i_spi_prepare_message() function where the transfer structure is not available. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 97 + 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 8533f4e..88ad45e 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -88,8 +88,12 @@ #define SUN6I_TXDATA_REG 0x200 #define SUN6I_RXDATA_REG 0x300 +#define SUN6I_SPI_MODE_BITS(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST) + +#define SUN6I_SPI_MAX_SPEED_HZ 1 +#define SUN6I_SPI_MIN_SPEED_HZ 3000 + struct sun6i_spi { - struct spi_master *master; void __iomem*base_addr; struct clk *hclk; struct clk *mclk; @@ -189,6 +193,9 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) else reg &= ~SUN6I_TFR_CTL_CS_LEVEL; + /* We want to control the chip select manually */ + reg |= SUN6I_TFR_CTL_CS_MANUAL; + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); } @@ -197,6 +204,39 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) return SUN6I_MAX_XFER_SIZE - 1; } +static int sun6i_spi_prepare_message(struct spi_master *master, +struct spi_message *msg) +{ + struct spi_device *spi = msg->spi; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + u32 reg; + + /* +* Setup the transfer control register: Chip Select, +* polarities, etc. +*/ + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + + if (spi->mode & SPI_CPOL) + reg |= SUN6I_TFR_CTL_CPOL; + else + reg &= ~SUN6I_TFR_CTL_CPOL; + + if (spi->mode & SPI_CPHA) + reg |= SUN6I_TFR_CTL_CPHA; + else + reg &= ~SUN6I_TFR_CTL_CPHA; + + if (spi->mode & SPI_LSB_FIRST) + reg |= SUN6I_TFR_CTL_FBS; + else + reg &= ~SUN6I_TFR_CTL_FBS; + + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); + + return 0; +} + static int sun6i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) @@ -235,27 +275,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); - /* -* Setup the transfer control register: Chip Select, -* polarities, etc. -*/ - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); - - if (spi->mode & SPI_CPOL) - reg |= SUN6I_TFR_CTL_CPOL; - else - reg &= ~SUN6I_TFR_CTL_CPOL; - - if (spi->mode & SPI_CPHA) - reg |= SUN6I_TFR_CTL_CPHA; - else - reg &= ~SUN6I_TFR_CTL_CPHA; - - if (spi->mode & SPI_LSB_FIRST) - reg |= SUN6I_TFR_CTL_FBS; - else - reg &= ~SUN6I_TFR_CTL_FBS; + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); /* * If it's a TX only transfer, we don't want to fill the RX * FIFO with bogus data @@ -265,11 +286,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master, else reg |= SUN6I_TFR_CTL_DHB; - /* We want to control the chip select manually */ - reg |= SUN6I_TFR_CTL_CS_MANUAL; - 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)) { @@ -442,12 +461,24 @@ static int sun6i_spi_probe(struct platform_device *pdev) struct resource *res; int ret = 0, irq; - master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); + master = spi_alloc_master(&pdev->dev, sizeof(*sspi)); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); return -ENOMEM; } + master->max_speed_hz = SUN6I_SPI_MAX_SPEED_HZ; + master->min_speed_hz = SUN6I_SPI_MIN_SPEED_HZ; + master->num_chipselect = 4; + master->mode_bits = SUN6I_SPI_MODE_BITS; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->set_cs = sun6i_spi_set_cs; + master->prepare_message = sun6i_spi_prepare_message
Re: [PATCH v2 1/6] spi: core: handle timeout error from transfer_one()
On 04/03/2018 06:52 PM, Mark Brown wrote: On Tue, Apr 03, 2018 at 06:29:00PM +0300, Sergey Suloev wrote: As long as sun4i/sun6i SPI drivers have overriden the default "wait for completion" procedure then we need to properly handle -ETIMEDOUT error from transfer_one(). Why is this connected to those drivers specifically? These 2 drivers have their own "waiting" code and not using the code from SPI core.
Re: [PATCH v2 1/6] spi: core: handle timeout error from transfer_one()
On 04/03/2018 07:18 PM, Mark Brown wrote: On Tue, Apr 03, 2018 at 07:00:55PM +0300, Sergey Suloev wrote: On 04/03/2018 06:52 PM, Mark Brown wrote: On Tue, Apr 03, 2018 at 06:29:00PM +0300, Sergey Suloev wrote: As long as sun4i/sun6i SPI drivers have overriden the default "wait for completion" procedure then we need to properly handle -ETIMEDOUT error from transfer_one(). Why is this connected to those drivers specifically? These 2 drivers have their own "waiting" code and not using the code from SPI core. Does this not apply to any other driver - why is this something we only have to do when these drivers do it? That's what's setting off alarm bells. sun4i/sun6i drivers have let's say "smart" waiting while SPI core uses a fixed interval to wait. I can't say for every SPI driver in kernel, that's outside of my area of expertise.
Re: [PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
On 04/04/2018 09:50 AM, Maxime Ripard wrote: On Tue, Apr 03, 2018 at 06:44:46PM +0300, Sergey Suloev wrote: There is no need to handle 3/4 empty interrupt as the maximum supported transfer length in PIO mode is equal to FIFO depth, i.e. 128 bytes for sun6i and 64 bytes for sun8i SoCs. Changes in v3: 1) Restored processing of 3/4 FIFO full interrupt. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 41 + 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 78acc1f..c09ad10 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -207,7 +207,10 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) { - return SUN6I_MAX_XFER_SIZE - 1; + struct spi_master *master = spi->master; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + return sspi->fifo_depth; Doesn't that effectively revert 3288d5cb40c0 ? Why do you need to do so? Need what? Change is supposed to restrict max transfer size for PIO mode otherwise it will fail. The maximum transfer length = FIFO depth for PIO mode. } static int sun6i_spi_prepare_message(struct spi_master *master, @@ -255,8 +258,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master, int ret = 0; u32 reg; - if (tfr->len > SUN6I_MAX_XFER_SIZE) - return -EINVAL; + /* A zero length transfer never finishes if programmed + in the hardware */ Improper comment style here. Please make sure to run checkpatch before sending your patches. ok + if (!tfr->len) + return 0; Can that case even happen? Not sure, just for safety. + /* Don't support transfer larger than the FIFO */ + if (tfr->len > sspi->fifo_depth) + return -EMSGSIZE; You're changing the return type, why? As a more appropriate one reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; @@ -278,8 +287,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, */ trig_level = sspi->fifo_depth / 4 * 3; sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, - (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | - (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); + (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS)); reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); @@ -343,11 +351,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); /* Enable the interrupts */ - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC | SUN6I_INT_CTL_RF_RDY); - if (tx_len > sspi->fifo_depth) - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); This would also need to be explained in your commit log. What exactly and in what way ? /* Start the transfer */ reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); @@ -376,7 +381,9 @@ out: static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) { struct sun6i_spi *sspi = dev_id; - u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); + u32 status; + + status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); Why is this change needed? A minor one, for readability. Maxime
Re: [PATCH v2 1/6] spi: core: handle timeout error from transfer_one()
On 04/04/2018 10:08 AM, Maxime Ripard wrote: On Tue, Apr 03, 2018 at 07:24:11PM +0300, Sergey Suloev wrote: On 04/03/2018 07:18 PM, Mark Brown wrote: On Tue, Apr 03, 2018 at 07:00:55PM +0300, Sergey Suloev wrote: On 04/03/2018 06:52 PM, Mark Brown wrote: On Tue, Apr 03, 2018 at 06:29:00PM +0300, Sergey Suloev wrote: As long as sun4i/sun6i SPI drivers have overriden the default "wait for completion" procedure then we need to properly handle -ETIMEDOUT error from transfer_one(). Why is this connected to those drivers specifically? These 2 drivers have their own "waiting" code and not using the code from SPI core. Does this not apply to any other driver - why is this something we only have to do when these drivers do it? That's what's setting off alarm bells. sun4i/sun6i drivers have let's say "smart" waiting while SPI core uses a fixed interval to wait. I can't say for every SPI driver in kernel, that's outside of my area of expertise. I'm not sure what's specific about the sun4i / sun6i case here. Your patch doesn't have anything to do with the delay before the timeout, but the fact that we return -ETIMEDOUT in the first place. And I'm pretty sure that papering over an error returned by a driver is not the right thing to do. Maxime ___ linux-arm-kernel mailing list linux-arm-ker...@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel do you mean the changes in spi.c are not required at all ? My point was to eat ETIMEDOUT error from transfer_one() as it is just a mark and shouldn't be handled as a normal error.
Re: [PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
On 04/09/2018 12:27 PM, Maxime Ripard wrote: On Fri, Apr 06, 2018 at 06:48:23PM +0300, Sergey Suloev wrote: On 04/06/2018 10:34 AM, Maxime Ripard wrote: On Thu, Apr 05, 2018 at 04:44:16PM +0300, Sergey Suloev wrote: On 04/05/2018 04:17 PM, Mark Brown wrote: On Thu, Apr 05, 2018 at 12:59:35PM +0300, Sergey Suloev wrote: On 04/05/2018 12:19 PM, Maxime Ripard wrote: The point of that patch was precisely to allow to send more data than the FIFO. You're breaking that behaviour without any justification, and this is not ok. I am sorry, but you can't. That's a hardware limitation. Are you positive about that? Normally you can add things to hardware FIFOs while they're being drained so so long as you can keep data flowing in at least as fast as it's being consumed. Well, normally yes, but this is not the case with the hardware that I own. My a20 (BPiM1+) and a31 (BPiM2) boards behaves differently. With a transfer larger than FIFO then TC interrupt never happens. Because you're not supposed to have a transfer larger than the FIFO, but to have to setup at first a transfer the size of the FIFO, and then when it's (or starts to be) depleted, fill it up again. According to what you said the driver must implement "transfer_one_message" instead of "transfer_one" I'm not sure what makes you think that I said that. Maxime ___ linux-arm-kernel mailing list linux-arm-ker...@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel Because current implementation tries to send more than FIFO-depth of data in a single call to "transfer_one" which is wrong.
Re: [PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
On 04/09/2018 01:50 PM, Mark Brown wrote: On Mon, Apr 09, 2018 at 01:26:23PM +0300, Sergey Suloev wrote: On 04/09/2018 12:27 PM, Maxime Ripard wrote: On Fri, Apr 06, 2018 at 06:48:23PM +0300, Sergey Suloev wrote: On 04/06/2018 10:34 AM, Maxime Ripard wrote: According to what you said the driver must implement "transfer_one_message" instead of "transfer_one" I'm not sure what makes you think that I said that. Because current implementation tries to send more than FIFO-depth of data in a single call to "transfer_one" which is wrong. No, that's absolutely not the case. All any of these functions has to do is transfer whatever they were asked to, how they do it is not at all important to the framework. I think you don't fully understand the issue. Let's talk about sun4i and sun6i SPI drivers separately. sun4i 1)it is correctly declaring max_transfer_size=FIFO depth for PIO mode but transfer_one() function doesn't follow the declaration allowing PIO transfers longer than FIFO depth by just refilling FIFO using 3/4 FIFO empty interrupt. I can definitely state here that long transfers WON'T WORK on real hardware. I tested it and that's why I can say that. But as soon as sun4i SPI driver is correctly declaring max_transfer_size then "smart" clients will work well by limiting a single transfer size to FIFO depth. I tested it with real hardware, again. sun6i 2) it allows PIO transfers of any length by declaring max_transfer_size to a huge number, i.e. you can ONLY make this driver work in PIO mode by limiting a single transfer size to FIFO depth (64 or 128 bytes) on client side and ignore max_transfer_size exposed by the driver. Again, tested with real hardware. All above doesn't work for DMA mode as there is no such limitation. I can't clearly explain what is happening in the hardware in PIO mode but it seems that TC interrupt doesn't arrive in time when refilling FIFO multiple times takes place and every long transfer will end up with a timeout error.
Re: [PATCH v3 3/6] spi: sun6i: restrict transfer length in PIO-mode
On 04/09/2018 02:36 PM, Maxime Ripard wrote: On Mon, Apr 09, 2018 at 02:10:40PM +0300, Sergey Suloev wrote: On 04/09/2018 01:50 PM, Mark Brown wrote: On Mon, Apr 09, 2018 at 01:26:23PM +0300, Sergey Suloev wrote: On 04/09/2018 12:27 PM, Maxime Ripard wrote: On Fri, Apr 06, 2018 at 06:48:23PM +0300, Sergey Suloev wrote: On 04/06/2018 10:34 AM, Maxime Ripard wrote: According to what you said the driver must implement "transfer_one_message" instead of "transfer_one" I'm not sure what makes you think that I said that. Because current implementation tries to send more than FIFO-depth of data in a single call to "transfer_one" which is wrong. No, that's absolutely not the case. All any of these functions has to do is transfer whatever they were asked to, how they do it is not at all important to the framework. I think you don't fully understand the issue. Let's talk about sun4i and sun6i SPI drivers separately. sun4i 1)it is correctly declaring max_transfer_size=FIFO depth for PIO mode but transfer_one() function doesn't follow the declaration allowing PIO transfers longer than FIFO depth by just refilling FIFO using 3/4 FIFO empty interrupt. I can definitely state here that long transfers WON'T WORK on real hardware. Surely the original author of the patch allowing to do just that disagrees with you. I am not getting the point why the driver is declaring the max transfer length value and not following the rule. And it's not about the hardware itself, it's about how the driver operates as well. I tested it and that's why I can say that. Then it must be fixed, and not silently reverted. But as soon as sun4i SPI driver is correctly declaring max_transfer_size then "smart" clients will work well by limiting a single transfer size to FIFO depth. I tested it with real hardware, again. This is really not my point. What would prevent you from doing multiple transfers in that case, and filling the FIFO entirely, waiting for it to be done, then resuming until you have sent the right number of bytes? Because it makes no sense IMHO. I can't see any single point in allowing long PIO transfers. Can you find at least one ? I think we should reuse as much SPI core code as possible. The SPI core can handle an SPI message with multiple transfers, all we need is to have max_transfer_size = FIFO depth and restrict it in transfer_one(). Maxime ___ linux-arm-kernel mailing list linux-arm-ker...@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
[PATCH 5/6] spi: sun4i: introduce register set/unset helpers
Two helper functions were added in order to update registers easily. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 40 +++- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 4f24e12..fc913d4 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -107,29 +107,29 @@ static inline void sun4i_spi_write(struct sun4i_spi *sspi, u32 reg, u32 value) writel(value, sspi->base_addr + reg); } -static inline u32 sun4i_spi_get_tx_fifo_count(struct sun4i_spi *sspi) +static inline void sun4i_spi_set(struct sun4i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG); - - reg >>= SUN4I_FIFO_STA_TF_CNT_BITS; + u32 reg = sun4i_spi_read(sspi, addr); - return reg & SUN4I_FIFO_STA_TF_CNT_MASK; + reg |= val; + sun4i_spi_write(sspi, addr, reg); } -static inline void sun4i_spi_enable_interrupt(struct sun4i_spi *sspi, u32 mask) +static inline void sun4i_spi_unset(struct sun4i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG); + u32 reg = sun4i_spi_read(sspi, addr); - reg |= mask; - sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg); + reg &= ~val; + sun4i_spi_write(sspi, addr, reg); } -static inline void sun4i_spi_disable_interrupt(struct sun4i_spi *sspi, u32 mask) +static inline u32 sun4i_spi_get_tx_fifo_count(struct sun4i_spi *sspi) { - u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG); + u32 reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG); - reg &= ~mask; - sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg); + reg >>= SUN4I_FIFO_STA_TF_CNT_BITS; + + return reg & SUN4I_FIFO_STA_TF_CNT_MASK; } static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) @@ -256,13 +256,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master, /* Clear pending interrupts */ sun4i_spi_write(sspi, SUN4I_INT_STA_REG, ~0); + /* Reset FIFOs */ + sun4i_spi_set(sspi, SUN4I_CTL_REG, + SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST); reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); - /* Reset FIFOs */ - sun4i_spi_write(sspi, SUN4I_CTL_REG, - reg | SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST); - /* * Setup the transfer control register: Chip Select, * polarities, etc. @@ -341,12 +340,11 @@ static int sun4i_spi_transfer_one(struct spi_master *master, */ sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); - /* Enable the transfer complete interrupt */ - sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC); + /* Enable transfer complete interrupt */ + sun4i_spi_set(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC); /* Start the transfer */ - reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); - sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH); + sun4i_spi_set(sspi, SUN4I_CTL_REG, SUN4I_CTL_XCH); ret = sun4i_spi_wait_for_transfer(spi, tfr); -- 2.16.2
[PATCH 6/6] spi: sun4i: add DMA transfers support
DMA transfers are now available for sun4i-family SoCs. The DMA mode is used automatically as soon as requested transfer length is more than FIFO length. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 291 1 file changed, 271 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index fc913d4..9928af7 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,6 +41,7 @@ #define SUN4I_CTL_CPHA BIT(2) #define SUN4I_CTL_CPOL BIT(3) #define SUN4I_CTL_CS_ACTIVE_LOWBIT(4) +#define SUN4I_CTL_DMA_DEDICATEDBIT(5) #define SUN4I_CTL_LMTF BIT(6) #define SUN4I_CTL_TF_RST BIT(8) #define SUN4I_CTL_RF_RST BIT(9) @@ -58,6 +61,8 @@ #define SUN4I_INT_STA_REG 0x10 #define SUN4I_DMA_CTL_REG 0x14 +#define SUN4I_CTL_DMA_RF_READY BIT(0) +#define SUN4I_CTL_DMA_TF_NOT_FULL BIT(10) #define SUN4I_WAIT_REG 0x18 @@ -169,6 +174,13 @@ static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) } } +static bool sun4i_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + return tfr->len > SUN4I_FIFO_DEPTH; +} + static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) { struct sun4i_spi *sspi = spi_master_get_devdata(spi->master); @@ -208,6 +220,11 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun4i_spi_max_transfer_size(struct spi_device *spi) { + struct spi_master *master = spi->master; + + if (master->can_dma) + return SUN4I_MAX_XFER_SIZE; + return SUN4I_FIFO_DEPTH; } @@ -235,6 +252,160 @@ static int sun4i_spi_wait_for_transfer(struct spi_device *spi, return 0; } +static void sun4i_spi_dma_callback(void *param) +{ + struct spi_master *master = param; + + dev_dbg(&master->dev, "DMA transfer complete\n"); + spi_finalize_current_transfer(master); +} + +static int sun4i_spi_dmap_prep_tx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_tx, + tfr->tx_sg.sgl, tfr->tx_sg.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare TX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun4i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_tx); + + return 0; +} + +static int sun4i_spi_dmap_prep_rx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_rx, + tfr->rx_sg.sgl, tfr->rx_sg.nents, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare RX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun4i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_rx); + + return 0; +} + +static int sun4i_spi_transfer_one_dma(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + struct sun4i_spi *sspi = spi_master_get_devdata(master); + dma_cookie_t tx_cookie = 0, rx_cookie = 0; + enum dma_status status; + int ret; + u32 reg = 0; + + dev_dbg(&master->dev, "Using DMA mode for transfer\n"); + + if (sspi->tx_buf) { + ret = sun4i_spi_dmap_prep_tx(master, tfr, &tx_cookie); + if (ret) + goto out; + + reg |= SUN4I_CTL_DMA_TF_NOT_FULL; + } + + if (s
[PATCH 1/6] spi: core: handle timeout error from transfer_one()
As long as sun4i/sun6i SPI drivers have overriden the default "wait for completion" procedure then we need to properly handle -ETIMEDOUT error from transfer_one(). Signed-off-by: Sergey Suloev --- drivers/spi/spi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b33a727..2dcd4f6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1028,7 +1028,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, reinit_completion(&ctlr->xfer_completion); ret = ctlr->transfer_one(ctlr, msg->spi, xfer); - if (ret < 0) { + if (ret < 0 && ret != -ETIMEDOUT) { SPI_STATISTICS_INCREMENT_FIELD(statm, errors); SPI_STATISTICS_INCREMENT_FIELD(stats, @@ -1051,7 +1051,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, msecs_to_jiffies(ms)); } - if (ms == 0) { + if (ms == 0 || ret == -ETIMEDOUT) { SPI_STATISTICS_INCREMENT_FIELD(statm, timedout); SPI_STATISTICS_INCREMENT_FIELD(stats, @@ -1059,6 +1059,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, dev_err(&msg->spi->dev, "SPI transfer timed out\n"); msg->status = -ETIMEDOUT; + ret = 0; } } else { if (xfer->len) -- 2.16.2
[PATCH 0/6] spi: Add support for DMA transfers in sun4i SPI driver
The following patchset provides corrections for PIO-mode and support for DMA transfers in sun4i SPI driver. Sergey Suloev (6): spi: core: handle timeout error from transfer_one() spi: sun4i: restrict transfer length in PIO-mode spi: sun4i: coding style/readability improvements spi: sun4i: use completion provided by SPI core driver spi: sun4i: introduce register set/unset helpers spi: sun4i: add DMA transfers support drivers/spi/spi-sun4i.c | 443 drivers/spi/spi.c | 5 +- 2 files changed, 341 insertions(+), 107 deletions(-) -- 2.16.2
[PATCH 4/6] spi: sun4i: use completion provided by SPI core driver
As long as the completion already provided by the SPI core then there is no need to waste extra-memory on this. Also a waiting function was added to avoid code duplication. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 62 - 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 2d716f1..4f24e12 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -92,8 +92,6 @@ struct sun4i_spi { struct clk *hclk; struct clk *mclk; - struct completion done; - const u8*tx_buf; u8 *rx_buf; int len; @@ -213,13 +211,36 @@ static size_t sun4i_spi_max_transfer_size(struct spi_device *spi) return SUN4I_FIFO_DEPTH; } +static int sun4i_spi_wait_for_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + unsigned int start, end, tx_time; + unsigned int timeout; + + /* calc required timeout from given speed & len values */ + tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); + start = jiffies; + timeout = wait_for_completion_timeout(&master->xfer_completion, + msecs_to_jiffies(tx_time)); + end = jiffies; + if (!timeout) { + dev_warn(&master->dev, +"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", +dev_name(&spi->dev), tfr->len, tfr->speed_hz, +jiffies_to_msecs(end - start), tx_time); + return -ETIMEDOUT; + } + + return 0; +} + static int sun4i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) { struct sun4i_spi *sspi = spi_master_get_devdata(master); - unsigned int mclk_rate, div, timeout; - unsigned int start, end, tx_time; + unsigned int mclk_rate, div; unsigned int tx_len = 0; int ret = 0; u32 reg; @@ -228,7 +249,6 @@ static int sun4i_spi_transfer_one(struct spi_master *master, if (tfr->len > SUN4I_FIFO_DEPTH) return -EMSGSIZE; - reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; sspi->rx_buf = tfr->rx_buf; sspi->len = tfr->len; @@ -328,22 +348,8 @@ static int sun4i_spi_transfer_one(struct spi_master *master, reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH); - tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); - start = jiffies; - timeout = wait_for_completion_timeout(&sspi->done, - msecs_to_jiffies(tx_time)); - end = jiffies; - if (!timeout) { - dev_warn(&master->dev, -"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", -dev_name(&spi->dev), tfr->len, tfr->speed_hz, -jiffies_to_msecs(end - start), tx_time); - ret = -ETIMEDOUT; - goto out; - } - + ret = sun4i_spi_wait_for_transfer(spi, tfr); -out: sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0); return ret; @@ -351,14 +357,18 @@ out: static irqreturn_t sun4i_spi_handler(int irq, void *dev_id) { - struct sun4i_spi *sspi = dev_id; - u32 status = sun4i_spi_read(sspi, SUN4I_INT_STA_REG); + struct spi_master *master = dev_id; + struct sun4i_spi *sspi = spi_master_get_devdata(master); + u32 status; + + status = sun4i_spi_read(sspi, SUN4I_INT_STA_REG); /* Transfer complete */ if (status & SUN4I_INT_CTL_TC) { - sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC); + sun4i_spi_write(sspi, SUN4I_INT_STA_REG, + SUN4I_INT_CTL_TC); sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); - complete(&sspi->done); + spi_finalize_current_transfer(master); return IRQ_HANDLED; } @@ -447,7 +457,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler, - 0, dev_name(&pdev->dev), sspi); + 0, dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); goto err_free_master; @@ -467,8 +477,6 @@ static int sun4i_spi_probe(stru
[PATCH 3/6] spi: sun4i: coding style/readability improvements
Minor changes to fulfill the coding style and improve the readability. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 32 +--- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 2a49c22..2d716f1 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -83,8 +83,11 @@ #define SUN4I_FIFO_STA_TF_CNT_MASK 0x7f #define SUN4I_FIFO_STA_TF_CNT_BITS 16 +#define SUN4I_SPI_MAX_SPEED_HZ 100 * 1000 * 1000 +#define SUN4I_SPI_MIN_SPEED_HZ 3 * 1000 +#define SUN4I_SPI_MODE_BITS(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST) + struct sun4i_spi { - struct spi_master *master; void __iomem*base_addr; struct clk *hclk; struct clk *mclk; @@ -409,12 +412,23 @@ static int sun4i_spi_probe(struct platform_device *pdev) struct resource *res; int ret = 0, irq; - master = spi_alloc_master(&pdev->dev, sizeof(struct sun4i_spi)); + master = spi_alloc_master(&pdev->dev, sizeof(*sspi)); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); return -ENOMEM; } + master->max_speed_hz = SUN4I_SPI_MAX_SPEED_HZ; + master->min_speed_hz = SUN4I_SPI_MIN_SPEED_HZ; + master->num_chipselect = 4; + master->mode_bits = SUN4I_SPI_MODE_BITS; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->set_cs = sun4i_spi_set_cs; + master->transfer_one = sun4i_spi_transfer_one; + master->max_transfer_size = sun4i_spi_max_transfer_size; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; + platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); @@ -433,24 +447,12 @@ static int sun4i_spi_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler, - 0, "sun4i-spi", sspi); + 0, dev_name(&pdev->dev), sspi); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); goto err_free_master; } - sspi->master = master; - master->max_speed_hz = 100 * 1000 * 1000; - master->min_speed_hz = 3 * 1000; - master->set_cs = sun4i_spi_set_cs; - master->transfer_one = sun4i_spi_transfer_one; - master->num_chipselect = 4; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; - master->bits_per_word_mask = SPI_BPW_MASK(8); - master->dev.of_node = pdev->dev.of_node; - master->auto_runtime_pm = true; - master->max_transfer_size = sun4i_spi_max_transfer_size; - sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sspi->hclk)) { dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); -- 2.16.2
[PATCH 2/6] spi: sun4i: restrict transfer length in PIO-mode
There is no need to handle 3/4 empty/full interrupts as the maximum supported transfer length in PIO mode is 64 bytes for sun4i-family SoCs. As long as a problem was reported previously with filling FIFO on A10s then we stick with 63 bytes depth. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun4i.c | 50 - 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 4141003..2a49c22 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -22,7 +22,12 @@ #include -#define SUN4I_FIFO_DEPTH 64 +/* + * FIFO length is 64 bytes + * But filling the FIFO fully might cause a timeout + * on some devices, for example on spi2 on A10s + */ +#define SUN4I_FIFO_DEPTH 63 #define SUN4I_RXDATA_REG 0x00 @@ -202,7 +207,7 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun4i_spi_max_transfer_size(struct spi_device *spi) { - return SUN4I_FIFO_DEPTH - 1; + return SUN4I_FIFO_DEPTH; } static int sun4i_spi_transfer_one(struct spi_master *master, @@ -216,11 +221,8 @@ static int sun4i_spi_transfer_one(struct spi_master *master, int ret = 0; u32 reg; - /* We don't support transfer larger than the FIFO */ - if (tfr->len > SUN4I_MAX_XFER_SIZE) - return -EMSGSIZE; - - if (tfr->tx_buf && tfr->len >= SUN4I_MAX_XFER_SIZE) + /* We don't support transfers larger than FIFO depth */ + if (tfr->len > SUN4I_FIFO_DEPTH) return -EMSGSIZE; reinit_completion(&sspi->done); @@ -313,17 +315,11 @@ static int sun4i_spi_transfer_one(struct spi_master *master, /* * Fill the TX FIFO -* Filling the FIFO fully causes timeout for some reason -* at least on spi2 on A10s */ - sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1); + sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); - /* Enable the interrupts */ - sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC | -SUN4I_INT_CTL_RF_F34); - /* Only enable Tx FIFO interrupt if we really need it */ - if (tx_len > SUN4I_FIFO_DEPTH) - sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34); + /* Enable the transfer complete interrupt */ + sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC); /* Start the transfer */ reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); @@ -363,28 +359,6 @@ static irqreturn_t sun4i_spi_handler(int irq, void *dev_id) return IRQ_HANDLED; } - /* Receive FIFO 3/4 full */ - if (status & SUN4I_INT_CTL_RF_F34) { - sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); - /* Only clear the interrupt _after_ draining the FIFO */ - sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_RF_F34); - return IRQ_HANDLED; - } - - /* Transmit FIFO 3/4 empty */ - if (status & SUN4I_INT_CTL_TF_E34) { - sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); - - if (!sspi->len) - /* nothing left to transmit */ - sun4i_spi_disable_interrupt(sspi, SUN4I_INT_CTL_TF_E34); - - /* Only clear the interrupt _after_ re-seeding the FIFO */ - sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TF_E34); - - return IRQ_HANDLED; - } - return IRQ_NONE; } -- 2.16.2
[PATCH 4/6] spi: sun6i: use completion provided by SPI core
As long as the completion is already provided by the SPI core then there is no need to waste extra-memory on this. Also a waiting function was added to avoid code duplication. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 50 - 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 13396bd..fc43752 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -99,8 +99,6 @@ struct sun6i_spi { struct clk *mclk; struct reset_control*rstc; - struct completion done; - const u8*tx_buf; u8 *rx_buf; int len; @@ -255,6 +253,30 @@ static int sun6i_spi_prepare_message(struct spi_master *master, return 0; } +static int sun6i_spi_wait_for_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + unsigned int start, end, tx_time; + unsigned int timeout; + + /* smart wait for completion */ + tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); + start = jiffies; + timeout = wait_for_completion_timeout(&master->xfer_completion, + msecs_to_jiffies(tx_time)); + end = jiffies; + if (!timeout) { + dev_warn(&master->dev, +"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", +dev_name(&spi->dev), tfr->len, tfr->speed_hz, +jiffies_to_msecs(end - start), tx_time); + return -ETIMEDOUT; + } + + return 0; +} + static int sun6i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) @@ -275,7 +297,6 @@ static int sun6i_spi_transfer_one(struct spi_master *master, if (tfr->len > sspi->fifo_depth) return -EMSGSIZE; - reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; sspi->rx_buf = tfr->rx_buf; sspi->len = tfr->len; @@ -342,21 +363,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master, reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); - tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); - start = jiffies; - timeout = wait_for_completion_timeout(&sspi->done, - msecs_to_jiffies(tx_time)); - end = jiffies; - if (!timeout) { - dev_warn(&master->dev, -"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", -dev_name(&spi->dev), tfr->len, tfr->speed_hz, -jiffies_to_msecs(end - start), tx_time); - ret = -ETIMEDOUT; - goto out; - } + /* Wait for completion */ + ret = sun6i_spi_wait_for_transfer(spi, tfr); -out: sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); return ret; @@ -364,7 +373,8 @@ out: static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) { - struct sun6i_spi *sspi = dev_id; + struct spi_master *master = dev_id; + struct sun6i_spi *sspi = spi_master_get_devdata(master); u32 status; status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); @@ -373,7 +383,7 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) if (status & SUN6I_INT_CTL_TC) { sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); sun6i_spi_drain_fifo(sspi, sspi->fifo_depth); - complete(&sspi->done); + spi_finalize_current_transfer(master); return IRQ_HANDLED; } @@ -494,8 +504,6 @@ static int sun6i_spi_probe(struct platform_device *pdev) goto err_free_master; } - init_completion(&sspi->done); - sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(sspi->rstc)) { dev_err(&pdev->dev, "Couldn't get reset controller\n"); -- 2.16.2
[PATCH 5/6] spi: sun6i: introduce register set/unset helpers
Two helper functions were added in order to update registers easily. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 31 +++ 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index fc43752..a6e6812 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -115,29 +115,29 @@ static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value) writel(value, sspi->base_addr + reg); } -static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi) +static inline void sun6i_spi_set(struct sun6i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); - - reg >>= SUN6I_FIFO_STA_TF_CNT_BITS; + u32 reg = sun6i_spi_read(sspi, addr); - return reg & SUN6I_FIFO_STA_TF_CNT_MASK; + reg |= val; + sun6i_spi_write(sspi, addr, reg); } -static inline void sun6i_spi_enable_interrupt(struct sun6i_spi *sspi, u32 mask) +static inline void sun6i_spi_unset(struct sun6i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG); + u32 reg = sun6i_spi_read(sspi, addr); - reg |= mask; - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); + reg &= ~val; + sun6i_spi_write(sspi, addr, reg); } -static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask) +static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi) { - u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG); + u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); + + reg >>= SUN6I_FIFO_STA_TF_CNT_BITS; - reg &= ~mask; - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); + return reg & SUN6I_FIFO_STA_TF_CNT_MASK; } static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len) @@ -357,11 +357,10 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); /* Enable transfer complete interrupt */ - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC); + sun6i_spi_set(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); /* Start the transfer */ - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); - sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); + sun6i_spi_set(sspi, SUN6I_TFR_CTL_REG, SUN6I_TFR_CTL_XCH); /* Wait for completion */ ret = sun6i_spi_wait_for_transfer(spi, tfr); -- 2.16.2
[PATCH 6/6] spi: sun6i: add DMA transfers support
DMA transfers are now available for sun6i and sun8i SoCs. The DMA mode is used automatically as soon as requested transfer length is more than FIFO length. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 296 1 file changed, 275 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index a6e6812..e74fa3d 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -55,11 +57,14 @@ #define SUN6I_FIFO_CTL_REG 0x18 #define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_MASK 0xff -#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS 0 +#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_POS 0 +#define SUN6I_FIFO_CTL_RF_DRQ_EN BIT(8) #define SUN6I_FIFO_CTL_RF_RST BIT(15) #define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_MASK 0xff -#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS 16 +#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_POS 16 +#define SUN6I_FIFO_CTL_TF_DRQ_EN BIT(24) #define SUN6I_FIFO_CTL_TF_RST BIT(31) +#define SUN6I_FIFO_CTL_DMA_DEDICATEBIT(9)|BIT(25) #define SUN6I_FIFO_STA_REG 0x1c #define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f @@ -177,6 +182,15 @@ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len) } } +static bool sun6i_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + return tfr->len > sspi->fifo_depth; +} + static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) { struct sun6i_spi *sspi = spi_master_get_devdata(spi->master); @@ -208,6 +222,9 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) struct spi_master *master = spi->master; struct sun6i_spi *sspi = spi_master_get_devdata(master); + if (master->can_dma) + return SUN6I_MAX_XFER_SIZE; + return sspi->fifo_depth; } @@ -277,15 +294,174 @@ static int sun6i_spi_wait_for_transfer(struct spi_device *spi, return 0; } +static void sun6i_spi_dma_callback(void *param) +{ + struct spi_master *master = param; + + dev_dbg(&master->dev, "DMA transfer complete\n"); + spi_finalize_current_transfer(master); +} + +static int sun6i_spi_dmap_prep_tx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_tx, + tfr->tx_sg.sgl, tfr->tx_sg.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare TX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun6i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_tx); + + return 0; +} + +static int sun6i_spi_dmap_prep_rx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_rx, + tfr->rx_sg.sgl, tfr->rx_sg.nents, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare RX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun6i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_rx); + + return 0; +} + +static int sun6i_spi_transfer_one_dma(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + dma_cookie_t tx_cookie = 0,rx_cookie = 0; + enum dma_status status; + int ret; + u32 reg, trig_level = 0; + + dev_dbg(&master->dev, "Using DMA mode for transfer\n"); + + reg = sun6i_spi_read(sspi, SUN6I_FIFO_CTL_REG); + + if (sspi-&
[PATCH 3/6] spi: sun6i: restrict transfer length in PIO-mode
There is no need to handle 3/4 empty/full interrupts as the maximum supported transfer length in PIO mode is 128 bytes for sun6i- and 64 bytes for sun8i-family SoCs. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 61 ++--- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index f992a7d..13396bd 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -207,7 +207,10 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) { - return SUN6I_MAX_XFER_SIZE - 1; + struct spi_master *master = spi->master; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + return sspi->fifo_depth; } static int sun6i_spi_prepare_message(struct spi_master *master, @@ -259,13 +262,18 @@ static int sun6i_spi_transfer_one(struct spi_master *master, struct sun6i_spi *sspi = spi_master_get_devdata(master); unsigned int mclk_rate, div, timeout; unsigned int start, end, tx_time; - unsigned int trig_level; unsigned int tx_len = 0; int ret = 0; u32 reg; - if (tfr->len > SUN6I_MAX_XFER_SIZE) - return -EINVAL; + /* A zero length transfer never finishes if programmed + in the hardware */ + if (!tfr->len) + return 0; + + /* Don't support transfer larger than the FIFO */ + if (tfr->len > sspi->fifo_depth) + return -EMSGSIZE; reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; @@ -279,17 +287,6 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST); - /* -* Setup FIFO interrupt trigger level -* Here we choose 3/4 of the full fifo depth, as it's the hardcoded -* value used in old generation of Allwinner SPI controller. -* (See spi-sun4i.c) -*/ - trig_level = sspi->fifo_depth / 4 * 3; - sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, - (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | - (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); - /* Ensure that we have a parent clock fast enough */ mclk_rate = clk_get_rate(sspi->mclk); @@ -338,12 +335,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, /* Fill the TX FIFO */ sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); - /* Enable the interrupts */ - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC | -SUN6I_INT_CTL_RF_RDY); - if (tx_len > sspi->fifo_depth) - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); + /* Enable transfer complete interrupt */ + sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC); /* Start the transfer */ reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); @@ -372,7 +365,9 @@ out: static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) { struct sun6i_spi *sspi = dev_id; - u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); + u32 status; + + status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); /* Transfer complete */ if (status & SUN6I_INT_CTL_TC) { @@ -382,28 +377,6 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) return IRQ_HANDLED; } - /* Receive FIFO 3/4 full */ - if (status & SUN6I_INT_CTL_RF_RDY) { - sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH); - /* Only clear the interrupt _after_ draining the FIFO */ - sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_RF_RDY); - return IRQ_HANDLED; - } - - /* Transmit FIFO 3/4 empty */ - if (status & SUN6I_INT_CTL_TF_ERQ) { - sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH); - - if (!sspi->len) - /* nothing left to transmit */ - sun6i_spi_disable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); - - /* Only clear the interrupt _after_ re-seeding the FIFO */ - sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TF_ERQ); - - return IRQ_HANDLED; - } - return IRQ_NONE; } -- 2.16.2
[PATCH 2/6] spi: sun6i: handle chip select polarity flag
The chip select polarity flag is declared as supported but is not handled in the code. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index ff790dc..f992a7d 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -193,6 +193,12 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) else reg &= ~SUN6I_TFR_CTL_CS_LEVEL; + /* Handle chip select "reverse" polarity */ + if (spi->mode & SPI_CS_HIGH) + reg &= ~SUN6I_TFR_CTL_SPOL; + else + reg |= SUN6I_TFR_CTL_SPOL; + /* We want to control the chip select manually */ reg |= SUN6I_TFR_CTL_CS_MANUAL; -- 2.16.2
[PATCH 0/6] spi: Add support for DMA transfers in sun6i SPI driver
The following patchset provides corrections for PIO-mode and support for DMA transfers in sun6i SPI driver. Sergey Suloev (6): spi: sun6i: coding style/readability improvements spi: sun6i: handle chip select polarity flag spi: sun6i: restrict transfer length in PIO-mode spi: sun6i: use completion provided by SPI core spi: sun6i: introduce register set/unset helpers spi: sun6i: add DMA transfers support drivers/spi/spi-sun6i.c | 501 1 file changed, 378 insertions(+), 123 deletions(-) -- 2.16.2
[PATCH 1/6] spi: sun6i: coding style/readability improvements
Minor changes to fulfill the coding style and improve the readability. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 115 +++- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 8533f4e..ff790dc 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -88,8 +88,12 @@ #define SUN6I_TXDATA_REG 0x200 #define SUN6I_RXDATA_REG 0x300 +#define SUN6I_SPI_MODE_BITS(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST) + +#define SUN6I_SPI_MAX_SPEED_HZ 1 +#define SUN6I_SPI_MIN_SPEED_HZ 3000 + struct sun6i_spi { - struct spi_master *master; void __iomem*base_addr; struct clk *hclk; struct clk *mclk; @@ -189,6 +193,9 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) else reg &= ~SUN6I_TFR_CTL_CS_LEVEL; + /* We want to control the chip select manually */ + reg |= SUN6I_TFR_CTL_CS_MANUAL; + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); } @@ -197,6 +204,48 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) return SUN6I_MAX_XFER_SIZE - 1; } +static int sun6i_spi_prepare_message(struct spi_master *master, +struct spi_message *msg) +{ + struct spi_device *spi = msg->spi; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + u32 reg; + + /* +* Setup the transfer control register: Chip Select, +* polarities, etc. +*/ + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + + if (spi->mode & SPI_CPOL) + reg |= SUN6I_TFR_CTL_CPOL; + else + reg &= ~SUN6I_TFR_CTL_CPOL; + + if (spi->mode & SPI_CPHA) + reg |= SUN6I_TFR_CTL_CPHA; + else + reg &= ~SUN6I_TFR_CTL_CPHA; + + if (spi->mode & SPI_LSB_FIRST) + reg |= SUN6I_TFR_CTL_FBS; + else + reg &= ~SUN6I_TFR_CTL_FBS; + + /* +* If it's a TX only transfer, we don't want to fill the RX +* FIFO with bogus data +*/ + if (sspi->rx_buf) + reg &= ~SUN6I_TFR_CTL_DHB; + else + reg |= SUN6I_TFR_CTL_DHB; + + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); + + return 0; +} + static int sun6i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) @@ -235,40 +284,6 @@ static int sun6i_spi_transfer_one(struct spi_master *master, (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); - /* -* Setup the transfer control register: Chip Select, -* polarities, etc. -*/ - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); - - if (spi->mode & SPI_CPOL) - reg |= SUN6I_TFR_CTL_CPOL; - else - reg &= ~SUN6I_TFR_CTL_CPOL; - - if (spi->mode & SPI_CPHA) - reg |= SUN6I_TFR_CTL_CPHA; - else - reg &= ~SUN6I_TFR_CTL_CPHA; - - if (spi->mode & SPI_LSB_FIRST) - reg |= SUN6I_TFR_CTL_FBS; - else - reg &= ~SUN6I_TFR_CTL_FBS; - - /* -* If it's a TX only transfer, we don't want to fill the RX -* FIFO with bogus data -*/ - if (sspi->rx_buf) - reg &= ~SUN6I_TFR_CTL_DHB; - else - reg |= SUN6I_TFR_CTL_DHB; - - /* We want to control the chip select manually */ - reg |= SUN6I_TFR_CTL_CS_MANUAL; - - 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); @@ -442,12 +457,24 @@ static int sun6i_spi_probe(struct platform_device *pdev) struct resource *res; int ret = 0, irq; - master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); + master = spi_alloc_master(&pdev->dev, sizeof(*sspi)); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); return -ENOMEM; } + master->max_speed_hz = SUN6I_SPI_MAX_SPEED_HZ; + master->min_speed_hz = SUN6I_SPI_MIN_SPEED_HZ; + master->num_chipselect = 4; + master->mode_bits = SUN6I_SPI_MODE_BITS; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->set_cs = sun6i_spi_set_cs; + master->prepare_message = sun6i_spi_prepare_message; + master->transfer_one =
[PATCH v2 2/6] spi: sun6i: handle chip select polarity flag
The chip select polarity flag is declared as supported but is not handled in the code. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 88ad45e..78acc1f 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -193,6 +193,12 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) else reg &= ~SUN6I_TFR_CTL_CS_LEVEL; + /* Handle chip select "reverse" polarity */ + if (spi->mode & SPI_CS_HIGH) + reg &= ~SUN6I_TFR_CTL_SPOL; + else + reg |= SUN6I_TFR_CTL_SPOL; + /* We want to control the chip select manually */ reg |= SUN6I_TFR_CTL_CS_MANUAL; -- 2.16.2
[PATCH v2 6/6] spi: sun6i: add DMA transfers support
DMA transfers are now available for sun6i and sun8i SoCs. The DMA mode is used automatically as soon as requested transfer length is more than FIFO length. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 296 1 file changed, 275 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 18f9344..5665c84 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -55,11 +57,14 @@ #define SUN6I_FIFO_CTL_REG 0x18 #define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_MASK 0xff -#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS 0 +#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_POS 0 +#define SUN6I_FIFO_CTL_RF_DRQ_EN BIT(8) #define SUN6I_FIFO_CTL_RF_RST BIT(15) #define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_MASK 0xff -#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS 16 +#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_POS 16 +#define SUN6I_FIFO_CTL_TF_DRQ_EN BIT(24) #define SUN6I_FIFO_CTL_TF_RST BIT(31) +#define SUN6I_FIFO_CTL_DMA_DEDICATEBIT(9)|BIT(25) #define SUN6I_FIFO_STA_REG 0x1c #define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f @@ -177,6 +182,15 @@ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len) } } +static bool sun6i_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + return tfr->len > sspi->fifo_depth; +} + static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) { struct sun6i_spi *sspi = spi_master_get_devdata(spi->master); @@ -208,6 +222,9 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) struct spi_master *master = spi->master; struct sun6i_spi *sspi = spi_master_get_devdata(master); + if (master->can_dma) + return SUN6I_MAX_XFER_SIZE; + return sspi->fifo_depth; } @@ -268,15 +285,174 @@ static int sun6i_spi_wait_for_transfer(struct spi_device *spi, return 0; } +static void sun6i_spi_dma_callback(void *param) +{ + struct spi_master *master = param; + + dev_dbg(&master->dev, "DMA transfer complete\n"); + spi_finalize_current_transfer(master); +} + +static int sun6i_spi_dmap_prep_tx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_tx, + tfr->tx_sg.sgl, tfr->tx_sg.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare TX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun6i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_tx); + + return 0; +} + +static int sun6i_spi_dmap_prep_rx(struct spi_master *master, + struct spi_transfer *tfr, + dma_cookie_t *cookie) +{ + struct dma_async_tx_descriptor *chan_desc = NULL; + + chan_desc = dmaengine_prep_slave_sg(master->dma_rx, + tfr->rx_sg.sgl, tfr->rx_sg.nents, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!chan_desc) { + dev_err(&master->dev, + "Couldn't prepare RX DMA slave\n"); + return -EIO; + } + + chan_desc->callback = sun6i_spi_dma_callback; + chan_desc->callback_param = master; + + *cookie = dmaengine_submit(chan_desc); + dma_async_issue_pending(master->dma_rx); + + return 0; +} + +static int sun6i_spi_transfer_one_dma(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + dma_cookie_t tx_cookie = 0,rx_cookie = 0; + enum dma_status status; + int ret; + u32 reg, trig_level = 0; + + dev_dbg(&master->dev, "Using DMA mode for transfer\n"); + + reg = sun6i_spi_read(sspi, SUN6I_FIFO_CTL_REG); + + if (sspi-&
[PATCH v2 0/6] spi: Add support for DMA transfers in sun6i SPI driver
The following patchset provides corrections for PIO-mode and support for DMA transfers in sun6i SPI driver. Changes in v2: 1) Fixed issue with misplacing a piece of code that requires access to the transfer structure into sun6i_spi_prepare_message() function where the transfer structure is not available. 2) Fixed issue with passing an invalid argument into devm_request_irq() function. Sergey Suloev (6): spi: sun6i: coding style/readability improvements spi: sun6i: handle chip select polarity flag spi: sun6i: restrict transfer length in PIO-mode spi: sun6i: use completion provided by SPI core spi: sun6i: introduce register set/unset helpers spi: sun6i: add DMA transfers support drivers/spi/spi-sun6i.c | 507 1 file changed, 381 insertions(+), 126 deletions(-) -- 2.16.2
[PATCH v2 3/6] spi: sun6i: restrict transfer length in PIO-mode
There is no need to handle 3/4 empty/full interrupts as the maximum supported transfer length in PIO mode is 128 bytes for sun6i- and 64 bytes for sun8i-family SoCs. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 61 ++--- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 78acc1f..4db1f20 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -207,7 +207,10 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) { - return SUN6I_MAX_XFER_SIZE - 1; + struct spi_master *master = spi->master; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + return sspi->fifo_depth; } static int sun6i_spi_prepare_message(struct spi_master *master, @@ -250,13 +253,18 @@ static int sun6i_spi_transfer_one(struct spi_master *master, struct sun6i_spi *sspi = spi_master_get_devdata(master); unsigned int mclk_rate, div, timeout; unsigned int start, end, tx_time; - unsigned int trig_level; unsigned int tx_len = 0; int ret = 0; u32 reg; - if (tfr->len > SUN6I_MAX_XFER_SIZE) - return -EINVAL; + /* A zero length transfer never finishes if programmed + in the hardware */ + if (!tfr->len) + return 0; + + /* Don't support transfer larger than the FIFO */ + if (tfr->len > sspi->fifo_depth) + return -EMSGSIZE; reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; @@ -270,17 +278,6 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST); - /* -* Setup FIFO interrupt trigger level -* Here we choose 3/4 of the full fifo depth, as it's the hardcoded -* value used in old generation of Allwinner SPI controller. -* (See spi-sun4i.c) -*/ - trig_level = sspi->fifo_depth / 4 * 3; - sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, - (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | - (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); /* @@ -342,12 +339,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, /* Fill the TX FIFO */ sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); - /* Enable the interrupts */ - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC | -SUN6I_INT_CTL_RF_RDY); - if (tx_len > sspi->fifo_depth) - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); + /* Enable transfer complete interrupt */ + sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC); /* Start the transfer */ reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); @@ -376,7 +369,9 @@ out: static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) { struct sun6i_spi *sspi = dev_id; - u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); + u32 status; + + status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); /* Transfer complete */ if (status & SUN6I_INT_CTL_TC) { @@ -386,28 +381,6 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) return IRQ_HANDLED; } - /* Receive FIFO 3/4 full */ - if (status & SUN6I_INT_CTL_RF_RDY) { - sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH); - /* Only clear the interrupt _after_ draining the FIFO */ - sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_RF_RDY); - return IRQ_HANDLED; - } - - /* Transmit FIFO 3/4 empty */ - if (status & SUN6I_INT_CTL_TF_ERQ) { - sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH); - - if (!sspi->len) - /* nothing left to transmit */ - sun6i_spi_disable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); - - /* Only clear the interrupt _after_ re-seeding the FIFO */ - sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TF_ERQ); - - return IRQ_HANDLED; - } - return IRQ_NONE; } -- 2.16.2
[PATCH v2 5/6] spi: sun6i: introduce register set/unset helpers
Two helper functions were added in order to update registers easily. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 39 +-- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 210cef9..18f9344 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -115,29 +115,29 @@ static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value) writel(value, sspi->base_addr + reg); } -static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi) +static inline void sun6i_spi_set(struct sun6i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); - - reg >>= SUN6I_FIFO_STA_TF_CNT_BITS; + u32 reg = sun6i_spi_read(sspi, addr); - return reg & SUN6I_FIFO_STA_TF_CNT_MASK; + reg |= val; + sun6i_spi_write(sspi, addr, reg); } -static inline void sun6i_spi_enable_interrupt(struct sun6i_spi *sspi, u32 mask) +static inline void sun6i_spi_unset(struct sun6i_spi *sspi, u32 addr, u32 val) { - u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG); + u32 reg = sun6i_spi_read(sspi, addr); - reg |= mask; - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); + reg &= ~val; + sun6i_spi_write(sspi, addr, reg); } -static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask) +static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi) { - u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG); + u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); - reg &= ~mask; - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); + reg >>= SUN6I_FIFO_STA_TF_CNT_BITS; + + return reg & SUN6I_FIFO_STA_TF_CNT_MASK; } static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len) @@ -299,18 +299,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST); - - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); /* * If it's a TX only transfer, we don't want to fill the RX * FIFO with bogus data */ if (sspi->rx_buf) - reg &= ~SUN6I_TFR_CTL_DHB; + sun6i_spi_unset(sspi, SUN6I_TFR_CTL_REG, SUN6I_TFR_CTL_DHB); else - reg |= SUN6I_TFR_CTL_DHB; - - sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); + sun6i_spi_set(sspi, SUN6I_TFR_CTL_REG, SUN6I_TFR_CTL_DHB); /* Ensure that we have a parent clock fast enough */ @@ -361,11 +357,10 @@ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); /* Enable transfer complete interrupt */ - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC); + sun6i_spi_set(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); /* Start the transfer */ - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); - sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); + sun6i_spi_set(sspi, SUN6I_TFR_CTL_REG, SUN6I_TFR_CTL_XCH); /* Wait for completion */ ret = sun6i_spi_wait_for_transfer(spi, tfr); -- 2.16.2
[PATCH v2 4/6] spi: sun6i: use completion provided by SPI core
As long as the completion is already provided by the SPI core then there is no need to waste extra-memory on this. Also a waiting function was added to avoid code duplication. Changes in v2: 1) Fixed issue with passing an invalid argument into devm_request_irq() function. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 52 - 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 4db1f20..210cef9 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -99,8 +99,6 @@ struct sun6i_spi { struct clk *mclk; struct reset_control*rstc; - struct completion done; - const u8*tx_buf; u8 *rx_buf; int len; @@ -246,6 +244,30 @@ static int sun6i_spi_prepare_message(struct spi_master *master, return 0; } +static int sun6i_spi_wait_for_transfer(struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct spi_master *master = spi->master; + unsigned int start, end, tx_time; + unsigned int timeout; + + /* smart wait for completion */ + tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); + start = jiffies; + timeout = wait_for_completion_timeout(&master->xfer_completion, + msecs_to_jiffies(tx_time)); + end = jiffies; + if (!timeout) { + dev_warn(&master->dev, +"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", +dev_name(&spi->dev), tfr->len, tfr->speed_hz, +jiffies_to_msecs(end - start), tx_time); + return -ETIMEDOUT; + } + + return 0; +} + static int sun6i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) @@ -266,7 +288,6 @@ static int sun6i_spi_transfer_one(struct spi_master *master, if (tfr->len > sspi->fifo_depth) return -EMSGSIZE; - reinit_completion(&sspi->done); sspi->tx_buf = tfr->tx_buf; sspi->rx_buf = tfr->rx_buf; sspi->len = tfr->len; @@ -346,21 +367,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master, reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); - tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); - start = jiffies; - timeout = wait_for_completion_timeout(&sspi->done, - msecs_to_jiffies(tx_time)); - end = jiffies; - if (!timeout) { - dev_warn(&master->dev, -"%s: timeout transferring %u bytes@%iHz for %i(%i)ms", -dev_name(&spi->dev), tfr->len, tfr->speed_hz, -jiffies_to_msecs(end - start), tx_time); - ret = -ETIMEDOUT; - goto out; - } + /* Wait for completion */ + ret = sun6i_spi_wait_for_transfer(spi, tfr); -out: sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); return ret; @@ -368,7 +377,8 @@ out: static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) { - struct sun6i_spi *sspi = dev_id; + struct spi_master *master = dev_id; + struct sun6i_spi *sspi = spi_master_get_devdata(master); u32 status; status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); @@ -377,7 +387,7 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) if (status & SUN6I_INT_CTL_TC) { sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); sun6i_spi_drain_fifo(sspi, sspi->fifo_depth); - complete(&sspi->done); + spi_finalize_current_transfer(master); return IRQ_HANDLED; } @@ -476,7 +486,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) } ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler, - 0, dev_name(&pdev->dev), sspi); + 0, dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n"); goto err_free_master; @@ -498,8 +508,6 @@ static int sun6i_spi_probe(struct platform_device *pdev) goto err_free_master; } - init_completion(&sspi->done); - sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(sspi->rstc)) { dev_err(&pdev->dev, "Couldn't get reset controller\n"); -- 2.16.2
[PATCH v2 1/6] spi: sun6i: coding style/readability improvements
Minor changes to fulfill the coding style and improve the readability of the code. Changes in v2: 1) Fixed issue with misplacing a piece of code that requires access to the transfer structure into sun6i_spi_prepare_message() function where the transfer structure is not available. Signed-off-by: Sergey Suloev --- drivers/spi/spi-sun6i.c | 97 + 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 8533f4e..88ad45e 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -88,8 +88,12 @@ #define SUN6I_TXDATA_REG 0x200 #define SUN6I_RXDATA_REG 0x300 +#define SUN6I_SPI_MODE_BITS(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST) + +#define SUN6I_SPI_MAX_SPEED_HZ 1 +#define SUN6I_SPI_MIN_SPEED_HZ 3000 + struct sun6i_spi { - struct spi_master *master; void __iomem*base_addr; struct clk *hclk; struct clk *mclk; @@ -189,6 +193,9 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) else reg &= ~SUN6I_TFR_CTL_CS_LEVEL; + /* We want to control the chip select manually */ + reg |= SUN6I_TFR_CTL_CS_MANUAL; + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); } @@ -197,6 +204,39 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) return SUN6I_MAX_XFER_SIZE - 1; } +static int sun6i_spi_prepare_message(struct spi_master *master, +struct spi_message *msg) +{ + struct spi_device *spi = msg->spi; + struct sun6i_spi *sspi = spi_master_get_devdata(master); + u32 reg; + + /* +* Setup the transfer control register: Chip Select, +* polarities, etc. +*/ + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + + if (spi->mode & SPI_CPOL) + reg |= SUN6I_TFR_CTL_CPOL; + else + reg &= ~SUN6I_TFR_CTL_CPOL; + + if (spi->mode & SPI_CPHA) + reg |= SUN6I_TFR_CTL_CPHA; + else + reg &= ~SUN6I_TFR_CTL_CPHA; + + if (spi->mode & SPI_LSB_FIRST) + reg |= SUN6I_TFR_CTL_FBS; + else + reg &= ~SUN6I_TFR_CTL_FBS; + + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); + + return 0; +} + static int sun6i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) @@ -235,27 +275,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) | (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS)); - /* -* Setup the transfer control register: Chip Select, -* polarities, etc. -*/ - reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); - - if (spi->mode & SPI_CPOL) - reg |= SUN6I_TFR_CTL_CPOL; - else - reg &= ~SUN6I_TFR_CTL_CPOL; - - if (spi->mode & SPI_CPHA) - reg |= SUN6I_TFR_CTL_CPHA; - else - reg &= ~SUN6I_TFR_CTL_CPHA; - - if (spi->mode & SPI_LSB_FIRST) - reg |= SUN6I_TFR_CTL_FBS; - else - reg &= ~SUN6I_TFR_CTL_FBS; + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); /* * If it's a TX only transfer, we don't want to fill the RX * FIFO with bogus data @@ -265,11 +286,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master, else reg |= SUN6I_TFR_CTL_DHB; - /* We want to control the chip select manually */ - reg |= SUN6I_TFR_CTL_CS_MANUAL; - 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)) { @@ -442,12 +461,24 @@ static int sun6i_spi_probe(struct platform_device *pdev) struct resource *res; int ret = 0, irq; - master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); + master = spi_alloc_master(&pdev->dev, sizeof(*sspi)); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); return -ENOMEM; } + master->max_speed_hz = SUN6I_SPI_MAX_SPEED_HZ; + master->min_speed_hz = SUN6I_SPI_MIN_SPEED_HZ; + master->num_chipselect = 4; + master->mode_bits = SUN6I_SPI_MODE_BITS; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->set_cs = sun6i_spi_set_cs; + master->prepare_message = sun6i_spi_prepare_message
Re: [PATCH 0/3] Fix broken bananapi m2 devicetree/regulators
On 02/11/2018 01:07 AM, Philipp Rossak wrote: On 10.02.2018 22:08, Sergey Suloev wrote: On 02/11/2018 12:01 AM, Philipp Rossak wrote: Hey Sergey, Thanks for mentioning, but I think the problem has nothing to do with those patches. I tested them with the v4.15.0 Kernel since this is the last stable release and we are right now in the merging window. I tested the latest mainline, without those patches and the kernel is not booting (I can't see any uart output). Thanks, Philipp On 10.02.2018 14:56, Sergey Suloev wrote: On 02/09/2018 08:52 PM, Philipp Rossak wrote: This patchseries fixes the bananapi m1 devicetree, to be able to boot again. The first two patches update/improve the devicetree and the last patch adds all missing regulators. Regards, Philipp Philipp Rossak (3): arm: dts: sun6i: a31s: bpi-m2: update mmc supply nodes arm: dts: sun6i: a31s: bpi-m2: improve pmic properties arm: dts: sun6i: a31s: fix: bpi-m2: add missing regulators arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 70 +++- 1 file changed, 67 insertions(+), 3 deletions(-) patches are not working Thanks same problem, but after applying the patches my device is till hanging. Can you please share a bootlog? Here is mine [1]. As you can see I'm able to boot. I build it with this branch [2]. For testing you should replace the dtb and the uImage/zImage Philipp [1]: https://pastebin.com/mVjv3LDf [2]: https://github.com/embed-3d/linux/tree/testing/bpi-m2-regulator-test-2 I am going to test it and come back with outcome Thanks
Re: [PATCH 0/3] Fix broken bananapi m2 devicetree/regulators
On 02/11/2018 01:07 AM, Philipp Rossak wrote: On 10.02.2018 22:08, Sergey Suloev wrote: On 02/11/2018 12:01 AM, Philipp Rossak wrote: Hey Sergey, Thanks for mentioning, but I think the problem has nothing to do with those patches. I tested them with the v4.15.0 Kernel since this is the last stable release and we are right now in the merging window. I tested the latest mainline, without those patches and the kernel is not booting (I can't see any uart output). Thanks, Philipp On 10.02.2018 14:56, Sergey Suloev wrote: On 02/09/2018 08:52 PM, Philipp Rossak wrote: This patchseries fixes the bananapi m1 devicetree, to be able to boot again. The first two patches update/improve the devicetree and the last patch adds all missing regulators. Regards, Philipp Philipp Rossak (3): arm: dts: sun6i: a31s: bpi-m2: update mmc supply nodes arm: dts: sun6i: a31s: bpi-m2: improve pmic properties arm: dts: sun6i: a31s: fix: bpi-m2: add missing regulators arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 70 +++- 1 file changed, 67 insertions(+), 3 deletions(-) patches are not working Thanks same problem, but after applying the patches my device is till hanging. Can you please share a bootlog? Here is mine [1]. As you can see I'm able to boot. I build it with this branch [2]. For testing you should replace the dtb and the uImage/zImage Philipp [1]: https://pastebin.com/mVjv3LDf [2]: https://github.com/embed-3d/linux/tree/testing/bpi-m2-regulator-test-2 My dmesg is very similar to yours unless it hangs on the last line [1]. For this test I used kernel from tag v4.15 with no additional patching. [1] https://pastebin.com/3a6bk5Dk
Re: [PATCH 0/3] Fix broken bananapi m2 devicetree/regulators
On 02/11/2018 02:30 PM, Philipp Rossak wrote: Am Sonntag, den 11.02.2018, 10:55 +0300 schrieb Sergey Suloev: On 02/11/2018 01:07 AM, Philipp Rossak wrote: On 10.02.2018 22:08, Sergey Suloev wrote: On 02/11/2018 12:01 AM, Philipp Rossak wrote: Hey Sergey, Thanks for mentioning, but I think the problem has nothing to do with those patches. I tested them with the v4.15.0 Kernel since this is the last stable release and we are right now in the merging window. I tested the latest mainline, without those patches and the kernel is not booting (I can't see any uart output). Thanks, Philipp On 10.02.2018 14:56, Sergey Suloev wrote: On 02/09/2018 08:52 PM, Philipp Rossak wrote: This patchseries fixes the bananapi m1 devicetree, to be able to boot again. The first two patches update/improve the devicetree and the last patch adds all missing regulators. Regards, Philipp Philipp Rossak (3): arm: dts: sun6i: a31s: bpi-m2: update mmc supply nodes arm: dts: sun6i: a31s: bpi-m2: improve pmic properties arm: dts: sun6i: a31s: fix: bpi-m2: add missing regulators arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 70 +++- 1 file changed, 67 insertions(+), 3 deletions(-) patches are not working Thanks same problem, but after applying the patches my device is till hanging. Can you please share a bootlog? Here is mine [1]. As you can see I'm able to boot. I build it with this branch [2]. For testing you should replace the dtb and the uImage/zImage Philipp [1]: https://pastebin.com/mVjv3LDf [2]: https://github.com/embed-3d/linux/tree/testing/bpi-m2-regulator-tes t-2 My dmesg is very similar to yours unless it hangs on the last line [1]. For this test I used kernel from tag v4.15 with no additional patching. [1] https://pastebin.com/3a6bk5Dk For me it looks like you have a different kernel and also an different dtb that you patch! And you are applying also a fixup script. These patches should be applied on top of linux mainline. fixup script: Applying kernel provided DT fixup script (sun6i-a31s-fixup.scr) Linux versions: 4.15.0-3-g1bbe5edde691 vs 4.15.0-sunxi-dirty In addition to that you are also booting into fb/hdmi. If you use my branch (uImage and dtb) and replace the current boot.cmd with this (to get the boot.scr you need to use mkimage) it should work: setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait panic=10 load mmc 0:1 0x4300 ${fdtfile} || load mmc 0:1 0x4300 boot/${fdtfile} load mmc 0:1 0x4200 uImage || load mmc 0:1 0x4200 boot/uImage bootm 0x4200 - 0x4300 the script is doing nothing, it is empty. The problem is related to hdmi. The following dts code makes it happen: &hdmi { status = "okay"; };
Re: [PATCH 0/3] Fix broken bananapi m2 devicetree/regulators
On 02/11/2018 02:30 PM, Philipp Rossak wrote: Am Sonntag, den 11.02.2018, 10:55 +0300 schrieb Sergey Suloev: On 02/11/2018 01:07 AM, Philipp Rossak wrote: On 10.02.2018 22:08, Sergey Suloev wrote: On 02/11/2018 12:01 AM, Philipp Rossak wrote: Hey Sergey, Thanks for mentioning, but I think the problem has nothing to do with those patches. I tested them with the v4.15.0 Kernel since this is the last stable release and we are right now in the merging window. I tested the latest mainline, without those patches and the kernel is not booting (I can't see any uart output). Thanks, Philipp On 10.02.2018 14:56, Sergey Suloev wrote: On 02/09/2018 08:52 PM, Philipp Rossak wrote: This patchseries fixes the bananapi m1 devicetree, to be able to boot again. The first two patches update/improve the devicetree and the last patch adds all missing regulators. Regards, Philipp Philipp Rossak (3): arm: dts: sun6i: a31s: bpi-m2: update mmc supply nodes arm: dts: sun6i: a31s: bpi-m2: improve pmic properties arm: dts: sun6i: a31s: fix: bpi-m2: add missing regulators arch/arm/boot/dts/sun6i-a31s-sinovoip-bpi-m2.dts | 70 +++- 1 file changed, 67 insertions(+), 3 deletions(-) patches are not working Thanks same problem, but after applying the patches my device is till hanging. Can you please share a bootlog? Here is mine [1]. As you can see I'm able to boot. I build it with this branch [2]. For testing you should replace the dtb and the uImage/zImage Philipp [1]: https://pastebin.com/mVjv3LDf [2]: https://github.com/embed-3d/linux/tree/testing/bpi-m2-regulator-tes t-2 My dmesg is very similar to yours unless it hangs on the last line [1]. For this test I used kernel from tag v4.15 with no additional patching. [1] https://pastebin.com/3a6bk5Dk For me it looks like you have a different kernel and also an different dtb that you patch! And you are applying also a fixup script. These patches should be applied on top of linux mainline. fixup script: Applying kernel provided DT fixup script (sun6i-a31s-fixup.scr) Linux versions: 4.15.0-3-g1bbe5edde691 vs 4.15.0-sunxi-dirty In addition to that you are also booting into fb/hdmi. If you use my branch (uImage and dtb) and replace the current boot.cmd with this (to get the boot.scr you need to use mkimage) it should work: setenv bootargs console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait panic=10 load mmc 0:1 0x4300 ${fdtfile} || load mmc 0:1 0x4300 boot/${fdtfile} load mmc 0:1 0x4200 uImage || load mmc 0:1 0x4200 boot/uImage bootm 0x4200 - 0x4300 ok, I confirm that your patches are not related to the issue. It is the problem with sun4i drm / hdmi which is not working on bpi m2. Thanks
Re: [PATCH 20/21] ASoC: sun4i-i2s: Add support for TDM slots
Hi, Maxime, On 8/21/19 3:05 PM, Maxime Ripard wrote: Hi, On Tue, Aug 20, 2019 at 08:46:30AM +0300, Sergey Suloev wrote: Hi, Maxime, On 8/19/19 10:25 PM, Maxime Ripard wrote: From: Maxime Ripard The i2s controller supports TDM, for up to 8 slots. Let's support the TDM API. Signed-off-by: Maxime Ripard --- sound/soc/sunxi/sun4i-i2s.c | 40 -- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 0dac09814b65..4f76daeaaed7 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -168,6 +168,8 @@ struct sun4i_i2s { struct reset_control *rst; unsigned intmclk_freq; + unsigned intslots; + unsigned intslot_width; struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; @@ -287,7 +289,7 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, unsigned int rate, - unsigned int channels, + unsigned int slots, unsigned int word_size) { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); @@ -335,7 +337,7 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, bclk_parent_rate = i2s->variant->get_bclk_parent_rate(i2s); bclk_div = sun4i_i2s_get_bclk_div(i2s, bclk_parent_rate, - rate, channels, word_size); + rate, slots, word_size); if (bclk_div < 0) { dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div); return -EINVAL; @@ -419,6 +421,10 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, const struct snd_pcm_hw_params *params) { unsigned int channels = params_channels(params); + unsigned int slots = channels; + + if (i2s->slots) + slots = i2s->slots; /* Map the channels for playback and capture */ regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG, 0x76543210); @@ -428,7 +434,6 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, SUN4I_I2S_CHAN_SEL_MASK, SUN4I_I2S_CHAN_SEL(channels)); - regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG, SUN4I_I2S_CHAN_SEL_MASK, SUN4I_I2S_CHAN_SEL(channels)); @@ -452,10 +457,18 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int word_size = params_width(params); unsigned int channels = params_channels(params); + unsigned int slots = channels; int ret, sr, wss; u32 width; + if (i2s->slots) + slots = i2s->slots; + + if (i2s->slot_width) + word_size = i2s->slot_width; + ret = i2s->variant->set_chan_cfg(i2s, params); if (ret < 0) { dev_err(dai->dev, "Invalid channel configuration\n"); @@ -477,15 +490,14 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, if (sr < 0) return -EINVAL; - wss = i2s->variant->get_wss(i2s, params_width(params)); + wss = i2s->variant->get_wss(i2s, word_size); if (wss < 0) return -EINVAL; regmap_field_write(i2s->field_fmt_wss, wss); regmap_field_write(i2s->field_fmt_sr, sr); - return sun4i_i2s_set_clk_rate(dai, params_rate(params), - channels, params_width(params)); + return sun4i_i2s_set_clk_rate(dai, params_rate(params), slots, word_size); } static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s, @@ -785,10 +797,26 @@ static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, return 0; } +static int sun4i_i2s_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + if (slots > 8) + return -EINVAL; + + i2s->slots = slots; + i2s->slot_width = slot_width; + + return 0; +} + static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { .hw_params = sun4i_i2s_hw_params, .set_fmt= sun4i_i2s_set_fmt, .set_sysclk = sun4i_i2s_set_sysclk, +
Re: [PATCH 20/21] ASoC: sun4i-i2s: Add support for TDM slots
Hi, Maxime, On 8/19/19 10:25 PM, Maxime Ripard wrote: From: Maxime Ripard The i2s controller supports TDM, for up to 8 slots. Let's support the TDM API. Signed-off-by: Maxime Ripard --- sound/soc/sunxi/sun4i-i2s.c | 40 -- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 0dac09814b65..4f76daeaaed7 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -168,6 +168,8 @@ struct sun4i_i2s { struct reset_control *rst; unsigned int mclk_freq; + unsigned intslots; + unsigned intslot_width; struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; @@ -287,7 +289,7 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, unsigned int rate, - unsigned int channels, + unsigned int slots, unsigned int word_size) { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); @@ -335,7 +337,7 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, bclk_parent_rate = i2s->variant->get_bclk_parent_rate(i2s); bclk_div = sun4i_i2s_get_bclk_div(i2s, bclk_parent_rate, - rate, channels, word_size); + rate, slots, word_size); if (bclk_div < 0) { dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div); return -EINVAL; @@ -419,6 +421,10 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, const struct snd_pcm_hw_params *params) { unsigned int channels = params_channels(params); + unsigned int slots = channels; + + if (i2s->slots) + slots = i2s->slots; /* Map the channels for playback and capture */ regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG, 0x76543210); @@ -428,7 +434,6 @@ static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, SUN4I_I2S_CHAN_SEL_MASK, SUN4I_I2S_CHAN_SEL(channels)); - regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG, SUN4I_I2S_CHAN_SEL_MASK, SUN4I_I2S_CHAN_SEL(channels)); @@ -452,10 +457,18 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int word_size = params_width(params); unsigned int channels = params_channels(params); + unsigned int slots = channels; int ret, sr, wss; u32 width; + if (i2s->slots) + slots = i2s->slots; + + if (i2s->slot_width) + word_size = i2s->slot_width; + ret = i2s->variant->set_chan_cfg(i2s, params); if (ret < 0) { dev_err(dai->dev, "Invalid channel configuration\n"); @@ -477,15 +490,14 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, if (sr < 0) return -EINVAL; - wss = i2s->variant->get_wss(i2s, params_width(params)); + wss = i2s->variant->get_wss(i2s, word_size); if (wss < 0) return -EINVAL; regmap_field_write(i2s->field_fmt_wss, wss); regmap_field_write(i2s->field_fmt_sr, sr); - return sun4i_i2s_set_clk_rate(dai, params_rate(params), - channels, params_width(params)); + return sun4i_i2s_set_clk_rate(dai, params_rate(params), slots, word_size); } static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s, @@ -785,10 +797,26 @@ static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, return 0; } +static int sun4i_i2s_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + if (slots > 8) + return -EINVAL; + + i2s->slots = slots; + i2s->slot_width = slot_width; + + return 0; +} + static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { .hw_params = sun4i_i2s_hw_params, .set_fmt= sun4i_i2s_set_fmt, .set_sysclk = sun4i_i2s_set_sysclk, + .set_tdm_slot = sun4i_i2s_set_tdm_slot, .trigger= sun4i_i2s_trigger, }; it seems like you forgot to implement sun4i_i2s_dai_ops.set_bclk_ratio because, as I far as I understand, it should alter tdm slots functionality ind
Re: BananaPi M2 support
Hi, ChenYu, thanks for for the help. I tried to use "root=PARTUUID=a09ef512-7f29-47b8-8e9a-db5d1d58d8be" in kernel boot parameters, the new log is here https://pastebin.com/EL3uaRpE. Nothing has actually changed, the only difference is that it now says: [ 1.750270] Waiting for root device PARTUUID=a09ef512-7f29-47b8-8e9a-db5d1d58d8be... I verified that a09ef512-7f29-47b8-8e9a-db5d1d58d8be is the actual partition id from my sdcard. Thank you, Sergey On 17.11.2020 20:36, Chen-Yu Tsai wrote: Hi, On Wed, Nov 18, 2020 at 1:06 AM Sergey Suloev wrote: Hi, ChenYu, I have tried to build and run linux-next by tag "next-20201117". Now the boot log looks different but the kernel still hangs. See https://pastebin.com/gFk7XuBc Due to the new asynchronous probing of mmc controllers, the mmcblock device numbers likely have changed, as seen here: [ 1.652275] mmc1: new high speed SDHC card at address 0001 [ 1.652568] hub 1-1:1.0: USB hub found [ 1.658587] mmcblk1: mmc1:0001 EB1QT 29.8 GiB [ 1.661777] hub 1-1:1.0: 4 ports detected [ 1.670263] mmcblk1: p1 You should change your root device specification to use PARTUUID, instead of hardcoding the index. Regards ChenYu Thank you, Sergey On 17.11.2020 11:06, Chen-Yu Tsai wrote: Hi, Please try linux-next. There were some regulator fixes that got merged recently. One of them fixes an infinite recursion when resolving regulator supplies. ChenYu On Tue, Nov 17, 2020 at 1:12 AM Sergey Suloev wrote: Hi, Maxime, it just hangs on that last lines and nothing happens anymore, see 5.18 log. On 16.11.2020 18:52, Maxime Ripard wrote: Hi, On Sat, Nov 14, 2020 at 08:20:54PM +0300, Sergey Suloev wrote: Hi, I noticed that BananaPi M2 (A31 SoC) does not boot anymore on modern kernels. The problem arises somewhere between 5.7.19 - 5.8.18. I have saved boot logs for both versions https://pastebin.com/DTRZi8R7 and https://pastebin.com/PS2hq07A. Logs have been taken on clean/non-patched kernel with default config, u-boot v2020.10. The kernel versions 5.7.x and below work well (I tried 5.5.19 and 5.6.19). The versions 5.8.18 and above all fail (5.9 and 5.10). Could you look at the problem or provide an advice about further investigation, please ? I'm not quite sure what the issue is exactly? How does it fail to boot? Maxime Thank you, Sergey
BananaPi M2 support
Hi, I noticed that BananaPi M2 (A31 SoC) does not boot anymore on modern kernels. The problem arises somewhere between 5.7.19 - 5.8.18. I have saved boot logs for both versions https://pastebin.com/DTRZi8R7 and https://pastebin.com/PS2hq07A. Logs have been taken on clean/non-patched kernel with default config, u-boot v2020.10. The kernel versions 5.7.x and below work well (I tried 5.5.19 and 5.6.19). The versions 5.8.18 and above all fail (5.9 and 5.10). Could you look at the problem or provide an advice about further investigation, please ? Thank you
Re: BananaPi M2 support
Hi, Maxime, it just hangs on that last lines and nothing happens anymore, see 5.18 log. On 16.11.2020 18:52, Maxime Ripard wrote: Hi, On Sat, Nov 14, 2020 at 08:20:54PM +0300, Sergey Suloev wrote: Hi, I noticed that BananaPi M2 (A31 SoC) does not boot anymore on modern kernels. The problem arises somewhere between 5.7.19 - 5.8.18. I have saved boot logs for both versions https://pastebin.com/DTRZi8R7 and https://pastebin.com/PS2hq07A. Logs have been taken on clean/non-patched kernel with default config, u-boot v2020.10. The kernel versions 5.7.x and below work well (I tried 5.5.19 and 5.6.19). The versions 5.8.18 and above all fail (5.9 and 5.10). Could you look at the problem or provide an advice about further investigation, please ? I'm not quite sure what the issue is exactly? How does it fail to boot? Maxime Thank you, Sergey
Re: BananaPi M2 support
Hi, ChenYu, I have tried to build and run linux-next by tag "next-20201117". Now the boot log looks different but the kernel still hangs. See https://pastebin.com/gFk7XuBc Thank you, Sergey On 17.11.2020 11:06, Chen-Yu Tsai wrote: Hi, Please try linux-next. There were some regulator fixes that got merged recently. One of them fixes an infinite recursion when resolving regulator supplies. ChenYu On Tue, Nov 17, 2020 at 1:12 AM Sergey Suloev wrote: Hi, Maxime, it just hangs on that last lines and nothing happens anymore, see 5.18 log. On 16.11.2020 18:52, Maxime Ripard wrote: Hi, On Sat, Nov 14, 2020 at 08:20:54PM +0300, Sergey Suloev wrote: Hi, I noticed that BananaPi M2 (A31 SoC) does not boot anymore on modern kernels. The problem arises somewhere between 5.7.19 - 5.8.18. I have saved boot logs for both versions https://pastebin.com/DTRZi8R7 and https://pastebin.com/PS2hq07A. Logs have been taken on clean/non-patched kernel with default config, u-boot v2020.10. The kernel versions 5.7.x and below work well (I tried 5.5.19 and 5.6.19). The versions 5.8.18 and above all fail (5.9 and 5.10). Could you look at the problem or provide an advice about further investigation, please ? I'm not quite sure what the issue is exactly? How does it fail to boot? Maxime Thank you, Sergey