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>
This commit is contained in:
Sunny Luo 2020-03-12 14:31:24 +01:00 committed by Mark Brown
parent b9dfb20eed
commit a6cda1f905
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View 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>
@ -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);