Squashed commit of the following:
authorMiroslav Ondra <ondra@faster.cz>
Tue, 13 Aug 2019 10:36:27 +0000 (12:36 +0200)
committerMiroslav Ondra <ondra@faster.cz>
Tue, 13 Aug 2019 10:36:27 +0000 (12:36 +0200)
    add ci/cd support for amd64
    fix rtc-unipi for kernel > 4.17
    fix module version 1.23. fix ci/cd. add uSE_UNIPI_CPUFREQ_PATCH directive
    remove cpufreq management in unipi_spi_set_cs. Hangups with Iqrf spi

15 files changed:
.gitignore
Makefile
debian/clean
debian/control
debian/rules
debian/unipi-kernel-modules-dkms.dkms
debian/unipi-kernel-modules-dkms.install.in [deleted file]
modules/.dw485-unipi/8250_dwunipi.c [new file with mode: 0644]
modules/.dw485-unipi/Kconfig [new file with mode: 0644]
modules/.dw485-unipi/Makefile [new file with mode: 0644]
modules/rtc-unipi/Makefile [new file with mode: 0644]
modules/rtc-unipi/rtc-unipi.c [new file with mode: 0644]
modules/unipi/Makefile
modules/unipi/src/unipi_common.h
modules/unipi/src/unipi_spi.c

index 91818a1c56464e4238d948491faeb7e16598f92b..3152749ff78d39dde74f19d2db04945b5b3ded20 100644 (file)
@@ -5,6 +5,7 @@
 *.rc
 */*/bin/*
 !*/*/bin/.dummy
+/tmp/
 modules/unipi/tmp*
 modules/unipi/.tmp_versions
 modules/unipi/unipi.mod.c
index fb7e5a2d603c18b5a65c9ff88ecad904b0beec26..a3c8213cf1bc5a619bed4486a25b96ef7c44e099 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,7 @@
 # if necessary.
  
 MODULES_DIR_PATH = ${PWD}/modules/
-MODULES_LIST = unipi/
-TRANSFER_MODULE = unipi/
+MODULES_LIST = unipi/ rtc-unipi/
 
 .PHONY: default
 #default: symlink ;
@@ -22,15 +21,13 @@ install:
                cd ${MODULES_DIR_PATH}$$m; make modules_install INSTALL_MOD_PATH=${DESTDIR};\
                done
 
-clean: 
+dkms:
        for m in ${MODULES_LIST}; do\
-               cd ${MODULES_DIR_PATH}$$m; make clean;\
+               cd ${MODULES_DIR_PATH}$$m; make dkms INSTALL_MOD_PATH=${DESTDIR}/$$m;\
                done
 
-transfer:
-       cd ${MODULES_DIR_PATH}${TRANSFER_MODULE}; make transfer;
-       
-symlink:
+clean: 
        for m in ${MODULES_LIST}; do\
-               cd ${MODULES_DIR_PATH}$$m; make symlink;\
+               cd ${MODULES_DIR_PATH}$$m; make clean;\
                done
+
index e9022e4d3dd425aa162da4a90c235d30e14c5fab..c365c7ed6ab4880f04bb753ce37cd306e21abaca 100644 (file)
@@ -1,6 +1,7 @@
+debian/unipi-kernel-modules.changelog
 debian/neuron-kernel.install
 debian/unipi-kernel-modules-dkms.install
 debian/neuron-kernel.substvars
 debian/neuron-kernel.postinst.debhelper 
 debian/neuron-kernel.postrm.debhelper
-debian/neuron-kernel/
\ No newline at end of file
+debian/neuron-kernel/
index 1bfcf0941643c549cd26c69d7ee23c6238dd7088..1e87eb8d13da8f581091334b8dd9ddd25d96f02b 100644 (file)
@@ -16,7 +16,7 @@ Description: UniPi Neuron kernel modules
 
 Package: unipi-kernel-modules-dkms
 Architecture: linux-any
-Pre-Depends: raspberrypi-kernel-headers | linux-headers(>= 4.0)
+Pre-Depends: raspberrypi-kernel-headers | axon-kernel-headers(>= 4.0) | linux-headers(>=4.0)
 Depends: ${misc:Depends}, unipi-common, dkms
 Replaces: neuron-kernel
 Conflicts: neuron-kernel
@@ -25,3 +25,11 @@ Description: UniPi Neuron kernel modules - DKMS source
  used by internal boards in the UniPi Neuron/Axon controllers.
  Can be used with DKMS so that local kernel images are automatically
  built and installed every time relevant kernel packages are upgraded.
+
+Package: unipi-kernel-modules
+Architecture: arm64
+Depends: ${misc:Depends}, unipi-common, axon-kernel-image(=${AXON-KERNEL-VER})
+Description: UniPi Axon kernel modules
+ Binary kernel modules for UniPi Axon controller. Compiled for axon-kernel-image 
+ version. 
+
index 752aa42cd37c8365a8dcb0b842f1197cb41e8855..f7d37183ee0e3f2cb7369a919f70eecaaad1c13a 100755 (executable)
 #export DEB_CFLAGS_MAINT_APPEND  = -Wall -pedantic
 # package maintainers to append LDFLAGS
 #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
+export DEB_LDFLAGS_SET = -zrelro
 
 MOD_VERSION=$(shell dpkg-parsechangelog |grep ^Version:|cut -d ' ' -f 2)
 
+ifeq ($(shell dpkg-architecture -q DEB_HOST_ARCH),armhf)
 ifeq ($(shell dpkg-architecture -q DEB_BUILD_ARCH),armhf)
-
 RPI_FIRMWARE_VER = $(shell dpkg-query -f='$${Version}' -W raspberrypi-kernel-headers)
-LINUX_DIR_PATH = $(shell dpkg -L raspberrypi-kernel-headers | sed -n '/^\/lib\/modules\/.*-v7.*\/build$$/p')
+LINUX_DIR_PATH = $(shell dpkg -L raspberrypi-kernel-headers | sed -n '/^\/lib\/modules\/.*-v7.*\/build$$/p'|head -1)
 KERNEL_VERSION = $(subst /lib/modules/,,$(subst /build,,$(LINUX_DIR_PATH)))
-
 else
-
 .raspbian-versions:
        misc/check-raspbian
 
 include ./.raspbian-versions
 LINUX_DIR_PATH = ${PWD}/tmp/linux-raspberrypi-kernel_${RPI_FIRMWARE_VER}
+endif
+endif
 
+ifeq ($(shell dpkg-architecture -q DEB_HOST_ARCH),arm64)
+AXON_KERNEL_VER = $(shell dpkg-query -f='$${Version}' -W axon-kernel-headers)
+#AXON_KERNEL_VER = 4.14.111
+LINUX_DIR_PATH = $(shell dpkg -L axon-kernel-headers | sed -n '/^\/lib\/modules\/.*\/build$$/p')
+#LINUX_DIR_PATH = '/lib/modules/4.14.111/build'
+KERNEL_VERSION = $(subst /lib/modules/,,$(subst /build,,$(LINUX_DIR_PATH)))
 endif
 
+
 %:
        dh $@ --with dkms
 
@@ -38,35 +46,58 @@ endif
 override_dh_installmodules:
        DH_AUTOSCRIPTDIR=debian/autoscripts dh_installmodules
 
-
+ifeq ($(shell dpkg-architecture -q DEB_HOST_ARCH),armhf)
 override_dh_prep:
        @dh_prep --exclude=neuron-kernel.substvars
        @echo RPI-FIRMWARE-VER=${RPI_FIRMWARE_VER} >> debian/neuron-kernel.substvars
-       @sed "s/#MOD_VERSION#/$(MOD_VERSION)/g" debian/unipi-kernel-modules-dkms.install.in > debian/unipi-kernel-modules-dkms.install
        ( sed 's/)/.${RPI_FIRMWARE_VER})/;q' debian/changelog; \
          printf "\n  * Compiled for raspberrypi-kernel\n";\
          printf "\n -- auto-generator <info@unipi.technology>  %s\n\n" "`date -R`"; \
          cat debian/changelog;\
        ) >debian/neuron-kernel.changelog
+endif 
+ifeq ($(shell dpkg-architecture -q DEB_HOST_ARCH),arm64)
+override_dh_prep:
+       @dh_prep --exclude=unipi-kernel-modules.substvars
+       @echo AXON-KERNEL-VER=${AXON_KERNEL_VER} > debian/unipi-kernel-modules.substvars
+       ( sed 's/)/.${AXON_KERNEL_VER})/;q' debian/changelog; \
+         printf "\n  * Compiled for axon-kernel-image\n";\
+         printf "\n -- auto-generator <info@unipi.technology>  %s\n\n" "`date -R`"; \
+         cat debian/changelog;\
+       ) >debian/unipi-kernel-modules.changelog
+endif
 
+#ifeq ($(shell dpkg-architecture -q DEB_HOST_ARCH),arm64)
 override_dh_dkms:
        dh_dkms -V $(MOD_VERSION)
+       make dkms DESTDIR=$(PWD)/debian//unipi-kernel-modules-dkms/usr/src/unipi-$(MOD_VERSION)
+#endif
 
 #override_dh_build:
 
 ifneq ($(shell dpkg-architecture -q DEB_BUILD_ARCH),armhf)
+ifeq ($(shell dpkg-architecture -q DEB_HOST_ARCH),armhf)
 override_dh_auto_configure:
        misc/check-raspbian
        misc/current
 endif
+endif
 
 override_dh_auto_build:
        dh_auto_build -- CCPREFIX=${DEB_TARGET_GNU_TYPE}- \
                LINUX_DIR_PATH=$(LINUX_DIR_PATH) ARCH=${DEB_TARGET_ARCH_CPU}
 
+ifeq ($(shell dpkg-architecture -q DEB_HOST_ARCH),arm64)
+override_dh_auto_install:
+       dh_auto_install --destdir=debian/unipi-kernel-modules -- CCPREFIX=${DEB_TARGET_GNU_TYPE}- \
+               LINUX_DIR_PATH=$(LINUX_DIR_PATH) ARCH=${DEB_TARGET_ARCH_CPU}
+endif
+
+ifeq ($(shell dpkg-architecture -q DEB_HOST_ARCH),armhf)
 override_dh_auto_install:
        dh_auto_install --destdir=debian/neuron-kernel -- CCPREFIX=${DEB_TARGET_GNU_TYPE}- \
                LINUX_DIR_PATH=$(LINUX_DIR_PATH) ARCH=${DEB_TARGET_ARCH_CPU}
+endif
 
 
 #override_dh_gencontrol:
index e02b3b49a2b44562ce7a941fb99e950e10e7c0a2..b19708ef387fc44b274e38558d562f2e1ad63490 100644 (file)
@@ -1,10 +1,18 @@
+
 PACKAGE_NAME="unipi"
 PACKAGE_VERSION="#MODULE_VERSION#"
 
 BUILT_MODULE_NAME[0]="unipi"
