mirror of https://gitee.com/openkylin/linux.git
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:
commit
58898fd82c
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
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) ||
|
||||
|
|
Loading…
Reference in New Issue