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:
Tomasz Bursztyka 2016-01-18 17:16:24 +01:00 committed by Anas Nashif
parent 655fb9fb2e
commit 3ef1517c1a
3 changed files with 84 additions and 0 deletions

View file

@ -232,6 +232,11 @@ config SPI_DW_ARC_AUX_REGS
registers and thus their access is different than memory registers and thus their access is different than memory
mapped registers. 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 choice
depends on SPI_DW depends on SPI_DW
prompt "DesignWare SPI interrupt trigger condition" prompt "DesignWare SPI interrupt trigger condition"
@ -282,6 +287,17 @@ config SPI_DW_PORT_0_CLOCK_GATE_SUBSYS
depends on SPI_DW_CLOCK_GATE depends on SPI_DW_CLOCK_GATE
default 0 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 config SPI_DW_PORT_0_DRV_NAME
string string
prompt "Designware SPI port 0 device name" 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 depends on SPI_DW_CLOCK_GATE
default 0 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 config SPI_DW_PORT_1_DRV_NAME
string string
prompt "Designware SPI port 1 device name" prompt "Designware SPI port 1 device name"

View file

@ -157,6 +157,43 @@ static inline void _clock_off(struct device *dev)
#define _clock_off(...) #define _clock_off(...)
#endif #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) static void completed(struct device *dev, int error)
{ {
struct spi_dw_config *info = dev->config->config_info; struct spi_dw_config *info = dev->config->config_info;
@ -179,6 +216,7 @@ static void completed(struct device *dev, int error)
/* Disabling interrupts */ /* Disabling interrupts */
write_imr(DW_SPI_IMR_MASK, info->regs); write_imr(DW_SPI_IMR_MASK, info->regs);
_spi_control_cs(dev, 0);
synchronous_call_complete(&spi->sync); synchronous_call_complete(&spi->sync);
} }
@ -386,6 +424,8 @@ static int spi_dw_transceive(struct device *dev,
/* Slave select */ /* Slave select */
write_ser(spi->slave, info->regs); write_ser(spi->slave, info->regs);
_spi_control_cs(dev, 1);
/* Enable interrupts */ /* Enable interrupts */
write_imr(DW_SPI_IMR_UNMASK, info->regs); write_imr(DW_SPI_IMR_UNMASK, info->regs);
@ -490,6 +530,8 @@ int spi_dw_init(struct device *dev)
synchronous_call_init(&spi->sync); synchronous_call_init(&spi->sync);
_spi_config_cs(dev);
write_imr(DW_SPI_IMR_MASK, info->regs); write_imr(DW_SPI_IMR_MASK, info->regs);
clear_bit_ssienr(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 #ifdef CONFIG_SPI_DW_CLOCK_GATE
.clock_data = UINT_TO_POINTER(CONFIG_SPI_DW_PORT_0_CLOCK_GATE_SUBSYS), .clock_data = UINT_TO_POINTER(CONFIG_SPI_DW_PORT_0_CLOCK_GATE_SUBSYS),
#endif /* CONFIG_SPI_DW_CLOCK_GATE */ #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 .config_func = spi_config_0_irq
}; };
@ -555,6 +601,10 @@ struct spi_dw_config spi_dw_config_1 = {
#ifdef CONFIG_SPI_DW_CLOCK_GATE #ifdef CONFIG_SPI_DW_CLOCK_GATE
.clock_data = UINT_TO_POINTER(CONFIG_SPI_DW_PORT_1_CLOCK_GATE_SUBSYS), .clock_data = UINT_TO_POINTER(CONFIG_SPI_DW_PORT_1_CLOCK_GATE_SUBSYS),
#endif /* CONFIG_SPI_DW_CLOCK_GATE */ #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 .config_func = spi_config_1_irq
}; };

View file

@ -31,6 +31,10 @@ struct spi_dw_config {
#ifdef CONFIG_SPI_DW_CLOCK_GATE #ifdef CONFIG_SPI_DW_CLOCK_GATE
void *clock_data; void *clock_data;
#endif /* CONFIG_SPI_DW_CLOCK_GATE */ #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; spi_dw_config_t config_func;
}; };
@ -44,6 +48,9 @@ struct spi_dw_data {
#ifdef CONFIG_SPI_DW_CLOCK_GATE #ifdef CONFIG_SPI_DW_CLOCK_GATE
struct device *clock; struct device *clock;
#endif /* CONFIG_SPI_DW_CLOCK_GATE */ #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; uint8_t *tx_buf;
uint32_t tx_buf_len; uint32_t tx_buf_len;
uint8_t *rx_buf; uint8_t *rx_buf;