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
This commit is contained in:
Mark Brown 2021-02-08 15:01:47 +00:00
commit 58898fd82c
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 83 additions and 21 deletions

View File

@ -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) {

View File

@ -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)
return spi->controller->set_cs_timing(spi, setup, hold,
inactive);
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;
}
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) ||