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 #include #include -#include #include #include -#include #include -#include #include #include #include @@ -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 + * 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 +#include + +/* 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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* 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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* 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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/********************************************************************** + * 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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include "clkdev_if.h" + +#include + +/********************************************************************** + * 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 + * 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 + * 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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +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 . + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +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 . + * 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 +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#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 + * 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 +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#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 +# 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 + +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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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 +__FBSDID("$FreeBSD$"); + +#include "opt_ddb.h" +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FDT +#include +#include +#endif + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#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 + * Copyright (c) 2004-2010 Juli Mallett + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 +#include +#include +#include + +#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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +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 + * Copyright (c) 2009, Oleksandr Tymoshenko + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +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 + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#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 + * 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 + +/* 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 + * Copyright 2013-2015 John Wehle + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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);