-BUILT_MODULE_LOCATION[0]=""
+BUILT_MODULE_LOCATION[0]="unipi"
 DEST_MODULE_LOCATION[0]="/extra"
+BUILT_MODULE_NAME[1]="rtc-unipi"
+BUILT_MODULE_LOCATION[1]="rtc-unipi"
+DEST_MODULE_LOCATION[1]="/extra"
+
 AUTOINSTALL=yes
+REMAKE_INITRD=yes
 
-MAKE[0]="make -C $kernel_source_dir M=${dkms_tree}/unipi/${module_version}/build modules"
+MAKE[0]="make -C $kernel_source_dir M=${dkms_tree}/unipi/${module_version}/build/unipi modules;\
+        make -C $kernel_source_dir M=${dkms_tree}/unipi/${module_version}/build/rtc-unipi modules"
 CLEAN="make -C ${dkms_tree}/unipi/${module_version} clean; rm -rf ${dkms_tree}/unipi/${module_version}/build/{Module.*,modules.order,OBJ.*}"
+
diff --git a/debian/unipi-kernel-modules-dkms.install.in b/debian/unipi-kernel-modules-dkms.install.in
deleted file mode 100644 (file)
index fb99166..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-modules/unipi/src      usr/src/unipi-#MOD_VERSION#
-modules/unipi/Makefile usr/src/unipi-#MOD_VERSION#
diff --git a/modules/.dw485-unipi/8250_dwunipi.c b/modules/.dw485-unipi/8250_dwunipi.c
new file mode 100644 (file)
index 0000000..9c42e27
--- /dev/null
@@ -0,0 +1,1028 @@
+/*
+ * Synopsys DesignWare 8250 driver for UniPi 485 ports
+ *
+ * Copyright 2018 Miroslav Ondra, Faster CZ
+ * Copyright 2011 Picochip, Jamie Iles.
+ * Copyright 2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the
+ * LCR is written whilst busy.  If it is, then a busy detect interrupt is
+ * raised, the LCR needs to be rewritten and the uart status register read.
+ */
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/byteorder.h>
+
+#include "8250.h"
+
+/* Offsets for the DesignWare specific registers */
+#define DW_UART_USR    0x1f /* UART Status Register */
+#define DW_UART_CPR    0xf4 /* Component Parameter Register */
+#define DW_UART_UCV    0xf8 /* UART Component Version */
+
+/* Component Parameter Register bits */
+#define DW_UART_CPR_ABP_DATA_WIDTH     (3 << 0)
+#define DW_UART_CPR_AFCE_MODE          (1 << 4)
+#define DW_UART_CPR_THRE_MODE          (1 << 5)
+#define DW_UART_CPR_SIR_MODE           (1 << 6)
+#define DW_UART_CPR_SIR_LP_MODE                (1 << 7)
+#define DW_UART_CPR_ADDITIONAL_FEATURES        (1 << 8)
+#define DW_UART_CPR_FIFO_ACCESS                (1 << 9)
+#define DW_UART_CPR_FIFO_STAT          (1 << 10)
+#define DW_UART_CPR_SHADOW             (1 << 11)
+#define DW_UART_CPR_ENCODED_PARMS      (1 << 12)
+#define DW_UART_CPR_DMA_EXTRA          (1 << 13)
+#define DW_UART_CPR_FIFO_MODE          (0xff << 16)
+/* Helper for fifo size calculation */
+#define DW_UART_CPR_FIFO_SIZE(a)       (((a >> 16) & 0xff) * 16)
+
+/* DesignWare specific register fields */
+#define DW_UART_MCR_SIRE               BIT(6)
+
+struct dwunipi8250_data {
+       u8                      usr_reg;
+       int                     line;
+       int                     msr_mask_on;
+       int                     msr_mask_off;
+       struct clk              *clk;
+       struct clk              *pclk;
+       struct reset_control    *rst;
+       struct uart_8250_dma    dma;
+
+       unsigned int            skip_autocfg:1;
+       unsigned int            uart_16550_compatible:1;
+       int                     rts_gpio;
+       int                     enable_gpio;
+       u64             chardelay;
+    struct hrtimer hrt;
+       void        (*serial_out)(struct uart_port *, int, int);
+};
+
+static inline int dwunipi8250_modify_msr(struct uart_port *p, int offset, int value)
+{
+       struct dwunipi8250_data *d = p->private_data;
+
+       /* Override any modem control signals if needed */
+       if (offset == UART_MSR) {
+               value |= d->msr_mask_on;
+               value &= ~d->msr_mask_off;
+       }
+
+       return value;
+}
+
+static void dwunipi8250_force_idle(struct uart_port *p)
+{
+       struct uart_8250_port *up = up_to_u8250p(p);
+
+       serial8250_clear_and_reinit_fifos(up);
+       (void)p->serial_in(p, UART_RX);
+}
+
+static void dwunipi8250_check_lcr(struct uart_port *p, int value)
+{
+       void __iomem *offset = p->membase + (UART_LCR << p->regshift);
+       int tries = 1000;
+
+       /* Make sure LCR write wasn't ignored */
+       while (tries--) {
+               unsigned int lcr = p->serial_in(p, UART_LCR);
+
+               if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR))
+                       return;
+
+               dwunipi8250_force_idle(p);
+
+#ifdef CONFIG_64BIT
+               if (p->type == PORT_OCTEON)
+                       __raw_writeq(value & 0xff, offset);
+               else
+#endif
+               if (p->iotype == UPIO_MEM32)
+                       writel(value, offset);
+               else if (p->iotype == UPIO_MEM32BE)
+                       iowrite32be(value, offset);
+               else
+                       writeb(value, offset);
+       }
+       /*
+        * FIXME: this deadlocks if port->lock is already held
+        * dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+        */
+}
+
+static void dwunipi8250_gpio_serial_out(struct uart_port *p, int offset, int value)
+{
+       struct dwunipi8250_data *d = p->private_data;
+
+       if (offset == UART_MCR) {
+               if (d->rts_gpio >= 0)
+                       gpio_set_value(d->rts_gpio, (value & UART_MCR_RTS)? 0 : 1);
+       }
+       d->serial_out(p,offset,value);
+}
+
+static void dwunipi8250_serial_out(struct uart_port *p, int offset, int value)
+{
+       struct dwunipi8250_data *d = p->private_data;
+       writeb(value, p->membase + (offset << p->regshift));
+
+       if (offset == UART_LCR && !d->uart_16550_compatible)
+                       dwunipi8250_check_lcr(p, value);
+       /*if (offset == UART_LCR ) {
+               if (value & UART_LCR_SBC) {
+                       serial_port_out(p, UART_MCR, xxx);
+               }
+               if (!d->uart_16550_compatible)
+                       dwunipi8250_check_lcr(p, value);
+       }*/
+}
+
+static unsigned int dwunipi8250_serial_in(struct uart_port *p, int offset)
+{
+       unsigned int value = readb(p->membase + (offset << p->regshift));
+
+       return dwunipi8250_modify_msr(p, offset, value);
+}
+
+#ifdef CONFIG_64BIT
+static unsigned int dwunipi8250_serial_inq(struct uart_port *p, int offset)
+{
+       unsigned int value;
+
+       value = (u8)__raw_readq(p->membase + (offset << p->regshift));
+
+       return dwunipi8250_modify_msr(p, offset, value);
+}
+
+static void dwunipi8250_serial_outq(struct uart_port *p, int offset, int value)
+{
+       struct dwunipi8250_data *d = p->private_data;
+
+       value &= 0xff;
+       __raw_writeq(value, p->membase + (offset << p->regshift));
+       /* Read back to ensure register write ordering. */
+       __raw_readq(p->membase + (UART_LCR << p->regshift));
+
+       if (offset == UART_LCR && !d->uart_16550_compatible)
+               dwunipi8250_check_lcr(p, value);
+}
+#endif /* CONFIG_64BIT */
+
+static void dwunipi8250_serial_out32(struct uart_port *p, int offset, int value)
+{
+       struct dwunipi8250_data *d = p->private_data;
+
+       writel(value, p->membase + (offset << p->regshift));
+
+       if (offset == UART_LCR && !d->uart_16550_compatible)
+               dwunipi8250_check_lcr(p, value);
+}
+
+static unsigned int dwunipi8250_serial_in32(struct uart_port *p, int offset)
+{
+       unsigned int value = readl(p->membase + (offset << p->regshift));
+
+       return dwunipi8250_modify_msr(p, offset, value);
+}
+
+static void dwunipi8250_serial_out32be(struct uart_port *p, int offset, int value)
+{
+       struct dwunipi8250_data *d = p->private_data;
+
+       iowrite32be(value, p->membase + (offset << p->regshift));
+
+       if (offset == UART_LCR && !d->uart_16550_compatible)
+               dwunipi8250_check_lcr(p, value);
+}
+
+static unsigned int dwunipi8250_serial_in32be(struct uart_port *p, int offset)
+{
+       unsigned int value = ioread32be(p->membase + (offset << p->regshift));
+
+       return dwunipi8250_modify_msr(p, offset, value);
+}
+
+
+static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
+{
+       switch (iir & 0x3f) {
+       case UART_IIR_RX_TIMEOUT:
+               serial8250_rx_dma_flush(up);
+               /* fall-through */
+       case UART_IIR_RLSI:
+               return true;
+       }
+       return up->dma->rx_dma(up);
+}
+
+static inline void dwunipi8250_em485_rts_after_send(struct uart_8250_port *p)
+{
+       unsigned char mcr = serial8250_in_MCR(p);
+
+       if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
+               mcr |= UART_MCR_RTS;
+       else
+               mcr &= ~UART_MCR_RTS;
+       serial8250_out_MCR(p, mcr);
+}
+
+
+int dwunipi8250_handle_irq(struct uart_port *port, unsigned int iir)
+{
+       unsigned char status;
+       unsigned long flags;
+       struct uart_8250_port *up = up_to_u8250p(port);
+       struct dwunipi8250_data *d = port->private_data;
+    struct circ_buf *xmit = &port->state->xmit;
+    int fifolen, do_timer = 0;
+    u64 interval;
+
+       if (iir & UART_IIR_NO_INT) {
+        if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
+            /* Clear the USR */
+            (void)port->serial_in(port, d->usr_reg);
+            return 1;
+        }
+               return 0;
+    }
+
+       spin_lock_irqsave(&port->lock, flags);
+       status = serial_port_in(port, UART_LSR);
+       if (!up->dma && ((iir & 0x3f) == UART_IIR_RX_TIMEOUT)) {
+       /*
+        * There are ways to get Designware-based UARTs into a state where
+        * they are asserting UART_IIR_RX_TIMEOUT but there is no actual
+        * data available.  If we see such a case then we'll do a bogus
+        * read.  If we don't do this then the "RX TIMEOUT" interrupt will
+        * fire forever.
+        *
+        * This problem has only been observed so far when not in DMA mode
+        * so we limit the workaround only to non-DMA mode.
+        */
+               if (!(status & (UART_LSR_DR | UART_LSR_BI)))
+            (void) port->serial_in(port, UART_RX);
+    }
+    if (status & (UART_LSR_DR | UART_LSR_BI)) { 
+        if (!up->em485 || !(up->ier & UART_IER_THRI)) { /* Don't process rx chars during transmitting - in fifo can be garbage */
+            if (!up->dma || handle_rx_dma(up, iir)) {
+                status = serial8250_rx_chars(up, status);
+            }
+        }
+       }
+       serial8250_modem_status(up);
+    
+    if (up->em485 && (iir & UART_IIR_THRI)) {
+        if (!(port->x_char) && (uart_circ_empty(xmit))) {
+            do_timer = 1;
+        }
+        if (uart_tx_stopped(port)) { 
+            dwunipi8250_em485_rts_after_send(up); //???
+        }
+    }
+
+       if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE) /*&& (iir & UART_IIR_THRI)*/)
+               serial8250_tx_chars(up);
+
+    if (do_timer) {
+           fifolen = serial_port_in(port, 0x20);
+        if (up->ier & UART_IER_THRI) {
+            //serial8250_clear_and_reinit_fifos(up); /* clear fifo - garbage*/
+            up->ier &= ~UART_IER_THRI;
+            serial_out(up, UART_IER, up->ier);
+        }
+        if (!hrtimer_active(&d->hrt)) {
+            interval = (fifolen+1) * d->chardelay + 5000;  /* delay rts = FIFO * tim_for_one_char + 5us*/
+            hrtimer_start(&d->hrt, ns_to_ktime(interval), HRTIMER_MODE_REL);
+        }
+
+    }
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       return 1;
+}
+
+
+static int default_dwunipi8250_handle_irq(struct uart_port *p)
+{
+       struct uart_8250_port *up = up_to_u8250p(p);
+       unsigned int iir;
+       int ret;
+
+       serial8250_rpm_get(up);
+       iir = p->serial_in(p, UART_IIR);
+       ret = dwunipi8250_handle_irq(p, iir);
+       serial8250_rpm_put(up);
+       return ret;
+}
+
+
+#define BOTH_EMPTY     (UART_LSR_TEMT | UART_LSR_THRE)
+
+static enum hrtimer_restart    dwunipi8250_hrtimer_callback(struct hrtimer *hrtp)
+{
+       struct dwunipi8250_data *d;
+       struct uart_8250_port *up;
+    unsigned char lsr;
+    u64 fifolen;
+    ktime_t interval;
+
+    //printk(KERN_INFO "UART: hrt fired\n");
+       d = container_of(hrtp, struct dwunipi8250_data, hrt);
+    up = serial8250_get_port(d->line);
+
+    if ((up->ier & UART_IER_THRI)) {
+        /* if THR interrupt is enabled, timer is not valid anymore, port continues transmitting */
+        return HRTIMER_NORESTART;
+    }
+
+       lsr = serial_in(up, UART_LSR);
+       /*
+        * To provide required timeing and allow FIFO transfer,
+        * __stop_tx_rs485() must be called only when both FIFO and
+        * shift register are empty. It is for device driver to enable
+        * interrupt on TEMT.
+        */
+       if ((lsr & BOTH_EMPTY) == BOTH_EMPTY) {         /* THR and FIFO are empty */
+            dwunipi8250_em485_rts_after_send(up);        /* mnaage rts pin */
+            serial8250_clear_and_reinit_fifos(up);  /* clear fifo - garbage*/
+            up->ier |= UART_IER_RLSI | UART_IER_RDI;/* set interrupts mask to reading */
+            serial_out(up, UART_IER, up->ier);
+            return HRTIMER_NORESTART;               /* transmitting is finished */
+    }
+    
+    /* read TX queue, calc delay and restart timer */
+    fifolen = serial_in(up, 0x20);
+    interval = (fifolen+1) * d->chardelay + 5000;  /* delay rts = FIFO * tim_for_one_char + 5us*/
+    hrtimer_forward_now(hrtp, ns_to_ktime(interval));
+       return HRTIMER_RESTART;
+}
+
+int dwunipi8250_em485_init(struct uart_8250_port *p)
+{
+    int ret;
+       struct dwunipi8250_data *d = p->port.private_data;
+       ret = serial8250_em485_init(p);
+    /* create high res timer for 485 driving */
+    hrtimer_init(&d->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+    d->hrt.function = dwunipi8250_hrtimer_callback;
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dwunipi8250_em485_init);
+
+/**
+ *     dwunipi8250_em485_destroy() - put uart_8250_port into normal state
+ *     @p:     uart_8250_port port instance
+ *
+ *     The function is used to stop rs485 software emulating on the
+ *     &struct uart_8250_port* @p. The function is idempotent, so it is safe to
+ *     call it multiple times.
+ *
+ *     The function is supposed to be called from .rs485_config callback
+ *     or from any other callback protected with p->port.lock spinlock.
+ *
+ *     See also serial8250_em485_init()
+ */
+void dwunipi8250_em485_destroy(struct uart_8250_port *p)
+{
+       struct dwunipi8250_data *d = p->port.private_data;
+       serial8250_em485_destroy(p);
+    hrtimer_try_to_cancel(&d->hrt);
+}
+EXPORT_SYMBOL_GPL(dwunipi8250_em485_destroy);
+
+
+static int dwunipi8250_rs485_config(struct uart_port *port, struct serial_rs485 *rs485)
+{
+       struct uart_8250_port *up = up_to_u8250p(port);
+       /* Clamp the delays to [0, 100ms] */
+       rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U);
+       rs485->delay_rts_after_send  = min(rs485->delay_rts_after_send, 100U);
+       port->rs485 = *rs485;
+       /*
+        * Both dwunipi8250_em485_init and dwunipi8250_em485_destroy
+        * are idempotent
+        */
+       if (rs485->flags & SER_RS485_ENABLED) {
+               int ret = dwunipi8250_em485_init(up);
+
+               if (ret) {
+                       rs485->flags &= ~SER_RS485_ENABLED;
+                       port->rs485.flags &= ~SER_RS485_ENABLED;
+               }
+               return ret;
+       }
+       dwunipi8250_em485_destroy(up);
+       return 0;
+ }
+
+void dwunipi8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct uart_8250_port *up = up_to_u8250p(port);
+    unsigned char mcr = 0;
+
+    /* mask RTS changing in case of rs485 mode */
+       if (port->rs485.flags & SER_RS485_ENABLED) {
+        mcr = serial8250_in_MCR(up) & UART_MCR_RTS;
+    } else {
+        if (mctrl & TIOCM_RTS)
+            mcr |= UART_MCR_RTS;
+    }
+       if (mctrl & TIOCM_DTR)
+        mcr |= UART_MCR_DTR;
+       if (mctrl & TIOCM_OUT1)
+               mcr |= UART_MCR_OUT1;
+       if (mctrl & TIOCM_OUT2)
+               mcr |= UART_MCR_OUT2;
+    if (mctrl & TIOCM_LOOP)
+               mcr |= UART_MCR_LOOP;
+
+       mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
+
+       serial8250_out_MCR(up, mcr);
+}
+
+
+static void
+dwunipi8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
+{
+       if (!state)
+               pm_runtime_get_sync(port->dev);
+
+       serial8250_do_pm(port, state, old);
+
+       if (state)
+               pm_runtime_put_sync_suspend(port->dev);
+}
+
+static unsigned int dwunipi8250_get_bits(unsigned int cflag)
+{
+    unsigned int bits;
+
+       /* byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS5:
+               bits = 7;
+               break;
+       case CS6:
+               bits = 8;
+               break;
+       case CS7:
+               bits = 9;
+               break;
+       default:
+               bits = 10;
+               break; /* CS8 */
+       }
+
+       if (cflag & CSTOPB)
+               bits++;
+       if (cflag & PARENB)
+               bits++;
+       return bits;
+}
+
+static void dwunipi8250_set_termios(struct uart_port *p, struct ktermios *termios,
+                              struct ktermios *old)
+{
+       unsigned int baud = tty_termios_baud_rate(termios);
+       struct dwunipi8250_data *d = p->private_data;
+       long rate;
+       int ret;
+
+       if (IS_ERR(d->clk) || !old)
+               goto out;
+
+       clk_disable_unprepare(d->clk);
+       rate = clk_round_rate(d->clk, baud * 16);
+       if (rate < 0)
+               ret = rate;
+       else if (rate == 0)
+               ret = -ENOENT;
+       else
+               ret = clk_set_rate(d->clk, rate);
+       clk_prepare_enable(d->clk);
+
+       if (!ret)
+               p->uartclk = rate;
+
+out:
+       p->status &= ~UPSTAT_AUTOCTS;
+       if (termios->c_cflag & CRTSCTS)
+               p->status |= UPSTAT_AUTOCTS;
+
+       serial8250_do_set_termios(p, termios, old);
+       if (termios->c_ospeed)
+               d->chardelay = (dwunipi8250_get_bits(termios->c_cflag) * (u64)1000000000) / termios->c_ospeed ;
+       else
+               d->chardelay = 0;
+}
+
+static void dwunipi8250_set_ldisc(struct uart_port *p, struct ktermios *termios)
+{
+       struct uart_8250_port *up = up_to_u8250p(p);
+       unsigned int mcr = p->serial_in(p, UART_MCR);
+
+       if (up->capabilities & UART_CAP_IRDA) {
+               if (termios->c_line == N_IRDA)
+                       mcr |= DW_UART_MCR_SIRE;
+               else
+                       mcr &= ~DW_UART_MCR_SIRE;
+
+               p->serial_out(p, UART_MCR, mcr);
+       }
+       serial8250_do_set_ldisc(p, termios);
+}
+
+/*
+ * dwunipi8250_fallback_dma_filter will prevent the UART from getting just any free
+ * channel on platforms that have DMA engines, but don't have any channels
+ * assigned to the UART.
+ *
+ * REVISIT: This is a work around for limitation in the DMA Engine API. Once the
+ * core problem is fixed, this function is no longer needed.
+ */
+static bool dwunipi8250_fallback_dma_filter(struct dma_chan *chan, void *param)
+{
+       return false;
+}
+
+static bool dwunipi8250_idma_filter(struct dma_chan *chan, void *param)
+{
+       return param == chan->device->dev->parent;
+}
+
+static int dwunipi8250_startup(struct uart_port *port)
+{
+       struct dwunipi8250_data *d = port->private_data;
+       if (d->enable_gpio >= 0) {
+               gpio_set_value(d->enable_gpio, 1);
+       }
+       return serial8250_do_startup(port);
+}
+
+static void dwunipi8250_gpio_setup(struct uart_port *port, struct device_node *np, struct dwunipi8250_data *data)
+{
+       enum of_gpio_flags flags;
+       int ret, gpio_pin;
+
+    if (!np) return;
+
+       /* check for tx enable gpio */
+       gpio_pin = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags);
+       if (gpio_is_valid(gpio_pin)) {
+               ret = devm_gpio_request(port->dev, gpio_pin, "dw-apb-uart");
+               flags = 0; // initial value of gpio output
+               if (ret >= 0) {
+                       if (gpio_direction_output(gpio_pin, flags) >= 0) {
+                               data->rts_gpio = gpio_pin;
+                       data->serial_out = port->serial_out;
+                       port->serial_out = dwunipi8250_gpio_serial_out;
+                       }
+               }
+       }
+       //} else if (d->rts_gpio == -EPROBE_DEFER) {
+       //return -EPROBE_DEFER;
+
+       /* check for enable gpio */
+       gpio_pin = of_get_named_gpio_flags(np, "enable-gpio", 0, &flags);
+       if (gpio_is_valid(gpio_pin)) {
+               ret = devm_gpio_request(port->dev, gpio_pin, "dw-apb-uart");
+               flags = 0; // initial value of gpio output
+               if (ret >= 0) {
+                       if (gpio_direction_output(gpio_pin, flags) >= 0)
+                               data->enable_gpio = gpio_pin;
+               }
+       }
+}
+
+static void dwunipi8250_rs485_setup(struct uart_port *port, struct device_node *np, struct dwunipi8250_data *data)
+{
+       struct serial_rs485 *rs485conf = &port->rs485;
+
+       rs485conf->flags = 0;
+
+       if (of_property_read_bool(np, "rs485-rts-active-high"))
+               rs485conf->flags |= SER_RS485_RTS_ON_SEND;
+       else
+               rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+    
+       /*if (of_property_read_u32_array(np, "rs485-rts-delay",
+                                   rs485_delay, 2) == 0) {
+               rs485conf->delay_rts_before_send = rs485_delay[0];
+               rs485conf->delay_rts_after_send = rs485_delay[1];
+       }*/
+
+       if (of_property_read_bool(np, "rs485-rx-during-tx"))
+               rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+       if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) {
+               rs485conf->flags |= SER_RS485_ENABLED;
+    }
+}
+
+static void dwunipi8250_quirks(struct uart_port *p, struct dwunipi8250_data *data)
+{
+       if (p->dev->of_node) {
+               struct device_node *np = p->dev->of_node;
+               int id;
+
+               /* get index of serial line, if found in DT aliases */
+               id = of_alias_get_id(np, "serial");
+               if (id >= 0)
+                       p->line = id;
+#ifdef CONFIG_64BIT
+               if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) {
+                       p->serial_in = dwunipi8250_serial_inq;
+                       p->serial_out = dwunipi8250_serial_outq;
+                       p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+                       p->type = PORT_OCTEON;
+                       data->usr_reg = 0x27;
+                       data->skip_autocfg = true;
+               }
+#endif
+               if (of_device_is_big_endian(p->dev->of_node)) {
+                       p->iotype = UPIO_MEM32BE;
+                       p->serial_in = dwunipi8250_serial_in32be;
+                       p->serial_out = dwunipi8250_serial_out32be;
+               }
+               dwunipi8250_gpio_setup(p, np, data);
+               dwunipi8250_rs485_setup(p, np, data);
+       } else if (has_acpi_companion(p->dev)) {
+               const struct acpi_device_id *id;
+
+               id = acpi_match_device(p->dev->driver->acpi_match_table,
+                                      p->dev);
+               if (id && !strcmp(id->id, "APMC0D08")) {
+                       p->iotype = UPIO_MEM32;
+                       p->regshift = 2;
+                       p->serial_in = dwunipi8250_serial_in32;
+                       data->uart_16550_compatible = true;
+               }
+       }
+
+       /* Platforms with iDMA */
+       if (platform_get_resource_byname(to_platform_device(p->dev),
+                                        IORESOURCE_MEM, "lpss_priv")) {
+               data->dma.rx_param = p->dev->parent;
+               data->dma.tx_param = p->dev->parent;
+               data->dma.fn = dwunipi8250_idma_filter;
+       }
+}
+
+static void dwunipi8250_setup_port(struct uart_port *p)
+{
+       struct uart_8250_port *up = up_to_u8250p(p);
+       u32 reg;
+
+       /*
+        * If the Component Version Register returns zero, we know that
+        * ADDITIONAL_FEATURES are not enabled. No need to go any further.
+        */
+       if (p->iotype == UPIO_MEM32BE)
+               reg = ioread32be(p->membase + DW_UART_UCV);
+       else
+               reg = readl(p->membase + DW_UART_UCV);
+       if (!reg)
+               return;
+
+       dev_dbg(p->dev, "UniPi 485 via Designware UART version %c.%c%c\n",
+               (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
+
+       if (p->iotype == UPIO_MEM32BE)
+               reg = ioread32be(p->membase + DW_UART_CPR);
+       else
+               reg = readl(p->membase + DW_UART_CPR);
+       if (!reg)
+               return;
+
+       /* Select the type based on fifo */
+       if (reg & DW_UART_CPR_FIFO_MODE) {
+               p->type = PORT_16550A;
+               p->flags |= UPF_FIXED_TYPE;
+               p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
+               up->capabilities = UART_CAP_FIFO;
+       }
+
+       if (reg & DW_UART_CPR_AFCE_MODE)
+               up->capabilities |= UART_CAP_AFE;
+
+       if (reg & DW_UART_CPR_SIR_MODE)
+               up->capabilities |= UART_CAP_IRDA;
+}
+
+static int dwunipi8250_probe(struct platform_device *pdev)
+{
+       struct uart_8250_port uart = {};
+       struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       int irq = platform_get_irq(pdev, 0);
+       struct uart_port *p = &uart.port;
+       struct device *dev = &pdev->dev;
+       struct dwunipi8250_data *data;
+       int err;
+       u32 val;
+
+       if (!regs) {
+               dev_err(dev, "no registers defined\n");
+               return -EINVAL;
+       }
+
+       if (irq < 0) {
+               if (irq != -EPROBE_DEFER)
+                       dev_err(dev, "cannot get irq\n");
+               return irq;
+       }
+
+       spin_lock_init(&p->lock);
+       p->mapbase      = regs->start;
+       p->irq          = irq;
+       p->handle_irq   = default_dwunipi8250_handle_irq;
+       p->pm           = dwunipi8250_do_pm;
+       p->type         = PORT_8250;
+       p->flags        = UPF_SHARE_IRQ | UPF_FIXED_PORT;
+       p->dev          = dev;
+       p->iotype       = UPIO_MEM;
+       p->serial_in    = dwunipi8250_serial_in;
+       p->serial_out   = dwunipi8250_serial_out;
+       p->set_ldisc    = dwunipi8250_set_ldisc;
+       p->set_termios  = dwunipi8250_set_termios;
+    p->set_mctrl    = dwunipi8250_do_set_mctrl;
+    p->rs485_config = dwunipi8250_rs485_config;
+
+       p->membase = devm_ioremap(dev, regs->start, resource_size(regs));
+       if (!p->membase)
+               return -ENOMEM;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->dma.fn = dwunipi8250_fallback_dma_filter;
+       data->usr_reg = DW_UART_USR;
+       data->rts_gpio = -EINVAL;
+       data->enable_gpio = -EINVAL;
+    data->chardelay = 0;
+       p->private_data = data;
+
+       data->uart_16550_compatible = device_property_read_bool(dev,
+                                               "snps,uart-16550-compatible");
+
+       err = device_property_read_u32(dev, "reg-shift", &val);
+       if (!err)
+               p->regshift = val;
+
+       err = device_property_read_u32(dev, "reg-io-width", &val);
+       if (!err && val == 4) {
+               p->iotype = UPIO_MEM32;
+               p->serial_in = dwunipi8250_serial_in32;
+               p->serial_out = dwunipi8250_serial_out32;
+       }
+
+       if (device_property_read_bool(dev, "dcd-override")) {
+               /* Always report DCD as active */
+               data->msr_mask_on |= UART_MSR_DCD;
+               data->msr_mask_off |= UART_MSR_DDCD;
+       }
+
+       if (device_property_read_bool(dev, "dsr-override")) {
+               /* Always report DSR as active */
+               data->msr_mask_on |= UART_MSR_DSR;
+               data->msr_mask_off |= UART_MSR_DDSR;
+       }
+
+       if (device_property_read_bool(dev, "cts-override")) {
+               /* Always report CTS as active */
+               data->msr_mask_on |= UART_MSR_CTS;
+               data->msr_mask_off |= UART_MSR_DCTS;
+       }
+
+       if (device_property_read_bool(dev, "ri-override")) {
+               /* Always report Ring indicator as inactive */
+               data->msr_mask_off |= UART_MSR_RI;
+               data->msr_mask_off |= UART_MSR_TERI;
+       }
+
+       /* Always ask for fixed clock rate from a property. */
+       device_property_read_u32(dev, "clock-frequency", &p->uartclk);
+
+       /* If there is separate baudclk, get the rate from it. */
+       data->clk = devm_clk_get(dev, "baudclk");
+       if (IS_ERR(data->clk) && PTR_ERR(data->clk) != -EPROBE_DEFER)
+               data->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+       if (!IS_ERR_OR_NULL(data->clk)) {
+               err = clk_prepare_enable(data->clk);
+               if (err)
+                       dev_warn(dev, "could not enable optional baudclk: %d\n",
+                                err);
+               else
+                       p->uartclk = clk_get_rate(data->clk);
+       }
+
+       /* If no clock rate is defined, fail. */
+       if (!p->uartclk) {
+               dev_err(dev, "clock rate not defined\n");
+               err = -EINVAL;
+               goto err_clk;
+       }
+
+       data->pclk = devm_clk_get(dev, "apb_pclk");
+       if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) {
+               err = -EPROBE_DEFER;
+               goto err_clk;
+       }
+       if (!IS_ERR(data->pclk)) {
+               err = clk_prepare_enable(data->pclk);
+               if (err) {
+                       dev_err(dev, "could not enable apb_pclk\n");
+                       goto err_clk;
+               }
+       }
+
+       data->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
+       if (IS_ERR(data->rst)) {
+               err = PTR_ERR(data->rst);
+               goto err_pclk;
+       }
+       reset_control_deassert(data->rst);
+
+       dwunipi8250_quirks(p, data);
+       if (data->enable_gpio >= 0) {
+               p->startup = dwunipi8250_startup;
+       }
+
+       /* If the Busy Functionality is not implemented, don't handle it */
+       if (data->uart_16550_compatible)
+               p->handle_irq = NULL;
+
+       if (!data->skip_autocfg)
+               dwunipi8250_setup_port(p);
+
+       /* If we have a valid fifosize, try hooking up DMA */
+       if (p->fifosize) {
+               data->dma.rxconf.src_maxburst = p->fifosize / 4;
+               data->dma.txconf.dst_maxburst = p->fifosize / 4;
+               uart.dma = &data->dma;
+       }
+
+       data->line = serial8250_register_8250_port(&uart);
+       if (data->line < 0) {
+               err = data->line;
+               goto err_reset;
+       }
+
+       platform_set_drvdata(pdev, data);
+
+    if (p->rs485.flags & SER_RS485_ENABLED) {
+        dwunipi8250_em485_init(serial8250_get_port(data->line));
+    }
+
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
+       return 0;
+
+err_reset:
+       reset_control_assert(data->rst);
+
+err_pclk:
+       if (!IS_ERR(data->pclk))
+               clk_disable_unprepare(data->pclk);
+
+err_clk:
+       if (!IS_ERR(data->clk))
+               clk_disable_unprepare(data->clk);
+
+       return err;
+}
+
+static int dwunipi8250_remove(struct platform_device *pdev)
+{
+       struct dwunipi8250_data *data = platform_get_drvdata(pdev);
+
+       pm_runtime_get_sync(&pdev->dev);
+
+       serial8250_unregister_port(data->line);
+
+       reset_control_assert(data->rst);
+
+       if (!IS_ERR(data->pclk))
+               clk_disable_unprepare(data->pclk);
+
+       if (!IS_ERR(data->clk))
+               clk_disable_unprepare(data->clk);
+
+       pm_runtime_disable(&pdev->dev);
+       pm_runtime_put_noidle(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dwunipi8250_suspend(struct device *dev)
+{
+       struct dwunipi8250_data *data = dev_get_drvdata(dev);
+
+       serial8250_suspend_port(data->line);
+
+       return 0;
+}
+
+static int dwunipi8250_resume(struct device *dev)
+{
+       struct dwunipi8250_data *data = dev_get_drvdata(dev);
+
+       serial8250_resume_port(data->line);
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int dwunipi8250_runtime_suspend(struct device *dev)
+{
+       struct dwunipi8250_data *data = dev_get_drvdata(dev);
+
+       if (!IS_ERR(data->clk))
+               clk_disable_unprepare(data->clk);
+
+       if (!IS_ERR(data->pclk))
+               clk_disable_unprepare(data->pclk);
+
+       return 0;
+}
+
+static int dwunipi8250_runtime_resume(struct device *dev)
+{
+       struct dwunipi8250_data *data = dev_get_drvdata(dev);
+
+       if (!IS_ERR(data->pclk))
+               clk_prepare_enable(data->pclk);
+
+       if (!IS_ERR(data->clk))
+               clk_prepare_enable(data->clk);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops dwunipi8250_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(dwunipi8250_suspend, dwunipi8250_resume)
+       SET_RUNTIME_PM_OPS(dwunipi8250_runtime_suspend, dwunipi8250_runtime_resume, NULL)
+};
+
+static const struct of_device_id dwunipi8250_of_match[] = {
+       { .compatible = "unipi,dw-apb-485" },
+       { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dwunipi8250_of_match);
+
+static const struct acpi_device_id dwunipi8250_acpi_match[] = {
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, dwunipi8250_acpi_match);
+
+static struct platform_driver dwunipi8250_platform_driver = {
+       .driver = {
+               .name           = "dw-apb-485",
+               .pm             = &dwunipi8250_pm_ops,
+               .of_match_table = dwunipi8250_of_match,
+               .acpi_match_table = ACPI_PTR(dwunipi8250_acpi_match),
+       },
+       .probe                  = dwunipi8250_probe,
+       .remove                 = dwunipi8250_remove,
+};
+
+module_platform_driver(dwunipi8250_platform_driver);
+
+MODULE_AUTHOR("Miroslav Ondra");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("UniPi 485 port via Synopsys DesignWare 8250 serial uart driver");
+MODULE_ALIAS("platform:dw-apb-485");
diff --git a/modules/.dw485-unipi/Kconfig b/modules/.dw485-unipi/Kconfig
new file mode 100644 (file)
index 0000000..d117cd1
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# The 8250/16550 serial drivers.  You shouldn't be in this list unless
+# you somehow have an implicit or explicit dependency on SERIAL_8250.
+#
+
+config SERIAL_8250_DW_UNIPI
+       tristate "Support for Synopsys DesignWare 8250 in 485 mode on UniPi "
+       depends on SERIAL_8250
+       help
+         Selecting this option will enable handling of 485 ports on UniPi
+         controllers .
+
+
+#config RXTX_LED_TRIG
+#       bool "RXTX Serial LED Triggers"
+#       depends on LEDS_CLASS && LEDS_TRIGGERS
+#       help
+#         This option adds LED triggers for RX/TX serial activity.
+#
+#         Say Y here if you are working on a system with led-class supported
+#         LEDs and you want to use them as activity indicators for serial port.
diff --git a/modules/.dw485-unipi/Makefile b/modules/.dw485-unipi/Makefile
new file mode 100644 (file)
index 0000000..11b4bf6
--- /dev/null
@@ -0,0 +1,35 @@
+# Note: Compiling kernel modules requires creating symlinks, which is not possible on certain 
+# filesystems (notably VirtualBox vmfs); therefore we allow using /run/ through the 'symlink' target,
+# if necessary.
+LINUX_DIR_PATH = /lib/modules/$(shell uname -r)/build
+ifdef CCPREFIX
+    CCPAR = CROSS_COMPILE=${CCPREFIX}
+endif
+ifdef ARCH
+    CCPAR += ARCH=${ARCH}
+endif
+
+MODULE_MAKE_FILE = Makefile
+
+C_SRC_FILES = 8250_dwunipi.c
+
+OBJ_FILES = 8250_dwunipi.o
+
+KERNEL_MODULE_NAME = dw-apb-485
+obj-m += ${KERNEL_MODULE_NAME}.o
+dw-apb-485-objs := ${OBJ_FILES}
+
+.PHONY: default
+default: all ;
+
+all: 
+       make $(CCPAR) -C $(LINUX_DIR_PATH) M=${PWD} modules
+
+modules_install: 
+       make $(CCPAR) -C $(LINUX_DIR_PATH) M=${PWD} modules_install
+
+clean: 
+       rm -f ${rtc-unipi-objs} .*.o.cmd
+       rm -f ${KERNEL_MODULE_NAME}.ko ${KERNEL_MODULE_NAME}.mod.c .*.o.cmd .*.ko.cmd *.o modules.order Module.symvers
+       rm -rf .tmp_versions
diff --git a/modules/rtc-unipi/Makefile b/modules/rtc-unipi/Makefile
new file mode 100644 (file)
index 0000000..2fd9e22
--- /dev/null
@@ -0,0 +1,39 @@
+# Note: Compiling kernel modules requires creating symlinks, which is not possible on certain 
+# filesystems (notably VirtualBox vmfs); therefore we allow using /run/ through the 'symlink' target,
+# if necessary.
+LINUX_DIR_PATH = /lib/modules/$(shell uname -r)/build
+ifdef CCPREFIX
+    CCPAR = CROSS_COMPILE=${CCPREFIX}
+endif
+ifdef ARCH
+    CCPAR += ARCH=${ARCH}
+endif
+
+MODULE_MAKE_FILE = Makefile
+INSTALL = install
+
+C_SRC_FILES = rtc-unipi.c
+
+OBJ_FILES  = $(C_SRC_FILES:.c=.o)
+
+KERNEL_MODULE_NAME = rtc-unipi
+obj-m += ${KERNEL_MODULE_NAME}.o
+#rtc-unipi-objs := ${OBJ_FILES}
+
+.PHONY: default
+default: all ;
+
+all: 
+       make $(CCPAR) -C $(LINUX_DIR_PATH) M=${PWD} modules
+
+modules_install: 
+       make $(CCPAR) -C $(LINUX_DIR_PATH) M=${PWD} modules_install
+
+dkms:
+       $(INSTALL) -D $(C_SRC_FILES) $(MODULE_MAKE_FILE) -t $(INSTALL_MOD_PATH)
+
+clean: 
+       rm -f ${rtc-unipi-objs} .*.o.cmd
+       rm -f ${KERNEL_MODULE_NAME}.ko ${KERNEL_MODULE_NAME}.mod.c .*.o.cmd .*.ko.cmd *.o modules.order Module.symvers
+       rm -rf .tmp_versions
diff --git a/modules/rtc-unipi/rtc-unipi.c b/modules/rtc-unipi/rtc-unipi.c
new file mode 100644 (file)
index 0000000..51b6f19
--- /dev/null
@@ -0,0 +1,704 @@
+/*
+ * rtc-unipi.c - RTC driver for mcp794xx based on rtc-ds1307.c
+ *               with calibration support
+ *
+ *  Copyright (C) 2005 James Chapman (ds1337 core)
+ *  Copyright (C) 2006 David Brownell
+ *  Copyright (C) 2009 Matthias Fuchs (rx8025 support)
+ *  Copyright (C) 2012 Bertrand Achard (nvram access fixes)
+ *  Copyright (C) 2019 Miroslav Ondra 
+ *
+ * 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/acpi.h>
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/rtc/ds1307.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/version.h>
+
+
+/* RTC registers don't differ much, except for the century flag */
+#define DS1307_REG_SECS                0x00    /* 00-59 */
+#      define DS1307_BIT_CH            0x80
+#      define DS1340_BIT_nEOSC         0x80
+#      define MCP794XX_BIT_ST          0x80
+#define DS1307_REG_MIN         0x01    /* 00-59 */
+#      define M41T0_BIT_OF             0x80
+#define DS1307_REG_HOUR                0x02    /* 00-23, or 1-12{am,pm} */
+#      define DS1307_BIT_12HR          0x40    /* in REG_HOUR */
+#      define DS1307_BIT_PM            0x20    /* in REG_HOUR */
+#      define DS1340_BIT_CENTURY_EN    0x80    /* in REG_HOUR */
+#      define DS1340_BIT_CENTURY       0x40    /* in REG_HOUR */
+#define DS1307_REG_WDAY                0x03    /* 01-07 */
+#      define MCP794XX_BIT_VBATEN      0x08
+#define DS1307_REG_MDAY                0x04    /* 01-31 */
+#define DS1307_REG_MONTH       0x05    /* 01-12 */
+#      define DS1337_BIT_CENTURY       0x80    /* in REG_MONTH */
+#define DS1307_REG_YEAR                0x06    /* 00-99 */
+
+/*
+ * Other registers (control, status, alarms, trickle charge, NVRAM, etc)
+ * start at 7, and they differ a LOT. Only control and status matter for
+ * basic RTC date and time functionality; be careful using them.
+ */
+#define DS1307_REG_CONTROL     0x07            /* or ds1338 */
+#      define DS1307_BIT_OUT           0x80
+#      define DS1338_BIT_OSF           0x20
+#      define DS1307_BIT_SQWE          0x10
+#      define DS1307_BIT_RS1           0x02
+#      define DS1307_BIT_RS0           0x01
+
+
+#define MCP794XX_NVRAM_OFFSET  0x20
+#define MCP794XX_NVRAM_SIZE            0x40
+
+
+struct rtc_unipi {
+       struct nvmem_config     nvmem_cfg;
+       /*enum ds_type          type;*/
+       unsigned long           flags;
+#define HAS_NVRAM      0               /* bit 0 == sysfs file active */
+#define HAS_ALARM      1               /* bit 1 == irq claimed */
+       struct device           *dev;
+       struct regmap           *regmap;
+       const char                      *name;
+       struct rtc_device       *rtc;
+/*
+#ifdef CONFIG_COMMON_CLK
+       struct clk_hw           clks[2];
+#endif
+*/
+};
+
+/*struct chip_desc { */
+       /*unsigned              alarm:1;*/
+       /*u16                   nvram_offset;
+       u16                     nvram_size;*/
+       /*u8                    offset; *//* register's offset */
+       /*u8                    century_reg;
+       u8                      century_enable_bit;
+       u8                      century_bit;*/
+       /*u8                    bbsqi_bit; */
+       /*irq_handler_t         irq_handler;*/
+       /*const struct rtc_class_ops *rtc_ops;*/
+/*};*/
+
+static int mcp794xx_get_time(struct device *dev, struct rtc_time *t);
+static int mcp794xx_set_time(struct device *dev, struct rtc_time *t);
+static irqreturn_t mcp794xx_irq(int irq, void *dev_id);
+static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t);
+static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t);
+static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled);
+static int mcp794xx_read_offset(struct device *dev, long *offset);
+static int mcp794xx_set_offset(struct device *dev, long offset);
+
+
+static const struct rtc_class_ops mcp794xx_rtc_ops = {
+       .read_time      = mcp794xx_get_time,
+       .set_time       = mcp794xx_set_time,
+       .read_alarm     = mcp794xx_read_alarm,
+       .set_alarm      = mcp794xx_set_alarm,
+       .alarm_irq_enable = mcp794xx_alarm_irq_enable,
+       .read_offset    = mcp794xx_read_offset,
+       .set_offset     = mcp794xx_set_offset,
+};
+
+/*static const struct chip_desc mcpchip = {*/
+               /*.alarm                = 1,*/
+               /* this is battery backed SRAM */
+               /* .nvram_offset        = 0x20,
+               .nvram_size     = 0x40, */
+               /*.irq_handler = mcp794xx_irq,*/
+               /*.rtc_ops = &mcp794xx_rtc_ops,*/
+/*};*/
+
+static const struct i2c_device_id rtc_unipi_id[] = {
+       { "unipi-mcp7941x", 0 /*mcp794xx */},
+       { "rtc-unipi", 0 /*mcp794xx */},
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, rtc_unipi_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtc_unipi_of_match[] = {
+       {
+               .compatible = "unipi,unipi-mcp7941x",
+               .data = NULL /*(void *)mcp794xx*/
+       },
+       {
+               .compatible = "unipi,rtc-unipi",
+               .data = NULL /*(void *)mcp794xx*/
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(of, rtc_unipi_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rtc_unipi_acpi_ids[] = {
+       { .id = "MCP7940X", .driver_data = 0 /*mcp794xx */},
+       { .id = "MCP7941X", .driver_data = 0 /*mcp794xx */},
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, rtc_unipi_acpi_ids);
+#endif
+
+
+/*----------------------------------------------------------------------*/
+
+static int mcp794xx_get_time(struct device *dev, struct rtc_time *t)
+{
+       struct rtc_unipi        *rtc_unipi = dev_get_drvdata(dev);
+       int             tmp, ret;
+       /*const struct chip_desc *chip = &chips[ds1307->type];*/
+       u8 regs[7];
+
+       /* read the RTC date and time registers all at once */
+       ret = regmap_bulk_read(rtc_unipi->regmap, 0/*chip->offset*/, regs,
+                              sizeof(regs));
+       if (ret) {
+               dev_err(dev, "%s error %d\n", "read", ret);
+               return ret;
+       }
+
+       dev_dbg(dev, "%s: %7ph\n", "read", regs);
+
+       t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f);
+       t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f);
+       tmp = regs[DS1307_REG_HOUR] & 0x3f;
+       t->tm_hour = bcd2bin(tmp);
+       t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1;
+       t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f);
+       tmp = regs[DS1307_REG_MONTH] & 0x1f;
+       t->tm_mon = bcd2bin(tmp) - 1;
+       t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100;
+       dev_dbg(dev, "%s secs=%d, mins=%d, "
+               "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+               "read", t->tm_sec, t->tm_min,
+               t->tm_hour, t->tm_mday,
+               t->tm_mon, t->tm_year, t->tm_wday);
+
+       /* initial clock setting can be undefined */
+       return rtc_valid_tm(t);
+}
+
+static int mcp794xx_set_time(struct device *dev, struct rtc_time *t)
+{
+       struct rtc_unipi        *rtc_unipi = dev_get_drvdata(dev);
+       /*const struct chip_desc *chip = &chips[ds1307->type];*/
+       int             result;
+       int             tmp;
+       u8              regs[7];
+
+       dev_dbg(dev, "%s secs=%d, mins=%d, "
+               "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+               "write", t->tm_sec, t->tm_min,
+               t->tm_hour, t->tm_mday,
+               t->tm_mon, t->tm_year, t->tm_wday);
+
+       if (t->tm_year < 100)
+               return -EINVAL;
+//#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
+//     if (t->tm_year > (/*chip->century_bit ? 299 : */199))
+//             return -EINVAL;
+//#else
+       if (t->tm_year > 199)
+               return -EINVAL;
+//#endif
+
+       regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
+       regs[DS1307_REG_MIN] = bin2bcd(t->tm_min);
+       regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
+       regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1);
+       regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday);
+       regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1);
+
+       /* assume 20YY not 19YY */
+       tmp = t->tm_year - 100;
+       regs[DS1307_REG_YEAR] = bin2bcd(tmp);
+
+
+       //if (ds1307->type == mcp794xx) {
+               /*
+                * these bits were cleared when preparing the date/time
+                * values and need to be set again before writing the
+                * regsfer out to the device.
+                */
+               regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST;
+               regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN;
+       //}
+
+       dev_dbg(dev, "%s: %7ph\n", "write", regs);
+
+       result = regmap_bulk_write(rtc_unipi->regmap, 0 /*chip->offset*/, regs,
+                                  sizeof(regs));
+       if (result) {
+               dev_err(dev, "%s error %d\n", "write", result);
+               return result;
+       }
+       return 0;
+}
+
+
+
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Alarm support for mcp794xx devices.
+ */
+
+#define MCP794XX_REG_WEEKDAY           0x3
+#define MCP794XX_REG_WEEKDAY_WDAY_MASK 0x7
+#define MCP794XX_REG_CONTROL           0x07
+#      define MCP794XX_BIT_ALM0_EN     0x10
+#      define MCP794XX_BIT_ALM1_EN     0x20
+#   define MCP794XX_BIT_EXTOSC  0x08
+#define MCP794XX_REG_ALARM0_BASE       0x0a
+#define MCP794XX_REG_ALARM0_CTRL       0x0d
+#define MCP794XX_REG_ALARM1_BASE       0x11
+#define MCP794XX_REG_ALARM1_CTRL       0x14
+#      define MCP794XX_BIT_ALMX_IF     BIT(3)
+#      define MCP794XX_BIT_ALMX_C0     BIT(4)
+#      define MCP794XX_BIT_ALMX_C1     BIT(5)
+#      define MCP794XX_BIT_ALMX_C2     BIT(6)
+#      define MCP794XX_BIT_ALMX_POL    BIT(7)
+#      define MCP794XX_MSK_ALMX_MATCH  (MCP794XX_BIT_ALMX_C0 | \
+                                        MCP794XX_BIT_ALMX_C1 | \
+                                        MCP794XX_BIT_ALMX_C2)
+
+#define MCP794XX_REG_CALIBRATION       0x08
+
+static irqreturn_t mcp794xx_irq(int irq, void *dev_id)
+{
+       struct rtc_unipi           *rtc_unipi = dev_id;
+       struct mutex            *lock = &rtc_unipi->rtc->ops_lock;
+       int reg, ret;
+
+       mutex_lock(lock);
+
+       /* Check and clear alarm 0 interrupt flag. */
+       ret = regmap_read(rtc_unipi->regmap, MCP794XX_REG_ALARM0_CTRL, &reg);
+       if (ret)
+               goto out;
+       if (!(reg & MCP794XX_BIT_ALMX_IF))
+               goto out;
+       reg &= ~MCP794XX_BIT_ALMX_IF;
+       ret = regmap_write(rtc_unipi->regmap, MCP794XX_REG_ALARM0_CTRL, reg);
+       if (ret)
+               goto out;
+
+       /* Disable alarm 0. */
+       ret = regmap_update_bits(rtc_unipi->regmap, MCP794XX_REG_CONTROL,
+                                MCP794XX_BIT_ALM0_EN, 0);
+       if (ret)
+               goto out;
+
+       rtc_update_irq(rtc_unipi->rtc, 1, RTC_AF | RTC_IRQF);
+
+out:
+       mutex_unlock(lock);
+
+       return IRQ_HANDLED;
+}
+
+static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+       struct rtc_unipi *rtc_unipi = dev_get_drvdata(dev);
+       u8 regs[10];
+       int ret;
+
+       if (!test_bit(HAS_ALARM, &rtc_unipi->flags))
+               return -EINVAL;
+
+       /* Read control and alarm 0 registers. */
+       ret = regmap_bulk_read(rtc_unipi->regmap, MCP794XX_REG_CONTROL, regs,
+                              sizeof(regs));
+       if (ret)
+               return ret;
+
+       t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN);
+
+       /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
+       t->time.tm_sec = bcd2bin(regs[3] & 0x7f);
+       t->time.tm_min = bcd2bin(regs[4] & 0x7f);
+       t->time.tm_hour = bcd2bin(regs[5] & 0x3f);
+       t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1;
+       t->time.tm_mday = bcd2bin(regs[7] & 0x3f);
+       t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1;
+       t->time.tm_year = -1;
+       t->time.tm_yday = -1;
+       t->time.tm_isdst = -1;
+
+       dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+               "enabled=%d polarity=%d irq=%d match=%lu\n", __func__,
+               t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+               t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled,
+               !!(regs[6] & MCP794XX_BIT_ALMX_POL),
+               !!(regs[6] & MCP794XX_BIT_ALMX_IF),
+               (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4);
+
+       return 0;
+}
+
+static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+       struct rtc_unipi *rtc_unipi = dev_get_drvdata(dev);
+       unsigned char regs[10];
+       int ret;
+
+       if (!test_bit(HAS_ALARM, &rtc_unipi->flags))
+               return -EINVAL;
+
+       dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+               "enabled=%d pending=%d\n", __func__,
+               t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+               t->time.tm_wday, t->time.tm_mday, t->time.tm_mon,
+               t->enabled, t->pending);
+
+       /* Read control and alarm 0 registers. */
+       ret = regmap_bulk_read(rtc_unipi->regmap, MCP794XX_REG_CONTROL, regs,
+                              sizeof(regs));
+       if (ret)
+               return ret;
+
+       /* Set alarm 0, using 24-hour and day-of-month modes. */
+       regs[3] = bin2bcd(t->time.tm_sec);
+       regs[4] = bin2bcd(t->time.tm_min);
+       regs[5] = bin2bcd(t->time.tm_hour);
+       regs[6] = bin2bcd(t->time.tm_wday + 1);
+       regs[7] = bin2bcd(t->time.tm_mday);
+       regs[8] = bin2bcd(t->time.tm_mon + 1);
+
+       /* Clear the alarm 0 interrupt flag. */
+       regs[6] &= ~MCP794XX_BIT_ALMX_IF;
+       /* Set alarm match: second, minute, hour, day, date, month. */
+       regs[6] |= MCP794XX_MSK_ALMX_MATCH;
+       /* Disable interrupt. We will not enable until completely programmed */
+       regs[0] &= ~MCP794XX_BIT_ALM0_EN;
+
+       ret = regmap_bulk_write(rtc_unipi->regmap, MCP794XX_REG_CONTROL, regs,
+                               sizeof(regs));
+       if (ret)
+               return ret;
+
+       if (!t->enabled)
+               return 0;
+       regs[0] |= MCP794XX_BIT_ALM0_EN;
+       return regmap_write(rtc_unipi->regmap, MCP794XX_REG_CONTROL, regs[0]);
+}
+
+static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+       struct rtc_unipi *rtc_unipi = dev_get_drvdata(dev);
+
+       if (!test_bit(HAS_ALARM, &rtc_unipi->flags))
+               return -EINVAL;
+
+       return regmap_update_bits(rtc_unipi->regmap, MCP794XX_REG_CONTROL,
+                                 MCP794XX_BIT_ALM0_EN,
+                                 enabled ? MCP794XX_BIT_ALM0_EN : 0);
+}
+
+static int mcp794xx_read_offset(struct device *dev, long *offset)
+{
+       struct rtc_unipi *rtc_unipi = dev_get_drvdata(dev);
+       int ret;
+       int reg;
+
+       ret = regmap_read(rtc_unipi->regmap, MCP794XX_REG_CALIBRATION, &reg);
+       if (ret < 0)
+               return ret;
+
+       if (reg & 0x80) { 
+               *offset = ((long) -(reg & 0x7f)); 
+       } else {
+               *offset = ((long) ((s8)(reg & 0x7f)));
+       }
+
+       return 0;
+}
+
+/*
+
+ */
+static int mcp794xx_set_offset(struct device *dev, long offset)
+{
+       struct rtc_unipi *rtc_unipi = dev_get_drvdata(dev);
+       s8 reg;
+
+       if (offset > 127)
+               reg = 127;
+       else if (offset < -127)
+               reg = 127 | 0x80;
+       else if (offset < 0)
+               reg = ((s8)(-offset)) | 0x80;
+       else
+               reg = (s8)(offset);
+
+       return regmap_write(rtc_unipi->regmap, MCP794XX_REG_CALIBRATION, reg);
+}
+
+/*----------------------------------------------------------------------*/
+
+static int rtc_unipi_nvram_read(void *priv, unsigned int offset, void *val,
+                            size_t bytes)
+{
+       struct rtc_unipi *rtc_unipi = priv;
+       /*const struct chip_desc *chip = &chips[ds1307->type];*/
+
+       return regmap_bulk_read(rtc_unipi->regmap, MCP794XX_NVRAM_OFFSET + offset,
+                               val, bytes);
+}
+
+static int rtc_unipi_nvram_write(void *priv, unsigned int offset, void *val,
+                             size_t bytes)
+{
+       struct rtc_unipi *rtc_unipi = priv;
+       /*const struct chip_desc *chip = &chips[ds1307->type];*/
+
+       return regmap_bulk_write(rtc_unipi->regmap, MCP794XX_NVRAM_OFFSET + offset,
+                                val, bytes);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+
+static const struct regmap_config regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+static int rtc_unipi_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct rtc_unipi                *rtc_unipi;
+       int                     err = -ENODEV;
+       int                     tmp, wday;
+       /*const struct chip_desc        *chip;*/
+       bool                    want_irq;
+       bool                    rtc_unipi_can_wakeup_device = false;
+       unsigned char           regs[8];
+       /*struct rtc_unipi_platform_data *pdata = dev_get_platdata(&client->dev);*/
+       struct rtc_time         tm;
+       unsigned long           timestamp;
+
+       rtc_unipi = devm_kzalloc(&client->dev, sizeof(struct rtc_unipi), GFP_KERNEL);
+       if (!rtc_unipi)
+               return -ENOMEM;
+
+       dev_set_drvdata(&client->dev, rtc_unipi);
+       rtc_unipi->dev = &client->dev;
+       rtc_unipi->name = client->name;
+
+       rtc_unipi->regmap = devm_regmap_init_i2c(client, &regmap_config);
+       if (IS_ERR(rtc_unipi->regmap)) {
+               dev_err(rtc_unipi->dev, "regmap allocation failed\n");
+               return PTR_ERR(rtc_unipi->regmap);
+       }
+
+       i2c_set_clientdata(client, rtc_unipi);
+
+       /*
+       if (client->dev.of_node) {
+               ds1307->type = (enum ds_type)
+                       of_device_get_match_data(&client->dev);
+               //chip = &chips[ds1307->type];
+       } else if (id) {
+               //chip = &chips[id->driver_data];
+               ds1307->type = id->driver_data;
+       } else {
+               const struct acpi_device_id *acpi_id;
+
+               acpi_id = acpi_match_device(ACPI_PTR(ds1307_acpi_ids),
+                                           ds1307->dev);
+               if (!acpi_id)
+                       return -ENODEV;
+               //chip = &chips[acpi_id->driver_data];
+               ds1307->type = acpi_id->driver_data;
+       }
+       */
+       want_irq = client->irq > 0/* && chip->alarm*/;
+
+#ifdef CONFIG_OF
+/*
+ * For devices with no IRQ directly connected to the SoC, the RTC chip
+ * can be forced as a wakeup source by stating that explicitly in
+ * the device's .dts file using the "wakeup-source" boolean property.
+ * If the "wakeup-source" property is set, don't request an IRQ.
+ * This will guarantee the 'wakealarm' sysfs entry is available on the device,
+ * if supported by the RTC.
+ */
+       if (/*chip->alarm && */ of_property_read_bool(client->dev.of_node, "wakeup-source"))
+               rtc_unipi_can_wakeup_device = true;
+#endif
+
+
+read_rtc:
+       /* read RTC registers */
+       err = regmap_bulk_read(rtc_unipi->regmap, 0 /*chip->offset*/, regs,
+                              sizeof(regs));
+       if (err) {
+               dev_dbg(rtc_unipi->dev, "read error %d\n", err);
+               goto exit;
+       }
+
+       /*
+        * minimal sanity checking; some chips (like DS1340) don't
+        * specify the extra bits as must-be-zero, but there are
+        * still a few values that are clearly out-of-range.
+        */
+       tmp = regs[DS1307_REG_SECS];
+
+               /* make sure that the backup battery is enabled */
+               if (!(regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) {
+                       regmap_write(rtc_unipi->regmap, DS1307_REG_WDAY,
+                                    regs[DS1307_REG_WDAY] |
+                                    MCP794XX_BIT_VBATEN);
+               }
+
+               /* clock halted?  turn it on, so clock can tick. */
+               if (!(tmp & MCP794XX_BIT_ST)) {
+                       regmap_write(rtc_unipi->regmap, DS1307_REG_SECS,
+                                    MCP794XX_BIT_ST);
+                       dev_warn(rtc_unipi->dev, "SET TIME!\n");
+                       goto read_rtc;
+               }
+               if (regs[MCP794XX_REG_CONTROL] & MCP794XX_BIT_EXTOSC) {
+                       regmap_write(rtc_unipi->regmap, MCP794XX_REG_CONTROL,
+                                   regs[MCP794XX_REG_CONTROL] & ~MCP794XX_BIT_EXTOSC);
+                       dev_warn(rtc_unipi->dev, "BAD OSCILLATOR SETTING!\n");
+               }
+
+
+       tmp = regs[DS1307_REG_HOUR];
+       if ((tmp & DS1307_BIT_12HR)) {
+               /*
+                * Be sure we're in 24 hour mode.  Multi-master systems
+                * take note...
+                */
+               tmp = bcd2bin(tmp & 0x1f);
+               if (tmp == 12)
+                       tmp = 0;
+               if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
+                       tmp += 12;
+               regmap_write(rtc_unipi->regmap, /*chip->offset + */DS1307_REG_HOUR,
+                            bin2bcd(tmp));
+       }
+       /*
+        * Some IPs have weekday reset value = 0x1 which might not correct
+        * hence compute the wday using the current date/month/year values
+        */
+       mcp794xx_get_time(rtc_unipi->dev, &tm);
+       wday = tm.tm_wday;
+       timestamp = rtc_tm_to_time64(&tm);
+       rtc_time64_to_tm(timestamp, &tm);
+
+       /*
+        * Check if reset wday is different from the computed wday
+        * If different then set the wday which we computed using
+        * timestamp
+        */
+       if (wday != tm.tm_wday)
+               regmap_update_bits(rtc_unipi->regmap, MCP794XX_REG_WEEKDAY,
+                                  MCP794XX_REG_WEEKDAY_WDAY_MASK,
+                                  tm.tm_wday + 1);
+
+       if (want_irq || rtc_unipi_can_wakeup_device) {
+               device_set_wakeup_capable(rtc_unipi->dev, true);
+               set_bit(HAS_ALARM, &rtc_unipi->flags);
+       }
+
+       rtc_unipi->rtc = devm_rtc_allocate_device(rtc_unipi->dev);
+       if (IS_ERR(rtc_unipi->rtc))
+               return PTR_ERR(rtc_unipi->rtc);
+
+       if (rtc_unipi_can_wakeup_device && !want_irq) {
+               dev_info(rtc_unipi->dev,
+                        "'wakeup-source' is set, request for an IRQ is disabled!\n");
+               /* We cannot support UIE mode if we do not have an IRQ line */
+               rtc_unipi->rtc->uie_unsupported = 1;
+       }
+
+       if (want_irq) {
+               err = devm_request_threaded_irq(rtc_unipi->dev, client->irq, NULL,
+                                               mcp794xx_irq,
+                                               IRQF_SHARED | IRQF_ONESHOT,
+                                               rtc_unipi->name, rtc_unipi);
+               if (err) {
+                       client->irq = 0;
+                       device_set_wakeup_capable(rtc_unipi->dev, false);
+                       clear_bit(HAS_ALARM, &rtc_unipi->flags);
+                       dev_err(rtc_unipi->dev, "unable to request IRQ!\n");
+               } else {
+                       dev_dbg(rtc_unipi->dev, "got IRQ %d\n", client->irq);
+               }
+       }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)
+       /*if (chip->nvram_size) {*/
+               rtc_unipi->nvmem_cfg.name = "rtc_unipi_nvram";
+               rtc_unipi->nvmem_cfg.word_size = 1;
+               rtc_unipi->nvmem_cfg.stride = 1;
+               rtc_unipi->nvmem_cfg.size = MCP794XX_NVRAM_SIZE;
+               rtc_unipi->nvmem_cfg.reg_read = rtc_unipi_nvram_read;
+               rtc_unipi->nvmem_cfg.reg_write = rtc_unipi_nvram_write;
+               rtc_unipi->nvmem_cfg.priv = rtc_unipi;
+
+               rtc_unipi->rtc->nvmem_config = &rtc_unipi->nvmem_cfg;
+               rtc_unipi->rtc->nvram_old_abi = true;
+       /*}*/
+#else
+           struct nvmem_config nvmem_cfg = {
+               .name = "rtc_unipi_nvram",
+               .word_size = 1,
+               .stride = 1,
+               .size = MCP794XX_NVRAM_SIZE,
+               .reg_read = rtc_unipi_nvram_read,
+               .reg_write = rtc_unipi_nvram_write,
+               .priv = rtc_unipi,
+           };
+
+           rtc_unipi->rtc->nvram_old_abi = true;
+           rtc_nvmem_register(rtc_unipi->rtc, &nvmem_cfg);
+
+#endif
+       rtc_unipi->rtc->ops = &mcp794xx_rtc_ops; /*chip->rtc_ops ?: &ds13xx_rtc_ops;*/
+       err = rtc_register_device(rtc_unipi->rtc);
+       if (err)
+               return err;
+
+       return 0;
+
+exit:
+       return err;
+}
+
+static struct i2c_driver rtc_unipi_driver = {
+       .driver = {
+               .name   = "rtc-unipi",
+               .of_match_table = of_match_ptr(rtc_unipi_of_match),
+               .acpi_match_table = ACPI_PTR(rtc_unipi_acpi_ids),
+       },
+       .probe          = rtc_unipi_probe,
+       .id_table       = rtc_unipi_id,
+};
+
+module_i2c_driver(rtc_unipi_driver);
+
+MODULE_DESCRIPTION("RTC driver for DS1307 and similar chips");
+MODULE_LICENSE("GPL");
index 5c3ffd301ce46ded935d48b12cbbdb7158e72726..48abdb963781a3d11bf272576d95d1ba5c6ada2b 100644 (file)
@@ -10,47 +10,43 @@ ifdef ARCH
     CCPAR += ARCH=${ARCH}
 endif
 
