summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2011-03-23 16:48:09 +0530
committerNiket Sirsi <nsirsi@nvidia.com>2011-08-16 16:21:20 -0700
commit9f4a4ab7234dcf130d897faa12e9d965edaa3c50 (patch)
tree7f03358b8a3d458692572e338a64c48289ac1187
parentb942099aeca50f883472926dd01f9f8d486c5149 (diff)
spi: tegra: Supporting HW based CS control
Supporting the hw based CS to communicate to spi device. This provides the constraints in hold and setup time of CS before clock start and clock ends. The hw based CS can be selected if spi client provide the option through the device controler data and only one transfer per message is requested. Reviewed-on: http://git-master/r/23988 Change-Id: Iba6d1efd8c5406f1b44ddd44b6f739ac529a94df Reviewed-on: http://git-master/r/47150 Tested-by: Cho-Che Cheng <jacheng@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--drivers/spi/spi_tegra.c122
-rw-r--r--include/linux/spi-tegra.h10
2 files changed, 93 insertions, 39 deletions
diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c
index 78993ad17dcc..99b03d4dd7be 100644
--- a/drivers/spi/spi_tegra.c
+++ b/drivers/spi/spi_tegra.c
@@ -132,6 +132,7 @@
#define SLINK_STATUS2 0x01c
#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0)
#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f0000) >> 16)
+#define SLINK_SS_HOLD_TIME(val) (((val) & 0xF) << 6)
#define SLINK_TX_FIFO 0x100
#define SLINK_RX_FIFO 0x180
@@ -148,11 +149,6 @@ static const unsigned long spi_tegra_req_sels[] = {
TEGRA_DMA_REQ_SEL_SL2B2,
TEGRA_DMA_REQ_SEL_SL2B3,
TEGRA_DMA_REQ_SEL_SL2B4,
-#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
- TEGRA_DMA_REQ_SEL_SL2B5,
- TEGRA_DMA_REQ_SEL_SL2B6,
-#endif
-
};
#define DEFAULT_SPI_DMA_BUF_LEN (16*1024)
@@ -212,6 +208,8 @@ struct spi_tegra_data {
bool clk_state;
bool is_suspended;
+ bool is_hw_based_cs;
+
struct completion rx_dma_complete;
struct completion tx_dma_complete;
@@ -565,14 +563,20 @@ static int spi_tegra_start_cpu_based_transfer(
}
static void spi_tegra_start_transfer(struct spi_device *spi,
- struct spi_transfer *t, bool is_first_of_msg)
+ struct spi_transfer *t, bool is_first_of_msg,
+ bool is_single_xfer)
{
struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
u32 speed;
u8 bits_per_word;
- unsigned long val;
unsigned total_fifo_words;
int ret;
+ struct tegra_spi_device_controller_data *cdata = spi->controller_data;
+ unsigned long command;
+ unsigned long command2;
+ int cs_setup_count;
+ int cs_hold_count;
+
unsigned int cs_pol_bit[] = {
SLINK_CS_POLARITY,
SLINK_CS_POLARITY1,
@@ -589,8 +593,17 @@ static void spi_tegra_start_transfer(struct spi_device *spi,
tspi->cur_speed = speed;
}
- if (is_first_of_msg) {
+ tspi->cur = t;
+ tspi->cur_spi = spi;
+ tspi->cur_pos = 0;
+ tspi->cur_rx_pos = 0;
+ tspi->cur_tx_pos = 0;
+ tspi->rx_complete = 0;
+ tspi->tx_complete = 0;
+ total_fifo_words = spi_tegra_calculate_curr_xfer_param(spi, tspi, t);
+ command2 = tspi->def_command2_reg;
+ if (is_first_of_msg) {
if (!tspi->is_clkon_always) {
if (!tspi->clk_state) {
clk_enable(tspi->clk);
@@ -600,52 +613,65 @@ static void spi_tegra_start_transfer(struct spi_device *spi,
spi_tegra_clear_status(tspi);
- val = tspi->def_command_reg;
- val |= SLINK_BIT_LENGTH(bits_per_word - 1);
-
- val ^= cs_pol_bit[spi->chip_select];
+ command = tspi->def_command_reg;
+ command |= SLINK_BIT_LENGTH(bits_per_word - 1);
+
+ /* possibly use the hw based chip select */
+ tspi->is_hw_based_cs = false;
+ if (cdata && cdata->is_hw_based_cs && is_single_xfer) {
+ if ((tspi->curr_dma_words * tspi->bytes_per_word) ==
+ (t->len - tspi->cur_pos)) {
+ cs_setup_count = cdata->cs_setup_clk_count >> 1;
+ if (cs_setup_count > 3)
+ cs_setup_count = 3;
+ cs_hold_count = cdata->cs_hold_clk_count;
+ if (cs_hold_count > 0xF)
+ cs_hold_count = 0xF;
+ tspi->is_hw_based_cs = true;
+
+ command &= ~SLINK_CS_SW;
+ command2 &= ~SLINK_SS_SETUP(3);
+ command2 |= SLINK_SS_SETUP(cs_setup_count);
+ }
+ }
+ if (!tspi->is_hw_based_cs) {
+ command |= SLINK_CS_SW;
+ command ^= cs_pol_bit[spi->chip_select];
+ }
- val &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA;
+ command &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA;
if (spi->mode & SPI_CPHA)
- val |= SLINK_CK_SDA;
+ command |= SLINK_CK_SDA;
if (spi->mode & SPI_CPOL)
- val |= SLINK_IDLE_SCLK_DRIVE_HIGH;
+ command |= SLINK_IDLE_SCLK_DRIVE_HIGH;
else
- val |= SLINK_IDLE_SCLK_DRIVE_LOW;
+ command |= SLINK_IDLE_SCLK_DRIVE_LOW;
} else {
- val = tspi->command_reg;
- val &= ~SLINK_BIT_LENGTH(~0);
- val |= SLINK_BIT_LENGTH(bits_per_word - 1);
+ command = tspi->command_reg;
+ command &= ~SLINK_BIT_LENGTH(~0);
+ command |= SLINK_BIT_LENGTH(bits_per_word - 1);
}
- spi_tegra_writel(tspi, val, SLINK_COMMAND);
- tspi->command_reg = val;
+
+ spi_tegra_writel(tspi, command, SLINK_COMMAND);
+ tspi->command_reg = command;
dev_dbg(&tspi->pdev->dev, "The def 0x%x and written 0x%lx\n",
- tspi->def_command_reg, val);
+ tspi->def_command_reg, command);
- val = tspi->def_command2_reg;
- val &= ~(SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN);
+ command2 &= ~(SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN);
tspi->cur_direction = 0;
if (t->rx_buf) {
- val |= SLINK_RXEN;
+ command2 |= SLINK_RXEN;
tspi->cur_direction |= DATA_DIR_RX;
}
if (t->tx_buf) {
- val |= SLINK_TXEN;
+ command2 |= SLINK_TXEN;
tspi->cur_direction |= DATA_DIR_TX;
}
- val |= SLINK_SS_EN_CS(spi->chip_select);
- spi_tegra_writel(tspi, val, SLINK_COMMAND2);
-
- tspi->cur = t;
- tspi->cur_spi = spi;
- tspi->cur_pos = 0;
- tspi->cur_rx_pos = 0;
- tspi->cur_tx_pos = 0;
- tspi->rx_complete = 0;
- tspi->tx_complete = 0;
- total_fifo_words = spi_tegra_calculate_curr_xfer_param(spi, tspi, t);
+ command2 |= SLINK_SS_EN_CS(spi->chip_select);
+ spi_tegra_writel(tspi, command2, SLINK_COMMAND2);
+ tspi->command2_reg = command2;
if (total_fifo_words > SPI_FIFO_DEPTH)
ret = spi_tegra_start_dma_based_transfer(tspi, t);
@@ -658,12 +684,14 @@ static void spi_tegra_start_message(struct spi_device *spi,
struct spi_message *m)
{
struct spi_transfer *t;
+ int single_xfer = 0;
+ single_xfer = list_is_singular(&m->transfers);
m->actual_length = 0;
m->status = 0;
t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list);
- spi_tegra_start_transfer(spi, t, true);
+ spi_tegra_start_transfer(spi, t, true, single_xfer);
}
static int spi_tegra_setup(struct spi_device *spi)
@@ -720,6 +748,7 @@ static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m)
struct spi_transfer *t;
unsigned long flags;
int was_empty;
+ int bytes_per_word;
if (list_empty(&m->transfers) || !m->complete)
return -EINVAL;
@@ -731,6 +760,15 @@ static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m)
if (t->len == 0)
return -EINVAL;
+ /* Check that the all words are available */
+ if (t->bits_per_word)
+ bytes_per_word = (t->bits_per_word + 7)/8;
+ else
+ bytes_per_word = (spi->bits_per_word + 7)/8;
+
+ if (t->len % bytes_per_word != 0)
+ return -EINVAL;
+
if (!t->rx_buf && !t->tx_buf)
return -EINVAL;
}
@@ -761,6 +799,12 @@ static void spi_tegra_curr_transfer_complete(struct spi_tegra_data *tspi,
struct spi_message *m;
struct spi_device *spi;
+ /* Check if CS need to be toggele here */
+ if (tspi->cur && tspi->cur->cs_change &&
+ tspi->cur->delay_usecs) {
+ udelay(tspi->cur->delay_usecs);
+ }
+
m = list_first_entry(&tspi->queue, struct spi_message, queue);
if (err)
m->status = -EIO;
@@ -770,7 +814,7 @@ static void spi_tegra_curr_transfer_complete(struct spi_tegra_data *tspi,
if (!list_is_last(&tspi->cur->transfer_list, &m->transfers)) {
tspi->cur = list_first_entry(&tspi->cur->transfer_list,
struct spi_transfer, transfer_list);
- spi_tegra_start_transfer(spi, tspi->cur, false);
+ spi_tegra_start_transfer(spi, tspi->cur, false, 0);
} else {
list_del(&m->queue);
m->complete(m->context);
diff --git a/include/linux/spi-tegra.h b/include/linux/spi-tegra.h
index 2fe1af9c239e..3e0d26d51c50 100644
--- a/include/linux/spi-tegra.h
+++ b/include/linux/spi-tegra.h
@@ -26,4 +26,14 @@ struct tegra_spi_platform_data {
int max_dma_buffer;
bool is_clkon_always;
};
+
+/* Controller data from device to pass some info like
+ * hw based chip select can be used or not and if yes
+ * then CS hold and setup time. */
+struct tegra_spi_device_controller_data {
+ bool is_hw_based_cs;
+ int cs_setup_clk_count;
+ int cs_hold_clk_count;
+};
+
#endif /* _LINUX_SPI_TEGRA_H */