build/patch/kernel/archive/imx6-5.10/0008-ARM-dts-driver-imx6-udooqdl-add-arduino-manager-driv.patch

512 lines
15 KiB
Diff
Raw Normal View History

From 9a4d8e886600c7c330590a2435dd74eb16d480ce Mon Sep 17 00:00:00 2001
From: Steve Arnold <nerdboy@gentoo.org>
Date: Fri, 15 Dec 2017 16:43:22 -0800
Subject: [PATCH 8/9] ARM: dts,driver: imx6,udooqdl: add arduino manager driver
and update dts
* note this is required to upload sketches to sam3 from arduino IDE
Signed-off-by: Steve Arnold <nerdboy@gentoo.org>
---
arch/arm/boot/dts/imx6qdl-udoo.dtsi | 20 ++
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/udoo_ard.c | 417 ++++++++++++++++++++++++++++
4 files changed, 445 insertions(+)
create mode 100755 drivers/misc/udoo_ard.c
diff --git a/arch/arm/boot/dts/imx6qdl-udoo.dtsi b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
index 4781a9e04338..554f601eb72a 100644
--- a/arch/arm/boot/dts/imx6qdl-udoo.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
@@ -84,6 +84,17 @@
mux-int-port = <1>;
mux-ext-port = <6>;
};
+
+ udoo_ard: udoo_ard_manager {
+ compatible = "udoo,imx6q-udoo-ard";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_udooard>;
+ bossac-clk-gpio = <&gpio6 3 0>;
+ bossac-dat-gpio = <&gpio5 18 0>;
+ bossac-erase-gpio = <&gpio4 21 0>;
+ bossac-reset-gpio = <&gpio1 0 0>;
+ status = "okay";
+ };
};
&fec {
@@ -201,6 +212,15 @@
>;
};
+ pinctrl_udooard: udooardgrp {
+ fsl,pins = <
+ MX6QDL_PAD_DISP0_DAT0__GPIO4_IO21 0x80000000
+ MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03 0x80000000
+ MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18 0x80000000
+ MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x80000000
+ >;
+ };
+
pinctrl_usdhc3: usdhc3grp {
fsl,pins = <
MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 2cf9db44e4b2..f4616fc61808 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -487,6 +487,13 @@ config PVPANIC
a paravirtualized device provided by QEMU; it lets a virtual machine
(guest) communicate panic events to the host.
+config UDOO_ARD
+ tristate "UDOO-Arduino erase/reset Driver"
+ default y
+ help
+ This driver is used to erase and reset arduino board via command sent
+ over USB-to-SERIAL connection.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index dcc801e8834e..13e132dd62c9 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,6 +54,7 @@ obj-y += cape/
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
+obj-$(CONFIG_UDOO_ARD) += udoo_ard.o
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
obj-$(CONFIG_OCXL) += ocxl/
obj-y += cardreader/
diff --git a/drivers/misc/udoo_ard.c b/drivers/misc/udoo_ard.c
new file mode 100755
index 000000000000..2210738e09c0
--- /dev/null
+++ b/drivers/misc/udoo_ard.c
@@ -0,0 +1,417 @@
+/*
+ * udoo_ard.c
+ * UDOO quad/dual Arduino flash erase / CPU resetter
+ *
+ * Copyright (C) 2013-2015 Aidilab srl
+ * Author: UDOO Team <social@udoo.org>
+ * Author: Giuseppe Pagano <giuseppe.pagano@seco.com>
+ * Author: Francesco Montefoschi <francesco.monte@gmail.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/sched/clock.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/uaccess.h>
+
+#define DRIVER_NAME "udoo_ard"
+#define PINCTRL_DEFAULT "default"
+#define AUTH_TOKEN 0x5A5A
+#define MAX_MSEC_SINCE_LAST_IRQ 400
+#define GRAY_TIME_BETWEEN_RESET 10000 // In this time we can't accept new erase/reset code
+
+static struct workqueue_struct *erase_reset_wq;
+typedef struct {
+ struct work_struct erase_reset_work;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default;
+ int step;
+ int cmdcode;
+ int erase_reset_lock;
+ int gpio_bossac_clk;
+ int gpio_bossac_dat;
+ int gpio_ard_erase;
+ int gpio_ard_reset;
+ unsigned long last_int_time_in_ns;
+ unsigned long last_int_time_in_sec;
+} erase_reset_work_t;
+
+erase_reset_work_t *work;
+static u32 origTX, origRX; // original UART4 TX/RX pad control registers
+static int major; // for /dev/udoo_ard
+static struct class *udoo_class;
+
+static struct platform_device_id udoo_ard_devtype[] = {
+ {
+ /* keep it for coldfire */
+ .name = DRIVER_NAME,
+ .driver_data = 0,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, udoo_ard_devtype);
+
+static const struct of_device_id udoo_ard_dt_ids[] = {
+ { .compatible = "udoo,imx6q-udoo-ard", .data = &udoo_ard_devtype[0], },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, udoo_ard_dt_ids);
+
+static void disable_serial(void)
+{
+ u32 addrTX;
+ void __iomem *_addrTX;
+
+ printk("[bossac] Disable UART4 serial port.\n");
+
+ addrTX = 0x20E01F8;
+ _addrTX = ioremap(addrTX, 8);
+
+ origTX = __raw_readl(_addrTX);
+ origRX = __raw_readl(_addrTX + 0x4);
+
+ __raw_writel(0x15, _addrTX);
+ __raw_writel(0x15, _addrTX + 0x4);
+
+ iounmap(_addrTX);
+}
+
+static void enable_serial(void)
+{
+ u32 addrTX;
+ void __iomem *_addrTX;
+
+ printk("[bossac] Enable UART4 serial port.\n");
+
+ addrTX = 0x20E01F8;
+ _addrTX = ioremap(addrTX, 8);
+
+ __raw_writel(origTX, _addrTX);
+ __raw_writel(origRX, _addrTX + 0x4);
+
+ iounmap(_addrTX);
+}
+
+static void erase_reset(void)
+{
+ printk("[bossac] UDOO ERASE and RESET on Sam3x started.\n");
+
+ gpio_direction_input(work->gpio_ard_erase);
+ gpio_set_value(work->gpio_ard_reset, 1);
+ msleep(1);
+
+ gpio_direction_output(work->gpio_ard_erase, 1);
+ msleep(300);
+ gpio_direction_input(work->gpio_ard_erase);
+
+ msleep(10);
+ gpio_set_value(work->gpio_ard_reset, 0);
+
+ msleep(80);
+ gpio_set_value(work->gpio_ard_reset, 1);
+
+ printk("[bossac] UDOO ERASE and RESET on Sam3x EXECUTED.\n");
+}
+
+static void shutdown_sam3x(void)
+{
+ printk("[bossac] RESET on Sam3x.\n");
+
+ gpio_set_value(work->gpio_ard_reset, 0);
+}
+
+static void erase_reset_wq_function( struct work_struct *work2)
+{
+ disable_serial();
+ erase_reset();
+ msleep(GRAY_TIME_BETWEEN_RESET);
+
+ work->erase_reset_lock = 0;
+}
+
+/*
+ * Called everytime the gpio_bossac_clk signal toggles.
+ * If the auth token (16 bit) is found, we look for the command code (4 bit).
+ * The code 0x0F is sent by Bossac to trigger an erase/reset (to achieve this,
+ * erase_reset_wq is scheduled). Before starting to program the flash, we disable
+ * the UART4 serial port, otherwise there is too noise on the serial lines (the
+ * programming port and UART4 port are connected together, see hw schematics).
+ * When Bossac finishes to flash/verify, the code 0x00 is sent which re-enables
+ * the UART4 port.
+ */
+static irqreturn_t udoo_bossac_req(int irq, void *dev_id)
+{
+ int retval, auth_bit, expected_bit, msec_since_last_irq;
+ u64 nowsec;
+ unsigned long rem_nsec;
+ erase_reset_work_t *erase_reset_work;
+
+ auth_bit = 0;
+ if (gpio_get_value(work->gpio_bossac_dat) != 0x0) {
+ auth_bit = 1;
+ }
+
+ erase_reset_work = (erase_reset_work_t *)work;
+
+ nowsec = local_clock();
+ rem_nsec = do_div(nowsec, 1000000000) ;
+ msec_since_last_irq = (((unsigned long)nowsec * 1000) + rem_nsec/1000000 ) - (((unsigned long)erase_reset_work->last_int_time_in_sec * 1000) + erase_reset_work->last_int_time_in_ns/1000000);
+
+ if (msec_since_last_irq > MAX_MSEC_SINCE_LAST_IRQ) {
+ erase_reset_work->step = 0;
+#ifdef DEBUG
+ printk("[bossac] Reset authentication timeout!\n");
+#endif
+ }
+
+#ifdef DEBUG
+ printk("[bossac] STEP %d -> 0x%d \n", erase_reset_work->step, auth_bit);
+#endif
+ erase_reset_work->last_int_time_in_ns = rem_nsec;
+ erase_reset_work->last_int_time_in_sec = nowsec;
+
+ if ( erase_reset_work->step < 16 ) { // Authenticating received token bit.
+ expected_bit = (( AUTH_TOKEN >> erase_reset_work->step ) & 0x01 );
+ if ( auth_bit == expected_bit ) {
+ erase_reset_work->step = erase_reset_work->step + 1;
+ } else {
+ erase_reset_work->step = 0;
+ }
+ } else { // Passed all authentication step. Receiving command code.
+ erase_reset_work->cmdcode = erase_reset_work->cmdcode | (auth_bit << (erase_reset_work->step - 16));
+ erase_reset_work->step = erase_reset_work->step + 1;
+ }
+
+#ifdef DEBUG
+ printk("erase_reset_work->erase_reset_lock = %d \n", erase_reset_work->erase_reset_lock);
+#endif
+ if ( erase_reset_work->step == 20 ) { // Passed authentication and code acquiring step.
+#ifdef DEBUG
+ printk("[bossac] Received code = 0x%04x \n", erase_reset_work->cmdcode);
+#endif
+ if (erase_reset_work->cmdcode == 0xF) {
+ if (erase_reset_work->erase_reset_lock == 0) {
+ erase_reset_work->erase_reset_lock = 1;
+ retval = queue_work( erase_reset_wq, (struct work_struct *)work );
+ } else {
+#ifdef DEBUG
+ printk("Erase and reset operation already in progress. Do nothing.\n");
+#endif
+ }
+ } else {
+ enable_serial();
+ }
+ erase_reset_work->step = 0;
+ erase_reset_work->cmdcode = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Takes control of clock, data, erase, reset GPIOs.
+ */
+static int gpio_setup(void)
+{
+ int ret;
+
+ ret = gpio_request(work->gpio_bossac_clk, "BOSSA_CLK");
+ if (ret) {
+ printk(KERN_ERR "request BOSSA_CLK IRQ\n");
+ return -1;
+ } else {
+ gpio_direction_input(work->gpio_bossac_clk);
+ }
+
+ ret = gpio_request(work->gpio_bossac_dat, "BOSSA_DAT");
+ if (ret) {
+ printk(KERN_ERR "request BOSSA_DAT IRQ\n");
+ return -1;
+ } else {
+ gpio_direction_input(work->gpio_bossac_dat);
+ }
+
+ ret = gpio_request(work->gpio_ard_erase, "BOSSAC");
+ if (ret) {
+ printk(KERN_ERR "request GPIO FOR ARDUINO ERASE\n");
+ return -1;
+ } else {
+ gpio_direction_input(work->gpio_ard_erase);
+ }
+
+ ret = gpio_request(work->gpio_ard_reset, "BOSSAC");
+ if (ret) {
+ printk(KERN_ERR "request GPIO FOR ARDUINO RESET\n");
+ return -1;
+ } else {
+ gpio_direction_output(work->gpio_ard_reset, 1);
+ }
+
+ return 0;
+}
+
+static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t *off)
+{
+ char msg[10];
+ long res;
+
+ if (len > 10)
+ return -EINVAL;
+
+
+ res = copy_from_user(msg, buff, len);
+ if (res) {
+ return -EFAULT;
+ }
+ msg[len] = '\0';
+
+ if (strcmp(msg, "erase")==0) {
+ erase_reset();
+ } else if (strcmp(msg, "shutdown")==0) {
+ shutdown_sam3x();
+ } else if (strcmp(msg, "uartoff")==0) {
+ disable_serial();
+ } else if (strcmp(msg, "uarton")==0) {
+ enable_serial();
+ } else {
+ printk("[bossac] udoo_ard invalid operation! %s", msg);
+ }
+
+ return len;
+}
+
+static struct file_operations fops = {
+ .write = device_write,
+};
+
+/*
+ * If a fdt udoo_ard entry is found, we register an IRQ on bossac clock line
+ * and we create /dev/udoo_ard
+ */
+static int udoo_ard_probe(struct platform_device *pdev)
+{
+ int retval;
+ struct device *temp_class;
+ struct platform_device *bdev;
+ struct device_node *np;
+
+ bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
+ np = pdev->dev.of_node;
+
+ if (!np)
+ return -ENODEV;
+
+ work = (erase_reset_work_t *)kmalloc(sizeof(erase_reset_work_t), GFP_KERNEL);
+ if (work) {
+ work->gpio_ard_reset = of_get_named_gpio(np, "bossac-reset-gpio", 0);
+ work->gpio_ard_erase = of_get_named_gpio(np, "bossac-erase-gpio", 0);
+ work->gpio_bossac_clk = of_get_named_gpio(np, "bossac-clk-gpio", 0);
+ work->gpio_bossac_dat = of_get_named_gpio(np, "bossac-dat-gpio", 0);
+ work->pinctrl = devm_pinctrl_get(&pdev->dev);
+ work->pins_default = pinctrl_lookup_state(work->pinctrl, PINCTRL_DEFAULT);
+ } else {
+ printk("[bossac] Failed to allocate data structure.");
+ return -ENOMEM;
+ }
+
+ pinctrl_select_state(work->pinctrl, work->pins_default);
+ gpio_setup();
+
+ printk("[bossac] Registering IRQ %d for BOSSAC Arduino erase/reset operation\n", gpio_to_irq(work->gpio_bossac_clk));
+ retval = request_irq(gpio_to_irq(work->gpio_bossac_clk), udoo_bossac_req, IRQF_TRIGGER_FALLING, "UDOO", bdev);
+
+ major = register_chrdev(major, "udoo_ard", &fops);
+ if (major < 0) {
+ printk(KERN_ERR "[bossac] Cannot get major for UDOO Ard\n");
+ return -EBUSY;
+ }
+
+ udoo_class = class_create(THIS_MODULE, "udoo_ard");
+ if (IS_ERR(udoo_class)) {
+ return PTR_ERR(udoo_class);
+ }
+
+ temp_class = device_create(udoo_class, NULL, MKDEV(major, 0), NULL, "udoo_ard");
+ if (IS_ERR(temp_class)) {
+ return PTR_ERR(temp_class);
+ }
+
+ printk("[bossac] Created device file /dev/udoo_ard\n");
+
+ erase_reset_wq = create_workqueue("erase_reset_queue");
+ if (erase_reset_wq) {
+
+ /* Queue some work (item 1) */
+ if (work) {
+ INIT_WORK( (struct work_struct *)work, erase_reset_wq_function );
+ work->step = 1;
+ work->cmdcode = 0;
+ work->last_int_time_in_ns = 0;
+ work->last_int_time_in_sec = 0;
+ work->erase_reset_lock = 0;
+ // retval = queue_work( erase_reset_wq, (struct work_struct *)work );
+ }
+ }
+ return 0;
+}
+
+static int udoo_ard_remove(struct platform_device *pdev)
+{
+ printk("[bossac] Unloading UDOO ard driver.\n");
+ free_irq(gpio_to_irq(work->gpio_bossac_clk), NULL);
+
+ gpio_free(work->gpio_ard_reset);
+ gpio_free(work->gpio_ard_erase);
+ gpio_free(work->gpio_bossac_clk);
+ gpio_free(work->gpio_bossac_dat);
+
+ device_destroy(udoo_class, MKDEV(major, 0));
+ class_destroy(udoo_class);
+ unregister_chrdev(major, "udoo_ard");
+
+ return 0;
+}
+
+static struct platform_driver udoo_ard_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = udoo_ard_dt_ids,
+ },
+ .id_table = udoo_ard_devtype,
+ .probe = udoo_ard_probe,
+ .remove = udoo_ard_remove,
+};
+
+module_platform_driver(udoo_ard_driver);
+
+MODULE_ALIAS("platform:"DRIVER_NAME);
+MODULE_LICENSE("GPL");
--
2.20.1