aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'sys-kernel/armv7multi-sources/files/4.14.5-udooqdl-add-arduino-manager-driv.patch')
-rw-r--r--sys-kernel/armv7multi-sources/files/4.14.5-udooqdl-add-arduino-manager-driv.patch511
1 files changed, 511 insertions, 0 deletions
diff --git a/sys-kernel/armv7multi-sources/files/4.14.5-udooqdl-add-arduino-manager-driv.patch b/sys-kernel/armv7multi-sources/files/4.14.5-udooqdl-add-arduino-manager-driv.patch
new file mode 100644
index 0000000..dec36b2
--- /dev/null
+++ b/sys-kernel/armv7multi-sources/files/4.14.5-udooqdl-add-arduino-manager-driv.patch
@@ -0,0 +1,511 @@
+From a35b9dfc80338cf2049c75b0f4af22776001d5d3 Mon Sep 17 00:00:00 2001
+From: Steve Arnold <nerdboy@gentoo.org>
+Date: Fri, 15 Dec 2017 16:43:22 -0800
+Subject: [PATCH] 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 e28f90005130..fd404d3299a5 100644
+--- a/arch/arm/boot/dts/imx6qdl-udoo.dtsi
++++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi
+@@ -89,6 +89,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 {
+@@ -200,6 +211,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 f1c4c891cda3..afd54fdc983d 100644
+--- a/drivers/misc/Kconfig
++++ b/drivers/misc/Kconfig
+@@ -536,6 +536,13 @@ config TIEQEP
+ To compile this driver as a module, choose M here: the module
+ will be called tieqep.
+
++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 3225f1e37dfc..b68828f424c4 100644
+--- a/drivers/misc/Makefile
++++ b/drivers/misc/Makefile
+@@ -57,6 +57,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_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
+ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
+ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
+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.15.0
+