summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/boot/dts/tegra124-apalis-eval.dts17
-rw-r--r--arch/arm/configs/apalis-tk1_defconfig1
-rw-r--r--drivers/input/touchscreen/apalis-tk1-k20_ts.c6
-rw-r--r--drivers/mfd/apalis-tk1-k20-ezp.h2
-rw-r--r--drivers/mfd/apalis-tk1-k20.c255
-rw-r--r--drivers/net/can/Kconfig6
-rw-r--r--drivers/net/can/Makefile1
-rw-r--r--drivers/net/can/apalis-tk1-k20-can.c792
-rw-r--r--include/linux/mfd/apalis-tk1-k20.h90
9 files changed, 1061 insertions, 109 deletions
diff --git a/arch/arm/boot/dts/tegra124-apalis-eval.dts b/arch/arm/boot/dts/tegra124-apalis-eval.dts
index 9e32f873f949..a3f3f019a059 100644
--- a/arch/arm/boot/dts/tegra124-apalis-eval.dts
+++ b/arch/arm/boot/dts/tegra124-apalis-eval.dts
@@ -134,28 +134,33 @@
/* SPI2: MCU SPI */
spi@7000d600 {
status = "okay";
- spi-max-frequency = <25000000>;
+ spi-max-frequency = <6000000>;
k20mcu: apalis-tk1-k20@1 {
compatible = "toradex,apalis-tk1-k20";
reg = <1>;
- spi-max-frequency = <10000000>;
+ spi-max-frequency = <6000000>;
interrupt-parent =<&gpio>;
interrupts = <TEGRA_GPIO(K, 2) IRQ_TYPE_EDGE_FALLING>;
rst-gpio = <&gpio TEGRA_GPIO(BB, 6) GPIO_ACTIVE_HIGH>;
/* GPIO based CS used to enter K20 EzPort mode */
ezport-cs-gpio = <&gpio TEGRA_GPIO(W, 2) GPIO_ACTIVE_HIGH>;
- /* SPI CS under GPIO controll due to K20 quirks */
- spi-cs-gpio = <&gpio TEGRA_GPIO(X, 6) GPIO_ACTIVE_HIGH>;
/* extra INT lines between K20 and TK1 */
int2-gpio = <&gpio TEGRA_GPIO(J, 2) GPIO_ACTIVE_HIGH>;
int3-gpio = <&gpio TEGRA_GPIO(I, 5) GPIO_ACTIVE_HIGH>;
int4-gpio = <&gpio TEGRA_GPIO(J, 0) GPIO_ACTIVE_HIGH>;
- toradex,apalis-tk1-k20-uses-gpio;
toradex,apalis-tk1-k20-uses-adc;
+ toradex,apalis-tk1-k20-uses-can;
+ toradex,apalis-tk1-k20-uses-gpio;
toradex,apalis-tk1-k20-uses-tsc;
+
+ controller-data {
+ nvidia,enable-hw-based-cs;
+ nvidia,cs-setup-clk-count = <12>;
+ nvidia,cs-hold-clk-count = <12>;
+ };
};
/* spidev on K20 bus, can be used with custom firmware for userspace
@@ -163,7 +168,7 @@
spidev2: spidev@2 {
compatible = "spidev";
reg = <2>;
- spi-max-frequency = <3500000>;
+ spi-max-frequency = <2000000>;
};
};
diff --git a/arch/arm/configs/apalis-tk1_defconfig b/arch/arm/configs/apalis-tk1_defconfig
index bf53b49ea7fe..2a6c23a37d52 100644
--- a/arch/arm/configs/apalis-tk1_defconfig
+++ b/arch/arm/configs/apalis-tk1_defconfig
@@ -147,6 +147,7 @@ CONFIG_NET_ACT_GACT=y
CONFIG_NET_ACT_MIRRED=y
CONFIG_CAN=y
CONFIG_CAN_MCP251X=y
+CONFIG_CAN_APALIS_TK1_K20=m
CONFIG_BT=m
CONFIG_BT_RFCOMM=m
CONFIG_BT_BNEP=m
diff --git a/drivers/input/touchscreen/apalis-tk1-k20_ts.c b/drivers/input/touchscreen/apalis-tk1-k20_ts.c
index 43cb25fa8fc9..38e4a3003c8d 100644
--- a/drivers/input/touchscreen/apalis-tk1-k20_ts.c
+++ b/drivers/input/touchscreen/apalis-tk1-k20_ts.c
@@ -37,7 +37,7 @@ static irqreturn_t apalis_tk1_k20_ts_handler(int irq, void *data)
* Kick off reading coordinates. Note that if work happens already
* be queued for future execution (it rearms itself) it will not
* be rescheduled for immediate execution here. However the rearm
- * delay is HZ / 50 which is acceptable.
+ * delay is HZ / 25 which is acceptable.
*/
queue_delayed_work(priv->workq, &priv->work, 0);
@@ -167,8 +167,8 @@ static int __init apalis_tk1_k20_ts_probe(struct platform_device *pdev)
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- input_set_abs_params(idev, ABS_X, 0, 0xffff, 0, 0);
- input_set_abs_params(idev, ABS_Y, 0, 0xffff, 0, 0);
+ input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0);
input_set_abs_params(idev, ABS_PRESSURE, 0, 0xfff, 0, 0);
idev->open = apalis_tk1_k20_ts_open;
diff --git a/drivers/mfd/apalis-tk1-k20-ezp.h b/drivers/mfd/apalis-tk1-k20-ezp.h
index 4a7262c85d8f..b2c65e4b60fd 100644
--- a/drivers/mfd/apalis-tk1-k20-ezp.h
+++ b/drivers/mfd/apalis-tk1-k20-ezp.h
@@ -36,7 +36,7 @@
#define APALIS_TK1_K20_EZP_STA_WEF BIT(6)
#define APALIS_TK1_K20_EZP_STA_FS BIT(7)
-#define APALIS_TK1_K20_EZP_MAX_SPEED 3180000
+#define APALIS_TK1_K20_EZP_MAX_SPEED 2000000
#define APALIS_TK1_K20_EZP_MAX_DATA 32
#define APALIS_TK1_K20_EZP_WRITE_SIZE 32
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);
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index e456b70933c2..c8287d04a51b 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -83,6 +83,12 @@ config CAN_MCP251X
---help---
Driver for the Microchip MCP251x SPI CAN controllers.
+config CAN_APALIS_TK1_K20
+ tristate "Apalis TK1 K20 CAN controllers"
+ depends on MFD_APALIS_TK1_K20
+ ---help---
+ Driver for the Apalis TK1 K20 CAN controllers.
+
config CAN_BFIN
depends on BF534 || BF536 || BF537 || BF538 || BF539 || BF54x
tristate "Analog Devices Blackfin on-chip CAN"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index c7440392adbb..940782b20494 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_CAN_CC770) += cc770/
obj-$(CONFIG_CAN_AT91) += at91_can.o
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
+obj-$(CONFIG_CAN_APALIS_TK1_K20) += apalis-tk1-k20-can.o
obj-$(CONFIG_CAN_BFIN) += bfin_can.o
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
diff --git a/drivers/net/can/apalis-tk1-k20-can.c b/drivers/net/can/apalis-tk1-k20-can.c
new file mode 100644
index 000000000000..cff3dca7bf20
--- /dev/null
+++ b/drivers/net/can/apalis-tk1-k20-can.c
@@ -0,0 +1,792 @@
+/*
+ * CAN bus driver for Apalis TK1 K20 CAN Controller over MFD device
+ * based on MCP251x CAN driver
+ */
+
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/can/led.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/apalis-tk1-k20.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#define GET_BYTE(val, byte) \
+ (((val) >> ((byte) * 8)) & 0xff)
+#define SET_BYTE(val, byte) \
+ (((val) & 0xff) << ((byte) * 8))
+
+/* Buffer size required for the largest transfer (i.e., reading a
+ * frame)
+ */
+#define CAN_FRAME_MAX_LEN 8
+#define CAN_HEADER_MAX_LEN 5
+#define CAN_TRANSFER_BUF_LEN (CAN_HEADER_MAX_LEN + CAN_FRAME_MAX_LEN)
+#define CAN_FRAME_MAX_BITS 128
+#define CAN_MAX_CONTINOUS_READ 8
+
+#define MB_DLC_OFF 0
+#define MB_EID_OFF 1
+#define MB_RTR_SHIFT 4
+#define MB_IDE_SHIFT 5
+#define MB_DLC_MASK 0xF
+#define MB_EID_LEN 4
+
+#define CANCTRL_MODMASK 0x03
+#define CANCTRL_INTMASK 0x38
+#define CANCTRL_INTEN BIT(2)
+#define CANINTF_RX BIT(3)
+#define CANINTF_TX BIT(4)
+#define CANINTF_ERR BIT(5)
+
+#define EFLG_EWARN 0x01
+#define EFLG_RXWAR 0x02
+#define EFLG_TXWAR 0x04
+#define EFLG_RXEP 0x08
+#define EFLG_TXEP 0x10
+#define EFLG_TXBO 0x20
+#define EFLG_RXOVR 0x40
+
+#define TX_ECHO_SKB_MAX 1
+
+#define K20_CAN_MAX_ID 1
+
+#define DEVICE_NAME "apalis-tk1-k20-can"
+
+static const struct can_bittiming_const apalis_tk1_k20_can_bittiming_const = {
+ .name = "tk1-k20-can",
+ .tseg1_min = 3,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct apalis_tk1_k20_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct apalis_tk1_k20_regmap *apalis_tk1_k20;
+ struct apalis_tk1_k20_can_platform_data *pdata;
+
+ struct sk_buff *tx_skb;
+ int tx_len;
+
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct work_struct restart_work;
+ struct mutex apalis_tk1_k20_can_lock;
+
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_RESTART 4
+ int restart_tx;
+};
+
+static void apalis_tk1_k20_can_clean(struct net_device *net)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+
+ if (priv->tx_skb || priv->tx_len)
+ net->stats.tx_errors++;
+ if (priv->tx_skb)
+ dev_kfree_skb(priv->tx_skb);
+ if (priv->tx_len)
+ can_free_echo_skb(priv->net, 0);
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+}
+
+static void apalis_tk1_k20_can_hw_tx_frame(struct net_device *net, u8 *buf,
+ int len, int tx_buf_idx)
+{
+ /* TODO: Implement multiple TX buffer handling */
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+
+ apalis_tk1_k20_lock(priv->apalis_tk1_k20);
+
+ apalis_tk1_k20_reg_write_bulk(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN_OUT_BUF
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id), buf,
+ len);
+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20);
+}
+
+static void apalis_tk1_k20_can_hw_tx(struct net_device *net,
+ struct can_frame *frame, int tx_buf_idx)
+{
+ u8 buf[CAN_TRANSFER_BUF_LEN];
+
+ buf[MB_DLC_OFF] = frame->can_dlc;
+ memcpy(buf + MB_EID_OFF, &frame->can_id, MB_EID_LEN);
+ memcpy(buf + CAN_HEADER_MAX_LEN, frame->data, frame->can_dlc);
+
+ apalis_tk1_k20_can_hw_tx_frame(net, buf, frame->can_dlc, tx_buf_idx);
+
+}
+
+static void apalis_tk1_k20_can_hw_rx_frame(struct net_device *net, u8 *buf,
+ int buf_idx)
+{
+ /* TODO: Implement multiple RX buffer handling */
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+
+ apalis_tk1_k20_lock(priv->apalis_tk1_k20);
+
+ apalis_tk1_k20_reg_read_bulk(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN_OUT_BUF
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id), buf,
+ CAN_TRANSFER_BUF_LEN);
+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20);
+}
+
+static void apalis_tk1_k20_can_hw_rx(struct net_device *net, int buf_idx)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u8 buf[CAN_TRANSFER_BUF_LEN];
+
+ skb = alloc_can_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&net->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return;
+ }
+
+ apalis_tk1_k20_can_hw_rx_frame(net, buf, buf_idx);
+ memcpy(&frame->can_id, buf + MB_EID_OFF, MB_EID_LEN);
+ /* Data length */
+ frame->can_dlc = get_can_dlc(buf[MB_DLC_OFF]);
+ memcpy(frame->data, buf + CAN_HEADER_MAX_LEN, frame->can_dlc);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->can_dlc;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+}
+
+static netdev_tx_t apalis_tk1_k20_can_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+
+ if (priv->tx_skb || priv->tx_len) {
+ dev_warn(&net->dev, "hard_xmit called while TX busy\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (can_dropped_invalid_skb(net, skb))
+ return NETDEV_TX_OK;
+
+ netif_stop_queue(net);
+ priv->tx_skb = skb;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int apalis_tk1_k20_can_do_set_mode(struct net_device *net,
+ enum can_mode mode)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ apalis_tk1_k20_can_clean(net);
+ /* We have to delay work since I/O may sleep */
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ priv->restart_tx = 1;
+ if (priv->can.restart_ms == 0)
+ priv->after_suspend = AFTER_SUSPEND_RESTART;
+ queue_work(priv->wq, &priv->restart_work);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int apalis_tk1_k20_can_set_normal_mode(struct net_device *net)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+
+ apalis_tk1_k20_lock(priv->apalis_tk1_k20);
+ /* Enable interrupts */
+ apalis_tk1_k20_reg_rmw(priv->apalis_tk1_k20, APALIS_TK1_K20_CANREG
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ CANCTRL_INTEN, CANCTRL_INTEN);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ /* Put device into loopback mode */
+ apalis_tk1_k20_reg_rmw(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CANREG
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ CANCTRL_MODMASK, CAN_CTRLMODE_LOOPBACK);
+ } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
+ /* Put device into listen-only mode */
+ apalis_tk1_k20_reg_rmw(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CANREG
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ CANCTRL_MODMASK, CAN_CTRLMODE_LISTENONLY);
+ } else {
+ /* Put device into normal mode */
+ apalis_tk1_k20_reg_rmw(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CANREG
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ CANCTRL_MODMASK, 0x00);
+ }
+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20);
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ return 0;
+}
+
+static int apalis_tk1_k20_can_do_set_bittiming(struct net_device *net)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+
+ apalis_tk1_k20_lock(priv->apalis_tk1_k20);
+ apalis_tk1_k20_reg_write(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN_BAUD_REG
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ bt->bitrate);
+ apalis_tk1_k20_reg_write(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN_BIT_1
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ ((bt->sjw & 0x3) << 6) | ((bt->phase_seg2 & 0x7) << 3)
+ | (bt->phase_seg2 & 0x7));
+ apalis_tk1_k20_reg_write(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN_BIT_2
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ (bt->prop_seg & 0x7));
+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20);
+ dev_vdbg(priv->apalis_tk1_k20->dev, "Setting CAN%d bit timing"
+ "RJW = %d, PSEG1 = %d, PSEG2 = %d, PROPSEG = %d \n",
+ priv->pdata->id, bt->sjw, bt->phase_seg1,
+ bt->phase_seg2, bt->prop_seg);
+ dev_vdbg(priv->apalis_tk1_k20->dev, "Setting CAN%d bit timing"
+ "bitrate = %d \n", priv->pdata->id, bt->bitrate);
+
+ return 0;
+}
+
+static int apalis_tk1_k20_can_setup(struct net_device *net,
+ struct apalis_tk1_k20_priv *priv)
+{
+ apalis_tk1_k20_can_do_set_bittiming(net);
+
+ return 0;
+}
+
+static int apalis_tk1_k20_can_hw_reset(struct net_device *net)
+{
+ return 0;
+}
+
+static void apalis_tk1_k20_can_open_clean(struct net_device *net)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+ struct apalis_tk1_k20_can_platform_data *pdata = priv->pdata;
+
+ if (pdata->id == 0)
+ apalis_tk1_k20_irq_free(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN0_IRQ, priv);
+ if (pdata->id == 1)
+ apalis_tk1_k20_irq_free(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN1_IRQ, priv);
+ close_candev(net);
+}
+
+static int apalis_tk1_k20_can_stop(struct net_device *net)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+ struct apalis_tk1_k20_can_platform_data *pdata = priv->pdata;
+
+ close_candev(net);
+
+ priv->force_quit = 1;
+ if (pdata->id == 0)
+ apalis_tk1_k20_irq_free(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN0_IRQ, priv);
+ if (pdata->id == 1)
+ apalis_tk1_k20_irq_free(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN1_IRQ, priv);
+ destroy_workqueue(priv->wq);
+ priv->wq = NULL;
+
+ mutex_lock(&priv->apalis_tk1_k20_can_lock);
+ if (pdata->id == 0)
+ apalis_tk1_k20_irq_mask(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN0_IRQ);
+ if (pdata->id == 1)
+ apalis_tk1_k20_irq_mask(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN1_IRQ);
+ /* Disable and clear pending interrupts */
+ priv->can.state = CAN_STATE_STOPPED;
+
+ mutex_unlock(&priv->apalis_tk1_k20_can_lock);
+
+ can_led_event(net, CAN_LED_EVENT_STOP);
+
+ return 0;
+}
+
+static void apalis_tk1_k20_can_error_skb(struct net_device *net, int can_id,
+ int data1)
+{
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ skb = alloc_can_err_skb(net, &frame);
+ if (skb) {
+ frame->can_id |= can_id;
+ frame->data[1] = data1;
+ netif_rx_ni(skb);
+ } else {
+ netdev_err(net, "cannot allocate error skb\n");
+ }
+}
+
+static void apalis_tk1_k20_can_tx_work_handler(struct work_struct *ws)
+{
+ struct apalis_tk1_k20_priv *priv = container_of(ws,
+ struct apalis_tk1_k20_priv, tx_work);
+ struct net_device *net = priv->net;
+ struct can_frame *frame;
+
+ mutex_lock(&priv->apalis_tk1_k20_can_lock);
+ if (priv->tx_skb) {
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ apalis_tk1_k20_can_clean(net);
+ } else {
+ frame = (struct can_frame *)priv->tx_skb->data;
+
+ if (frame->can_dlc > CAN_FRAME_MAX_LEN)
+ frame->can_dlc = CAN_FRAME_MAX_LEN;
+ apalis_tk1_k20_can_hw_tx(net, frame, 0);
+ priv->tx_len = 1 + frame->can_dlc;
+ can_put_echo_skb(priv->tx_skb, net, 0);
+ priv->tx_skb = NULL;
+ }
+ }
+ mutex_unlock(&priv->apalis_tk1_k20_can_lock);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int apalis_tk1_k20_can_suspend(struct device *dev)
+{
+ struct apalis_tk1_k20_priv *priv = dev_get_drvdata(dev);
+ struct apalis_tk1_k20_can_platform_data *pdata = priv->pdata;
+
+ priv->force_quit = 1;
+
+ mutex_lock(&priv->apalis_tk1_k20_can_lock);
+ if (pdata->id == 0)
+ apalis_tk1_k20_irq_mask(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN0_IRQ);
+ if (pdata->id == 1)
+ apalis_tk1_k20_irq_mask(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN1_IRQ);
+ /* Disable interrupts */
+
+ mutex_unlock(&priv->apalis_tk1_k20_can_lock);
+ /*
+ * Note: at this point neither IST nor workqueues are running.
+ * open/stop cannot be called anyway so locking is not needed
+ */
+ if (netif_running(priv->net)) {
+ netif_device_detach(priv->net);
+
+ priv->after_suspend = AFTER_SUSPEND_UP;
+ } else {
+ priv->after_suspend = AFTER_SUSPEND_DOWN;
+ }
+
+ return 0;
+}
+
+static int apalis_tk1_k20_can_resume(struct device *dev)
+{
+ struct apalis_tk1_k20_priv *priv = dev_get_drvdata(dev);
+ struct apalis_tk1_k20_can_platform_data *pdata = priv->pdata;
+
+ if (priv->after_suspend & AFTER_SUSPEND_UP)
+ queue_work(priv->wq, &priv->restart_work);
+ else
+ priv->after_suspend = 0;
+
+ priv->force_quit = 0;
+ mutex_lock(&priv->apalis_tk1_k20_can_lock);
+ if (pdata->id == 0)
+ apalis_tk1_k20_irq_unmask(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN0_IRQ);
+ if (pdata->id == 1)
+ apalis_tk1_k20_irq_unmask(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN1_IRQ);
+ /* Enable interrupts */
+ priv->can.state = CAN_STATE_STOPPED;
+
+ mutex_unlock(&priv->apalis_tk1_k20_can_lock);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(apalis_tk1_k20_can_pm_ops, apalis_tk1_k20_can_suspend,
+ apalis_tk1_k20_can_resume);
+#endif
+
+static void apalis_tk1_k20_can_restart_work_handler(struct work_struct *ws)
+{
+ struct apalis_tk1_k20_priv *priv = container_of(ws,
+ struct apalis_tk1_k20_priv, restart_work);
+ struct net_device *net = priv->net;
+
+ mutex_lock(&priv->apalis_tk1_k20_can_lock);
+ if (priv->after_suspend) {
+ mdelay(10);
+ apalis_tk1_k20_can_hw_reset(net);
+ apalis_tk1_k20_can_setup(net, priv);
+ if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
+ apalis_tk1_k20_can_set_normal_mode(net);
+ } else if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ netif_device_attach(net);
+ apalis_tk1_k20_can_clean(net);
+ apalis_tk1_k20_can_set_normal_mode(net);
+ netif_wake_queue(net);
+ }
+ priv->after_suspend = 0;
+ priv->force_quit = 0;
+ }
+
+ if (priv->restart_tx) {
+ priv->restart_tx = 0;
+ apalis_tk1_k20_can_clean(net);
+ netif_wake_queue(net);
+ apalis_tk1_k20_can_error_skb(net, CAN_ERR_RESTARTED, 0);
+ }
+ mutex_unlock(&priv->apalis_tk1_k20_can_lock);
+}
+
+static irqreturn_t apalis_tk1_k20_can_ist(int irq, void *dev_id)
+{
+ struct apalis_tk1_k20_priv *priv = dev_id;
+ struct net_device *net = priv->net;
+ int max_continous_read = CAN_MAX_CONTINOUS_READ;
+
+ mutex_lock(&priv->apalis_tk1_k20_can_lock);
+ while (!priv->force_quit && max_continous_read) {
+ enum can_state new_state;
+ int ret;
+ u32 intf, eflag;
+ u8 clear_intf = 0;
+ int can_id = 0, data1 = 0;
+ apalis_tk1_k20_lock(priv->apalis_tk1_k20);
+ ret = apalis_tk1_k20_reg_read(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CANREG
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ &intf);
+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20);
+
+ if (ret) {
+ dev_err(&net->dev, "Communication error\n");
+ break;
+ }
+
+ max_continous_read--;
+
+ intf &= CANCTRL_INTMASK;
+ /* receive */
+ if (intf & CANINTF_RX)
+ apalis_tk1_k20_can_hw_rx(net, 0);
+
+
+ /* any error or TX interrupt we need to clear? */
+ if (intf & (CANINTF_ERR | CANINTF_TX))
+ clear_intf |= intf & (CANINTF_ERR | CANINTF_TX);
+ apalis_tk1_k20_lock(priv->apalis_tk1_k20);
+ if (clear_intf)
+ ret = apalis_tk1_k20_reg_rmw(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CANREG
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(
+ priv->pdata->id),
+ CANCTRL_INTMASK, 0x00);
+ if (ret) {
+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20);
+ dev_err(&net->dev, "Communication error\n");
+ break;
+ }
+ ret = apalis_tk1_k20_reg_read(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CANERR
+ + APALIS_TK1_K20_CAN_DEV_OFFSET(priv->pdata->id),
+ &eflag);
+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20);
+ if (ret) {
+ dev_err(&net->dev, "Communication error\n");
+ break;
+ }
+ /* Update can state */
+ if (intf & CANINTF_ERR) {
+ if (eflag & EFLG_TXBO) {
+ new_state = CAN_STATE_BUS_OFF;
+ can_id |= CAN_ERR_BUSOFF;
+ } else if (eflag & EFLG_TXEP) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_TX_PASSIVE;
+ } else if (eflag & EFLG_RXEP) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_RX_PASSIVE;
+ } else if (eflag & EFLG_TXWAR) {
+ new_state = CAN_STATE_ERROR_WARNING;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_TX_WARNING;
+ } else if (eflag & EFLG_RXWAR) {
+ new_state = CAN_STATE_ERROR_WARNING;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_RX_WARNING;
+ } else {
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+ }
+
+ /* Update can state statistics */
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_ACTIVE:
+ if (new_state >= CAN_STATE_ERROR_WARNING
+ && new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_warning++;
+ case CAN_STATE_ERROR_WARNING: /* fallthrough */
+ if (new_state >= CAN_STATE_ERROR_PASSIVE
+ && new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_passive++;
+ break;
+ default:
+ break;
+ }
+ priv->can.state = new_state;
+
+ if (intf & CANINTF_ERR) {
+ /* Handle overflow counters */
+ if (eflag & EFLG_RXOVR) {
+ if (eflag & EFLG_RXOVR) {
+ net->stats.rx_over_errors++;
+ net->stats.rx_errors++;
+ }
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ apalis_tk1_k20_can_error_skb(net, can_id, data1);
+ }
+
+ if (priv->can.state == CAN_STATE_BUS_OFF
+ && priv->can.restart_ms == 0) {
+ priv->force_quit = 1;
+ can_bus_off(net);
+ break;
+ }
+
+ if (intf == 0)
+ break;
+
+ if (intf & CANINTF_TX) {
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += priv->tx_len - 1;
+ can_led_event(net, CAN_LED_EVENT_TX);
+ if (priv->tx_len) {
+ can_get_echo_skb(net, 0);
+ priv->tx_len = 0;
+ }
+ netif_wake_queue(net);
+ }
+
+ }
+ mutex_unlock(&priv->apalis_tk1_k20_can_lock);
+ return IRQ_HANDLED;
+}
+
+static int apalis_tk1_k20_can_open(struct net_device *net)
+{
+ struct apalis_tk1_k20_priv *priv = netdev_priv(net);
+ struct apalis_tk1_k20_can_platform_data *pdata = priv->pdata;
+ int ret;
+
+ ret = open_candev(net);
+ if (ret) {
+ dev_err(&net->dev, "unable to initialize CAN\n");
+ return ret;
+ }
+
+ mutex_lock(&priv->apalis_tk1_k20_can_lock);
+
+ priv->force_quit = 0;
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+
+ apalis_tk1_k20_lock(priv->apalis_tk1_k20);
+
+ if (pdata->id == 0)
+ ret = apalis_tk1_k20_irq_request(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN0_IRQ, apalis_tk1_k20_can_ist,
+ DEVICE_NAME, priv);
+ if (pdata->id == 1)
+ ret = apalis_tk1_k20_irq_request(priv->apalis_tk1_k20,
+ APALIS_TK1_K20_CAN1_IRQ, apalis_tk1_k20_can_ist,
+ DEVICE_NAME, priv);
+
+ if (ret) {
+ dev_err(&net->dev, "failed to acquire IRQ\n");
+ close_candev(net);
+ goto open_unlock;
+ }
+
+ priv->wq = create_freezable_workqueue("apalis_tk1_k20_wq");
+ INIT_WORK(&priv->tx_work, apalis_tk1_k20_can_tx_work_handler);
+ INIT_WORK(&priv->restart_work, apalis_tk1_k20_can_restart_work_handler);
+
+ ret = apalis_tk1_k20_can_hw_reset(net);
+ if (ret) {
+ apalis_tk1_k20_can_open_clean(net);
+ goto open_unlock;
+ }
+ ret = apalis_tk1_k20_can_setup(net, priv);
+ if (ret) {
+ apalis_tk1_k20_can_open_clean(net);
+ goto open_unlock;
+ }
+ ret = apalis_tk1_k20_can_set_normal_mode(net);
+ if (ret) {
+ apalis_tk1_k20_can_open_clean(net);
+ goto open_unlock;
+ }
+
+ can_led_event(net, CAN_LED_EVENT_OPEN);
+
+ netif_wake_queue(net);
+
+open_unlock:
+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20);
+ mutex_unlock(&priv->apalis_tk1_k20_can_lock);
+ return ret;
+}
+
+static const struct net_device_ops apalis_tk1_k20_netdev_ops = {
+ .ndo_open = apalis_tk1_k20_can_open,
+ .ndo_stop = apalis_tk1_k20_can_stop,
+ .ndo_start_xmit = apalis_tk1_k20_can_hard_start_xmit,
+};
+
+static int apalis_tk1_k20_can_probe(struct platform_device *pdev)
+{
+ struct net_device *net;
+ struct apalis_tk1_k20_priv *priv;
+ struct apalis_tk1_k20_can_platform_data *pdata = pdev->dev.platform_data;
+ int ret = -ENODEV;
+
+ if (!pdata) {
+ pdata = kmalloc(sizeof(struct apalis_tk1_k20_can_platform_data),
+ GFP_KERNEL);
+ if (pdev->id == -1)
+ pdata->id = 0 ;
+ if (pdev->id >= 0 && pdev->id <= K20_CAN_MAX_ID)
+ pdata->id = pdev->id;
+ else
+ goto error_out;
+ }
+
+ if (pdata->id > K20_CAN_MAX_ID)
+ goto error_out;
+ /* Allocate can/net device */
+ net = alloc_candev(sizeof(struct apalis_tk1_k20_priv), TX_ECHO_SKB_MAX);
+ if (!net) {
+ ret = -ENOMEM;
+ goto error_out;
+ }
+
+ net->netdev_ops = &apalis_tk1_k20_netdev_ops;
+ net->flags |= IFF_ECHO;
+
+ priv = netdev_priv(net);
+ priv->can.bittiming_const = &apalis_tk1_k20_can_bittiming_const;
+ priv->can.do_set_mode = apalis_tk1_k20_can_do_set_mode;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY;
+ priv->net = net;
+ priv->pdata = pdata;
+
+ mutex_init(&priv->apalis_tk1_k20_can_lock);
+
+ SET_NETDEV_DEV(net, &pdev->dev);
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = register_candev(net);
+ if (ret)
+ goto error_probe;
+
+ devm_can_led_init(net);
+
+ dev_info(&pdev->dev, "probed %d\n", pdev->id);
+
+ return ret;
+
+error_probe:
+ free_candev(net);
+error_out:
+ return ret;
+}
+
+static int apalis_tk1_k20_can_remove(struct platform_device *pdev)
+{
+ struct apalis_tk1_k20_priv *priv = platform_get_drvdata(pdev);
+ struct net_device *net = priv->net;
+
+ unregister_candev(net);
+ free_candev(net);
+
+ return 0;
+}
+
+static const struct platform_device_id apalis_tk1_k20_can_idtable[] = {
+ {.name ="apalis-tk1-k20-can", },
+ { /* sentinel */}
+};
+
+MODULE_DEVICE_TABLE(platform, apalis_tk1_k20_can_idtable);
+
+static struct platform_driver apalis_tk1_k20_can_driver = {
+ .id_table = apalis_tk1_k20_can_idtable,
+ .remove = __exit_p(apalis_tk1_k20_can_remove),
+ .driver = {
+ .name = DEVICE_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
+ .pm = &apalis_tk1_k20_can_pm_ops,
+#endif
+ },
+};
+
+module_platform_driver_probe(apalis_tk1_k20_can_driver,
+ &apalis_tk1_k20_can_probe);
+
+MODULE_DESCRIPTION("CAN driver for K20 MCU on Apalis TK1");
+MODULE_AUTHOR("Dominik Sliwa <dominik.sliwa@toradex.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/apalis-tk1-k20.h b/include/linux/mfd/apalis-tk1-k20.h
index 29229a09d8c4..48ae8030c8f5 100644
--- a/include/linux/mfd/apalis-tk1-k20.h
+++ b/include/linux/mfd/apalis-tk1-k20.h
@@ -24,56 +24,78 @@
#define APALIS_TK1_K20_MAX_BULK (64)
/* General registers*/
-#define APALIS_TK1_K20_STAREG 0x00 /* General status register RO */
+#define APALIS_TK1_K20_STAREG 0x00 /* general status register RO */
#define APALIS_TK1_K20_REVREG 0x01 /* FW revision register RO*/
#define APALIS_TK1_K20_IRQREG 0x02 /* IRQ status RW(write of 1 will reset the bit) */
-#define APALIS_TK1_K20_CTRREG 0x03 /* General control register RW */
+#define APALIS_TK1_K20_CTRREG 0x03 /* general control register RW */
#define APALIS_TK1_K20_MSQREG 0x04 /* IRQ mask register RW */
-/* CAN Registers */
-#define APALIS_TK1_K20_CANREG 0x10 /* CAN control & status register RW */
-#define APALIS_TK1_K20_CAN_BAUD_REG 0x11 /* CAN Baud set register RW */
-#define APALIS_TK1_K20_CAN_IN_BUF_CNT 0x12 /* CAN IN BUF Received Data Count RO */
-#define APALIS_TK1_K20_CAN_IN_BUF 0x13 /* CAN IN BUF RO */
-#define APALIS_TK1_K20_CAN_OUT_BUF_CNT 0x14 /* CAN OUT BUF Data Count WO, must be written before bulk write to APALIS_TK1_K20_CAN0_OUT_BUF_CNT */
-#define APALIS_TK1_K20_CAN_OUT_FIF0 0x15 /* CAN OUT BUF WO */
+/* 0x05-0x0F Reserved */
-#define APALIS_TK1_K20_CAN_DEV_OFFSET(x) (x ? 0:0x10)
+/* CAN Registers */
+#define APALIS_TK1_K20_CANREG 0x10 /* CAN0 control & status register RW */
+#define APALIS_TK1_K20_CANERR 0x11 /* CAN0 error register RW */
+#define APALIS_TK1_K20_CAN_BAUD_REG 0x12 /* CAN0 baud set register RW */
+#define APALIS_TK1_K20_CAN_BIT_1 0x13 /* CAN0 bit timing register 1 RW */
+#define APALIS_TK1_K20_CAN_BIT_2 0x14 /* CAN0 bit timing register 2 RW */
+#define APALIS_TK1_K20_CAN_IN_BUF_CNT 0x15 /* CAN0 IN received data count RO */
+#define APALIS_TK1_K20_CAN_IN_BUF 0x16 /* CAN0 IN RO */
+/* buffer size is 13 bytes */
+#define APALIS_TK1_K20_CAN_IN_BUF_END 0x22 /* CAN0 IN RO */
+#define APALIS_TK1_K20_CAN_OUT_BUF_CNT 0x23 /* CAN0 OUT data Count WO */
+#define APALIS_TK1_K20_CAN_OUT_BUF 0x26 /* CAN0 OUT WO */
+/* buffer size is 13 bytes */
+#define APALIS_TK1_K20_CAN_OUT_BUF_END (APALIS_TK1_K20_CAN_OUT_BUF + 13 - 1)/* CAN OUT BUF END */
+#define APALIS_TK1_K20_CAN_DEV_OFFSET(x) (x ? 0x30 : 0)
+
+/* 0x33-0x3F Reserved */
+/* 0x40-0x62 CAN1 registers same layout as CAN0*/
+/* 0x63-0x6F Reserved */
/* ADC Registers */
-#define APALIS_TK1_K20_ADCREG 0x30 /* ADC control & status register RW */
-#define APALIS_TK1_K20_ADC_CH0L 0x31 /* ADC Channel 0 LSB RO */
-#define APALIS_TK1_K20_ADC_CH0H 0x32 /* ADC Channel 0 MSB RO */
-#define APALIS_TK1_K20_ADC_CH1L 0x33 /* ADC Channel 1 LSB RO */
-#define APALIS_TK1_K20_ADC_CH1H 0x34 /* ADC Channel 1 MSB RO */
-#define APALIS_TK1_K20_ADC_CH2L 0x35 /* ADC Channel 2 LSB RO */
-#define APALIS_TK1_K20_ADC_CH2H 0x36 /* ADC Channel 2 MSB RO */
-#define APALIS_TK1_K20_ADC_CH3L 0x37 /* ADC Channel 3 LSB RO */
-#define APALIS_TK1_K20_ADC_CH3H 0x38 /* ADC Channel 3 MSB RO */
+#define APALIS_TK1_K20_ADCREG 0x70 /* ADC control & status register RW */
+#define APALIS_TK1_K20_ADC_CH0L 0x71 /* ADC Channel 0 LSB RO */
+#define APALIS_TK1_K20_ADC_CH0H 0x72 /* ADC Channel 0 MSB RO */
+#define APALIS_TK1_K20_ADC_CH1L 0x73 /* ADC Channel 1 LSB RO */
+#define APALIS_TK1_K20_ADC_CH1H 0x74 /* ADC Channel 1 MSB RO */
+#define APALIS_TK1_K20_ADC_CH2L 0x75 /* ADC Channel 2 LSB RO */
+#define APALIS_TK1_K20_ADC_CH2H 0x76 /* ADC Channel 2 MSB RO */
+#define APALIS_TK1_K20_ADC_CH3L 0x77 /* ADC Channel 3 LSB RO */
+#define APALIS_TK1_K20_ADC_CH3H 0x78 /* ADC Channel 3 MSB RO */
/* Bulk read of LSB register can be use to read entire 16-bit in one command */
+/* Bulk read of APALIS_TK1_K20_ADC_CH0L register can be use to read all
+ * ADC channels in one command */
+
+/* 0x79-0x7F reserved */
/* TSC Register */
-#define APALIS_TK1_K20_TSCREG 0x40 /* TSC control & status register RW */
-#define APALIS_TK1_K20_TSC_XML 0x41 /* TSC X- data LSB RO */
-#define APALIS_TK1_K20_TSC_XMH 0x42 /* TSC X- data MSB RO */
-#define APALIS_TK1_K20_TSC_XPL 0x43 /* TSC X+ data LSB RO */
-#define APALIS_TK1_K20_TSC_XPH 0x44 /* TSC X+ data MSB RO */
-#define APALIS_TK1_K20_TSC_YML 0x45 /* TSC Y- data LSB RO */
-#define APALIS_TK1_K20_TSC_YMH 0x46 /* TSC Y- data MSB RO */
-#define APALIS_TK1_K20_TSC_YPL 0x47 /* TSC Y+ data LSB RO */
-#define APALIS_TK1_K20_TSC_YPH 0x48 /* TSC Y+ data MSB RO */
+#define APALIS_TK1_K20_TSCREG 0x80 /* TSC control & status register RW */
+#define APALIS_TK1_K20_TSC_XML 0x81 /* TSC X- data LSB RO */
+#define APALIS_TK1_K20_TSC_XMH 0x82 /* TSC X- data MSB RO */
+#define APALIS_TK1_K20_TSC_XPL 0x83 /* TSC X+ data LSB RO */
+#define APALIS_TK1_K20_TSC_XPH 0x84 /* TSC X+ data MSB RO */
+#define APALIS_TK1_K20_TSC_YML 0x85 /* TSC Y- data LSB RO */
+#define APALIS_TK1_K20_TSC_YMH 0x86 /* TSC Y- data MSB RO */
+#define APALIS_TK1_K20_TSC_YPL 0x87 /* TSC Y+ data LSB RO */
+#define APALIS_TK1_K20_TSC_YPH 0x88 /* TSC Y+ data MSB RO */
/* Bulk read of LSB register can be use to read entire 16-bit in one command */
#define APALIS_TK1_K20_TSC_ENA BIT(0)
#define APALIS_TK1_K20_TSC_ENA_MASK BIT(0)
+/* 0x89-0x8F Reserved */
+
/* GPIO Registers */
-#define APALIS_TK1_K20_GPIOREG 0x50 /* GPIO control & status register RW */
-#define APALIS_TK1_K20_GPIO_NO 0x51 /* currently configured GPIO RW */
-#define APALIS_TK1_K20_GPIO_STA 0x52 /* Status register for the APALIS_TK1_K20_GPIO_NO GPIO RW */
+#define APALIS_TK1_K20_GPIOREG 0x90 /* GPIO control & status register RW */
+#define APALIS_TK1_K20_GPIO_NO 0x91 /* currently configured GPIO RW */
+#define APALIS_TK1_K20_GPIO_STA 0x92 /* Status register for the APALIS_TK1_K20_GPIO_NO GPIO RW */
/* MSB | 0 ... 0 | VALUE | Output-1 / Input-0 | LSB */
#define APALIS_TK1_K20_GPIO_STA_OE BIT(0)
#define APALIS_TK1_K20_GPIO_STA_VAL BIT(1)
+/* 0x93-0xFD Reserved */
+#define APALIS_TK1_K20_RET_REQ 0xFE
+/* 0xFF Reserved */
+
/* Interrupt flags */
#define APALIS_TK1_K20_GEN_IRQ 0
#define APALIS_TK1_K20_CAN0_IRQ 1
@@ -82,10 +104,10 @@
#define APALIS_TK1_K20_TSC_IRQ 4
#define APALIS_TK1_K20_GPIO_IRQ 5
-#define APALIS_TK1_K20_FW_VER 0x05
+#define APALIS_TK1_K20_FW_VER 0x09
#define FW_MINOR (APALIS_TK1_K20_FW_VER & 0x0F)
-#define FW_MAJOR ((APALIS_TK1_K20_FW_VER & 0xF0) >> 8)
+#define FW_MAJOR ((APALIS_TK1_K20_FW_VER & 0xF0) >> 4)
#define TK1_K20_SENTINEL 0x55
#define TK1_K20_INVAL 0xAA
@@ -94,7 +116,7 @@
#define APALIS_TK1_K20_IRQ_REG_CNT 1
#define APALIS_TK1_K20_IRQ_PER_REG 8
-#define APALIS_TK1_K20_MAX_SPI_SPEED 10000000
+#define APALIS_TK1_K20_MAX_SPI_SPEED 6000000
struct apalis_tk1_k20_regmap {
struct regmap *regmap;