spi: dw: add support for gpio controlled chip select
Also, use this opportunity to let spi_chip_sel() handle chip-select deactivation as well. Signed-off-by: Baruch Siach <baruch@tkos.co.il> Signed-off-by: Mark Brown <broonie@linaro.org>
This commit is contained in:
Родитель
20e5ea1915
Коммит
d9c73bb8a3
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
|
|
||||||
#include "spi-dw.h"
|
#include "spi-dw.h"
|
||||||
|
|
||||||
|
@ -70,6 +71,27 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
||||||
dws->num_cs = 4;
|
dws->num_cs = 4;
|
||||||
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
||||||
|
|
||||||
|
if (pdev->dev.of_node) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < dws->num_cs; i++) {
|
||||||
|
int cs_gpio = of_get_named_gpio(pdev->dev.of_node,
|
||||||
|
"cs-gpios", i);
|
||||||
|
|
||||||
|
if (cs_gpio == -EPROBE_DEFER) {
|
||||||
|
ret = cs_gpio;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpio_is_valid(cs_gpio)) {
|
||||||
|
ret = devm_gpio_request(&pdev->dev, cs_gpio,
|
||||||
|
dev_name(&pdev->dev));
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = dw_spi_add_host(&pdev->dev, dws);
|
ret = dw_spi_add_host(&pdev->dev, dws);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
|
||||||
#include "spi-dw.h"
|
#include "spi-dw.h"
|
||||||
|
|
||||||
|
@ -36,9 +37,6 @@
|
||||||
#define DONE_STATE ((void *)2)
|
#define DONE_STATE ((void *)2)
|
||||||
#define ERROR_STATE ((void *)-1)
|
#define ERROR_STATE ((void *)-1)
|
||||||
|
|
||||||
#define MRST_SPI_DEASSERT 0
|
|
||||||
#define MRST_SPI_ASSERT 1
|
|
||||||
|
|
||||||
/* Slave spi_dev related */
|
/* Slave spi_dev related */
|
||||||
struct chip_data {
|
struct chip_data {
|
||||||
u16 cr0;
|
u16 cr0;
|
||||||
|
@ -272,8 +270,8 @@ static void giveback(struct dw_spi *dws)
|
||||||
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
|
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
|
||||||
transfer_list);
|
transfer_list);
|
||||||
|
|
||||||
if (!last_transfer->cs_change && dws->cs_control)
|
if (!last_transfer->cs_change)
|
||||||
dws->cs_control(MRST_SPI_DEASSERT);
|
spi_chip_sel(dws, dws->cur_msg->spi, 0);
|
||||||
|
|
||||||
spi_finalize_current_message(dws->master);
|
spi_finalize_current_message(dws->master);
|
||||||
}
|
}
|
||||||
|
@ -493,7 +491,7 @@ static void pump_transfers(unsigned long data)
|
||||||
dw_writew(dws, DW_SPI_CTRL0, cr0);
|
dw_writew(dws, DW_SPI_CTRL0, cr0);
|
||||||
|
|
||||||
spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
|
spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
|
||||||
spi_chip_sel(dws, spi->chip_select);
|
spi_chip_sel(dws, spi, 1);
|
||||||
|
|
||||||
/* Set the interrupt mask, for poll mode just disable all int */
|
/* Set the interrupt mask, for poll mode just disable all int */
|
||||||
spi_mask_intr(dws, 0xff);
|
spi_mask_intr(dws, 0xff);
|
||||||
|
@ -544,6 +542,7 @@ static int dw_spi_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct dw_spi_chip *chip_info = NULL;
|
struct dw_spi_chip *chip_info = NULL;
|
||||||
struct chip_data *chip;
|
struct chip_data *chip;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* Only alloc on first setup */
|
/* Only alloc on first setup */
|
||||||
chip = spi_get_ctldata(spi);
|
chip = spi_get_ctldata(spi);
|
||||||
|
@ -597,6 +596,13 @@ static int dw_spi_setup(struct spi_device *spi)
|
||||||
| (spi->mode << SPI_MODE_OFFSET)
|
| (spi->mode << SPI_MODE_OFFSET)
|
||||||
| (chip->tmode << SPI_TMOD_OFFSET);
|
| (chip->tmode << SPI_TMOD_OFFSET);
|
||||||
|
|
||||||
|
if (gpio_is_valid(spi->cs_gpio)) {
|
||||||
|
ret = gpio_direction_output(spi->cs_gpio,
|
||||||
|
!(spi->mode & SPI_CS_HIGH));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
|
||||||
/* Register offsets */
|
/* Register offsets */
|
||||||
#define DW_SPI_CTRL0 0x00
|
#define DW_SPI_CTRL0 0x00
|
||||||
|
@ -178,15 +179,20 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div)
|
||||||
dw_writel(dws, DW_SPI_BAUDR, div);
|
dw_writel(dws, DW_SPI_BAUDR, div);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
|
static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi,
|
||||||
|
int active)
|
||||||
{
|
{
|
||||||
if (cs > dws->num_cs)
|
u16 cs = spi->chip_select;
|
||||||
return;
|
int gpio_val = active ? (spi->mode & SPI_CS_HIGH) :
|
||||||
|
!(spi->mode & SPI_CS_HIGH);
|
||||||
|
|
||||||
if (dws->cs_control)
|
if (dws->cs_control)
|
||||||
dws->cs_control(1);
|
dws->cs_control(active);
|
||||||
|
if (gpio_is_valid(spi->cs_gpio))
|
||||||
|
gpio_set_value(spi->cs_gpio, gpio_val);
|
||||||
|
|
||||||
dw_writel(dws, DW_SPI_SER, 1 << cs);
|
if (active)
|
||||||
|
dw_writel(dws, DW_SPI_SER, 1 << cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable IRQ bits */
|
/* Disable IRQ bits */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче