summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSunny Luo <sunny.luo@amlogic.com>2020-03-12 14:31:24 +0100
committerMark Brown <broonie@kernel.org>2020-03-12 17:22:50 +0000
commita6cda1f905b4a5442eecce94bda1e136f7e1e539 (patch)
treea0ae67c5817c1c02e537d480cfd5506ce898fe97
parentb9dfb20eed5c7dab37d6267a985dbe16df9e4293 (diff)
spi: meson-spicc: enhance output enable feature
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>
-rw-r--r--drivers/spi/spi-meson-spicc.c53
1 files changed, 51 insertions, 2 deletions
diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c
index 8425e5ae1273..ba70ef94a82a 100644
--- a/drivers/spi/spi-meson-spicc.c
+++ b/drivers/spi/spi-meson-spicc.c
@@ -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>
@@ -113,12 +115,23 @@
#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);