summaryrefslogtreecommitdiff
path: root/drivers/mfd/apalis-tk1-k20.c
diff options
context:
space:
mode:
authorDominik Sliwa <dominik.sliwa@toradex.com>2016-12-01 14:18:47 +0100
committerMarcel Ziswiler <marcel.ziswiler@toradex.com>2017-06-30 18:24:32 +0200
commitd52931aac9516724d3067516ca1c4ef762f0daea (patch)
tree35f53a8e91616b8184c95b2ae6899a8c17186315 /drivers/mfd/apalis-tk1-k20.c
parente5ea4ac452f04e3d534567928b39afef7d11b94c (diff)
apalis-tk1-k20: can and spi improvements
This patch includes CAN driver and improvements in SPI communications for Apalis TK1 k20 based MFD. Requires firmware version 0.9. Signed-off-by: Dominik Sliwa <dominik.sliwa@toradex.com> Acked-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Diffstat (limited to 'drivers/mfd/apalis-tk1-k20.c')
-rw-r--r--drivers/mfd/apalis-tk1-k20.c255
1 files changed, 190 insertions, 65 deletions
diff --git a/drivers/mfd/apalis-tk1-k20.c b/drivers/mfd/apalis-tk1-k20.c
index ef8d64a64b71..8d3227a18912 100644
--- a/drivers/mfd/apalis-tk1-k20.c
+++ b/drivers/mfd/apalis-tk1-k20.c
@@ -53,16 +53,20 @@ static const struct regmap_config apalis_tk1_k20_regmap_spi_config = {
.max_register = APALIS_TK1_K20_NUMREGS,
.cache_type = REGCACHE_NONE,
-
+#ifdef CONFIG_EXPERIMENTAL_K20_HSMODE
+ .use_single_rw = 0,
+#else
.use_single_rw = 1,
+#endif
+
};
static int apalis_tk1_k20_spi_read(void *context, const void *reg,
size_t reg_size, void *val, size_t val_size)
{
- unsigned char w[3] = {APALIS_TK1_K20_READ_INST,
- *((unsigned char *) reg), 0};
- unsigned char r[3];
+ unsigned char w[APALIS_TK1_K20_MAX_BULK] = {APALIS_TK1_K20_READ_INST,
+ val_size, *((unsigned char *) reg)};
+ unsigned char r[APALIS_TK1_K20_MAX_BULK];
unsigned char *p = val;
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
@@ -70,29 +74,83 @@ static int apalis_tk1_k20_spi_read(void *context, const void *reg,
.tx_buf = w,
.rx_buf = r,
.len = 3,
+ .cs_change = 0,
+ .delay_usecs = 0,
};
+#ifdef CONFIG_EXPERIMENTAL_K20_HSMODE
+ struct spi_transfer ts[APALIS_TK1_K20_MAX_BULK / APALIS_TK1_K20_MAX_MSG];
+ int i = 0;
+#endif
struct spi_message m;
int ret;
-
spi->mode = SPI_MODE_1;
- if (val_size != 1 || reg_size != 1)
- return -ENOTSUPP;
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- ret = spi_sync(spi, &m);
- spi_message_init(&m);
- t.len = 3;
- spi_message_add_tail(&t, &m);
- ret = spi_sync(spi, &m);
+ if (reg_size != 1)
+ return -ENOTSUPP;
- dev_vdbg(dev, "Apalis TK1 K20 MFD SPI read reg 0x%X: 0x%X 0x%X\n",
- *((unsigned char *) reg), r[1], r[2]);
+ if (val_size == 1) {
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(spi, &m);
+ /* no need to reinit the message*/
+ t.len = 2;
+ /* just use the same transfer */
+ ret = spi_sync(spi, &m);
+#ifdef CONFIG_EXPERIMENTAL_K20_HSMODE
+ } else if ((val_size > 1) && (val_size < APALIS_TK1_K20_MAX_BULK)) {
+ spi_message_init(&m);
+ w[0] = APALIS_TK1_K20_BULK_READ_INST;
+ t.len = 3;
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(spi, &m);
+
+ if (val_size == 2) {
+ ret = spi_sync(spi, &m);
+ } else {
+ spi_message_init(&m);
+ for (i = 0; (4 * i) < (val_size - 2); i++) {
+ ts[i].tx_buf = NULL;
+ ts[i].rx_buf = &r[2 + (4 * i)];
+ ts[i].len = (val_size - 2 - (4 * i) >= 4) ?
+ 4 : (val_size - 2 - (4 * i));
+ ts[i].cs_change = 0;
+ ts[i].delay_usecs = 2;
+
+ spi_message_add_tail(&ts[i], &m);
+ }
+ ret = spi_sync(spi, &m);
+ }
+#endif
+ } else {
+ return -ENOTSUPP;
+ }
- if (r[1] == TK1_K20_SENTINEL)
- memcpy(p, &r[2], 1);
- else
+ if (r[0] == TK1_K20_SENTINEL)
+ memcpy(p, &r[1], val_size);
+ else {
+ if (r[0] != TK1_K20_INVAL) {
+ dev_err(dev, "K20 FIFO Bug!");
+ /* we've probably hit the K20 FIFO bug, try to resync */
+ spi_message_init(&m);
+ w[0] = APALIS_TK1_K20_READ_INST;
+ w[1] = 0x01;
+ w[2] = APALIS_TK1_K20_RET_REQ;
+ w[3] = 0x00;
+ t.tx_buf = w;
+ t.len = 4;
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(spi, &m);
+ spi_message_init(&m);
+ t.len = val_size + 1;
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(spi, &m);
+ if (r[0] == TK1_K20_SENTINEL) {
+ memcpy(p, &r[1], val_size);
+ return ret;
+ }
+ }
return -EIO;
+ }
return ret;
}
@@ -102,21 +160,63 @@ static int apalis_tk1_k20_spi_write(void *context, const void *data,
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
- uint8_t out_data[3];
+ uint8_t out_data[APALIS_TK1_K20_MAX_BULK];
+ int ret;
+#ifdef CONFIG_EXPERIMENTAL_K20_HSMODE
+ struct spi_device *spi = to_spi_device(dev);
+ struct spi_transfer t = {
+ .tx_buf = out_data,
+ .rx_buf = NULL,
+ .cs_change = 0,
+ .delay_usecs = 2,
+ };
+ struct spi_transfer ts[APALIS_TK1_K20_MAX_BULK / APALIS_TK1_K20_MAX_MSG];
+ int i = 0;
+#endif
spi->mode = SPI_MODE_1;
- if (count != 2) {
- dev_err(dev, "Apalis TK1 K20 MFD write count = %d\n", count);
+ if (count == 2) {
+ out_data[0] = APALIS_TK1_K20_WRITE_INST;
+ out_data[1] = ((uint8_t *)data)[0];
+ out_data[2] = ((uint8_t *)data)[1];
+ ret = spi_write(spi, out_data, 3);
+#ifdef CONFIG_EXPERIMENTAL_K20_HSMODE
+ } else if (count == 2) {
+ out_data[0] = APALIS_TK1_K20_BULK_WRITE_INST;
+ out_data[1] = count - 1;
+ memcpy(&out_data[2], data, count);
+ ret = spi_write(spi, out_data, 4);
+ } else if ( (count > 2 ) && (count < APALIS_TK1_K20_MAX_BULK)) {
+
+ spi_message_init(&m);
+ out_data[0] = APALIS_TK1_K20_BULK_WRITE_INST;
+ out_data[1] = count -1;
+ memcpy(&out_data[2], data, count);
+ t.tx_buf = out_data;
+ t.len = 4;
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(spi, &m);
+
+ spi_message_init(&m);
+ for (i = 1; (4 * i) < (count - 2); i++) {
+ ts[i].tx_buf = &out_data[i * 4];
+ ts[i].len = (count - 2 - (4 * i) >= 4) ?
+ 4 : (count - 2 - (4 * i));
+ ts[i].cs_change = 0;
+ ts[i].delay_usecs = 2;
+
+ spi_message_add_tail(&ts[i], &m);
+ }
+ ret = spi_sync(spi, &m);
+ }
+#endif
+ } else {
+ dev_err(dev, "Apalis TK1 K20 MFD Invalid write count = %d\n",
+ count);
return -ENOTSUPP;
}
- out_data[0] = APALIS_TK1_K20_WRITE_INST;
- out_data[1] = ((uint8_t *)data)[0];
- out_data[2] = ((uint8_t *)data)[1];
- dev_vdbg(dev, "Apalis TK1 K20 MFD SPI write 0x%X 0x%X\n", out_data[1],
- out_data[2]);
-
- return spi_write(spi, out_data, 3);
+ return ret;
}
static struct regmap_bus regmap_apalis_tk1_k20_bus = {
@@ -287,9 +387,10 @@ int apalis_tk1_k20_irq_free(struct apalis_tk1_k20_regmap *apalis_tk1_k20,
}
EXPORT_SYMBOL(apalis_tk1_k20_irq_free);
-static int apalis_tk1_k20_add_subdevice_pdata(
+
+static int apalis_tk1_k20_add_subdevice_pdata_id(
struct apalis_tk1_k20_regmap *apalis_tk1_k20, const char *name,
- void *pdata, size_t pdata_size)
+ void *pdata, size_t pdata_size, int id)
{
struct mfd_cell cell = {
.platform_data = pdata,
@@ -300,10 +401,18 @@ static int apalis_tk1_k20_add_subdevice_pdata(
if (!cell.name)
return -ENOMEM;
- return mfd_add_devices(apalis_tk1_k20->dev, -1, &cell, 1, NULL, 0,
+ return mfd_add_devices(apalis_tk1_k20->dev, id, &cell, 1, NULL, 0,
regmap_irq_get_domain(apalis_tk1_k20->irq_data));
}
+static int apalis_tk1_k20_add_subdevice_pdata(
+ struct apalis_tk1_k20_regmap *apalis_tk1_k20, const char *name,
+ void *pdata, size_t pdata_size)
+{
+ return apalis_tk1_k20_add_subdevice_pdata_id(apalis_tk1_k20, name,
+ pdata, pdata_size, -1);
+}
+
static int apalis_tk1_k20_add_subdevice(
struct apalis_tk1_k20_regmap *apalis_tk1_k20, const char *name)
{
@@ -316,7 +425,7 @@ static void apalis_tk1_k20_reset_chip(
{
udelay(10);
gpio_set_value(apalis_tk1_k20->reset_gpio, 0);
- udelay(10);
+ msleep(10);
gpio_set_value(apalis_tk1_k20->reset_gpio, 1);
udelay(10);
}
@@ -339,6 +448,7 @@ static int apalis_tk1_k20_read_ezport(
int ret;
spi->mode = SPI_MODE_0;
+
if (count > APALIS_TK1_K20_EZP_MAX_DATA)
return -ENOSPC;
@@ -392,6 +502,7 @@ static int apalis_tk1_k20_write_ezport(
int ret;
spi->mode = SPI_MODE_0;
+
if (count > APALIS_TK1_K20_EZP_MAX_DATA)
return -ENOSPC;
@@ -460,8 +571,9 @@ static int apalis_tk1_k20_enter_ezport(
uint8_t status = 0x00;
uint8_t buffer;
gpio_set_value(apalis_tk1_k20->ezpcs_gpio, 0);
+ msleep(10);
apalis_tk1_k20_reset_chip(apalis_tk1_k20);
- msleep(30);
+ msleep(10);
gpio_set_value(apalis_tk1_k20->ezpcs_gpio, 1);
if (apalis_tk1_k20_read_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_RDSR,
0, 1, &buffer) < 0)
@@ -534,17 +646,17 @@ static int apalis_tk1_k20_flash_chip_ezport(
APALIS_TK1_K20_EZP_SP, i,
transfer_size, fw_chunk) < 0)
goto bad;
-
+ udelay(2000);
if (apalis_tk1_k20_read_ezport(apalis_tk1_k20,
APALIS_TK1_K20_EZP_RDSR, 0, 1, &buffer) < 0)
goto bad;
j = 0;
while (buffer & APALIS_TK1_K20_EZP_STA_WIP) {
- udelay(100);
+ msleep(10);
if ((apalis_tk1_k20_read_ezport(apalis_tk1_k20,
APALIS_TK1_K20_EZP_RDSR, 0, 1,
- &buffer) < 0) || (j > 1000))
+ &buffer) < 0) || (j > 10000))
goto bad;
j++;
}
@@ -617,12 +729,6 @@ static int apalis_tk1_k20_probe_gpios_dt(
gpio_request(apalis_tk1_k20->ezpcs_gpio, "apalis-tk1-k20-ezpcs");
gpio_direction_output(apalis_tk1_k20->ezpcs_gpio, 1);
- apalis_tk1_k20->appcs_gpio = of_get_named_gpio(np, "spi-cs-gpio", 0);
- if (apalis_tk1_k20->appcs_gpio < 0)
- return apalis_tk1_k20->appcs_gpio;
- gpio_request(apalis_tk1_k20->appcs_gpio, "apalis-tk1-k20-appcs");
- gpio_direction_output(apalis_tk1_k20->appcs_gpio, 1);
-
apalis_tk1_k20->int2_gpio = of_get_named_gpio(np, "int2-gpio", 0);
if (apalis_tk1_k20->int2_gpio < 0)
return apalis_tk1_k20->int2_gpio;
@@ -659,7 +765,6 @@ int apalis_tk1_k20_dev_init(struct device *dev)
if (pdata) {
apalis_tk1_k20->ezpcs_gpio = pdata->ezpcs_gpio;
apalis_tk1_k20->reset_gpio = pdata->reset_gpio;
- apalis_tk1_k20->appcs_gpio = pdata->appcs_gpio;
apalis_tk1_k20->int2_gpio = pdata->int2_gpio;
} else {
dev_err(dev, "Error claiming GPIOs\n");
@@ -667,10 +772,8 @@ int apalis_tk1_k20_dev_init(struct device *dev)
goto bad;
}
}
-
apalis_tk1_k20_reset_chip(apalis_tk1_k20);
- msleep(100);
- gpio_set_value(apalis_tk1_k20->appcs_gpio, 0);
+ msleep(10);
ret = apalis_tk1_k20_reg_read(apalis_tk1_k20, APALIS_TK1_K20_REVREG,
&revision);
@@ -686,11 +789,24 @@ int apalis_tk1_k20_dev_init(struct device *dev)
goto bad;
}
- if (fw_entry->size == 1)
- erase_only = 1;
+ if ((fw_entry == NULL) && (revision != APALIS_TK1_K20_FW_VER)) {
+ dev_err(apalis_tk1_k20->dev,
+ "Unsupported firmware version %d.%d and no local"
+ " firmware file available.\n",
+ (revision & 0xF0 >> 8),
+ (revision & 0x0F));
+ ret = -ENOTSUPP;
+ goto bad;
+ }
+
+ if (fw_entry != NULL) {
+ if (fw_entry->size == 1)
+ erase_only = 1;
+ }
if ((apalis_tk1_k20_get_fw_revision() != APALIS_TK1_K20_FW_VER) &&
- (revision != APALIS_TK1_K20_FW_VER) && !erase_only) {
+ (revision != APALIS_TK1_K20_FW_VER) && !erase_only &&
+ (fw_entry != NULL)) {
dev_err(apalis_tk1_k20->dev,
"Unsupported firmware version in both the device as well"
" as the local firmware file.\n");
@@ -700,7 +816,8 @@ int apalis_tk1_k20_dev_init(struct device *dev)
}
if ((revision != APALIS_TK1_K20_FW_VER) && !erase_only &&
- (!apalis_tk1_k20_fw_ezport_status())) {
+ (!apalis_tk1_k20_fw_ezport_status()) &&
+ (fw_entry != NULL)) {
dev_err(apalis_tk1_k20->dev,
"Unsupported firmware version in the device and the "
"local firmware file disables the EZ Port.\n");
@@ -709,8 +826,14 @@ int apalis_tk1_k20_dev_init(struct device *dev)
goto bad;
}
- if ((revision != APALIS_TK1_K20_FW_VER) || erase_only) {
- if (apalis_tk1_k20_enter_ezport(apalis_tk1_k20) < 0) {
+ if (((revision != APALIS_TK1_K20_FW_VER) || erase_only) &&
+ (fw_entry != NULL)) {
+ i = 0;
+ while (apalis_tk1_k20_enter_ezport(apalis_tk1_k20) < 0
+ && i++ < 5) {
+ msleep(50);
+ }
+ if (i >= 5) {
dev_err(apalis_tk1_k20->dev,
"Problem entering EZ port mode.\n");
release_firmware(fw_entry);
@@ -739,12 +862,12 @@ int apalis_tk1_k20_dev_init(struct device *dev)
goto bad;
}
}
- release_firmware(fw_entry);
+ if (fw_entry != NULL)
+ release_firmware(fw_entry);
- gpio_set_value(apalis_tk1_k20->appcs_gpio, 1);
+ msleep(10);
apalis_tk1_k20_reset_chip(apalis_tk1_k20);
- msleep(100);
- gpio_set_value(apalis_tk1_k20->appcs_gpio, 0);
+ msleep(10);
ret = apalis_tk1_k20_reg_read(apalis_tk1_k20, APALIS_TK1_K20_REVREG,
&revision);
@@ -758,7 +881,7 @@ int apalis_tk1_k20_dev_init(struct device *dev)
if (revision != APALIS_TK1_K20_FW_VER) {
dev_err(apalis_tk1_k20->dev,
"Unsupported firmware version %d.%d.\n",
- ((revision & 0xF0) >> 8), (revision & 0x0F));
+ ((revision & 0xF0) >> 4), (revision & 0x0F));
ret = -ENOTSUPP;
goto bad;
}
@@ -803,6 +926,7 @@ int apalis_tk1_k20_dev_init(struct device *dev)
&pdata->adc, sizeof(pdata->adc));
if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_CAN) {
+ /* We have 2 CAN devices inside K20 */
pdata->can0.id = 0;
pdata->can1.id = 1;
apalis_tk1_k20_add_subdevice_pdata(apalis_tk1_k20,
@@ -826,8 +950,13 @@ int apalis_tk1_k20_dev_init(struct device *dev)
"apalis-tk1-k20-adc");
if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_CAN) {
- apalis_tk1_k20_add_subdevice(apalis_tk1_k20,
- "apalis-tk1-k20-can");
+ /* We have 2 CAN devices inside K20 */
+ apalis_tk1_k20_add_subdevice_pdata_id(apalis_tk1_k20,
+ "apalis-tk1-k20-can",
+ NULL, 0, 0);
+ apalis_tk1_k20_add_subdevice_pdata_id(apalis_tk1_k20,
+ "apalis-tk1-k20-can",
+ NULL, 0, 1);
}
if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_GPIO)
@@ -845,8 +974,6 @@ bad:
gpio_free(apalis_tk1_k20->ezpcs_gpio);
if (apalis_tk1_k20->reset_gpio >= 0)
gpio_free(apalis_tk1_k20->reset_gpio);
- if (apalis_tk1_k20->appcs_gpio >= 0)
- gpio_free(apalis_tk1_k20->appcs_gpio);
if (apalis_tk1_k20->int2_gpio >= 0)
gpio_free(apalis_tk1_k20->int2_gpio);
@@ -870,7 +997,7 @@ static int apalis_tk1_k20_spi_probe(struct spi_device *spi)
apalis_tk1_k20->irq = spi->irq;
- spi->max_speed_hz = (spi->max_speed_hz > APALIS_TK1_K20_MAX_SPI_SPEED) ?
+ spi->max_speed_hz = (spi->max_speed_hz >= APALIS_TK1_K20_MAX_SPI_SPEED) ?
APALIS_TK1_K20_MAX_SPI_SPEED : spi->max_speed_hz;
ret = spi_setup(spi);
@@ -898,8 +1025,6 @@ static int apalis_tk1_k20_spi_remove(struct spi_device *spi)
gpio_free(apalis_tk1_k20->ezpcs_gpio);
if (apalis_tk1_k20->reset_gpio >= 0)
gpio_free(apalis_tk1_k20->reset_gpio);
- if (apalis_tk1_k20->appcs_gpio >= 0)
- gpio_free(apalis_tk1_k20->appcs_gpio);
if (apalis_tk1_k20->int2_gpio >= 0)
gpio_free(apalis_tk1_k20->int2_gpio);