summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2011-03-23 16:48:09 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:43:22 -0800
commit1d9cfd0a6d4f19733b7ea8cbb11dc455b1ce66a6 (patch)
treec998d061f17c1deb7f0e086092e1230030987225
parent74687bd6c007e8addd5af4c5757ba53ca82556c2 (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. Original-Change-Id: I56d5e466361cb8b3710646e01494ddac46791ae4 Reviewed-on: http://git-master/r/23988 Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com> Tested-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-by: Amit Kamath <akamath@nvidia.com> Reviewed-by: Ramachandrudu Kandhala <rkandhala@nvidia.com> Original-Change-Id: I52b1dcdefa199cd11ae7f838c61411a6268a2d32 Rebase-Id: Rd9f2c70e8c8551ea5ca6ce698a172ef00c08ca67
-rw-r--r--drivers/spi/spi-tegra.c126
-rw-r--r--include/linux/spi-tegra.h10
2 files changed, 102 insertions, 34 deletions
diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c
index c4e2b615f34f..f20a9bae75d1 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
@@ -212,6 +213,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 +568,23 @@ 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;
+#if defined CONFIG_ARCH_TEGRA_3x_SOC
+ unsigned long status2;
+#endif
+ int cs_setup_count;
+ int cs_hold_count;
+
unsigned int cs_pol_bit[] = {
SLINK_CS_POLARITY,
SLINK_CS_POLARITY1,
@@ -589,8 +601,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 +621,71 @@ 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 defined CONFIG_ARCH_TEGRA_3x_SOC
+ status2 = spi_tegra_readl(tspi, SLINK_STATUS2);
+ status2 &= ~SLINK_SS_HOLD_TIME(0xF);
+ status2 |= SLINK_SS_HOLD_TIME(cs_hold_count);
+ spi_tegra_writel(tspi, status2, SLINK_STATUS2);
+#endif
+ }
+ }
+ 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 +698,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 +762,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 +774,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 +813,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 +828,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 */