-SYMLINK_DIR_PATH = /run/kernel/unipi_spi
-
-SRC_DIR_PATH = $(PWD)/src
-BIN_DIR_PATH = $(PWD)/bin
 
 MODULE_MAKE_FILE = Makefile
+INSTALL = install
 
-C_SRC_FILES = unipi_spi.c
-C_SRC_FILES += unipi_iio.c
-C_SRC_FILES += unipi_gpio.c
-C_SRC_FILES += unipi_uart.c
-C_SRC_FILES += unipi_sysfs.c
-C_SRC_FILES += unipi_misc.c
-C_SRC_FILES += unipi_platform.c
-C_SRC_FILES += unipi_tty.c
+C_SRC_FILES = src/unipi_spi.c
+C_SRC_FILES += src/unipi_iio.c
+C_SRC_FILES += src/unipi_gpio.c
+C_SRC_FILES += src/unipi_uart.c
+C_SRC_FILES += src/unipi_sysfs.c
+C_SRC_FILES += src/unipi_misc.c
+C_SRC_FILES += src/unipi_platform.c
+C_SRC_FILES += src/unipi_tty.c
 
-H_SRC_FILES = unipi_spi.h
-H_SRC_FILES += unipi_iio.h
-H_SRC_FILES += unipi_gpio.h
-H_SRC_FILES += unipi_uart.h
-H_SRC_FILES += unipi_sysfs.h
-H_SRC_FILES += unipi_misc.h
-H_SRC_FILES += unipi_platform.h
-H_SRC_FILES += unipi_common.h
-H_SRC_FILES += unipi_tty.h
+H_SRC_FILES = src/unipi_spi.h
+H_SRC_FILES += src/unipi_iio.h
+H_SRC_FILES += src/unipi_gpio.h
+H_SRC_FILES += src/unipi_uart.h
+H_SRC_FILES += src/unipi_sysfs.h
+H_SRC_FILES += src/unipi_misc.h
+H_SRC_FILES += src/unipi_platform.h
+H_SRC_FILES += src/unipi_common.h
+H_SRC_FILES += src/unipi_tty.h
 
