spi: meson-spicc: enhance output enable feature
authorSunny Luo <sunny.luo@amlogic.com>
Thu, 12 Mar 2020 13:31:24 +0000 (14:31 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 12 Mar 2020 17:22:50 +0000 (17:22 +0000)
The SPICC controller in Meson-AXG is capable of driving the CLK/MOSI/SS
signal lines through the idle state (between two transmission operation),
which avoid the signals floating in unexpected state.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
Signed-off-by: Sunny Luo <sunny.luo@amlogic.com>
Link: https://lore.kernel.org/r/20200312133131.26430-3-narmstrong@baylibre.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-meson-spicc.c

index 8425e5a..ba70ef9 100644 (file)
@@ -9,11 +9,13 @@
 
 #include <linux/bitfield.h>
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/types.h>
 
 #define SPICC_DWADDR   0x24    /* Write Address of DMA */
 
+#define SPICC_ENH_CTL0 0x38    /* Enhanced Feature */
+#define SPICC_ENH_MOSI_OEN             BIT(25)
+#define SPICC_ENH_CLK_OEN              BIT(26)
+#define SPICC_ENH_CS_OEN               BIT(27)
+#define SPICC_ENH_CLK_CS_DELAY_EN      BIT(28)
+#define SPICC_ENH_MAIN_CLK_AO          BIT(29)
+
 #define writel_bits_relaxed(mask, val, addr) \
        writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
 
 #define SPICC_BURST_MAX        16
 #define SPICC_FIFO_HALF 10
 
+struct meson_spicc_data {
+       bool                            has_oen;
+};
+
 struct meson_spicc_device {
        struct spi_master               *master;
        struct platform_device          *pdev;
@@ -126,6 +139,7 @@ struct meson_spicc_device {
        struct clk                      *core;
        struct spi_message              *message;
        struct spi_transfer             *xfer;
+       const struct meson_spicc_data   *data;
        u8                              *tx_buf;
        u8                              *rx_buf;
        unsigned int                    bytes_per_word;
@@ -136,6 +150,19 @@ struct meson_spicc_device {
        bool                            is_last_burst;
 };
 
+static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
+{
+       u32 conf;
+
+       if (!spicc->data->has_oen)
+               return;
+
+       conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0) |
+               SPICC_ENH_MOSI_OEN | SPICC_ENH_CLK_OEN | SPICC_ENH_CS_OEN;
+
+       writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
+}
+
 static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
 {
        return !!FIELD_GET(SPICC_TF,
@@ -489,6 +516,13 @@ static int meson_spicc_probe(struct platform_device *pdev)
        spicc = spi_master_get_devdata(master);
        spicc->master = master;
 
+       spicc->data = of_device_get_match_data(&pdev->dev);
+       if (!spicc->data) {
+               dev_err(&pdev->dev, "failed to get match data\n");
+               ret = -EINVAL;
+               goto out_master;
+       }
+
        spicc->pdev = pdev;
        platform_set_drvdata(pdev, spicc);
 
@@ -548,6 +582,8 @@ static int meson_spicc_probe(struct platform_device *pdev)
        else
                master->max_speed_hz = rate >> 2;
 
+       meson_spicc_oen_enable(spicc);
+
        ret = devm_spi_register_master(&pdev->dev, master);
        if (ret) {
                dev_err(&pdev->dev, "spi master registration failed\n");
@@ -577,9 +613,22 @@ static int meson_spicc_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct meson_spicc_data meson_spicc_gx_data = {
+};
+
+static const struct meson_spicc_data meson_spicc_axg_data = {
+       .has_oen                = true,
+};
+
 static const struct of_device_id meson_spicc_of_match[] = {
-       { .compatible = "amlogic,meson-gx-spicc", },
-       { .compatible = "amlogic,meson-axg-spicc", },
+       {
+               .compatible     = "amlogic,meson-gx-spicc",
+               .data           = &meson_spicc_gx_data,
+       },
+       {
+               .compatible = "amlogic,meson-axg-spicc",
+               .data           = &meson_spicc_axg_data,
+       },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, meson_spicc_of_match);