diff options
Diffstat (limited to 'recipes-kernel/linux/linux-toradex-mainline-4.14/0012-apalis-tk1-support-for-k20-mfd.patch')
-rw-r--r-- | recipes-kernel/linux/linux-toradex-mainline-4.14/0012-apalis-tk1-support-for-k20-mfd.patch | 3185 |
1 files changed, 0 insertions, 3185 deletions
diff --git a/recipes-kernel/linux/linux-toradex-mainline-4.14/0012-apalis-tk1-support-for-k20-mfd.patch b/recipes-kernel/linux/linux-toradex-mainline-4.14/0012-apalis-tk1-support-for-k20-mfd.patch deleted file mode 100644 index 3042ca4..0000000 --- a/recipes-kernel/linux/linux-toradex-mainline-4.14/0012-apalis-tk1-support-for-k20-mfd.patch +++ /dev/null @@ -1,3185 +0,0 @@ -From 55301c43044ec23e14509a5515a868b7ae995aa1 Mon Sep 17 00:00:00 2001 -Message-Id: <55301c43044ec23e14509a5515a868b7ae995aa1.1531317141.git.marcel.ziswiler@toradex.com> -In-Reply-To: <6654e1bd342708a683daf47e7558455f709a3e7e.1531317141.git.marcel.ziswiler@toradex.com> -References: <6654e1bd342708a683daf47e7558455f709a3e7e.1531317141.git.marcel.ziswiler@toradex.com> -From: Dominik Sliwa <dominik.sliwa@toradex.com> -Date: Wed, 18 Apr 2018 12:22:26 +0200 -Subject: [PATCH 12/33] apalis-tk1: support for k20 mfd - -Signed-off-by: Dominik Sliwa <dominik.sliwa@toradex.com> -Acked-by: Marcel Ziswiler <marcel.ziswiler@toradex.com> ---- - arch/arm/boot/dts/tegra124-apalis-v1.2.dtsi | 28 + - arch/arm/configs/tegra_defconfig | 7 + - drivers/gpio/Kconfig | 6 + - drivers/gpio/Makefile | 1 + - drivers/gpio/gpio-apalis-tk1-k20.c | 221 +++++ - drivers/iio/adc/Kconfig | 6 + - drivers/iio/adc/Makefile | 1 + - drivers/iio/adc/apalis-tk1-k20_adc.c | 200 +++++ - drivers/input/touchscreen/Kconfig | 10 + - drivers/input/touchscreen/Makefile | 1 + - drivers/input/touchscreen/apalis-tk1-k20_ts.c | 234 ++++++ - drivers/mfd/Kconfig | 14 + - drivers/mfd/Makefile | 1 + - drivers/mfd/apalis-tk1-k20-ezp.h | 47 ++ - drivers/mfd/apalis-tk1-k20.c | 1062 +++++++++++++++++++++++++ - drivers/net/can/Kconfig | 6 + - drivers/net/can/Makefile | 1 + - drivers/net/can/apalis-tk1-k20-can.c | 817 +++++++++++++++++++ - drivers/spi/spi-tegra114.c | 7 +- - include/linux/mfd/apalis-tk1-k20-api.h | 123 +++ - include/linux/mfd/apalis-tk1-k20.h | 114 +++ - 21 files changed, 2901 insertions(+), 6 deletions(-) - create mode 100644 drivers/gpio/gpio-apalis-tk1-k20.c - create mode 100644 drivers/iio/adc/apalis-tk1-k20_adc.c - create mode 100644 drivers/input/touchscreen/apalis-tk1-k20_ts.c - create mode 100644 drivers/mfd/apalis-tk1-k20-ezp.h - create mode 100644 drivers/mfd/apalis-tk1-k20.c - create mode 100644 drivers/net/can/apalis-tk1-k20-can.c - create mode 100644 include/linux/mfd/apalis-tk1-k20-api.h - create mode 100644 include/linux/mfd/apalis-tk1-k20.h - -diff --git a/arch/arm/boot/dts/tegra124-apalis-v1.2.dtsi b/arch/arm/boot/dts/tegra124-apalis-v1.2.dtsi -index bb67edb016c5..93cf49c10954 100644 ---- a/arch/arm/boot/dts/tegra124-apalis-v1.2.dtsi -+++ b/arch/arm/boot/dts/tegra124-apalis-v1.2.dtsi -@@ -1756,6 +1756,34 @@ - spi@7000d600 { - status = "okay"; - spi-max-frequency = <25000000>; -+ -+ k20mcu: apalis-tk1-k20@1 { -+ compatible = "toradex,apalis-tk1-k20"; -+ reg = <1>; -+ spi-max-frequency = <6120000>; -+ interrupt-parent =<&gpio>; -+ interrupts = <TEGRA_GPIO(K, 2) (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)>, -+ <TEGRA_GPIO(I, 5) (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)>, /* INT3 CAN0 */ -+ <TEGRA_GPIO(J, 0) (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)>; /* INT4 CAN1 */ -+ 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>; -+ /* extra INT lines between K20 and TK1 */ -+ int2-gpio = <&gpio TEGRA_GPIO(J, 2) GPIO_ACTIVE_HIGH>; -+ -+ 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 = <1>; -+ nvidia,cs-hold-clk-count = <1>; -+ }; -+ }; -+ - }; - - pmc@7000e400 { -diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig -index cfe997c617fc..e8c9bdafa1b9 100644 ---- a/arch/arm/configs/tegra_defconfig -+++ b/arch/arm/configs/tegra_defconfig -@@ -63,6 +63,7 @@ CONFIG_IPV6_MIP6=y - CONFIG_IPV6_TUNNEL=y - CONFIG_IPV6_MULTIPLE_TABLES=y - CONFIG_CAN=y -+CONFIG_CAN_APALIS_TK1_K20=m - CONFIG_CAN_MCP251X=y - CONFIG_BT=y - CONFIG_BT_RFCOMM=y -@@ -126,6 +127,7 @@ CONFIG_KEYBOARD_TEGRA=y - CONFIG_KEYBOARD_CROS_EC=y - CONFIG_MOUSE_PS2_ELANTECH=y - CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_APALIS_TK1_K20=m - CONFIG_TOUCHSCREEN_ATMEL_MXT=y - CONFIG_TOUCHSCREEN_WM97XX=y - # CONFIG_TOUCHSCREEN_WM9705 is not set -@@ -155,6 +157,7 @@ CONFIG_GPIO_PCA953X_IRQ=y - CONFIG_GPIO_PALMAS=y - CONFIG_GPIO_TPS6586X=y - CONFIG_GPIO_TPS65910=y -+CONFIG_GPIO_APALIS_TK1_K20=m - CONFIG_POWER_RESET=y - CONFIG_POWER_RESET_AS3722=y - CONFIG_POWER_RESET_GPIO=y -@@ -165,6 +168,8 @@ CONFIG_SENSORS_LM95245=y - CONFIG_WATCHDOG=y - CONFIG_TEGRA_WATCHDOG=y - CONFIG_MFD_AS3722=y -+CONFIG_MFD_APALIS_TK1_K20=m -+CONFIG_APALIS_TK1_K20_EZP=y - CONFIG_MFD_CROS_EC=y - CONFIG_MFD_CROS_EC_SPI=y - CONFIG_MFD_MAX8907=y -@@ -276,6 +281,7 @@ CONFIG_ARCH_TEGRA_114_SOC=y - CONFIG_ARCH_TEGRA_124_SOC=y - CONFIG_MEMORY=y - CONFIG_IIO=y -+CONFIG_APALIS_TK1_K20_ADC=m - CONFIG_MPU3050_I2C=y - CONFIG_SENSORS_ISL29018=y - CONFIG_SENSORS_ISL29028=y -@@ -309,6 +315,7 @@ CONFIG_ROOT_NFS=y - CONFIG_NLS_CODEPAGE_437=y - CONFIG_NLS_ISO8859_1=y - CONFIG_PRINTK_TIME=y -+CONFIG_DYNAMIC_DEBUG=y - CONFIG_DEBUG_INFO=y - CONFIG_MAGIC_SYSRQ=y - CONFIG_DEBUG_SLAB=y -diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig -index 3f80f167ed56..c5b006c10d37 100644 ---- a/drivers/gpio/Kconfig -+++ b/drivers/gpio/Kconfig -@@ -1248,6 +1248,12 @@ endmenu - menu "SPI GPIO expanders" - depends on SPI_MASTER - -+config GPIO_APALIS_TK1_K20 -+ tristate "GPIOs of K20 MCU on Apalis TK1" -+ depends on MFD_APALIS_TK1_K20 -+ help -+ This enables support for GPIOs of K20 MCU found on Apalis TK1. -+ - config GPIO_74X164 - tristate "74x164 serial-in/parallel-out 8-bits shift register" - depends on OF_GPIO -diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile -index 8a2dfba3b231..8ea42ecdf87e 100644 ---- a/drivers/gpio/Makefile -+++ b/drivers/gpio/Makefile -@@ -29,6 +29,7 @@ obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o - obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o - obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o - obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o -+obj-$(CONFIG_GPIO_APALIS_TK1_K20) += gpio-apalis-tk1-k20.o - obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o - obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o - obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o -diff --git a/drivers/gpio/gpio-apalis-tk1-k20.c b/drivers/gpio/gpio-apalis-tk1-k20.c -new file mode 100644 -index 000000000000..7b44f93df85b ---- /dev/null -+++ b/drivers/gpio/gpio-apalis-tk1-k20.c -@@ -0,0 +1,221 @@ -+/* -+ * Copyright 2016-2017 Toradex AG -+ * Dominik Sliwa <dominik.sliwa@toradex.com> -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. -+ */ -+ -+#include <linux/mfd/apalis-tk1-k20.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/gpio.h> -+#include <linux/slab.h> -+#include <linux/platform_device.h> -+ -+struct apalis_tk1_k20_gpio { -+ struct gpio_chip chip; -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20; -+}; -+ -+static int apalis_tk1_k20_gpio_input(struct gpio_chip *chip, -+ unsigned int offset) -+{ -+ struct apalis_tk1_k20_gpio *gpio = container_of(chip, -+ struct apalis_tk1_k20_gpio, chip); -+ -+ apalis_tk1_k20_lock(gpio->apalis_tk1_k20); -+ -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_NO, -+ offset); -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_STA, -+ 0); -+ -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+ -+ return 0; -+} -+ -+static int apalis_tk1_k20_gpio_output(struct gpio_chip *chip, -+ unsigned int offset, -+ int value) -+{ -+ struct apalis_tk1_k20_gpio *gpio = container_of(chip, -+ struct apalis_tk1_k20_gpio, chip); -+ int status; -+ -+ apalis_tk1_k20_lock(gpio->apalis_tk1_k20); -+ -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_NO, -+ offset); -+ status = APALIS_TK1_K20_GPIO_STA_OE; -+ status += value ? APALIS_TK1_K20_GPIO_STA_VAL : 0; -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_STA, -+ status); -+ -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+ -+ return 0; -+} -+ -+static int apalis_tk1_k20_gpio_get(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct apalis_tk1_k20_gpio *gpio = container_of(chip, -+ struct apalis_tk1_k20_gpio, chip); -+ int value; -+ -+ apalis_tk1_k20_lock(gpio->apalis_tk1_k20); -+ -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_NO, -+ offset); -+ if (apalis_tk1_k20_reg_read(gpio->apalis_tk1_k20, -+ APALIS_TK1_K20_GPIO_STA, &value) < 0) { -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+ return -ENODEV; -+ } -+ value &= APALIS_TK1_K20_GPIO_STA_VAL; -+ -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+ -+ return value ? 1 : 0; -+} -+ -+static int apalis_tk1_k20_gpio_request(struct gpio_chip *chip, -+ unsigned int offset) -+{ -+ struct apalis_tk1_k20_gpio *gpio = container_of(chip, -+ struct apalis_tk1_k20_gpio, chip); -+ int status = 0; -+ -+ dev_dbg(gpio->apalis_tk1_k20->dev, "APALIS TK1 K20 GPIO %d\n", -+ offset); -+ -+ apalis_tk1_k20_lock(gpio->apalis_tk1_k20); -+ -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_NO, -+ offset); -+ if (apalis_tk1_k20_reg_read(gpio->apalis_tk1_k20, -+ APALIS_TK1_K20_GPIO_NO, &status) < 0) { -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+ return -ENODEV; -+ } -+ -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+ -+ return status; -+} -+ -+static void apalis_tk1_k20_gpio_free(struct gpio_chip *chip, -+ unsigned int offset) -+{ -+ struct apalis_tk1_k20_gpio *gpio = -+ container_of(chip, struct apalis_tk1_k20_gpio, chip); -+ -+ apalis_tk1_k20_lock(gpio->apalis_tk1_k20); -+ -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_NO, -+ offset); -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, -+ APALIS_TK1_K20_GPIO_STA, 0); -+ -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+} -+ -+ -+static void apalis_tk1_k20_gpio_set(struct gpio_chip *chip, unsigned int offset, -+ int value) -+{ -+ struct apalis_tk1_k20_gpio *gpio = container_of(chip, -+ struct apalis_tk1_k20_gpio, chip); -+ int status; -+ -+ apalis_tk1_k20_lock(gpio->apalis_tk1_k20); -+ -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_NO, -+ offset); -+ if (apalis_tk1_k20_reg_read(gpio->apalis_tk1_k20, -+ APALIS_TK1_K20_GPIO_STA, &status) < 0) { -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+ return; -+ } -+ -+ status &= ~APALIS_TK1_K20_GPIO_STA_VAL; -+ status += value ? APALIS_TK1_K20_GPIO_STA_VAL : 0; -+ apalis_tk1_k20_reg_write(gpio->apalis_tk1_k20, APALIS_TK1_K20_GPIO_STA, -+ status); -+ -+ apalis_tk1_k20_unlock(gpio->apalis_tk1_k20); -+} -+ -+static int apalis_tk1_k20_gpio_probe(struct platform_device *pdev) -+{ -+ struct apalis_tk1_k20_gpio *priv; -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20; -+ int status; -+ -+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ apalis_tk1_k20 = dev_get_drvdata(pdev->dev.parent); -+ if (!apalis_tk1_k20) -+ return -ENODEV; -+ priv->apalis_tk1_k20 = apalis_tk1_k20; -+ -+ platform_set_drvdata(pdev, priv); -+ -+ apalis_tk1_k20_lock(apalis_tk1_k20); -+ -+ /* TBD: some code */ -+ -+ apalis_tk1_k20_unlock(apalis_tk1_k20); -+ -+ priv->chip.base = -1; -+ priv->chip.can_sleep = 1; -+ priv->chip.parent = &pdev->dev; -+ priv->chip.owner = THIS_MODULE; -+ priv->chip.get = apalis_tk1_k20_gpio_get; -+ priv->chip.set = apalis_tk1_k20_gpio_set; -+ priv->chip.direction_input = apalis_tk1_k20_gpio_input; -+ priv->chip.direction_output = apalis_tk1_k20_gpio_output; -+ priv->chip.request = apalis_tk1_k20_gpio_request; -+ priv->chip.free = apalis_tk1_k20_gpio_free; -+ /* TODO: include as a define somewhere */ -+ priv->chip.ngpio = 160; -+ -+ status = gpiochip_add(&priv->chip); -+ -+ return status; -+} -+ -+static int apalis_tk1_k20_gpio_remove(struct platform_device *pdev) -+{ -+ struct apalis_tk1_k20_gpio *priv = platform_get_drvdata(pdev); -+ -+ gpiochip_remove(&priv->chip); -+ return 0; -+} -+ -+static const struct platform_device_id apalis_tk1_k20_gpio_idtable[] = { -+ { -+ .name = "apalis-tk1-k20-gpio", -+ }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(platform, apalis_tk1_k20_gpio_idtable); -+ -+static struct platform_driver apalis_tk1_k20_gpio_driver = { -+ .id_table = apalis_tk1_k20_gpio_idtable, -+ .remove = apalis_tk1_k20_gpio_remove, -+ .probe = apalis_tk1_k20_gpio_probe, -+ .driver = { -+ .name = "apalis-tk1-k20-gpio", -+ }, -+}; -+ -+module_platform_driver(apalis_tk1_k20_gpio_driver); -+ -+MODULE_DESCRIPTION("GPIO driver for K20 MCU on Apalis TK1"); -+MODULE_AUTHOR("Dominik Sliwa <dominik.sliwa@toradex.com>"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig -index 369a2c632e46..d4ec5fad1467 100644 ---- a/drivers/iio/adc/Kconfig -+++ b/drivers/iio/adc/Kconfig -@@ -116,6 +116,12 @@ config AD7923 - To compile this driver as a module, choose M here: the - module will be called ad7923. - -+config APALIS_TK1_K20_ADC -+ tristate "ADCs of K20 MCU on Apalis TK1" -+ depends on MFD_APALIS_TK1_K20 -+ help -+ This enables support for ADCs of K20 MCU found on Apalis TK1. -+ - config AD799X - tristate "Analog Devices AD799x ADC driver" - depends on I2C -diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile -index 9572c1090f35..5f761384b305 100644 ---- a/drivers/iio/adc/Makefile -+++ b/drivers/iio/adc/Makefile -@@ -16,6 +16,7 @@ obj-$(CONFIG_AD7793) += ad7793.o - obj-$(CONFIG_AD7887) += ad7887.o - obj-$(CONFIG_AD799X) += ad799x.o - obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o -+obj-$(CONFIG_APALIS_TK1_K20_ADC) += apalis-tk1-k20_adc.o - obj-$(CONFIG_AT91_ADC) += at91_adc.o - obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o - obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o -diff --git a/drivers/iio/adc/apalis-tk1-k20_adc.c b/drivers/iio/adc/apalis-tk1-k20_adc.c -new file mode 100644 -index 000000000000..b45e51df5c81 ---- /dev/null -+++ b/drivers/iio/adc/apalis-tk1-k20_adc.c -@@ -0,0 +1,200 @@ -+/* -+ * Copyright 2016-2017 Toradex AG -+ * Dominik Sliwa <dominik.sliwa@toradex.com> -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. -+ */ -+ -+#include <linux/mfd/apalis-tk1-k20.h> -+#include <linux/delay.h> -+#include <linux/iio/iio.h> -+#include <linux/iio/driver.h> -+#include <linux/iio/machine.h> -+#include <linux/module.h> -+#include <linux/mutex.h> -+#include <linux/platform_device.h> -+#include <linux/slab.h> -+ -+struct apalis_tk1_k20_adc { -+ struct iio_dev chip; -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20; -+}; -+ -+static int apalis_tk1_k20_get_adc_result(struct apalis_tk1_k20_adc *adc, int id, -+ int *val) -+{ -+ uint8_t adc_read[2]; -+ int adc_register; -+ -+ switch (id) { -+ case APALIS_TK1_K20_ADC1: -+ adc_register = APALIS_TK1_K20_ADC_CH0L; -+ break; -+ case APALIS_TK1_K20_ADC2: -+ adc_register = APALIS_TK1_K20_ADC_CH1L; -+ break; -+ case APALIS_TK1_K20_ADC3: -+ adc_register = APALIS_TK1_K20_ADC_CH2L; -+ break; -+ case APALIS_TK1_K20_ADC4: -+ adc_register = APALIS_TK1_K20_ADC_CH3L; -+ break; -+ default: -+ return -ENODEV; -+ } -+ -+ apalis_tk1_k20_lock(adc->apalis_tk1_k20); -+ -+ if (apalis_tk1_k20_reg_read_bulk(adc->apalis_tk1_k20, adc_register, -+ adc_read, 2) < 0) { -+ apalis_tk1_k20_unlock(adc->apalis_tk1_k20); -+ return -EIO; -+ } -+ *val = (adc_read[1] << 8) + adc_read[0]; -+ -+ apalis_tk1_k20_unlock(adc->apalis_tk1_k20); -+ -+ return 0; -+} -+ -+static int apalis_tk1_k20_adc_read_raw(struct iio_dev *indio_dev, -+ struct iio_chan_spec const *chan, -+ int *val, int *val2, long mask) -+{ -+ struct apalis_tk1_k20_adc *adc = iio_priv(indio_dev); -+ enum apalis_tk1_k20_adc_id id = chan->channel; -+ int ret; -+ -+ switch (mask) { -+ case IIO_CHAN_INFO_RAW: -+ ret = apalis_tk1_k20_get_adc_result(adc, id, val) ? -+ -EIO : IIO_VAL_INT; -+ break; -+ case IIO_CHAN_INFO_SCALE: -+ *val = APALIS_TK1_K20_VADC_MILI; -+ *val2 = APALIS_TK1_K20_ADC_BITS; -+ ret = IIO_VAL_FRACTIONAL_LOG2; -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+ return ret; -+} -+ -+static const struct iio_info apalis_tk1_k20_adc_info = { -+ .read_raw = &apalis_tk1_k20_adc_read_raw, -+ .driver_module = THIS_MODULE, -+}; -+ -+#define APALIS_TK1_K20_CHAN(_id, _type) { \ -+ .type = _type, \ -+ .indexed = 1, \ -+ .channel = APALIS_TK1_K20_##_id, \ -+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ -+ BIT(IIO_CHAN_INFO_SCALE), \ -+ .datasheet_name = #_id, \ -+} -+ -+static const struct iio_chan_spec apalis_tk1_k20_adc_channels[] = { -+ [APALIS_TK1_K20_ADC1] = APALIS_TK1_K20_CHAN(ADC1, IIO_VOLTAGE), -+ [APALIS_TK1_K20_ADC2] = APALIS_TK1_K20_CHAN(ADC2, IIO_VOLTAGE), -+ [APALIS_TK1_K20_ADC3] = APALIS_TK1_K20_CHAN(ADC3, IIO_VOLTAGE), -+ [APALIS_TK1_K20_ADC4] = APALIS_TK1_K20_CHAN(ADC4, IIO_VOLTAGE), -+}; -+ -+ -+static int apalis_tk1_k20_adc_probe(struct platform_device *pdev) -+{ -+ struct apalis_tk1_k20_adc *priv; -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20; -+ struct iio_dev *indio_dev; -+ int ret; -+ -+ indio_dev = iio_device_alloc(sizeof(*priv)); -+ if (!indio_dev) -+ return -ENOMEM; -+ -+ apalis_tk1_k20 = dev_get_drvdata(pdev->dev.parent); -+ if (!apalis_tk1_k20) { -+ ret = -ENODEV; -+ goto err_iio_device; -+ } -+ -+ priv = iio_priv(indio_dev); -+ priv->apalis_tk1_k20 = apalis_tk1_k20; -+ -+ apalis_tk1_k20_lock(apalis_tk1_k20); -+ -+ /* Enable ADC */ -+ apalis_tk1_k20_reg_write(apalis_tk1_k20, APALIS_TK1_K20_ADCREG, 0x01); -+ -+ apalis_tk1_k20_unlock(apalis_tk1_k20); -+ -+ platform_set_drvdata(pdev, indio_dev); -+ -+ indio_dev->dev.of_node = pdev->dev.of_node; -+ indio_dev->dev.parent = &pdev->dev; -+ indio_dev->name = pdev->name; -+ indio_dev->modes = INDIO_DIRECT_MODE; -+ indio_dev->info = &apalis_tk1_k20_adc_info; -+ indio_dev->channels = apalis_tk1_k20_adc_channels; -+ indio_dev->num_channels = ARRAY_SIZE(apalis_tk1_k20_adc_channels); -+ -+ ret = iio_device_register(indio_dev); -+ if (ret) { -+ dev_err(&pdev->dev, "iio dev register err: %d\n", ret); -+ goto err_iio_device; -+ } -+ -+ return 0; -+ -+err_iio_device: -+ iio_device_free(indio_dev); -+ return ret; -+} -+ -+static int apalis_tk1_k20_adc_remove(struct platform_device *pdev) -+{ -+ struct iio_dev *indio_dev = platform_get_drvdata(pdev); -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20 = dev_get_drvdata( -+ pdev->dev.parent); -+ -+ apalis_tk1_k20_lock(apalis_tk1_k20); -+ -+ /* Disable ADC */ -+ apalis_tk1_k20_reg_write(apalis_tk1_k20, APALIS_TK1_K20_ADCREG, 0x00); -+ -+ apalis_tk1_k20_unlock(apalis_tk1_k20); -+ -+ iio_device_unregister(indio_dev); -+ iio_device_free(indio_dev); -+ -+ return 0; -+} -+ -+static const struct platform_device_id apalis_tk1_k20_adc_idtable[] = { -+ { -+ .name = "apalis-tk1-k20-adc", -+ }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(platform, apalis_tk1_k20_adc_idtable); -+ -+static struct platform_driver apalis_tk1_k20_adc_driver = { -+ .id_table = apalis_tk1_k20_adc_idtable, -+ .remove = apalis_tk1_k20_adc_remove, -+ .probe = apalis_tk1_k20_adc_probe, -+ .driver = { -+ .name = "apalis-tk1-k20-adc", -+ }, -+}; -+ -+module_platform_driver(apalis_tk1_k20_adc_driver); -+ -+MODULE_DESCRIPTION("K20 ADCs on Apalis TK1"); -+MODULE_AUTHOR("Dominik Sliwa"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig -index 64b30fe273fd..81e9c604f612 100644 ---- a/drivers/input/touchscreen/Kconfig -+++ b/drivers/input/touchscreen/Kconfig -@@ -92,6 +92,16 @@ config TOUCHSCREEN_AD7879_SPI - To compile this driver as a module, choose M here: the - module will be called ad7879-spi. - -+config TOUCHSCREEN_APALIS_TK1_K20 -+ tristate "K20 based touchscreen controller on Apalis TK1" -+ depends on MFD_APALIS_TK1_K20 -+ help -+ Say Y here if you want to support the touchscreen controller -+ implemented in the K20 found on Apalis TK1. -+ -+ To compile this driver as a module, choose M here: the module will be -+ called apalis-tk1-k20_ts. -+ - config TOUCHSCREEN_AR1021_I2C - tristate "Microchip AR1020/1021 i2c touchscreen" - depends on I2C && OF -diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile -index 850c1562555a..d9352ec30431 100644 ---- a/drivers/input/touchscreen/Makefile -+++ b/drivers/input/touchscreen/Makefile -@@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o - obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o - obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o - obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o -+obj-$(CONFIG_TOUCHSCREEN_APALIS_TK1_K20) += apalis-tk1-k20_ts.o - obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o - obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o - obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o -diff --git a/drivers/input/touchscreen/apalis-tk1-k20_ts.c b/drivers/input/touchscreen/apalis-tk1-k20_ts.c -new file mode 100644 -index 000000000000..ef9e56c94685 ---- /dev/null -+++ b/drivers/input/touchscreen/apalis-tk1-k20_ts.c -@@ -0,0 +1,234 @@ -+/* -+ * Copyright 2016-2017 Toradex AG -+ * Dominik Sliwa <dominik.sliwa@toradex.com> -+ * -+ * Based on driver for the Freescale Semiconductor MC13783 touchscreen by: -+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. -+ * Copyright (C) 2009 Sascha Hauer, Pengutronix -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published by -+ * the Free Software Foundation. -+ */ -+ -+#include <linux/platform_device.h> -+#include <linux/mfd/apalis-tk1-k20.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/input.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/init.h> -+ -+#define APALIS_TK1_K20_TS_NAME "apalis-tk1-k20-ts" -+ -+struct apalis_tk1_k20_ts { -+ struct input_dev *idev; -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20; -+ struct delayed_work work; -+ struct workqueue_struct *workq; -+ uint16_t sample[4]; -+}; -+ -+static irqreturn_t apalis_tk1_k20_ts_handler(int irq, void *data) -+{ -+ struct apalis_tk1_k20_ts *priv = 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 / 25 which is acceptable. -+ */ -+ queue_delayed_work(priv->workq, &priv->work, 0); -+ -+ return IRQ_HANDLED; -+} -+ -+static void apalis_tk1_k20_ts_report_sample(struct apalis_tk1_k20_ts *priv) -+{ -+ struct input_dev *idev = priv->idev; -+ int xp, xm, yp, ym; -+ int x, y, press, btn; -+ -+ xp = priv->sample[1]; -+ xm = priv->sample[0]; -+ yp = priv->sample[3]; -+ ym = priv->sample[2]; -+ -+ x = (xp + xm) / 2; -+ y = (yp + ym) / 2; -+ press = (abs(yp - ym) + abs(xp - xm)) / 2; -+ -+ if ((yp != 0) && (xp != 0)) { -+ btn = 1; -+ input_report_abs(idev, ABS_X, x); -+ input_report_abs(idev, ABS_Y, y); -+ -+ dev_dbg(&idev->dev, "report (%d, %d, %d)\n", -+ x, y, press); -+ queue_delayed_work(priv->workq, &priv->work, HZ / 25); -+ } else { -+ dev_dbg(&idev->dev, "report release\n"); -+ btn = 0; -+ } -+ -+ input_report_abs(idev, ABS_PRESSURE, press); -+ input_report_key(idev, BTN_TOUCH, btn); -+ input_sync(idev); -+} -+ -+static void apalis_tk1_k20_ts_work(struct work_struct *work) -+{ -+ struct apalis_tk1_k20_ts *priv = -+ container_of(work, struct apalis_tk1_k20_ts, work.work); -+ uint8_t buf[8], i; -+ -+ apalis_tk1_k20_lock(priv->apalis_tk1_k20); -+ -+ if (apalis_tk1_k20_reg_read_bulk(priv->apalis_tk1_k20, -+ APALIS_TK1_K20_TSC_XML, buf, 8) < 0) { -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ dev_err(&priv->idev->dev, "Error reading data\n"); -+ return; -+ } -+ -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ -+ for (i = 0; i < 4; i++) -+ priv->sample[i] = (buf[(2 * i) + 1] << 8) + buf[2 * i]; -+ -+ apalis_tk1_k20_ts_report_sample(priv); -+} -+ -+static int apalis_tk1_k20_ts_open(struct input_dev *dev) -+{ -+ struct apalis_tk1_k20_ts *priv = input_get_drvdata(dev); -+ int ret; -+ -+ apalis_tk1_k20_lock(priv->apalis_tk1_k20); -+ -+ ret = apalis_tk1_k20_irq_request(priv->apalis_tk1_k20, -+ APALIS_TK1_K20_TSC_IRQ, apalis_tk1_k20_ts_handler, -+ APALIS_TK1_K20_TS_NAME, priv); -+ if (ret) -+ goto out; -+ -+ ret = apalis_tk1_k20_reg_rmw(priv->apalis_tk1_k20, -+ APALIS_TK1_K20_TSCREG, APALIS_TK1_K20_TSC_ENA_MASK, -+ APALIS_TK1_K20_TSC_ENA); -+ if (ret) -+ apalis_tk1_k20_irq_free(priv->apalis_tk1_k20, -+ APALIS_TK1_K20_TSC_IRQ, priv); -+ -+out: -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ -+ return ret; -+} -+ -+static void apalis_tk1_k20_ts_close(struct input_dev *dev) -+{ -+ struct apalis_tk1_k20_ts *priv = input_get_drvdata(dev); -+ -+ apalis_tk1_k20_lock(priv->apalis_tk1_k20); -+ -+ apalis_tk1_k20_reg_rmw(priv->apalis_tk1_k20, APALIS_TK1_K20_TSCREG, -+ APALIS_TK1_K20_TSC_ENA_MASK, 0); -+ apalis_tk1_k20_irq_free(priv->apalis_tk1_k20, APALIS_TK1_K20_TSC_IRQ, -+ priv); -+ -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ -+ cancel_delayed_work_sync(&priv->work); -+} -+ -+static int apalis_tk1_k20_ts_probe(struct platform_device *pdev) -+{ -+ struct apalis_tk1_k20_ts *priv; -+ struct input_dev *idev; -+ int ret = -ENOMEM; -+ -+ priv = kzalloc(sizeof(*priv), GFP_KERNEL); -+ idev = input_allocate_device(); -+ if (!priv || !idev) -+ goto err_free_mem; -+ -+ INIT_DELAYED_WORK(&priv->work, apalis_tk1_k20_ts_work); -+ -+ priv->apalis_tk1_k20 = dev_get_drvdata(pdev->dev.parent); -+ priv->idev = idev; -+ -+ priv->workq = create_singlethread_workqueue("apalis_tk1_k20_ts"); -+ if (!priv->workq) -+ goto err_free_mem; -+ -+ idev->name = APALIS_TK1_K20_TS_NAME; -+ idev->dev.parent = &pdev->dev; -+ -+ 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, 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; -+ idev->close = apalis_tk1_k20_ts_close; -+ -+ input_set_drvdata(idev, priv); -+ -+ ret = input_register_device(priv->idev); -+ if (ret) { -+ dev_err(&pdev->dev, -+ "register input device failed with %d\n", ret); -+ goto err_destroy_wq; -+ } -+ -+ platform_set_drvdata(pdev, priv); -+ -+ return 0; -+ -+err_destroy_wq: -+ destroy_workqueue(priv->workq); -+err_free_mem: -+ input_free_device(idev); -+ kfree(priv); -+ -+ return ret; -+} -+ -+static int apalis_tk1_k20_ts_remove(struct platform_device *pdev) -+{ -+ struct apalis_tk1_k20_ts *priv = platform_get_drvdata(pdev); -+ -+ platform_set_drvdata(pdev, NULL); -+ -+ destroy_workqueue(priv->workq); -+ input_unregister_device(priv->idev); -+ kfree(priv); -+ -+ return 0; -+} -+ -+static const struct platform_device_id apalis_tk1_k20_ts_idtable[] = { -+ { -+ .name = "apalis-tk1-k20-ts", -+ }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(platform, apalis_tk1_k20_ts_idtable); -+ -+static struct platform_driver apalis_tk1_k20_ts_driver = { -+ .id_table = apalis_tk1_k20_ts_idtable, -+ .probe = apalis_tk1_k20_ts_probe, -+ .remove = apalis_tk1_k20_ts_remove, -+ .driver = { -+ .name = APALIS_TK1_K20_TS_NAME, -+ }, -+}; -+ -+module_platform_driver(apalis_tk1_k20_ts_driver); -+ -+MODULE_DESCRIPTION("K20 touchscreen controller on Apalis TK1"); -+MODULE_AUTHOR("Dominik Sliwa <dominik.sliwa@toradex.com>"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig -index fc5e4fef89d2..cd0a1dfd0a63 100644 ---- a/drivers/mfd/Kconfig -+++ b/drivers/mfd/Kconfig -@@ -190,6 +190,20 @@ config MFD_AXP20X_RSB - components like regulators or the PEK (Power Enable Key) under the - corresponding menus. - -+config MFD_APALIS_TK1_K20 -+ tristate "K20 on Apalis TK1" -+ depends on SPI_MASTER -+ select MFD_CORE -+ help -+ The Kinetis MK20DN512 companion micro controller found on Apalis TK1 -+ supports CAN, resistive touch, GPIOs and analog inputs. -+ -+config APALIS_TK1_K20_EZP -+ bool "K20 on Apalis TK1 programming via EZ Port" -+ depends on MFD_APALIS_TK1_K20 -+ help -+ Support for flashing new K20 firmware using EZ-Port functionality. -+ - config MFD_CROS_EC - tristate "ChromeOS Embedded Controller" - select MFD_CORE -diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile -index 8703ff17998e..ba4f60862f9f 100644 ---- a/drivers/mfd/Makefile -+++ b/drivers/mfd/Makefile -@@ -9,6 +9,7 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o - obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o - obj-$(CONFIG_MFD_ACT8945A) += act8945a.o - obj-$(CONFIG_MFD_SM501) += sm501.o -+obj-$(CONFIG_MFD_APALIS_TK1_K20) += apalis-tk1-k20.o - obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o - obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o - obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o -diff --git a/drivers/mfd/apalis-tk1-k20-ezp.h b/drivers/mfd/apalis-tk1-k20-ezp.h -new file mode 100644 -index 000000000000..e89d6adbe471 ---- /dev/null -+++ b/drivers/mfd/apalis-tk1-k20-ezp.h -@@ -0,0 +1,47 @@ -+/* -+ * Copyright 2016-2017 Toradex AG -+ * Dominik Sliwa <dominik.sliwa@toradex.com> -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. -+ */ -+ -+#ifndef __DRIVERS_MFD_APALIS_TK1_K20_H -+#define __DRIVERS_MFD_APALIS_TK1_K20_H -+ -+#ifdef CONFIG_APALIS_TK1_K20_EZP -+#define APALIS_TK1_K20_FW_FOPT_ADDR 0x40D -+#define APALIS_TK1_K20_FOPT_EZP_ENA BIT(1) -+#define APALIS_TK1_K20_FW_VER_ADDR 0x410 -+ -+#define APALIS_TK1_K20_FLASH_SIZE 0x80000 -+ -+/* EZ Port commands */ -+#define APALIS_TK1_K20_EZP_WREN 0x06 -+#define APALIS_TK1_K20_EZP_WRDI 0x04 -+#define APALIS_TK1_K20_EZP_RDSR 0x05 -+#define APALIS_TK1_K20_EZP_READ 0x03 -+#define APALIS_TK1_K20_EZP_FREAD 0x0B -+#define APALIS_TK1_K20_EZP_SP 0x02 -+#define APALIS_TK1_K20_EZP_SE 0xD8 -+#define APALIS_TK1_K20_EZP_BE 0xC7 -+#define APALIS_TK1_K20_EZP_RESET 0xB9 -+#define APALIS_TK1_K20_EZP_WRFCCOB 0xBA -+#define APALIS_TK1_K20_EZP_FRDFCOOB 0xBB -+ -+/* Bits of EZ Port status register */ -+#define APALIS_TK1_K20_EZP_STA_WIP BIT(0) -+#define APALIS_TK1_K20_EZP_STA_WEN BIT(1) -+#define APALIS_TK1_K20_EZP_STA_BEDIS BIT(2) -+#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 4080000 -+#define APALIS_TK1_K20_EZP_MAX_DATA 32 -+#define APALIS_TK1_K20_EZP_WRITE_SIZE 32 -+ -+static const struct firmware *fw_entry; -+#endif /* CONFIG_APALIS_TK1_K20_EZP */ -+ -+#endif /* __DRIVERS_MFD_APALIS_TK1_K20_H */ -diff --git a/drivers/mfd/apalis-tk1-k20.c b/drivers/mfd/apalis-tk1-k20.c -new file mode 100644 -index 000000000000..913be65c33e6 ---- /dev/null -+++ b/drivers/mfd/apalis-tk1-k20.c -@@ -0,0 +1,1062 @@ -+/* -+ * Copyright 2016-2017 Toradex AG -+ * Dominik Sliwa <dominik.sliwa@toradex.com> -+ * -+ * based on an driver for MC13xxx by: -+ * Copyright 2009-2010 Pengutronix -+ * Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de> -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. -+ */ -+ -+#include <linux/slab.h> -+#include <linux/module.h> -+#include <linux/platform_device.h> -+#include <linux/interrupt.h> -+#include <linux/mfd/core.h> -+#include <linux/mfd/apalis-tk1-k20.h> -+#include <linux/of.h> -+#include <linux/of_device.h> -+#include <linux/of_gpio.h> -+#include <linux/of_irq.h> -+#include <linux/err.h> -+#include <linux/firmware.h> -+#include <linux/spi/spi.h> -+#include <linux/delay.h> -+ -+#include "apalis-tk1-k20-ezp.h" -+#define APALIS_TK1_K20_MAX_MSG 4 -+ -+static const struct spi_device_id apalis_tk1_k20_device_ids[] = { -+ { -+ .name = "apalis-tk1-k20", -+ }, { -+ /* sentinel */ -+ } -+}; -+MODULE_DEVICE_TABLE(spi, apalis_tk1_k20_device_ids); -+ -+static const struct of_device_id apalis_tk1_k20_dt_ids[] = { -+ { -+ .compatible = "toradex,apalis-tk1-k20", -+ }, { -+ /* sentinel */ -+ } -+}; -+MODULE_DEVICE_TABLE(of, apalis_tk1_k20_dt_ids); -+ -+static const struct regmap_config apalis_tk1_k20_regmap_spi_config = { -+ .reg_bits = 8, -+ .pad_bits = 0, -+ .val_bits = 8, -+ -+ .max_register = APALIS_TK1_K20_LAST_REG, -+ -+ .cache_type = REGCACHE_NONE, -+ .use_single_rw = 0, -+}; -+ -+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[APALIS_TK1_K20_MAX_BULK] = {APALIS_TK1_K20_READ_INST, -+ *((unsigned char *) reg), val_size, 0x00, 0x00, 0x00, -+ 0x00}; -+ unsigned char r[APALIS_TK1_K20_MAX_BULK]; -+ unsigned char *p = val; -+ int i = 0, j = 0; -+ struct device *dev = context; -+ struct spi_device *spi = to_spi_device(dev); -+ struct spi_transfer t = { -+ .tx_buf = w, -+ .rx_buf = r, -+ .len = 8, -+ .cs_change = 0, -+ .delay_usecs = 0, -+ }; -+ -+ struct spi_message m; -+ int ret; -+ spi->mode = SPI_MODE_1; -+ -+ if (reg_size != 1) -+ return -ENOTSUPP; -+ -+ if (val_size == 1) { -+ spi_message_init(&m); -+ spi_message_add_tail(&t, &m); -+ ret = spi_sync(spi, &m); -+ -+ for (i = 3; i < 7; i++ ) -+ { -+ if (((unsigned char *)t.rx_buf)[i] == 0x55) { -+ *p = ((unsigned char *)t.rx_buf)[i + 1]; -+ return ret; -+ } -+ } -+ -+ for (j = 0; j < APALIS_TK1_MAX_RETRY_CNT; j++) { -+ udelay(250 * j * j); -+ t.tx_buf = w; -+ t.rx_buf = r; -+ spi_message_init(&m); -+ spi_message_add_tail(&t, &m); -+ ret = spi_sync(spi, &m); -+ for (i = 3; i < 7; i++ ) -+ { -+ if (((unsigned char *)t.rx_buf)[i] == 0x55) { -+ *p = ((unsigned char *)t.rx_buf)[i + 1]; -+ return ret; -+ } -+ } -+ } -+ ret = -EIO; -+ -+ } else if ((val_size > 1) && (val_size < APALIS_TK1_K20_MAX_BULK)) { -+ t.len = 5; -+ w[0] = APALIS_TK1_K20_BULK_READ_INST; -+ spi_message_init(&m); -+ spi_message_add_tail(&t, &m); -+ ret = spi_sync(spi, &m); -+ /* no need to reinit the message*/ -+ t.len = val_size; -+ t.rx_buf = p; -+ /* just use the same transfer */ -+ ret = spi_sync(spi, &m); -+ -+ } else { -+ return -ENOTSUPP; -+ } -+ -+ return ret; -+} -+ -+ -+static int apalis_tk1_k20_spi_write(void *context, const void *data, -+ size_t count) -+{ -+ struct device *dev = context; -+ struct spi_device *spi = to_spi_device(dev); -+ uint8_t out_data[APALIS_TK1_K20_MAX_BULK]; -+ int ret; -+ -+ -+ spi->mode = SPI_MODE_1; -+ -+ 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); -+ -+ } else if ((count > 2) && (count < APALIS_TK1_K20_MAX_BULK)) { -+ out_data[0] = APALIS_TK1_K20_BULK_WRITE_INST; -+ out_data[1] = ((uint8_t *)data)[0]; -+ out_data[2] = count - 1; -+ memcpy(&out_data[3], &((uint8_t *)data)[1], count - 1); -+ ret = spi_write(spi, out_data, count + 2); -+ } else { -+ dev_err(dev, "Apalis TK1 K20 MFD invalid write count = %d\n", -+ count); -+ return -ENOTSUPP; -+ } -+ return ret; -+} -+ -+static struct regmap_bus regmap_apalis_tk1_k20_bus = { -+ .write = apalis_tk1_k20_spi_write, -+ .read = apalis_tk1_k20_spi_read, -+}; -+ -+void apalis_tk1_k20_lock(struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ if (!mutex_trylock(&apalis_tk1_k20->lock)) { -+ dev_dbg(apalis_tk1_k20->dev, "wait for %s from %ps\n", -+ __func__, __builtin_return_address(0)); -+ -+ mutex_lock(&apalis_tk1_k20->lock); -+ } -+ dev_dbg(apalis_tk1_k20->dev, "%s from %ps\n", -+ __func__, __builtin_return_address(0)); -+} -+EXPORT_SYMBOL(apalis_tk1_k20_lock); -+ -+void apalis_tk1_k20_unlock(struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ dev_dbg(apalis_tk1_k20->dev, "%s from %ps\n", -+ __func__, __builtin_return_address(0)); -+ mutex_unlock(&apalis_tk1_k20->lock); -+} -+EXPORT_SYMBOL(apalis_tk1_k20_unlock); -+ -+int apalis_tk1_k20_reg_read(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ unsigned int offset, u32 *val) -+{ -+ int ret; -+ -+ ret = regmap_read(apalis_tk1_k20->regmap, offset, val); -+ dev_dbg(apalis_tk1_k20->dev, "[0x%02x] -> 0x%02x ret = %d\n", offset, -+ *val, ret); -+ -+ return ret; -+} -+EXPORT_SYMBOL(apalis_tk1_k20_reg_read); -+ -+int apalis_tk1_k20_reg_write(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ unsigned int offset, u32 val) -+{ -+ int ret; -+ -+ if (val >= BIT(24)) -+ return -EINVAL; -+ -+ ret = regmap_write(apalis_tk1_k20->regmap, offset, val); -+ -+ dev_dbg(apalis_tk1_k20->dev, "[0x%02x] <- 0x%02x ret = %d\n", offset, val, -+ ret); -+ -+ return ret; -+} -+EXPORT_SYMBOL(apalis_tk1_k20_reg_write); -+ -+int apalis_tk1_k20_reg_read_bulk(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ unsigned int offset, -+ uint8_t *val, size_t size) -+{ -+ int ret; -+ -+ if (size > APALIS_TK1_K20_MAX_BULK) -+ return -EINVAL; -+ -+ ret = regmap_bulk_read(apalis_tk1_k20->regmap, offset, val, size); -+ dev_dbg(apalis_tk1_k20->dev, "bulk read %d bytes from [0x%02x]\n", -+ size, offset); -+ -+ return ret; -+} -+EXPORT_SYMBOL(apalis_tk1_k20_reg_read_bulk); -+ -+int apalis_tk1_k20_reg_write_bulk(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ unsigned int offset, uint8_t *buf, size_t size) -+{ -+ dev_dbg(apalis_tk1_k20->dev, "bulk write %d bytes to [0x%02x]\n", -+ (unsigned int)size, offset); -+ -+ if (size > APALIS_TK1_K20_MAX_BULK) -+ return -EINVAL; -+ return regmap_bulk_write(apalis_tk1_k20->regmap, offset, buf, size); -+} -+EXPORT_SYMBOL(apalis_tk1_k20_reg_write_bulk); -+ -+int apalis_tk1_k20_reg_rmw(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ unsigned int offset, u32 mask, u32 val) -+{ -+ dev_dbg(apalis_tk1_k20->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n", -+ offset, val, mask); -+ -+ return regmap_update_bits(apalis_tk1_k20->regmap, offset, mask, val); -+} -+EXPORT_SYMBOL(apalis_tk1_k20_reg_rmw); -+ -+int apalis_tk1_k20_irq_mask(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ int irq) -+{ -+ int virq = -1; -+ if (irq != APALIS_TK1_K20_CAN1_IRQ && irq != APALIS_TK1_K20_CAN0_IRQ) { -+ virq = regmap_irq_get_virq(apalis_tk1_k20->irq_data, irq); -+ -+ } else { -+ virq = (irq == APALIS_TK1_K20_CAN0_IRQ) ? -+ apalis_tk1_k20->can0_irq:apalis_tk1_k20->can1_irq; -+ } -+ -+ disable_irq_nosync(virq); -+ -+ return 0; -+} -+EXPORT_SYMBOL(apalis_tk1_k20_irq_mask); -+ -+int apalis_tk1_k20_irq_unmask(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ int irq) -+{ -+ int virq = -1; -+ if (irq != APALIS_TK1_K20_CAN1_IRQ && irq != APALIS_TK1_K20_CAN0_IRQ) { -+ virq = regmap_irq_get_virq(apalis_tk1_k20->irq_data, irq); -+ -+ } else { -+ virq = (irq == APALIS_TK1_K20_CAN0_IRQ) ? -+ apalis_tk1_k20->can0_irq:apalis_tk1_k20->can1_irq; -+ } -+ -+ enable_irq(virq); -+ -+ return 0; -+} -+EXPORT_SYMBOL(apalis_tk1_k20_irq_unmask); -+ -+int apalis_tk1_k20_irq_status(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ int irq, int *enabled, int *pending) -+{ -+ int ret; -+ unsigned int offmask = APALIS_TK1_K20_MSQREG; -+ unsigned int offstat = APALIS_TK1_K20_IRQREG; -+ u32 irqbit = 1 << irq; -+ -+ if (irq < 0 || irq >= ARRAY_SIZE(apalis_tk1_k20->irqs)) -+ return -EINVAL; -+ -+ if (enabled) { -+ u32 mask; -+ -+ ret = apalis_tk1_k20_reg_read(apalis_tk1_k20, offmask, &mask); -+ if (ret) -+ return ret; -+ -+ *enabled = mask & irqbit; -+ } -+ -+ if (pending) { -+ u32 stat; -+ -+ ret = apalis_tk1_k20_reg_read(apalis_tk1_k20, offstat, &stat); -+ if (ret) -+ return ret; -+ -+ *pending = stat & irqbit; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(apalis_tk1_k20_irq_status); -+ -+int apalis_tk1_k20_irq_request(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ int irq, irq_handler_t handler, const char *name, void *dev) -+{ -+ int virq = -1; -+ int irq_flags = IRQF_ONESHOT; -+ if (irq != APALIS_TK1_K20_CAN1_IRQ && irq != APALIS_TK1_K20_CAN0_IRQ) { -+ virq = regmap_irq_get_virq(apalis_tk1_k20->irq_data, irq); -+ irq_flags = IRQF_ONESHOT; -+ } else { -+ virq = (irq == APALIS_TK1_K20_CAN0_IRQ) ? -+ apalis_tk1_k20->can0_irq:apalis_tk1_k20->can1_irq; -+ irq_flags = IRQF_ONESHOT | IRQF_TRIGGER_FALLING | -+ IRQF_TRIGGER_RISING; -+ } -+ return devm_request_threaded_irq(apalis_tk1_k20->dev, virq, -+ NULL, handler, irq_flags, name, dev); -+} -+EXPORT_SYMBOL(apalis_tk1_k20_irq_request); -+ -+int apalis_tk1_k20_irq_free(struct apalis_tk1_k20_regmap *apalis_tk1_k20, -+ int irq, void *dev) -+{ -+ int virq = -1; -+ if (irq != APALIS_TK1_K20_CAN1_IRQ && irq != APALIS_TK1_K20_CAN0_IRQ) { -+ virq = regmap_irq_get_virq(apalis_tk1_k20->irq_data, irq); -+ -+ } else { -+ virq = (irq == APALIS_TK1_K20_CAN0_IRQ) ? -+ apalis_tk1_k20->can0_irq:apalis_tk1_k20->can1_irq; -+ } -+ -+ devm_free_irq(apalis_tk1_k20->dev, virq, dev); -+ -+ return 0; -+} -+EXPORT_SYMBOL(apalis_tk1_k20_irq_free); -+ -+ -+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, int id) -+{ -+ struct mfd_cell cell = { -+ .platform_data = pdata, -+ .pdata_size = pdata_size, -+ }; -+ -+ cell.name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); -+ if (!cell.name) -+ return -ENOMEM; -+ -+ 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) -+{ -+ return apalis_tk1_k20_add_subdevice_pdata(apalis_tk1_k20, name, NULL, -+ 0); -+} -+ -+static void apalis_tk1_k20_reset_chip( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ udelay(10); -+ gpio_set_value(apalis_tk1_k20->reset_gpio, 0); -+ msleep(10); -+ gpio_set_value(apalis_tk1_k20->reset_gpio, 1); -+ udelay(10); -+} -+ -+#ifdef CONFIG_APALIS_TK1_K20_EZP -+static int apalis_tk1_k20_read_ezport( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20, uint8_t command, -+ int addr, int count, uint8_t *buffer) -+{ -+ uint8_t w[4 + APALIS_TK1_K20_EZP_MAX_DATA]; -+ uint8_t r[4 + APALIS_TK1_K20_EZP_MAX_DATA]; -+ uint8_t *out; -+ struct spi_message m; -+ struct spi_device *spi = to_spi_device(apalis_tk1_k20->dev); -+ struct spi_transfer t = { -+ .tx_buf = w, -+ .rx_buf = r, -+ .speed_hz = APALIS_TK1_K20_EZP_MAX_SPEED, -+ }; -+ int ret; -+ -+ spi->mode = SPI_MODE_0; -+ -+ if (count > APALIS_TK1_K20_EZP_MAX_DATA) -+ return -ENOSPC; -+ -+ memset(w, 0, 4 + count); -+ -+ switch (command) { -+ case APALIS_TK1_K20_EZP_READ: -+ case APALIS_TK1_K20_EZP_FREAD: -+ t.len = 4 + count; -+ w[1] = (addr & 0xFF0000) >> 16; -+ w[2] = (addr & 0xFF00) >> 8; -+ w[3] = (addr & 0xFC); -+ out = &r[4]; -+ break; -+ case APALIS_TK1_K20_EZP_RDSR: -+ case APALIS_TK1_K20_EZP_FRDFCOOB: -+ t.len = 1 + count; -+ out = &r[1]; -+ break; -+ default: -+ return -EINVAL; -+ } -+ w[0] = command; -+ -+ gpio_set_value(apalis_tk1_k20->ezpcs_gpio, 0); -+ spi_message_init(&m); -+ spi_message_add_tail(&t, &m); -+ ret = spi_sync(spi, &m); -+ gpio_set_value(apalis_tk1_k20->ezpcs_gpio, 1); -+ if (ret != 0) -+ return ret; -+ -+ memcpy(buffer, out, count); -+ -+ return 0; -+} -+ -+static int apalis_tk1_k20_write_ezport( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20, uint8_t command, -+ int addr, int count, const uint8_t *buffer) -+{ -+ uint8_t w[4 + APALIS_TK1_K20_EZP_MAX_DATA]; -+ uint8_t r[4 + APALIS_TK1_K20_EZP_MAX_DATA]; -+ struct spi_message m; -+ struct spi_device *spi = to_spi_device(apalis_tk1_k20->dev); -+ struct spi_transfer t = { -+ .tx_buf = w, -+ .rx_buf = r, -+ .speed_hz = APALIS_TK1_K20_EZP_MAX_SPEED, -+ }; -+ int ret; -+ -+ spi->mode = SPI_MODE_0; -+ -+ if (count > APALIS_TK1_K20_EZP_MAX_DATA) -+ return -ENOSPC; -+ -+ switch (command) { -+ case APALIS_TK1_K20_EZP_SP: -+ case APALIS_TK1_K20_EZP_SE: -+ t.len = 4 + count; -+ w[1] = (addr & 0xFF0000) >> 16; -+ w[2] = (addr & 0xFF00) >> 8; -+ w[3] = (addr & 0xF8); -+ memcpy(&w[4], buffer, count); -+ break; -+ case APALIS_TK1_K20_EZP_WREN: -+ case APALIS_TK1_K20_EZP_WRDI: -+ case APALIS_TK1_K20_EZP_BE: -+ case APALIS_TK1_K20_EZP_RESET: -+ case APALIS_TK1_K20_EZP_WRFCCOB: -+ t.len = 1 + count; -+ memcpy(&w[1], buffer, count); -+ break; -+ default: -+ return -EINVAL; -+ } -+ w[0] = command; -+ -+ gpio_set_value(apalis_tk1_k20->ezpcs_gpio, 0); -+ spi_message_init(&m); -+ spi_message_add_tail(&t, &m); -+ ret = spi_sync(spi, &m); -+ gpio_set_value(apalis_tk1_k20->ezpcs_gpio, 1); -+ -+ return ret; -+} -+ -+static int apalis_tk1_k20_set_wren_ezport( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ uint8_t buffer; -+ -+ if (apalis_tk1_k20_write_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_WREN, -+ 0, 0, NULL) < 0) -+ return -EIO; -+ if (apalis_tk1_k20_read_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_RDSR, -+ 0, 1, &buffer) < 0) -+ return -EIO; -+ if ((buffer & APALIS_TK1_K20_EZP_STA_WEN)) -+ return 0; -+ -+ /* If it failed try one last time */ -+ if (apalis_tk1_k20_write_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_WREN, -+ 0, 0, NULL) < 0) -+ return -EIO; -+ if (apalis_tk1_k20_read_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_RDSR, -+ 0, 1, &buffer) < 0) -+ return -EIO; -+ if ((buffer & APALIS_TK1_K20_EZP_STA_WEN)) -+ return 0; -+ -+ return -EIO; -+ -+} -+ -+static int apalis_tk1_k20_enter_ezport( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ 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(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) -+ goto bad; -+ status = buffer; -+ if (apalis_tk1_k20_write_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_WREN, -+ 0, 0, NULL) < 0) -+ goto bad; -+ if (apalis_tk1_k20_read_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_RDSR, -+ 0, 1, &buffer) < 0) -+ goto bad; -+ if ((buffer & APALIS_TK1_K20_EZP_STA_WEN) && buffer != status) -+ return 0; -+ -+bad: -+ dev_err(apalis_tk1_k20->dev, "Error entering EZ Port mode.\n"); -+ return -EIO; -+} -+ -+static int apalis_tk1_k20_erase_chip_ezport( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ uint8_t buffer[16]; -+ int i; -+ -+ if (apalis_tk1_k20_set_wren_ezport(apalis_tk1_k20)) -+ goto bad; -+ if (apalis_tk1_k20_write_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_BE, -+ 0, 0, NULL) < 0) -+ goto bad; -+ -+ if (apalis_tk1_k20_read_ezport(apalis_tk1_k20, APALIS_TK1_K20_EZP_RDSR, -+ 0, 1, buffer) < 0) -+ goto bad; -+ -+ i = 0; -+ while (buffer[0] & APALIS_TK1_K20_EZP_STA_WIP) { -+ msleep(20); -+ if ((apalis_tk1_k20_read_ezport(apalis_tk1_k20, -+ APALIS_TK1_K20_EZP_RDSR, 0, 1, buffer) < 0) || (i > 50)) -+ goto bad; -+ i++; -+ } -+ -+ return 0; -+ -+bad: -+ dev_err(apalis_tk1_k20->dev, "Error erasing the chip.\n"); -+ return -EIO; -+} -+ -+static int apalis_tk1_k20_flash_chip_ezport( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ uint8_t buffer; -+ const uint8_t *fw_chunk; -+ int i, j, transfer_size; -+ -+ for (i = 0; i < fw_entry->size;) { -+ if (apalis_tk1_k20_set_wren_ezport(apalis_tk1_k20)) -+ goto bad; -+ -+ fw_chunk = fw_entry->data + i; -+ transfer_size = (i + APALIS_TK1_K20_EZP_WRITE_SIZE < -+ fw_entry->size) ? APALIS_TK1_K20_EZP_WRITE_SIZE -+ : (fw_entry->size - i - 1); -+ dev_dbg(apalis_tk1_k20->dev, -+ "Apalis TK1 K20 MFD transfer_size = %d addr = 0x%X\n", -+ transfer_size, i); -+ if (apalis_tk1_k20_write_ezport(apalis_tk1_k20, -+ 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) { -+ msleep(10); -+ if ((apalis_tk1_k20_read_ezport(apalis_tk1_k20, -+ APALIS_TK1_K20_EZP_RDSR, 0, 1, -+ &buffer) < 0) || (j > 10000)) -+ goto bad; -+ j++; -+ } -+ i += APALIS_TK1_K20_EZP_WRITE_SIZE; -+ } -+ -+ return 0; -+ -+bad: -+ dev_err(apalis_tk1_k20->dev, "Error writing to the chip.\n"); -+ return -EIO; -+} -+ -+static uint8_t apalis_tk1_k20_fw_ezport_status(void) -+{ -+ return fw_entry->data[APALIS_TK1_K20_FW_FOPT_ADDR] & -+ APALIS_TK1_K20_FOPT_EZP_ENA; -+} -+ -+static uint8_t apalis_tk1_k20_get_fw_revision(void) -+{ -+ if (fw_entry) -+ if (fw_entry->size > APALIS_TK1_K20_FW_VER_ADDR) -+ return fw_entry->data[APALIS_TK1_K20_FW_VER_ADDR]; -+ return 0; -+} -+#endif /* CONFIG_APALIS_TK1_K20_EZP */ -+ -+ -+#ifdef CONFIG_OF -+static int apalis_tk1_k20_probe_flags_dt( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ struct device_node *np = apalis_tk1_k20->dev->of_node; -+ -+ if (!np) -+ return -ENODEV; -+ -+ if (of_property_read_bool(np, "toradex,apalis-tk1-k20-uses-adc")) -+ apalis_tk1_k20->flags |= APALIS_TK1_K20_USES_ADC; -+ -+ if (of_property_read_bool(np, "toradex,apalis-tk1-k20-uses-tsc")) -+ apalis_tk1_k20->flags |= APALIS_TK1_K20_USES_TSC; -+ -+ if (of_property_read_bool(np, "toradex,apalis-tk1-k20-uses-can")) -+ apalis_tk1_k20->flags |= APALIS_TK1_K20_USES_CAN; -+ -+ if (of_property_read_bool(np, "toradex,apalis-tk1-k20-uses-gpio")) -+ apalis_tk1_k20->flags |= APALIS_TK1_K20_USES_GPIO; -+ -+ return 0; -+} -+ -+static int apalis_tk1_k20_probe_gpios_dt( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ struct device_node *np = apalis_tk1_k20->dev->of_node; -+ -+ if (!np) -+ return -ENODEV; -+ -+ apalis_tk1_k20->reset_gpio = of_get_named_gpio(np, "rst-gpio", 0); -+ if (apalis_tk1_k20->reset_gpio < 0) -+ return apalis_tk1_k20->reset_gpio; -+ gpio_request(apalis_tk1_k20->reset_gpio, "apalis-tk1-k20-reset"); -+ gpio_direction_output(apalis_tk1_k20->reset_gpio, 1); -+ -+ apalis_tk1_k20->ezpcs_gpio = of_get_named_gpio(np, "ezport-cs-gpio", 0); -+ if (apalis_tk1_k20->ezpcs_gpio < 0) -+ return apalis_tk1_k20->ezpcs_gpio; -+ gpio_request(apalis_tk1_k20->ezpcs_gpio, "apalis-tk1-k20-ezpcs"); -+ gpio_direction_output(apalis_tk1_k20->ezpcs_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; -+ gpio_request(apalis_tk1_k20->int2_gpio, "apalis-tk1-k20-int2"); -+ gpio_direction_output(apalis_tk1_k20->int2_gpio, 1); -+ -+ return 0; -+} -+#else -+static inline int apalis_tk1_k20_probe_flags_dt( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ return -ENODEV; -+} -+static inline int apalis_tk1_k20_probe_gpios_dt( -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20) -+{ -+ return -ENODEV; -+} -+#endif -+ -+int apalis_tk1_k20_dev_init(struct device *dev) -+{ -+ struct apalis_tk1_k20_platform_data *pdata = dev_get_platdata(dev); -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20 = dev_get_drvdata(dev); -+ uint32_t revision = 0x00; -+ int ret, i; -+ int erase_only = 0; -+ -+ apalis_tk1_k20->dev = dev; -+ -+ ret = apalis_tk1_k20_probe_gpios_dt(apalis_tk1_k20); -+ if ((ret < 0) && pdata) { -+ if (pdata) { -+ apalis_tk1_k20->ezpcs_gpio = pdata->ezpcs_gpio; -+ apalis_tk1_k20->reset_gpio = pdata->reset_gpio; -+ apalis_tk1_k20->int2_gpio = pdata->int2_gpio; -+ } else { -+ dev_err(dev, "Error claiming GPIOs\n"); -+ ret = -EINVAL; -+ goto bad; -+ } -+ } -+ apalis_tk1_k20_reset_chip(apalis_tk1_k20); -+ msleep(10); -+ ret = apalis_tk1_k20_reg_read(apalis_tk1_k20, APALIS_TK1_K20_REVREG, -+ &revision); -+ -+#ifdef CONFIG_APALIS_TK1_K20_EZP -+ if ((request_firmware(&fw_entry, "apalis-tk1-k20.bin", dev) < 0) -+ && (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) && (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 && -+ (fw_entry != NULL)) { -+ dev_err(apalis_tk1_k20->dev, -+ "Unsupported firmware version in both the device " \ -+ "as well as the local firmware file.\n"); -+ release_firmware(fw_entry); -+ ret = -ENOTSUPP; -+ goto bad; -+ } -+ -+ if ((revision != APALIS_TK1_K20_FW_VER) && !erase_only && -+ (!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"); -+ release_firmware(fw_entry); -+ ret = -ENOTSUPP; -+ goto bad; -+ } -+ -+ 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); -+ ret = -EIO; -+ goto bad; -+ } -+ if (apalis_tk1_k20_erase_chip_ezport(apalis_tk1_k20) < 0) { -+ dev_err(apalis_tk1_k20->dev, -+ "Problem erasing the chip.\n"); -+ release_firmware(fw_entry); -+ ret = -EPROBE_DEFER; -+ goto bad; -+ } -+ if (erase_only) { -+ dev_err(apalis_tk1_k20->dev, -+ "Chip fully erased.\n"); -+ release_firmware(fw_entry); -+ ret = -EIO; -+ goto bad; -+ } -+ if (apalis_tk1_k20_flash_chip_ezport(apalis_tk1_k20) < 0) { -+ dev_err(apalis_tk1_k20->dev, -+ "Problem flashing new firmware.\n"); -+ release_firmware(fw_entry); -+ ret = -EPROBE_DEFER; -+ goto bad; -+ } -+ } -+ if (fw_entry != NULL) -+ release_firmware(fw_entry); -+ -+ msleep(10); -+ apalis_tk1_k20_reset_chip(apalis_tk1_k20); -+ msleep(10); -+ -+ ret = apalis_tk1_k20_reg_read(apalis_tk1_k20, APALIS_TK1_K20_REVREG, -+ &revision); -+#endif /* CONFIG_APALIS_TK1_K20_EZP */ -+ -+ if (ret) { -+ dev_err(apalis_tk1_k20->dev, "Device is not answering.\n"); -+ goto bad; -+ } -+ -+ if (revision != APALIS_TK1_K20_FW_VER) { -+ dev_err(apalis_tk1_k20->dev, -+ "Unsupported firmware version %d.%d.\n", -+ ((revision & 0xF0) >> 4), (revision & 0x0F)); -+ ret = -ENOTSUPP; -+ goto bad; -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(apalis_tk1_k20->irqs); i++) { -+ apalis_tk1_k20->irqs[i].reg_offset = i / -+ APALIS_TK1_K20_IRQ_PER_REG; -+ apalis_tk1_k20->irqs[i].mask = BIT(i % -+ APALIS_TK1_K20_IRQ_PER_REG); -+ } -+ -+ apalis_tk1_k20->irq_chip.name = dev_name(dev); -+ apalis_tk1_k20->irq_chip.status_base = APALIS_TK1_K20_IRQREG; -+ apalis_tk1_k20->irq_chip.mask_base = APALIS_TK1_K20_MSQREG; -+ apalis_tk1_k20->irq_chip.ack_base = 0; -+ apalis_tk1_k20->irq_chip.irq_reg_stride = 0; -+ apalis_tk1_k20->irq_chip.num_regs = APALIS_TK1_K20_IRQ_REG_CNT; -+ apalis_tk1_k20->irq_chip.irqs = apalis_tk1_k20->irqs; -+ apalis_tk1_k20->irq_chip.num_irqs = ARRAY_SIZE(apalis_tk1_k20->irqs); -+ -+ ret = regmap_add_irq_chip(apalis_tk1_k20->regmap, apalis_tk1_k20->irq, -+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING | -+ IRQF_TRIGGER_RISING, 0, &apalis_tk1_k20->irq_chip, -+ &apalis_tk1_k20->irq_data); -+ if (ret) -+ goto bad; -+ -+ mutex_init(&apalis_tk1_k20->lock); -+ -+ if (apalis_tk1_k20_probe_flags_dt(apalis_tk1_k20) < 0 && pdata) -+ apalis_tk1_k20->flags = pdata->flags; -+ -+ if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_CAN) { -+ apalis_tk1_k20->can0_irq = irq_of_parse_and_map( -+ apalis_tk1_k20->dev->of_node, 1); -+ apalis_tk1_k20->can1_irq = irq_of_parse_and_map( -+ apalis_tk1_k20->dev->of_node, 2); -+ if (apalis_tk1_k20->can0_irq == 0 || -+ apalis_tk1_k20->can1_irq == 0) { -+ apalis_tk1_k20->flags &= ~APALIS_TK1_K20_USES_CAN; -+ dev_err(apalis_tk1_k20->dev, -+ "Missing CAN interrupts.\n"); -+ } -+ } -+ -+ if (pdata) { -+ if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_TSC) -+ apalis_tk1_k20_add_subdevice_pdata(apalis_tk1_k20, -+ "apalis-tk1-k20-ts", -+ &pdata->touch, sizeof(pdata->touch)); -+ -+ if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_ADC) -+ apalis_tk1_k20_add_subdevice_pdata(apalis_tk1_k20, -+ "apalis-tk1-k20-adc", -+ &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, -+ "apalis-tk1-k20-can", -+ &pdata->can0, sizeof(pdata->can0)); -+ apalis_tk1_k20_add_subdevice_pdata(apalis_tk1_k20, -+ "apalis-tk1-k20-can", -+ &pdata->can1, sizeof(pdata->can1)); -+ } -+ if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_GPIO) -+ apalis_tk1_k20_add_subdevice_pdata(apalis_tk1_k20, -+ "apalis-tk1-k20-gpio", -+ &pdata->gpio, sizeof(pdata->gpio)); -+ } else { -+ if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_TSC) -+ apalis_tk1_k20_add_subdevice(apalis_tk1_k20, -+ "apalis-tk1-k20-ts"); -+ -+ if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_ADC) -+ apalis_tk1_k20_add_subdevice(apalis_tk1_k20, -+ "apalis-tk1-k20-adc"); -+ -+ if (apalis_tk1_k20->flags & APALIS_TK1_K20_USES_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) -+ apalis_tk1_k20_add_subdevice(apalis_tk1_k20, -+ "apalis-tk1-k20-gpio"); -+ } -+ -+ dev_info(apalis_tk1_k20->dev, "Apalis TK1 K20 MFD driver. " -+ "Firmware version %d.%d.\n", FW_MAJOR, FW_MINOR); -+ -+ return 0; -+ -+bad: -+ if (apalis_tk1_k20->ezpcs_gpio >= 0) -+ 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->int2_gpio >= 0) -+ gpio_free(apalis_tk1_k20->int2_gpio); -+ -+ return ret; -+} -+ -+ -+static int apalis_tk1_k20_spi_probe(struct spi_device *spi) -+{ -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20; -+ int ret; -+ -+ apalis_tk1_k20 = devm_kzalloc(&spi->dev, sizeof(*apalis_tk1_k20), -+ GFP_KERNEL); -+ if (!apalis_tk1_k20) -+ return -ENOMEM; -+ -+ dev_set_drvdata(&spi->dev, apalis_tk1_k20); -+ -+ spi->mode = SPI_MODE_1; -+ -+ apalis_tk1_k20->irq = spi->irq; -+ -+ 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); -+ if (ret) -+ return ret; -+ -+ apalis_tk1_k20->regmap = devm_regmap_init(&spi->dev, -+ ®map_apalis_tk1_k20_bus, &spi->dev, -+ &apalis_tk1_k20_regmap_spi_config); -+ if (IS_ERR(apalis_tk1_k20->regmap)) { -+ ret = PTR_ERR(apalis_tk1_k20->regmap); -+ dev_err(&spi->dev, "Failed to initialize regmap: %d\n", ret); -+ return ret; -+ } -+ -+ return apalis_tk1_k20_dev_init(&spi->dev); -+} -+ -+static int apalis_tk1_k20_spi_remove(struct spi_device *spi) -+{ -+ struct apalis_tk1_k20_regmap *apalis_tk1_k20 = -+ dev_get_drvdata(&spi->dev); -+ -+ if (apalis_tk1_k20->ezpcs_gpio >= 0) -+ 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->int2_gpio >= 0) -+ gpio_free(apalis_tk1_k20->int2_gpio); -+ -+ kfree(spi->controller_data); -+ spi->controller_data = NULL; -+ -+ mfd_remove_devices(&spi->dev); -+ regmap_del_irq_chip(apalis_tk1_k20->irq, apalis_tk1_k20->irq_data); -+ mutex_destroy(&apalis_tk1_k20->lock); -+ -+ return 0; -+} -+ -+static struct spi_driver apalis_tk1_k20_spi_driver = { -+ .id_table = apalis_tk1_k20_device_ids, -+ .driver = { -+ .name = "apalis-tk1-k20", -+ .of_match_table = apalis_tk1_k20_dt_ids, -+ }, -+ .probe = apalis_tk1_k20_spi_probe, -+ .remove = apalis_tk1_k20_spi_remove, -+}; -+ -+static int __init apalis_tk1_k20_init(void) -+{ -+ return spi_register_driver(&apalis_tk1_k20_spi_driver); -+} -+subsys_initcall(apalis_tk1_k20_init); -+ -+static void __exit apalis_tk1_k20_exit(void) -+{ -+ spi_unregister_driver(&apalis_tk1_k20_spi_driver); -+} -+module_exit(apalis_tk1_k20_exit); -+ -+MODULE_DESCRIPTION("MFD driver for Kinetis MK20DN512 MCU on Apalis TK1"); -+MODULE_AUTHOR("Dominik Sliwa <dominik.sliwa@toradex.com>"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig -index ac4ff394bc56..16b2d8e2d16a 100644 ---- a/drivers/net/can/Kconfig -+++ b/drivers/net/can/Kconfig -@@ -88,6 +88,12 @@ config CAN_AT91 - This is a driver for the SoC CAN controller in Atmel's AT91SAM9263 - and AT91SAM9X5 processors. - -+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 02b8ed794564..80ca9eba724a 100644 ---- a/drivers/net/can/Makefile -+++ b/drivers/net/can/Makefile -@@ -19,6 +19,7 @@ obj-y += usb/ - obj-y += softing/ - - obj-$(CONFIG_CAN_AT91) += at91_can.o -+obj-$(CONFIG_CAN_APALIS_TK1_K20) += apalis-tk1-k20-can.o - obj-$(CONFIG_CAN_BFIN) += bfin_can.o - obj-$(CONFIG_CAN_CC770) += cc770/ - obj-$(CONFIG_CAN_C_CAN) += c_can/ -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..e24adbb35dfd ---- /dev/null -+++ b/drivers/net/can/apalis-tk1-k20-can.c -@@ -0,0 +1,817 @@ -+/* -+ * Copyright 2016-2017 Toradex AG -+ * Dominik Sliwa <dominik.sliwa@toradex.com> -+ * -+ * CAN bus driver for Apalis TK1 K20 CAN Controller over MFD device -+ * based on MCP251x CAN driver -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. -+ */ -+ -+#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> -+ -+/* 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 MB_DLC_OFF 4 -+#define MB_EID_OFF 0 -+#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_INTEN BIT(2) -+#define CANINTF_RX BIT(3) -+#define CANINTF_TX BIT(4) -+#define CANINTF_ERR BIT(5) -+#define CANCTRL_INTMASK (CANINTF_RX | CANINTF_TX | CANINTF_ERR) -+ -+#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 -+ + CAN_HEADER_MAX_LEN, tx_buf_idx); -+} -+ -+static void apalis_tk1_k20_can_hw_rx(struct net_device *net, int buf_idx) -+{ -+ int i = 0; -+ struct apalis_tk1_k20_priv *priv = netdev_priv(net); -+ struct sk_buff *skb; -+ struct can_frame *frame; -+ u8 buf[CAN_TRANSFER_BUF_LEN * APALIS_TK1_MAX_CAN_DMA_XREF]; -+ u32 frame_available = 0; -+ -+ apalis_tk1_k20_lock(priv->apalis_tk1_k20); -+ apalis_tk1_k20_reg_read(priv->apalis_tk1_k20, -+ APALIS_TK1_K20_CAN_IN_BUF_CNT -+ + APALIS_TK1_K20_CAN_DEV_OFFSET( -+ priv->pdata->id), &frame_available); -+ frame_available = min(frame_available, APALIS_TK1_MAX_CAN_DMA_XREF); -+ apalis_tk1_k20_reg_read_bulk(priv->apalis_tk1_k20, -+ APALIS_TK1_K20_CAN_IN_BUF -+ + APALIS_TK1_K20_CAN_DEV_OFFSET( -+ priv->pdata->id), buf, -+ CAN_TRANSFER_BUF_LEN * frame_available); -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ -+ for (i = 0; i < frame_available; i++) { -+ skb = alloc_can_skb(priv->net, &frame); -+ if (!skb) { -+ dev_err(&net->dev, "cannot allocate RX skb\n"); -+ priv->net->stats.rx_dropped++; -+ return; -+ } -+ memcpy(&frame->can_id, &buf[i * CAN_TRANSFER_BUF_LEN] -+ + MB_EID_OFF, MB_EID_LEN); -+ /* Data length */ -+ frame->can_dlc = get_can_dlc(buf[i * CAN_TRANSFER_BUF_LEN -+ + MB_DLC_OFF]); -+ memcpy(frame->data, &buf[i * CAN_TRANSFER_BUF_LEN] -+ + 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; -+ -+ if ((bt->bitrate / APALIS_TK1_CAN_CLK_UNIT) > 0xFF) -+ return -EINVAL; -+ -+ 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_CAN_CLK_UNIT) & 0xFF); -+ 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_seg1 & 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_dbg(priv->apalis_tk1_k20->dev, "Setting CAN%d bitrate " \ -+ "to %d (0x%X * 6.25kHz)\n", priv->pdata->id, bt->bitrate, -+ (bt->bitrate / APALIS_TK1_CAN_CLK_UNIT) & 0xFF); -+ dev_dbg(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_dbg(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); -+ apalis_tk1_k20_lock(priv->apalis_tk1_k20); -+ 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; -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ 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); -+ apalis_tk1_k20_lock(priv->apalis_tk1_k20); -+ 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 */ -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ 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); -+ apalis_tk1_k20_lock(priv->apalis_tk1_k20); -+ 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; -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ 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; -+ -+ mutex_lock(&priv->apalis_tk1_k20_can_lock); -+ while (!priv->force_quit) { -+ enum can_state new_state = CAN_STATE_ERROR_ACTIVE; -+ 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; -+ } -+ -+ intf &= CANCTRL_INTMASK; -+ /* receive */ -+ if (intf & CANINTF_RX) -+ apalis_tk1_k20_can_hw_rx(net, 0); -+ -+ /* any error interrupt we need to clear? */ -+ if (intf & CANINTF_ERR) -+ clear_intf |= intf & CANINTF_ERR; -+ apalis_tk1_k20_lock(priv->apalis_tk1_k20); -+ if (clear_intf) -+ ret = apalis_tk1_k20_reg_write(priv->apalis_tk1_k20, -+ APALIS_TK1_K20_CANREG_CLR -+ + APALIS_TK1_K20_CAN_DEV_OFFSET( -+ priv->pdata->id),clear_intf); -+ if (ret) { -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ dev_err(&net->dev, "Communication error\n"); -+ break; -+ } -+ -+ /* Update can state */ -+ if (intf & CANINTF_ERR) { -+ 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; -+ } -+ 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; -+ } -+ } -+ else { -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ } -+ -+ /* 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); -+ if (!(intf & (CANINTF_RX | CANINTF_ERR))) -+ break; -+ } -+ } -+ 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); -+ apalis_tk1_k20_unlock(priv->apalis_tk1_k20); -+ 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: -+ 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.clock.freq = 8000000; -+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | -+ CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; -+ priv->net = net; -+ priv->pdata = pdata; -+ priv->apalis_tk1_k20 = dev_get_drvdata(pdev->dev.parent); -+ -+ 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 = apalis_tk1_k20_can_remove, -+ .probe = apalis_tk1_k20_can_probe, -+ .driver = { -+ .name = DEVICE_NAME, -+#ifdef CONFIG_PM_SLEEP -+ .pm = &apalis_tk1_k20_can_pm_ops, -+#endif -+ }, -+}; -+ -+module_platform_driver(apalis_tk1_k20_can_driver); -+ -+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/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c -index 44550182a4a3..329dd49724a1 100644 ---- a/drivers/spi/spi-tegra114.c -+++ b/drivers/spi/spi-tegra114.c -@@ -703,12 +703,6 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, - } else - tegra_spi_writel(tspi, command1, SPI_COMMAND1); - -- command1 |= SPI_CS_SW_HW; -- if (spi->mode & SPI_CS_HIGH) -- command1 |= SPI_CS_SS_VAL; -- else -- command1 &= ~SPI_CS_SS_VAL; -- - tegra_spi_writel(tspi, 0, SPI_COMMAND2); - } else { - command1 = tspi->command1_reg; -@@ -776,6 +770,7 @@ static int tegra_spi_setup(struct spi_device *spi) - - spin_lock_irqsave(&tspi->lock, flags); - val = tspi->def_command1_reg; -+ val |= SPI_CS_SEL(spi->chip_select); - if (spi->mode & SPI_CS_HIGH) - val &= ~SPI_CS_POL_INACTIVE(spi->chip_select); - else -diff --git a/include/linux/mfd/apalis-tk1-k20-api.h b/include/linux/mfd/apalis-tk1-k20-api.h -new file mode 100644 -index 000000000000..85bbf9f28ca4 ---- /dev/null -+++ b/include/linux/mfd/apalis-tk1-k20-api.h -@@ -0,0 +1,123 @@ -+/* -+ * Copyright 2016-2017 Toradex AG -+ * Dominik Sliwa <dominik.sliwa@toradex.com> -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. -+ */ -+ -+#ifndef __LINUX_MFD_APALIS_TK1_K20_API_H -+#define __LINUX_MFD_APALIS_TK1_K20_API_H -+ -+/* Commands and registers used in SPI communication */ -+ -+/* Commands*/ -+#define APALIS_TK1_K20_READ_INST 0x0F -+#define APALIS_TK1_K20_WRITE_INST 0xF0 -+#define APALIS_TK1_K20_BULK_WRITE_INST 0x3C -+#define APALIS_TK1_K20_BULK_READ_INST 0xC3 -+ -+#define APALIS_TK1_K20_MAX_BULK 250u -+#define APALIS_TK1_K20_HEADER 4u -+ -+/* General registers*/ -+#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 RO(reset of read) */ -+#define APALIS_TK1_K20_CTRREG 0x03 /* general control register RW */ -+#define APALIS_TK1_K20_MSQREG 0x04 /* IRQ mask register RW */ -+ -+/* 0x05-0x0F Reserved */ -+ -+/* CAN Registers */ -+#define APALIS_TK1_K20_CANREG 0x10 /* CAN0 control & status register RW */ -+#define APALIS_TK1_K20_CANREG_CLR 0x11 /* CAN0 CANREG clear register WO */ -+#define APALIS_TK1_K20_CANERR 0x12 /* CAN0 error register RW */ -+#define APALIS_TK1_K20_CAN_BAUD_REG 0x13 /* CAN0 baud set register RW */ -+#define APALIS_TK1_K20_CAN_BIT_1 0x14 /* CAN0 bit timing register 1 RW */ -+#define APALIS_TK1_K20_CAN_BIT_2 0x15 /* CAN0 bit timing register 2 RW */ -+#define APALIS_TK1_K20_CAN_IN_BUF_CNT 0x16 /* CAN0 IN received data count RO */ -+#define APALIS_TK1_K20_CAN_IN_BUF 0x17 /* CAN0 IN RO */ -+/* buffer size is 13 bytes */ -+#define APALIS_TK1_K20_CAN_IN_BUF_END 0x23 /* CAN0 IN RO */ -+#define APALIS_TK1_K20_CAN_OUT_BUF 0x24 /* 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_OFFSET 0x30 -+#define APALIS_TK1_K20_CAN_DEV_OFFSET(x) (x ? APALIS_TK1_K20_CAN_OFFSET : 0) -+ -+/* 0x30-0x3F Reserved */ -+/* 0x40-0x62 CAN1 registers same layout as CAN0*/ -+/* 0x63-0x6F Reserved */ -+ -+/* ADC Registers */ -+#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 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 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-0xFC Reserved */ -+#define APALIS_TK1_K20_LAST_REG 0xFD -+#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 -+#define APALIS_TK1_K20_CAN1_IRQ 2 -+#define APALIS_TK1_K20_ADC_IRQ 3 -+#define APALIS_TK1_K20_TSC_IRQ 4 -+#define APALIS_TK1_K20_GPIO_IRQ 5 -+ -+#define APALIS_TK1_K20_FW_VER 0x10 -+ -+#define FW_MINOR (APALIS_TK1_K20_FW_VER & 0x0F) -+#define FW_MAJOR ((APALIS_TK1_K20_FW_VER & 0xF0) >> 4) -+ -+#define TK1_K20_SENTINEL 0x55 -+#define TK1_K20_INVAL 0xAA -+ -+#define APALIS_TK1_K20_NUMREGS 0x3f -+#define APALIS_TK1_K20_IRQ_REG_CNT 1 -+#define APALIS_TK1_K20_IRQ_PER_REG 8 -+ -+#define APALIS_TK1_CAN_CLK_UNIT 6250 -+ -+#define APALIS_TK1_MAX_CAN_DMA_XREF 19u -+ -+#endif /* ifndef __LINUX_MFD_APALIS_TK1_K20_API_H */ -diff --git a/include/linux/mfd/apalis-tk1-k20.h b/include/linux/mfd/apalis-tk1-k20.h -new file mode 100644 -index 000000000000..6d9e42b6002e ---- /dev/null -+++ b/include/linux/mfd/apalis-tk1-k20.h -@@ -0,0 +1,114 @@ -+/* -+ * Copyright 2016-2017 Toradex AG -+ * Dominik Sliwa <dominik.sliwa@toradex.com> -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. -+ */ -+ -+#ifndef __LINUX_MFD_APALIS_TK1_K20_H -+#define __LINUX_MFD_APALIS_TK1_K20_H -+ -+#include <linux/interrupt.h> -+#include <linux/mutex.h> -+#include <linux/regmap.h> -+#include <linux/mfd/apalis-tk1-k20-api.h> -+ -+#define APALIS_TK1_MAX_RETRY_CNT 4 -+ -+#define APALIS_TK1_K20_MAX_SPI_SPEED 6120000 -+ -+struct apalis_tk1_k20_regmap { -+ struct regmap *regmap; -+ -+ struct device *dev; -+ -+ struct regmap_irq irqs[APALIS_TK1_K20_IRQ_REG_CNT * APALIS_TK1_K20_IRQ_PER_REG]; -+ struct regmap_irq_chip irq_chip; -+ struct regmap_irq_chip_data *irq_data; -+ int can0_irq; -+ int can1_irq; -+ -+ struct mutex lock; -+ int irq; -+ int flags; -+ -+ int ezpcs_gpio; -+ int reset_gpio; -+ int appcs_gpio; -+ int int2_gpio; -+}; -+ -+void apalis_tk1_k20_lock(struct apalis_tk1_k20_regmap *apalis_tk1_k20); -+void apalis_tk1_k20_unlock(struct apalis_tk1_k20_regmap *apalis_tk1_k20); -+ -+int apalis_tk1_k20_reg_read(struct apalis_tk1_k20_regmap *apalis_tk1_k20, unsigned int offset, u32 *val); -+int apalis_tk1_k20_reg_write(struct apalis_tk1_k20_regmap *apalis_tk1_k20, unsigned int offset, u32 val); -+int apalis_tk1_k20_reg_read_bulk(struct apalis_tk1_k20_regmap *apalis_tk1_k20, unsigned int offset, -+ uint8_t *val, size_t size); -+int apalis_tk1_k20_reg_write_bulk(struct apalis_tk1_k20_regmap *apalis_tk1_k20, unsigned int offset, -+ uint8_t *val, size_t size); -+int apalis_tk1_k20_reg_rmw(struct apalis_tk1_k20_regmap *apalis_tk1_k20, unsigned int offset, -+ u32 mask, u32 val); -+ -+int apalis_tk1_k20_irq_mask(struct apalis_tk1_k20_regmap *apalis_tk1_k20, int irq); -+int apalis_tk1_k20_irq_unmask(struct apalis_tk1_k20_regmap *apalis_tk1_k20, int irq); -+int apalis_tk1_k20_irq_request(struct apalis_tk1_k20_regmap *apalis_tk1_k20, int irq, -+ irq_handler_t handler, const char *name, void *dev); -+int apalis_tk1_k20_irq_free(struct apalis_tk1_k20_regmap *apalis_tk1_k20, int irq, void *dev); -+ -+int apalis_tk1_k20_irq_status(struct apalis_tk1_k20_regmap *apalis_tk1_k20, int irq, -+ int *enabled, int *pending); -+ -+int apalis_tk1_k20_get_flags(struct apalis_tk1_k20_regmap *apalis_tk1_k20); -+ -+struct apalis_tk1_k20_can_platform_data { -+ uint8_t id; -+ u16 status; -+}; -+ -+struct apalis_tk1_k20_tsc_platform_data { -+ u16 status; -+}; -+ -+struct apalis_tk1_k20_adc_platform_data { -+ u16 status; -+}; -+ -+struct apalis_tk1_k20_gpio_platform_data { -+ u16 status; -+}; -+ -+#define APALIS_TK1_K20_USES_TSC BIT(0) -+#define APALIS_TK1_K20_USES_ADC BIT(1) -+#define APALIS_TK1_K20_USES_CAN BIT(2) -+#define APALIS_TK1_K20_USES_GPIO BIT(3) -+ -+struct apalis_tk1_k20_platform_data { -+ unsigned int flags; -+ -+ struct apalis_tk1_k20_tsc_platform_data touch; -+ struct apalis_tk1_k20_adc_platform_data adc; -+ struct apalis_tk1_k20_can_platform_data can0; -+ struct apalis_tk1_k20_can_platform_data can1; -+ struct apalis_tk1_k20_gpio_platform_data gpio; -+ -+ int ezpcs_gpio; -+ int reset_gpio; -+ int appcs_gpio; -+ int int2_gpio; -+}; -+ -+#define APALIS_TK1_K20_ADC_CHANNELS 4 -+#define APALIS_TK1_K20_ADC_BITS 16 -+#define APALIS_TK1_K20_VADC_MILI 3300 -+ -+enum apalis_tk1_k20_adc_id { -+ APALIS_TK1_K20_ADC1, -+ APALIS_TK1_K20_ADC2, -+ APALIS_TK1_K20_ADC3, -+ APALIS_TK1_K20_ADC4 -+}; -+ -+#endif /* ifndef __LINUX_MFD_APALIS_TK1_K20_H */ --- -2.14.4 - |