-OBJ_FILES = src/unipi_spi.o
-OBJ_FILES += src/unipi_iio.o
-OBJ_FILES += src/unipi_gpio.o
-OBJ_FILES += src/unipi_uart.o
-OBJ_FILES += src/unipi_sysfs.o
-OBJ_FILES += src/unipi_misc.o
-OBJ_FILES += src/unipi_platform.o
-OBJ_FILES += src/unipi_tty.o
+OBJ_FILES  = $(C_SRC_FILES:.c=.o)
+#OBJ_FILES = src/unipi_spi.o
+#OBJ_FILES += src/unipi_iio.o
+#OBJ_FILES += src/unipi_gpio.o
+#OBJ_FILES += src/unipi_uart.o
+#OBJ_FILES += src/unipi_sysfs.o
+#OBJ_FILES += src/unipi_misc.o
+#OBJ_FILES += src/unipi_platform.o
+#OBJ_FILES += src/unipi_tty.o
 
 KERNEL_MODULE_NAME = unipi
 obj-m += ${KERNEL_MODULE_NAME}.o
 unipi-objs := ${OBJ_FILES}
 
-TARGET_PLC_PATH = tomunipi:
-
 .PHONY: default
 default: all ;
 
@@ -60,25 +56,12 @@ all:
 modules_install: 
        make $(CCPAR) -C $(LINUX_DIR_PATH) M=${PWD} modules_install
 
