aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/dd/dd.c30
-rw-r--r--sys/mips/conf/CANNA29
-rw-r--r--sys/mips/conf/CI2031
-rw-r--r--sys/mips/conf/JZ478092
-rw-r--r--sys/mips/conf/JZ4780.hints2
-rw-r--r--sys/mips/conf/X100089
-rw-r--r--sys/mips/conf/X1000.hints2
-rw-r--r--sys/mips/ingenic/files.jz478026
-rw-r--r--sys/mips/ingenic/files.x100017
-rw-r--r--sys/mips/ingenic/jz4780_clk.h93
-rw-r--r--sys/mips/ingenic/jz4780_clk_gen.c317
-rw-r--r--sys/mips/ingenic/jz4780_clk_otg.c167
-rw-r--r--sys/mips/ingenic/jz4780_clk_pll.c234
-rw-r--r--sys/mips/ingenic/jz4780_clock.c831
-rw-r--r--sys/mips/ingenic/jz4780_clock.h36
-rw-r--r--sys/mips/ingenic/jz4780_cpuregs.h73
-rw-r--r--sys/mips/ingenic/jz4780_dme.c124
-rw-r--r--sys/mips/ingenic/jz4780_dwc_fdt.c202
-rw-r--r--sys/mips/ingenic/jz4780_efuse.c213
-rw-r--r--sys/mips/ingenic/jz4780_ehci.c345
-rw-r--r--sys/mips/ingenic/jz4780_gpio.c830
-rw-r--r--sys/mips/ingenic/jz4780_gpio_if.m41
-rw-r--r--sys/mips/ingenic/jz4780_intr.c333
-rw-r--r--sys/mips/ingenic/jz4780_machdep.c296
-rw-r--r--sys/mips/ingenic/jz4780_mmc.c1002
-rw-r--r--sys/mips/ingenic/jz4780_mp.c182
-rw-r--r--sys/mips/ingenic/jz4780_mpboot.S62
-rw-r--r--sys/mips/ingenic/jz4780_nand.c123
-rw-r--r--sys/mips/ingenic/jz4780_nemc.c373
-rw-r--r--sys/mips/ingenic/jz4780_ohci.c318
-rw-r--r--sys/mips/ingenic/jz4780_pinctrl.c260
-rw-r--r--sys/mips/ingenic/jz4780_pinctrl.h33
-rw-r--r--sys/mips/ingenic/jz4780_regs.h787
-rw-r--r--sys/mips/ingenic/jz4780_timer.c337
-rw-r--r--sys/mips/ingenic/jz4780_uart.c220
35 files changed, 8120 insertions, 30 deletions
diff --git a/bin/dd/dd.c b/bin/dd/dd.c
index a13213dcc8c6..56f8efef7a53 100644
--- a/bin/dd/dd.c
+++ b/bin/dd/dd.c
@@ -48,13 +48,10 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/conf.h>
-#include <sys/capsicum.h>
#include <sys/disklabel.h>
#include <sys/filio.h>
-#include <sys/mtio.h>
#include <assert.h>
-#include <capsicum_helpers.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
@@ -95,10 +92,6 @@ main(int argc __unused, char *argv[])
jcl(argv);
setup();
- caph_cache_catpages();
- if (cap_enter() == -1 && errno != ENOSYS)
- err(1, "unable to enter capability mode");
-
(void)signal(SIGINFO, siginfo_handler);
(void)signal(SIGINT, terminate);
@@ -132,8 +125,6 @@ static void
setup(void)
{
u_int cnt;
- cap_rights_t rights;
- unsigned long cmds[] = { FIODTYPE, MTIOCTOP };
if (in.name == NULL) {
in.name = "stdin";
@@ -142,20 +133,13 @@ setup(void)
in.fd = open(in.name, O_RDONLY, 0);
if (in.fd == -1)
err(1, "%s", in.name);
- if (caph_limit_stdin() == -1)
- err(1, "unable to limit capability rights");
}
getfdtype(&in);
- cap_rights_init(&rights, CAP_READ, CAP_SEEK);
- if (cap_rights_limit(in.fd, &rights) == -1 && errno != ENOSYS)
- err(1, "unable to limit capability rights");
-
if (files_cnt > 1 && !(in.flags & ISTAPE))
errx(1, "files is not supported for non-tape devices");
- cap_rights_set(&rights, CAP_WRITE, CAP_FTRUNCATE, CAP_IOCTL);
if (out.name == NULL) {
/* No way to check for read access here. */
out.fd = STDOUT_FILENO;
@@ -172,27 +156,13 @@ setup(void)
if (out.fd == -1) {
out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
out.flags |= NOREAD;
- cap_rights_clear(&rights, CAP_READ);
}
if (out.fd == -1)
err(1, "%s", out.name);
- if (caph_limit_stdout() == -1)
- err(1, "unable to limit capability rights");
}
getfdtype(&out);
- if (cap_rights_limit(out.fd, &rights) == -1 && errno != ENOSYS)
- err(1, "unable to limit capability rights");
- if (cap_ioctls_limit(out.fd, cmds, nitems(cmds)) == -1 &&
- errno != ENOSYS)
- err(1, "unable to limit capability rights");
-
- if (in.fd != STDERR_FILENO && out.fd != STDERR_FILENO) {
- if (caph_limit_stderr() == -1)
- err(1, "unable to limit capability rights");
- }
-
/*
* Allocate space for the input and output buffers. If not doing
* record oriented I/O, only need a single buffer.
diff --git a/sys/mips/conf/CANNA b/sys/mips/conf/CANNA
new file mode 100644
index 000000000000..0d6f81b52c30
--- /dev/null
+++ b/sys/mips/conf/CANNA
@@ -0,0 +1,29 @@
+# CANNA -- Kernel config for Ingenic CANNA board
+#
+# $FreeBSD$
+
+include "X1000"
+ident CANNA
+
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=ingenic/canna.dts
+
+#options KTR
+#options KTR_CPUMASK=0x3
+#options KTR_MASK=(KTR_GEN)
+#options KTR_COMPILE=(KTR_GEN)
+#options KTR_VERBOSE
+
+# Uncomment for NFS root
+#options BOOTP
+#options BOOTP_NFSROOT
+#options BOOTP_NFSV3
+#options BOOTP_WIRED_TO=dme0
+#options BOOTP_COMPAT
+options ROOTDEVNAME=\"ufs:mmcsd0s3\"
+
+makeoptions TRAMPLOADADDR=0x88000000
+
+#options VERBOSE_SYSINIT
+options PRINTF_BUFR_SIZE=256
diff --git a/sys/mips/conf/CI20 b/sys/mips/conf/CI20
new file mode 100644
index 000000000000..9d1940d85e85
--- /dev/null
+++ b/sys/mips/conf/CI20
@@ -0,0 +1,31 @@
+# CI20 -- Kernel config for Creator CI20 board
+#
+# $FreeBSD$
+
+include "JZ4780"
+ident CI20
+
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=ingenic/ci20.dts
+
+#options KTR
+#options KTR_CPUMASK=0x3
+#options KTR_MASK=(KTR_GEN)
+#options KTR_COMPILE=(KTR_GEN)
+#options KTR_VERBOSE
+
+# Uncomment for NFS root
+#options BOOTP
+#options BOOTP_NFSROOT
+#options BOOTP_NFSV3
+#options BOOTP_WIRED_TO=dme0
+#options BOOTP_COMPAT
+
+options ROOTDEVNAME=\"ufs:mmcsd0\"
+
+makeoptions TRAMPLOADADDR=0x88000000
+
+#options VERBOSE_SYSINIT
+device dme
+options PRINTF_BUFR_SIZE=256
diff --git a/sys/mips/conf/JZ4780 b/sys/mips/conf/JZ4780
new file mode 100644
index 000000000000..3df33a1cdb2e
--- /dev/null
+++ b/sys/mips/conf/JZ4780
@@ -0,0 +1,92 @@
+# JZ4780 -- Kernel config for Ingenic JZ47XX boards
+#
+# $FreeBSD$
+
+ident JZ4780
+machine mips mipsel
+cpu CPU_XBURST
+cpu CPU_MIPS4KC
+
+makeoptions KERNLOADADDR=0x80020000
+makeoptions ARCH_FLAGS="-EL -march=mips32r2"
+
+# Don't build any modules yet.
+makeoptions MODULES_OVERRIDE=""
+
+files "../ingenic/files.jz4780"
+hints "JZ4780.hints" #Default places to look for devices.
+
+makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
+
+options INTRNG # Borrow interrupt code from ARM
+options MIPS_NIRQ=264 # 8 cpuintc + 64 intc + 6 * 23 gpio
+
+options DDB
+options KDB
+options BREAK_TO_DEBUGGER
+
+options COMPAT_FREEBSD10
+
+options SCHED_4BSD #4BSD scheduler
+options INET #InterNETworking
+options NFSCL #Network Filesystem Client
+options NFS_ROOT #NFS usable as /, requires NFSCL
+options NFSLOCKD #Network Lock Manager
+options PSEUDOFS #Pseudo-filesystem framework
+options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions
+
+options FFS #Berkeley Fast Filesystem
+options SOFTUPDATES #Enable FFS soft updates support
+options UFS_ACL #Support for access control lists
+options UFS_DIRHASH #Improve performance on big directories
+#options ROOTDEVNAME=\"ufs:ada0\"
+
+options GEOM_LABEL # Provides labelization
+options GEOM_PART_GPT # GUID Partition Tables.
+#options GEOM_RAID # Soft RAID functionality.
+
+# Debugging for use in -current
+#options DEADLKRES #Enable the deadlock resolver
+options INVARIANTS #Enable calls of extra sanity checking
+options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS
+#options WITNESS #Enable checks to detect deadlocks and cycles
+#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed
+
+# Make an SMP-capable kernel by default
+# options SMP # Symmetric MultiProcessor Kernel
+
+device loop
+device ether
+#device le
+device miibus
+device bpf
+device md
+device uart
+device random
+
+device fdt_pinctrl
+
+device clk
+device regulator
+device ext_resources
+
+device gpio
+
+device scbus
+device da
+
+device mmc
+device mmcsd
+
+# USB support
+options USB_DEBUG # enable debug msgs
+options USB_HOST_ALIGN=128 # L2 cache line size
+device ohci # OHCI PCI->USB interface
+device ehci # EHCI PCI->USB interface (USB 2.0)
+device dwcotg # DesignWare HS OTG controller
+device usb # USB Bus (required)
+#device udbp # USB Double Bulk Pipe devices
+device uhid # "Human Interface Devices"
+#device ulpt # Printer
+device umass # Disks/Mass storage - Requires scbus and da
+device ums # Mouse
diff --git a/sys/mips/conf/JZ4780.hints b/sys/mips/conf/JZ4780.hints
new file mode 100644
index 000000000000..6986b85032de
--- /dev/null
+++ b/sys/mips/conf/JZ4780.hints
@@ -0,0 +1,2 @@
+# $FreeBSD$
+# device.hints
diff --git a/sys/mips/conf/X1000 b/sys/mips/conf/X1000
new file mode 100644
index 000000000000..8659f1112e4b
--- /dev/null
+++ b/sys/mips/conf/X1000
@@ -0,0 +1,89 @@
+# X1000 -- Kernel config for Ingenic X1000 boards
+#
+# $FreeBSD$
+
+ident X1000
+machine mips mipsel
+cpu CPU_XBURST
+cpu CPU_MIPS4KC
+
+makeoptions KERNLOADADDR=0x80020000
+makeoptions ARCH_FLAGS="-EL -march=mips32r2"
+
+# Don't build any modules yet.
+makeoptions MODULES_OVERRIDE=""
+
+files "../ingenic/files.x1000"
+hints "X1000.hints" #Default places to look for devices.
+
+makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
+
+options INTRNG # Borrow interrupt code from ARM
+options MIPS_NIRQ=264 # 8 cpuintc + 64 intc + 6 * 23 gpio
+
+options DDB
+options KDB
+options BREAK_TO_DEBUGGER
+
+options COMPAT_FREEBSD10
+
+options SCHED_4BSD #4BSD scheduler
+options INET #InterNETworking
+options NFSCL #Network Filesystem Client
+options NFS_ROOT #NFS usable as /, requires NFSCL
+options NFSLOCKD #Network Lock Manager
+options PSEUDOFS #Pseudo-filesystem framework
+options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions
+
+options FFS #Berkeley Fast Filesystem
+options SOFTUPDATES #Enable FFS soft updates support
+options UFS_ACL #Support for access control lists
+options UFS_DIRHASH #Improve performance on big directories
+#options ROOTDEVNAME=\"ufs:ada0\"
+
+options GEOM_LABEL # Provides labelization
+options GEOM_PART_GPT # GUID Partition Tables.
+#options GEOM_RAID # Soft RAID functionality.
+
+# Debugging for use in -current
+#options DEADLKRES #Enable the deadlock resolver
+options INVARIANTS #Enable calls of extra sanity checking
+options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS
+#options WITNESS #Enable checks to detect deadlocks and cycles
+#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed
+
+device loop
+device ether
+#device le
+device miibus
+device bpf
+device md
+device uart
+device random
+
+device fdt_pinctrl
+
+device clk
+device regulator
+device ext_resources
+
+device gpio
+
+device scbus
+device da
+
+device mmc
+device mmcsd
+
+# USB support
+#options USB_DEBUG # enable debug msgs
+#options USB_HOST_ALIGN=128 # L2 cache line size
+#device ohci # OHCI PCI->USB interface
+#device ehci # EHCI PCI->USB interface (USB 2.0)
+#device dwcotg # DesignWare HS OTG controller
+#device usb # USB Bus (required)
+#device udbp # USB Double Bulk Pipe devices
+#device uhid # "Human Interface Devices"
+#device ulpt # Printer
+#device umass # Disks/Mass storage - Requires scbus and da
+#device ums # Mouse
diff --git a/sys/mips/conf/X1000.hints b/sys/mips/conf/X1000.hints
new file mode 100644
index 000000000000..6986b85032de
--- /dev/null
+++ b/sys/mips/conf/X1000.hints
@@ -0,0 +1,2 @@
+# $FreeBSD$
+# device.hints
diff --git a/sys/mips/ingenic/files.jz4780 b/sys/mips/ingenic/files.jz4780
new file mode 100644
index 000000000000..74f9609b8d21
--- /dev/null
+++ b/sys/mips/ingenic/files.jz4780
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+mips/ingenic/jz4780_dwc_fdt.c optional dwcotg
+mips/ingenic/jz4780_ehci.c optional ehci
+mips/ingenic/jz4780_mmc.c optional mmc
+mips/ingenic/jz4780_ohci.c optional ohci
+mips/ingenic/jz4780_uart.c optional uart
+
+mips/ingenic/jz4780_clock.c standard
+mips/ingenic/jz4780_clk_gen.c standard
+mips/ingenic/jz4780_clk_otg.c standard
+mips/ingenic/jz4780_clk_pll.c standard
+mips/ingenic/jz4780_efuse.c standard
+mips/ingenic/jz4780_intr.c standard
+mips/ingenic/jz4780_gpio.c standard
+mips/ingenic/jz4780_machdep.c standard
+mips/ingenic/jz4780_nemc.c standard
+mips/ingenic/jz4780_pinctrl.c standard
+mips/ingenic/jz4780_timer.c standard
+
+# SMP
+mips/ingenic/jz4780_mp.c optional smp
+mips/ingenic/jz4780_mpboot.S optional smp
+
+# Custom interface between pinctrl and gpio
+mips/ingenic/jz4780_gpio_if.m standard
diff --git a/sys/mips/ingenic/files.x1000 b/sys/mips/ingenic/files.x1000
new file mode 100644
index 000000000000..db3ae4604de5
--- /dev/null
+++ b/sys/mips/ingenic/files.x1000
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+mips/ingenic/jz4780_mmc.c optional mmc
+mips/ingenic/jz4780_uart.c optional uart
+
+mips/ingenic/jz4780_clock.c standard
+mips/ingenic/jz4780_clk_gen.c standard
+mips/ingenic/jz4780_clk_otg.c standard
+mips/ingenic/jz4780_clk_pll.c standard
+mips/ingenic/jz4780_intr.c standard
+mips/ingenic/jz4780_gpio.c standard
+mips/ingenic/jz4780_machdep.c standard
+mips/ingenic/jz4780_pinctrl.c standard
+mips/ingenic/jz4780_timer.c standard
+
+# Custom interface between pinctrl and gpio
+mips/ingenic/jz4780_gpio_if.m standard
diff --git a/sys/mips/ingenic/jz4780_clk.h b/sys/mips/ingenic/jz4780_clk.h
new file mode 100644
index 000000000000..43ade45dd699
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_clk.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MIPS_INGENIC_JZ4780_CLK_H
+#define _MIPS_INGENIC_JZ4780_CLK_H
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/clk/clk_gate.h>
+
+/* Convenience bitfiled manipulation macros */
+#define REG_MSK(field) (((1u << field ## _WIDTH) - 1) << field ##_SHIFT)
+#define REG_VAL(field, val) ((val) << field ##_SHIFT)
+#define REG_CLR(reg, field) ((reg) & ~REG_MSK(field))
+#define REG_GET(reg, field) (((reg) & REG_MSK(field)) >> field ##_SHIFT)
+#define REG_SET(reg, field, val) (REG_CLR(reg, field) | REG_VAL(field, val))
+
+/* Common clock macros */
+#define CLK_LOCK(_sc) mtx_lock((_sc)->clk_mtx)
+#define CLK_UNLOCK(_sc) mtx_unlock((_sc)->clk_mtx)
+
+#define CLK_WR_4(_sc, off, val) bus_write_4((_sc)->clk_res, (off), (val))
+#define CLK_RD_4(_sc, off) bus_read_4((_sc)->clk_res, (off))
+
+struct jz4780_clk_mux_descr {
+ uint16_t mux_reg;
+ uint16_t mux_shift: 5;
+ uint16_t mux_bits: 5;
+ uint16_t mux_map: 4; /* Map into mux space */
+};
+
+struct jz4780_clk_div_descr {
+ uint16_t div_reg;
+ uint16_t div_shift: 5;
+ uint16_t div_bits: 5;
+ uint16_t div_lg: 5;
+ int div_ce_bit: 6; /* -1, if CE bit is not present */
+ int div_st_bit: 6; /* Can be negative */
+ int div_busy_bit: 6; /* Can be negative */
+};
+
+struct jz4780_clk_descr {
+ uint16_t clk_id: 6;
+ uint16_t clk_type: 3;
+ int clk_gate_bit: 7; /* Can be negative */
+ struct jz4780_clk_mux_descr clk_mux;
+ struct jz4780_clk_div_descr clk_div;
+ const char *clk_name;
+ const char *clk_pnames[4];
+};
+
+/* clk_type bits */
+#define CLK_MASK_GATE 0x01
+#define CLK_MASK_DIV 0x02
+#define CLK_MASK_MUX 0x04
+
+extern int jz4780_clk_gen_register(struct clkdom *clkdom,
+ const struct jz4780_clk_descr *descr, struct mtx *dev_mtx,
+ struct resource *mem_res);
+
+extern int jz4780_clk_pll_register(struct clkdom *clkdom,
+ struct clknode_init_def *clkdef, struct mtx *dev_mtx,
+ struct resource *mem_res, uint32_t mem_reg);
+
+extern int jz4780_clk_otg_register(struct clkdom *clkdom,
+ struct clknode_init_def *clkdef, struct mtx *dev_mtx,
+ struct resource *mem_res);
+
+#endif /* _MIPS_INGENIC_JZ4780_CLK_PLL_H */
diff --git a/sys/mips/ingenic/jz4780_clk_gen.c b/sys/mips/ingenic/jz4780_clk_gen.c
new file mode 100644
index 000000000000..7c65fb824d93
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_clk_gen.c
@@ -0,0 +1,317 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Ingenic JZ4780 generic CGU clock driver.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+
+#include <mips/ingenic/jz4780_clk.h>
+#include <mips/ingenic/jz4780_regs.h>
+
+/* JZ4780 generic mux and div clocks implementation */
+static int jz4780_clk_gen_init(struct clknode *clk, device_t dev);
+static int jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq);
+static int jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
+ uint64_t *fout, int flags, int *stop);
+static int jz4780_clk_gen_set_gate(struct clknode *clk, bool enable);
+static int jz4780_clk_gen_set_mux(struct clknode *clk, int src);
+
+struct jz4780_clk_gen_sc {
+ struct mtx *clk_mtx;
+ struct resource *clk_res;
+ int clk_reg;
+ const struct jz4780_clk_descr *clk_descr;
+};
+
+/*
+ * JZ4780 clock PLL clock methods
+ */
+static clknode_method_t jz4780_clk_gen_methods[] = {
+ CLKNODEMETHOD(clknode_init, jz4780_clk_gen_init),
+ CLKNODEMETHOD(clknode_set_gate, jz4780_clk_gen_set_gate),
+ CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_gen_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, jz4780_clk_gen_set_freq),
+ CLKNODEMETHOD(clknode_set_mux, jz4780_clk_gen_set_mux),
+
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_gen_class, jz4780_clk_gen_methods,
+ sizeof(struct jz4780_clk_gen_sc), clknode_class);
+
+static inline unsigned
+mux_to_reg(unsigned src, unsigned map)
+{
+ unsigned ret, bit;
+
+ bit = (1u << 3);
+ for (ret = 0; bit; ret++, bit >>= 1) {
+ if (map & bit) {
+ if (src-- == 0)
+ return (ret);
+ }
+ }
+ panic("mux_to_reg");
+}
+
+static inline unsigned
+reg_to_mux(unsigned reg, unsigned map)
+{
+ unsigned ret, bit;
+
+ bit = (1u << 3);
+ for (ret = 0; reg; reg--, bit >>= 1)
+ if (map & bit)
+ ret++;
+ return (ret);
+}
+
+static int
+jz4780_clk_gen_init(struct clknode *clk, device_t dev)
+{
+ struct jz4780_clk_gen_sc *sc;
+ uint32_t reg, msk, parent_idx;
+
+ sc = clknode_get_softc(clk);
+ CLK_LOCK(sc);
+ /* Figure our parent out */
+ if (sc->clk_descr->clk_type & CLK_MASK_MUX) {
+ msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
+ reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
+ reg = (reg >> sc->clk_descr->clk_mux.mux_shift) & msk;
+ parent_idx = reg_to_mux(reg, sc->clk_descr->clk_mux.mux_map);
+ } else
+ parent_idx = 0;
+ CLK_UNLOCK(sc);
+
+ clknode_init_parent_idx(clk, parent_idx);
+ return (0);
+}
+
+static int
+jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct jz4780_clk_gen_sc *sc;
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+
+ /* Calculate divisor frequency */
+ if (sc->clk_descr->clk_type & CLK_MASK_DIV) {
+ uint32_t msk;
+
+ msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
+ reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
+ reg = (reg >> sc->clk_descr->clk_div.div_shift) & msk;
+ reg = (reg + 1) << sc->clk_descr->clk_div.div_lg;
+ *freq /= reg;
+ }
+ return (0);
+}
+
+#define DIV_TIMEOUT 100
+
+static int
+jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
+ uint64_t *fout, int flags, int *stop)
+{
+ struct jz4780_clk_gen_sc *sc;
+ uint64_t _fout;
+ uint32_t divider, div_reg, div_msk, reg;
+ int rv;
+
+ sc = clknode_get_softc(clk);
+
+ divider = fin / *fout;
+
+ /* Adjust for divider multiplier */
+ div_reg = divider >> sc->clk_descr->clk_div.div_lg;
+ divider = div_reg << sc->clk_descr->clk_div.div_lg;
+
+ _fout = fin / divider;
+
+ /* Rounding */
+ if ((flags & CLK_SET_ROUND_UP) && (*fout < _fout))
+ div_reg--;
+ else if ((flags & CLK_SET_ROUND_DOWN) && (*fout > _fout))
+ div_reg++;
+ if (div_reg == 0)
+ div_reg = 1;
+
+ div_msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
+
+ *stop = 1;
+ if (div_reg > div_msk + 1) {
+ *stop = 0;
+ div_reg = div_msk;
+ }
+
+ divider = (div_reg << sc->clk_descr->clk_div.div_lg);
+ div_reg--;
+
+ if ((flags & CLK_SET_DRYRUN) != 0) {
+ if (*stop != 0 && *fout != fin / divider &&
+ (flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0)
+ return (ERANGE);
+ *fout = fin / divider;
+ return (0);
+ }
+
+ CLK_LOCK(sc);
+ /* Apply the new divider value */
+ reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
+ reg &= ~(div_msk << sc->clk_descr->clk_div.div_shift);
+ reg |= (div_reg << sc->clk_descr->clk_div.div_shift);
+ /* Set the change enable bit, it present */
+ if (sc->clk_descr->clk_div.div_ce_bit >= 0)
+ reg |= (1u << sc->clk_descr->clk_div.div_ce_bit);
+ /* Clear stop bit, it present */
+ if (sc->clk_descr->clk_div.div_st_bit >= 0)
+ reg &= ~(1u << sc->clk_descr->clk_div.div_st_bit);
+ /* Initiate the change */
+ CLK_WR_4(sc, sc->clk_descr->clk_div.div_reg, reg);
+
+ /* Wait for busy bit to clear indicating the change is complete */
+ rv = 0;
+ if (sc->clk_descr->clk_div.div_busy_bit >= 0) {
+ int i;
+
+ for (i = 0; i < DIV_TIMEOUT; i++) {
+ reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
+ if (!(reg & (1u << sc->clk_descr->clk_div.div_busy_bit)))
+ break;
+ DELAY(1000);
+ }
+ if (i == DIV_TIMEOUT)
+ rv = ETIMEDOUT;
+ }
+ CLK_UNLOCK(sc);
+
+ *fout = fin / divider;
+ return (rv);
+}
+
+static int
+jz4780_clk_gen_set_mux(struct clknode *clk, int src)
+{
+ struct jz4780_clk_gen_sc *sc;
+ uint32_t reg, msk;
+
+ sc = clknode_get_softc(clk);
+
+ /* Only mux nodes are capable of being reparented */
+ if (!(sc->clk_descr->clk_type & CLK_MASK_MUX))
+ return (src ? EINVAL : 0);
+
+ msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
+ src = mux_to_reg(src & msk, sc->clk_descr->clk_mux.mux_map);
+
+ CLK_LOCK(sc);
+ reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
+ reg &= ~(msk << sc->clk_descr->clk_mux.mux_shift);
+ reg |= (src << sc->clk_descr->clk_mux.mux_shift);
+ CLK_WR_4(sc, sc->clk_descr->clk_mux.mux_reg, reg);
+ CLK_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+jz4780_clk_gen_set_gate(struct clknode *clk, bool enable)
+{
+ struct jz4780_clk_gen_sc *sc;
+ uint32_t off, reg, bit;
+
+ sc = clknode_get_softc(clk);
+
+ /* Check is clock can be gated */
+ if (sc->clk_descr->clk_gate_bit < 0)
+ return 0;
+
+ bit = sc->clk_descr->clk_gate_bit;
+ if (bit < 32) {
+ off = JZ_CLKGR0;
+ } else {
+ off = JZ_CLKGR1;
+ bit -= 32;
+ }
+
+ CLK_LOCK(sc);
+ reg = CLK_RD_4(sc, off);
+ if (enable)
+ reg &= ~(1u << bit);
+ else
+ reg |= (1u << bit);
+ CLK_WR_4(sc, off, reg);
+ CLK_UNLOCK(sc);
+
+ return (0);
+}
+
+
+int jz4780_clk_gen_register(struct clkdom *clkdom,
+ const struct jz4780_clk_descr *descr, struct mtx *dev_mtx,
+ struct resource *mem_res)
+{
+ struct clknode_init_def clkdef;
+ struct clknode *clk;
+ struct jz4780_clk_gen_sc *sc;
+
+ clkdef.id = descr->clk_id;
+ clkdef.name = __DECONST(char *, descr->clk_name);
+ /* Silly const games to work around API deficiency */
+ clkdef.parent_names = (const char **)(uintptr_t)&descr->clk_pnames[0];
+ clkdef.flags = CLK_NODE_STATIC_STRINGS;
+ if (descr->clk_type & CLK_MASK_MUX)
+ clkdef.parent_cnt = __bitcount16(descr->clk_mux.mux_map);
+ else
+ clkdef.parent_cnt = 1;
+
+ clk = clknode_create(clkdom, &jz4780_clk_gen_class, &clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+ sc->clk_mtx = dev_mtx;
+ sc->clk_res = mem_res;
+ sc->clk_descr = descr;
+ clknode_register(clkdom, clk);
+
+ return (0);
+}
diff --git a/sys/mips/ingenic/jz4780_clk_otg.c b/sys/mips/ingenic/jz4780_clk_otg.c
new file mode 100644
index 000000000000..26f2753a1a5f
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_clk_otg.c
@@ -0,0 +1,167 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Ingenic JZ4780 OTG PHY clock driver.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+
+#include <mips/ingenic/jz4780_clk.h>
+#include <mips/ingenic/jz4780_regs.h>
+
+/* JZ4780 OTG PHY clock */
+static int jz4780_clk_otg_init(struct clknode *clk, device_t dev);
+static int jz4780_clk_otg_recalc_freq(struct clknode *clk, uint64_t *freq);
+static int jz4780_clk_otg_set_freq(struct clknode *clk, uint64_t fin,
+ uint64_t *fout, int flags, int *stop);
+
+struct jz4780_clk_otg_sc {
+ struct mtx *clk_mtx;
+ struct resource *clk_res;
+};
+
+/*
+ * JZ4780 OTG PHY clock methods
+ */
+static clknode_method_t jz4780_clk_otg_methods[] = {
+ CLKNODEMETHOD(clknode_init, jz4780_clk_otg_init),
+ CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_otg_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, jz4780_clk_otg_set_freq),
+
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_otg_class, jz4780_clk_otg_methods,
+ sizeof(struct jz4780_clk_otg_sc), clknode_class);
+
+static int
+jz4780_clk_otg_init(struct clknode *clk, device_t dev)
+{
+ struct jz4780_clk_otg_sc *sc;
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+ CLK_LOCK(sc);
+ /* Force the use fo the core clock */
+ reg = CLK_RD_4(sc, JZ_USBPCR1);
+ reg &= ~PCR_REFCLK_M;
+ reg |= PCR_REFCLK_CORE;
+ CLK_WR_4(sc, JZ_USBPCR1, reg);
+ CLK_UNLOCK(sc);
+
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static const struct {
+ uint32_t div_val;
+ uint32_t freq;
+} otg_div_table[] = {
+ { PCR_CLK_12, 12000000 },
+ { PCR_CLK_192, 19200000 },
+ { PCR_CLK_24, 24000000 },
+ { PCR_CLK_48, 48000000 }
+};
+
+static int
+jz4780_clk_otg_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct jz4780_clk_otg_sc *sc;
+ uint32_t reg;
+ int i;
+
+ sc = clknode_get_softc(clk);
+ reg = CLK_RD_4(sc, JZ_USBPCR1);
+ reg &= PCR_CLK_M;
+
+ for (i = 0; i < nitems(otg_div_table); i++)
+ if (otg_div_table[i].div_val == reg)
+ *freq = otg_div_table[i].freq;
+ return (0);
+}
+
+static int
+jz4780_clk_otg_set_freq(struct clknode *clk, uint64_t fin,
+ uint64_t *fout, int flags, int *stop)
+{
+ struct jz4780_clk_otg_sc *sc;
+ uint32_t reg;
+ int i;
+
+ sc = clknode_get_softc(clk);
+
+ for (i = 0; i < nitems(otg_div_table) - 1; i++) {
+ if (*fout < (otg_div_table[i].freq + otg_div_table[i + 1].freq) / 2)
+ break;
+ }
+
+ *fout = otg_div_table[i].freq;
+
+ *stop = 1;
+ if (flags & CLK_SET_DRYRUN)
+ return (0);
+
+ CLK_LOCK(sc);
+ reg = CLK_RD_4(sc, JZ_USBPCR1);
+ /* Set the calculated values */
+ reg &= ~PCR_CLK_M;
+ reg |= otg_div_table[i].div_val;
+ /* Initiate the change */
+ CLK_WR_4(sc, JZ_USBPCR1, reg);
+ CLK_UNLOCK(sc);
+
+ return (0);
+}
+
+int jz4780_clk_otg_register(struct clkdom *clkdom,
+ struct clknode_init_def *clkdef, struct mtx *dev_mtx,
+ struct resource *mem_res)
+{
+ struct clknode *clk;
+ struct jz4780_clk_otg_sc *sc;
+
+ clk = clknode_create(clkdom, &jz4780_clk_otg_class, clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+ sc->clk_mtx = dev_mtx;
+ sc->clk_res = mem_res;
+ clknode_register(clkdom, clk);
+ return (0);
+}
diff --git a/sys/mips/ingenic/jz4780_clk_pll.c b/sys/mips/ingenic/jz4780_clk_pll.c
new file mode 100644
index 000000000000..23710e54ac7f
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_clk_pll.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Ingenic JZ4780 CGU driver.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+
+#include <mips/ingenic/jz4780_clk.h>
+
+/**********************************************************************
+ * JZ4780 PLL control register bit fields
+ **********************************************************************/
+#define CGU_PLL_M_SHIFT 19
+#define CGU_PLL_M_WIDTH 13
+
+#define CGU_PLL_N_SHIFT 13
+#define CGU_PLL_N_WIDTH 6
+
+#define CGU_PLL_OD_SHIFT 9
+#define CGU_PLL_OD_WIDTH 4
+
+#define CGU_PLL_LOCK_SHIFT 6
+#define CGU_PLL_LOCK_WIDTH 1
+
+#define CGU_PLL_ON_SHIFT 4
+#define CGU_PLL_ON_WIDTH 1
+
+#define CGU_PLL_MODE_SHIFT 3
+#define CGU_PLL_MODE_WIDTH 1
+
+#define CGU_PLL_BP_SHIFT 1
+#define CGU_PLL_BP_WIDTH 1
+
+#define CGU_PLL_EN_SHIFT 0
+#define CGU_PLL_EN_WIDTH 1
+
+/* JZ4780 PLL clock */
+static int jz4780_clk_pll_init(struct clknode *clk, device_t dev);
+static int jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq);
+static int jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin,
+ uint64_t *fout, int flags, int *stop);
+
+struct jz4780_clk_pll_sc {
+ struct mtx *clk_mtx;
+ struct resource *clk_res;
+ uint32_t clk_reg;
+};
+
+/*
+ * JZ4780 PLL clock methods
+ */
+static clknode_method_t jz4780_clk_pll_methods[] = {
+ CLKNODEMETHOD(clknode_init, jz4780_clk_pll_init),
+ CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_pll_recalc_freq),
+ CLKNODEMETHOD(clknode_set_freq, jz4780_clk_pll_set_freq),
+
+ CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_pll_class, jz4780_clk_pll_methods,
+ sizeof(struct jz4780_clk_pll_sc), clknode_class);
+
+static int
+jz4780_clk_pll_init(struct clknode *clk, device_t dev)
+{
+ struct jz4780_clk_pll_sc *sc;
+ uint32_t reg;
+
+ sc = clknode_get_softc(clk);
+ CLK_LOCK(sc);
+ reg = CLK_RD_4(sc, sc->clk_reg);
+ CLK_WR_4(sc, sc->clk_reg, reg);
+ CLK_UNLOCK(sc);
+
+ clknode_init_parent_idx(clk, 0);
+ return (0);
+}
+
+static int
+jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq)
+{
+ struct jz4780_clk_pll_sc *sc;
+ uint32_t reg, m, n, od;
+
+ sc = clknode_get_softc(clk);
+ reg = CLK_RD_4(sc, sc->clk_reg);
+
+ /* Check for PLL enabled status */
+ if (REG_GET(reg, CGU_PLL_EN) == 0) {
+ *freq = 0;
+ return 0;
+ }
+
+ /* Return parent frequency if PPL is being bypassed */
+ if (REG_GET(reg, CGU_PLL_BP) != 0)
+ return 0;
+
+ m = REG_GET(reg, CGU_PLL_M) + 1;
+ n = REG_GET(reg, CGU_PLL_N) + 1;
+ od = REG_GET(reg, CGU_PLL_OD) + 1;
+
+ /* Sanity check values */
+ if (m == 0 || n == 0 || od == 0) {
+ *freq = 0;
+ return (EINVAL);
+ }
+
+ *freq = ((*freq / n) * m) / od;
+ return (0);
+}
+
+#define MHZ (1000 * 1000)
+#define PLL_TIMEOUT 100
+
+static int
+jz4780_clk_pll_wait_lock(struct jz4780_clk_pll_sc *sc)
+{
+ int i;
+
+ for (i = 0; i < PLL_TIMEOUT; i++) {
+ if (CLK_RD_4(sc, sc->clk_reg) & REG_VAL(CGU_PLL_LOCK, 1))
+ return (0);
+ DELAY(1000);
+ }
+ return (ETIMEDOUT);
+}
+
+static int
+jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin,
+ uint64_t *fout, int flags, int *stop)
+{
+ struct jz4780_clk_pll_sc *sc;
+ uint32_t reg, m, n, od;
+ int rv;
+
+ sc = clknode_get_softc(clk);
+
+ /* Should be able to figure all clocks with m & n only */
+ od = 1;
+
+ m = MIN((uint32_t)(*fout / MHZ), (1u << CGU_PLL_M_WIDTH));
+ m = MIN(m, 1);
+
+ n = MIN((uint32_t)(fin / MHZ), (1u << CGU_PLL_N_WIDTH));
+ n = MIN(n, 1);
+
+ if (flags & CLK_SET_DRYRUN) {
+ if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
+ (*fout != (((fin / n) * m) / od)))
+ return (ERANGE);
+
+ *fout = ((fin / n) * m) / od;
+ return (0);
+ }
+
+ CLK_LOCK(sc);
+ reg = CLK_RD_4(sc, sc->clk_reg);
+
+ /* Set the calculated values */
+ reg = REG_SET(reg, CGU_PLL_M, m - 1);
+ reg = REG_SET(reg, CGU_PLL_N, n - 1);
+ reg = REG_SET(reg, CGU_PLL_OD, od - 1);
+
+ /* Enable the PLL */
+ reg = REG_SET(reg, CGU_PLL_EN, 1);
+ reg = REG_SET(reg, CGU_PLL_BP, 0);
+
+ /* Initiate the change */
+ CLK_WR_4(sc, sc->clk_reg, reg);
+
+ /* Wait for PLL to lock */
+ rv = jz4780_clk_pll_wait_lock(sc);
+ CLK_UNLOCK(sc);
+ if (rv != 0)
+ return (rv);
+
+ *fout = ((fin / n) * m) / od;
+ return (0);
+}
+
+int jz4780_clk_pll_register(struct clkdom *clkdom,
+ struct clknode_init_def *clkdef, struct mtx *dev_mtx,
+ struct resource *mem_res, uint32_t mem_reg)
+{
+ struct clknode *clk;
+ struct jz4780_clk_pll_sc *sc;
+
+ clk = clknode_create(clkdom, &jz4780_clk_pll_class, clkdef);
+ if (clk == NULL)
+ return (1);
+
+ sc = clknode_get_softc(clk);
+ sc->clk_mtx = dev_mtx;
+ sc->clk_res = mem_res;
+ sc->clk_reg = mem_reg;
+ clknode_register(clkdom, clk);
+ return (0);
+}
diff --git a/sys/mips/ingenic/jz4780_clock.c b/sys/mips/ingenic/jz4780_clock.c
new file mode 100644
index 000000000000..7261cb00ee44
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_clock.c
@@ -0,0 +1,831 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Ingenic JZ4780 CGU driver.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/ingenic/jz4780_clk.h>
+#include <mips/ingenic/jz4780_regs.h>
+#include <mips/ingenic/jz4780_clock.h>
+
+#include "clkdev_if.h"
+
+#include <gnu/dts/include/dt-bindings/clock/jz4780-cgu.h>
+
+/**********************************************************************
+ * JZ4780 CGU clock domain
+ **********************************************************************/
+struct jz4780_clock_softc {
+ device_t dev;
+ struct resource *res[1];
+ struct mtx mtx;
+ struct clkdom *clkdom;
+};
+
+static struct resource_spec jz4780_clock_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct jz4780_clk_pll_def {
+ uint16_t clk_id;
+ uint16_t clk_reg;
+ const char *clk_name;
+ const char *clk_pname[1];
+};
+
+#define PLL(_id, cname, pname, reg) { \
+ .clk_id = _id, \
+ .clk_reg = reg, \
+ .clk_name = cname, \
+ .clk_pname[0] = pname, \
+}
+
+struct jz4780_clk_gate_def {
+ uint16_t clk_id;
+ uint16_t clk_bit;
+ const char *clk_name;
+ const char *clk_pname[1];
+};
+
+#define GATE(_id, cname, pname, bit) { \
+ .clk_id = _id, \
+ .clk_bit = bit, \
+ .clk_name = cname, \
+ .clk_pname[0] = pname, \
+}
+
+#define MUX(reg, shift, bits, map) \
+ .clk_mux.mux_reg = (reg), \
+ .clk_mux.mux_shift = (shift), \
+ .clk_mux.mux_bits = (bits), \
+ .clk_mux.mux_map = (map),
+#define NO_MUX
+
+#define DIV(reg, shift, lg, bits, ce, st, bb) \
+ .clk_div.div_reg = (reg), \
+ .clk_div.div_shift = (shift), \
+ .clk_div.div_bits = (bits), \
+ .clk_div.div_lg = (lg), \
+ .clk_div.div_ce_bit = (ce), \
+ .clk_div.div_st_bit = (st), \
+ .clk_div.div_busy_bit = (bb),
+#define NO_DIV \
+
+#define GATEBIT(bit) \
+ .clk_gate_bit = (bit),
+#define NO_GATE \
+ .clk_gate_bit = (-1),
+
+#define PLIST(pnames...) \
+ .clk_pnames = { pnames },
+
+#define GENCLK(id, name, type, parents, mux, div, gt) { \
+ .clk_id = id, \
+ .clk_type = type, \
+ .clk_name = name, \
+ parents \
+ mux \
+ div \
+ gt \
+}
+
+/* PLL definitions */
+static struct jz4780_clk_pll_def pll_clks[] = {
+ PLL(JZ4780_CLK_APLL, "apll", "ext", JZ_CPAPCR),
+ PLL(JZ4780_CLK_MPLL, "mpll", "ext", JZ_CPMPCR),
+ PLL(JZ4780_CLK_EPLL, "epll", "ext", JZ_CPEPCR),
+ PLL(JZ4780_CLK_VPLL, "vpll", "ext", JZ_CPVPCR),
+};
+
+/* OTG PHY clock (reuse gate def structure */
+static struct jz4780_clk_gate_def otg_clks[] = {
+ GATE(JZ4780_CLK_OTGPHY, "otg_phy", "ext", 0),
+};
+
+static const struct jz4780_clk_descr gen_clks[] = {
+ GENCLK(JZ4780_CLK_SCLKA, "sclk_a", CLK_MASK_MUX,
+ PLIST("apll", "ext", "rtc"),
+ MUX(JZ_CPCCR, 30, 2, 0x7),
+ NO_DIV,
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_CPUMUX, "cpumux", CLK_MASK_MUX,
+ PLIST("sclk_a", "mpll", "epll"),
+ MUX(JZ_CPCCR, 28, 2, 0x7),
+ NO_DIV,
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_CPU, "cpu", CLK_MASK_DIV,
+ PLIST("cpumux"),
+ NO_MUX,
+ DIV(JZ_CPCCR, 0, 0, 4, 22, -1, -1),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_L2CACHE, "l2cache", CLK_MASK_DIV,
+ PLIST("cpumux"),
+ NO_MUX,
+ DIV(JZ_CPCCR, 4, 0, 4, -1, -1, -1),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_AHB0, "ahb0", CLK_MASK_MUX | CLK_MASK_DIV,
+ PLIST("sclk_a", "mpll", "epll"),
+ MUX(JZ_CPCCR, 26, 2, 0x7),
+ DIV(JZ_CPCCR, 8, 0, 4, 21, -1, -1),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_AHB2PMUX, "ahb2_apb_mux", CLK_MASK_MUX,
+ PLIST("sclk_a", "mpll", "rtc"),
+ MUX(JZ_CPCCR, 24, 2, 0x7),
+ NO_DIV,
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_AHB2, "ahb2", CLK_MASK_DIV,
+ PLIST("ahb2_apb_mux"),
+ NO_MUX,
+ DIV(JZ_CPCCR, 12, 0, 4, 20, -1, -1),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_PCLK, "pclk", CLK_MASK_DIV,
+ PLIST("ahb2_apb_mux"),
+ NO_MUX,
+ DIV(JZ_CPCCR, 16, 0, 4, 20, -1, -1),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_DDR, "ddr", CLK_MASK_MUX | CLK_MASK_DIV,
+ PLIST("sclk_a", "mpll"),
+ MUX(JZ_DDCDR, 30, 2, 0x6),
+ DIV(JZ_DDCDR, 0, 0, 4, 29, 28, 27),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_VPU, "vpu", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
+ PLIST("sclk_a", "mpll", "epll"),
+ MUX(JZ_VPUCDR, 30, 2, 0xe),
+ DIV(JZ_VPUCDR, 0, 0, 4, 29, 28, 27),
+ GATEBIT(32 + 2)
+ ),
+
+ GENCLK(JZ4780_CLK_I2SPLL, "i2s_pll", CLK_MASK_MUX | CLK_MASK_DIV,
+ PLIST("sclk_a", "epll"),
+ MUX(JZ_I2SCDR, 30, 1, 0xc),
+ DIV(JZ_I2SCDR, 0, 0, 8, 29, 28, 27),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_I2S, "i2s", CLK_MASK_MUX,
+ PLIST("ext", "i2s_pll"),
+ MUX(JZ_I2SCDR, 31, 1, 0xc),
+ NO_DIV,
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_LCD0PIXCLK, "lcd0pixclk", CLK_MASK_MUX | CLK_MASK_DIV,
+ PLIST("sclk_a", "mpll", "vpll"),
+ MUX(JZ_LP0CDR, 30, 2, 0xe),
+ DIV(JZ_LP0CDR, 0, 0, 8, 28, 27, 26),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_LCD1PIXCLK, "lcd1pixclk", CLK_MASK_MUX | CLK_MASK_DIV,
+ PLIST("sclk_a", "mpll", "vpll"),
+ MUX(JZ_LP1CDR, 30, 2, 0xe),
+ DIV(JZ_LP1CDR, 0, 0, 8, 28, 27, 26),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_MSCMUX, "msc_mux", CLK_MASK_MUX,
+ PLIST("sclk_a", "mpll"),
+ MUX(JZ_MSC0CDR, 30, 2, 0x6),
+ NO_DIV,
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_MSC0, "msc0", CLK_MASK_DIV | CLK_MASK_GATE,
+ PLIST("msc_mux"),
+ NO_MUX,
+ DIV(JZ_MSC0CDR, 0, 1, 8, 29, 28, 27),
+ GATEBIT(3)
+ ),
+
+ GENCLK(JZ4780_CLK_MSC1, "msc1", CLK_MASK_DIV | CLK_MASK_GATE,
+ PLIST("msc_mux"),
+ NO_MUX,
+ DIV(JZ_MSC1CDR, 0, 1, 8, 29, 28, 27),
+ GATEBIT(11)
+ ),
+
+ GENCLK(JZ4780_CLK_MSC2, "msc2", CLK_MASK_DIV | CLK_MASK_GATE,
+ PLIST("msc_mux"),
+ NO_MUX,
+ DIV(JZ_MSC2CDR, 0, 1, 8, 29, 28, 27),
+ GATEBIT(12)
+ ),
+
+ GENCLK(JZ4780_CLK_UHC, "uhc", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
+ PLIST("sclk_a", "mpll", "epll", "otg_phy"),
+ MUX(JZ_UHCCDR, 30, 2, 0xf),
+ DIV(JZ_UHCCDR, 0, 0, 8, 29, 28, 27),
+ GATEBIT(24)
+ ),
+
+ GENCLK(JZ4780_CLK_SSIPLL, "ssi_pll", CLK_MASK_MUX | CLK_MASK_DIV,
+ PLIST("sclk_a", "mpll"),
+ MUX(JZ_SSICDR, 30, 1, 0xc),
+ DIV(JZ_SSICDR, 0, 0, 8, 29, 28, 27),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_SSI, "ssi", CLK_MASK_MUX,
+ PLIST("ext", "ssi_pll"),
+ MUX(JZ_SSICDR, 31, 1, 0xc),
+ NO_DIV,
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_CIMMCLK, "cim_mclk", CLK_MASK_MUX | CLK_MASK_DIV,
+ PLIST("sclk_a", "mpll"),
+ MUX(JZ_CIMCDR, 31, 1, 0xc),
+ DIV(JZ_CIMCDR, 0, 0, 8, 30, 29, 28),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_PCMPLL, "pcm_pll", CLK_MASK_MUX | CLK_MASK_DIV,
+ PLIST("sclk_a", "mpll", "epll", "vpll"),
+ MUX(JZ_PCMCDR, 29, 2, 0xf),
+ DIV(JZ_PCMCDR, 0, 0, 8, 28, 27, 26),
+ NO_GATE
+ ),
+
+ GENCLK(JZ4780_CLK_PCM, "pcm", CLK_MASK_MUX | CLK_MASK_GATE,
+ PLIST("ext", "pcm_pll"),
+ MUX(JZ_PCMCDR, 31, 1, 0xc),
+ NO_DIV,
+ GATEBIT(32 + 3)
+ ),
+
+ GENCLK(JZ4780_CLK_GPU, "gpu", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
+ PLIST("sclk_a", "mpll", "epll"),
+ MUX(JZ_GPUCDR, 30, 2, 0x7),
+ DIV(JZ_GPUCDR, 0, 0, 4, 29, 28, 27),
+ GATEBIT(32 + 4)
+ ),
+
+ GENCLK(JZ4780_CLK_HDMI, "hdmi", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
+ PLIST("sclk_a", "mpll", "vpll"),
+ MUX(JZ_HDMICDR, 30, 2, 0xe),
+ DIV(JZ_HDMICDR, 0, 0, 8, 29, 28, 26),
+ GATEBIT(32 + 9)
+ ),
+
+ GENCLK(JZ4780_CLK_BCH, "bch", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
+ PLIST("sclk_a", "mpll", "epll"),
+ MUX(JZ_BCHCDR, 30, 2, 0x7),
+ DIV(JZ_BCHCDR, 0, 0, 4, 29, 28, 27),
+ GATEBIT(1)
+ ),
+};
+
+static struct jz4780_clk_gate_def gate_clks[] = {
+ GATE(JZ4780_CLK_NEMC, "nemc", "ahb2", 0),
+ GATE(JZ4780_CLK_OTG0, "otg0", "ext", 2),
+ GATE(JZ4780_CLK_SSI0, "ssi0", "ssi", 4),
+ GATE(JZ4780_CLK_SMB0, "smb0", "pclk", 5),
+ GATE(JZ4780_CLK_SMB1, "smb1", "pclk", 6),
+ GATE(JZ4780_CLK_SCC, "scc", "ext", 7),
+ GATE(JZ4780_CLK_AIC, "aic", "ext", 8),
+ GATE(JZ4780_CLK_TSSI0, "tssi0", "ext", 9),
+ GATE(JZ4780_CLK_OWI, "owi", "ext", 10),
+ GATE(JZ4780_CLK_KBC, "kbc", "ext", 13),
+ GATE(JZ4780_CLK_SADC, "sadc", "ext", 14),
+ GATE(JZ4780_CLK_UART0, "uart0", "ext", 15),
+ GATE(JZ4780_CLK_UART1, "uart1", "ext", 16),
+ GATE(JZ4780_CLK_UART2, "uart2", "ext", 17),
+ GATE(JZ4780_CLK_UART3, "uart3", "ext", 18),
+ GATE(JZ4780_CLK_SSI1, "ssi1", "ssi", 19),
+ GATE(JZ4780_CLK_SSI2, "ssi2", "ssi", 20),
+ GATE(JZ4780_CLK_PDMA, "pdma", "ext", 21),
+ GATE(JZ4780_CLK_GPS, "gps", "ext", 22),
+ GATE(JZ4780_CLK_MAC, "mac", "ext", 23),
+ GATE(JZ4780_CLK_SMB2, "smb2", "pclk", 25),
+ GATE(JZ4780_CLK_CIM, "cim", "ext", 26),
+ GATE(JZ4780_CLK_LCD, "lcd", "ext", 28),
+ GATE(JZ4780_CLK_TVE, "tve", "lcd", 27),
+ GATE(JZ4780_CLK_IPU, "ipu", "ext", 29),
+ GATE(JZ4780_CLK_DDR0, "ddr0", "ddr", 30),
+ GATE(JZ4780_CLK_DDR1, "ddr1", "ddr", 31),
+ GATE(JZ4780_CLK_SMB3, "smb3", "pclk", 32 + 0),
+ GATE(JZ4780_CLK_TSSI1, "tssi1", "ext", 32 + 1),
+ GATE(JZ4780_CLK_COMPRESS, "compress", "ext", 32 + 5),
+ GATE(JZ4780_CLK_AIC1, "aic1", "ext", 32 + 6),
+ GATE(JZ4780_CLK_GPVLC, "gpvlc", "ext", 32 + 7),
+ GATE(JZ4780_CLK_OTG1, "otg1", "ext", 32 + 8),
+ GATE(JZ4780_CLK_UART4, "uart4", "ext", 32 + 10),
+ GATE(JZ4780_CLK_AHBMON, "ahb_mon", "ext", 32 + 11),
+ GATE(JZ4780_CLK_SMB4, "smb4", "pclk", 32 + 12),
+ GATE(JZ4780_CLK_DES, "des", "ext", 32 + 13),
+ GATE(JZ4780_CLK_X2D, "x2d", "ext", 32 + 14),
+ GATE(JZ4780_CLK_CORE1, "core1", "cpu", 32 + 15),
+};
+
+static int
+jz4780_clock_register(struct jz4780_clock_softc *sc)
+{
+ int i, ret;
+
+ /* Register PLLs */
+ for (i = 0; i < nitems(pll_clks); i++) {
+ struct clknode_init_def clkdef;
+
+ clkdef.id = pll_clks[i].clk_id;
+ clkdef.name = __DECONST(char *, pll_clks[i].clk_name);
+ clkdef.parent_names = pll_clks[i].clk_pname;
+ clkdef.parent_cnt = 1;
+ clkdef.flags = CLK_NODE_STATIC_STRINGS;
+
+ ret = jz4780_clk_pll_register(sc->clkdom, &clkdef, &sc->mtx,
+ sc->res[0], pll_clks[i].clk_reg);
+ if (ret != 0)
+ return (ret);
+ }
+
+ /* Register OTG clock */
+ for (i = 0; i < nitems(otg_clks); i++) {
+ struct clknode_init_def clkdef;
+
+ clkdef.id = otg_clks[i].clk_id;
+ clkdef.name = __DECONST(char *, otg_clks[i].clk_name);
+ clkdef.parent_names = otg_clks[i].clk_pname;
+ clkdef.parent_cnt = 1;
+ clkdef.flags = CLK_NODE_STATIC_STRINGS;
+
+ ret = jz4780_clk_otg_register(sc->clkdom, &clkdef, &sc->mtx,
+ sc->res[0]);
+ if (ret != 0)
+ return (ret);
+ }
+
+ /* Register muxes and divisors */
+ for (i = 0; i < nitems(gen_clks); i++) {
+ ret = jz4780_clk_gen_register(sc->clkdom, &gen_clks[i],
+ &sc->mtx, sc->res[0]);
+ if (ret != 0)
+ return (ret);
+ }
+
+ /* Register simple gates */
+ for (i = 0; i < nitems(gate_clks); i++) {
+ struct clk_gate_def gatedef;
+
+ gatedef.clkdef.id = gate_clks[i].clk_id;
+ gatedef.clkdef.name = __DECONST(char *, gate_clks[i].clk_name);
+ gatedef.clkdef.parent_names = gate_clks[i].clk_pname;
+ gatedef.clkdef.parent_cnt = 1;
+ gatedef.clkdef.flags = CLK_NODE_STATIC_STRINGS;
+
+ if (gate_clks[i].clk_bit < 32) {
+ gatedef.offset = JZ_CLKGR0;
+ gatedef.shift = gate_clks[i].clk_bit;
+ } else {
+ gatedef.offset = JZ_CLKGR1;
+ gatedef.shift = gate_clks[i].clk_bit - 32;
+ }
+ gatedef.mask = 1;
+ gatedef.on_value = 0;
+ gatedef.off_value = 1;
+ gatedef.gate_flags = 0;
+
+ ret = clknode_gate_register(sc->clkdom, &gatedef);
+ if (ret != 0)
+ return (ret);
+
+ }
+
+ return (0);
+}
+
+static int
+jz4780_clock_fixup(struct jz4780_clock_softc *sc)
+{
+ struct clknode *clk_uhc;
+ int ret;
+
+ /*
+ * Make UHC mux use MPLL as the source. It defaults to OTG_PHY
+ * and that somehow just does not work.
+ */
+ clkdom_xlock(sc->clkdom);
+
+ /* Assume the worst */
+ ret = ENXIO;
+
+ clk_uhc = clknode_find_by_id(sc->clkdom, JZ4780_CLK_UHC);
+ if (clk_uhc != NULL) {
+ ret = clknode_set_parent_by_name(clk_uhc, "mpll");
+ if (ret != 0)
+ device_printf(sc->dev,
+ "unable to reparent uhc clock\n");
+ else
+ ret = clknode_set_freq(clk_uhc, 48000000, 0, 0);
+ if (ret != 0)
+ device_printf(sc->dev, "unable to init uhc clock\n");
+ } else
+ device_printf(sc->dev, "unable to lookup uhc clock\n");
+
+ clkdom_unlock(sc->clkdom);
+ return (ret);
+}
+
+#define CGU_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define CGU_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define CGU_LOCK_INIT(sc) \
+ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
+ "jz4780-cgu", MTX_DEF)
+#define CGU_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
+
+#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg))
+
+static int
+jz4780_clock_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-cgu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Ingenic jz4780 CGU");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_clock_attach(device_t dev)
+{
+ struct jz4780_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (bus_alloc_resources(dev, jz4780_clock_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ sc->dev = dev;
+ CGU_LOCK_INIT(sc);
+
+ sc->clkdom = clkdom_create(dev);
+ if (sc->clkdom == NULL)
+ goto fail;
+ if (jz4780_clock_register(sc) != 0)
+ goto fail;
+ if (clkdom_finit(sc->clkdom) != 0)
+ goto fail;
+ if (jz4780_clock_fixup(sc) != 0)
+ goto fail;
+ if (bootverbose)
+ clkdom_dump(sc->clkdom);
+
+ return (0);
+fail:
+ bus_release_resources(dev, jz4780_clock_spec, sc->res);
+ CGU_LOCK_DESTROY(sc);
+
+ return (ENXIO);
+}
+
+static int
+jz4780_clock_detach(device_t dev)
+{
+ struct jz4780_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_release_resources(dev, jz4780_clock_spec, sc->res);
+ CGU_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static int
+jz4780_clock_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+ struct jz4780_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ CSR_WRITE_4(sc, addr, val);
+ return (0);
+}
+
+static int
+jz4780_clock_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+ struct jz4780_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ *val = CSR_READ_4(sc, addr);
+ return (0);
+}
+
+static int
+jz4780_clock_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask,
+ uint32_t set_mask)
+{
+ struct jz4780_clock_softc *sc;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+ val = CSR_READ_4(sc, addr);
+ val &= clear_mask;
+ val |= set_mask;
+ CSR_WRITE_4(sc, addr, val);
+ return (0);
+}
+
+static void
+jz4780_clock_device_lock(device_t dev)
+{
+ struct jz4780_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ CGU_LOCK(sc);
+}
+
+static void
+jz4780_clock_device_unlock(device_t dev)
+{
+ struct jz4780_clock_softc *sc;
+
+ sc = device_get_softc(dev);
+ CGU_UNLOCK(sc);
+}
+
+static device_method_t jz4780_clock_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_clock_probe),
+ DEVMETHOD(device_attach, jz4780_clock_attach),
+ DEVMETHOD(device_detach, jz4780_clock_detach),
+
+ /* Clock device interface */
+ DEVMETHOD(clkdev_write_4, jz4780_clock_write_4),
+ DEVMETHOD(clkdev_read_4, jz4780_clock_read_4),
+ DEVMETHOD(clkdev_modify_4, jz4780_clock_modify_4),
+ DEVMETHOD(clkdev_device_lock, jz4780_clock_device_lock),
+ DEVMETHOD(clkdev_device_unlock, jz4780_clock_device_unlock),
+
+ DEVMETHOD_END
+};
+
+static driver_t jz4780_clock_driver = {
+ "cgu",
+ jz4780_clock_methods,
+ sizeof(struct jz4780_clock_softc),
+};
+
+static devclass_t jz4780_clock_devclass;
+
+EARLY_DRIVER_MODULE(jz4780_clock, simplebus, jz4780_clock_driver,
+ jz4780_clock_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE);
+
+static int
+jz4780_ehci_clk_config(struct jz4780_clock_softc *sc)
+{
+ clk_t phy_clk, ext_clk;
+ uint64_t phy_freq;
+ int err;
+
+ phy_clk = NULL;
+ ext_clk = NULL;
+ err = -1;
+
+ /* Set phy timing by copying it from ext */
+ if (clk_get_by_id(sc->dev, sc->clkdom, JZ4780_CLK_OTGPHY,
+ &phy_clk) != 0)
+ goto done;
+ if (clk_get_parent(phy_clk, &ext_clk) != 0)
+ goto done;
+ if (clk_get_freq(ext_clk, &phy_freq) != 0)
+ goto done;
+ if (clk_set_freq(phy_clk, phy_freq, 0) != 0)
+ goto done;
+ err = 0;
+done:
+ clk_release(ext_clk);
+ clk_release(phy_clk);
+
+ return (err);
+}
+
+int
+jz4780_ohci_enable(void)
+{
+ device_t dev;
+ struct jz4780_clock_softc *sc;
+ uint32_t reg;
+
+ dev = devclass_get_device(jz4780_clock_devclass, 0);
+ if (dev == NULL)
+ return (-1);
+
+ sc = device_get_softc(dev);
+ CGU_LOCK(sc);
+
+ /* Do not force port1 to suspend mode */
+ reg = CSR_READ_4(sc, JZ_OPCR);
+ reg |= OPCR_SPENDN1;
+ CSR_WRITE_4(sc, JZ_OPCR, reg);
+
+ CGU_UNLOCK(sc);
+ return (0);
+}
+
+int
+jz4780_ehci_enable(void)
+{
+ device_t dev;
+ struct jz4780_clock_softc *sc;
+ uint32_t reg;
+
+ dev = devclass_get_device(jz4780_clock_devclass, 0);
+ if (dev == NULL)
+ return (-1);
+
+ sc = device_get_softc(dev);
+
+ /*
+ * EHCI should use MPPL as a parent, but Linux configures OTG
+ * clock anyway. Follow their lead blindly.
+ */
+ if (jz4780_ehci_clk_config(sc) != 0)
+ return (-1);
+
+ CGU_LOCK(sc);
+
+ /* Enable OTG, should not be necessary since we use PLL clock */
+ reg = CSR_READ_4(sc, JZ_USBPCR);
+ reg &= ~(PCR_OTG_DISABLE);
+ CSR_WRITE_4(sc, JZ_USBPCR, reg);
+
+ /* Do not force port1 to suspend mode */
+ reg = CSR_READ_4(sc, JZ_OPCR);
+ reg |= OPCR_SPENDN1;
+ CSR_WRITE_4(sc, JZ_OPCR, reg);
+
+ /* D- pulldown */
+ reg = CSR_READ_4(sc, JZ_USBPCR1);
+ reg |= PCR_DMPD1;
+ CSR_WRITE_4(sc, JZ_USBPCR1, reg);
+
+ /* D+ pulldown */
+ reg = CSR_READ_4(sc, JZ_USBPCR1);
+ reg |= PCR_DPPD1;
+ CSR_WRITE_4(sc, JZ_USBPCR1, reg);
+
+ /* 16 bit bus witdth for port 1*/
+ reg = CSR_READ_4(sc, JZ_USBPCR1);
+ reg |= PCR_WORD_I_F1 | PCR_WORD_I_F0;
+ CSR_WRITE_4(sc, JZ_USBPCR1, reg);
+
+ /* Reset USB */
+ reg = CSR_READ_4(sc, JZ_USBPCR);
+ reg |= PCR_POR;
+ CSR_WRITE_4(sc, JZ_USBPCR, reg);
+ DELAY(1);
+ reg = CSR_READ_4(sc, JZ_USBPCR);
+ reg &= ~(PCR_POR);
+ CSR_WRITE_4(sc, JZ_USBPCR, reg);
+
+ /* Soft-reset USB */
+ reg = CSR_READ_4(sc, JZ_SRBC);
+ reg |= SRBC_UHC_SR;
+ CSR_WRITE_4(sc, JZ_SRBC, reg);
+ /* 300ms */
+ DELAY(300*hz/1000);
+
+ reg = CSR_READ_4(sc, JZ_SRBC);
+ reg &= ~(SRBC_UHC_SR);
+ CSR_WRITE_4(sc, JZ_SRBC, reg);
+
+ /* 300ms */
+ DELAY(300*hz/1000);
+
+ CGU_UNLOCK(sc);
+ return (0);
+}
+
+#define USBRESET_DETECT_TIME 0x96
+
+int
+jz4780_otg_enable(void)
+{
+ device_t dev;
+ struct jz4780_clock_softc *sc;
+ uint32_t reg;
+
+ dev = devclass_get_device(jz4780_clock_devclass, 0);
+ if (dev == NULL)
+ return (-1);
+
+ sc = device_get_softc(dev);
+
+ CGU_LOCK(sc);
+
+ /* Select Synopsys OTG mode */
+ reg = CSR_READ_4(sc, JZ_USBPCR1);
+ reg |= PCR_SYNOPSYS;
+
+ /* Set UTMI bus width to 16 bit */
+ reg |= PCR_WORD_I_F0 | PCR_WORD_I_F1;
+ CSR_WRITE_4(sc, JZ_USBPCR1, reg);
+
+ /* Blah */
+ reg = CSR_READ_4(sc, JZ_USBVBFIL);
+ reg = REG_SET(reg, USBVBFIL_IDDIGFIL, 0);
+ reg = REG_SET(reg, USBVBFIL_USBVBFIL, 0);
+ CSR_WRITE_4(sc, JZ_USBVBFIL, reg);
+
+ /* Setup reset detect time */
+ reg = CSR_READ_4(sc, JZ_USBRDT);
+ reg = REG_SET(reg, USBRDT_USBRDT, USBRESET_DETECT_TIME);
+ reg |= USBRDT_VBFIL_LD_EN;
+ CSR_WRITE_4(sc, JZ_USBRDT, reg);
+
+ /* Setup USBPCR bits */
+ reg = CSR_READ_4(sc, JZ_USBPCR);
+ reg |= PCR_USB_MODE;
+ reg |= PCR_COMMONONN;
+ reg |= PCR_VBUSVLDEXT;
+ reg |= PCR_VBUSVLDEXTSEL;
+ reg &= ~(PCR_OTG_DISABLE);
+ CSR_WRITE_4(sc, JZ_USBPCR, reg);
+
+ /* Reset USB */
+ reg = CSR_READ_4(sc, JZ_USBPCR);
+ reg |= PCR_POR;
+ CSR_WRITE_4(sc, JZ_USBPCR, reg);
+ DELAY(1000);
+ reg = CSR_READ_4(sc, JZ_USBPCR);
+ reg &= ~(PCR_POR);
+ CSR_WRITE_4(sc, JZ_USBPCR, reg);
+
+ /* Unsuspend OTG port */
+ reg = CSR_READ_4(sc, JZ_OPCR);
+ reg |= OPCR_SPENDN0;
+ CSR_WRITE_4(sc, JZ_OPCR, reg);
+
+ CGU_UNLOCK(sc);
+ return (0);
+}
diff --git a/sys/mips/ingenic/jz4780_clock.h b/sys/mips/ingenic/jz4780_clock.h
new file mode 100644
index 000000000000..226e81bc67ad
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_clock.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef JZ4780_CLOCK_H
+#define JZ4780_CLOCK_H
+
+extern int jz4780_ehci_enable(void);
+extern int jz4780_ohci_enable(void);
+extern int jz4780_otg_enable(void);
+
+#endif
diff --git a/sys/mips/ingenic/jz4780_cpuregs.h b/sys/mips/ingenic/jz4780_cpuregs.h
new file mode 100644
index 000000000000..355730738459
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_cpuregs.h
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef JZ4780_CPUREGS_H
+#define JZ4780_CPUREGS_H
+
+/* Core control register */
+#define JZ_CORECTL_SLP1M_SHIFT 17
+#define JZ_CORECTL_SLP1M (1u << JZ_CORECTL_SLP1M_SHIFT)
+#define JZ_CORECTL_SLP0M_SHIFT 16
+#define JZ_CORECTL_SLP0M (1u << JZ_CORECTL_SLP0M_SHIFT)
+#define JZ_CORECTL_RPC1_SHIFT 9
+#define JZ_CORECTL_RPC1 (1u << JZ_CORECTL_RPC1_SHIFT)
+#define JZ_CORECTL_RPC0_SHIFT 8
+#define JZ_CORECTL_RPC0 (1u << JZ_CORECTL_RPC0_SHIFT)
+#define JZ_CORECTL_SWRST1_SHIFT 1
+#define JZ_CORECTL_SWRST1 (1u << JZ_CORECTL_SWRST1_SHIFT)
+#define JZ_CORECTL_SWRST0_SHIFT 0
+#define JZ_CORECTL_SWRST0 (1u << JZ_CORECTL_SWRST0_SHIFT)
+
+/* Core status register */
+#define JZ_CORESTS_SLP1_SHIFT 17
+#define JZ_CORESTS_SLP1 (1u << JZ_CORESTS_SLP1_SHIFT)
+#define JZ_CORESTS_SLP0_SHIFT 16
+#define JZ_CORESTS_SLP0 (1u << JZ_CORESTS_SLP0_SHIFT)
+#define JZ_CORESTS_IRQ1P_SHIFT 9
+#define JZ_CORESTS_IRQ1P (1u << JZ_CORESTS_IRQ1P_SHIFT)
+#define JZ_CORESTS_IRQ0P_SHIFT 8
+#define JZ_CORESTS_IRQ0P (1u << JZ_CORESTS_IRQ0P_SHIFT)
+#define JZ_CORESTS_MIRQ1P_SHIFT 1
+#define JZ_CORESTS_MIRQ1P (1u << JZ_CORESTS_MIRQ1P_SHIFT)
+#define JZ_CORESTS_MIRQ0P_SHIFT 0
+#define JZ_CORESTS_MIRQ0P (1u << JZ_CORESTS_MIRQ0P_SHIFT)
+
+/* Reset entry and IRQ mask */
+#define JZ_REIM_ENTRY_SHIFT 16
+#define JZ_REIM_ENTRY_WIDTH 16
+#define JZ_REIM_ENTRY_MASK (0xFFFFu << JZ_REIM_ENTRY_SHIFT)
+#define JZ_REIM_IRQ1M_SHIFT 9
+#define JZ_REIM_IRQ1M (1u << JZ_REIM_IRQ1M_SHIFT)
+#define JZ_REIM_IRQ0M_SHIFT 8
+#define JZ_REIM_IRQ0M (1u << JZ_REIM_IRQ0M_SHIFT)
+#define JZ_REIM_MIRQ1M_SHIFT 1
+#define JZ_REIM_MIRQ1M (1u << JZ_REIM_MIRQ1M_SHIFT)
+#define JZ_REIM_MIRQ0M_SHIFT 0
+#define JZ_REIM_MIRQ0M (1u << JZ_REIM_MIRQ0M_SHIFT)
+
+#endif /* JZ4780_CPUREGS_H */
diff --git a/sys/mips/ingenic/jz4780_dme.c b/sys/mips/ingenic/jz4780_dme.c
new file mode 100644
index 000000000000..a612be6a799a
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_dme.c
@@ -0,0 +1,124 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+struct jz4780_dme_softc {
+ device_t dev;
+ struct resource *res[2];
+};
+
+static struct resource_spec jz4780_dme_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int jz4780_dme_probe(device_t dev);
+static int jz4780_dme_attach(device_t dev);
+static int jz4780_dme_detach(device_t dev);
+
+static int
+jz4780_dme_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "davicom,dm9000"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Davicom DM9000C 10/100BaseTX");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_dme_attach(device_t dev)
+{
+ struct jz4780_dme_softc *sc = device_get_softc(dev);
+
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, jz4780_dme_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+jz4780_dme_detach(device_t dev)
+{
+ struct jz4780_dme_softc *sc = device_get_softc(dev);
+
+ bus_release_resources(dev, jz4780_dme_spec, sc->res);
+ return (0);
+}
+
+static device_method_t jz4780_dme_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_dme_probe),
+ DEVMETHOD(device_attach, jz4780_dme_attach),
+ DEVMETHOD(device_detach, jz4780_dme_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t jz4780_dme_driver = {
+ "dme",
+ jz4780_dme_methods,
+ sizeof(struct jz4780_dme_softc),
+};
+
+static devclass_t jz4780_dme_devclass;
+
+DRIVER_MODULE(jz4780_dme, simplebus, jz4780_dme_driver,
+ jz4780_dme_devclass, 0, 0);
diff --git a/sys/mips/ingenic/jz4780_dwc_fdt.c b/sys/mips/ingenic/jz4780_dwc_fdt.c
new file mode 100644
index 000000000000..c5af46b633eb
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_dwc_fdt.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/condvar.h>
+#include <sys/module.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/controller/dwc_otg.h>
+#include <dev/usb/controller/dwc_otg_fdt.h>
+
+#include <mips/ingenic/jz4780_clock.h>
+#include <mips/ingenic/jz4780_regs.h>
+
+static device_probe_t jz4780_dwc_otg_probe;
+static device_attach_t jz4780_dwc_otg_attach;
+static device_detach_t jz4780_dwc_otg_detach;
+
+struct jz4780_dwc_otg_softc {
+ struct dwc_otg_fdt_softc base; /* storage for DWC OTG code */
+ clk_t phy_clk;
+ clk_t otg_clk;
+};
+
+static int
+jz4780_dwc_otg_clk_enable(device_t dev)
+{
+ struct jz4780_dwc_otg_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ /* Configure and enable phy clock */
+ err = clk_get_by_ofw_name(dev, 0, "otg_phy", &sc->phy_clk);
+ if (err != 0) {
+ device_printf(dev, "unable to lookup %s clock\n", "otg_phy");
+ return (err);
+ }
+ err = clk_set_freq(sc->phy_clk, 48000000, 0);
+ if (err != 0) {
+ device_printf(dev, "unable to set %s clock to 48 kHZ\n",
+ "otg_phy");
+ return (err);
+ }
+ err = clk_enable(sc->phy_clk);
+ if (err != 0) {
+ device_printf(dev, "unable to enable %s clock\n", "otg_phy");
+ return (err);
+ }
+
+ /* Configure and enable otg1 clock */
+ err = clk_get_by_ofw_name(dev, 0, "otg1", &sc->otg_clk);
+ if (err != 0) {
+ device_printf(dev, "unable to lookup %s clock\n", "otg1");
+ return (err);
+ }
+ err = clk_enable(sc->phy_clk);
+ if (err != 0) {
+ device_printf(dev, "unable to enable %s clock\n", "otg1");
+ return (err);
+ }
+
+ return (0);
+}
+
+static int
+jz4780_dwc_otg_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-otg"))
+ return (ENXIO);
+
+ device_set_desc(dev, "DWC OTG 2.0 integrated USB controller (jz4780)");
+
+ return (BUS_PROBE_VENDOR);
+}
+
+static int
+jz4780_dwc_otg_attach(device_t dev)
+{
+ struct jz4780_dwc_otg_softc *sc;
+ struct resource *res;
+ int err, rid;
+
+ sc = device_get_softc(dev);
+
+ err = jz4780_dwc_otg_clk_enable(dev);
+ if (err != 0)
+ goto fail;
+
+ err = jz4780_otg_enable();
+ if (err != 0) {
+ device_printf(dev, "CGU failed to enable OTG\n");
+ goto fail;
+ }
+
+ /* Voodoo: Switch off VBUS overcurrent detection in OTG PHY */
+ res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (res != NULL) {
+ uint32_t reg;
+
+ reg = bus_read_4(res, JZ_DWC2_GUSBCFG);
+ reg |= 0xc;
+ bus_write_4(res, JZ_DWC2_GUSBCFG, reg);
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, res);
+ }
+
+ sc->base.sc_otg.sc_phy_type = DWC_OTG_PHY_UTMI;
+ sc->base.sc_otg.sc_phy_bits = 16;
+
+ err = dwc_otg_attach(dev);
+ if (err != 0)
+ goto fail;
+
+ return (0);
+fail:
+ if (sc->otg_clk)
+ clk_release(sc->otg_clk);
+ if (sc->phy_clk)
+ clk_release(sc->phy_clk);
+ return (err);
+}
+
+static int
+jz4780_dwc_otg_detach(device_t dev)
+{
+ struct jz4780_dwc_otg_softc *sc;
+ int err;
+
+ err = dwc_otg_detach(dev);
+ if (err != 0)
+ return (err);
+
+ sc = device_get_softc(dev);
+ if (sc->otg_clk)
+ clk_release(sc->otg_clk);
+ if (sc->phy_clk)
+ clk_release(sc->phy_clk);
+ return (0);
+}
+
+static device_method_t jz4780_dwc_otg_methods[] = {
+ /* bus interface */
+ DEVMETHOD(device_probe, jz4780_dwc_otg_probe),
+ DEVMETHOD(device_attach, jz4780_dwc_otg_attach),
+ DEVMETHOD(device_detach, jz4780_dwc_otg_detach),
+
+ DEVMETHOD_END
+};
+
+static devclass_t jz4780_dwc_otg_devclass;
+
+DEFINE_CLASS_1(jzotg, jz4780_dwc_otg_driver, jz4780_dwc_otg_methods,
+ sizeof(struct jz4780_dwc_otg_softc), dwc_otg_driver);
+DRIVER_MODULE(jzotg, simplebus, jz4780_dwc_otg_driver,
+ jz4780_dwc_otg_devclass, 0, 0);
+MODULE_DEPEND(jzotg, usb, 1, 1, 1);
diff --git a/sys/mips/ingenic/jz4780_efuse.c b/sys/mips/ingenic/jz4780_efuse.c
new file mode 100644
index 000000000000..4c4ca7ce02f0
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_efuse.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 2015 Alexander Kabaev <kan@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/resource.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+
+static struct ofw_compat_data compat_data[] = {
+ {"ingenic,jz4780-efuse", 1},
+ {NULL, 0}
+};
+
+struct jz4780_efuse_data {
+ uint32_t serial_num;
+ uint32_t date;
+ uint8_t nanufacturer[2];
+ uint8_t macaddr[6];
+} __packed;
+
+static struct resource_spec jz4780_efuse_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct jz4780_efuse_softc {
+ device_t dev;
+ struct resource *res[1];
+ struct jz4780_efuse_data data;
+};
+
+#define CSR_WRITE_4(sc, reg, val) \
+ bus_write_4((sc)->res[0], (reg), (val))
+#define CSR_READ_4(sc, reg) \
+ bus_read_4((sc)->res[0], (reg))
+
+#define JZ_EFUSE_BANK_SIZE (4096 / 8) /* Bank size is 4096 bits */
+
+static int
+jz4780_efuse_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static void
+jz4780_efuse_read_chunk(struct jz4780_efuse_softc *sc, int addr, uint8_t *buf, int len)
+{
+ uint32_t abuf;
+ int i, count;
+
+ /* Setup to read proper bank */
+ CSR_WRITE_4(sc, JZ_EFUCTRL, JZ_EFUSE_READ |
+ (addr < JZ_EFUSE_BANK_SIZE ? 0: JZ_EFUSE_BANK) |
+ (addr << JZ_EFUSE_ADDR_SHIFT) |
+ ((len - 1) << JZ_EFUSE_SIZE_SHIFT));
+ /* Wait for read to complete */
+ while ((CSR_READ_4(sc, JZ_EFUSTATE) & JZ_EFUSE_RD_DONE) == 0)
+ DELAY(1000);
+
+ /* Round to 4 bytes for the simple loop below */
+ count = len & ~3;
+
+ for (i = 0; i < count; i += 4) {
+ abuf = CSR_READ_4(sc, JZ_EFUDATA0 + i);
+ memcpy(buf, &abuf, 4);
+ buf += 4;
+ }
+
+ /* Read partial word and assign it byte-by-byte */
+ if (i < len) {
+ abuf = CSR_READ_4(sc, JZ_EFUDATA0 + i);
+ for (/* none */; i < len; i++) {
+ buf[i] = abuf & 0xff;
+ abuf >>= 8;
+ }
+ }
+}
+
+static void
+jz4780_efuse_read(struct jz4780_efuse_softc *sc, int addr, void *buf, int len)
+{
+ int chunk;
+
+ while (len > 0) {
+ chunk = (len > 32) ? 32 : len;
+ jz4780_efuse_read_chunk(sc, addr, buf, chunk);
+ len -= chunk;
+ buf = (void *)((uintptr_t)buf + chunk);
+ addr += chunk;
+ }
+}
+
+static void
+jz4780_efuse_update_kenv(struct jz4780_efuse_softc *sc)
+{
+ char macstr[sizeof("xx:xx:xx:xx:xx:xx")];
+
+ /*
+ * Update hint in kernel env only if none is available yet.
+ * It is quite possible one was set by command line already.
+ */
+ if (kern_getenv("hint.dme.0.macaddr") == NULL) {
+ snprintf(macstr, sizeof(macstr), "%6D",
+ sc->data.macaddr, ":");
+ kern_setenv("hint.dme.0.macaddr", macstr);
+ }
+}
+
+static int
+jz4780_efuse_attach(device_t dev)
+{
+ struct jz4780_efuse_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, jz4780_efuse_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ /*
+ * Default RD_STROBE to 4 h2clk cycles, should already be set to 4 by reset
+ * but configure it anyway.
+ */
+ CSR_WRITE_4(sc, JZ_EFUCFG, 0x00040000);
+
+ /* Read user-id segment */
+ jz4780_efuse_read(sc, 0x18, &sc->data, sizeof(sc->data));
+
+ /*
+ * Set resource hints for the dme device to discover its
+ * MAC address, if not set already.
+ */
+ jz4780_efuse_update_kenv(sc);
+
+ /* Resource conflicts with NEMC, release early */
+ bus_release_resources(dev, jz4780_efuse_spec, sc->res);
+ return (0);
+}
+
+static int
+jz4780_efuse_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static device_method_t jz4780_efuse_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_efuse_probe),
+ DEVMETHOD(device_attach, jz4780_efuse_attach),
+ DEVMETHOD(device_detach, jz4780_efuse_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t jz4780_efuse_driver = {
+ "efuse",
+ jz4780_efuse_methods,
+ sizeof(struct jz4780_efuse_softc),
+};
+
+static devclass_t jz4780_efuse_devclass;
+EARLY_DRIVER_MODULE(jz4780_efuse, simplebus, jz4780_efuse_driver,
+ jz4780_efuse_devclass, 0, 0, BUS_PASS_TIMER);
diff --git a/sys/mips/ingenic/jz4780_ehci.c b/sys/mips/ingenic/jz4780_ehci.c
new file mode 100644
index 000000000000..c6cd2dc9d7b6
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_ehci.c
@@ -0,0 +1,345 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * JZ4780 attachment driver for the USB Enhanced Host Controller.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <mips/ingenic/jz4780_clock.h>
+#include <mips/ingenic/jz4780_regs.h>
+
+#define EHCI_HC_DEVSTR "Ingenic JZ4780 EHCI"
+
+struct jz4780_ehci_softc {
+ ehci_softc_t base; /* storage for EHCI code */
+ clk_t clk;
+ struct gpiobus_pin *gpio_vbus;
+};
+
+static device_probe_t jz4780_ehci_probe;
+static device_attach_t jz4780_ehci_attach;
+static device_detach_t jz4780_ehci_detach;
+
+static int
+jz4780_ehci_vbus_gpio_enable(device_t dev)
+{
+ struct gpiobus_pin *gpio_vbus;
+ struct jz4780_ehci_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ err = ofw_gpiobus_parse_gpios(dev, "ingenic,vbus-gpio", &gpio_vbus);
+ /*
+ * The pin can ne already mapped by other device. Optimistically
+ * surge ahead.
+ */
+ if (err <= 0)
+ return (0);
+
+ sc->gpio_vbus = gpio_vbus;
+ if (err > 1) {
+ device_printf(dev, "too many vbus gpios\n");
+ return (ENXIO);
+ }
+
+ if (sc->gpio_vbus != NULL) {
+ err = GPIO_PIN_SETFLAGS(sc->gpio_vbus->dev, sc->gpio_vbus->pin,
+ GPIO_PIN_OUTPUT);
+ if (err != 0) {
+ device_printf(dev, "Cannot configure GPIO pin %d on %s\n",
+ sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev));
+ return (err);
+ }
+
+ err = GPIO_PIN_SET(sc->gpio_vbus->dev, sc->gpio_vbus->pin, 1);
+ if (err != 0) {
+ device_printf(dev, "Cannot configure GPIO pin %d on %s\n",
+ sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev));
+ return (err);
+ }
+ }
+ return (0);
+}
+
+static int
+jz4780_ehci_clk_enable(device_t dev)
+{
+ struct jz4780_ehci_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+ if (err != 0) {
+ device_printf(dev, "unable to lookup device clock\n");
+ return (err);
+ }
+ err = clk_enable(sc->clk);
+ if (err != 0) {
+ device_printf(dev, "unable to enable device clock\n");
+ return (err);
+ }
+ err = clk_set_freq(sc->clk, 48000000, 0);
+ if (err != 0) {
+ device_printf(dev, "unable to set device clock to 48 kHZ\n");
+ return (err);
+ }
+ return (0);
+}
+
+static void
+jz4780_ehci_intr(void *arg)
+{
+
+ ehci_interrupt(arg);
+}
+
+static int
+jz4780_ehci_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-ehci"))
+ return (ENXIO);
+
+ device_set_desc(dev, EHCI_HC_DEVSTR);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_ehci_attach(device_t dev)
+{
+ struct jz4780_ehci_softc *isc;
+ ehci_softc_t *sc;
+ int err;
+ int rid;
+ uint32_t reg;
+
+ isc = device_get_softc(dev);
+ sc = &isc->base;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+ sc->sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+
+ sc->sc_bus.usbrev = USB_REV_2_0;
+
+ err = jz4780_ehci_vbus_gpio_enable(dev);
+ if (err)
+ goto error;
+
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(dev, "Could not map memory\n");
+ goto error;
+ }
+
+ /*
+ * Craft special resource for bus space ops that handle
+ * byte-alignment of non-word addresses.
+ */
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ err = jz4780_ehci_clk_enable(dev);
+ if (err)
+ goto error;
+
+ if (jz4780_ehci_enable() != 0) {
+ device_printf(dev, "CGU failed to enable EHCI\n");
+ err = ENXIO;
+ goto error;
+ }
+
+ EWRITE4(sc, EHCI_USBINTR, 0);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "Could not allocate irq\n");
+ goto error;
+ }
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(dev, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
+
+ sprintf(sc->sc_vendor, "Ingenic");
+
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, jz4780_ehci_intr, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ err = ehci_init(sc);
+ if (!err) {
+ /* Voodoo: set utmi data bus width on controller to 16 bit */
+ reg = EREAD4(sc, JZ_EHCI_REG_UTMI_BUS);
+ reg |= UTMI_BUS_WIDTH;
+ EWRITE4(sc, JZ_EHCI_REG_UTMI_BUS, reg);
+
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(dev, "USB init failed err=%d\n", err);
+ goto error;
+ }
+ return (0);
+
+error:
+ jz4780_ehci_detach(dev);
+ return (ENXIO);
+}
+
+static int
+jz4780_ehci_detach(device_t dev)
+{
+ struct jz4780_ehci_softc *isc;
+ ehci_softc_t *sc;
+ device_t bdev;
+ int err;
+
+ isc = device_get_softc(dev);
+ sc = &isc->base;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ ehci_detach(sc);
+
+ err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(dev, "Could not tear down irq, %d\n",
+ err);
+ sc->sc_intr_hdl = NULL;
+ }
+
+ if (sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+
+ if (isc->clk)
+ clk_release(isc->clk);
+
+ usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+ free(isc->gpio_vbus, M_DEVBUF);
+ return (0);
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_ehci_probe),
+ DEVMETHOD(device_attach, jz4780_ehci_attach),
+ DEVMETHOD(device_detach, jz4780_ehci_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t ehci_driver = {
+ .name = "ehci",
+ .methods = ehci_methods,
+ .size = sizeof(struct jz4780_ehci_softc),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(ehci, usb, 1, 1, 1);
+MODULE_DEPEND(ehci, gpio, 1, 1, 1);
diff --git a/sys/mips/ingenic/jz4780_gpio.c b/sys/mips/ingenic/jz4780_gpio.c
new file mode 100644
index 000000000000..aa45e31c3bb7
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_gpio.c
@@ -0,0 +1,830 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+#include <gnu/dts/include/dt-bindings/interrupt-controller/irq.h>
+
+#include "jz4780_gpio_if.h"
+#include "gpio_if.h"
+#include "pic_if.h"
+
+#define JZ4780_GPIO_PINS 32
+
+enum pin_function {
+ JZ_FUNC_DEV_0,
+ JZ_FUNC_DEV_1,
+ JZ_FUNC_DEV_2,
+ JZ_FUNC_DEV_3,
+ JZ_FUNC_GPIO,
+ JZ_FUNC_INTR,
+};
+
+struct jz4780_gpio_pin {
+ struct intr_irqsrc pin_irqsrc;
+ enum intr_trigger intr_trigger;
+ enum intr_polarity intr_polarity;
+ enum pin_function pin_func;
+ uint32_t pin_caps;
+ uint32_t pin_flags;
+ uint32_t pin_num;
+ char pin_name[GPIOMAXNAME];
+};
+
+struct jz4780_gpio_softc {
+ device_t dev;
+ device_t busdev;
+ struct resource *res[2];
+ struct mtx mtx;
+ struct jz4780_gpio_pin pins[JZ4780_GPIO_PINS];
+ void *intrhand;
+};
+
+static struct resource_spec jz4780_gpio_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int jz4780_gpio_probe(device_t dev);
+static int jz4780_gpio_attach(device_t dev);
+static int jz4780_gpio_detach(device_t dev);
+static int jz4780_gpio_intr(void *arg);
+
+#define JZ4780_GPIO_LOCK(sc) mtx_lock_spin(&(sc)->mtx)
+#define JZ4780_GPIO_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx)
+#define JZ4780_GPIO_LOCK_INIT(sc) \
+ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
+ "jz4780_gpio", MTX_SPIN)
+#define JZ4780_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
+
+#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
+#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg))
+
+static int
+jz4780_gpio_probe(device_t dev)
+{
+ phandle_t node;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ /* We only like particular parent */
+ if (!ofw_bus_is_compatible(device_get_parent(dev),
+ "ingenic,jz4780-pinctrl"))
+ return (ENXIO);
+
+ /* ... and only specific children os that parent */
+ node = ofw_bus_get_node(dev);
+ if (!OF_hasprop(node, "gpio-controller"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Ingenic JZ4780 GPIO Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_gpio_pin_set_func(struct jz4780_gpio_softc *sc, uint32_t pin,
+ uint32_t func)
+{
+ uint32_t mask = (1u << pin);
+
+ if (func > (uint32_t)JZ_FUNC_DEV_3)
+ return (EINVAL);
+
+ CSR_WRITE_4(sc, JZ_GPIO_INTC, mask);
+ CSR_WRITE_4(sc, JZ_GPIO_MASKC, mask);
+ if (func & 2)
+ CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask);
+ else
+ CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask);
+ if (func & 1)
+ CSR_WRITE_4(sc, JZ_GPIO_PAT0S, mask);
+ else
+ CSR_WRITE_4(sc, JZ_GPIO_PAT0C, mask);
+
+ sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+ sc->pins[pin].pin_func = (enum pin_function)func;
+ return (0);
+}
+
+static int
+jz4780_gpio_pin_set_direction(struct jz4780_gpio_softc *sc,
+ uint32_t pin, uint32_t dir)
+{
+ uint32_t mask = (1u << pin);
+
+ switch (dir) {
+ case GPIO_PIN_OUTPUT:
+ if (sc->pins[pin].pin_caps & dir)
+ CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask);
+ else
+ return (EINVAL);
+ break;
+ case GPIO_PIN_INPUT:
+ if (sc->pins[pin].pin_caps & dir)
+ CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask);
+ else
+ return (EINVAL);
+ break;
+ }
+
+ sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
+ sc->pins[pin].pin_flags |= dir;
+ return (0);
+}
+
+static int
+jz4780_gpio_pin_set_bias(struct jz4780_gpio_softc *sc,
+ uint32_t pin, uint32_t bias)
+{
+ uint32_t mask = (1u << pin);
+
+ switch (bias) {
+ case GPIO_PIN_PULLUP:
+ case GPIO_PIN_PULLDOWN:
+ if (sc->pins[pin].pin_caps & bias)
+ CSR_WRITE_4(sc, JZ_GPIO_DPULLC, mask);
+ else
+ return (EINVAL);
+ break;
+ case 0:
+ CSR_WRITE_4(sc, JZ_GPIO_DPULLS, mask);
+ break;
+ default:
+ return (ENOTSUP);
+ }
+
+ sc->pins[pin].pin_flags &= ~(GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
+ sc->pins[pin].pin_flags |= bias;
+ return (0);
+}
+
+/*
+ * Decode pin configuration using this map
+ */
+#if 0
+INT MASK PAT1 PAT0
+1 x 0 0 /* intr, level, low */
+1 x 0 1 /* intr, level, high */
+1 x 1 0 /* intr, edge, falling */
+1 x 1 1 /* intr, edge, rising */
+0 0 0 0 /* function, func 0 */
+0 0 0 1 /* function, func 1 */
+0 0 1 0 /* function, func 2 */
+0 0 1 0 /* function, func 3 */
+0 1 0 0 /* gpio, output 0 */
+0 1 0 1 /* gpio, output 1 */
+0 1 1 x /* gpio, input */
+#endif
+
+static void
+jz4780_gpio_pin_probe(struct jz4780_gpio_softc *sc, uint32_t pin)
+{
+ uint32_t mask = (1u << pin);
+ uint32_t val;
+
+ /* Clear cached gpio config */
+ sc->pins[pin].pin_flags = 0;
+
+ /* First check if pin is in interrupt mode */
+ val = CSR_READ_4(sc, JZ_GPIO_INT);
+ if (val & mask) {
+ /* Pin is in interrupt mode, decode interrupt triggering mode */
+ val = CSR_READ_4(sc, JZ_GPIO_PAT1);
+ if (val & mask)
+ sc->pins[pin].intr_trigger = INTR_TRIGGER_EDGE;
+ else
+ sc->pins[pin].intr_trigger = INTR_TRIGGER_LEVEL;
+ /* Decode interrupt polarity */
+ val = CSR_READ_4(sc, JZ_GPIO_PAT0);
+ if (val & mask)
+ sc->pins[pin].intr_polarity = INTR_POLARITY_HIGH;
+ else
+ sc->pins[pin].intr_polarity = INTR_POLARITY_LOW;
+
+ sc->pins[pin].pin_func = JZ_FUNC_INTR;
+ sc->pins[pin].pin_flags = 0;
+ return;
+ }
+ /* Next check if pin is in gpio mode */
+ val = CSR_READ_4(sc, JZ_GPIO_MASK);
+ if (val & mask) {
+ /* Pin is in gpio mode, decode direction and bias */
+ val = CSR_READ_4(sc, JZ_GPIO_PAT1);
+ if (val & mask)
+ sc->pins[pin].pin_flags |= GPIO_PIN_INPUT;
+ else
+ sc->pins[pin].pin_flags |= GPIO_PIN_OUTPUT;
+ /* Check for bias */
+ val = CSR_READ_4(sc, JZ_GPIO_DPULL);
+ if ((val & mask) == 0)
+ sc->pins[pin].pin_flags |= sc->pins[pin].pin_caps &
+ (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
+ sc->pins[pin].pin_func = JZ_FUNC_GPIO;
+ return;
+ }
+ /* By exclusion, pin is in alternate function mode */
+ val = CSR_READ_4(sc, JZ_GPIO_DPULL);
+ if ((val & mask) == 0)
+ sc->pins[pin].pin_flags = sc->pins[pin].pin_caps &
+ (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
+ val = ((CSR_READ_4(sc, JZ_GPIO_PAT1) & mask) >> pin) << 1;
+ val = val | ((CSR_READ_4(sc, JZ_GPIO_PAT1) & mask) >> pin);
+ sc->pins[pin].pin_func = (enum pin_function)val;
+}
+
+static int
+jz4780_gpio_register_isrcs(struct jz4780_gpio_softc *sc)
+{
+ int error;
+ uint32_t irq, i;
+ struct intr_irqsrc *isrc;
+ const char *name;
+
+ name = device_get_nameunit(sc->dev);
+ for (irq = 0; irq < JZ4780_GPIO_PINS; irq++) {
+ isrc = &sc->pins[irq].pin_irqsrc;
+ error = intr_isrc_register(isrc, sc->dev, 0, "%s,%d",
+ name, irq);
+ if (error != 0) {
+ for (i = 0; i < irq; i++)
+ intr_isrc_deregister(&sc->pins[i].pin_irqsrc);
+ device_printf(sc->dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+jz4780_gpio_attach(device_t dev)
+{
+ struct jz4780_gpio_softc *sc = device_get_softc(dev);
+ phandle_t node;
+ uint32_t i, pd_pins, pu_pins;
+
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, jz4780_gpio_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ JZ4780_GPIO_LOCK_INIT(sc);
+
+ node = ofw_bus_get_node(dev);
+ OF_getencprop(node, "ingenic,pull-ups", &pu_pins, sizeof(pu_pins));
+ OF_getencprop(node, "ingenic,pull-downs", &pd_pins, sizeof(pd_pins));
+
+ for (i = 0; i < JZ4780_GPIO_PINS; i++) {
+ sc->pins[i].pin_num = i;
+ sc->pins[i].pin_caps |= GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+ if (pu_pins & (1 << i))
+ sc->pins[i].pin_caps |= GPIO_PIN_PULLUP;
+ if (pd_pins & (1 << i))
+ sc->pins[i].pin_caps |= GPIO_PIN_PULLDOWN;
+ sc->pins[i].intr_polarity = INTR_POLARITY_CONFORM;
+ sc->pins[i].intr_trigger = INTR_TRIGGER_CONFORM;
+
+ snprintf(sc->pins[i].pin_name, GPIOMAXNAME - 1, "gpio%c%d",
+ device_get_unit(dev) + 'a', i);
+ sc->pins[i].pin_name[GPIOMAXNAME - 1] = '\0';
+
+ jz4780_gpio_pin_probe(sc, i);
+ }
+
+ if (jz4780_gpio_register_isrcs(sc) != 0)
+ goto fail;
+
+ if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
+ device_printf(dev, "could not register PIC\n");
+ goto fail;
+ }
+
+ if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+ jz4780_gpio_intr, NULL, sc, &sc->intrhand) != 0)
+ goto fail_pic;
+
+ sc->busdev = gpiobus_attach_bus(dev);
+ if (sc->busdev == NULL)
+ goto fail_pic;
+
+ return (0);
+fail_pic:
+ intr_pic_deregister(dev, OF_xref_from_node(node));
+fail:
+ if (sc->intrhand != NULL)
+ bus_teardown_intr(dev, sc->res[1], sc->intrhand);
+ bus_release_resources(dev, jz4780_gpio_spec, sc->res);
+ JZ4780_GPIO_LOCK_DESTROY(sc);
+ return (ENXIO);
+}
+
+static int
+jz4780_gpio_detach(device_t dev)
+{
+ struct jz4780_gpio_softc *sc = device_get_softc(dev);
+
+ bus_release_resources(dev, jz4780_gpio_spec, sc->res);
+ JZ4780_GPIO_LOCK_DESTROY(sc);
+ return (0);
+}
+
+static int
+jz4780_gpio_configure_pin(device_t dev, uint32_t pin, uint32_t func,
+ uint32_t flags)
+{
+ struct jz4780_gpio_softc *sc;
+ int retval;
+
+ if (pin >= JZ4780_GPIO_PINS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ JZ4780_GPIO_LOCK(sc);
+ retval = jz4780_gpio_pin_set_func(sc, pin, func);
+ if (retval == 0)
+ retval = jz4780_gpio_pin_set_bias(sc, pin, flags);
+ JZ4780_GPIO_UNLOCK(sc);
+ return (retval);
+}
+
+static device_t
+jz4780_gpio_get_bus(device_t dev)
+{
+ struct jz4780_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->busdev);
+}
+
+static int
+jz4780_gpio_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = JZ4780_GPIO_PINS - 1;
+ return (0);
+}
+
+static int
+jz4780_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct jz4780_gpio_softc *sc;
+
+ if (pin >= JZ4780_GPIO_PINS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ JZ4780_GPIO_LOCK(sc);
+ *caps = sc->pins[pin].pin_caps;
+ JZ4780_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+jz4780_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct jz4780_gpio_softc *sc;
+
+ if (pin >= JZ4780_GPIO_PINS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ JZ4780_GPIO_LOCK(sc);
+ *flags = sc->pins[pin].pin_flags;
+ JZ4780_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+jz4780_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct jz4780_gpio_softc *sc;
+
+ if (pin >= JZ4780_GPIO_PINS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ strncpy(name, sc->pins[pin].pin_name, GPIOMAXNAME - 1);
+ name[GPIOMAXNAME - 1] = '\0';
+
+ return (0);
+}
+
+static int
+jz4780_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct jz4780_gpio_softc *sc;
+ int retval;
+
+ if (pin >= JZ4780_GPIO_PINS)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ JZ4780_GPIO_LOCK(sc);
+ retval = jz4780_gpio_pin_set_direction(sc, pin,
+ flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT));
+ if (retval == 0)
+ retval = jz4780_gpio_pin_set_bias(sc, pin,
+ flags & (GPIO_PIN_PULLDOWN | GPIO_PIN_PULLUP));
+ JZ4780_GPIO_UNLOCK(sc);
+
+ return (retval);
+}
+
+static int
+jz4780_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct jz4780_gpio_softc *sc;
+ uint32_t mask;
+ int retval;
+
+ if (pin >= JZ4780_GPIO_PINS)
+ return (EINVAL);
+
+ retval = EINVAL;
+ mask = (1u << pin);
+ sc = device_get_softc(dev);
+ JZ4780_GPIO_LOCK(sc);
+ if (sc->pins[pin].pin_func == JZ_FUNC_GPIO) {
+ CSR_WRITE_4(sc, value ? JZ_GPIO_PAT0S : JZ_GPIO_PAT0C, mask);
+ retval = 0;
+ }
+ JZ4780_GPIO_UNLOCK(sc);
+
+ return (retval);
+}
+
+static int
+jz4780_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct jz4780_gpio_softc *sc;
+ uint32_t data, mask;
+
+ if (pin >= JZ4780_GPIO_PINS)
+ return (EINVAL);
+
+ mask = (1u << pin);
+ sc = device_get_softc(dev);
+ JZ4780_GPIO_LOCK(sc);
+ data = CSR_READ_4(sc, JZ_GPIO_PIN);
+ JZ4780_GPIO_UNLOCK(sc);
+ *val = (data & mask) ? 1 : 0;
+
+ return (0);
+}
+
+static int
+jz4780_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct jz4780_gpio_softc *sc;
+ uint32_t data, mask;
+ int retval;
+
+ if (pin >= JZ4780_GPIO_PINS)
+ return (EINVAL);
+
+ retval = EINVAL;
+ mask = (1u << pin);
+ sc = device_get_softc(dev);
+ JZ4780_GPIO_LOCK(sc);
+ if (sc->pins[pin].pin_func == JZ_FUNC_GPIO &&
+ sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT) {
+ data = CSR_READ_4(sc, JZ_GPIO_PIN);
+ CSR_WRITE_4(sc, (data & mask) ? JZ_GPIO_PAT0C : JZ_GPIO_PAT0S,
+ mask);
+ retval = 0;
+ }
+ JZ4780_GPIO_UNLOCK(sc);
+
+ return (retval);
+}
+
+#ifdef FDT
+static int
+jz_gpio_map_intr_fdt(device_t dev, struct intr_map_data *data, u_int *irqp,
+ enum intr_polarity *polp, enum intr_trigger *trigp)
+{
+ struct jz4780_gpio_softc *sc;
+ struct intr_map_data_fdt *daf;
+
+ sc = device_get_softc(dev);
+ daf = (struct intr_map_data_fdt *)data;
+
+ if (data == NULL || data->type != INTR_MAP_DATA_FDT ||
+ daf->ncells == 0 || daf->ncells > 2)
+ return (EINVAL);
+
+ *irqp = daf->cells[0];
+ if (daf->ncells == 1) {
+ *trigp = INTR_TRIGGER_CONFORM;
+ *polp = INTR_POLARITY_CONFORM;
+ return (0);
+ }
+
+ switch (daf->cells[1])
+ {
+ case IRQ_TYPE_EDGE_RISING:
+ *trigp = INTR_TRIGGER_EDGE;
+ *polp = INTR_POLARITY_HIGH;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ *trigp = INTR_TRIGGER_EDGE;
+ *polp = INTR_POLARITY_LOW;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ *trigp = INTR_TRIGGER_LEVEL;
+ *polp = INTR_POLARITY_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ *trigp = INTR_TRIGGER_LEVEL;
+ *polp = INTR_POLARITY_LOW;
+ break;
+ default:
+ device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n",
+ daf->cells[1]);
+ return (ENOTSUP);
+ }
+
+ return (0);
+}
+#endif
+
+static int
+jz_gpio_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
+ enum intr_polarity *polp, enum intr_trigger *trigp)
+{
+ struct jz4780_gpio_softc *sc;
+ enum intr_polarity pol;
+ enum intr_trigger trig;
+ u_int irq;
+
+ sc = device_get_softc(dev);
+ switch (data->type) {
+#ifdef FDT
+ case INTR_MAP_DATA_FDT:
+ if (jz_gpio_map_intr_fdt(dev, data, &irq, &pol, &trig) != 0)
+ return (EINVAL);
+ break;
+#endif
+ default:
+ return (EINVAL);
+ }
+
+ if (irq >= nitems(sc->pins))
+ return (EINVAL);
+
+ *irqp = irq;
+ if (polp != NULL)
+ *polp = pol;
+ if (trigp != NULL)
+ *trigp = trig;
+ return (0);
+}
+
+static int
+jz4780_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct jz4780_gpio_softc *sc;
+ int retval;
+ u_int irq;
+
+ retval = jz_gpio_map_intr(dev, data, &irq, NULL, NULL);
+ if (retval == 0) {
+ sc = device_get_softc(dev);
+ *isrcp = &sc->pins[irq].pin_irqsrc;
+ }
+ return (retval);
+}
+
+static int
+jz4780_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+ struct resource *res, struct intr_map_data *data)
+{
+ struct jz4780_gpio_softc *sc;
+ struct jz4780_gpio_pin *pin;
+ enum intr_polarity pol;
+ enum intr_trigger trig;
+ uint32_t mask, irq;
+
+ if (data == NULL)
+ return (ENOTSUP);
+
+ /* Get config for resource. */
+ if (jz_gpio_map_intr(dev, data, &irq, &pol, &trig))
+ return (EINVAL);
+
+ pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc);
+ if (isrc != &pin->pin_irqsrc)
+ return (EINVAL);
+
+ /* Compare config if this is not first setup. */
+ if (isrc->isrc_handlers != 0) {
+ if ((pol != INTR_POLARITY_CONFORM && pol != pin->intr_polarity) ||
+ (trig != INTR_TRIGGER_CONFORM && trig != pin->intr_trigger))
+ return (EINVAL);
+ else
+ return (0);
+ }
+
+ if (pol == INTR_POLARITY_CONFORM)
+ pol = INTR_POLARITY_LOW; /* just pick some */
+ if (trig == INTR_TRIGGER_CONFORM)
+ trig = INTR_TRIGGER_EDGE; /* just pick some */
+
+ sc = device_get_softc(dev);
+ mask = 1u << pin->pin_num;
+
+ JZ4780_GPIO_LOCK(sc);
+ CSR_WRITE_4(sc, JZ_GPIO_MASKS, mask);
+ CSR_WRITE_4(sc, JZ_GPIO_INTS, mask);
+
+ if (trig == INTR_TRIGGER_LEVEL)
+ CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask);
+ else
+ CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask);
+
+ if (pol == INTR_POLARITY_LOW)
+ CSR_WRITE_4(sc, JZ_GPIO_PAT0C, mask);
+ else
+ CSR_WRITE_4(sc, JZ_GPIO_PAT0S, mask);
+
+ pin->pin_func = JZ_FUNC_INTR;
+ pin->intr_trigger = trig;
+ pin->intr_polarity = pol;
+
+ CSR_WRITE_4(sc, JZ_GPIO_FLAGC, mask);
+ CSR_WRITE_4(sc, JZ_GPIO_MASKC, mask);
+ JZ4780_GPIO_UNLOCK(sc);
+ return (0);
+}
+
+static void
+jz4780_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct jz4780_gpio_softc *sc;
+ struct jz4780_gpio_pin *pin;
+
+ sc = device_get_softc(dev);
+ pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc);
+
+ CSR_WRITE_4(sc, JZ_GPIO_MASKC, 1u << pin->pin_num);
+}
+
+static void
+jz4780_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct jz4780_gpio_softc *sc;
+ struct jz4780_gpio_pin *pin;
+
+ sc = device_get_softc(dev);
+ pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc);
+
+ CSR_WRITE_4(sc, JZ_GPIO_MASKS, 1u << pin->pin_num);
+}
+
+static void
+jz4780_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ jz4780_gpio_pic_disable_intr(dev, isrc);
+}
+
+static void
+jz4780_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ jz4780_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+jz4780_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct jz4780_gpio_softc *sc;
+ struct jz4780_gpio_pin *pin;
+
+ sc = device_get_softc(dev);
+ pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc);
+
+ CSR_WRITE_4(sc, JZ_GPIO_FLAGC, 1u << pin->pin_num);
+}
+
+static int
+jz4780_gpio_intr(void *arg)
+{
+ struct jz4780_gpio_softc *sc;
+ uint32_t i, interrupts;
+
+ sc = arg;
+ interrupts = CSR_READ_4(sc, JZ_GPIO_FLAG);
+
+ for (i = 0; interrupts != 0; i++, interrupts >>= 1) {
+ if ((interrupts & 0x1) == 0)
+ continue;
+ if (intr_isrc_dispatch(&sc->pins[i].pin_irqsrc,
+ curthread->td_intr_frame) != 0) {
+ device_printf(sc->dev, "spurious interrupt %d\n", i);
+ PIC_DISABLE_INTR(sc->dev, &sc->pins[i].pin_irqsrc);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static device_method_t jz4780_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_gpio_probe),
+ DEVMETHOD(device_attach, jz4780_gpio_attach),
+ DEVMETHOD(device_detach, jz4780_gpio_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, jz4780_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, jz4780_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, jz4780_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, jz4780_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, jz4780_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, jz4780_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, jz4780_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, jz4780_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, jz4780_gpio_pin_toggle),
+
+ /* Custom interface to set pin function */
+ DEVMETHOD(jz4780_gpio_configure_pin, jz4780_gpio_configure_pin),
+
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_setup_intr, jz4780_gpio_pic_setup_intr),
+ DEVMETHOD(pic_enable_intr, jz4780_gpio_pic_enable_intr),
+ DEVMETHOD(pic_disable_intr, jz4780_gpio_pic_disable_intr),
+ DEVMETHOD(pic_map_intr, jz4780_gpio_pic_map_intr),
+ DEVMETHOD(pic_post_filter, jz4780_gpio_pic_post_filter),
+ DEVMETHOD(pic_post_ithread, jz4780_gpio_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, jz4780_gpio_pic_pre_ithread),
+
+ DEVMETHOD_END
+};
+
+static driver_t jz4780_gpio_driver = {
+ "gpio",
+ jz4780_gpio_methods,
+ sizeof(struct jz4780_gpio_softc),
+};
+
+static devclass_t jz4780_gpio_devclass;
+
+EARLY_DRIVER_MODULE(jz4780_gpio, simplebus, jz4780_gpio_driver,
+ jz4780_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/mips/ingenic/jz4780_gpio_if.m b/sys/mips/ingenic/jz4780_gpio_if.m
new file mode 100644
index 000000000000..2bbe7ee04929
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_gpio_if.m
@@ -0,0 +1,41 @@
+#-
+# Copyright (c) 2015 Alexander Kabaev <kan@FreeBSD.org>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+
+INTERFACE jz4780_gpio;
+
+/**
+ * Configures pin as specified by FDT pinctrl entry
+ */
+METHOD int configure_pin {
+ device_t dev;
+ uint32_t gpio;
+ uint32_t func;
+ uint32_t flags;
+};
diff --git a/sys/mips/ingenic/jz4780_intr.c b/sys/mips/ingenic/jz4780_intr.c
new file mode 100644
index 000000000000..c28f5f90c163
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_intr.c
@@ -0,0 +1,333 @@
+/*-
+ * Copyright (c) 2015 Alexander Kabaev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/cpuset.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+#include <sys/sched.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/smp.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+
+#include "pic_if.h"
+
+#define JZ4780_NIRQS 64
+
+static int jz4780_pic_intr(void *);
+
+struct jz4780_pic_isrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct jz4780_pic_softc {
+ device_t pic_dev;
+ void * pic_intrhand;
+ struct resource * pic_res[2];
+ struct jz4780_pic_isrc pic_irqs[JZ4780_NIRQS];
+ uint32_t nirqs;
+};
+
+#define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc)
+
+static struct resource_spec jz4780_pic_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */
+ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt */
+ { -1, 0 }
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"ingenic,jz4780-intc", true},
+ {NULL, false}
+};
+
+#define READ4(_sc, _reg) bus_read_4((_sc)->pic_res[0], _reg)
+#define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->pic_res[0], _reg, _val)
+
+static int
+jz4780_pic_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+ device_set_desc(dev, "JZ4780 Interrupt Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static inline void
+pic_irq_unmask(struct jz4780_pic_softc *sc, u_int irq)
+{
+ if (irq < 32)
+ WRITE4(sc, JZ_ICMCR0, (1u << irq));
+ else
+ WRITE4(sc, JZ_ICMCR1, (1u << (irq - 32)));
+}
+
+static inline void
+pic_irq_mask(struct jz4780_pic_softc *sc, u_int irq)
+{
+ if (irq < 32)
+ WRITE4(sc, JZ_ICMSR0, (1u << irq));
+ else
+ WRITE4(sc, JZ_ICMSR1, (1u << (irq - 32)));
+}
+
+static inline intptr_t
+pic_xref(device_t dev)
+{
+ return (OF_xref_from_node(ofw_bus_get_node(dev)));
+}
+
+static int
+jz4780_pic_register_isrcs(struct jz4780_pic_softc *sc)
+{
+ int error;
+ uint32_t irq, i;
+ struct intr_irqsrc *isrc;
+ const char *name;
+
+ name = device_get_nameunit(sc->pic_dev);
+ for (irq = 0; irq < sc->nirqs; irq++) {
+ sc->pic_irqs[irq].irq = irq;
+ isrc = PIC_INTR_ISRC(sc, irq);
+ error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s,%d",
+ name, irq);
+ if (error != 0) {
+ for (i = 0; i < irq; i++)
+ intr_isrc_deregister(PIC_INTR_ISRC(sc, irq));
+ device_printf(sc->pic_dev, "%s failed", __func__);
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+jz4780_pic_attach(device_t dev)
+{
+ struct jz4780_pic_softc *sc;
+ intptr_t xref;
+
+ xref = pic_xref(dev);
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, jz4780_pic_spec, sc->pic_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->pic_dev = dev;
+
+ /* Set the number of interrupts */
+ sc->nirqs = nitems(sc->pic_irqs);
+
+ /* Mask all interrupts */
+ WRITE4(sc, JZ_ICMR0, 0xFFFFFFFF);
+ WRITE4(sc, JZ_ICMR1, 0xFFFFFFFF);
+
+ /* Register the interrupts */
+ if (jz4780_pic_register_isrcs(sc) != 0) {
+ device_printf(dev, "could not register PIC ISRCs\n");
+ goto cleanup;
+ }
+
+ /*
+ * Now, when everything is initialized, it's right time to
+ * register interrupt controller to interrupt framefork.
+ */
+ if (intr_pic_register(dev, xref) == NULL) {
+ device_printf(dev, "could not register PIC\n");
+ goto cleanup;
+ }
+
+ if (bus_setup_intr(dev, sc->pic_res[1], INTR_TYPE_CLK,
+ jz4780_pic_intr, NULL, sc, &sc->pic_intrhand)) {
+ device_printf(dev, "could not setup irq handler\n");
+ intr_pic_deregister(dev, xref);
+ goto cleanup;
+ }
+
+ return (0);
+
+cleanup:
+ bus_release_resources(dev, jz4780_pic_spec, sc->pic_res);
+
+ return(ENXIO);
+}
+
+static int
+jz4780_pic_intr(void *arg)
+{
+ struct jz4780_pic_softc *sc = arg;
+ struct intr_irqsrc *isrc;
+ struct thread *td;
+ uint32_t i, intr;
+
+ td = curthread;
+ /* Workaround: do not inflate intr nesting level */
+ td->td_intr_nesting_level--;
+
+ intr = READ4(sc, JZ_ICPR0);
+ while ((i = fls(intr)) != 0) {
+ i--;
+ intr &= ~(1u << i);
+
+ isrc = PIC_INTR_ISRC(sc, i);
+ if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
+ device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i);
+ pic_irq_mask(sc, i);
+ continue;
+ }
+ }
+
+ KASSERT(i == 0, ("all interrupts handled"));
+
+ intr = READ4(sc, JZ_ICPR1);
+ while ((i = fls(intr)) != 0) {
+ i--;
+ intr &= ~(1u << i);
+ i += 32;
+
+ isrc = PIC_INTR_ISRC(sc, i);
+ if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
+ device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i);
+ pic_irq_mask(sc, i);
+ continue;
+ }
+ }
+
+ KASSERT(i == 0, ("all interrupts handled"));
+ td->td_intr_nesting_level++;
+
+ return (FILTER_HANDLED);
+}
+
+static int
+jz4780_pic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+#ifdef FDT
+ struct jz4780_pic_softc *sc;
+ struct intr_map_data_fdt *daf;
+
+ sc = device_get_softc(dev);
+ daf = (struct intr_map_data_fdt *)data;
+
+ if (data == NULL || data->type != INTR_MAP_DATA_FDT ||
+ daf->ncells != 1 || daf->cells[0] >= sc->nirqs)
+ return (EINVAL);
+
+ *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]);
+ return (0);
+#else
+ return (EINVAL);
+#endif
+}
+
+static void
+jz4780_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct jz4780_pic_isrc *pic_isrc;
+
+ pic_isrc = (struct jz4780_pic_isrc *)isrc;
+ pic_irq_unmask(device_get_softc(dev), pic_isrc->irq);
+}
+
+static void
+jz4780_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct jz4780_pic_isrc *pic_isrc;
+
+ pic_isrc = (struct jz4780_pic_isrc *)isrc;
+ pic_irq_mask(device_get_softc(dev), pic_isrc->irq);
+}
+
+static void
+jz4780_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ jz4780_pic_disable_intr(dev, isrc);
+}
+
+static void
+jz4780_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+ jz4780_pic_enable_intr(dev, isrc);
+}
+
+static device_method_t jz4780_pic_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_pic_probe),
+ DEVMETHOD(device_attach, jz4780_pic_attach),
+ /* Interrupt controller interface */
+ DEVMETHOD(pic_enable_intr, jz4780_pic_enable_intr),
+ DEVMETHOD(pic_disable_intr, jz4780_pic_disable_intr),
+ DEVMETHOD(pic_map_intr, jz4780_pic_map_intr),
+ DEVMETHOD(pic_post_ithread, jz4780_pic_post_ithread),
+ DEVMETHOD(pic_pre_ithread, jz4780_pic_pre_ithread),
+ { 0, 0 }
+};
+
+static driver_t jz4780_pic_driver = {
+ "intc",
+ jz4780_pic_methods,
+ sizeof(struct jz4780_pic_softc),
+};
+
+static devclass_t jz4780_pic_devclass;
+
+EARLY_DRIVER_MODULE(intc, ofwbus, jz4780_pic_driver, jz4780_pic_devclass, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
diff --git a/sys/mips/ingenic/jz4780_machdep.c b/sys/mips/ingenic/jz4780_machdep.c
new file mode 100644
index 000000000000..b125b53acabb
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_machdep.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 2009 Oleksandr Tymoshenko
+ * Copyright (c) 2015 Alexander Kabaev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cons.h>
+#include <sys/kdb.h>
+#include <sys/reboot.h>
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#endif
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+
+#include <net/ethernet.h>
+
+#include <machine/clock.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/hwfunc.h>
+#include <machine/md_var.h>
+#include <machine/trap.h>
+#include <machine/vmparam.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+#include <mips/ingenic/jz4780_cpuregs.h>
+
+uint32_t * const led = (uint32_t *)0xb0010548;
+
+extern char edata[], end[];
+static char boot1_env[4096];
+
+void
+platform_cpu_init(void)
+{
+ uint32_t reg;
+
+ /*
+ * Do not expect mbox interrups while writing
+ * mbox
+ */
+ reg = mips_rd_xburst_reim();
+ reg &= ~JZ_REIM_MIRQ0M;
+ mips_wr_xburst_reim(reg);
+
+ /* Clean mailboxes */
+ mips_wr_xburst_mbox0(0);
+ mips_wr_xburst_mbox1(0);
+ mips_wr_xburst_core_sts(~JZ_CORESTS_MIRQ0P);
+
+ /* Unmask mbox interrupts */
+ reg |= JZ_REIM_MIRQ0M;
+ mips_wr_xburst_reim(reg);
+}
+
+void
+platform_reset(void)
+{
+ /*
+ * For now, provoke a watchdog reset in about a second, so UART buffers
+ * have a fighting chance to flush before we pull the plug
+ */
+ writereg(JZ_TCU_BASE + JZ_WDOG_TCER, 0); /* disable watchdog */
+ writereg(JZ_TCU_BASE + JZ_WDOG_TCNT, 0); /* reset counter */
+ writereg(JZ_TCU_BASE + JZ_WDOG_TDR, 128); /* wait for ~1s */
+ writereg(JZ_TCU_BASE + JZ_WDOG_TCSR, TCSR_RTC_EN | TCSR_DIV_256);
+ writereg(JZ_TCU_BASE + JZ_WDOG_TCER, TCER_ENABLE); /* fire! */
+
+ /* Wait for reset */
+ while (1)
+ ;
+}
+
+static void
+mips_init(void)
+{
+ int i;
+#ifdef FDT
+ struct mem_region mr[FDT_MEM_REGIONS];
+ uint64_t val;
+ int mr_cnt;
+ int j;
+#endif
+
+ for (i = 0; i < 10; i++) {
+ phys_avail[i] = 0;
+ }
+
+ /* The minimal amount of memory Ingenic SoC can have. */
+ dump_avail[0] = phys_avail[0] = MIPS_KSEG0_TO_PHYS(kernel_kseg0_end);
+ physmem = realmem = btoc(32 * 1024 * 1024);
+
+ /*
+ * X1000 mips cpu special.
+ * TODO: do anyone know what is this ?
+ */
+ __asm(
+ "li $2, 0xa9000000 \n\t"
+ "mtc0 $2, $5, 4 \n\t"
+ "nop \n\t"
+ ::"r"(2));
+
+#ifdef FDT
+ if (fdt_get_mem_regions(mr, &mr_cnt, &val) == 0) {
+
+ physmem = realmem = btoc(val);
+
+ KASSERT((phys_avail[0] >= mr[0].mr_start) && \
+ (phys_avail[0] < (mr[0].mr_start + mr[0].mr_size)),
+ ("First region is not within FDT memory range"));
+
+ /* Limit size of the first region */
+ phys_avail[1] = (mr[0].mr_start + MIN(mr[0].mr_size, ctob(realmem)));
+ dump_avail[1] = phys_avail[1];
+
+ /* Add the rest of regions */
+ for (i = 1, j = 2; i < mr_cnt; i++, j+=2) {
+ phys_avail[j] = mr[i].mr_start;
+ phys_avail[j+1] = (mr[i].mr_start + mr[i].mr_size);
+ dump_avail[j] = phys_avail[j];
+ dump_avail[j+1] = phys_avail[j+1];
+ }
+ }
+#endif
+
+ init_param1();
+ init_param2(physmem);
+ mips_cpu_init();
+ pmap_bootstrap();
+ mips_proc0_init();
+ mutex_init();
+ kdb_init();
+ led[0] = 0x8000;
+#ifdef KDB
+ if (boothowto & RB_KDB)
+ kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
+#endif
+}
+
+static void
+_parse_bootarg(char *v)
+{
+ char *n;
+
+ if (*v == '-') {
+ while (*v != '\0') {
+ v++;
+ switch (*v) {
+ case 'a': boothowto |= RB_ASKNAME; break;
+ /* Someone should simulate that ;-) */
+ case 'C': boothowto |= RB_CDROM; break;
+ case 'd': boothowto |= RB_KDB; break;
+ case 'D': boothowto |= RB_MULTIPLE; break;
+ case 'm': boothowto |= RB_MUTE; break;
+ case 'g': boothowto |= RB_GDB; break;
+ case 'h': boothowto |= RB_SERIAL; break;
+ case 'p': boothowto |= RB_PAUSE; break;
+ case 'r': boothowto |= RB_DFLTROOT; break;
+ case 's': boothowto |= RB_SINGLE; break;
+ case 'v': boothowto |= RB_VERBOSE; break;
+ }
+ }
+ } else {
+ n = strsep(&v, "=");
+ if (v == NULL)
+ kern_setenv(n, "1");
+ else
+ kern_setenv(n, v);
+ }
+}
+
+static void
+_parse_cmdline(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 1; i < argc; i++)
+ _parse_bootarg(argv[i]);
+}
+
+#ifdef FDT
+/* Parse cmd line args as env - copied from xlp_machdep. */
+/* XXX-BZ this should really be centrally provided for all (boot) code. */
+static void
+_parse_bootargs(char *cmdline)
+{
+ char *v;
+
+ while ((v = strsep(&cmdline, " \n")) != NULL) {
+ if (*v == '\0')
+ continue;
+ _parse_bootarg(v);
+ }
+}
+#endif
+
+void
+platform_start(__register_t a0, __register_t a1,
+ __register_t a2 __unused, __register_t a3 __unused)
+{
+ char **argv;
+ int argc;
+ vm_offset_t kernend;
+#ifdef FDT
+ vm_offset_t dtbp;
+ phandle_t chosen;
+ char buf[2048]; /* early stack supposedly big enough */
+#endif
+ /*
+ * clear the BSS and SBSS segments, this should be first call in
+ * the function
+ */
+ kernend = (vm_offset_t)&end;
+ memset(&edata, 0, kernend - (vm_offset_t)(&edata));
+
+ mips_postboot_fixup();
+
+ /* Initialize pcpu stuff */
+ mips_pcpu0_init();
+
+ /* Something to hold kernel env until kmem is available */
+ init_static_kenv(boot1_env, sizeof(boot1_env));
+#ifdef FDT
+ /*
+ * Find the dtb passed in by the boot loader (currently fictional).
+ */
+ dtbp = (vm_offset_t)NULL;
+
+#if defined(FDT_DTB_STATIC)
+ /*
+ * In case the device tree blob was not retrieved (from metadata) try
+ * to use the statically embedded one.
+ */
+ if (dtbp == (vm_offset_t)NULL)
+ dtbp = (vm_offset_t)&fdt_static_dtb;
+#else
+#error "Non-static FDT not supported on JZ4780"
+#endif
+ if (OF_install(OFW_FDT, 0) == FALSE)
+ while (1);
+ if (OF_init((void *)dtbp) != 0)
+ while (1);
+#endif
+
+ cninit();
+#ifdef FDT
+ /*
+ * Get bootargs from FDT if specified.
+ */
+ chosen = OF_finddevice("/chosen");
+ if (OF_getprop(chosen, "bootargs", buf, sizeof(buf)) != -1)
+ _parse_bootargs(buf);
+#endif
+ /* Parse cmdline from U-Boot */
+ argc = a0;
+ argv = (char **)a1;
+ _parse_cmdline(argc, argv);
+
+ mips_init();
+}
diff --git a/sys/mips/ingenic/jz4780_mmc.c b/sys/mips/ingenic/jz4780_mmc.c
new file mode 100644
index 000000000000..0cf6be2971e0
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_mmc.c
@@ -0,0 +1,1002 @@
+/*-
+ * Copyright (c) 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+
+#undef JZ_MMC_DEBUG
+
+#define JZ_MSC_MEMRES 0
+#define JZ_MSC_IRQRES 1
+#define JZ_MSC_RESSZ 2
+#define JZ_MSC_DMA_SEGS 128
+#define JZ_MSC_DMA_MAX_SIZE MAXPHYS
+
+#define JZ_MSC_INT_ERR_BITS (JZ_INT_CRC_RES_ERR | JZ_INT_CRC_READ_ERR | \
+ JZ_INT_CRC_WRITE_ERR | JZ_INT_TIMEOUT_RES | \
+ JZ_INT_TIMEOUT_READ)
+static int jz4780_mmc_pio_mode = 0;
+
+TUNABLE_INT("hw.jz.mmc.pio_mode", &jz4780_mmc_pio_mode);
+
+struct jz4780_mmc_dma_desc {
+ uint32_t dma_next;
+ uint32_t dma_phys;
+ uint32_t dma_len;
+ uint32_t dma_cmd;
+};
+
+struct jz4780_mmc_softc {
+ bus_space_handle_t sc_bsh;
+ bus_space_tag_t sc_bst;
+ device_t sc_dev;
+ clk_t sc_clk;
+ int sc_bus_busy;
+ int sc_resid;
+ int sc_timeout;
+ struct callout sc_timeoutc;
+ struct mmc_host sc_host;
+ struct mmc_request * sc_req;
+ struct mtx sc_mtx;
+ struct resource * sc_res[JZ_MSC_RESSZ];
+ uint32_t sc_intr_seen;
+ uint32_t sc_intr_mask;
+ uint32_t sc_intr_wait;
+ void * sc_intrhand;
+ uint32_t sc_cmdat;
+
+ /* Fields required for DMA access. */
+ bus_addr_t sc_dma_desc_phys;
+ bus_dmamap_t sc_dma_map;
+ bus_dma_tag_t sc_dma_tag;
+ void * sc_dma_desc;
+ bus_dmamap_t sc_dma_buf_map;
+ bus_dma_tag_t sc_dma_buf_tag;
+ int sc_dma_inuse;
+ int sc_dma_map_err;
+ uint32_t sc_dma_ctl;
+};
+
+static struct resource_spec jz4780_mmc_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
+ { -1, 0, 0 }
+};
+
+static int jz4780_mmc_probe(device_t);
+static int jz4780_mmc_attach(device_t);
+static int jz4780_mmc_detach(device_t);
+static int jz4780_mmc_setup_dma(struct jz4780_mmc_softc *);
+static int jz4780_mmc_reset(struct jz4780_mmc_softc *);
+static void jz4780_mmc_intr(void *);
+static int jz4780_mmc_enable_clock(struct jz4780_mmc_softc *);
+static int jz4780_mmc_config_clock(struct jz4780_mmc_softc *, uint32_t);
+
+static int jz4780_mmc_update_ios(device_t, device_t);
+static int jz4780_mmc_request(device_t, device_t, struct mmc_request *);
+static int jz4780_mmc_get_ro(device_t, device_t);
+static int jz4780_mmc_acquire_host(device_t, device_t);
+static int jz4780_mmc_release_host(device_t, device_t);
+
+#define JZ_MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define JZ_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define JZ_MMC_READ_2(_sc, _reg) \
+ bus_space_read_2((_sc)->sc_bst, (_sc)->sc_bsh, _reg)
+#define JZ_MMC_WRITE_2(_sc, _reg, _value) \
+ bus_space_write_2((_sc)->sc_bst, (_sc)->sc_bsh, _reg, _value)
+#define JZ_MMC_READ_4(_sc, _reg) \
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _reg)
+#define JZ_MMC_WRITE_4(_sc, _reg, _value) \
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _reg, _value)
+
+static int
+jz4780_mmc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-mmc"))
+ return (ENXIO);
+ if (device_get_unit(dev) > 0) /* XXXKAN */
+ return (ENXIO);
+ device_set_desc(dev, "Ingenic JZ4780 Integrated MMC/SD controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_mmc_attach(device_t dev)
+{
+ struct jz4780_mmc_softc *sc;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *tree;
+ device_t child;
+ ssize_t len;
+ pcell_t prop;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_req = NULL;
+ if (bus_alloc_resources(dev, jz4780_mmc_res_spec, sc->sc_res) != 0) {
+ device_printf(dev, "cannot allocate device resources\n");
+ return (ENXIO);
+ }
+ sc->sc_bst = rman_get_bustag(sc->sc_res[JZ_MSC_MEMRES]);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_res[JZ_MSC_MEMRES]);
+ if (bus_setup_intr(dev, sc->sc_res[JZ_MSC_IRQRES],
+ INTR_TYPE_MISC | INTR_MPSAFE, NULL, jz4780_mmc_intr, sc,
+ &sc->sc_intrhand)) {
+ bus_release_resources(dev, jz4780_mmc_res_spec, sc->sc_res);
+ device_printf(dev, "cannot setup interrupt handler\n");
+ return (ENXIO);
+ }
+ sc->sc_timeout = 10;
+ ctx = device_get_sysctl_ctx(dev);
+ tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "req_timeout", CTLFLAG_RW,
+ &sc->sc_timeout, 0, "Request timeout in seconds");
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), "jz4780_mmc",
+ MTX_DEF);
+ callout_init_mtx(&sc->sc_timeoutc, &sc->sc_mtx, 0);
+
+ /* Reset controller. */
+ if (jz4780_mmc_reset(sc) != 0) {
+ device_printf(dev, "cannot reset the controller\n");
+ goto fail;
+ }
+ if (jz4780_mmc_pio_mode == 0 && jz4780_mmc_setup_dma(sc) != 0) {
+ device_printf(sc->sc_dev, "Couldn't setup DMA!\n");
+ jz4780_mmc_pio_mode = 1;
+ }
+ if (bootverbose)
+ device_printf(sc->sc_dev, "DMA status: %s\n",
+ jz4780_mmc_pio_mode ? "disabled" : "enabled");
+
+ node = ofw_bus_get_node(dev);
+ /* Determine max operating frequency */
+ sc->sc_host.f_max = 24000000;
+ len = OF_getencprop(node, "max-frequency", &prop, sizeof(prop));
+ if (len / sizeof(prop) == 1)
+ sc->sc_host.f_max = prop;
+ sc->sc_host.f_min = sc->sc_host.f_max / 128;
+
+ sc->sc_host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
+ sc->sc_host.caps = MMC_CAP_HSPEED;
+ sc->sc_host.mode = mode_sd;
+ /*
+ * Check for bus-width property, default to both 4 and 8 bit
+ * if no bus width is specified.
+ */
+ len = OF_getencprop(node, "bus-width", &prop, sizeof(prop));
+ if (len / sizeof(prop) != 1)
+ sc->sc_host.caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
+ else if (prop == 8)
+ sc->sc_host.caps |= MMC_CAP_8_BIT_DATA;
+ else if (prop == 4)
+ sc->sc_host.caps |= MMC_CAP_4_BIT_DATA;
+ /* Activate the module clock. */
+ if (jz4780_mmc_enable_clock(sc) != 0) {
+ device_printf(dev, "cannot activate mmc clock\n");
+ goto fail;
+ }
+
+ child = device_add_child(dev, "mmc", -1);
+ if (child == NULL) {
+ device_printf(dev, "attaching MMC bus failed!\n");
+ goto fail;
+ }
+ if (device_probe_and_attach(child) != 0) {
+ device_printf(dev, "attaching MMC child failed!\n");
+ device_delete_child(dev, child);
+ goto fail;
+ }
+
+ return (0);
+
+fail:
+ callout_drain(&sc->sc_timeoutc);
+ mtx_destroy(&sc->sc_mtx);
+ bus_teardown_intr(dev, sc->sc_res[JZ_MSC_IRQRES], sc->sc_intrhand);
+ bus_release_resources(dev, jz4780_mmc_res_spec, sc->sc_res);
+ if (sc->sc_clk != NULL)
+ clk_release(sc->sc_clk);
+ return (ENXIO);
+}
+
+static int
+jz4780_mmc_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static int
+jz4780_mmc_enable_clock(struct jz4780_mmc_softc *sc)
+{
+ int err;
+
+ err = clk_get_by_ofw_name(sc->sc_dev, 0, "mmc", &sc->sc_clk);
+ if (err == 0)
+ err = clk_enable(sc->sc_clk);
+ if (err == 0)
+ err = clk_set_freq(sc->sc_clk, sc->sc_host.f_max, 0);
+ if (err != 0)
+ clk_release(sc->sc_clk);
+ return (err);
+}
+
+static void
+jz4780_mmc_dma_desc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
+{
+ struct jz4780_mmc_softc *sc;
+
+ sc = (struct jz4780_mmc_softc *)arg;
+ if (err) {
+ sc->sc_dma_map_err = err;
+ return;
+ }
+ sc->sc_dma_desc_phys = segs[0].ds_addr;
+}
+
+static int
+jz4780_mmc_setup_dma(struct jz4780_mmc_softc *sc)
+{
+ int dma_desc_size, error;
+
+ /* Allocate the DMA descriptor memory. */
+ dma_desc_size = sizeof(struct jz4780_mmc_dma_desc) * JZ_MSC_DMA_SEGS;
+ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ dma_desc_size, 1, dma_desc_size, 0, NULL, NULL, &sc->sc_dma_tag);
+ if (error)
+ return (error);
+ error = bus_dmamem_alloc(sc->sc_dma_tag, &sc->sc_dma_desc,
+ BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->sc_dma_map);
+ if (error)
+ return (error);
+
+ error = bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map,
+ sc->sc_dma_desc, dma_desc_size, jz4780_mmc_dma_desc_cb, sc, 0);
+ if (error)
+ return (error);
+ if (sc->sc_dma_map_err)
+ return (sc->sc_dma_map_err);
+
+ /* Create the DMA map for data transfers. */
+ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
+ JZ_MSC_DMA_MAX_SIZE * JZ_MSC_DMA_SEGS, JZ_MSC_DMA_SEGS,
+ JZ_MSC_DMA_MAX_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL,
+ &sc->sc_dma_buf_tag);
+ if (error)
+ return (error);
+ error = bus_dmamap_create(sc->sc_dma_buf_tag, 0,
+ &sc->sc_dma_buf_map);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+static void
+jz4780_mmc_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
+{
+ struct jz4780_mmc_dma_desc *dma_desc;
+ struct jz4780_mmc_softc *sc;
+ uint32_t dma_desc_phys;
+ int i;
+
+ sc = (struct jz4780_mmc_softc *)arg;
+ sc->sc_dma_map_err = err;
+ dma_desc = sc->sc_dma_desc;
+ dma_desc_phys = sc->sc_dma_desc_phys;
+
+ /* Note nsegs is guaranteed to be zero if err is non-zero. */
+ for (i = 0; i < nsegs; i++) {
+ dma_desc[i].dma_phys = segs[i].ds_addr;
+ dma_desc[i].dma_len = segs[i].ds_len;
+ if (i < (nsegs - 1)) {
+ dma_desc_phys += sizeof(struct jz4780_mmc_dma_desc);
+ dma_desc[i].dma_next = dma_desc_phys;
+ dma_desc[i].dma_cmd = (i << 16) | JZ_DMA_LINK;
+ } else {
+ dma_desc[i].dma_next = 0;
+ dma_desc[i].dma_cmd = (i << 16) | JZ_DMA_ENDI;
+ }
+#ifdef JZ_MMC_DEBUG
+ device_printf(sc->sc_dev, "%d: desc %#x phys %#x len %d next %#x cmd %#x\n",
+ i, dma_desc_phys - sizeof(struct jz4780_mmc_dma_desc),
+ dma_desc[i].dma_phys, dma_desc[i].dma_len,
+ dma_desc[i].dma_next, dma_desc[i].dma_cmd);
+#endif
+ }
+}
+
+static int
+jz4780_mmc_prepare_dma(struct jz4780_mmc_softc *sc)
+{
+ bus_dmasync_op_t sync_op;
+ int error;
+ struct mmc_command *cmd;
+ uint32_t off;
+
+ cmd = sc->sc_req->cmd;
+ if (cmd->data->len > JZ_MSC_DMA_MAX_SIZE * JZ_MSC_DMA_SEGS)
+ return (EFBIG);
+ error = bus_dmamap_load(sc->sc_dma_buf_tag, sc->sc_dma_buf_map,
+ cmd->data->data, cmd->data->len, jz4780_mmc_dma_cb, sc,
+ BUS_DMA_NOWAIT);
+ if (error)
+ return (error);
+ if (sc->sc_dma_map_err)
+ return (sc->sc_dma_map_err);
+
+ sc->sc_dma_inuse = 1;
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ sync_op = BUS_DMASYNC_PREWRITE;
+ else
+ sync_op = BUS_DMASYNC_PREREAD;
+ bus_dmamap_sync(sc->sc_dma_buf_tag, sc->sc_dma_buf_map, sync_op);
+ bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, BUS_DMASYNC_PREWRITE);
+
+ /* Configure default DMA parameters */
+ sc->sc_dma_ctl = JZ_MODE_SEL | JZ_INCR_64 | JZ_DMAEN;
+
+ /* Enable unaligned buffer handling */
+ off = (uintptr_t)cmd->data->data & 3;
+ if (off != 0)
+ sc->sc_dma_ctl |= (off << JZ_AOFST_S) | JZ_ALIGNEN;
+ return (0);
+}
+
+static void
+jz4780_mmc_start_dma(struct jz4780_mmc_softc *sc)
+{
+
+ /* Set the address of the first descriptor */
+ JZ_MMC_WRITE_4(sc, JZ_MSC_DMANDA, sc->sc_dma_desc_phys);
+ /* Enable and start the dma engine */
+ JZ_MMC_WRITE_4(sc, JZ_MSC_DMAC, sc->sc_dma_ctl);
+}
+
+static int
+jz4780_mmc_reset(struct jz4780_mmc_softc *sc)
+{
+ int timeout;
+ int reg;
+
+ /* Stop the clock */
+ reg = JZ_MMC_READ_4(sc, JZ_MSC_CTRL);
+ reg &= ~(JZ_CLOCK_CTRL_M);
+ reg |= JZ_CLOCK_STOP;
+ JZ_MMC_WRITE_4(sc, JZ_MSC_CTRL, reg);
+
+ timeout = 1000;
+ while (--timeout > 0) {
+ if ((JZ_MMC_READ_4(sc, JZ_MSC_STAT) & JZ_CLK_EN) == 0)
+ break;
+ DELAY(100);
+ }
+ if (timeout == 0) {
+ device_printf(sc->sc_dev, "Failed to stop clk.\n");
+ return (ETIMEDOUT);
+ }
+
+ /* Reset */
+ reg = JZ_MMC_READ_4(sc, JZ_MSC_CTRL);
+ reg |= JZ_RESET;
+ JZ_MMC_WRITE_4(sc, JZ_MSC_CTRL, reg);
+
+ timeout = 10;
+ while (--timeout > 0) {
+ if ((JZ_MMC_READ_4(sc, JZ_MSC_STAT) & JZ_IS_RESETTING) == 0)
+ break;
+ DELAY(1000);
+ }
+
+ if (timeout == 0) {
+ /*
+ * X1000 never clears reseting bit.
+ * Ignore for now.
+ */
+ }
+
+ /* Set the timeouts. */
+ JZ_MMC_WRITE_4(sc, JZ_MSC_RESTO, 0xffff);
+ JZ_MMC_WRITE_4(sc, JZ_MSC_RDTO, 0xffffffff);
+
+ /* Mask all interrupt initially */
+ JZ_MMC_WRITE_4(sc, JZ_MSC_IMASK, 0xffffffff);
+ /* Clear pending interrupts. */
+ JZ_MMC_WRITE_4(sc, JZ_MSC_IFLG, 0xffffffff);
+
+ /* Remember interrupts we always want */
+ sc->sc_intr_mask = JZ_MSC_INT_ERR_BITS;
+
+ return (0);
+}
+
+static void
+jz4780_mmc_req_done(struct jz4780_mmc_softc *sc)
+{
+ struct mmc_command *cmd;
+ struct mmc_request *req;
+ bus_dmasync_op_t sync_op;
+
+ cmd = sc->sc_req->cmd;
+ /* Reset the controller in case of errors */
+ if (cmd->error != MMC_ERR_NONE)
+ jz4780_mmc_reset(sc);
+ /* Unmap DMA if necessary */
+ if (sc->sc_dma_inuse == 1) {
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ sync_op = BUS_DMASYNC_POSTWRITE;
+ else
+ sync_op = BUS_DMASYNC_POSTREAD;
+ bus_dmamap_sync(sc->sc_dma_buf_tag, sc->sc_dma_buf_map,
+ sync_op);
+ bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dma_buf_tag, sc->sc_dma_buf_map);
+ }
+ req = sc->sc_req;
+ callout_stop(&sc->sc_timeoutc);
+ sc->sc_req = NULL;
+ sc->sc_resid = 0;
+ sc->sc_dma_inuse = 0;
+ sc->sc_dma_map_err = 0;
+ sc->sc_intr_wait = 0;
+ sc->sc_intr_seen = 0;
+ req->done(req);
+}
+
+static void
+jz4780_mmc_read_response(struct jz4780_mmc_softc *sc)
+{
+ struct mmc_command *cmd;
+ int i;
+
+ cmd = sc->sc_req->cmd;
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ uint16_t val;
+
+ val = JZ_MMC_READ_2(sc, JZ_MSC_RES);
+ for (i = 0; i < 4; i++) {
+ cmd->resp[i] = val << 24;
+ val = JZ_MMC_READ_2(sc, JZ_MSC_RES);
+ cmd->resp[i] |= val << 8;
+ val = JZ_MMC_READ_2(sc, JZ_MSC_RES);
+ cmd->resp[i] |= val >> 8;
+ }
+ } else {
+ cmd->resp[0] = JZ_MMC_READ_2(sc, JZ_MSC_RES) << 24;
+ cmd->resp[0] |= JZ_MMC_READ_2(sc, JZ_MSC_RES) << 8;
+ cmd->resp[0] |= JZ_MMC_READ_2(sc, JZ_MSC_RES) & 0xff;
+ }
+ }
+}
+
+static void
+jz4780_mmc_req_ok(struct jz4780_mmc_softc *sc)
+{
+ struct mmc_command *cmd;
+
+ cmd = sc->sc_req->cmd;
+ /* All data has been transferred ? */
+ if (cmd->data != NULL && (sc->sc_resid << 2) < cmd->data->len)
+ cmd->error = MMC_ERR_FAILED;
+ jz4780_mmc_req_done(sc);
+}
+
+static void
+jz4780_mmc_timeout(void *arg)
+{
+ struct jz4780_mmc_softc *sc;
+
+ sc = (struct jz4780_mmc_softc *)arg;
+ if (sc->sc_req != NULL) {
+ device_printf(sc->sc_dev, "controller timeout, rint %#x stat %#x\n",
+ JZ_MMC_READ_4(sc, JZ_MSC_IFLG), JZ_MMC_READ_4(sc, JZ_MSC_STAT));
+ sc->sc_req->cmd->error = MMC_ERR_TIMEOUT;
+ jz4780_mmc_req_done(sc);
+ } else
+ device_printf(sc->sc_dev,
+ "Spurious timeout - no active request\n");
+}
+
+static int
+jz4780_mmc_pio_transfer(struct jz4780_mmc_softc *sc, struct mmc_data *data)
+{
+ uint32_t mask, *buf;
+ int i, write;
+
+ buf = (uint32_t *)data->data;
+ write = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
+ mask = write ? JZ_DATA_FIFO_FULL : JZ_DATA_FIFO_EMPTY;
+ for (i = sc->sc_resid; i < (data->len >> 2); i++) {
+ if ((JZ_MMC_READ_4(sc, JZ_MSC_STAT) & mask))
+ return (1);
+ if (write)
+ JZ_MMC_WRITE_4(sc, JZ_MSC_TXFIFO, buf[i]);
+ else
+ buf[i] = JZ_MMC_READ_4(sc, JZ_MSC_RXFIFO);
+ sc->sc_resid = i + 1;
+ }
+
+ /* Done with pio transfer, shut FIFO interrupts down */
+ mask = JZ_MMC_READ_4(sc, JZ_MSC_IMASK);
+ mask |= (JZ_INT_TXFIFO_WR_REQ | JZ_INT_RXFIFO_RD_REQ);
+ JZ_MMC_WRITE_4(sc, JZ_MSC_IMASK, mask);
+ return (0);
+}
+
+static void
+jz4780_mmc_intr(void *arg)
+{
+ struct jz4780_mmc_softc *sc;
+ struct mmc_data *data;
+ uint32_t rint;
+
+ sc = (struct jz4780_mmc_softc *)arg;
+ JZ_MMC_LOCK(sc);
+ rint = JZ_MMC_READ_4(sc, JZ_MSC_IFLG);
+#if defined(JZ_MMC_DEBUG)
+ device_printf(sc->sc_dev, "rint: %#x, stat: %#x\n",
+ rint, JZ_MMC_READ_4(sc, JZ_MSC_STAT));
+ if (sc->sc_dma_inuse == 1 && (sc->sc_intr_seen & JZ_INT_DMAEND) == 0)
+ device_printf(sc->sc_dev, "\tdmada %#x dmanext %#x dmac %#x"
+ " dmalen %d dmacmd %#x\n",
+ JZ_MMC_READ_4(sc, JZ_MSC_DMADA),
+ JZ_MMC_READ_4(sc, JZ_MSC_DMANDA),
+ JZ_MMC_READ_4(sc, JZ_MSC_DMAC),
+ JZ_MMC_READ_4(sc, JZ_MSC_DMALEN),
+ JZ_MMC_READ_4(sc, JZ_MSC_DMACMD));
+#endif
+ if (sc->sc_req == NULL) {
+ device_printf(sc->sc_dev,
+ "Spurious interrupt - no active request, rint: 0x%08X\n",
+ rint);
+ goto end;
+ }
+ if (rint & JZ_MSC_INT_ERR_BITS) {
+#if defined(JZ_MMC_DEBUG)
+ device_printf(sc->sc_dev, "controller error, rint %#x stat %#x\n",
+ rint, JZ_MMC_READ_4(sc, JZ_MSC_STAT));
+#endif
+ if (rint & (JZ_INT_TIMEOUT_RES | JZ_INT_TIMEOUT_READ))
+ sc->sc_req->cmd->error = MMC_ERR_TIMEOUT;
+ else
+ sc->sc_req->cmd->error = MMC_ERR_FAILED;
+ jz4780_mmc_req_done(sc);
+ goto end;
+ }
+ data = sc->sc_req->cmd->data;
+ /* Check for command response */
+ if (rint & JZ_INT_END_CMD_RES) {
+ jz4780_mmc_read_response(sc);
+ if (sc->sc_dma_inuse == 1)
+ jz4780_mmc_start_dma(sc);
+ }
+ if (data != NULL) {
+ if (sc->sc_dma_inuse == 1 && (rint & JZ_INT_DMAEND))
+ sc->sc_resid = data->len >> 2;
+ else if (sc->sc_dma_inuse == 0 &&
+ (rint & (JZ_INT_TXFIFO_WR_REQ | JZ_INT_RXFIFO_RD_REQ)))
+ jz4780_mmc_pio_transfer(sc, data);
+ }
+ sc->sc_intr_seen |= rint;
+ if ((sc->sc_intr_seen & sc->sc_intr_wait) == sc->sc_intr_wait)
+ jz4780_mmc_req_ok(sc);
+end:
+ JZ_MMC_WRITE_4(sc, JZ_MSC_IFLG, rint);
+ JZ_MMC_UNLOCK(sc);
+}
+
+static int
+jz4780_mmc_request(device_t bus, device_t child, struct mmc_request *req)
+{
+ struct jz4780_mmc_softc *sc;
+ struct mmc_command *cmd;
+ uint32_t cmdat, ctrl, iwait;
+ int blksz;
+
+ sc = device_get_softc(bus);
+ JZ_MMC_LOCK(sc);
+ if (sc->sc_req != NULL) {
+ JZ_MMC_UNLOCK(sc);
+ return (EBUSY);
+ }
+ /* Start with template value */
+ cmdat = sc->sc_cmdat;
+ iwait = JZ_INT_END_CMD_RES;
+
+ /* Configure response format */
+ cmd = req->cmd;
+ switch (MMC_RSP(cmd->flags)) {
+ case MMC_RSP_R1:
+ case MMC_RSP_R1B:
+ cmdat |= JZ_RES_R1;
+ break;
+ case MMC_RSP_R2:
+ cmdat |= JZ_RES_R2;
+ break;
+ case MMC_RSP_R3:
+ cmdat |= JZ_RES_R3;
+ break;
+ };
+ if (cmd->opcode == MMC_GO_IDLE_STATE)
+ cmdat |= JZ_INIT;
+ if (cmd->flags & MMC_RSP_BUSY) {
+ cmdat |= JZ_BUSY;
+ iwait |= JZ_INT_PRG_DONE;
+ }
+
+ sc->sc_req = req;
+ sc->sc_resid = 0;
+ cmd->error = MMC_ERR_NONE;
+
+ if (cmd->data != NULL) {
+ cmdat |= JZ_DATA_EN;
+ if (cmd->data->flags & MMC_DATA_MULTI) {
+ cmdat |= JZ_AUTO_CMD12;
+ iwait |= JZ_INT_AUTO_CMD12_DONE;
+ }
+ if (cmd->data->flags & MMC_DATA_WRITE) {
+ cmdat |= JZ_WRITE;
+ iwait |= JZ_INT_PRG_DONE;
+ }
+ if (cmd->data->flags & MMC_DATA_STREAM)
+ cmdat |= JZ_STREAM;
+ else
+ iwait |= JZ_INT_DATA_TRAN_DONE;
+
+ blksz = min(cmd->data->len, MMC_SECTOR_SIZE);
+ JZ_MMC_WRITE_4(sc, JZ_MSC_BLKLEN, blksz);
+ JZ_MMC_WRITE_4(sc, JZ_MSC_NOB, cmd->data->len / blksz);
+
+ /* Attempt to setup DMA for this transaction */
+ if (jz4780_mmc_pio_mode == 0)
+ jz4780_mmc_prepare_dma(sc);
+ if (sc->sc_dma_inuse != 0) {
+ /* Wait for DMA completion interrupt */
+ iwait |= JZ_INT_DMAEND;
+ } else {
+ iwait |= (cmd->data->flags & MMC_DATA_WRITE) ?
+ JZ_INT_TXFIFO_WR_REQ : JZ_INT_RXFIFO_RD_REQ;
+ JZ_MMC_WRITE_4(sc, JZ_MSC_DMAC, 0);
+ }
+ }
+
+ sc->sc_intr_seen = 0;
+ sc->sc_intr_wait = iwait;
+ JZ_MMC_WRITE_4(sc, JZ_MSC_IMASK, ~(sc->sc_intr_mask | iwait));
+
+#if defined(JZ_MMC_DEBUG)
+ device_printf(sc->sc_dev,
+ "REQUEST: CMD%u arg %#x flags %#x cmdat %#x sc_intr_wait = %#x\n",
+ cmd->opcode, cmd->arg, cmd->flags, cmdat, sc->sc_intr_wait);
+#endif
+
+ JZ_MMC_WRITE_4(sc, JZ_MSC_ARG, cmd->arg);
+ JZ_MMC_WRITE_4(sc, JZ_MSC_CMD, cmd->opcode);
+ JZ_MMC_WRITE_4(sc, JZ_MSC_CMDAT, cmdat);
+
+ ctrl = JZ_MMC_READ_4(sc, JZ_MSC_CTRL);
+ ctrl |= JZ_START_OP | JZ_CLOCK_START;
+ JZ_MMC_WRITE_4(sc, JZ_MSC_CTRL, ctrl);
+
+ callout_reset(&sc->sc_timeoutc, sc->sc_timeout * hz,
+ jz4780_mmc_timeout, sc);
+ JZ_MMC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+jz4780_mmc_read_ivar(device_t bus, device_t child, int which,
+ uintptr_t *result)
+{
+ struct jz4780_mmc_softc *sc;
+
+ sc = device_get_softc(bus);
+ switch (which) {
+ default:
+ return (EINVAL);
+ case MMCBR_IVAR_BUS_MODE:
+ *(int *)result = sc->sc_host.ios.bus_mode;
+ break;
+ case MMCBR_IVAR_BUS_WIDTH:
+ *(int *)result = sc->sc_host.ios.bus_width;
+ break;
+ case MMCBR_IVAR_CHIP_SELECT:
+ *(int *)result = sc->sc_host.ios.chip_select;
+ break;
+ case MMCBR_IVAR_CLOCK:
+ *(int *)result = sc->sc_host.ios.clock;
+ break;
+ case MMCBR_IVAR_F_MIN:
+ *(int *)result = sc->sc_host.f_min;
+ break;
+ case MMCBR_IVAR_F_MAX:
+ *(int *)result = sc->sc_host.f_max;
+ break;
+ case MMCBR_IVAR_HOST_OCR:
+ *(int *)result = sc->sc_host.host_ocr;
+ break;
+ case MMCBR_IVAR_MODE:
+ *(int *)result = sc->sc_host.mode;
+ break;
+ case MMCBR_IVAR_OCR:
+ *(int *)result = sc->sc_host.ocr;
+ break;
+ case MMCBR_IVAR_POWER_MODE:
+ *(int *)result = sc->sc_host.ios.power_mode;
+ break;
+ case MMCBR_IVAR_VDD:
+ *(int *)result = sc->sc_host.ios.vdd;
+ break;
+ case MMCBR_IVAR_CAPS:
+ *(int *)result = sc->sc_host.caps;
+ break;
+ case MMCBR_IVAR_MAX_DATA:
+ *(int *)result = 65535;
+ break;
+ case MMCBR_IVAR_TIMING:
+ *(int *)result = sc->sc_host.ios.timing;
+ break;
+ }
+
+ return (0);
+}
+
+static int
+jz4780_mmc_write_ivar(device_t bus, device_t child, int which,
+ uintptr_t value)
+{
+ struct jz4780_mmc_softc *sc;
+
+ sc = device_get_softc(bus);
+ switch (which) {
+ default:
+ return (EINVAL);
+ case MMCBR_IVAR_BUS_MODE:
+ sc->sc_host.ios.bus_mode = value;
+ break;
+ case MMCBR_IVAR_BUS_WIDTH:
+ sc->sc_host.ios.bus_width = value;
+ break;
+ case MMCBR_IVAR_CHIP_SELECT:
+ sc->sc_host.ios.chip_select = value;
+ break;
+ case MMCBR_IVAR_CLOCK:
+ sc->sc_host.ios.clock = value;
+ break;
+ case MMCBR_IVAR_MODE:
+ sc->sc_host.mode = value;
+ break;
+ case MMCBR_IVAR_OCR:
+ sc->sc_host.ocr = value;
+ break;
+ case MMCBR_IVAR_POWER_MODE:
+ sc->sc_host.ios.power_mode = value;
+ break;
+ case MMCBR_IVAR_VDD:
+ sc->sc_host.ios.vdd = value;
+ break;
+ case MMCBR_IVAR_TIMING:
+ sc->sc_host.ios.timing = value;
+ break;
+ /* These are read-only */
+ case MMCBR_IVAR_CAPS:
+ case MMCBR_IVAR_HOST_OCR:
+ case MMCBR_IVAR_F_MIN:
+ case MMCBR_IVAR_F_MAX:
+ case MMCBR_IVAR_MAX_DATA:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+static int
+jz4780_mmc_disable_clock(struct jz4780_mmc_softc *sc)
+{
+ int timeout;
+
+ JZ_MMC_WRITE_4(sc, JZ_MSC_CTRL,
+ JZ_MMC_READ_4(sc, JZ_MSC_CTRL) | JZ_CLOCK_STOP);
+
+ for (timeout = 1000; timeout > 0; timeout--)
+ if ((JZ_MMC_READ_4(sc, JZ_MSC_STAT) & JZ_CLK_EN) == 0)
+ return (0);
+ return (ETIMEDOUT);
+}
+
+static int
+jz4780_mmc_config_clock(struct jz4780_mmc_softc *sc, uint32_t freq)
+{
+ uint64_t rate;
+ uint32_t clk_freq;
+ int err, div;
+
+ err = jz4780_mmc_disable_clock(sc);
+ if (err != 0)
+ return (err);
+
+ clk_get_freq(sc->sc_clk, &rate);
+ clk_freq = (uint32_t)rate;
+
+ div = 0;
+ while (clk_freq > freq) {
+ div++;
+ clk_freq >>= 1;
+ }
+ if (div >= 7)
+ div = 7;
+#if defined(JZ_MMC_DEBUG)
+ if (div != JZ_MMC_READ_4(sc, JZ_MSC_CLKRT))
+ device_printf(sc->sc_dev,
+ "UPDATE_IOS: clk -> %u\n", clk_freq);
+#endif
+ JZ_MMC_WRITE_4(sc, JZ_MSC_CLKRT, div);
+ return (0);
+}
+
+static int
+jz4780_mmc_update_ios(device_t bus, device_t child)
+{
+ struct jz4780_mmc_softc *sc;
+ struct mmc_ios *ios;
+ int error;
+
+ sc = device_get_softc(bus);
+ ios = &sc->sc_host.ios;
+ if (ios->clock) {
+ /* Set the MMC clock. */
+ error = jz4780_mmc_config_clock(sc, ios->clock);
+ if (error != 0)
+ return (error);
+ }
+
+ /* Set the bus width. */
+ switch (ios->bus_width) {
+ case bus_width_1:
+ sc->sc_cmdat &= ~(JZ_BUS_WIDTH_M);
+ sc->sc_cmdat |= JZ_BUS_1BIT;
+ break;
+ case bus_width_4:
+ sc->sc_cmdat &= ~(JZ_BUS_WIDTH_M);
+ sc->sc_cmdat |= JZ_BUS_4BIT;
+ break;
+ case bus_width_8:
+ sc->sc_cmdat &= ~(JZ_BUS_WIDTH_M);
+ sc->sc_cmdat |= JZ_BUS_8BIT;
+ break;
+ }
+ return (0);
+}
+
+static int
+jz4780_mmc_get_ro(device_t bus, device_t child)
+{
+
+ return (0);
+}
+
+static int
+jz4780_mmc_acquire_host(device_t bus, device_t child)
+{
+ struct jz4780_mmc_softc *sc;
+ int error;
+
+ sc = device_get_softc(bus);
+ JZ_MMC_LOCK(sc);
+ while (sc->sc_bus_busy) {
+ error = msleep(sc, &sc->sc_mtx, PCATCH, "mmchw", 0);
+ if (error != 0) {
+ JZ_MMC_UNLOCK(sc);
+ return (error);
+ }
+ }
+ sc->sc_bus_busy++;
+ JZ_MMC_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+jz4780_mmc_release_host(device_t bus, device_t child)
+{
+ struct jz4780_mmc_softc *sc;
+
+ sc = device_get_softc(bus);
+ JZ_MMC_LOCK(sc);
+ sc->sc_bus_busy--;
+ wakeup(sc);
+ JZ_MMC_UNLOCK(sc);
+
+ return (0);
+}
+
+static device_method_t jz4780_mmc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_mmc_probe),
+ DEVMETHOD(device_attach, jz4780_mmc_attach),
+ DEVMETHOD(device_detach, jz4780_mmc_detach),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, jz4780_mmc_read_ivar),
+ DEVMETHOD(bus_write_ivar, jz4780_mmc_write_ivar),
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ /* MMC bridge interface */
+ DEVMETHOD(mmcbr_update_ios, jz4780_mmc_update_ios),
+ DEVMETHOD(mmcbr_request, jz4780_mmc_request),
+ DEVMETHOD(mmcbr_get_ro, jz4780_mmc_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, jz4780_mmc_acquire_host),
+ DEVMETHOD(mmcbr_release_host, jz4780_mmc_release_host),
+
+ DEVMETHOD_END
+};
+
+static devclass_t jz4780_mmc_devclass;
+
+static driver_t jz4780_mmc_driver = {
+ "jzmmc",
+ jz4780_mmc_methods,
+ sizeof(struct jz4780_mmc_softc),
+};
+
+DRIVER_MODULE(jzmmc, simplebus, jz4780_mmc_driver, jz4780_mmc_devclass, 0, 0);
+DRIVER_MODULE(mmc, jzmmc, mmc_driver, mmc_devclass, NULL, NULL);
+MODULE_DEPEND(jzmmc, mmc, 1, 1, 1);
diff --git a/sys/mips/ingenic/jz4780_mp.c b/sys/mips/ingenic/jz4780_mp.c
new file mode 100644
index 000000000000..2f6c8f31fe4f
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_mp.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * Copyright (c) 2004-2010 Juli Mallett <jmallett@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/smp.h>
+#include <sys/systm.h>
+
+#include <machine/cpufunc.h>
+#include <machine/hwfunc.h>
+#include <machine/md_var.h>
+#include <machine/smp.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+#include <mips/ingenic/jz4780_cpuregs.h>
+
+void jz4780_mpentry(void);
+
+#define JZ4780_MAXCPU 2
+
+void
+platform_ipi_send(int cpuid)
+{
+
+ if (cpuid == 0)
+ mips_wr_xburst_mbox0(1);
+ else
+ mips_wr_xburst_mbox1(1);
+}
+
+void
+platform_ipi_clear(void)
+{
+ int cpuid = PCPU_GET(cpuid);
+ uint32_t action;
+
+ action = (cpuid == 0) ? mips_rd_xburst_mbox0() : mips_rd_xburst_mbox1();
+ KASSERT(action == 1, ("CPU %d: unexpected IPIs: %#x", cpuid, action));
+ mips_wr_xburst_core_sts(~(JZ_CORESTS_MIRQ0P << cpuid));
+}
+
+int
+platform_processor_id(void)
+{
+
+ return (mips_rd_ebase() & 7);
+}
+
+int
+platform_ipi_hardintr_num(void)
+{
+
+ return (1);
+}
+
+int
+platform_ipi_softintr_num(void)
+{
+
+ return (-1);
+}
+
+void
+platform_init_ap(int cpuid)
+{
+ unsigned reg;
+
+ /*
+ * Clear any pending IPIs.
+ */
+ mips_wr_xburst_core_sts(~(JZ_CORESTS_MIRQ0P << cpuid));
+
+ /* Allow IPI mbox for this core */
+ reg = mips_rd_xburst_reim();
+ reg |= (JZ_REIM_MIRQ0M << cpuid);
+ mips_wr_xburst_reim(reg);
+
+ /*
+ * Unmask the ipi interrupts.
+ */
+ reg = hard_int_mask(platform_ipi_hardintr_num());
+ set_intr_mask(reg);
+}
+
+void
+platform_cpu_mask(cpuset_t *mask)
+{
+ uint32_t i, m;
+
+ CPU_ZERO(mask);
+ for (i = 0, m = 1 ; i < JZ4780_MAXCPU; i++, m <<= 1)
+ CPU_SET(i, mask);
+}
+
+struct cpu_group *
+platform_smp_topo(void)
+{
+ return (smp_topo_none());
+}
+
+static void
+jz4780_core_powerup(void)
+{
+ uint32_t reg;
+
+ reg = readreg(JZ_CGU_BASE + JZ_LPCR);
+ reg &= ~LPCR_PD_SCPU;
+ writereg(JZ_CGU_BASE + JZ_LPCR, reg);
+ do {
+ reg = readreg(JZ_CGU_BASE + JZ_LPCR);
+ } while ((reg & LPCR_SCPUS) != 0);
+}
+
+/*
+ * Spin up the second code. The code is roughly modeled after
+ * similar routine in Linux.
+ */
+int
+platform_start_ap(int cpuid)
+{
+ uint32_t reg, addr;
+
+ if (cpuid >= JZ4780_MAXCPU)
+ return (EINVAL);
+
+ /* Figure out address of mpentry in KSEG1 */
+ addr = MIPS_PHYS_TO_KSEG1(MIPS_KSEG0_TO_PHYS(jz4780_mpentry));
+ KASSERT((addr & ~JZ_REIM_ENTRY_MASK) == 0,
+ ("Unaligned mpentry"));
+
+ /* Configure core alternative entry point */
+ reg = mips_rd_xburst_reim();
+ reg &= ~JZ_REIM_ENTRY_MASK;
+ reg |= addr & JZ_REIM_ENTRY_MASK;
+
+ /* Allow this core to get IPIs from one being started */
+ reg |= JZ_REIM_MIRQ0M;
+ mips_wr_xburst_reim(reg);
+
+ /* Force core into reset and enable use of alternate entry point */
+ reg = mips_rd_xburst_core_ctl();
+ reg |= (JZ_CORECTL_SWRST0 << cpuid) | (JZ_CORECTL_RPC0 << cpuid);
+ mips_wr_xburst_core_ctl(reg);
+
+ /* Power the core up */
+ jz4780_core_powerup();
+
+ /* Take the core out of reset */
+ reg &= ~(JZ_CORECTL_SWRST0 << cpuid);
+ mips_wr_xburst_core_ctl(reg);
+
+ return (0);
+}
diff --git a/sys/mips/ingenic/jz4780_mpboot.S b/sys/mips/ingenic/jz4780_mpboot.S
new file mode 100644
index 000000000000..2c1ed5e081a2
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_mpboot.S
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2015 Alexander Kabaev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+#include <machine/cpu.h>
+#include <machine/cpuregs.h>
+#include <machine/cache_r4k.h>
+
+#include "assym.s"
+
+#define CACHE_SIZE (32 * 1024)
+#define CACHE_LINESIZE 32
+
+ .text
+ .set noat
+ .set noreorder
+ .section .text.mpentry_jz4780
+ .balign 0x10000
+
+GLOBAL(jz4780_mpentry)
+
+ /* Initialize caches */
+ li t0, MIPS_KSEG0_START
+ ori t1, t0, CACHE_SIZE
+ mtc0 zero, MIPS_COP_0_TAG_LO
+ COP0_SYNC
+1: cache CACHEOP_R4K_INDEX_STORE_TAG | CACHE_R4K_I, 0(t0)
+ cache CACHEOP_R4K_INDEX_STORE_TAG | CACHE_R4K_D, 0(t0)
+ bne t0, t1, 1b
+ addiu t0, t0, CACHE_LINESIZE
+
+ /* Set TLB page mask */
+ mtc0 zero, MIPS_COP_0_TLB_PG_MASK
+ COP0_SYNC
+
+ j mpentry
+ nop
diff --git a/sys/mips/ingenic/jz4780_nand.c b/sys/mips/ingenic/jz4780_nand.c
new file mode 100644
index 000000000000..4a658dbc044a
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_nand.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+struct jz4780_nand_softc {
+ device_t dev;
+ struct resource *res[1];
+};
+
+static struct resource_spec jz4780_nand_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int jz4780_nand_probe(device_t dev);
+static int jz4780_nand_attach(device_t dev);
+static int jz4780_nand_detach(device_t dev);
+
+static int
+jz4780_nand_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-nand"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Ingenic JZ4780 NAND Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_nand_attach(device_t dev)
+{
+ struct jz4780_nand_softc *sc = device_get_softc(dev);
+
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, jz4780_nand_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+jz4780_nand_detach(device_t dev)
+{
+ struct jz4780_nand_softc *sc = device_get_softc(dev);
+
+ bus_release_resources(dev, jz4780_nand_spec, sc->res);
+ return (0);
+}
+
+static device_method_t jz4780_nand_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_nand_probe),
+ DEVMETHOD(device_attach, jz4780_nand_attach),
+ DEVMETHOD(device_detach, jz4780_nand_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t jz4780_nand_driver = {
+ "nand",
+ jz4780_nand_methods,
+ sizeof(struct jz4780_nand_softc),
+};
+
+static devclass_t jz4780_nand_devclass;
+
+DRIVER_MODULE(jz4780_nand, simplebus, jz4780_nand_driver,
+ jz4780_nand_devclass, 0, 0);
diff --git a/sys/mips/ingenic/jz4780_nemc.c b/sys/mips/ingenic/jz4780_nemc.c
new file mode 100644
index 000000000000..5c0b11515765
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_nemc.c
@@ -0,0 +1,373 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+
+struct jz4780_nemc_devinfo {
+ struct simplebus_devinfo sinfo;
+ uint32_t bank;
+};
+
+struct jz4780_nemc_softc {
+ struct simplebus_softc simplebus_sc;
+ device_t dev;
+ struct resource *res[1];
+ uint32_t banks;
+ uint32_t clock_tick_psecs;
+ clk_t clk;
+};
+
+static struct resource_spec jz4780_nemc_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
+#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
+
+static int jz4780_nemc_probe(device_t dev);
+static int jz4780_nemc_attach(device_t dev);
+static int jz4780_nemc_detach(device_t dev);
+
+static int
+jz4780_nemc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-nemc"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Ingenic JZ4780 NEMC");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+#define JZ4780_NEMC_NS_TO_TICKS(sc, val) howmany((val) * 1000, (sc)->clock_tick_psecs)
+
+/* Use table from JZ4780 programmers manual to convert ticks to tBP/tAW register values */
+static const uint8_t ticks_to_tBP_tAW[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 1:1 mapping */
+ 11, 11, /* 12 cycles */
+ 12, 12, 12, /* 15 cycles */
+ 13, 13, 13, 13, 13, /* 20 cycles */
+ 14, 14, 14, 14, 14, /* 25 cycles */
+ 15, 15, 15, 15, 15, 15 /* 31 cycles */
+};
+
+static int
+jz4780_nemc_configure_bank(struct jz4780_nemc_softc *sc,
+ device_t dev, u_int bank)
+{
+ uint32_t smcr, cycles;
+ phandle_t node;
+ pcell_t val;
+
+ /* Check if bank is configured already */
+ if (sc->banks & (1 << bank))
+ return 0;
+
+ smcr = CSR_READ_4(sc, JZ_NEMC_SMCR(bank));
+
+ smcr &= ~JZ_NEMC_SMCR_SMT_MASK;
+ smcr |= JZ_NEMC_SMCR_SMT_NORMAL << JZ_NEMC_SMCR_SMT_SHIFT;
+
+ node = ofw_bus_get_node(dev);
+ if (OF_getencprop(node, "ingenic,nemc-tAS", &val, sizeof(val)) > 0) {
+ cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
+ if (cycles > 15) {
+ device_printf(sc->dev,
+ "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
+ "ingenic,nemc-tAS", val, cycles, 15);
+ return -1;
+ }
+ smcr &= ~JZ_NEMC_SMCR_TAS_MASK;
+ smcr |= cycles << JZ_NEMC_SMCR_TAS_SHIFT;
+ }
+
+ if (OF_getencprop(node, "ingenic,nemc-tAH", &val, sizeof(val)) > 0) {
+ cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
+ if (cycles > 15) {
+ device_printf(sc->dev,
+ "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
+ "ingenic,nemc-tAH", val, cycles, 15);
+ return -1;
+ }
+ smcr &= ~JZ_NEMC_SMCR_TAH_MASK;
+ smcr |= cycles << JZ_NEMC_SMCR_TAH_SHIFT;
+ }
+
+ if (OF_getencprop(node, "ingenic,nemc-tBP", &val, sizeof(val)) > 0) {
+ cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
+ if (cycles > 31) {
+ device_printf(sc->dev,
+ "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
+ "ingenic,nemc-tBP", val, cycles, 15);
+ return -1;
+ }
+ smcr &= ~JZ_NEMC_SMCR_TBP_MASK;
+ smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TBP_SHIFT;
+ }
+
+ if (OF_getencprop(node, "ingenic,nemc-tAW", &val, sizeof(val)) > 0) {
+ cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
+ if (cycles > 31) {
+ device_printf(sc->dev,
+ "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
+ "ingenic,nemc-tAW", val, cycles, 15);
+ return -1;
+ }
+ smcr &= ~JZ_NEMC_SMCR_TAW_MASK;
+ smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TAW_SHIFT;
+ }
+
+ if (OF_getencprop(node, "ingenic,nemc-tSTRV", &val, sizeof(val)) > 0) {
+ cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
+ if (cycles > 63) {
+ device_printf(sc->dev,
+ "invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
+ "ingenic,nemc-tSTRV", val, cycles, 15);
+ return -1;
+ }
+ smcr &= ~JZ_NEMC_SMCR_STRV_MASK;
+ smcr |= cycles << JZ_NEMC_SMCR_STRV_SHIFT;
+ }
+ CSR_WRITE_4(sc, JZ_NEMC_SMCR(bank), smcr);
+ sc->banks |= (1 << bank);
+ return 0;
+}
+
+/* Wholesale copy of simplebus routine */
+static int
+jz4780_nemc_fill_ranges(phandle_t node, struct simplebus_softc *sc)
+{
+ int host_address_cells;
+ cell_t *base_ranges;
+ ssize_t nbase_ranges;
+ int err;
+ int i, j, k;
+
+ err = OF_searchencprop(OF_parent(node), "#address-cells",
+ &host_address_cells, sizeof(host_address_cells));
+ if (err <= 0)
+ return (-1);
+
+ nbase_ranges = OF_getproplen(node, "ranges");
+ if (nbase_ranges < 0)
+ return (-1);
+ sc->nranges = nbase_ranges / sizeof(cell_t) /
+ (sc->acells + host_address_cells + sc->scells);
+ if (sc->nranges == 0)
+ return (0);
+
+ sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
+ M_DEVBUF, M_WAITOK);
+ base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
+ OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
+
+ for (i = 0, j = 0; i < sc->nranges; i++) {
+ sc->ranges[i].bus = 0;
+ for (k = 0; k < sc->acells; k++) {
+ sc->ranges[i].bus <<= 32;
+ sc->ranges[i].bus |= base_ranges[j++];
+ }
+ sc->ranges[i].host = 0;
+ for (k = 0; k < host_address_cells; k++) {
+ sc->ranges[i].host <<= 32;
+ sc->ranges[i].host |= base_ranges[j++];
+ }
+ sc->ranges[i].size = 0;
+ for (k = 0; k < sc->scells; k++) {
+ sc->ranges[i].size <<= 32;
+ sc->ranges[i].size |= base_ranges[j++];
+ }
+ }
+
+ free(base_ranges, M_DEVBUF);
+ return (sc->nranges);
+}
+
+static int
+jz4780_nemc_attach(device_t dev)
+{
+ struct jz4780_nemc_softc *sc = device_get_softc(dev);
+ phandle_t node;
+ uint64_t freq;
+
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, jz4780_nemc_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ node = ofw_bus_get_node(dev);
+
+ /* Initialize simplebus and enumerate resources */
+ simplebus_init(dev, node);
+
+ if (jz4780_nemc_fill_ranges(node, &sc->simplebus_sc) < 0)
+ goto error;
+
+ /* Figure our underlying clock rate. */
+ if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) {
+ device_printf(dev, "could not lookup device clock\n");
+ goto error;
+ }
+ if (clk_enable(sc->clk) != 0) {
+ device_printf(dev, "could not enable device clock\n");
+ goto error;
+ }
+ if (clk_get_freq(sc->clk, &freq) != 0) {
+ device_printf(dev, "could not determine clock speed\n");
+ goto error;
+ }
+
+ /* Convert clock frequency to picoseconds-per-tick value. */
+ sc->clock_tick_psecs = (uint32_t)(1000000000000ULL / freq);
+
+ /*
+ * Allow devices to identify.
+ */
+ bus_generic_probe(dev);
+
+ /*
+ * Now walk the tree and attach top level devices
+ */
+ for (node = OF_child(node); node > 0; node = OF_peer(node))
+ simplebus_add_device(dev, node, 0, NULL, -1, NULL);
+
+ return (bus_generic_attach(dev));
+error:
+ jz4780_nemc_detach(dev);
+ return (ENXIO);
+}
+
+static int
+jz4780_nemc_detach(device_t dev)
+{
+ struct jz4780_nemc_softc *sc = device_get_softc(dev);
+
+ bus_generic_detach(dev);
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+ bus_release_resources(dev, jz4780_nemc_spec, sc->res);
+ return (0);
+}
+
+static int
+jz4780_nemc_decode_bank(struct simplebus_softc *sc, struct resource *r,
+ u_int *bank)
+{
+ rman_res_t start, end;
+ int i;
+
+ start = rman_get_start(r);
+ end = rman_get_end(r);
+
+ /* Remap through ranges property */
+ for (i = 0; i < sc->nranges; i++) {
+ if (start >= sc->ranges[i].host && end <
+ sc->ranges[i].host + sc->ranges[i].size) {
+ *bank = (sc->ranges[i].bus >> 32);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+static int
+jz4780_nemc_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *r)
+{
+ struct jz4780_nemc_softc *sc;
+ u_int bank;
+ int err;
+
+ if (type == SYS_RES_MEMORY) {
+ sc = device_get_softc(bus);
+
+ /* Figure out on what bank device is residing */
+ err = jz4780_nemc_decode_bank(&sc->simplebus_sc, r, &bank);
+ if (err == 0) {
+ /* Attempt to configure the bank if not done already */
+ err = jz4780_nemc_configure_bank(sc, child, bank);
+ if (err != 0)
+ return (err);
+ }
+ }
+
+ /* Call default implementation to finish the work */
+ return (bus_generic_activate_resource(bus, child,
+ type, rid, r));
+}
+
+static device_method_t jz4780_nemc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_nemc_probe),
+ DEVMETHOD(device_attach, jz4780_nemc_attach),
+ DEVMETHOD(device_detach, jz4780_nemc_detach),
+
+ /* Overrides to configure bank on resource activation */
+ DEVMETHOD(bus_activate_resource, jz4780_nemc_activate_resource),
+
+ DEVMETHOD_END
+};
+
+static devclass_t jz4780_nemc_devclass;
+DEFINE_CLASS_1(nemc, jz4780_nemc_driver, jz4780_nemc_methods,
+ sizeof(struct jz4780_nemc_softc), simplebus_driver);
+DRIVER_MODULE(jz4780_nemc, simplebus, jz4780_nemc_driver,
+ jz4780_nemc_devclass, 0, 0);
diff --git a/sys/mips/ingenic/jz4780_ohci.c b/sys/mips/ingenic/jz4780_ohci.c
new file mode 100644
index 000000000000..3b4ffdbfcebb
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_ohci.c
@@ -0,0 +1,318 @@
+/*-
+ * Copyright (c) 2015, Alexander Kabaev <kan@FreeBSD.org>
+ * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/condvar.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/gpio.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ohci.h>
+#include <dev/usb/controller/ohcireg.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include <mips/ingenic/jz4780_clock.h>
+#include <mips/ingenic/jz4780_regs.h>
+
+static int jz4780_ohci_attach(device_t dev);
+static int jz4780_ohci_detach(device_t dev);
+static int jz4780_ohci_probe(device_t dev);
+
+struct jz4780_ohci_softc
+{
+ struct ohci_softc sc_ohci;
+ struct gpiobus_pin *gpio_vbus;
+ clk_t clk;
+};
+
+static int
+jz4780_ohci_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-ohci"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Ingenic JZ4780 OHCI");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_ohci_vbus_gpio_enable(device_t dev, struct jz4780_ohci_softc *sc)
+{
+ struct gpiobus_pin *gpio_vbus;
+ int error;
+
+ error = ofw_gpiobus_parse_gpios(dev, "ingenic,vbus-gpio", &gpio_vbus);
+ /*
+ * The pin can be mapped already by other device. Assume it also has need
+ * activated and proceed happily.
+ */
+ if (error <= 0)
+ return (0);
+
+ sc->gpio_vbus = gpio_vbus;
+ if (error > 1) {
+ device_printf(dev, "too many vbus gpios\n");
+ return (ENXIO);
+ }
+
+ if (sc->gpio_vbus != NULL) {
+ error = GPIO_PIN_SET(sc->gpio_vbus->dev, sc->gpio_vbus->pin, 1);
+ if (error != 0) {
+ device_printf(dev, "Cannot configure GPIO pin %d on %s\n",
+ sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev));
+ return (error);
+ }
+
+ error = GPIO_PIN_SETFLAGS(sc->gpio_vbus->dev, sc->gpio_vbus->pin,
+ GPIO_PIN_OUTPUT);
+ if (error != 0) {
+ device_printf(dev, "Cannot configure GPIO pin %d on %s\n",
+ sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev));
+ return (error);
+ }
+ }
+ return (0);
+}
+
+static int
+jz4780_ohci_clk_enable(device_t dev)
+{
+ struct jz4780_ohci_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+
+ err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
+ if (err != 0) {
+ device_printf(dev, "unable to lookup device clock\n");
+ return (err);
+ }
+ err = clk_enable(sc->clk);
+ if (err != 0) {
+ device_printf(dev, "unable to enable device clock\n");
+ return (err);
+ }
+ err = clk_set_freq(sc->clk, 48000000, 0);
+ if (err != 0) {
+ device_printf(dev, "unable to set device clock to 48 kHZ\n");
+ return (err);
+ }
+ return (0);
+}
+
+static int
+jz4780_ohci_attach(device_t dev)
+{
+ struct jz4780_ohci_softc *sc = device_get_softc(dev);
+ int err;
+ int rid;
+
+ /* initialize some bus fields */
+ sc->sc_ohci.sc_bus.parent = dev;
+ sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices;
+ sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES;
+ sc->sc_ohci.sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus,
+ USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+
+ sc->sc_ohci.sc_dev = dev;
+
+ /* frob vbus gpio */
+ err = jz4780_ohci_vbus_gpio_enable(dev, sc);
+ if (err)
+ goto error;
+
+ err = jz4780_ohci_clk_enable(dev);
+ if (err)
+ goto error;
+
+ rid = 0;
+ sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_ohci.sc_io_res == NULL) {
+ err = ENOMEM;
+ goto error;
+ }
+ sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res);
+ sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res);
+ sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res);
+
+ rid = 0;
+ sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->sc_ohci.sc_irq_res == NULL) {
+ err = ENOMEM;
+ goto error;
+ }
+
+ if (jz4780_ohci_enable() != 0) {
+ device_printf(dev, "CGU failed to enable OHCI\n");
+ err = ENXIO;
+ goto error;
+ }
+
+ sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (sc->sc_ohci.sc_bus.bdev == NULL) {
+ err = ENOMEM;
+ goto error;
+ }
+ device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus);
+
+ err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res,
+ INTR_TYPE_BIO | INTR_MPSAFE, NULL,
+ (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl);
+ if (err) {
+ err = ENXIO;
+ goto error;
+ }
+
+ strlcpy(sc->sc_ohci.sc_vendor, "Ingenic", sizeof(sc->sc_ohci.sc_vendor));
+ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0);
+
+ err = ohci_init(&sc->sc_ohci);
+ if (!err)
+ err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev);
+
+ if (err)
+ goto error;
+ return (0);
+
+error:
+ if (err)
+ jz4780_ohci_detach(dev);
+ return (err);
+}
+
+static int
+jz4780_ohci_detach(device_t dev)
+{
+ struct jz4780_ohci_softc *sc = device_get_softc(dev);
+ device_t bdev;
+
+ if (sc->sc_ohci.sc_bus.bdev) {
+ bdev = sc->sc_ohci.sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ /*
+ * Put the controller into reset, then disable clocks and do
+ * the MI tear down. We have to disable the clocks/hardware
+ * after we do the rest of the teardown. We also disable the
+ * clocks in the opposite order we acquire them, but that
+ * doesn't seem to be absolutely necessary. We free up the
+ * clocks after we disable them, so the system could, in
+ * theory, reuse them.
+ */
+ if (sc->sc_ohci.sc_io_res != NULL) {
+ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl,
+ OHCI_CONTROL, 0);
+ }
+
+ if (sc->sc_ohci.sc_intr_hdl) {
+ bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl);
+ sc->sc_ohci.sc_intr_hdl = NULL;
+ }
+
+ if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) {
+ /*
+ * only call ohci_detach() after ohci_init()
+ */
+ ohci_detach(&sc->sc_ohci);
+
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res);
+ sc->sc_ohci.sc_irq_res = NULL;
+ }
+ if (sc->sc_ohci.sc_io_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.sc_io_res);
+ sc->sc_ohci.sc_io_res = NULL;
+ sc->sc_ohci.sc_io_tag = 0;
+ sc->sc_ohci.sc_io_hdl = 0;
+ }
+
+ if (sc->clk != NULL)
+ clk_release(sc->clk);
+
+ usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc);
+ free(sc->gpio_vbus, M_DEVBUF);
+ return (0);
+}
+
+static device_method_t ohci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_ohci_probe),
+ DEVMETHOD(device_attach, jz4780_ohci_attach),
+ DEVMETHOD(device_detach, jz4780_ohci_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+static driver_t ohci_driver = {
+ .name = "ohci",
+ .methods = ohci_methods,
+ .size = sizeof(struct jz4780_ohci_softc),
+};
+
+static devclass_t ohci_devclass;
+
+DRIVER_MODULE(ohci, simplebus, ohci_driver, ohci_devclass, 0, 0);
diff --git a/sys/mips/ingenic/jz4780_pinctrl.c b/sys/mips/ingenic/jz4780_pinctrl.c
new file mode 100644
index 000000000000..adacb169147f
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_pinctrl.c
@@ -0,0 +1,260 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Ingenic JZ4780 pinctrl driver.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/fdt/simplebus.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+
+#include "jz4780_gpio_if.h"
+
+struct jz4780_pinctrl_softc {
+ struct simplebus_softc ssc;
+ device_t dev;
+};
+
+#define CHIP_REG_STRIDE 256
+#define CHIP_REG_OFFSET(base, chip) ((base) + (chip) * CHIP_REG_STRIDE)
+
+static int
+jz4780_pinctrl_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-pinctrl"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Ingenic JZ4780 GPIO");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_pinctrl_attach(device_t dev)
+{
+ struct jz4780_pinctrl_softc *sc;
+ struct resource_list *rs;
+ struct resource_list_entry *re;
+ phandle_t dt_parent, dt_child;
+ int i, ret;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ /*
+ * Fetch our own resource list to dole memory between children
+ */
+ rs = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
+ if (rs == NULL)
+ return (ENXIO);
+ re = resource_list_find(rs, SYS_RES_MEMORY, 0);
+ if (re == NULL)
+ return (ENXIO);
+
+ simplebus_init(dev, 0);
+
+ /* Iterate over this node children, looking for pin controllers */
+ dt_parent = ofw_bus_get_node(dev);
+ i = 0;
+ for (dt_child = OF_child(dt_parent); dt_child != 0;
+ dt_child = OF_peer(dt_child)) {
+ struct simplebus_devinfo *ndi;
+ device_t child;
+ bus_addr_t phys;
+ bus_size_t size;
+
+ /* Add gpio controller child */
+ if (!OF_hasprop(dt_child, "gpio-controller"))
+ continue;
+ child = simplebus_add_device(dev, dt_child, 0, NULL, -1, NULL);
+ if (child == NULL)
+ break;
+ /* Setup child resources */
+ phys = CHIP_REG_OFFSET(re->start, i);
+ size = CHIP_REG_STRIDE;
+ if (phys + size - 1 <= re->end) {
+ ndi = device_get_ivars(child);
+ resource_list_add(&ndi->rl, SYS_RES_MEMORY, 0,
+ phys, phys + size - 1, size);
+ }
+ i++;
+ }
+
+ ret = bus_generic_attach(dev);
+ if (ret == 0) {
+ fdt_pinctrl_register(dev, "ingenic,pins");
+ fdt_pinctrl_configure_tree(dev);
+ }
+ return (ret);
+}
+
+static int
+jz4780_pinctrl_detach(device_t dev)
+{
+
+ bus_generic_detach(dev);
+ return (0);
+}
+
+struct jx4780_bias_prop {
+ const char *name;
+ uint32_t bias;
+};
+
+static struct jx4780_bias_prop jx4780_bias_table[] = {
+ { "bias-disable", 0 },
+ { "bias-pull-up", GPIO_PIN_PULLUP },
+ { "bias-pull-down", GPIO_PIN_PULLDOWN },
+};
+
+static int
+jz4780_pinctrl_parse_pincfg(phandle_t pincfgxref, uint32_t *bias_value)
+{
+ phandle_t pincfg_node;
+ int i;
+
+ pincfg_node = OF_node_from_xref(pincfgxref);
+ for (i = 0; i < nitems(jx4780_bias_table); i++) {
+ if (OF_hasprop(pincfg_node, jx4780_bias_table[i].name)) {
+ *bias_value = jx4780_bias_table[i].bias;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static device_t
+jz4780_pinctrl_chip_lookup(struct jz4780_pinctrl_softc *sc, phandle_t chipxref)
+{
+ device_t chipdev;
+
+ chipdev = OF_device_from_xref(chipxref);
+ return chipdev;
+}
+
+static int
+jz4780_pinctrl_configure_pins(device_t dev, phandle_t cfgxref)
+{
+ struct jz4780_pinctrl_softc *sc = device_get_softc(dev);
+ device_t chip;
+ phandle_t node;
+ ssize_t i, len;
+ uint32_t *value, *pconf;
+ int result;
+
+ node = OF_node_from_xref(cfgxref);
+
+ len = OF_getencprop_alloc(node, "ingenic,pins", sizeof(uint32_t) * 4,
+ (void **)&value);
+ if (len < 0) {
+ device_printf(dev,
+ "missing ingenic,pins attribute in FDT\n");
+ return (ENXIO);
+ }
+
+ pconf = value;
+ result = EINVAL;
+ for (i = 0; i < len; i++, pconf += 4) {
+ uint32_t bias;
+
+ /* Lookup the chip that handles this configuration */
+ chip = jz4780_pinctrl_chip_lookup(sc, pconf[0]);
+ if (chip == NULL) {
+ device_printf(dev,
+ "invalid gpio controller reference in FDT\n");
+ goto done;
+ }
+
+ if (jz4780_pinctrl_parse_pincfg(pconf[3], &bias) != 0) {
+ device_printf(dev,
+ "invalid pin bias for pin %u on %s in FDT\n",
+ pconf[1], ofw_bus_get_name(chip));
+ goto done;
+ }
+
+ result = JZ4780_GPIO_CONFIGURE_PIN(chip, pconf[1], pconf[2],
+ bias);
+ if (result != 0) {
+ device_printf(dev,
+ "failed to configure pin %u on %s\n", pconf[1],
+ ofw_bus_get_name(chip));
+ goto done;
+ }
+ }
+
+ result = 0;
+done:
+ free(value, M_OFWPROP);
+ return (result);
+}
+
+
+static device_method_t jz4780_pinctrl_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_pinctrl_probe),
+ DEVMETHOD(device_attach, jz4780_pinctrl_attach),
+ DEVMETHOD(device_detach, jz4780_pinctrl_detach),
+
+ /* fdt_pinctrl interface */
+ DEVMETHOD(fdt_pinctrl_configure, jz4780_pinctrl_configure_pins),
+
+ DEVMETHOD_END
+};
+
+static devclass_t jz4780_pinctrl_devclass;
+DEFINE_CLASS_1(pinctrl, jz4780_pinctrl_driver, jz4780_pinctrl_methods,
+ sizeof(struct jz4780_pinctrl_softc), simplebus_driver);
+EARLY_DRIVER_MODULE(pinctrl, simplebus, jz4780_pinctrl_driver,
+ jz4780_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/mips/ingenic/jz4780_pinctrl.h b/sys/mips/ingenic/jz4780_pinctrl.h
new file mode 100644
index 000000000000..4aaa96e00016
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_pinctrl.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MIPS_INGENIC_JZ4780_PINCTRL_H
+#define _MIPS_INGENIC_JZ4780_PINCTRL_H
+
+
+#endif /* _MIPS_INGENIC_JZ4780_PINCTRL_H */
diff --git a/sys/mips/ingenic/jz4780_regs.h b/sys/mips/ingenic/jz4780_regs.h
new file mode 100644
index 000000000000..221c21784925
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_regs.h
@@ -0,0 +1,787 @@
+/* $NetBSD: ingenic_regs.h,v 1.22 2015/10/08 17:54:30 macallan Exp $ */
+
+/*-
+ * Copyright (c) 2014 Michael Lorenz
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef JZ4780_REGS_H
+#define JZ4780_REGS_H
+
+/* for mips_wbflush() */
+#include <machine/locore.h>
+
+/* UARTs, mostly 16550 compatible with 32bit spaced registers */
+#define JZ_UART0 0x10030000
+#define JZ_UART1 0x10031000
+#define JZ_UART2 0x10032000
+#define JZ_UART3 0x10033000
+#define JZ_UART4 0x10034000
+
+/* LCD controller base addresses, registers are in jzfb_regs.h */
+#define JZ_LCDC0_BASE 0x13050000
+#define JZ_LCDC1_BASE 0x130a0000
+
+/* TCU unit base address */
+#define JZ_TCU_BASE 0x10002000
+
+/* Watchdog */
+#define JZ_WDOG_TDR 0x00000000 /* compare */
+#define JZ_WDOG_TCER 0x00000004
+ #define TCER_ENABLE 0x01 /* enable counter */
+#define JZ_WDOG_TCNT 0x00000008 /* 16bit up count */
+#define JZ_WDOG_TCSR 0x0000000c
+ #define TCSR_PCK_EN 0x01 /* PCLK */
+ #define TCSR_RTC_EN 0x02 /* RTCCLK - 32.768kHz */
+ #define TCSR_EXT_EN 0x04 /* EXTCLK - 48MHz */
+ #define TCSR_PRESCALE_M 0x38
+ #define TCSR_DIV_1 0x00
+ #define TCSR_DIV_4 0x08
+ #define TCSR_DIV_16 0x10
+ #define TCSR_DIV_64 0x18
+ #define TCSR_DIV_256 0x20
+ #define TCSR_DIV_1024 0x28
+
+/* timers and PWMs */
+#define JZ_TC_TER 0x00000010 /* TC enable reg, ro */
+#define JZ_TC_TESR 0x00000014 /* TC enable set reg. */
+ #define TESR_TCST0 0x0001 /* enable counter 0 */
+ #define TESR_TCST1 0x0002 /* enable counter 1 */
+ #define TESR_TCST2 0x0004 /* enable counter 2 */
+ #define TESR_TCST3 0x0008 /* enable counter 3 */
+ #define TESR_TCST4 0x0010 /* enable counter 4 */
+ #define TESR_TCST5 0x0020 /* enable counter 5 */
+ #define TESR_TCST6 0x0040 /* enable counter 6 */
+ #define TESR_TCST7 0x0080 /* enable counter 7 */
+ #define TESR_OST 0x8000 /* enable OST */
+#define JZ_TC_TECR 0x00000018 /* TC enable clear reg. */
+#define JZ_TC_TFR 0x00000020
+ #define TFR_FFLAG0 0x00000001 /* channel 0 */
+ #define TFR_FFLAG1 0x00000002 /* channel 1 */
+ #define TFR_FFLAG2 0x00000004 /* channel 2 */
+ #define TFR_FFLAG3 0x00000008 /* channel 3 */
+ #define TFR_FFLAG4 0x00000010 /* channel 4 */
+ #define TFR_FFLAG5 0x00000020 /* channel 5 */
+ #define TFR_FFLAG6 0x00000040 /* channel 6 */
+ #define TFR_FFLAG7 0x00000080 /* channel 7 */
+ #define TFR_OSTFLAG 0x00008000 /* OS timer */
+#define JZ_TC_TFSR 0x00000024 /* timer flag set */
+#define JZ_TC_TFCR 0x00000028 /* timer flag clear */
+#define JZ_TC_TMR 0x00000030 /* timer flag mask */
+ #define TMR_FMASK(n) (1 << (n))
+ #define TMR_HMASK(n) (1 << ((n) + 16))
+#define JZ_TC_TMSR 0x00000034 /* timer flag mask set */
+#define JZ_TC_TMCR 0x00000038 /* timer flag mask clear*/
+
+#define JZ_TC_TDFR(n) (0x00000040 + (n * 0x10)) /* FULL compare */
+#define JZ_TC_TDHR(n) (0x00000044 + (n * 0x10)) /* HALF compare */
+#define JZ_TC_TCNT(n) (0x00000048 + (n * 0x10)) /* count */
+
+#define JZ_TC_TCSR(n) (0x0000004c + (n * 0x10))
+/* same bits as in JZ_WDOG_TCSR */
+
+/* operating system timer */
+#define JZ_OST_DATA 0x000000e0 /* compare */
+#define JZ_OST_CNT_LO 0x000000e4
+#define JZ_OST_CNT_HI 0x000000e8
+#define JZ_OST_CTRL 0x000000ec
+ #define OSTC_PCK_EN 0x0001 /* use PCLK */
+ #define OSTC_RTC_EN 0x0002 /* use RTCCLK */
+ #define OSTC_EXT_EN 0x0004 /* use EXTCLK */
+ #define OSTC_PRESCALE_M 0x0038
+ #define OSTC_DIV_1 0x0000
+ #define OSTC_DIV_4 0x0008
+ #define OSTC_DIV_16 0x0010
+ #define OSTC_DIV_64 0x0018
+ #define OSTC_DIV_256 0x0020
+ #define OSTC_DIV_1024 0x0028
+ #define OSTC_SHUTDOWN 0x0200
+ #define OSTC_MODE 0x8000 /* 0 - reset to 0 when = OST_DATA */
+#define JZ_OST_CNT_U32 0x000000fc /* copy of CNT_HI when reading CNT_LO */
+
+static inline void
+writereg(uint32_t reg, uint32_t val)
+{
+ *(volatile int32_t *)MIPS_PHYS_TO_KSEG1(reg) = val;
+ mips_wbflush();
+}
+
+static inline uint32_t
+readreg(uint32_t reg)
+{
+ mips_wbflush();
+ return *(volatile int32_t *)MIPS_PHYS_TO_KSEG1(reg);
+}
+
+/* Clock management */
+#define JZ_CGU_BASE 0x10000000
+
+#define JZ_CPCCR 0x00000000 /* Clock Control Register */
+ #define JZ_PDIV_M 0x000f0000 /* PCLK divider mask */
+ #define JZ_PDIV_S 16 /* PCLK divider shift */
+ #define JZ_CDIV_M 0x0000000f /* CPU clock divider mask */
+ #define JZ_CDIV_S 0 /* CPU clock divider shift */
+#define JZ_CPAPCR 0x00000010 /* APLL */
+#define JZ_CPMPCR 0x00000014 /* MPLL */
+#define JZ_CPEPCR 0x00000018 /* EPLL */
+#define JZ_CPVPCR 0x0000001C /* VPLL */
+ #define JZ_PLLM_S 19 /* PLL multiplier shift */
+ #define JZ_PLLM_M 0xfff80000 /* PLL multiplier mask */
+ #define JZ_PLLN_S 13 /* PLL divider shift */
+ #define JZ_PLLN_M 0x0007e000 /* PLL divider mask */
+ #define JZ_PLLP_S 9 /* PLL postdivider shift */
+ #define JZ_PLLP_M 0x00001700 /* PLL postdivider mask */
+ #define JZ_PLLON 0x00000010 /* PLL is on and stable */
+ #define JZ_PLLBP 0x00000002 /* PLL bypass */
+ #define JZ_PLLEN 0x00000001 /* PLL enable */
+#define JZ_CLKGR0 0x00000020 /* Clock Gating Registers */
+ #define CLK_NEMC (1 << 0)
+ #define CLK_BCH (1 << 1)
+ #define CLK_OTG0 (1 << 2)
+ #define CLK_MSC0 (1 << 3)
+ #define CLK_SSI0 (1 << 4)
+ #define CLK_SMB0 (1 << 5)
+ #define CLK_SMB1 (1 << 6)
+ #define CLK_SCC (1 << 7)
+ #define CLK_AIC (1 << 8)
+ #define CLK_TSSI0 (1 << 9)
+ #define CLK_OWI (1 << 10)
+ #define CLK_MSC1 (1 << 11)
+ #define CLK_MSC2 (1 << 12)
+ #define CLK_KBC (1 << 13)
+ #define CLK_SADC (1 << 14)
+ #define CLK_UART0 (1 << 15)
+ #define CLK_UART1 (1 << 16)
+ #define CLK_UART2 (1 << 17)
+ #define CLK_UART3 (1 << 18)
+ #define CLK_SSI1 (1 << 19)
+ #define CLK_SSI2 (1 << 20)
+ #define CLK_PDMA (1 << 21)
+ #define CLK_GPS (1 << 22)
+ #define CLK_MAC (1 << 23)
+ #define CLK_UHC (1 << 24)
+ #define CLK_SMB2 (1 << 25)
+ #define CLK_CIM (1 << 26)
+ #define CLK_TVE (1 << 27)
+ #define CLK_LCD (1 << 28)
+ #define CLK_IPU (1 << 29)
+ #define CLK_DDR0 (1 << 30)
+ #define CLK_DDR1 (1 << 31)
+#define JZ_CLKGR1 0x00000028 /* Clock Gating Registers */
+ #define CLK_SMB3 (1 << 0)
+ #define CLK_TSSI1 (1 << 1)
+ #define CLK_VPU (1 << 2)
+ #define CLK_PCM (1 << 3)
+ #define CLK_GPU (1 << 4)
+ #define CLK_COMPRESS (1 << 5)
+ #define CLK_AIC1 (1 << 6)
+ #define CLK_GPVLC (1 << 7)
+ #define CLK_OTG1 (1 << 8)
+ #define CLK_HDMI (1 << 9)
+ #define CLK_UART4 (1 << 10)
+ #define CLK_AHB_MON (1 << 11)
+ #define CLK_SMB4 (1 << 12)
+ #define CLK_DES (1 << 13)
+ #define CLK_X2D (1 << 14)
+ #define CLK_P1 (1 << 15)
+#define JZ_DDCDR 0x0000002c /* DDR clock divider register */
+#define JZ_VPUCDR 0x00000030 /* VPU clock divider register */
+#define JZ_I2SCDR 0x00000060 /* I2S device clock divider register */
+#define JZ_I2S1CDR 0x000000a0 /* I2S device clock divider register */
+#define JZ_USBCDR 0x00000050 /* OTG PHY clock divider register */
+#define JZ_LP0CDR 0x00000054 /* LCD0 pix clock divider register */
+#define JZ_LP1CDR 0x00000064 /* LCD1 pix clock divider register */
+#define JZ_MSC0CDR 0x00000068 /* MSC0 clock divider register */
+#define JZ_MSC1CDR 0x000000a4 /* MSC1 clock divider register */
+#define JZ_MSC2CDR 0x000000a8 /* MSC2 clock divider register */
+ #define MSCCDR_SCLK_A 0x40000000
+ #define MSCCDR_MPLL 0x80000000
+ #define MSCCDR_CE 0x20000000
+ #define MSCCDR_BUSY 0x10000000
+ #define MSCCDR_STOP 0x08000000
+ #define MSCCDR_PHASE 0x00008000 /* 0 - 90deg phase, 1 - 180 */
+ #define MSCCDR_DIV_M 0x000000ff /* src / ((div + 1) * 2) */
+ #define UHCCDR_DIV_M 0x000000ff
+#define JZ_UHCCDR 0x0000006c /* UHC 48M clock divider register */
+ #define UHCCDR_SCLK_A 0x00000000
+ #define UHCCDR_MPLL 0x40000000
+ #define UHCCDR_EPLL 0x80000000
+ #define UHCCDR_OTG_PHY 0xc0000000
+ #define UHCCDR_CLK_MASK 0xc0000000
+ #define UHCCDR_CE 0x20000000
+ #define UHCCDR_BUSY 0x10000000
+ #define UHCCDR_STOP 0x08000000
+ #define UHCCDR_DIV_M 0x000000ff
+ #define UHCCDR_DIV(d) (d)
+#define JZ_SSICDR 0x00000074 /* SSI clock divider register */
+#define JZ_CIMCDR 0x0000007c /* CIM MCLK clock divider register */
+#define JZ_PCMCDR 0x00000084 /* PCM device clock divider register */
+#define JZ_GPUCDR 0x00000088 /* GPU clock divider register */
+#define JZ_HDMICDR 0x0000008c /* HDMI clock divider register */
+#define JZ_BCHCDR 0x000000ac /* BCH clock divider register */
+#define JZ_CPM_INTR 0x000000b0 /* CPM interrupt register */
+#define JZ_CPM_INTRE 0x000000b4 /* CPM interrupt enable register */
+#define JZ_CPSPR 0x00000034 /* CPM scratch register */
+#define JZ_CPSRPR 0x00000038 /* CPM scratch protected register */
+#define JZ_USBPCR 0x0000003c /* USB parameter control register */
+ #define PCR_USB_MODE 0x80000000 /* 1 - otg */
+ #define PCR_AVLD_REG 0x40000000
+ #define PCR_IDPULLUP_MASK 0x30000000
+ #define PCR_INCR_MASK 0x08000000
+ #define PCR_TCRISETUNE 0x04000000
+ #define PCR_COMMONONN 0x02000000
+ #define PCR_VBUSVLDEXT 0x01000000
+ #define PCR_VBUSVLDEXTSEL 0x00800000
+ #define PCR_POR 0x00400000
+ #define PCR_SIDDQ 0x00200000
+ #define PCR_OTG_DISABLE 0x00100000
+ #define PCR_COMPDISTN_M 0x000e0000
+ #define PCR_OTGTUNE 0x0001c000
+ #define PCR_SQRXTUNE 0x00003800
+ #define PCR_TXFSLSTUNE 0x00000780
+ #define PCR_TXPREEMPHTUNE 0x00000040
+ #define PCR_TXHSXVTUNE 0x00000030
+ #define PCR_TXVREFTUNE 0x0000000f
+#define JZ_USBRDT 0x00000040 /* Reset detect timer register */
+ #define USBRDT_USBRDT_SHIFT 0
+ #define USBRDT_USBRDT_WIDTH 23
+ #define USBRDT_VBFIL_LD_EN 0x01000000
+#define JZ_USBVBFIL 0x00000044 /* USB jitter filter register */
+ #define USBVBFIL_IDDIGFIL_SHIFT 16
+ #define USBVBFIL_IDDIGFIL_WIDTH 16
+ #define USBVBFIL_USBVBFIL_SHIFT 0
+ #define USBVBFIL_USBVBFIL_WIDTH 16
+#define JZ_USBPCR1 0x00000048 /* USB parameter control register 1 */
+ #define PCR_SYNOPSYS 0x10000000 /* Mentor mode otherwise */
+ #define PCR_REFCLK_CORE 0x08000000
+ #define PCR_REFCLK_XO25 0x04000000
+ #define PCR_REFCLK_CO 0x00000000
+ #define PCR_REFCLK_M 0x0c000000
+ #define PCR_CLK_M 0x03000000 /* clock */
+ #define PCR_CLK_192 0x03000000 /* 19.2MHz */
+ #define PCR_CLK_48 0x02000000 /* 48MHz */
+ #define PCR_CLK_24 0x01000000 /* 24MHz */
+ #define PCR_CLK_12 0x00000000 /* 12MHz */
+ #define PCR_DMPD1 0x00800000 /* pull down D- on port 1 */
+ #define PCR_DPPD1 0x00400000 /* pull down D+ on port 1 */
+ #define PCR_PORT0_RST 0x00200000 /* port 0 reset */
+ #define PCR_PORT1_RST 0x00100000 /* port 1 reset */
+ #define PCR_WORD_I_F0 0x00080000 /* 1: 16bit/30M, 8/60 otherw. */
+ #define PCR_WORD_I_F1 0x00040000 /* same for port 1 */
+ #define PCR_COMPDISTUNE 0x00038000 /* disconnect threshold */
+ #define PCR_SQRXTUNE1 0x00007000 /* squelch threshold */
+ #define PCR_TXFSLSTUNE1 0x00000f00 /* FS/LS impedance adj. */
+ #define PCR_TXPREEMPH 0x00000080 /* HS transm. pre-emphasis */
+ #define PCR_TXHSXVTUNE1 0x00000060 /* dp/dm voltage adj. */
+ #define PCR_TXVREFTUNE1 0x00000017 /* HS DC voltage adj. */
+ #define PCR_TXRISETUNE1 0x00000001 /* rise/fall wave adj. */
+
+/* power manager */
+#define JZ_LPCR 0x00000004
+ #define LPCR_PD_SCPU (1u << 31) /* CPU1 power down */
+ #define LPCR_PD_VPU (1u << 30) /* VPU power down */
+ #define LPCR_PD_GPU (1u << 29) /* GPU power down */
+ #define LPCR_PD_GPS (1u << 28) /* GPS power down */
+ #define LPCR_SCPUS (1u << 27) /* CPU1 power down status */
+ #define LPCR_VPUS (1u << 26) /* VPU power down status */
+ #define LPCR_GPUS (1u << 25) /* GPU power down status */
+ #define LPCR_GPSS (1u << 24) /* GPS power down status */
+ #define LPCR_GPU_IDLE (1u << 20) /* GPU idle status */
+ #define LPCR_PST_SHIFT 8 /* Power stability time */
+ #define LPCR_PST_MASK (0xFFFu << 8)
+ #define LPCR_DUTY_SHIFT 3 /* CPU clock duty */
+ #define LPCR_DUTY_MASK (0x1Fu << 3)
+ #define LPCR_DOZE (1u << 2) /* Doze mode */
+ #define LPCR_LPM_SHIFT 0 /* Low power mode */
+ #define LPCR_LPM_MASK (0x03u << 0)
+
+#define JZ_OPCR 0x00000024 /* Oscillator Power Control Reg. */
+ #define OPCR_IDLE_DIS 0x80000000 /* don't stop CPU clk on idle */
+ #define OPCR_GPU_CLK_ST 0x40000000 /* stop GPU clock */
+ #define OPCR_L2CM_M 0x0c000000
+ #define OPCR_L2CM_ON 0x00000000 /* L2 stays on in sleep */
+ #define OPCR_L2CM_RET 0x04000000 /* L2 retention mode in sleep */
+ #define OPCR_L2CM_OFF 0x08000000 /* L2 powers down in sleep */
+ #define OPCR_SPENDN0 0x00000080 /* 0 - OTG port forced down */
+ #define OPCR_SPENDN1 0x00000040 /* 0 - UHC port forced down */
+ #define OPCR_BUS_MODE 0x00000020 /* 1 - bursts */
+ #define OPCR_O1SE 0x00000010 /* EXTCLK on in sleep */
+ #define OPCR_PD 0x00000008 /* P0 down in sleep */
+ #define OPCR_ERCS 0x00000004 /* 1 RTCCLK, 0 EXTCLK/512 */
+ #define OPCR_CPU_MODE 0x00000002 /* 1 access 'accelerated' */
+ #define OPCR_OSE 0x00000001 /* disable EXTCLK */
+
+#define JZ_SPCR0 0x000000b8 /* SRAM Power Control Registers */
+#define JZ_SPCR1 0x000000bc
+#define JZ_SRBC 0x000000c4 /* Soft Reset & Bus Control */
+ #define SRBC_UHC_SR 0x00004000 /* UHC soft reset*/
+
+/*
+ * random number generator
+ *
+ * Its function currently isn't documented by Ingenic.
+ * However, testing suggests that it works as expected.
+ */
+#define JZ_ERNG 0x000000d8
+#define JZ_RNG 0x000000dc
+
+/* Interrupt controller */
+#define JZ_ICBASE 0x10001000 /* IC base address */
+#define JZ_ICSR0 0x00000000 /* raw IRQ line status */
+#define JZ_ICMR0 0x00000004 /* IRQ mask, 1 masks IRQ */
+#define JZ_ICMSR0 0x00000008 /* sets bits in mask register */
+#define JZ_ICMCR0 0x0000000c /* clears bits in mask register */
+#define JZ_ICPR0 0x00000010 /* line status after masking */
+
+#define JZ_ICSR1 0x00000020 /* raw IRQ line status */
+#define JZ_ICMR1 0x00000024 /* IRQ mask, 1 masks IRQ */
+#define JZ_ICMSR1 0x00000028 /* sets bits in mask register */
+#define JZ_ICMCR1 0x0000002c /* clears bits in maks register */
+#define JZ_ICPR1 0x00000030 /* line status after masking */
+
+#define JZ_DSR0 0x00000034 /* source for PDMA */
+#define JZ_DMR0 0x00000038 /* mask for PDMA */
+#define JZ_DPR0 0x0000003c /* pending for PDMA */
+
+#define JZ_DSR1 0x00000040 /* source for PDMA */
+#define JZ_DMR1 0x00000044 /* mask for PDMA */
+#define JZ_DPR1 0x00000048 /* pending for PDMA */
+
+/* memory controller */
+#define JZ_DMMAP0 0x13010024
+#define JZ_DMMAP1 0x13010028
+ #define DMMAP_BASE 0x0000ff00 /* base PADDR of memory chunk */
+ #define DMMAP_MASK 0x000000ff /* mask which bits of PADDR are
+ * constant */
+/* USB controllers */
+#define JZ_EHCI_BASE 0x13490000
+#define JZ_EHCI_REG_UTMI_BUS 0x000000b0
+ #define UTMI_BUS_WIDTH 0x00000040
+#define JZ_OHCI_BASE 0x134a0000
+
+#define JZ_DWC2_BASE 0x13500000
+#define JZ_DWC2_GUSBCFG 0
+
+/* Ethernet */
+#define JZ_DME_BASE 0x16000000
+#define JZ_DME_IO 0
+#define JZ_DME_DATA 2
+
+/* GPIO */
+#define JZ_GPIO_A_BASE 0x10010000
+#define JZ_GPIO_B_BASE 0x10010100
+#define JZ_GPIO_C_BASE 0x10010200
+#define JZ_GPIO_D_BASE 0x10010300
+#define JZ_GPIO_E_BASE 0x10010400
+#define JZ_GPIO_F_BASE 0x10010500
+
+/* GPIO registers per port */
+#define JZ_GPIO_PIN 0x00000000 /* pin level register */
+/* 0 - normal gpio, 1 - interrupt */
+#define JZ_GPIO_INT 0x00000010 /* interrupt register */
+#define JZ_GPIO_INTS 0x00000014 /* interrupt set register */
+#define JZ_GPIO_INTC 0x00000018 /* interrupt clear register */
+/*
+ * INT == 1: 1 disables interrupt
+ * INT == 0: device select, see below
+ */
+#define JZ_GPIO_MASK 0x00000020 /* port mask register */
+#define JZ_GPIO_MASKS 0x00000024 /* port mask set register */
+#define JZ_GPIO_MASKC 0x00000028 /* port mask clear register */
+/*
+ * INT == 1: 0 - level triggered, 1 - edge triggered
+ * INT == 0: 0 - device select, see below
+ */
+#define JZ_GPIO_PAT1 0x00000030 /* pattern 1 register */
+#define JZ_GPIO_PAT1S 0x00000034 /* pattern 1 set register */
+#define JZ_GPIO_PAT1C 0x00000038 /* pattern 1 clear register */
+/*
+ * INT == 1:
+ * PAT1 == 0: 0 - trigger on low, 1 - trigger on high
+ * PAT0 == 1: 0 - trigger on falling edge, 1 - trigger on rising edge
+ * INT == 0:
+ * MASK == 0:
+ * PAT1 == 0: 0 - device 0, 1 - device 1
+ * PAT0 == 1: 0 - device 2, 1 - device 3
+ * MASK == 1:
+ * PAT1 == 0: set gpio output
+ * PAT1 == 1: pin is input
+ */
+#define JZ_GPIO_PAT0 0x00000040 /* pattern 0 register */
+#define JZ_GPIO_PAT0S 0x00000044 /* pattern 0 set register */
+#define JZ_GPIO_PAT0C 0x00000048 /* pattern 0 clear register */
+/* 1 - interrupt happened */
+#define JZ_GPIO_FLAG 0x00000050 /* flag register */
+#define JZ_GPIO_FLAGC 0x00000058 /* flag clear register */
+/* 1 - disable pull up/down resistors */
+#define JZ_GPIO_DPULL 0x00000070 /* pull disable register */
+#define JZ_GPIO_DPULLS 0x00000074 /* pull disable set register */
+#define JZ_GPIO_DPULLC 0x00000078 /* pull disable clear register */
+/* the following are uncommented in the manual */
+#define JZ_GPIO_DRVL 0x00000080 /* drive low register */
+#define JZ_GPIO_DRVLS 0x00000084 /* drive low set register */
+#define JZ_GPIO_DRVLC 0x00000088 /* drive low clear register */
+#define JZ_GPIO_DIR 0x00000090 /* direction register */
+#define JZ_GPIO_DIRS 0x00000094 /* direction register */
+#define JZ_GPIO_DIRC 0x00000098 /* direction register */
+#define JZ_GPIO_DRVH 0x000000a0 /* drive high register */
+#define JZ_GPIO_DRVHS 0x000000a4 /* drive high set register */
+#define JZ_GPIO_DRVHC 0x000000a8 /* drive high clear register */
+
+/* I2C / SMBus */
+#define JZ_SMB0_BASE 0x10050000
+#define JZ_SMB1_BASE 0x10051000
+#define JZ_SMB2_BASE 0x10052000
+#define JZ_SMB3_BASE 0x10053000
+#define JZ_SMB4_BASE 0x10054000
+
+/* SMBus register offsets, per port */
+#define JZ_SMBCON 0x00 /* SMB control */
+ #define JZ_STPHLD 0x80 /* Stop Hold Enable bit */
+ #define JZ_SLVDIS 0x40 /* 1 - slave disabled */
+ #define JZ_REST 0x20 /* 1 - allow RESTART */
+ #define JZ_MATP 0x10 /* 1 - enable 10bit addr. for master */
+ #define JZ_SATP 0x08 /* 1 - enable 10bit addr. for slave */
+ #define JZ_SPD_M 0x06 /* bus speed control */
+ #define JZ_SPD_100KB 0x02 /* 100kBit/s mode */
+ #define JZ_SPD_400KB 0x04 /* 400kBit/s mode */
+ #define JZ_MD 0x01 /* enable master */
+#define JZ_SMBTAR 0x04 /* SMB target address */
+ #define JZ_SMATP 0x1000 /* enable 10bit master addr */
+ #define JZ_SPECIAL 0x0800 /* 1 - special command */
+ #define JZ_START 0x0400 /* 1 - send START */
+ #define JZ_SMBTAR_M 0x03ff /* target address */
+#define JZ_SMBSAR 0x08 /* SMB slave address */
+#define JZ_SMBDC 0x10 /* SMB data buffer and command */
+ #define JZ_CMD 0x100 /* 1 - read, 0 - write */
+ #define JZ_DATA 0x0ff
+#define JZ_SMBSHCNT 0x14 /* Standard speed SMB SCL high count */
+#define JZ_SMBSLCNT 0x18 /* Standard speed SMB SCL low count */
+#define JZ_SMBFHCNT 0x1C /* Fast speed SMB SCL high count */
+#define JZ_SMBFLCNT 0x20 /* Fast speed SMB SCL low count */
+#define JZ_SMBINTST 0x2C /* SMB Interrupt Status */
+ #define JZ_ISTT 0x400 /* START or RESTART occured */
+ #define JZ_ISTP 0x200 /* STOP occured */
+ #define JZ_TXABT 0x40 /* ABORT occured */
+ #define JZ_TXEMP 0x10 /* TX FIFO is low */
+ #define JZ_TXOF 0x08 /* TX FIFO is high */
+ #define JZ_RXFL 0x04 /* RX FIFO is at JZ_SMBRXTL*/
+ #define JZ_RXOF 0x02 /* RX FIFO is high */
+ #define JZ_RXUF 0x01 /* RX FIFO underflow */
+#define JZ_SMBINTM 0x30 /* SMB Interrupt Mask */
+#define JZ_SMBRXTL 0x38 /* SMB RxFIFO Threshold */
+#define JZ_SMBTXTL 0x3C /* SMB TxFIFO Threshold */
+#define JZ_SMBCINT 0x40 /* Clear Interrupts */
+ #define JZ_CLEARALL 0x01
+#define JZ_SMBCRXUF 0x44 /* Clear RXUF Interrupt */
+#define JZ_SMBCRXOF 0x48 /* Clear RX_OVER Interrupt */
+#define JZ_SMBCTXOF 0x4C /* Clear TX_OVER Interrupt */
+#define JZ_SMBCRXREQ 0x50 /* Clear RDREQ Interrupt */
+#define JZ_SMBCTXABT 0x54 /* Clear TX_ABRT Interrupt */
+#define JZ_SMBCRXDN 0x58 /* Clear RX_DONE Interrupt */
+#define JZ_SMBCACT 0x5c /* Clear ACTIVITY Interrupt */
+#define JZ_SMBCSTP 0x60 /* Clear STOP Interrupt */
+#define JZ_SMBCSTT 0x64 /* Clear START Interrupt */
+#define JZ_SMBCGC 0x68 /* Clear GEN_CALL Interrupt */
+#define JZ_SMBENB 0x6C /* SMB Enable */
+ #define JZ_ENABLE 0x01
+#define JZ_SMBST 0x70 /* SMB Status register */
+ #define JZ_SLVACT 0x40 /* slave is active */
+ #define JZ_MSTACT 0x20 /* master is active */
+ #define JZ_RFF 0x10 /* RX FIFO is full */
+ #define JZ_RFNE 0x08 /* RX FIFO not empty */
+ #define JZ_TFE 0x04 /* TX FIFO is empty */
+ #define JZ_TFNF 0x02 /* TX FIFO is not full */
+ #define JZ_ACT 0x01 /* JZ_SLVACT | JZ_MSTACT */
+#define JZ_SMBABTSRC 0x80 /* SMB Transmit Abort Status Register */
+#define JZ_SMBDMACR 0x88 /* DMA Control Register */
+#define JZ_SMBDMATDL 0x8c /* DMA Transmit Data Level */
+#define JZ_SMBDMARDL 0x90 /* DMA Receive Data Level */
+#define JZ_SMBSDASU 0x94 /* SMB SDA Setup Register */
+#define JZ_SMBACKGC 0x98 /* SMB ACK General Call Register */
+#define JZ_SMBENBST 0x9C /* SMB Enable Status Register */
+#define JZ_SMBSDAHD 0xD0 /* SMB SDA HolD time Register */
+ #define JZ_HDENB 0x100 /* enable hold time */
+
+/* SD/MMC hosts */
+#define JZ_MSC0_BASE 0x13450000
+#define JZ_MSC1_BASE 0x13460000
+#define JZ_MSC2_BASE 0x13470000
+
+#define JZ_MSC_CTRL 0x00
+ #define JZ_SEND_CCSD 0x8000
+ #define JZ_SEND_AS_CCSD 0x4000
+ #define JZ_EXIT_MULTIPLE 0x0080
+ #define JZ_EXIT_TRANSFER 0x0040
+ #define JZ_START_READWAIT 0x0020
+ #define JZ_STOP_READWAIT 0x0010
+ #define JZ_RESET 0x0008
+ #define JZ_START_OP 0x0004
+ #define JZ_CLOCK_CTRL_M 0x0003
+ #define JZ_CLOCK_START 0x0002
+ #define JZ_CLOCK_STOP 0x0001
+#define JZ_MSC_STAT 0x04
+ #define JZ_AUTO_CMD12_DONE 0x80000000
+ #define JZ_AUTO_CMD23_DONE 0x40000000
+ #define JZ_SVS 0x20000000
+ #define JZ_PIN_LEVEL_M 0x1f000000
+ #define JZ_BCE 0x00100000 /* boot CRC error */
+ #define JZ_BDE 0x00080000 /* boot data end */
+ #define JZ_BAE 0x00040000 /* boot acknowledge error */
+ #define JZ_BAR 0x00020000 /* boot ack. received */
+ #define JZ_DMAEND 0x00010000
+ #define JZ_IS_RESETTING 0x00008000
+ #define JZ_SDIO_INT_ACTIVE 0x00004000
+ #define JZ_PRG_DONE 0x00002000
+ #define JZ_DATA_TRAN_DONE 0x00001000
+ #define JZ_END_CMD_RES 0x00000800
+ #define JZ_DATA_FIFO_AFULL 0x00000400
+ #define JZ_IS_READWAIT 0x00000200
+ #define JZ_CLK_EN 0x00000100
+ #define JZ_DATA_FIFO_FULL 0x00000080
+ #define JZ_DATA_FIFO_EMPTY 0x00000040
+ #define JZ_CRC_RES_ERR 0x00000020
+ #define JZ_CRC_READ_ERR 0x00000010
+ #define JZ_CRC_WRITE_ERR_M 0x0000000c
+ #define JZ_CRC_WRITE_OK 0x00000000
+ #define JZ_CRC_CARD_ERR 0x00000004
+ #define JZ_CRC_NO_STATUS 0x00000008
+ #define JZ_TIME_OUT_RES 0x00000002
+ #define JZ_TIME_OUT_READ 0x00000001
+#define JZ_MSC_CLKRT 0x08
+ #define JZ_DEV_CLK 0x0
+ #define JZ_DEV_CLK_2 0x1 /* DEV_CLK / 2 */
+ #define JZ_DEV_CLK_4 0x2 /* DEV_CLK / 4 */
+ #define JZ_DEV_CLK_8 0x3 /* DEV_CLK / 8 */
+ #define JZ_DEV_CLK_16 0x4 /* DEV_CLK / 16 */
+ #define JZ_DEV_CLK_32 0x5 /* DEV_CLK / 32 */
+ #define JZ_DEV_CLK_64 0x6 /* DEV_CLK / 64 */
+ #define JZ_DEV_CLK_128 0x7 /* DEV_CLK / 128 */
+#define JZ_MSC_CMDAT 0x0c
+ #define JZ_CCS_EXPECTED 0x80000000
+ #define JZ_READ_CEATA 0x40000000
+ #define JZ_DIS_BOOT 0x08000000
+ #define JZ_ENA_BOOT 0x04000000
+ #define JZ_EXP_BOOT_ACK 0x02000000
+ #define JZ_BOOT_MODE 0x01000000
+ #define JZ_AUTO_CMD23 0x00040000
+ #define JZ_SDIO_PRDT 0x00020000
+ #define JZ_AUTO_CMD12 0x00010000
+ #define JZ_RTRG_M 0x0000c000 /* receive FIFO trigger */
+ #define JZ_RTRG_16 0x00000000 /* >= 16 */
+ #define JZ_RTRG_32 0x00004000 /* >= 32 */
+ #define JZ_RTRG_64 0x00008000 /* >= 64 */
+ #define JZ_RTRG_96 0x0000c000 /* >= 96 */
+ #define JZ_TTRG_M 0x00003000 /* transmit FIFO trigger */
+ #define JZ_TTRG_16 0x00000000 /* >= 16 */
+ #define JZ_TTRG_32 0x00001000 /* >= 32 */
+ #define JZ_TTRG_64 0x00002000 /* >= 64 */
+ #define JZ_TTRG_96 0x00003000 /* >= 96 */
+ #define JZ_IO_ABORT 0x00000800
+ #define JZ_BUS_WIDTH_M 0x00000600
+ #define JZ_BUS_1BIT 0x00000000
+ #define JZ_BUS_4BIT 0x00000400
+ #define JZ_BUS_8BIT 0x00000600
+ #define JZ_INIT 0x00000080 /* send 80 clk init before cmd */
+ #define JZ_BUSY 0x00000040
+ #define JZ_STREAM 0x00000020
+ #define JZ_WRITE 0x00000010 /* read otherwise */
+ #define JZ_DATA_EN 0x00000008
+ #define JZ_RESPONSE_M 0x00000007 /* response format */
+ #define JZ_RES_NONE 0x00000000
+ #define JZ_RES_R1 0x00000001 /* R1 and R1b */
+ #define JZ_RES_R2 0x00000002
+ #define JZ_RES_R3 0x00000003
+ #define JZ_RES_R4 0x00000004
+ #define JZ_RES_R5 0x00000005
+ #define JZ_RES_R6 0x00000006
+ #define JZ_RES_R7 0x00000007
+#define JZ_MSC_RESTO 0x10 /* 16bit response timeout in MSC_CLK */
+#define JZ_MSC_RDTO 0x14 /* 32bit read timeout in MSC_CLK */
+#define JZ_MSC_BLKLEN 0x18 /* 16bit block length */
+#define JZ_MSC_NOB 0x1c /* 16bit block counter */
+#define JZ_MSC_SNOB 0x20 /* 16bit successful block counter */
+#define JZ_MSC_IMASK 0x24 /* interrupt mask */
+ #define JZ_INT_AUTO_CMD23_DONE 0x40000000
+ #define JZ_INT_SVS 0x20000000
+ #define JZ_INT_PIN_LEVEL_M 0x1f000000
+ #define JZ_INT_BCE 0x00100000
+ #define JZ_INT_BDE 0x00080000
+ #define JZ_INT_BAE 0x00040000
+ #define JZ_INT_BAR 0x00020000
+ #define JZ_INT_DMAEND 0x00010000
+ #define JZ_INT_AUTO_CMD12_DONE 0x00008000
+ #define JZ_INT_DATA_FIFO_FULL 0x00004000
+ #define JZ_INT_DATA_FIFO_EMPTY 0x00002000
+ #define JZ_INT_CRC_RES_ERR 0x00001000
+ #define JZ_INT_CRC_READ_ERR 0x00000800
+ #define JZ_INT_CRC_WRITE_ERR 0x00000400
+ #define JZ_INT_TIMEOUT_RES 0x00000200
+ #define JZ_INT_TIMEOUT_READ 0x00000100
+ #define JZ_INT_SDIO 0x00000080
+ #define JZ_INT_TXFIFO_WR_REQ 0x00000040
+ #define JZ_INT_RXFIFO_RD_REQ 0x00000020
+ #define JZ_INT_END_CMD_RES 0x00000004
+ #define JZ_INT_PRG_DONE 0x00000002
+ #define JZ_INT_DATA_TRAN_DONE 0x00000001
+#define JZ_MSC_IFLG 0x28 /* interrupt flags */
+#define JZ_MSC_CMD 0x2c /* 6bit CMD index */
+#define JZ_MSC_ARG 0x30 /* 32bit argument */
+#define JZ_MSC_RES 0x34 /* 8x16bit response data FIFO */
+#define JZ_MSC_RXFIFO 0x38
+#define JZ_MSC_TXFIFO 0x3c
+#define JZ_MSC_LPM 0x40
+ #define JZ_DRV_SEL_M 0xc0000000
+ #define JZ_FALLING_EDGE 0x00000000
+ #define JZ_RISING_1NS 0x40000000 /* 1ns delay */
+ #define JZ_RISING_4 0x80000000 /* 1/4 MSC_CLK delay */
+ #define JZ_SMP_SEL 0x20000000 /* 1 - rising edge */
+ #define JZ_LPM 0x00000001 /* low power mode */
+#define JZ_MSC_DMAC 0x44
+ #define JZ_MODE_SEL 0x80 /* 1 - specify transfer length */
+ #define JZ_AOFST_M 0x60 /* address offset in bytes */
+ #define JZ_AOFST_S 6 /* addrress offset shift */
+ #define JZ_ALIGNEN 0x10 /* allow non-32bit-aligned transfers */
+ #define JZ_INCR_M 0x0c /* burst type */
+ #define JZ_INCR_16 0x00
+ #define JZ_INCR_32 0x04
+ #define JZ_INCR_64 0x08
+ #define JZ_DMASEL 0x02 /* 1 - SoC DMAC, 0 - MSC built-in */
+ #define JZ_DMAEN 0x01 /* enable DMA */
+#define JZ_MSC_DMANDA 0x48 /* next descriptor paddr */
+#define JZ_MSC_DMADA 0x4c /* current descriptor */
+#define JZ_MSC_DMALEN 0x50 /* transfer tength */
+#define JZ_MSC_DMACMD 0x54
+ #define JZ_DMA_IDI_M 0xff000000
+ #define JZ_DMA_ID_M 0x00ff0000
+ #define JZ_DMA_AOFST_M 0x00000600
+ #define JZ_DMA_ALIGN 0x00000100
+ #define JZ_DMA_ENDI 0x00000002
+ #define JZ_DMA_LINK 0x00000001
+#define JZ_MSC_CTRL2 0x58
+ #define JZ_PIP 0x1f000000 /* 1 - intr trigger on high */
+ #define JZ_RST_EN 0x00800000
+ #define JZ_STPRM 0x00000010
+ #define JZ_SVC 0x00000008
+ #define JZ_SMS_M 0x00000007
+ #define JZ_SMS_DEF 0x00000000 /* default speed */
+ #define JZ_SMS_HIGH 0x00000001 /* high speed */
+ #define JZ_SMS_SDR12 0x00000002
+ #define JZ_SMS_SDR25 0x00000003
+ #define JZ_SMS_SDR50 0x00000004
+#define JZ_MSC_RTCNT 0x5c /* RT FIFO count */
+
+/* EFUSE Slave Interface */
+#define JZ_EFUSE 0x134100D0
+#define JZ_EFUCTRL 0x00
+ #define JZ_EFUSE_BANK 0x40000000 /* select upper 4KBit */
+ #define JZ_EFUSE_ADDR_M 0x3fe00000 /* in bytes */
+ #define JZ_EFUSE_ADDR_SHIFT 21
+ #define JZ_EFUSE_SIZE_M 0x001f0000 /* in bytes */
+ #define JZ_EFUSE_SIZE_SHIFT 16
+ #define JZ_EFUSE_PROG 0x00008000 /* enable programming */
+ #define JZ_EFUSE_WRITE 0x00000002 /* write enable */
+ #define JZ_EFUSE_READ 0x00000001 /* read enable */
+#define JZ_EFUCFG 0x04
+ #define JZ_EFUSE_INT_E 0x80000000 /* which IRQ? */
+ #define JZ_EFUSE_RD_ADJ_M 0x00f00000
+ #define JZ_EFUSE_RD_STROBE 0x000f0000
+ #define JZ_EFUSE_WR_ADJUST 0x0000f000
+ #define JZ_EFUSE_WR_STROBE 0x00000fff
+#define JZ_EFUSTATE 0x08
+ #define JZ_EFUSE_GLOBAL_P 0x00008000 /* wr protect bits */
+ #define JZ_EFUSE_CHIPID_P 0x00004000
+ #define JZ_EFUSE_CUSTID_P 0x00002000
+ #define JZ_EFUSE_SECWR_EN 0x00001000
+ #define JZ_EFUSE_PC_P 0x00000800
+ #define JZ_EFUSE_HDMIKEY_P 0x00000400
+ #define JZ_EFUSE_SECKEY_P 0x00000200
+ #define JZ_EFUSE_SECBOOT_EN 0x00000100
+ #define JZ_EFUSE_HDMI_BUSY 0x00000004
+ #define JZ_EFUSE_WR_DONE 0x00000002
+ #define JZ_EFUSE_RD_DONE 0x00000001
+#define JZ_EFUDATA0 0x0C
+#define JZ_EFUDATA1 0x10
+#define JZ_EFUDATA2 0x14
+#define JZ_EFUDATA3 0x18
+#define JZ_EFUDATA4 0x1C
+#define JZ_EFUDATA5 0x20
+#define JZ_EFUDATA6 0x24
+#define JZ_EFUDATA7 0x28
+
+/* NEMC */
+#define JZ_NEMC_BASE 0x13410000
+#define JZ_NEMC_SMCR(n) (0x10 + (n) * 4)
+
+# define JZ_NEMC_SMCR_SMT_SHIFT 0
+# define JZ_NEMC_SMCR_SMT_WIDTH 1
+# define JZ_NEMC_SMCR_SMT_MASK (((1 << JZ_NEMC_SMCR_SMT_WIDTH) - 1) << JZ_NEMC_SMCR_SMT_SHIFT)
+# define JZ_NEMC_SMCR_SMT_NORMAL (0 << JZ_NEMC_SMCR_SMT_SHIFT)
+# define JZ_NEMC_SMCR_SMT_BROM (1 << JZ_NEMC_SMCR_SMT_SHIFT)
+
+# define JZ_NEMC_SMCR_BL_SHIFT 1
+# define JZ_NEMC_SMCR_BL_WIDTH 2
+# define JZ_NEMC_SMCR_BL_MASK (((1 << JZ_NEMC_SMCR_BL_WIDTH) - 1) << JZ_NEMC_SMCR_BL_SHIFT)
+# define JZ_NEMC_SMCR_BL(n) (((n) << JZ_NEMC_SMCR_BL_SHIFT)
+
+# define JZ_NEMC_SMCR_BW_SHIFT 6
+# define JZ_NEMC_SMCR_BW_WIDTH 2
+# define JZ_NEMC_SMCR_BW_MASK (((1 << JZ_NEMC_SMCR_BW_WIDTH) - 1) << JZ_NEMC_SMCR_BW_SHIFT)
+# define JZ_NEMC_SMCR_BW_8 (0 << JZ_NEMC_SMCR_BW_SHIFT)
+
+# define JZ_NEMC_SMCR_TAS_SHIFT 8
+# define JZ_NEMC_SMCR_TAS_WIDTH 4
+# define JZ_NEMC_SMCR_TAS_MASK (((1 << JZ_NEMC_SMCR_TAS_WIDTH) - 1) << JZ_NEMC_SMCR_TAS_SHIFT)
+
+# define JZ_NEMC_SMCR_TAH_SHIFT 12
+# define JZ_NEMC_SMCR_TAH_WIDTH 4
+# define JZ_NEMC_SMCR_TAH_MASK (((1 << JZ_NEMC_SMCR_TAH_WIDTH) - 1) << JZ_NEMC_SMCR_TAH_SHIFT)
+
+# define JZ_NEMC_SMCR_TBP_SHIFT 16
+# define JZ_NEMC_SMCR_TBP_WIDTH 4
+# define JZ_NEMC_SMCR_TBP_MASK (((1 << JZ_NEMC_SMCR_TBP_WIDTH) - 1) << JZ_NEMC_SMCR_TBP_SHIFT)
+
+# define JZ_NEMC_SMCR_TAW_SHIFT 20
+# define JZ_NEMC_SMCR_TAW_WIDTH 4
+# define JZ_NEMC_SMCR_TAW_MASK (((1 << JZ_NEMC_SMCR_TAW_WIDTH) - 1) << JZ_NEMC_SMCR_TAW_SHIFT)
+
+# define JZ_NEMC_SMCR_STRV_SHIFT 24
+# define JZ_NEMC_SMCR_STRV_WIDTH 4
+# define JZ_NEMC_SMCR_STRV_MASK (((1 << JZ_NEMC_SMCR_STRV_WIDTH) - 1) << JZ_NEMC_SMCR_STRV_SHIFT)
+
+#define JZ_NEMC_SACR(n) (0x30 + (n) * 4)
+
+# define JZ_NEMC_SACR_MASK_SHIFT 0
+# define JZ_NEMC_SACR_MASK_WIDTH 8
+# define JZ_NEMC_SACR_MASK_MASK (((1 << JZ_NEMC_SACR_MASK_WIDTH) - 1) << JZ_NEMC_SACR_MASK_SHIFT)
+
+# define JZ_NEMC_SACR_ADDR_SHIFT 0
+# define JZ_NEMC_SACR_ADDR_WIDTH 8
+# define JZ_NEMC_SACR_ADDR_MASK (((1 << JZ_NEMC_SACR_ADDR_WIDTH) - 1) << JZ_NEMC_SACR_ADDR_SHIFT)
+
+#define JC_NEMC_NFSCR 0x50
+
+#endif /* JZ4780_REGS_H */
diff --git a/sys/mips/ingenic/jz4780_timer.c b/sys/mips/ingenic/jz4780_timer.c
new file mode 100644
index 000000000000..e3cd04f068b3
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_timer.c
@@ -0,0 +1,337 @@
+/*-
+ * Copyright 2013-2015 Alexander Kabaev <kan@FreeBSD.org>
+ * Copyright 2013-2015 John Wehle <john@feith.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timetc.h>
+#include <sys/timeet.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/hwfunc.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <mips/ingenic/jz4780_regs.h>
+
+struct jz4780_timer_softc {
+ device_t dev;
+ struct resource * res[4];
+ void * ih_cookie;
+ struct eventtimer et;
+ struct timecounter tc;
+};
+
+static struct resource_spec jz4780_timer_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE }, /* OST */
+ { SYS_RES_IRQ, 1, RF_ACTIVE }, /* TC5 */
+ { SYS_RES_IRQ, 2, RF_ACTIVE }, /* TC0-4,6 */
+ { -1, 0 }
+};
+
+/*
+ * devclass_get_device / device_get_softc could be used
+ * to dynamically locate this, however the timers are a
+ * required device which can't be unloaded so there's
+ * no need for the overhead.
+ */
+static struct jz4780_timer_softc *jz4780_timer_sc = NULL;
+
+#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
+#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
+
+static unsigned
+jz4780_get_timecount(struct timecounter *tc)
+{
+ struct jz4780_timer_softc *sc =
+ (struct jz4780_timer_softc *)tc->tc_priv;
+
+ return CSR_READ_4(sc, JZ_OST_CNT_LO);
+}
+
+static int
+jz4780_hardclock(void *arg)
+{
+ struct jz4780_timer_softc *sc = (struct jz4780_timer_softc *)arg;
+
+ CSR_WRITE_4(sc, JZ_TC_TFCR, TFR_FFLAG5);
+ CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5);
+
+ if (sc->et.et_active)
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+
+ return (FILTER_HANDLED);
+}
+
+static int
+jz4780_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct jz4780_timer_softc *sc =
+ (struct jz4780_timer_softc *)et->et_priv;
+ uint32_t ticks;
+
+ ticks = (first * et->et_frequency) / SBT_1S;
+ if (ticks == 0)
+ return (EINVAL);
+
+ CSR_WRITE_4(sc, JZ_TC_TDFR(5), ticks);
+ CSR_WRITE_4(sc, JZ_TC_TCNT(5), 0);
+ CSR_WRITE_4(sc, JZ_TC_TESR, TESR_TCST5);
+
+ return (0);
+}
+
+static int
+jz4780_timer_stop(struct eventtimer *et)
+{
+ struct jz4780_timer_softc *sc =
+ (struct jz4780_timer_softc *)et->et_priv;
+
+ CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5);
+ return (0);
+}
+
+static int
+jz4780_timer_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-tcu"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Ingenic JZ4780 timer");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+jz4780_timer_attach(device_t dev)
+{
+ struct jz4780_timer_softc *sc = device_get_softc(dev);
+ pcell_t counter_freq;
+ clk_t clk;
+
+ /* There should be exactly one instance. */
+ if (jz4780_timer_sc != NULL)
+ return (ENXIO);
+
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, jz4780_timer_spec, sc->res)) {
+ device_printf(dev, "can not allocate resources for device\n");
+ return (ENXIO);
+ }
+
+ counter_freq = 0;
+ if (clk_get_by_name(dev, "ext", &clk) == 0) {
+ uint64_t clk_freq;
+
+ if (clk_get_freq(clk, &clk_freq) == 0)
+ counter_freq = (uint32_t)clk_freq / 16;
+ clk_release(clk);
+ }
+ if (counter_freq == 0) {
+ device_printf(dev, "unable to determine ext clock frequency\n");
+ /* Hardcode value we 'know' is correct */
+ counter_freq = 48000000 / 16;
+ }
+
+ /*
+ * Disable the timers, select the input for each timer,
+ * clear and then start OST.
+ */
+
+ /* Stop OST, if it happens to be running */
+ CSR_WRITE_4(sc, JZ_TC_TECR, TESR_OST);
+ /* Stop all other channels as well */
+ CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST0 | TESR_TCST1 | TESR_TCST2 |
+ TESR_TCST3 | TESR_TCST4 | TESR_TCST5 | TESR_TCST6 | TESR_TCST3);
+ /* Clear detect mask flags */
+ CSR_WRITE_4(sc, JZ_TC_TFCR, 0xFFFFFFFF);
+ /* Mask all interrupts */
+ CSR_WRITE_4(sc, JZ_TC_TMSR, 0xFFFFFFFF);
+
+ /* Init counter with known data */
+ CSR_WRITE_4(sc, JZ_OST_CTRL, 0);
+ CSR_WRITE_4(sc, JZ_OST_CNT_LO, 0);
+ CSR_WRITE_4(sc, JZ_OST_CNT_HI, 0);
+ CSR_WRITE_4(sc, JZ_OST_DATA, 0xffffffff);
+
+ /* Configure counter for external clock */
+ CSR_WRITE_4(sc, JZ_OST_CTRL, OSTC_EXT_EN | OSTC_MODE | OSTC_DIV_16);
+
+ /* Start the counter again */
+ CSR_WRITE_4(sc, JZ_TC_TESR, TESR_OST);
+
+ /* Configure TCU channel 5 similarly to OST and leave it disabled */
+ CSR_WRITE_4(sc, JZ_TC_TCSR(5), TCSR_EXT_EN | TCSR_DIV_16);
+ CSR_WRITE_4(sc, JZ_TC_TMCR, TMR_FMASK(5));
+
+ if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_CLK,
+ jz4780_hardclock, NULL, sc, &sc->ih_cookie)) {
+ device_printf(dev, "could not setup interrupt handler\n");
+ bus_release_resources(dev, jz4780_timer_spec, sc->res);
+ return (ENXIO);
+ }
+
+ sc->et.et_name = "JZ4780 TCU5";
+ sc->et.et_flags = ET_FLAGS_ONESHOT;
+ sc->et.et_frequency = counter_freq;
+ sc->et.et_quality = 1000;
+ sc->et.et_min_period = (0x00000002LLU * SBT_1S) / sc->et.et_frequency;
+ sc->et.et_max_period = (0x0000fffeLLU * SBT_1S) / sc->et.et_frequency;
+ sc->et.et_start = jz4780_timer_start;
+ sc->et.et_stop = jz4780_timer_stop;
+ sc->et.et_priv = sc;
+
+ et_register(&sc->et);
+
+ sc->tc.tc_get_timecount = jz4780_get_timecount;
+ sc->tc.tc_name = "JZ4780 OST";
+ sc->tc.tc_frequency = counter_freq;
+ sc->tc.tc_counter_mask = ~0u;
+ sc->tc.tc_quality = 1000;
+ sc->tc.tc_priv = sc;
+
+ tc_init(&sc->tc);
+
+ /* Now when tc is initialized, allow DELAY to find it */
+ jz4780_timer_sc = sc;
+
+ return (0);
+}
+
+static int
+jz4780_timer_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static device_method_t jz4780_timer_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_timer_probe),
+ DEVMETHOD(device_attach, jz4780_timer_attach),
+ DEVMETHOD(device_detach, jz4780_timer_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t jz4780_timer_driver = {
+ "timer",
+ jz4780_timer_methods,
+ sizeof(struct jz4780_timer_softc),
+};
+
+static devclass_t jz4780_timer_devclass;
+
+EARLY_DRIVER_MODULE(timer, simplebus, jz4780_timer_driver,
+ jz4780_timer_devclass, 0, 0, BUS_PASS_TIMER);
+
+void
+DELAY(int usec)
+{
+ uint32_t counter;
+ uint32_t delta, now, previous, remaining;
+
+ /* Timer has not yet been initialized */
+ if (jz4780_timer_sc == NULL) {
+ for (; usec > 0; usec--)
+ for (counter = 200; counter > 0; counter--) {
+ /* Prevent gcc from optimizing out the loop */
+ mips_rd_cause();
+ }
+ return;
+ }
+
+ /*
+ * Some of the other timers in the source tree do this calculation as:
+ *
+ * usec * ((sc->tc.tc_frequency / 1000000) + 1)
+ *
+ * which gives a fairly pessimistic result when tc_frequency is an exact
+ * multiple of 1000000. Given the data type and typical values for
+ * tc_frequency adding 999999 shouldn't overflow.
+ */
+ remaining = usec * ((jz4780_timer_sc->tc.tc_frequency + 999999) /
+ 1000000);
+
+ /*
+ * We add one since the first iteration may catch the counter just
+ * as it is changing.
+ */
+ remaining += 1;
+
+ previous = jz4780_get_timecount(&jz4780_timer_sc->tc);
+
+ for ( ; ; ) {
+ now = jz4780_get_timecount(&jz4780_timer_sc->tc);
+
+ /*
+ * If the timer has rolled over, then we have the case:
+ *
+ * if (previous > now) {
+ * delta = (0 - previous) + now
+ * }
+ *
+ * which is really no different then the normal case.
+ * Both cases are simply:
+ *
+ * delta = now - previous.
+ */
+ delta = now - previous;
+
+ if (delta >= remaining)
+ break;
+
+ previous = now;
+ remaining -= delta;
+ }
+}
+
+void
+platform_initclocks(void)
+{
+
+}
+
diff --git a/sys/mips/ingenic/jz4780_uart.c b/sys/mips/ingenic/jz4780_uart.c
new file mode 100644
index 000000000000..cf2e8a73fbca
--- /dev/null
+++ b/sys/mips/ingenic/jz4780_uart.c
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 2013 Ian Lepore
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <machine/bus.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.h>
+#include <dev/uart/uart_bus.h>
+#include <dev/uart/uart_dev_ns8250.h>
+#include <dev/ic/ns16550.h>
+#include "uart_if.h"
+
+/*
+ * High-level UART interface.
+ */
+struct jz4780_uart_softc {
+ struct ns8250_softc ns8250_base;
+ clk_t clk_mod;
+ clk_t clk_baud;
+};
+
+static int
+jz4780_bus_attach(struct uart_softc *sc)
+{
+ struct ns8250_softc *ns8250;
+ struct uart_bas *bas;
+ int rv;
+
+ ns8250 = (struct ns8250_softc *)sc;
+ bas = &sc->sc_bas;
+
+ rv = ns8250_bus_attach(sc);
+ if (rv != 0)
+ return (0);
+
+ /* Configure uart to use extra IER_RXTMOUT bit */
+ ns8250->ier_rxbits = IER_RXTMOUT | IER_EMSC | IER_ERLS | IER_ERXRDY;
+ ns8250->ier_mask = ~(ns8250->ier_rxbits);
+ ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask;
+ ns8250->ier |= ns8250->ier_rxbits;
+ uart_setreg(bas, REG_IER, ns8250->ier);
+ uart_barrier(bas);
+ return (0);
+}
+
+static kobj_method_t jz4780_uart_methods[] = {
+ KOBJMETHOD(uart_probe, ns8250_bus_probe),
+ KOBJMETHOD(uart_attach, jz4780_bus_attach),
+ KOBJMETHOD(uart_detach, ns8250_bus_detach),
+ KOBJMETHOD(uart_flush, ns8250_bus_flush),
+ KOBJMETHOD(uart_getsig, ns8250_bus_getsig),
+ KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl),
+ KOBJMETHOD(uart_ipend, ns8250_bus_ipend),
+ KOBJMETHOD(uart_param, ns8250_bus_param),
+ KOBJMETHOD(uart_receive, ns8250_bus_receive),
+ KOBJMETHOD(uart_setsig, ns8250_bus_setsig),
+ KOBJMETHOD(uart_transmit, ns8250_bus_transmit),
+ KOBJMETHOD(uart_grab, ns8250_bus_grab),
+ KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab),
+ KOBJMETHOD_END
+};
+
+static struct uart_class jz4780_uart_class = {
+ "jz4780_uart_class",
+ jz4780_uart_methods,
+ sizeof(struct jz4780_uart_softc),
+ .uc_ops = &uart_ns8250_ops,
+ .uc_range = 8,
+ .uc_rclk = 0,
+};
+
+/* Compatible devices. */
+static struct ofw_compat_data compat_data[] = {
+ {"ingenic,jz4780-uart", (uintptr_t)&jz4780_uart_class},
+ {NULL, (uintptr_t)NULL},
+};
+
+UART_FDT_CLASS(compat_data);
+
+/*
+ * UART Driver interface.
+ */
+static int
+jz4780_uart_get_shift(device_t dev)
+{
+ phandle_t node;
+ pcell_t shift;
+
+ node = ofw_bus_get_node(dev);
+ if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0)
+ shift = 2;
+ return ((int)shift);
+}
+
+static int
+jz4780_uart_probe(device_t dev)
+{
+ struct jz4780_uart_softc *sc;
+ uint64_t freq;
+ int shift;
+ int rv;
+ const struct ofw_compat_data *cd;
+
+ sc = device_get_softc(dev);
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ cd = ofw_bus_search_compatible(dev, compat_data);
+ if (cd->ocd_data == 0)
+ return (ENXIO);
+
+ /* Figure out clock setup */
+ rv = clk_get_by_ofw_name(dev, 0, "module", &sc->clk_mod);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get UART clock: %d\n", rv);
+ return (ENXIO);
+ }
+ rv = clk_enable(sc->clk_mod);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable UART clock: %d\n", rv);
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(dev, 0, "baud", &sc->clk_baud);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get UART clock: %d\n", rv);
+ return (ENXIO);
+ }
+ rv = clk_enable(sc->clk_baud);
+ if (rv != 0) {
+ device_printf(dev, "Cannot enable UART clock: %d\n", rv);
+ return (ENXIO);
+ }
+ rv = clk_get_freq(sc->clk_baud, &freq);
+ if (rv != 0) {
+ device_printf(dev, "Cannot determine UART clock frequency: %d\n", rv);
+ return (ENXIO);
+ }
+
+ if (bootverbose)
+ device_printf(dev, "got UART clock: %lld\n", freq);
+ sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data;
+ shift = jz4780_uart_get_shift(dev);
+ return (uart_bus_probe(dev, shift, (int)freq, 0, 0));
+}
+
+static int
+jz4780_uart_detach(device_t dev)
+{
+ struct jz4780_uart_softc *sc;
+ int rv;
+
+ rv = uart_bus_detach(dev);
+ if (rv != 0)
+ return (rv);
+
+ sc = device_get_softc(dev);
+ if (sc->clk_mod != NULL) {
+ clk_release(sc->clk_mod);
+ }
+ if (sc->clk_baud != NULL) {
+ clk_release(sc->clk_baud);
+ }
+ return (0);
+}
+
+static device_method_t jz4780_uart_bus_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, jz4780_uart_probe),
+ DEVMETHOD(device_attach, uart_bus_attach),
+ DEVMETHOD(device_detach, jz4780_uart_detach),
+ { 0, 0 }
+};
+
+static driver_t jz4780_uart_driver = {
+ uart_driver_name,
+ jz4780_uart_bus_methods,
+ sizeof(struct jz4780_uart_softc),
+};
+
+DRIVER_MODULE(jz4780_uart, simplebus, jz4780_uart_driver, uart_devclass,
+ 0, 0);