spi: dw: Add Kconfig option to emulate CS through a GPIO pin
It might be necessary to emulate CS through a GPIO pin depending on these 2 conditions: - the controller's CS pin is not wired, and thus a GPIO pin is the only option - The controller is unstable at a certain frequency and cannot set/unset CS reliably. This is actually a possible issue on DesignWare's SPI controller in Quark SE or Quarks D2000 where it has been found unstable at 1Mhz and above. Change-Id: Ib6a06577906c005ddd347070d476a367a9c3da8a Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
parent
655fb9fb2e
commit
3ef1517c1a
|
@ -232,6 +232,11 @@ config SPI_DW_ARC_AUX_REGS
|
|||
registers and thus their access is different than memory
|
||||
mapped registers.
|
||||
|
||||
config SPI_DW_CS_GPIO
|
||||
bool "SPI port CS pin is controlled via a GPIO port"
|
||||
depends on SPI_DW && GPIO
|
||||
default n
|
||||
|
||||
choice
|
||||
depends on SPI_DW
|
||||
prompt "DesignWare SPI interrupt trigger condition"
|
||||
|
@ -282,6 +287,17 @@ config SPI_DW_PORT_0_CLOCK_GATE_SUBSYS
|
|||
depends on SPI_DW_CLOCK_GATE
|
||||
default 0
|
||||
|
||||
config SPI_DW_PORT_0_CS_GPIO_PORT
|
||||
string
|
||||
prompt "The GPIO port which is used to control CS"
|
||||
depends on SPI_DW_PORT_0 && SPI_DW_CS_GPIO
|
||||
default GPIO_DW_0_NAME
|
||||
|
||||
config SPI_DW_PORT_0_CS_GPIO_PIN
|
||||
int "The GPIO PIN which is used to act as a CS pin"
|
||||
depends on SPI_DW_PORT_0 && SPI_DW_CS_GPIO
|
||||
default 0
|
||||
|
||||
config SPI_DW_PORT_0_DRV_NAME
|
||||
string
|
||||
prompt "Designware SPI port 0 device name"
|
||||
|
@ -319,6 +335,17 @@ config SPI_DW_PORT_1_CLOCK_GATE_SUBSYS
|
|||
depends on SPI_DW_CLOCK_GATE
|
||||
default 0
|
||||
|
||||
config SPI_DW_PORT_1_CS_GPIO_PORT
|
||||
string
|
||||
prompt "The GPIO port which is used to control CS"
|
||||
depends on SPI_DW_PORT_0 && SPI_DW_CS_GPIO
|
||||
default GPIO_DW_0_NAME
|
||||
|
||||
config SPI_DW_PORT_1_CS_GPIO_PIN
|
||||
int "The GPIO PIN which is used to act as a CS pin"
|
||||
depends on SPI_DW_PORT_0 && SPI_DW_CS_GPIO
|
||||
default 0
|
||||
|
||||
config SPI_DW_PORT_1_DRV_NAME
|
||||
string
|
||||
prompt "Designware SPI port 1 device name"
|
||||
|
|
|
@ -157,6 +157,43 @@ static inline void _clock_off(struct device *dev)
|
|||
#define _clock_off(...)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
||||
|
||||
#include <gpio.h>
|
||||
|
||||
static inline void _spi_config_cs(struct device *dev)
|
||||
{
|
||||
struct spi_dw_config *info = dev->config->config_info;
|
||||
struct spi_dw_data *spi = dev->driver_data;
|
||||
struct device *gpio;
|
||||
|
||||
gpio = device_get_binding(info->cs_gpio_name);
|
||||
if (!gpio) {
|
||||
spi->cs_gpio_port = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_pin_configure(gpio, info->cs_gpio_pin, GPIO_DIR_OUT);
|
||||
/* Default CS line to high (idling) */
|
||||
gpio_pin_write(gpio, info->cs_gpio_pin, 1);
|
||||
|
||||
spi->cs_gpio_port = gpio;
|
||||
}
|
||||
|
||||
static inline void _spi_control_cs(struct device *dev, int on)
|
||||
{
|
||||
struct spi_dw_config *info = dev->config->config_info;
|
||||
struct spi_dw_data *spi = dev->driver_data;
|
||||
|
||||
if (spi->cs_gpio_port) {
|
||||
gpio_pin_write(spi->cs_gpio_port, info->cs_gpio_pin, !on);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define _spi_control_cs(...)
|
||||
#define _spi_config_cs(...)
|
||||
#endif /* CONFIG_SPI_DW_CS_GPIO */
|
||||
|
||||
static void completed(struct device *dev, int error)
|
||||
{
|
||||
struct spi_dw_config *info = dev->config->config_info;
|
||||
|
@ -179,6 +216,7 @@ static void completed(struct device *dev, int error)
|
|||
/* Disabling interrupts */
|
||||
write_imr(DW_SPI_IMR_MASK, info->regs);
|
||||
|
||||
_spi_control_cs(dev, 0);
|
||||
synchronous_call_complete(&spi->sync);
|
||||
}
|
||||
|
||||
|
@ -386,6 +424,8 @@ static int spi_dw_transceive(struct device *dev,
|
|||
/* Slave select */
|
||||
write_ser(spi->slave, info->regs);
|
||||
|
||||
_spi_control_cs(dev, 1);
|
||||
|
||||
/* Enable interrupts */
|
||||
write_imr(DW_SPI_IMR_UNMASK, info->regs);
|
||||
|
||||
|
@ -490,6 +530,8 @@ int spi_dw_init(struct device *dev)
|
|||
|
||||
synchronous_call_init(&spi->sync);
|
||||
|
||||
_spi_config_cs(dev);
|
||||
|
||||
write_imr(DW_SPI_IMR_MASK, info->regs);
|
||||
clear_bit_ssienr(info->regs);
|
||||
|
||||
|
@ -526,6 +568,10 @@ struct spi_dw_config spi_dw_config_0 = {
|
|||
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||
.clock_data = UINT_TO_POINTER(CONFIG_SPI_DW_PORT_0_CLOCK_GATE_SUBSYS),
|
||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
||||
.cs_gpio_name = CONFIG_SPI_DW_PORT_0_CS_GPIO_PORT,
|
||||
.cs_gpio_pin = CONFIG_SPI_DW_PORT_0_CS_GPIO_PIN,
|
||||
#endif
|
||||
.config_func = spi_config_0_irq
|
||||
};
|
||||
|
||||
|
@ -555,6 +601,10 @@ struct spi_dw_config spi_dw_config_1 = {
|
|||
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||
.clock_data = UINT_TO_POINTER(CONFIG_SPI_DW_PORT_1_CLOCK_GATE_SUBSYS),
|
||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
||||
.cs_gpio_name = CONFIG_SPI_DW_PORT_1_CS_GPIO_PORT,
|
||||
.cs_gpio_pin = CONFIG_SPI_DW_PORT_1_CS_GPIO_PIN,
|
||||
#endif
|
||||
.config_func = spi_config_1_irq
|
||||
};
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ struct spi_dw_config {
|
|||
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||
void *clock_data;
|
||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
||||
char *cs_gpio_name;
|
||||
uint32_t cs_gpio_pin;
|
||||
#endif /* CONFIG_SPI_DW_CS_GPIO */
|
||||
spi_dw_config_t config_func;
|
||||
};
|
||||
|
||||
|
@ -44,6 +48,9 @@ struct spi_dw_data {
|
|||
#ifdef CONFIG_SPI_DW_CLOCK_GATE
|
||||
struct device *clock;
|
||||
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
|
||||
#ifdef CONFIG_SPI_DW_CS_GPIO
|
||||
struct device *cs_gpio_port;
|
||||
#endif /* CONFIG_SPI_DW_CS_GPIO */
|
||||
uint8_t *tx_buf;
|
||||
uint32_t tx_buf_len;
|
||||
uint8_t *rx_buf;
|
||||
|
|
Loading…
Reference in a new issue