+dkms:
+       $(INSTALL) -D $(MODULE_MAKE_FILE) -t $(INSTALL_MOD_PATH)
+       $(INSTALL) -D $(C_SRC_FILES) $(H_SRC_FILES) -t $(INSTALL_MOD_PATH)/src
+
 clean: 
        rm -f ${unipi-objs} src/.*.o.cmd
        rm -f ${KERNEL_MODULE_NAME}.ko ${KERNEL_MODULE_NAME}.mod.c .*.o.cmd .*.ko.cmd *.o modules.order Module.symvers
        rm -rf .tmp_versions
 
-transfer: clean symlink
-       scp ${BIN_DIR_PATH}/${KERNEL_MODULE_NAME}.ko ${TARGET_PLC_PATH}
-       
-symlink: clean
-       rm -r -f ${SYMLINK_DIR_PATH}
-       mkdir -p ${SYMLINK_DIR_PATH}/src
-       mkdir -p ${SYMLINK_DIR_PATH}/bin
-       cp ${PWD}/${MODULE_MAKE_FILE} ${SYMLINK_DIR_PATH} 
-       for f in ${C_SRC_FILES}; do\
-               ln -s ${SRC_DIR_PATH}/$$f ${SYMLINK_DIR_PATH}/src ;\
-               done
-       for f in ${H_SRC_FILES}; do\
-               ln -s ${SRC_DIR_PATH}/$$f ${SYMLINK_DIR_PATH}/src ;\
-               done
-       cd ${SYMLINK_DIR_PATH}; make all
-       mv ${SYMLINK_DIR_PATH}/${KERNEL_MODULE_NAME}.ko ${BIN_DIR_PATH}
-       rm -r -f ${SYMLINK_DIR_PATH}
index 932724c94f83ede29e162b3ad16b624a81acc180..4ce9a00b614e436feb0791c8842eec8feb137b8d 100644 (file)
@@ -52,7 +52,7 @@
 #if NEURONSPI_SCHED_REQUIRED > 0
        #include <uapi/linux/sched/types.h>
 #endif
