summaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2021-02-08 16:01:47 +0100
committerMark Brown <broonie@kernel.org>2021-02-08 16:01:47 +0100
commit58898fd82c67350f5be2fc4f22bc9517d66b1115 (patch)
tree31c9adc89384ab4d3ed6a3122c09e186dc5c3667 /drivers/spi
parentspi: Change provied to provided in the file spi.h (diff)
parentspi: mediatek: add set_cs_timing support (diff)
downloadlinux-58898fd82c67350f5be2fc4f22bc9517d66b1115.tar.xz
linux-58898fd82c67350f5be2fc4f22bc9517d66b1115.zip
Merge series "spi: add set_cs_timing support for HW/SW CS mode" from Leilk Liu <leilk.liu@mediatek.com>:
Some controllers only have one HW CS, if support multiple devices, other devices need to use SW CS. This patch adds the support of both HW and SW CS via cs_gpio. leilk.liu (3): spi: add power control when set_cs_timing spi: support CS timing for HW & SW mode spi: mediatek: add set_cs_timing support drivers/spi/spi-mt65xx.c | 72 +++++++++++++++++++++++++++++++--------- drivers/spi/spi.c | 32 +++++++++++++++--- 2 files changed, 83 insertions(+), 21 deletions(-) -- 2.18.0 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi-mt65xx.c72
-rw-r--r--drivers/spi/spi.c32
2 files changed, 83 insertions, 21 deletions
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 5d643051bf3d..976f73b9e299 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -287,7 +287,7 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
static void mtk_spi_prepare_transfer(struct spi_master *master,
struct spi_transfer *xfer)
{
- u32 spi_clk_hz, div, sck_time, cs_time, reg_val;
+ u32 spi_clk_hz, div, sck_time, reg_val;
struct mtk_spi *mdata = spi_master_get_devdata(master);
spi_clk_hz = clk_get_rate(mdata->spi_clk);
@@ -297,32 +297,25 @@ static void mtk_spi_prepare_transfer(struct spi_master *master,
div = 1;
sck_time = (div + 1) / 2;
- cs_time = sck_time * 2;
if (mdata->dev_comp->enhance_timing) {
- reg_val = (((sck_time - 1) & 0xffff)
+ reg_val = readl(mdata->base + SPI_CFG2_REG);
+ reg_val &= ~(0xffff << SPI_CFG2_SCK_HIGH_OFFSET);
+ reg_val |= (((sck_time - 1) & 0xffff)
<< SPI_CFG2_SCK_HIGH_OFFSET);
+ reg_val &= ~(0xffff << SPI_CFG2_SCK_LOW_OFFSET);
reg_val |= (((sck_time - 1) & 0xffff)
<< SPI_CFG2_SCK_LOW_OFFSET);
writel(reg_val, mdata->base + SPI_CFG2_REG);
- reg_val = (((cs_time - 1) & 0xffff)
- << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
- reg_val |= (((cs_time - 1) & 0xffff)
- << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
- writel(reg_val, mdata->base + SPI_CFG0_REG);
} else {
- reg_val = (((sck_time - 1) & 0xff)
+ reg_val = readl(mdata->base + SPI_CFG0_REG);
+ reg_val &= ~(0xff << SPI_CFG0_SCK_HIGH_OFFSET);
+ reg_val |= (((sck_time - 1) & 0xff)
<< SPI_CFG0_SCK_HIGH_OFFSET);
+ reg_val &= ~(0xff << SPI_CFG0_SCK_LOW_OFFSET);
reg_val |= (((sck_time - 1) & 0xff) << SPI_CFG0_SCK_LOW_OFFSET);
- reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET);
- reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG0_CS_SETUP_OFFSET);
writel(reg_val, mdata->base + SPI_CFG0_REG);
}
-
- reg_val = readl(mdata->base + SPI_CFG1_REG);
- reg_val &= ~SPI_CFG1_CS_IDLE_MASK;
- reg_val |= (((cs_time - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET);
- writel(reg_val, mdata->base + SPI_CFG1_REG);
}
static void mtk_spi_setup_packet(struct spi_master *master)
@@ -513,6 +506,52 @@ static bool mtk_spi_can_dma(struct spi_master *master,
(unsigned long)xfer->rx_buf % 4 == 0);
}
+static int mtk_spi_set_hw_cs_timing(struct spi_device *spi,
+ struct spi_delay *setup,
+ struct spi_delay *hold,
+ struct spi_delay *inactive)
+{
+ struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
+ u16 setup_dly, hold_dly, inactive_dly;
+ u32 reg_val;
+
+ if ((setup && setup->unit != SPI_DELAY_UNIT_SCK) ||
+ (hold && hold->unit != SPI_DELAY_UNIT_SCK) ||
+ (inactive && inactive->unit != SPI_DELAY_UNIT_SCK)) {
+ dev_err(&spi->dev,
+ "Invalid delay unit, should be SPI_DELAY_UNIT_SCK\n");
+ return -EINVAL;
+ }
+
+ setup_dly = setup ? setup->value : 1;
+ hold_dly = hold ? hold->value : 1;
+ inactive_dly = inactive ? inactive->value : 1;
+
+ reg_val = readl(mdata->base + SPI_CFG0_REG);
+ if (mdata->dev_comp->enhance_timing) {
+ reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
+ reg_val |= (((hold_dly - 1) & 0xffff)
+ << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
+ reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
+ reg_val |= (((setup_dly - 1) & 0xffff)
+ << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
+ } else {
+ reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET);
+ reg_val |= (((hold_dly - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET);
+ reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET);
+ reg_val |= (((setup_dly - 1) & 0xff)
+ << SPI_CFG0_CS_SETUP_OFFSET);
+ }
+ writel(reg_val, mdata->base + SPI_CFG0_REG);
+
+ reg_val = readl(mdata->base + SPI_CFG1_REG);
+ reg_val &= ~SPI_CFG1_CS_IDLE_MASK;
+ reg_val |= (((inactive_dly - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET);
+ writel(reg_val, mdata->base + SPI_CFG1_REG);
+
+ return 0;
+}
+
static int mtk_spi_setup(struct spi_device *spi)
{
struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
@@ -644,6 +683,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
master->transfer_one = mtk_spi_transfer_one;
master->can_dma = mtk_spi_can_dma;
master->setup = mtk_spi_setup;
+ master->set_cs_timing = mtk_spi_set_hw_cs_timing;
of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node);
if (!of_id) {
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index a17efa8c3047..a247fcac0dc7 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -810,7 +810,8 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
spi->controller->last_cs_enable = enable;
spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
- if (!spi->controller->set_cs_timing) {
+ if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) ||
+ !spi->controller->set_cs_timing) {
if (enable1)
spi_delay_exec(&spi->controller->cs_setup, NULL);
else
@@ -841,7 +842,8 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
spi->controller->set_cs(spi, !enable);
}
- if (!spi->controller->set_cs_timing) {
+ if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) ||
+ !spi->controller->set_cs_timing) {
if (!enable1)
spi_delay_exec(&spi->controller->cs_inactive, NULL);
}
@@ -3460,11 +3462,31 @@ EXPORT_SYMBOL_GPL(spi_setup);
int spi_set_cs_timing(struct spi_device *spi, struct spi_delay *setup,
struct spi_delay *hold, struct spi_delay *inactive)
{
+ struct device *parent = spi->controller->dev.parent;
size_t len;
+ int status;
+
+ if (spi->controller->set_cs_timing &&
+ !(spi->cs_gpiod || gpio_is_valid(spi->cs_gpio))) {
+ if (spi->controller->auto_runtime_pm) {
+ status = pm_runtime_get_sync(parent);
+ if (status < 0) {
+ pm_runtime_put_noidle(parent);
+ dev_err(&spi->controller->dev, "Failed to power device: %d\n",
+ status);
+ return status;
+ }
- if (spi->controller->set_cs_timing)
- return spi->controller->set_cs_timing(spi, setup, hold,
- inactive);
+ status = spi->controller->set_cs_timing(spi, setup,
+ hold, inactive);
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+ return status;
+ } else {
+ return spi->controller->set_cs_timing(spi, setup, hold,
+ inactive);
+ }
+ }
if ((setup && setup->unit == SPI_DELAY_UNIT_SCK) ||
(hold && hold->unit == SPI_DELAY_UNIT_SCK) ||