-#define NEURONSPI_MAJOR_VERSIONSTRING "Version 1.22:2019:03:27"
+#define NEURONSPI_MAJOR_VERSIONSTRING "Version 1.23:2019:07:31"
 
 #define NEURONSPI_MAX_DEVS                             3
 #define NEURONSPI_MAX_UART                             16
index f189dbfe4573d5c7fbd11e1ee16f7559aa72a048..9f9e7a604bd9d767780a4140a8417d0b9a11f1aa 100644 (file)
@@ -121,7 +121,9 @@ static struct neuronspi_op_buffer UNIPISPI_IDLE_MESSAGE = {
     u16 unipi_spi_master_flag = 0;
     void (*unipi_spi_master_set_cs)(struct spi_device *spi, bool enable) = NULL;
     //cycles_t unipi_spi_cs_cycles;
+#ifdef USE_UNIPI_CPUFREQ_PATCH
        static struct cpufreq_policy * current_policy = NULL;
+#endif
 
 static void unipi_spi_set_cs(struct spi_device *spi, bool enable)
 {
@@ -138,6 +140,7 @@ static void unipi_spi_set_cs(struct spi_device *spi, bool enable)
             udelay(NEURONSPI_LAST_TRANSFER_DELAY - udelta);
         }
     }
+#ifdef USE_UNIPI_CPUFREQ_PATCH
        //current_policy = cpufreq_cpu_get_raw(task_cpu(current));
        current_policy = cpufreq_cpu_get_raw(0);
        if (current_policy && !enable) {
@@ -153,7 +156,7 @@ static void unipi_spi_set_cs(struct spi_device *spi, bool enable)
         current_policy->transition_task = current;
         spin_unlock(&current_policy->transition_lock);
        }
-
+#endif
        if (gpio_is_valid(-spi->cs_gpio)) {
                gpio_set_value(-spi->cs_gpio, enable);
         if ((unipi_spi_master_set_cs != NULL)  &&
@@ -165,13 +168,13 @@ static void unipi_spi_set_cs(struct spi_device *spi, bool enable)
             unipi_spi_master_set_cs(spi, enable);
     }
     if (d_data) d_data->last_cs_cycles = cs_cycles;
-             
+#ifdef USE_UNIPI_CPUFREQ_PATCH             
        if (current_policy && enable) {
         current_policy->transition_ongoing = false;
         current_policy->transition_task = NULL;
         wake_up(&current_policy->transition_wait);     
        }
-
+#endif
 }
 
 
@@ -1133,7 +1136,7 @@ void neuronspi_irq_proc(struct kthread_work *ws)
 static enum hrtimer_restart neuronspi_poll_timer_func(struct hrtimer *timer)
 {
     struct neuronspi_driver_data* n_spi = ((container_of((timer), struct neuronspi_driver_data, poll_timer)));
-    struct spi_device *spi = neuronspi_s_dev [n_spi->neuron_index];
+    //struct spi_device *spi = neuronspi_s_dev [n_spi->neuron_index];
     
        unipi_spi_trace_1(KERN_INFO "UNIPISPI: nspi%d POLL IRQ\n", n_spi->neuron_index);
        kthread_queue_work(n_spi->primary_worker, &n_spi->irq_work);