Bring in support for Ingenic XBurst JZ4780 and

X1000 systems on chips.

Imgtec CI20 and Ingenic CANNA boards supported.

Submitted by:	Alexander Kabaev <kan@FreeBSD.org>
Reviewed by:	Ruslan Bukin <br@FreeBSD.org>
Sponsored by:	DARPA, AFRL
This commit is contained in:
Ruslan Bukin 2016-11-19 17:46:18 +00:00
parent 9caeb4305d
commit 9a8f61fb5b
35 changed files with 8120 additions and 30 deletions

View File

@ -48,13 +48,10 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/capsicum.h>
#include <sys/disklabel.h>
#include <sys/filio.h>
#include <sys/mtio.h>
#include <assert.h>
#include <capsicum_helpers.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
@ -95,10 +92,6 @@ main(int argc __unused, char *argv[])
jcl(argv);
setup();
caph_cache_catpages();
if (cap_enter() == -1 && errno != ENOSYS)
err(1, "unable to enter capability mode");
(void)signal(SIGINFO, siginfo_handler);
(void)signal(SIGINT, terminate);
@ -132,8 +125,6 @@ static void
setup(void)
{
u_int cnt;
cap_rights_t rights;
unsigned long cmds[] = { FIODTYPE, MTIOCTOP };
if (in.name == NULL) {
in.name = "stdin";
@ -142,20 +133,13 @@ setup(void)
in.fd = open(in.name, O_RDONLY, 0);
if (in.fd == -1)
err(1, "%s", in.name);
if (caph_limit_stdin() == -1)
err(1, "unable to limit capability rights");
}
getfdtype(&in);
cap_rights_init(&rights, CAP_READ, CAP_SEEK);
if (cap_rights_limit(in.fd, &rights) == -1 && errno != ENOSYS)
err(1, "unable to limit capability rights");
if (files_cnt > 1 && !(in.flags & ISTAPE))
errx(1, "files is not supported for non-tape devices");
cap_rights_set(&rights, CAP_WRITE, CAP_FTRUNCATE, CAP_IOCTL);
if (out.name == NULL) {
/* No way to check for read access here. */
out.fd = STDOUT_FILENO;
@ -172,27 +156,13 @@ setup(void)
if (out.fd == -1) {
out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
out.flags |= NOREAD;
cap_rights_clear(&rights, CAP_READ);
}
if (out.fd == -1)
err(1, "%s", out.name);
if (caph_limit_stdout() == -1)
err(1, "unable to limit capability rights");
}
getfdtype(&out);
if (cap_rights_limit(out.fd, &rights) == -1 && errno != ENOSYS)
err(1, "unable to limit capability rights");
if (cap_ioctls_limit(out.fd, cmds, nitems(cmds)) == -1 &&
errno != ENOSYS)
err(1, "unable to limit capability rights");
if (in.fd != STDERR_FILENO && out.fd != STDERR_FILENO) {
if (caph_limit_stderr() == -1)
err(1, "unable to limit capability rights");
}
/*
* Allocate space for the input and output buffers. If not doing
* record oriented I/O, only need a single buffer.

29
sys/mips/conf/CANNA Normal file
View File

@ -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

31
sys/mips/conf/CI20 Normal file
View File

@ -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

92
sys/mips/conf/JZ4780 Normal file
View File

@ -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

View File

@ -0,0 +1,2 @@
# $FreeBSD$
# device.hints

89
sys/mips/conf/X1000 Normal file
View File

@ -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

View File

@ -0,0 +1,2 @@
# $FreeBSD$
# device.hints

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,93 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _MIPS_INGENIC_JZ4780_CLK_H
#define _MIPS_INGENIC_JZ4780_CLK_H
#include <dev/extres/clk/clk.h>
#include <dev/extres/clk/clk_gate.h>
/* Convenience bitfiled manipulation macros */
#define REG_MSK(field) (((1u << field ## _WIDTH) - 1) << field ##_SHIFT)
#define REG_VAL(field, val) ((val) << field ##_SHIFT)
#define REG_CLR(reg, field) ((reg) & ~REG_MSK(field))
#define REG_GET(reg, field) (((reg) & REG_MSK(field)) >> field ##_SHIFT)
#define REG_SET(reg, field, val) (REG_CLR(reg, field) | REG_VAL(field, val))
/* Common clock macros */
#define CLK_LOCK(_sc) mtx_lock((_sc)->clk_mtx)
#define CLK_UNLOCK(_sc) mtx_unlock((_sc)->clk_mtx)
#define CLK_WR_4(_sc, off, val) bus_write_4((_sc)->clk_res, (off), (val))
#define CLK_RD_4(_sc, off) bus_read_4((_sc)->clk_res, (off))
struct jz4780_clk_mux_descr {
uint16_t mux_reg;
uint16_t mux_shift: 5;
uint16_t mux_bits: 5;
uint16_t mux_map: 4; /* Map into mux space */
};
struct jz4780_clk_div_descr {
uint16_t div_reg;
uint16_t div_shift: 5;
uint16_t div_bits: 5;
uint16_t div_lg: 5;
int div_ce_bit: 6; /* -1, if CE bit is not present */
int div_st_bit: 6; /* Can be negative */
int div_busy_bit: 6; /* Can be negative */
};
struct jz4780_clk_descr {
uint16_t clk_id: 6;
uint16_t clk_type: 3;
int clk_gate_bit: 7; /* Can be negative */
struct jz4780_clk_mux_descr clk_mux;
struct jz4780_clk_div_descr clk_div;
const char *clk_name;
const char *clk_pnames[4];
};
/* clk_type bits */
#define CLK_MASK_GATE 0x01
#define CLK_MASK_DIV 0x02
#define CLK_MASK_MUX 0x04
extern int jz4780_clk_gen_register(struct clkdom *clkdom,
const struct jz4780_clk_descr *descr, struct mtx *dev_mtx,
struct resource *mem_res);
extern int jz4780_clk_pll_register(struct clkdom *clkdom,
struct clknode_init_def *clkdef, struct mtx *dev_mtx,
struct resource *mem_res, uint32_t mem_reg);
extern int jz4780_clk_otg_register(struct clkdom *clkdom,
struct clknode_init_def *clkdef, struct mtx *dev_mtx,
struct resource *mem_res);
#endif /* _MIPS_INGENIC_JZ4780_CLK_PLL_H */

View File

@ -0,0 +1,317 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Ingenic JZ4780 generic CGU clock driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <machine/bus.h>
#include <mips/ingenic/jz4780_clk.h>
#include <mips/ingenic/jz4780_regs.h>
/* JZ4780 generic mux and div clocks implementation */
static int jz4780_clk_gen_init(struct clknode *clk, device_t dev);
static int jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq);
static int jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
uint64_t *fout, int flags, int *stop);
static int jz4780_clk_gen_set_gate(struct clknode *clk, bool enable);
static int jz4780_clk_gen_set_mux(struct clknode *clk, int src);
struct jz4780_clk_gen_sc {
struct mtx *clk_mtx;
struct resource *clk_res;
int clk_reg;
const struct jz4780_clk_descr *clk_descr;
};
/*
* JZ4780 clock PLL clock methods
*/
static clknode_method_t jz4780_clk_gen_methods[] = {
CLKNODEMETHOD(clknode_init, jz4780_clk_gen_init),
CLKNODEMETHOD(clknode_set_gate, jz4780_clk_gen_set_gate),
CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_gen_recalc_freq),
CLKNODEMETHOD(clknode_set_freq, jz4780_clk_gen_set_freq),
CLKNODEMETHOD(clknode_set_mux, jz4780_clk_gen_set_mux),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_gen_class, jz4780_clk_gen_methods,
sizeof(struct jz4780_clk_gen_sc), clknode_class);
static inline unsigned
mux_to_reg(unsigned src, unsigned map)
{
unsigned ret, bit;
bit = (1u << 3);
for (ret = 0; bit; ret++, bit >>= 1) {
if (map & bit) {
if (src-- == 0)
return (ret);
}
}
panic("mux_to_reg");
}
static inline unsigned
reg_to_mux(unsigned reg, unsigned map)
{
unsigned ret, bit;
bit = (1u << 3);
for (ret = 0; reg; reg--, bit >>= 1)
if (map & bit)
ret++;
return (ret);
}
static int
jz4780_clk_gen_init(struct clknode *clk, device_t dev)
{
struct jz4780_clk_gen_sc *sc;
uint32_t reg, msk, parent_idx;
sc = clknode_get_softc(clk);
CLK_LOCK(sc);
/* Figure our parent out */
if (sc->clk_descr->clk_type & CLK_MASK_MUX) {
msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
reg = (reg >> sc->clk_descr->clk_mux.mux_shift) & msk;
parent_idx = reg_to_mux(reg, sc->clk_descr->clk_mux.mux_map);
} else
parent_idx = 0;
CLK_UNLOCK(sc);
clknode_init_parent_idx(clk, parent_idx);
return (0);
}
static int
jz4780_clk_gen_recalc_freq(struct clknode *clk, uint64_t *freq)
{
struct jz4780_clk_gen_sc *sc;
uint32_t reg;
sc = clknode_get_softc(clk);
/* Calculate divisor frequency */
if (sc->clk_descr->clk_type & CLK_MASK_DIV) {
uint32_t msk;
msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
reg = (reg >> sc->clk_descr->clk_div.div_shift) & msk;
reg = (reg + 1) << sc->clk_descr->clk_div.div_lg;
*freq /= reg;
}
return (0);
}
#define DIV_TIMEOUT 100
static int
jz4780_clk_gen_set_freq(struct clknode *clk, uint64_t fin,
uint64_t *fout, int flags, int *stop)
{
struct jz4780_clk_gen_sc *sc;
uint64_t _fout;
uint32_t divider, div_reg, div_msk, reg;
int rv;
sc = clknode_get_softc(clk);
divider = fin / *fout;
/* Adjust for divider multiplier */
div_reg = divider >> sc->clk_descr->clk_div.div_lg;
divider = div_reg << sc->clk_descr->clk_div.div_lg;
_fout = fin / divider;
/* Rounding */
if ((flags & CLK_SET_ROUND_UP) && (*fout < _fout))
div_reg--;
else if ((flags & CLK_SET_ROUND_DOWN) && (*fout > _fout))
div_reg++;
if (div_reg == 0)
div_reg = 1;
div_msk = (1u << sc->clk_descr->clk_div.div_bits) - 1;
*stop = 1;
if (div_reg > div_msk + 1) {
*stop = 0;
div_reg = div_msk;
}
divider = (div_reg << sc->clk_descr->clk_div.div_lg);
div_reg--;
if ((flags & CLK_SET_DRYRUN) != 0) {
if (*stop != 0 && *fout != fin / divider &&
(flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0)
return (ERANGE);
*fout = fin / divider;
return (0);
}
CLK_LOCK(sc);
/* Apply the new divider value */
reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
reg &= ~(div_msk << sc->clk_descr->clk_div.div_shift);
reg |= (div_reg << sc->clk_descr->clk_div.div_shift);
/* Set the change enable bit, it present */
if (sc->clk_descr->clk_div.div_ce_bit >= 0)
reg |= (1u << sc->clk_descr->clk_div.div_ce_bit);
/* Clear stop bit, it present */
if (sc->clk_descr->clk_div.div_st_bit >= 0)
reg &= ~(1u << sc->clk_descr->clk_div.div_st_bit);
/* Initiate the change */
CLK_WR_4(sc, sc->clk_descr->clk_div.div_reg, reg);
/* Wait for busy bit to clear indicating the change is complete */
rv = 0;
if (sc->clk_descr->clk_div.div_busy_bit >= 0) {
int i;
for (i = 0; i < DIV_TIMEOUT; i++) {
reg = CLK_RD_4(sc, sc->clk_descr->clk_div.div_reg);
if (!(reg & (1u << sc->clk_descr->clk_div.div_busy_bit)))
break;
DELAY(1000);
}
if (i == DIV_TIMEOUT)
rv = ETIMEDOUT;
}
CLK_UNLOCK(sc);
*fout = fin / divider;
return (rv);
}
static int
jz4780_clk_gen_set_mux(struct clknode *clk, int src)
{
struct jz4780_clk_gen_sc *sc;
uint32_t reg, msk;
sc = clknode_get_softc(clk);
/* Only mux nodes are capable of being reparented */
if (!(sc->clk_descr->clk_type & CLK_MASK_MUX))
return (src ? EINVAL : 0);
msk = (1u << sc->clk_descr->clk_mux.mux_bits) - 1;
src = mux_to_reg(src & msk, sc->clk_descr->clk_mux.mux_map);
CLK_LOCK(sc);
reg = CLK_RD_4(sc, sc->clk_descr->clk_mux.mux_reg);
reg &= ~(msk << sc->clk_descr->clk_mux.mux_shift);
reg |= (src << sc->clk_descr->clk_mux.mux_shift);
CLK_WR_4(sc, sc->clk_descr->clk_mux.mux_reg, reg);
CLK_UNLOCK(sc);
return (0);
}
static int
jz4780_clk_gen_set_gate(struct clknode *clk, bool enable)
{
struct jz4780_clk_gen_sc *sc;
uint32_t off, reg, bit;
sc = clknode_get_softc(clk);
/* Check is clock can be gated */
if (sc->clk_descr->clk_gate_bit < 0)
return 0;
bit = sc->clk_descr->clk_gate_bit;
if (bit < 32) {
off = JZ_CLKGR0;
} else {
off = JZ_CLKGR1;
bit -= 32;
}
CLK_LOCK(sc);
reg = CLK_RD_4(sc, off);
if (enable)
reg &= ~(1u << bit);
else
reg |= (1u << bit);
CLK_WR_4(sc, off, reg);
CLK_UNLOCK(sc);
return (0);
}
int jz4780_clk_gen_register(struct clkdom *clkdom,
const struct jz4780_clk_descr *descr, struct mtx *dev_mtx,
struct resource *mem_res)
{
struct clknode_init_def clkdef;
struct clknode *clk;
struct jz4780_clk_gen_sc *sc;
clkdef.id = descr->clk_id;
clkdef.name = __DECONST(char *, descr->clk_name);
/* Silly const games to work around API deficiency */
clkdef.parent_names = (const char **)(uintptr_t)&descr->clk_pnames[0];
clkdef.flags = CLK_NODE_STATIC_STRINGS;
if (descr->clk_type & CLK_MASK_MUX)
clkdef.parent_cnt = __bitcount16(descr->clk_mux.mux_map);
else
clkdef.parent_cnt = 1;
clk = clknode_create(clkdom, &jz4780_clk_gen_class, &clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->clk_mtx = dev_mtx;
sc->clk_res = mem_res;
sc->clk_descr = descr;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,167 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Ingenic JZ4780 OTG PHY clock driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <machine/bus.h>
#include <mips/ingenic/jz4780_clk.h>
#include <mips/ingenic/jz4780_regs.h>
/* JZ4780 OTG PHY clock */
static int jz4780_clk_otg_init(struct clknode *clk, device_t dev);
static int jz4780_clk_otg_recalc_freq(struct clknode *clk, uint64_t *freq);
static int jz4780_clk_otg_set_freq(struct clknode *clk, uint64_t fin,
uint64_t *fout, int flags, int *stop);
struct jz4780_clk_otg_sc {
struct mtx *clk_mtx;
struct resource *clk_res;
};
/*
* JZ4780 OTG PHY clock methods
*/
static clknode_method_t jz4780_clk_otg_methods[] = {
CLKNODEMETHOD(clknode_init, jz4780_clk_otg_init),
CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_otg_recalc_freq),
CLKNODEMETHOD(clknode_set_freq, jz4780_clk_otg_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_otg_class, jz4780_clk_otg_methods,
sizeof(struct jz4780_clk_otg_sc), clknode_class);
static int
jz4780_clk_otg_init(struct clknode *clk, device_t dev)
{
struct jz4780_clk_otg_sc *sc;
uint32_t reg;
sc = clknode_get_softc(clk);
CLK_LOCK(sc);
/* Force the use fo the core clock */
reg = CLK_RD_4(sc, JZ_USBPCR1);
reg &= ~PCR_REFCLK_M;
reg |= PCR_REFCLK_CORE;
CLK_WR_4(sc, JZ_USBPCR1, reg);
CLK_UNLOCK(sc);
clknode_init_parent_idx(clk, 0);
return (0);
}
static const struct {
uint32_t div_val;
uint32_t freq;
} otg_div_table[] = {
{ PCR_CLK_12, 12000000 },
{ PCR_CLK_192, 19200000 },
{ PCR_CLK_24, 24000000 },
{ PCR_CLK_48, 48000000 }
};
static int
jz4780_clk_otg_recalc_freq(struct clknode *clk, uint64_t *freq)
{
struct jz4780_clk_otg_sc *sc;
uint32_t reg;
int i;
sc = clknode_get_softc(clk);
reg = CLK_RD_4(sc, JZ_USBPCR1);
reg &= PCR_CLK_M;
for (i = 0; i < nitems(otg_div_table); i++)
if (otg_div_table[i].div_val == reg)
*freq = otg_div_table[i].freq;
return (0);
}
static int
jz4780_clk_otg_set_freq(struct clknode *clk, uint64_t fin,
uint64_t *fout, int flags, int *stop)
{
struct jz4780_clk_otg_sc *sc;
uint32_t reg;
int i;
sc = clknode_get_softc(clk);
for (i = 0; i < nitems(otg_div_table) - 1; i++) {
if (*fout < (otg_div_table[i].freq + otg_div_table[i + 1].freq) / 2)
break;
}
*fout = otg_div_table[i].freq;
*stop = 1;
if (flags & CLK_SET_DRYRUN)
return (0);
CLK_LOCK(sc);
reg = CLK_RD_4(sc, JZ_USBPCR1);
/* Set the calculated values */
reg &= ~PCR_CLK_M;
reg |= otg_div_table[i].div_val;
/* Initiate the change */
CLK_WR_4(sc, JZ_USBPCR1, reg);
CLK_UNLOCK(sc);
return (0);
}
int jz4780_clk_otg_register(struct clkdom *clkdom,
struct clknode_init_def *clkdef, struct mtx *dev_mtx,
struct resource *mem_res)
{
struct clknode *clk;
struct jz4780_clk_otg_sc *sc;
clk = clknode_create(clkdom, &jz4780_clk_otg_class, clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->clk_mtx = dev_mtx;
sc->clk_res = mem_res;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,234 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Ingenic JZ4780 CGU driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <machine/bus.h>
#include <mips/ingenic/jz4780_clk.h>
/**********************************************************************
* JZ4780 PLL control register bit fields
**********************************************************************/
#define CGU_PLL_M_SHIFT 19
#define CGU_PLL_M_WIDTH 13
#define CGU_PLL_N_SHIFT 13
#define CGU_PLL_N_WIDTH 6
#define CGU_PLL_OD_SHIFT 9
#define CGU_PLL_OD_WIDTH 4
#define CGU_PLL_LOCK_SHIFT 6
#define CGU_PLL_LOCK_WIDTH 1
#define CGU_PLL_ON_SHIFT 4
#define CGU_PLL_ON_WIDTH 1
#define CGU_PLL_MODE_SHIFT 3
#define CGU_PLL_MODE_WIDTH 1
#define CGU_PLL_BP_SHIFT 1
#define CGU_PLL_BP_WIDTH 1
#define CGU_PLL_EN_SHIFT 0
#define CGU_PLL_EN_WIDTH 1
/* JZ4780 PLL clock */
static int jz4780_clk_pll_init(struct clknode *clk, device_t dev);
static int jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq);
static int jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin,
uint64_t *fout, int flags, int *stop);
struct jz4780_clk_pll_sc {
struct mtx *clk_mtx;
struct resource *clk_res;
uint32_t clk_reg;
};
/*
* JZ4780 PLL clock methods
*/
static clknode_method_t jz4780_clk_pll_methods[] = {
CLKNODEMETHOD(clknode_init, jz4780_clk_pll_init),
CLKNODEMETHOD(clknode_recalc_freq, jz4780_clk_pll_recalc_freq),
CLKNODEMETHOD(clknode_set_freq, jz4780_clk_pll_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(jz4780_clk_pll, jz4780_clk_pll_class, jz4780_clk_pll_methods,
sizeof(struct jz4780_clk_pll_sc), clknode_class);
static int
jz4780_clk_pll_init(struct clknode *clk, device_t dev)
{
struct jz4780_clk_pll_sc *sc;
uint32_t reg;
sc = clknode_get_softc(clk);
CLK_LOCK(sc);
reg = CLK_RD_4(sc, sc->clk_reg);
CLK_WR_4(sc, sc->clk_reg, reg);
CLK_UNLOCK(sc);
clknode_init_parent_idx(clk, 0);
return (0);
}
static int
jz4780_clk_pll_recalc_freq(struct clknode *clk, uint64_t *freq)
{
struct jz4780_clk_pll_sc *sc;
uint32_t reg, m, n, od;
sc = clknode_get_softc(clk);
reg = CLK_RD_4(sc, sc->clk_reg);
/* Check for PLL enabled status */
if (REG_GET(reg, CGU_PLL_EN) == 0) {
*freq = 0;
return 0;
}
/* Return parent frequency if PPL is being bypassed */
if (REG_GET(reg, CGU_PLL_BP) != 0)
return 0;
m = REG_GET(reg, CGU_PLL_M) + 1;
n = REG_GET(reg, CGU_PLL_N) + 1;
od = REG_GET(reg, CGU_PLL_OD) + 1;
/* Sanity check values */
if (m == 0 || n == 0 || od == 0) {
*freq = 0;
return (EINVAL);
}
*freq = ((*freq / n) * m) / od;
return (0);
}
#define MHZ (1000 * 1000)
#define PLL_TIMEOUT 100
static int
jz4780_clk_pll_wait_lock(struct jz4780_clk_pll_sc *sc)
{
int i;
for (i = 0; i < PLL_TIMEOUT; i++) {
if (CLK_RD_4(sc, sc->clk_reg) & REG_VAL(CGU_PLL_LOCK, 1))
return (0);
DELAY(1000);
}
return (ETIMEDOUT);
}
static int
jz4780_clk_pll_set_freq(struct clknode *clk, uint64_t fin,
uint64_t *fout, int flags, int *stop)
{
struct jz4780_clk_pll_sc *sc;
uint32_t reg, m, n, od;
int rv;
sc = clknode_get_softc(clk);
/* Should be able to figure all clocks with m & n only */
od = 1;
m = MIN((uint32_t)(*fout / MHZ), (1u << CGU_PLL_M_WIDTH));
m = MIN(m, 1);
n = MIN((uint32_t)(fin / MHZ), (1u << CGU_PLL_N_WIDTH));
n = MIN(n, 1);
if (flags & CLK_SET_DRYRUN) {
if (((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) &&
(*fout != (((fin / n) * m) / od)))
return (ERANGE);
*fout = ((fin / n) * m) / od;
return (0);
}
CLK_LOCK(sc);
reg = CLK_RD_4(sc, sc->clk_reg);
/* Set the calculated values */
reg = REG_SET(reg, CGU_PLL_M, m - 1);
reg = REG_SET(reg, CGU_PLL_N, n - 1);
reg = REG_SET(reg, CGU_PLL_OD, od - 1);
/* Enable the PLL */
reg = REG_SET(reg, CGU_PLL_EN, 1);
reg = REG_SET(reg, CGU_PLL_BP, 0);
/* Initiate the change */
CLK_WR_4(sc, sc->clk_reg, reg);
/* Wait for PLL to lock */
rv = jz4780_clk_pll_wait_lock(sc);
CLK_UNLOCK(sc);
if (rv != 0)
return (rv);
*fout = ((fin / n) * m) / od;
return (0);
}
int jz4780_clk_pll_register(struct clkdom *clkdom,
struct clknode_init_def *clkdef, struct mtx *dev_mtx,
struct resource *mem_res, uint32_t mem_reg)
{
struct clknode *clk;
struct jz4780_clk_pll_sc *sc;
clk = clknode_create(clkdom, &jz4780_clk_pll_class, clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->clk_mtx = dev_mtx;
sc->clk_res = mem_res;
sc->clk_reg = mem_reg;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,831 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Ingenic JZ4780 CGU driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <mips/ingenic/jz4780_clk.h>
#include <mips/ingenic/jz4780_regs.h>
#include <mips/ingenic/jz4780_clock.h>
#include "clkdev_if.h"
#include <gnu/dts/include/dt-bindings/clock/jz4780-cgu.h>
/**********************************************************************
* JZ4780 CGU clock domain
**********************************************************************/
struct jz4780_clock_softc {
device_t dev;
struct resource *res[1];
struct mtx mtx;
struct clkdom *clkdom;
};
static struct resource_spec jz4780_clock_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
struct jz4780_clk_pll_def {
uint16_t clk_id;
uint16_t clk_reg;
const char *clk_name;
const char *clk_pname[1];
};
#define PLL(_id, cname, pname, reg) { \
.clk_id = _id, \
.clk_reg = reg, \
.clk_name = cname, \
.clk_pname[0] = pname, \
}
struct jz4780_clk_gate_def {
uint16_t clk_id;
uint16_t clk_bit;
const char *clk_name;
const char *clk_pname[1];
};
#define GATE(_id, cname, pname, bit) { \
.clk_id = _id, \
.clk_bit = bit, \
.clk_name = cname, \
.clk_pname[0] = pname, \
}
#define MUX(reg, shift, bits, map) \
.clk_mux.mux_reg = (reg), \
.clk_mux.mux_shift = (shift), \
.clk_mux.mux_bits = (bits), \
.clk_mux.mux_map = (map),
#define NO_MUX
#define DIV(reg, shift, lg, bits, ce, st, bb) \
.clk_div.div_reg = (reg), \
.clk_div.div_shift = (shift), \
.clk_div.div_bits = (bits), \
.clk_div.div_lg = (lg), \
.clk_div.div_ce_bit = (ce), \
.clk_div.div_st_bit = (st), \
.clk_div.div_busy_bit = (bb),
#define NO_DIV \
#define GATEBIT(bit) \
.clk_gate_bit = (bit),
#define NO_GATE \
.clk_gate_bit = (-1),
#define PLIST(pnames...) \
.clk_pnames = { pnames },
#define GENCLK(id, name, type, parents, mux, div, gt) { \
.clk_id = id, \
.clk_type = type, \
.clk_name = name, \
parents \
mux \
div \
gt \
}
/* PLL definitions */
static struct jz4780_clk_pll_def pll_clks[] = {
PLL(JZ4780_CLK_APLL, "apll", "ext", JZ_CPAPCR),
PLL(JZ4780_CLK_MPLL, "mpll", "ext", JZ_CPMPCR),
PLL(JZ4780_CLK_EPLL, "epll", "ext", JZ_CPEPCR),
PLL(JZ4780_CLK_VPLL, "vpll", "ext", JZ_CPVPCR),
};
/* OTG PHY clock (reuse gate def structure */
static struct jz4780_clk_gate_def otg_clks[] = {
GATE(JZ4780_CLK_OTGPHY, "otg_phy", "ext", 0),
};
static const struct jz4780_clk_descr gen_clks[] = {
GENCLK(JZ4780_CLK_SCLKA, "sclk_a", CLK_MASK_MUX,
PLIST("apll", "ext", "rtc"),
MUX(JZ_CPCCR, 30, 2, 0x7),
NO_DIV,
NO_GATE
),
GENCLK(JZ4780_CLK_CPUMUX, "cpumux", CLK_MASK_MUX,
PLIST("sclk_a", "mpll", "epll"),
MUX(JZ_CPCCR, 28, 2, 0x7),
NO_DIV,
NO_GATE
),
GENCLK(JZ4780_CLK_CPU, "cpu", CLK_MASK_DIV,
PLIST("cpumux"),
NO_MUX,
DIV(JZ_CPCCR, 0, 0, 4, 22, -1, -1),
NO_GATE
),
GENCLK(JZ4780_CLK_L2CACHE, "l2cache", CLK_MASK_DIV,
PLIST("cpumux"),
NO_MUX,
DIV(JZ_CPCCR, 4, 0, 4, -1, -1, -1),
NO_GATE
),
GENCLK(JZ4780_CLK_AHB0, "ahb0", CLK_MASK_MUX | CLK_MASK_DIV,
PLIST("sclk_a", "mpll", "epll"),
MUX(JZ_CPCCR, 26, 2, 0x7),
DIV(JZ_CPCCR, 8, 0, 4, 21, -1, -1),
NO_GATE
),
GENCLK(JZ4780_CLK_AHB2PMUX, "ahb2_apb_mux", CLK_MASK_MUX,
PLIST("sclk_a", "mpll", "rtc"),
MUX(JZ_CPCCR, 24, 2, 0x7),
NO_DIV,
NO_GATE
),
GENCLK(JZ4780_CLK_AHB2, "ahb2", CLK_MASK_DIV,
PLIST("ahb2_apb_mux"),
NO_MUX,
DIV(JZ_CPCCR, 12, 0, 4, 20, -1, -1),
NO_GATE
),
GENCLK(JZ4780_CLK_PCLK, "pclk", CLK_MASK_DIV,
PLIST("ahb2_apb_mux"),
NO_MUX,
DIV(JZ_CPCCR, 16, 0, 4, 20, -1, -1),
NO_GATE
),
GENCLK(JZ4780_CLK_DDR, "ddr", CLK_MASK_MUX | CLK_MASK_DIV,
PLIST("sclk_a", "mpll"),
MUX(JZ_DDCDR, 30, 2, 0x6),
DIV(JZ_DDCDR, 0, 0, 4, 29, 28, 27),
NO_GATE
),
GENCLK(JZ4780_CLK_VPU, "vpu", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
PLIST("sclk_a", "mpll", "epll"),
MUX(JZ_VPUCDR, 30, 2, 0xe),
DIV(JZ_VPUCDR, 0, 0, 4, 29, 28, 27),
GATEBIT(32 + 2)
),
GENCLK(JZ4780_CLK_I2SPLL, "i2s_pll", CLK_MASK_MUX | CLK_MASK_DIV,
PLIST("sclk_a", "epll"),
MUX(JZ_I2SCDR, 30, 1, 0xc),
DIV(JZ_I2SCDR, 0, 0, 8, 29, 28, 27),
NO_GATE
),
GENCLK(JZ4780_CLK_I2S, "i2s", CLK_MASK_MUX,
PLIST("ext", "i2s_pll"),
MUX(JZ_I2SCDR, 31, 1, 0xc),
NO_DIV,
NO_GATE
),
GENCLK(JZ4780_CLK_LCD0PIXCLK, "lcd0pixclk", CLK_MASK_MUX | CLK_MASK_DIV,
PLIST("sclk_a", "mpll", "vpll"),
MUX(JZ_LP0CDR, 30, 2, 0xe),
DIV(JZ_LP0CDR, 0, 0, 8, 28, 27, 26),
NO_GATE
),
GENCLK(JZ4780_CLK_LCD1PIXCLK, "lcd1pixclk", CLK_MASK_MUX | CLK_MASK_DIV,
PLIST("sclk_a", "mpll", "vpll"),
MUX(JZ_LP1CDR, 30, 2, 0xe),
DIV(JZ_LP1CDR, 0, 0, 8, 28, 27, 26),
NO_GATE
),
GENCLK(JZ4780_CLK_MSCMUX, "msc_mux", CLK_MASK_MUX,
PLIST("sclk_a", "mpll"),
MUX(JZ_MSC0CDR, 30, 2, 0x6),
NO_DIV,
NO_GATE
),
GENCLK(JZ4780_CLK_MSC0, "msc0", CLK_MASK_DIV | CLK_MASK_GATE,
PLIST("msc_mux"),
NO_MUX,
DIV(JZ_MSC0CDR, 0, 1, 8, 29, 28, 27),
GATEBIT(3)
),
GENCLK(JZ4780_CLK_MSC1, "msc1", CLK_MASK_DIV | CLK_MASK_GATE,
PLIST("msc_mux"),
NO_MUX,
DIV(JZ_MSC1CDR, 0, 1, 8, 29, 28, 27),
GATEBIT(11)
),
GENCLK(JZ4780_CLK_MSC2, "msc2", CLK_MASK_DIV | CLK_MASK_GATE,
PLIST("msc_mux"),
NO_MUX,
DIV(JZ_MSC2CDR, 0, 1, 8, 29, 28, 27),
GATEBIT(12)
),
GENCLK(JZ4780_CLK_UHC, "uhc", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
PLIST("sclk_a", "mpll", "epll", "otg_phy"),
MUX(JZ_UHCCDR, 30, 2, 0xf),
DIV(JZ_UHCCDR, 0, 0, 8, 29, 28, 27),
GATEBIT(24)
),
GENCLK(JZ4780_CLK_SSIPLL, "ssi_pll", CLK_MASK_MUX | CLK_MASK_DIV,
PLIST("sclk_a", "mpll"),
MUX(JZ_SSICDR, 30, 1, 0xc),
DIV(JZ_SSICDR, 0, 0, 8, 29, 28, 27),
NO_GATE
),
GENCLK(JZ4780_CLK_SSI, "ssi", CLK_MASK_MUX,
PLIST("ext", "ssi_pll"),
MUX(JZ_SSICDR, 31, 1, 0xc),
NO_DIV,
NO_GATE
),
GENCLK(JZ4780_CLK_CIMMCLK, "cim_mclk", CLK_MASK_MUX | CLK_MASK_DIV,
PLIST("sclk_a", "mpll"),
MUX(JZ_CIMCDR, 31, 1, 0xc),
DIV(JZ_CIMCDR, 0, 0, 8, 30, 29, 28),
NO_GATE
),
GENCLK(JZ4780_CLK_PCMPLL, "pcm_pll", CLK_MASK_MUX | CLK_MASK_DIV,
PLIST("sclk_a", "mpll", "epll", "vpll"),
MUX(JZ_PCMCDR, 29, 2, 0xf),
DIV(JZ_PCMCDR, 0, 0, 8, 28, 27, 26),
NO_GATE
),
GENCLK(JZ4780_CLK_PCM, "pcm", CLK_MASK_MUX | CLK_MASK_GATE,
PLIST("ext", "pcm_pll"),
MUX(JZ_PCMCDR, 31, 1, 0xc),
NO_DIV,
GATEBIT(32 + 3)
),
GENCLK(JZ4780_CLK_GPU, "gpu", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
PLIST("sclk_a", "mpll", "epll"),
MUX(JZ_GPUCDR, 30, 2, 0x7),
DIV(JZ_GPUCDR, 0, 0, 4, 29, 28, 27),
GATEBIT(32 + 4)
),
GENCLK(JZ4780_CLK_HDMI, "hdmi", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
PLIST("sclk_a", "mpll", "vpll"),
MUX(JZ_HDMICDR, 30, 2, 0xe),
DIV(JZ_HDMICDR, 0, 0, 8, 29, 28, 26),
GATEBIT(32 + 9)
),
GENCLK(JZ4780_CLK_BCH, "bch", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE,
PLIST("sclk_a", "mpll", "epll"),
MUX(JZ_BCHCDR, 30, 2, 0x7),
DIV(JZ_BCHCDR, 0, 0, 4, 29, 28, 27),
GATEBIT(1)
),
};
static struct jz4780_clk_gate_def gate_clks[] = {
GATE(JZ4780_CLK_NEMC, "nemc", "ahb2", 0),
GATE(JZ4780_CLK_OTG0, "otg0", "ext", 2),
GATE(JZ4780_CLK_SSI0, "ssi0", "ssi", 4),
GATE(JZ4780_CLK_SMB0, "smb0", "pclk", 5),
GATE(JZ4780_CLK_SMB1, "smb1", "pclk", 6),
GATE(JZ4780_CLK_SCC, "scc", "ext", 7),
GATE(JZ4780_CLK_AIC, "aic", "ext", 8),
GATE(JZ4780_CLK_TSSI0, "tssi0", "ext", 9),
GATE(JZ4780_CLK_OWI, "owi", "ext", 10),
GATE(JZ4780_CLK_KBC, "kbc", "ext", 13),
GATE(JZ4780_CLK_SADC, "sadc", "ext", 14),
GATE(JZ4780_CLK_UART0, "uart0", "ext", 15),
GATE(JZ4780_CLK_UART1, "uart1", "ext", 16),
GATE(JZ4780_CLK_UART2, "uart2", "ext", 17),
GATE(JZ4780_CLK_UART3, "uart3", "ext", 18),
GATE(JZ4780_CLK_SSI1, "ssi1", "ssi", 19),
GATE(JZ4780_CLK_SSI2, "ssi2", "ssi", 20),
GATE(JZ4780_CLK_PDMA, "pdma", "ext", 21),
GATE(JZ4780_CLK_GPS, "gps", "ext", 22),
GATE(JZ4780_CLK_MAC, "mac", "ext", 23),
GATE(JZ4780_CLK_SMB2, "smb2", "pclk", 25),
GATE(JZ4780_CLK_CIM, "cim", "ext", 26),
GATE(JZ4780_CLK_LCD, "lcd", "ext", 28),
GATE(JZ4780_CLK_TVE, "tve", "lcd", 27),
GATE(JZ4780_CLK_IPU, "ipu", "ext", 29),
GATE(JZ4780_CLK_DDR0, "ddr0", "ddr", 30),
GATE(JZ4780_CLK_DDR1, "ddr1", "ddr", 31),
GATE(JZ4780_CLK_SMB3, "smb3", "pclk", 32 + 0),
GATE(JZ4780_CLK_TSSI1, "tssi1", "ext", 32 + 1),
GATE(JZ4780_CLK_COMPRESS, "compress", "ext", 32 + 5),
GATE(JZ4780_CLK_AIC1, "aic1", "ext", 32 + 6),
GATE(JZ4780_CLK_GPVLC, "gpvlc", "ext", 32 + 7),
GATE(JZ4780_CLK_OTG1, "otg1", "ext", 32 + 8),
GATE(JZ4780_CLK_UART4, "uart4", "ext", 32 + 10),
GATE(JZ4780_CLK_AHBMON, "ahb_mon", "ext", 32 + 11),
GATE(JZ4780_CLK_SMB4, "smb4", "pclk", 32 + 12),
GATE(JZ4780_CLK_DES, "des", "ext", 32 + 13),
GATE(JZ4780_CLK_X2D, "x2d", "ext", 32 + 14),
GATE(JZ4780_CLK_CORE1, "core1", "cpu", 32 + 15),
};
static int
jz4780_clock_register(struct jz4780_clock_softc *sc)
{
int i, ret;
/* Register PLLs */
for (i = 0; i < nitems(pll_clks); i++) {
struct clknode_init_def clkdef;
clkdef.id = pll_clks[i].clk_id;
clkdef.name = __DECONST(char *, pll_clks[i].clk_name);
clkdef.parent_names = pll_clks[i].clk_pname;
clkdef.parent_cnt = 1;
clkdef.flags = CLK_NODE_STATIC_STRINGS;
ret = jz4780_clk_pll_register(sc->clkdom, &clkdef, &sc->mtx,
sc->res[0], pll_clks[i].clk_reg);
if (ret != 0)
return (ret);
}
/* Register OTG clock */
for (i = 0; i < nitems(otg_clks); i++) {
struct clknode_init_def clkdef;
clkdef.id = otg_clks[i].clk_id;
clkdef.name = __DECONST(char *, otg_clks[i].clk_name);
clkdef.parent_names = otg_clks[i].clk_pname;
clkdef.parent_cnt = 1;
clkdef.flags = CLK_NODE_STATIC_STRINGS;
ret = jz4780_clk_otg_register(sc->clkdom, &clkdef, &sc->mtx,
sc->res[0]);
if (ret != 0)
return (ret);
}
/* Register muxes and divisors */
for (i = 0; i < nitems(gen_clks); i++) {
ret = jz4780_clk_gen_register(sc->clkdom, &gen_clks[i],
&sc->mtx, sc->res[0]);
if (ret != 0)
return (ret);
}
/* Register simple gates */
for (i = 0; i < nitems(gate_clks); i++) {
struct clk_gate_def gatedef;
gatedef.clkdef.id = gate_clks[i].clk_id;
gatedef.clkdef.name = __DECONST(char *, gate_clks[i].clk_name);
gatedef.clkdef.parent_names = gate_clks[i].clk_pname;
gatedef.clkdef.parent_cnt = 1;
gatedef.clkdef.flags = CLK_NODE_STATIC_STRINGS;
if (gate_clks[i].clk_bit < 32) {
gatedef.offset = JZ_CLKGR0;
gatedef.shift = gate_clks[i].clk_bit;
} else {
gatedef.offset = JZ_CLKGR1;
gatedef.shift = gate_clks[i].clk_bit - 32;
}
gatedef.mask = 1;
gatedef.on_value = 0;
gatedef.off_value = 1;
gatedef.gate_flags = 0;
ret = clknode_gate_register(sc->clkdom, &gatedef);
if (ret != 0)
return (ret);
}
return (0);
}
static int
jz4780_clock_fixup(struct jz4780_clock_softc *sc)
{
struct clknode *clk_uhc;
int ret;
/*
* Make UHC mux use MPLL as the source. It defaults to OTG_PHY
* and that somehow just does not work.
*/
clkdom_xlock(sc->clkdom);
/* Assume the worst */
ret = ENXIO;
clk_uhc = clknode_find_by_id(sc->clkdom, JZ4780_CLK_UHC);
if (clk_uhc != NULL) {
ret = clknode_set_parent_by_name(clk_uhc, "mpll");
if (ret != 0)
device_printf(sc->dev,
"unable to reparent uhc clock\n");
else
ret = clknode_set_freq(clk_uhc, 48000000, 0, 0);
if (ret != 0)
device_printf(sc->dev, "unable to init uhc clock\n");
} else
device_printf(sc->dev, "unable to lookup uhc clock\n");
clkdom_unlock(sc->clkdom);
return (ret);
}
#define CGU_LOCK(sc) mtx_lock(&(sc)->mtx)
#define CGU_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
#define CGU_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"jz4780-cgu", MTX_DEF)
#define CGU_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg))
static int
jz4780_clock_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-cgu"))
return (ENXIO);
device_set_desc(dev, "Ingenic jz4780 CGU");
return (BUS_PROBE_DEFAULT);
}
static int
jz4780_clock_attach(device_t dev)
{
struct jz4780_clock_softc *sc;
sc = device_get_softc(dev);
if (bus_alloc_resources(dev, jz4780_clock_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
return (ENXIO);
}
sc->dev = dev;
CGU_LOCK_INIT(sc);
sc->clkdom = clkdom_create(dev);
if (sc->clkdom == NULL)
goto fail;
if (jz4780_clock_register(sc) != 0)
goto fail;
if (clkdom_finit(sc->clkdom) != 0)
goto fail;
if (jz4780_clock_fixup(sc) != 0)
goto fail;
if (bootverbose)
clkdom_dump(sc->clkdom);
return (0);
fail:
bus_release_resources(dev, jz4780_clock_spec, sc->res);
CGU_LOCK_DESTROY(sc);
return (ENXIO);
}
static int
jz4780_clock_detach(device_t dev)
{
struct jz4780_clock_softc *sc;
sc = device_get_softc(dev);
bus_release_resources(dev, jz4780_clock_spec, sc->res);
CGU_LOCK_DESTROY(sc);
return (0);
}
static int
jz4780_clock_write_4(device_t dev, bus_addr_t addr, uint32_t val)
{
struct jz4780_clock_softc *sc;
sc = device_get_softc(dev);
CSR_WRITE_4(sc, addr, val);
return (0);
}
static int
jz4780_clock_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
{
struct jz4780_clock_softc *sc;
sc = device_get_softc(dev);
*val = CSR_READ_4(sc, addr);
return (0);
}
static int
jz4780_clock_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask,
uint32_t set_mask)
{
struct jz4780_clock_softc *sc;
uint32_t val;
sc = device_get_softc(dev);
val = CSR_READ_4(sc, addr);
val &= clear_mask;
val |= set_mask;
CSR_WRITE_4(sc, addr, val);
return (0);
}
static void
jz4780_clock_device_lock(device_t dev)
{
struct jz4780_clock_softc *sc;
sc = device_get_softc(dev);
CGU_LOCK(sc);
}
static void
jz4780_clock_device_unlock(device_t dev)
{
struct jz4780_clock_softc *sc;
sc = device_get_softc(dev);
CGU_UNLOCK(sc);
}
static device_method_t jz4780_clock_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_clock_probe),
DEVMETHOD(device_attach, jz4780_clock_attach),
DEVMETHOD(device_detach, jz4780_clock_detach),
/* Clock device interface */
DEVMETHOD(clkdev_write_4, jz4780_clock_write_4),
DEVMETHOD(clkdev_read_4, jz4780_clock_read_4),
DEVMETHOD(clkdev_modify_4, jz4780_clock_modify_4),
DEVMETHOD(clkdev_device_lock, jz4780_clock_device_lock),
DEVMETHOD(clkdev_device_unlock, jz4780_clock_device_unlock),
DEVMETHOD_END
};
static driver_t jz4780_clock_driver = {
"cgu",
jz4780_clock_methods,
sizeof(struct jz4780_clock_softc),
};
static devclass_t jz4780_clock_devclass;
EARLY_DRIVER_MODULE(jz4780_clock, simplebus, jz4780_clock_driver,
jz4780_clock_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE);
static int
jz4780_ehci_clk_config(struct jz4780_clock_softc *sc)
{
clk_t phy_clk, ext_clk;
uint64_t phy_freq;
int err;
phy_clk = NULL;
ext_clk = NULL;
err = -1;
/* Set phy timing by copying it from ext */
if (clk_get_by_id(sc->dev, sc->clkdom, JZ4780_CLK_OTGPHY,
&phy_clk) != 0)
goto done;
if (clk_get_parent(phy_clk, &ext_clk) != 0)
goto done;
if (clk_get_freq(ext_clk, &phy_freq) != 0)
goto done;
if (clk_set_freq(phy_clk, phy_freq, 0) != 0)
goto done;
err = 0;
done:
clk_release(ext_clk);
clk_release(phy_clk);
return (err);
}
int
jz4780_ohci_enable(void)
{
device_t dev;
struct jz4780_clock_softc *sc;
uint32_t reg;
dev = devclass_get_device(jz4780_clock_devclass, 0);
if (dev == NULL)
return (-1);
sc = device_get_softc(dev);
CGU_LOCK(sc);
/* Do not force port1 to suspend mode */
reg = CSR_READ_4(sc, JZ_OPCR);
reg |= OPCR_SPENDN1;
CSR_WRITE_4(sc, JZ_OPCR, reg);
CGU_UNLOCK(sc);
return (0);
}
int
jz4780_ehci_enable(void)
{
device_t dev;
struct jz4780_clock_softc *sc;
uint32_t reg;
dev = devclass_get_device(jz4780_clock_devclass, 0);
if (dev == NULL)
return (-1);
sc = device_get_softc(dev);
/*
* EHCI should use MPPL as a parent, but Linux configures OTG
* clock anyway. Follow their lead blindly.
*/
if (jz4780_ehci_clk_config(sc) != 0)
return (-1);
CGU_LOCK(sc);
/* Enable OTG, should not be necessary since we use PLL clock */
reg = CSR_READ_4(sc, JZ_USBPCR);
reg &= ~(PCR_OTG_DISABLE);
CSR_WRITE_4(sc, JZ_USBPCR, reg);
/* Do not force port1 to suspend mode */
reg = CSR_READ_4(sc, JZ_OPCR);
reg |= OPCR_SPENDN1;
CSR_WRITE_4(sc, JZ_OPCR, reg);
/* D- pulldown */
reg = CSR_READ_4(sc, JZ_USBPCR1);
reg |= PCR_DMPD1;
CSR_WRITE_4(sc, JZ_USBPCR1, reg);
/* D+ pulldown */
reg = CSR_READ_4(sc, JZ_USBPCR1);
reg |= PCR_DPPD1;
CSR_WRITE_4(sc, JZ_USBPCR1, reg);
/* 16 bit bus witdth for port 1*/
reg = CSR_READ_4(sc, JZ_USBPCR1);
reg |= PCR_WORD_I_F1 | PCR_WORD_I_F0;
CSR_WRITE_4(sc, JZ_USBPCR1, reg);
/* Reset USB */
reg = CSR_READ_4(sc, JZ_USBPCR);
reg |= PCR_POR;
CSR_WRITE_4(sc, JZ_USBPCR, reg);
DELAY(1);
reg = CSR_READ_4(sc, JZ_USBPCR);
reg &= ~(PCR_POR);
CSR_WRITE_4(sc, JZ_USBPCR, reg);
/* Soft-reset USB */
reg = CSR_READ_4(sc, JZ_SRBC);
reg |= SRBC_UHC_SR;
CSR_WRITE_4(sc, JZ_SRBC, reg);
/* 300ms */
DELAY(300*hz/1000);
reg = CSR_READ_4(sc, JZ_SRBC);
reg &= ~(SRBC_UHC_SR);
CSR_WRITE_4(sc, JZ_SRBC, reg);
/* 300ms */
DELAY(300*hz/1000);
CGU_UNLOCK(sc);
return (0);
}
#define USBRESET_DETECT_TIME 0x96
int
jz4780_otg_enable(void)
{
device_t dev;
struct jz4780_clock_softc *sc;
uint32_t reg;
dev = devclass_get_device(jz4780_clock_devclass, 0);
if (dev == NULL)
return (-1);
sc = device_get_softc(dev);
CGU_LOCK(sc);
/* Select Synopsys OTG mode */
reg = CSR_READ_4(sc, JZ_USBPCR1);
reg |= PCR_SYNOPSYS;
/* Set UTMI bus width to 16 bit */
reg |= PCR_WORD_I_F0 | PCR_WORD_I_F1;
CSR_WRITE_4(sc, JZ_USBPCR1, reg);
/* Blah */
reg = CSR_READ_4(sc, JZ_USBVBFIL);
reg = REG_SET(reg, USBVBFIL_IDDIGFIL, 0);
reg = REG_SET(reg, USBVBFIL_USBVBFIL, 0);
CSR_WRITE_4(sc, JZ_USBVBFIL, reg);
/* Setup reset detect time */
reg = CSR_READ_4(sc, JZ_USBRDT);
reg = REG_SET(reg, USBRDT_USBRDT, USBRESET_DETECT_TIME);
reg |= USBRDT_VBFIL_LD_EN;
CSR_WRITE_4(sc, JZ_USBRDT, reg);
/* Setup USBPCR bits */
reg = CSR_READ_4(sc, JZ_USBPCR);
reg |= PCR_USB_MODE;
reg |= PCR_COMMONONN;
reg |= PCR_VBUSVLDEXT;
reg |= PCR_VBUSVLDEXTSEL;
reg &= ~(PCR_OTG_DISABLE);
CSR_WRITE_4(sc, JZ_USBPCR, reg);
/* Reset USB */
reg = CSR_READ_4(sc, JZ_USBPCR);
reg |= PCR_POR;
CSR_WRITE_4(sc, JZ_USBPCR, reg);
DELAY(1000);
reg = CSR_READ_4(sc, JZ_USBPCR);
reg &= ~(PCR_POR);
CSR_WRITE_4(sc, JZ_USBPCR, reg);
/* Unsuspend OTG port */
reg = CSR_READ_4(sc, JZ_OPCR);
reg |= OPCR_SPENDN0;
CSR_WRITE_4(sc, JZ_OPCR, reg);
CGU_UNLOCK(sc);
return (0);
}

View File

@ -0,0 +1,36 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef JZ4780_CLOCK_H
#define JZ4780_CLOCK_H
extern int jz4780_ehci_enable(void);
extern int jz4780_ohci_enable(void);
extern int jz4780_otg_enable(void);
#endif

View File

@ -0,0 +1,73 @@
/*-
* Copyright (c) 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef JZ4780_CPUREGS_H
#define JZ4780_CPUREGS_H
/* Core control register */
#define JZ_CORECTL_SLP1M_SHIFT 17
#define JZ_CORECTL_SLP1M (1u << JZ_CORECTL_SLP1M_SHIFT)
#define JZ_CORECTL_SLP0M_SHIFT 16
#define JZ_CORECTL_SLP0M (1u << JZ_CORECTL_SLP0M_SHIFT)
#define JZ_CORECTL_RPC1_SHIFT 9
#define JZ_CORECTL_RPC1 (1u << JZ_CORECTL_RPC1_SHIFT)
#define JZ_CORECTL_RPC0_SHIFT 8
#define JZ_CORECTL_RPC0 (1u << JZ_CORECTL_RPC0_SHIFT)
#define JZ_CORECTL_SWRST1_SHIFT 1
#define JZ_CORECTL_SWRST1 (1u << JZ_CORECTL_SWRST1_SHIFT)
#define JZ_CORECTL_SWRST0_SHIFT 0
#define JZ_CORECTL_SWRST0 (1u << JZ_CORECTL_SWRST0_SHIFT)
/* Core status register */
#define JZ_CORESTS_SLP1_SHIFT 17
#define JZ_CORESTS_SLP1 (1u << JZ_CORESTS_SLP1_SHIFT)
#define JZ_CORESTS_SLP0_SHIFT 16
#define JZ_CORESTS_SLP0 (1u << JZ_CORESTS_SLP0_SHIFT)
#define JZ_CORESTS_IRQ1P_SHIFT 9
#define JZ_CORESTS_IRQ1P (1u << JZ_CORESTS_IRQ1P_SHIFT)
#define JZ_CORESTS_IRQ0P_SHIFT 8
#define JZ_CORESTS_IRQ0P (1u << JZ_CORESTS_IRQ0P_SHIFT)
#define JZ_CORESTS_MIRQ1P_SHIFT 1
#define JZ_CORESTS_MIRQ1P (1u << JZ_CORESTS_MIRQ1P_SHIFT)
#define JZ_CORESTS_MIRQ0P_SHIFT 0
#define JZ_CORESTS_MIRQ0P (1u << JZ_CORESTS_MIRQ0P_SHIFT)
/* Reset entry and IRQ mask */
#define JZ_REIM_ENTRY_SHIFT 16
#define JZ_REIM_ENTRY_WIDTH 16
#define JZ_REIM_ENTRY_MASK (0xFFFFu << JZ_REIM_ENTRY_SHIFT)
#define JZ_REIM_IRQ1M_SHIFT 9
#define JZ_REIM_IRQ1M (1u << JZ_REIM_IRQ1M_SHIFT)
#define JZ_REIM_IRQ0M_SHIFT 8
#define JZ_REIM_IRQ0M (1u << JZ_REIM_IRQ0M_SHIFT)
#define JZ_REIM_MIRQ1M_SHIFT 1
#define JZ_REIM_MIRQ1M (1u << JZ_REIM_MIRQ1M_SHIFT)
#define JZ_REIM_MIRQ0M_SHIFT 0
#define JZ_REIM_MIRQ0M (1u << JZ_REIM_MIRQ0M_SHIFT)
#endif /* JZ4780_CPUREGS_H */

View File

@ -0,0 +1,124 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
struct jz4780_dme_softc {
device_t dev;
struct resource *res[2];
};
static struct resource_spec jz4780_dme_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_MEMORY, 1, RF_ACTIVE },
{ -1, 0 }
};
static int jz4780_dme_probe(device_t dev);
static int jz4780_dme_attach(device_t dev);
static int jz4780_dme_detach(device_t dev);
static int
jz4780_dme_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "davicom,dm9000"))
return (ENXIO);
device_set_desc(dev, "Davicom DM9000C 10/100BaseTX");
return (BUS_PROBE_DEFAULT);
}
static int
jz4780_dme_attach(device_t dev)
{
struct jz4780_dme_softc *sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, jz4780_dme_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
return (ENXIO);
}
return (0);
}
static int
jz4780_dme_detach(device_t dev)
{
struct jz4780_dme_softc *sc = device_get_softc(dev);
bus_release_resources(dev, jz4780_dme_spec, sc->res);
return (0);
}
static device_method_t jz4780_dme_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_dme_probe),
DEVMETHOD(device_attach, jz4780_dme_attach),
DEVMETHOD(device_detach, jz4780_dme_detach),
DEVMETHOD_END
};
static driver_t jz4780_dme_driver = {
"dme",
jz4780_dme_methods,
sizeof(struct jz4780_dme_softc),
};
static devclass_t jz4780_dme_devclass;
DRIVER_MODULE(jz4780_dme, simplebus, jz4780_dme_driver,
jz4780_dme_devclass, 0, 0);

View File

@ -0,0 +1,202 @@
/*
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/callout.h>
#include <sys/condvar.h>
#include <sys/module.h>
#include <dev/extres/clk/clk.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/controller/dwc_otg.h>
#include <dev/usb/controller/dwc_otg_fdt.h>
#include <mips/ingenic/jz4780_clock.h>
#include <mips/ingenic/jz4780_regs.h>
static device_probe_t jz4780_dwc_otg_probe;
static device_attach_t jz4780_dwc_otg_attach;
static device_detach_t jz4780_dwc_otg_detach;
struct jz4780_dwc_otg_softc {
struct dwc_otg_fdt_softc base; /* storage for DWC OTG code */
clk_t phy_clk;
clk_t otg_clk;
};
static int
jz4780_dwc_otg_clk_enable(device_t dev)
{
struct jz4780_dwc_otg_softc *sc;
int err;
sc = device_get_softc(dev);
/* Configure and enable phy clock */
err = clk_get_by_ofw_name(dev, 0, "otg_phy", &sc->phy_clk);
if (err != 0) {
device_printf(dev, "unable to lookup %s clock\n", "otg_phy");
return (err);
}
err = clk_set_freq(sc->phy_clk, 48000000, 0);
if (err != 0) {
device_printf(dev, "unable to set %s clock to 48 kHZ\n",
"otg_phy");
return (err);
}
err = clk_enable(sc->phy_clk);
if (err != 0) {
device_printf(dev, "unable to enable %s clock\n", "otg_phy");
return (err);
}
/* Configure and enable otg1 clock */
err = clk_get_by_ofw_name(dev, 0, "otg1", &sc->otg_clk);
if (err != 0) {
device_printf(dev, "unable to lookup %s clock\n", "otg1");
return (err);
}
err = clk_enable(sc->phy_clk);
if (err != 0) {
device_printf(dev, "unable to enable %s clock\n", "otg1");
return (err);
}
return (0);
}
static int
jz4780_dwc_otg_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-otg"))
return (ENXIO);
device_set_desc(dev, "DWC OTG 2.0 integrated USB controller (jz4780)");
return (BUS_PROBE_VENDOR);
}
static int
jz4780_dwc_otg_attach(device_t dev)
{
struct jz4780_dwc_otg_softc *sc;
struct resource *res;
int err, rid;
sc = device_get_softc(dev);
err = jz4780_dwc_otg_clk_enable(dev);
if (err != 0)
goto fail;
err = jz4780_otg_enable();
if (err != 0) {
device_printf(dev, "CGU failed to enable OTG\n");
goto fail;
}
/* Voodoo: Switch off VBUS overcurrent detection in OTG PHY */
res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
if (res != NULL) {
uint32_t reg;
reg = bus_read_4(res, JZ_DWC2_GUSBCFG);
reg |= 0xc;
bus_write_4(res, JZ_DWC2_GUSBCFG, reg);
bus_release_resource(dev, SYS_RES_MEMORY, rid, res);
}
sc->base.sc_otg.sc_phy_type = DWC_OTG_PHY_UTMI;
sc->base.sc_otg.sc_phy_bits = 16;
err = dwc_otg_attach(dev);
if (err != 0)
goto fail;
return (0);
fail:
if (sc->otg_clk)
clk_release(sc->otg_clk);
if (sc->phy_clk)
clk_release(sc->phy_clk);
return (err);
}
static int
jz4780_dwc_otg_detach(device_t dev)
{
struct jz4780_dwc_otg_softc *sc;
int err;
err = dwc_otg_detach(dev);
if (err != 0)
return (err);
sc = device_get_softc(dev);
if (sc->otg_clk)
clk_release(sc->otg_clk);
if (sc->phy_clk)
clk_release(sc->phy_clk);
return (0);
}
static device_method_t jz4780_dwc_otg_methods[] = {
/* bus interface */
DEVMETHOD(device_probe, jz4780_dwc_otg_probe),
DEVMETHOD(device_attach, jz4780_dwc_otg_attach),
DEVMETHOD(device_detach, jz4780_dwc_otg_detach),
DEVMETHOD_END
};
static devclass_t jz4780_dwc_otg_devclass;
DEFINE_CLASS_1(jzotg, jz4780_dwc_otg_driver, jz4780_dwc_otg_methods,
sizeof(struct jz4780_dwc_otg_softc), dwc_otg_driver);
DRIVER_MODULE(jzotg, simplebus, jz4780_dwc_otg_driver,
jz4780_dwc_otg_devclass, 0, 0);
MODULE_DEPEND(jzotg, usb, 1, 1, 1);

View File

@ -0,0 +1,213 @@
/*-
* Copyright (c) 2015 Alexander Kabaev <kan@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/clock.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/module.h>
#include <sys/resource.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <mips/ingenic/jz4780_regs.h>
static struct ofw_compat_data compat_data[] = {
{"ingenic,jz4780-efuse", 1},
{NULL, 0}
};
struct jz4780_efuse_data {
uint32_t serial_num;
uint32_t date;
uint8_t nanufacturer[2];
uint8_t macaddr[6];
} __packed;
static struct resource_spec jz4780_efuse_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
struct jz4780_efuse_softc {
device_t dev;
struct resource *res[1];
struct jz4780_efuse_data data;
};
#define CSR_WRITE_4(sc, reg, val) \
bus_write_4((sc)->res[0], (reg), (val))
#define CSR_READ_4(sc, reg) \
bus_read_4((sc)->res[0], (reg))
#define JZ_EFUSE_BANK_SIZE (4096 / 8) /* Bank size is 4096 bits */
static int
jz4780_efuse_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
return (BUS_PROBE_DEFAULT);
}
static void
jz4780_efuse_read_chunk(struct jz4780_efuse_softc *sc, int addr, uint8_t *buf, int len)
{
uint32_t abuf;
int i, count;
/* Setup to read proper bank */
CSR_WRITE_4(sc, JZ_EFUCTRL, JZ_EFUSE_READ |
(addr < JZ_EFUSE_BANK_SIZE ? 0: JZ_EFUSE_BANK) |
(addr << JZ_EFUSE_ADDR_SHIFT) |
((len - 1) << JZ_EFUSE_SIZE_SHIFT));
/* Wait for read to complete */
while ((CSR_READ_4(sc, JZ_EFUSTATE) & JZ_EFUSE_RD_DONE) == 0)
DELAY(1000);
/* Round to 4 bytes for the simple loop below */
count = len & ~3;
for (i = 0; i < count; i += 4) {
abuf = CSR_READ_4(sc, JZ_EFUDATA0 + i);
memcpy(buf, &abuf, 4);
buf += 4;
}
/* Read partial word and assign it byte-by-byte */
if (i < len) {
abuf = CSR_READ_4(sc, JZ_EFUDATA0 + i);
for (/* none */; i < len; i++) {
buf[i] = abuf & 0xff;
abuf >>= 8;
}
}
}
static void
jz4780_efuse_read(struct jz4780_efuse_softc *sc, int addr, void *buf, int len)
{
int chunk;
while (len > 0) {
chunk = (len > 32) ? 32 : len;
jz4780_efuse_read_chunk(sc, addr, buf, chunk);
len -= chunk;
buf = (void *)((uintptr_t)buf + chunk);
addr += chunk;
}
}
static void
jz4780_efuse_update_kenv(struct jz4780_efuse_softc *sc)
{
char macstr[sizeof("xx:xx:xx:xx:xx:xx")];
/*
* Update hint in kernel env only if none is available yet.
* It is quite possible one was set by command line already.
*/
if (kern_getenv("hint.dme.0.macaddr") == NULL) {
snprintf(macstr, sizeof(macstr), "%6D",
sc->data.macaddr, ":");
kern_setenv("hint.dme.0.macaddr", macstr);
}
}
static int
jz4780_efuse_attach(device_t dev)
{
struct jz4780_efuse_softc *sc;
sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, jz4780_efuse_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
return (ENXIO);
}
/*
* Default RD_STROBE to 4 h2clk cycles, should already be set to 4 by reset
* but configure it anyway.
*/
CSR_WRITE_4(sc, JZ_EFUCFG, 0x00040000);
/* Read user-id segment */
jz4780_efuse_read(sc, 0x18, &sc->data, sizeof(sc->data));
/*
* Set resource hints for the dme device to discover its
* MAC address, if not set already.
*/
jz4780_efuse_update_kenv(sc);
/* Resource conflicts with NEMC, release early */
bus_release_resources(dev, jz4780_efuse_spec, sc->res);
return (0);
}
static int
jz4780_efuse_detach(device_t dev)
{
return (0);
}
static device_method_t jz4780_efuse_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_efuse_probe),
DEVMETHOD(device_attach, jz4780_efuse_attach),
DEVMETHOD(device_detach, jz4780_efuse_detach),
DEVMETHOD_END
};
static driver_t jz4780_efuse_driver = {
"efuse",
jz4780_efuse_methods,
sizeof(struct jz4780_efuse_softc),
};
static devclass_t jz4780_efuse_devclass;
EARLY_DRIVER_MODULE(jz4780_efuse, simplebus, jz4780_efuse_driver,
jz4780_efuse_devclass, 0, 0, BUS_PASS_TIMER);

View File

@ -0,0 +1,345 @@
/*-
* Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* JZ4780 attachment driver for the USB Enhanced Host Controller.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_bus.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/condvar.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/gpio.h>
#include <dev/extres/clk/clk.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_util.h>
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/controller/ehci.h>
#include <dev/usb/controller/ehcireg.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/gpio/gpiobusvar.h>
#include <mips/ingenic/jz4780_clock.h>
#include <mips/ingenic/jz4780_regs.h>
#define EHCI_HC_DEVSTR "Ingenic JZ4780 EHCI"
struct jz4780_ehci_softc {
ehci_softc_t base; /* storage for EHCI code */
clk_t clk;
struct gpiobus_pin *gpio_vbus;
};
static device_probe_t jz4780_ehci_probe;
static device_attach_t jz4780_ehci_attach;
static device_detach_t jz4780_ehci_detach;
static int
jz4780_ehci_vbus_gpio_enable(device_t dev)
{
struct gpiobus_pin *gpio_vbus;
struct jz4780_ehci_softc *sc;
int err;
sc = device_get_softc(dev);
err = ofw_gpiobus_parse_gpios(dev, "ingenic,vbus-gpio", &gpio_vbus);
/*
* The pin can ne already mapped by other device. Optimistically
* surge ahead.
*/
if (err <= 0)
return (0);
sc->gpio_vbus = gpio_vbus;
if (err > 1) {
device_printf(dev, "too many vbus gpios\n");
return (ENXIO);
}
if (sc->gpio_vbus != NULL) {
err = GPIO_PIN_SETFLAGS(sc->gpio_vbus->dev, sc->gpio_vbus->pin,
GPIO_PIN_OUTPUT);
if (err != 0) {
device_printf(dev, "Cannot configure GPIO pin %d on %s\n",
sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev));
return (err);
}
err = GPIO_PIN_SET(sc->gpio_vbus->dev, sc->gpio_vbus->pin, 1);
if (err != 0) {
device_printf(dev, "Cannot configure GPIO pin %d on %s\n",
sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev));
return (err);
}
}
return (0);
}
static int
jz4780_ehci_clk_enable(device_t dev)
{
struct jz4780_ehci_softc *sc;
int err;
sc = device_get_softc(dev);
err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
if (err != 0) {
device_printf(dev, "unable to lookup device clock\n");
return (err);
}
err = clk_enable(sc->clk);
if (err != 0) {
device_printf(dev, "unable to enable device clock\n");
return (err);
}
err = clk_set_freq(sc->clk, 48000000, 0);
if (err != 0) {
device_printf(dev, "unable to set device clock to 48 kHZ\n");
return (err);
}
return (0);
}
static void
jz4780_ehci_intr(void *arg)
{
ehci_interrupt(arg);
}
static int
jz4780_ehci_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-ehci"))
return (ENXIO);
device_set_desc(dev, EHCI_HC_DEVSTR);
return (BUS_PROBE_DEFAULT);
}
static int
jz4780_ehci_attach(device_t dev)
{
struct jz4780_ehci_softc *isc;
ehci_softc_t *sc;
int err;
int rid;
uint32_t reg;
isc = device_get_softc(dev);
sc = &isc->base;
/* initialise some bus fields */
sc->sc_bus.parent = dev;
sc->sc_bus.devices = sc->sc_devices;
sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
sc->sc_bus.dma_bits = 32;
/* get all DMA memory */
if (usb_bus_mem_alloc_all(&sc->sc_bus,
USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) {
return (ENOMEM);
}
sc->sc_bus.usbrev = USB_REV_2_0;
err = jz4780_ehci_vbus_gpio_enable(dev);
if (err)
goto error;
rid = 0;
sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
if (!sc->sc_io_res) {
device_printf(dev, "Could not map memory\n");
goto error;
}
/*
* Craft special resource for bus space ops that handle
* byte-alignment of non-word addresses.
*/
sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
sc->sc_io_size = rman_get_size(sc->sc_io_res);
err = jz4780_ehci_clk_enable(dev);
if (err)
goto error;
if (jz4780_ehci_enable() != 0) {
device_printf(dev, "CGU failed to enable EHCI\n");
err = ENXIO;
goto error;
}
EWRITE4(sc, EHCI_USBINTR, 0);
rid = 0;
sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_ACTIVE | RF_SHAREABLE);
if (sc->sc_irq_res == NULL) {
device_printf(dev, "Could not allocate irq\n");
goto error;
}
sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
if (!sc->sc_bus.bdev) {
device_printf(dev, "Could not add USB device\n");
goto error;
}
device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
sprintf(sc->sc_vendor, "Ingenic");
err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
NULL, jz4780_ehci_intr, sc, &sc->sc_intr_hdl);
if (err) {
device_printf(dev, "Could not setup irq, %d\n", err);
sc->sc_intr_hdl = NULL;
goto error;
}
err = ehci_init(sc);
if (!err) {
/* Voodoo: set utmi data bus width on controller to 16 bit */
reg = EREAD4(sc, JZ_EHCI_REG_UTMI_BUS);
reg |= UTMI_BUS_WIDTH;
EWRITE4(sc, JZ_EHCI_REG_UTMI_BUS, reg);
err = device_probe_and_attach(sc->sc_bus.bdev);
}
if (err) {
device_printf(dev, "USB init failed err=%d\n", err);
goto error;
}
return (0);
error:
jz4780_ehci_detach(dev);
return (ENXIO);
}
static int
jz4780_ehci_detach(device_t dev)
{
struct jz4780_ehci_softc *isc;
ehci_softc_t *sc;
device_t bdev;
int err;
isc = device_get_softc(dev);
sc = &isc->base;
if (sc->sc_bus.bdev) {
bdev = sc->sc_bus.bdev;
device_detach(bdev);
device_delete_child(dev, bdev);
}
/* during module unload there are lots of children leftover */
device_delete_children(dev);
if (sc->sc_irq_res && sc->sc_intr_hdl) {
/*
* only call ehci_detach() after ehci_init()
*/
ehci_detach(sc);
err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl);
if (err)
/* XXX or should we panic? */
device_printf(dev, "Could not tear down irq, %d\n",
err);
sc->sc_intr_hdl = NULL;
}
if (sc->sc_irq_res) {
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
sc->sc_irq_res = NULL;
}
if (sc->sc_io_res) {
bus_release_resource(dev, SYS_RES_MEMORY, 0,
sc->sc_io_res);
sc->sc_io_res = NULL;
}
if (isc->clk)
clk_release(isc->clk);
usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
free(isc->gpio_vbus, M_DEVBUF);
return (0);
}
static device_method_t ehci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_ehci_probe),
DEVMETHOD(device_attach, jz4780_ehci_attach),
DEVMETHOD(device_detach, jz4780_ehci_detach),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD_END
};
static driver_t ehci_driver = {
.name = "ehci",
.methods = ehci_methods,
.size = sizeof(struct jz4780_ehci_softc),
};
static devclass_t ehci_devclass;
DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
MODULE_DEPEND(ehci, usb, 1, 1, 1);
MODULE_DEPEND(ehci, gpio, 1, 1, 1);

View File

@ -0,0 +1,830 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/resource.h>
#include <sys/gpio.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/gpio/gpiobusvar.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <mips/ingenic/jz4780_regs.h>
#include <gnu/dts/include/dt-bindings/interrupt-controller/irq.h>
#include "jz4780_gpio_if.h"
#include "gpio_if.h"
#include "pic_if.h"
#define JZ4780_GPIO_PINS 32
enum pin_function {
JZ_FUNC_DEV_0,
JZ_FUNC_DEV_1,
JZ_FUNC_DEV_2,
JZ_FUNC_DEV_3,
JZ_FUNC_GPIO,
JZ_FUNC_INTR,
};
struct jz4780_gpio_pin {
struct intr_irqsrc pin_irqsrc;
enum intr_trigger intr_trigger;
enum intr_polarity intr_polarity;
enum pin_function pin_func;
uint32_t pin_caps;
uint32_t pin_flags;
uint32_t pin_num;
char pin_name[GPIOMAXNAME];
};
struct jz4780_gpio_softc {
device_t dev;
device_t busdev;
struct resource *res[2];
struct mtx mtx;
struct jz4780_gpio_pin pins[JZ4780_GPIO_PINS];
void *intrhand;
};
static struct resource_spec jz4780_gpio_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_IRQ, 0, RF_ACTIVE },
{ -1, 0 }
};
static int jz4780_gpio_probe(device_t dev);
static int jz4780_gpio_attach(device_t dev);
static int jz4780_gpio_detach(device_t dev);
static int jz4780_gpio_intr(void *arg);
#define JZ4780_GPIO_LOCK(sc) mtx_lock_spin(&(sc)->mtx)
#define JZ4780_GPIO_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx)
#define JZ4780_GPIO_LOCK_INIT(sc) \
mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
"jz4780_gpio", MTX_SPIN)
#define JZ4780_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx);
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg))
static int
jz4780_gpio_probe(device_t dev)
{
phandle_t node;
if (!ofw_bus_status_okay(dev))
return (ENXIO);
/* We only like particular parent */
if (!ofw_bus_is_compatible(device_get_parent(dev),
"ingenic,jz4780-pinctrl"))
return (ENXIO);
/* ... and only specific children os that parent */
node = ofw_bus_get_node(dev);
if (!OF_hasprop(node, "gpio-controller"))
return (ENXIO);
device_set_desc(dev, "Ingenic JZ4780 GPIO Controller");
return (BUS_PROBE_DEFAULT);
}
static int
jz4780_gpio_pin_set_func(struct jz4780_gpio_softc *sc, uint32_t pin,
uint32_t func)
{
uint32_t mask = (1u << pin);
if (func > (uint32_t)JZ_FUNC_DEV_3)
return (EINVAL);
CSR_WRITE_4(sc, JZ_GPIO_INTC, mask);
CSR_WRITE_4(sc, JZ_GPIO_MASKC, mask);
if (func & 2)
CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask);
else
CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask);
if (func & 1)
CSR_WRITE_4(sc, JZ_GPIO_PAT0S, mask);
else
CSR_WRITE_4(sc, JZ_GPIO_PAT0C, mask);
sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
sc->pins[pin].pin_func = (enum pin_function)func;
return (0);
}
static int
jz4780_gpio_pin_set_direction(struct jz4780_gpio_softc *sc,
uint32_t pin, uint32_t dir)
{
uint32_t mask = (1u << pin);
switch (dir) {
case GPIO_PIN_OUTPUT:
if (sc->pins[pin].pin_caps & dir)
CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask);
else
return (EINVAL);
break;
case GPIO_PIN_INPUT:
if (sc->pins[pin].pin_caps & dir)
CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask);
else
return (EINVAL);
break;
}
sc->pins[pin].pin_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
sc->pins[pin].pin_flags |= dir;
return (0);
}
static int
jz4780_gpio_pin_set_bias(struct jz4780_gpio_softc *sc,
uint32_t pin, uint32_t bias)
{
uint32_t mask = (1u << pin);
switch (bias) {
case GPIO_PIN_PULLUP:
case GPIO_PIN_PULLDOWN:
if (sc->pins[pin].pin_caps & bias)
CSR_WRITE_4(sc, JZ_GPIO_DPULLC, mask);
else
return (EINVAL);
break;
case 0:
CSR_WRITE_4(sc, JZ_GPIO_DPULLS, mask);
break;
default:
return (ENOTSUP);
}
sc->pins[pin].pin_flags &= ~(GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
sc->pins[pin].pin_flags |= bias;
return (0);
}
/*
* Decode pin configuration using this map
*/
#if 0
INT MASK PAT1 PAT0
1 x 0 0 /* intr, level, low */
1 x 0 1 /* intr, level, high */
1 x 1 0 /* intr, edge, falling */
1 x 1 1 /* intr, edge, rising */
0 0 0 0 /* function, func 0 */
0 0 0 1 /* function, func 1 */
0 0 1 0 /* function, func 2 */
0 0 1 0 /* function, func 3 */
0 1 0 0 /* gpio, output 0 */
0 1 0 1 /* gpio, output 1 */
0 1 1 x /* gpio, input */
#endif
static void
jz4780_gpio_pin_probe(struct jz4780_gpio_softc *sc, uint32_t pin)
{
uint32_t mask = (1u << pin);
uint32_t val;
/* Clear cached gpio config */
sc->pins[pin].pin_flags = 0;
/* First check if pin is in interrupt mode */
val = CSR_READ_4(sc, JZ_GPIO_INT);
if (val & mask) {
/* Pin is in interrupt mode, decode interrupt triggering mode */
val = CSR_READ_4(sc, JZ_GPIO_PAT1);
if (val & mask)
sc->pins[pin].intr_trigger = INTR_TRIGGER_EDGE;
else
sc->pins[pin].intr_trigger = INTR_TRIGGER_LEVEL;
/* Decode interrupt polarity */
val = CSR_READ_4(sc, JZ_GPIO_PAT0);
if (val & mask)
sc->pins[pin].intr_polarity = INTR_POLARITY_HIGH;
else
sc->pins[pin].intr_polarity = INTR_POLARITY_LOW;
sc->pins[pin].pin_func = JZ_FUNC_INTR;
sc->pins[pin].pin_flags = 0;
return;
}
/* Next check if pin is in gpio mode */
val = CSR_READ_4(sc, JZ_GPIO_MASK);
if (val & mask) {
/* Pin is in gpio mode, decode direction and bias */
val = CSR_READ_4(sc, JZ_GPIO_PAT1);
if (val & mask)
sc->pins[pin].pin_flags |= GPIO_PIN_INPUT;
else
sc->pins[pin].pin_flags |= GPIO_PIN_OUTPUT;
/* Check for bias */
val = CSR_READ_4(sc, JZ_GPIO_DPULL);
if ((val & mask) == 0)
sc->pins[pin].pin_flags |= sc->pins[pin].pin_caps &
(GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
sc->pins[pin].pin_func = JZ_FUNC_GPIO;
return;
}
/* By exclusion, pin is in alternate function mode */
val = CSR_READ_4(sc, JZ_GPIO_DPULL);
if ((val & mask) == 0)
sc->pins[pin].pin_flags = sc->pins[pin].pin_caps &
(GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
val = ((CSR_READ_4(sc, JZ_GPIO_PAT1) & mask) >> pin) << 1;
val = val | ((CSR_READ_4(sc, JZ_GPIO_PAT1) & mask) >> pin);
sc->pins[pin].pin_func = (enum pin_function)val;
}
static int
jz4780_gpio_register_isrcs(struct jz4780_gpio_softc *sc)
{
int error;
uint32_t irq, i;
struct intr_irqsrc *isrc;
const char *name;
name = device_get_nameunit(sc->dev);
for (irq = 0; irq < JZ4780_GPIO_PINS; irq++) {
isrc = &sc->pins[irq].pin_irqsrc;
error = intr_isrc_register(isrc, sc->dev, 0, "%s,%d",
name, irq);
if (error != 0) {
for (i = 0; i < irq; i++)
intr_isrc_deregister(&sc->pins[i].pin_irqsrc);
device_printf(sc->dev, "%s failed", __func__);
return (error);
}
}
return (0);
}
static int
jz4780_gpio_attach(device_t dev)
{
struct jz4780_gpio_softc *sc = device_get_softc(dev);
phandle_t node;
uint32_t i, pd_pins, pu_pins;
sc->dev = dev;
if (bus_alloc_resources(dev, jz4780_gpio_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
return (ENXIO);
}
JZ4780_GPIO_LOCK_INIT(sc);
node = ofw_bus_get_node(dev);
OF_getencprop(node, "ingenic,pull-ups", &pu_pins, sizeof(pu_pins));
OF_getencprop(node, "ingenic,pull-downs", &pd_pins, sizeof(pd_pins));
for (i = 0; i < JZ4780_GPIO_PINS; i++) {
sc->pins[i].pin_num = i;
sc->pins[i].pin_caps |= GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
if (pu_pins & (1 << i))
sc->pins[i].pin_caps |= GPIO_PIN_PULLUP;
if (pd_pins & (1 << i))
sc->pins[i].pin_caps |= GPIO_PIN_PULLDOWN;
sc->pins[i].intr_polarity = INTR_POLARITY_CONFORM;
sc->pins[i].intr_trigger = INTR_TRIGGER_CONFORM;
snprintf(sc->pins[i].pin_name, GPIOMAXNAME - 1, "gpio%c%d",
device_get_unit(dev) + 'a', i);
sc->pins[i].pin_name[GPIOMAXNAME - 1] = '\0';
jz4780_gpio_pin_probe(sc, i);
}
if (jz4780_gpio_register_isrcs(sc) != 0)
goto fail;
if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) {
device_printf(dev, "could not register PIC\n");
goto fail;
}
if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
jz4780_gpio_intr, NULL, sc, &sc->intrhand) != 0)
goto fail_pic;
sc->busdev = gpiobus_attach_bus(dev);
if (sc->busdev == NULL)
goto fail_pic;
return (0);
fail_pic:
intr_pic_deregister(dev, OF_xref_from_node(node));
fail:
if (sc->intrhand != NULL)
bus_teardown_intr(dev, sc->res[1], sc->intrhand);
bus_release_resources(dev, jz4780_gpio_spec, sc->res);
JZ4780_GPIO_LOCK_DESTROY(sc);
return (ENXIO);
}
static int
jz4780_gpio_detach(device_t dev)
{
struct jz4780_gpio_softc *sc = device_get_softc(dev);
bus_release_resources(dev, jz4780_gpio_spec, sc->res);
JZ4780_GPIO_LOCK_DESTROY(sc);
return (0);
}
static int
jz4780_gpio_configure_pin(device_t dev, uint32_t pin, uint32_t func,
uint32_t flags)
{
struct jz4780_gpio_softc *sc;
int retval;
if (pin >= JZ4780_GPIO_PINS)
return (EINVAL);
sc = device_get_softc(dev);
JZ4780_GPIO_LOCK(sc);
retval = jz4780_gpio_pin_set_func(sc, pin, func);
if (retval == 0)
retval = jz4780_gpio_pin_set_bias(sc, pin, flags);
JZ4780_GPIO_UNLOCK(sc);
return (retval);
}
static device_t
jz4780_gpio_get_bus(device_t dev)
{
struct jz4780_gpio_softc *sc;
sc = device_get_softc(dev);
return (sc->busdev);
}
static int
jz4780_gpio_pin_max(device_t dev, int *maxpin)
{
*maxpin = JZ4780_GPIO_PINS - 1;
return (0);
}
static int
jz4780_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
{
struct jz4780_gpio_softc *sc;
if (pin >= JZ4780_GPIO_PINS)
return (EINVAL);
sc = device_get_softc(dev);
JZ4780_GPIO_LOCK(sc);
*caps = sc->pins[pin].pin_caps;
JZ4780_GPIO_UNLOCK(sc);
return (0);
}
static int
jz4780_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
{
struct jz4780_gpio_softc *sc;
if (pin >= JZ4780_GPIO_PINS)
return (EINVAL);
sc = device_get_softc(dev);
JZ4780_GPIO_LOCK(sc);
*flags = sc->pins[pin].pin_flags;
JZ4780_GPIO_UNLOCK(sc);
return (0);
}
static int
jz4780_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
{
struct jz4780_gpio_softc *sc;
if (pin >= JZ4780_GPIO_PINS)
return (EINVAL);
sc = device_get_softc(dev);
strncpy(name, sc->pins[pin].pin_name, GPIOMAXNAME - 1);
name[GPIOMAXNAME - 1] = '\0';
return (0);
}
static int
jz4780_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
{
struct jz4780_gpio_softc *sc;
int retval;
if (pin >= JZ4780_GPIO_PINS)
return (EINVAL);
sc = device_get_softc(dev);
JZ4780_GPIO_LOCK(sc);
retval = jz4780_gpio_pin_set_direction(sc, pin,
flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT));
if (retval == 0)
retval = jz4780_gpio_pin_set_bias(sc, pin,
flags & (GPIO_PIN_PULLDOWN | GPIO_PIN_PULLUP));
JZ4780_GPIO_UNLOCK(sc);
return (retval);
}
static int
jz4780_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
{
struct jz4780_gpio_softc *sc;
uint32_t mask;
int retval;
if (pin >= JZ4780_GPIO_PINS)
return (EINVAL);
retval = EINVAL;
mask = (1u << pin);
sc = device_get_softc(dev);
JZ4780_GPIO_LOCK(sc);
if (sc->pins[pin].pin_func == JZ_FUNC_GPIO) {
CSR_WRITE_4(sc, value ? JZ_GPIO_PAT0S : JZ_GPIO_PAT0C, mask);
retval = 0;
}
JZ4780_GPIO_UNLOCK(sc);
return (retval);
}
static int
jz4780_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
{
struct jz4780_gpio_softc *sc;
uint32_t data, mask;
if (pin >= JZ4780_GPIO_PINS)
return (EINVAL);
mask = (1u << pin);
sc = device_get_softc(dev);
JZ4780_GPIO_LOCK(sc);
data = CSR_READ_4(sc, JZ_GPIO_PIN);
JZ4780_GPIO_UNLOCK(sc);
*val = (data & mask) ? 1 : 0;
return (0);
}
static int
jz4780_gpio_pin_toggle(device_t dev, uint32_t pin)
{
struct jz4780_gpio_softc *sc;
uint32_t data, mask;
int retval;
if (pin >= JZ4780_GPIO_PINS)
return (EINVAL);
retval = EINVAL;
mask = (1u << pin);
sc = device_get_softc(dev);
JZ4780_GPIO_LOCK(sc);
if (sc->pins[pin].pin_func == JZ_FUNC_GPIO &&
sc->pins[pin].pin_flags & GPIO_PIN_OUTPUT) {
data = CSR_READ_4(sc, JZ_GPIO_PIN);
CSR_WRITE_4(sc, (data & mask) ? JZ_GPIO_PAT0C : JZ_GPIO_PAT0S,
mask);
retval = 0;
}
JZ4780_GPIO_UNLOCK(sc);
return (retval);
}
#ifdef FDT
static int
jz_gpio_map_intr_fdt(device_t dev, struct intr_map_data *data, u_int *irqp,
enum intr_polarity *polp, enum intr_trigger *trigp)
{
struct jz4780_gpio_softc *sc;
struct intr_map_data_fdt *daf;
sc = device_get_softc(dev);
daf = (struct intr_map_data_fdt *)data;
if (data == NULL || data->type != INTR_MAP_DATA_FDT ||
daf->ncells == 0 || daf->ncells > 2)
return (EINVAL);
*irqp = daf->cells[0];
if (daf->ncells == 1) {
*trigp = INTR_TRIGGER_CONFORM;
*polp = INTR_POLARITY_CONFORM;
return (0);
}
switch (daf->cells[1])
{
case IRQ_TYPE_EDGE_RISING:
*trigp = INTR_TRIGGER_EDGE;
*polp = INTR_POLARITY_HIGH;
break;
case IRQ_TYPE_EDGE_FALLING:
*trigp = INTR_TRIGGER_EDGE;
*polp = INTR_POLARITY_LOW;
break;
case IRQ_TYPE_LEVEL_HIGH:
*trigp = INTR_TRIGGER_LEVEL;
*polp = INTR_POLARITY_HIGH;
break;
case IRQ_TYPE_LEVEL_LOW:
*trigp = INTR_TRIGGER_LEVEL;
*polp = INTR_POLARITY_LOW;
break;
default:
device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n",
daf->cells[1]);
return (ENOTSUP);
}
return (0);
}
#endif
static int
jz_gpio_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
enum intr_polarity *polp, enum intr_trigger *trigp)
{
struct jz4780_gpio_softc *sc;
enum intr_polarity pol;
enum intr_trigger trig;
u_int irq;
sc = device_get_softc(dev);
switch (data->type) {
#ifdef FDT
case INTR_MAP_DATA_FDT:
if (jz_gpio_map_intr_fdt(dev, data, &irq, &pol, &trig) != 0)
return (EINVAL);
break;
#endif
default:
return (EINVAL);
}
if (irq >= nitems(sc->pins))
return (EINVAL);
*irqp = irq;
if (polp != NULL)
*polp = pol;
if (trigp != NULL)
*trigp = trig;
return (0);
}
static int
jz4780_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
struct intr_irqsrc **isrcp)
{
struct jz4780_gpio_softc *sc;
int retval;
u_int irq;
retval = jz_gpio_map_intr(dev, data, &irq, NULL, NULL);
if (retval == 0) {
sc = device_get_softc(dev);
*isrcp = &sc->pins[irq].pin_irqsrc;
}
return (retval);
}
static int
jz4780_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
struct resource *res, struct intr_map_data *data)
{
struct jz4780_gpio_softc *sc;
struct jz4780_gpio_pin *pin;
enum intr_polarity pol;
enum intr_trigger trig;
uint32_t mask, irq;
if (data == NULL)
return (ENOTSUP);
/* Get config for resource. */
if (jz_gpio_map_intr(dev, data, &irq, &pol, &trig))
return (EINVAL);
pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc);
if (isrc != &pin->pin_irqsrc)
return (EINVAL);
/* Compare config if this is not first setup. */
if (isrc->isrc_handlers != 0) {
if ((pol != INTR_POLARITY_CONFORM && pol != pin->intr_polarity) ||
(trig != INTR_TRIGGER_CONFORM && trig != pin->intr_trigger))
return (EINVAL);
else
return (0);
}
if (pol == INTR_POLARITY_CONFORM)
pol = INTR_POLARITY_LOW; /* just pick some */
if (trig == INTR_TRIGGER_CONFORM)
trig = INTR_TRIGGER_EDGE; /* just pick some */
sc = device_get_softc(dev);
mask = 1u << pin->pin_num;
JZ4780_GPIO_LOCK(sc);
CSR_WRITE_4(sc, JZ_GPIO_MASKS, mask);
CSR_WRITE_4(sc, JZ_GPIO_INTS, mask);
if (trig == INTR_TRIGGER_LEVEL)
CSR_WRITE_4(sc, JZ_GPIO_PAT1C, mask);
else
CSR_WRITE_4(sc, JZ_GPIO_PAT1S, mask);
if (pol == INTR_POLARITY_LOW)
CSR_WRITE_4(sc, JZ_GPIO_PAT0C, mask);
else
CSR_WRITE_4(sc, JZ_GPIO_PAT0S, mask);
pin->pin_func = JZ_FUNC_INTR;
pin->intr_trigger = trig;
pin->intr_polarity = pol;
CSR_WRITE_4(sc, JZ_GPIO_FLAGC, mask);
CSR_WRITE_4(sc, JZ_GPIO_MASKC, mask);
JZ4780_GPIO_UNLOCK(sc);
return (0);
}
static void
jz4780_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct jz4780_gpio_softc *sc;
struct jz4780_gpio_pin *pin;
sc = device_get_softc(dev);
pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc);
CSR_WRITE_4(sc, JZ_GPIO_MASKC, 1u << pin->pin_num);
}
static void
jz4780_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct jz4780_gpio_softc *sc;
struct jz4780_gpio_pin *pin;
sc = device_get_softc(dev);
pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc);
CSR_WRITE_4(sc, JZ_GPIO_MASKS, 1u << pin->pin_num);
}
static void
jz4780_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
{
jz4780_gpio_pic_disable_intr(dev, isrc);
}
static void
jz4780_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
{
jz4780_gpio_pic_enable_intr(dev, isrc);
}
static void
jz4780_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
{
struct jz4780_gpio_softc *sc;
struct jz4780_gpio_pin *pin;
sc = device_get_softc(dev);
pin = __containerof(isrc, struct jz4780_gpio_pin, pin_irqsrc);
CSR_WRITE_4(sc, JZ_GPIO_FLAGC, 1u << pin->pin_num);
}
static int
jz4780_gpio_intr(void *arg)
{
struct jz4780_gpio_softc *sc;
uint32_t i, interrupts;
sc = arg;
interrupts = CSR_READ_4(sc, JZ_GPIO_FLAG);
for (i = 0; interrupts != 0; i++, interrupts >>= 1) {
if ((interrupts & 0x1) == 0)
continue;
if (intr_isrc_dispatch(&sc->pins[i].pin_irqsrc,
curthread->td_intr_frame) != 0) {
device_printf(sc->dev, "spurious interrupt %d\n", i);
PIC_DISABLE_INTR(sc->dev, &sc->pins[i].pin_irqsrc);
}
}
return (FILTER_HANDLED);
}
static device_method_t jz4780_gpio_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_gpio_probe),
DEVMETHOD(device_attach, jz4780_gpio_attach),
DEVMETHOD(device_detach, jz4780_gpio_detach),
/* GPIO protocol */
DEVMETHOD(gpio_get_bus, jz4780_gpio_get_bus),
DEVMETHOD(gpio_pin_max, jz4780_gpio_pin_max),
DEVMETHOD(gpio_pin_getname, jz4780_gpio_pin_getname),
DEVMETHOD(gpio_pin_getflags, jz4780_gpio_pin_getflags),
DEVMETHOD(gpio_pin_getcaps, jz4780_gpio_pin_getcaps),
DEVMETHOD(gpio_pin_setflags, jz4780_gpio_pin_setflags),
DEVMETHOD(gpio_pin_get, jz4780_gpio_pin_get),
DEVMETHOD(gpio_pin_set, jz4780_gpio_pin_set),
DEVMETHOD(gpio_pin_toggle, jz4780_gpio_pin_toggle),
/* Custom interface to set pin function */
DEVMETHOD(jz4780_gpio_configure_pin, jz4780_gpio_configure_pin),
/* Interrupt controller interface */
DEVMETHOD(pic_setup_intr, jz4780_gpio_pic_setup_intr),
DEVMETHOD(pic_enable_intr, jz4780_gpio_pic_enable_intr),
DEVMETHOD(pic_disable_intr, jz4780_gpio_pic_disable_intr),
DEVMETHOD(pic_map_intr, jz4780_gpio_pic_map_intr),
DEVMETHOD(pic_post_filter, jz4780_gpio_pic_post_filter),
DEVMETHOD(pic_post_ithread, jz4780_gpio_pic_post_ithread),
DEVMETHOD(pic_pre_ithread, jz4780_gpio_pic_pre_ithread),
DEVMETHOD_END
};
static driver_t jz4780_gpio_driver = {
"gpio",
jz4780_gpio_methods,
sizeof(struct jz4780_gpio_softc),
};
static devclass_t jz4780_gpio_devclass;
EARLY_DRIVER_MODULE(jz4780_gpio, simplebus, jz4780_gpio_driver,
jz4780_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);

View File

@ -0,0 +1,41 @@
#-
# Copyright (c) 2015 Alexander Kabaev <kan@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
#include <sys/bus.h>
INTERFACE jz4780_gpio;
/**
* Configures pin as specified by FDT pinctrl entry
*/
METHOD int configure_pin {
device_t dev;
uint32_t gpio;
uint32_t func;
uint32_t flags;
};

View File

@ -0,0 +1,333 @@
/*-
* Copyright (c) 2015 Alexander Kabaev
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "opt_platform.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/pcpu.h>
#include <sys/proc.h>
#include <sys/cpuset.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/smp.h>
#include <sys/sched.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/smp.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <mips/ingenic/jz4780_regs.h>
#include "pic_if.h"
#define JZ4780_NIRQS 64
static int jz4780_pic_intr(void *);
struct jz4780_pic_isrc {
struct intr_irqsrc isrc;
u_int irq;
};
struct jz4780_pic_softc {
device_t pic_dev;
void * pic_intrhand;
struct resource * pic_res[2];
struct jz4780_pic_isrc pic_irqs[JZ4780_NIRQS];
uint32_t nirqs;
};
#define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc)
static struct resource_spec jz4780_pic_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */
{ SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt */
{ -1, 0 }
};
static struct ofw_compat_data compat_data[] = {
{"ingenic,jz4780-intc", true},
{NULL, false}
};
#define READ4(_sc, _reg) bus_read_4((_sc)->pic_res[0], _reg)
#define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->pic_res[0], _reg, _val)
static int
jz4780_pic_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
return (ENXIO);
device_set_desc(dev, "JZ4780 Interrupt Controller");
return (BUS_PROBE_DEFAULT);
}
static inline void
pic_irq_unmask(struct jz4780_pic_softc *sc, u_int irq)
{
if (irq < 32)
WRITE4(sc, JZ_ICMCR0, (1u << irq));
else
WRITE4(sc, JZ_ICMCR1, (1u << (irq - 32)));
}
static inline void
pic_irq_mask(struct jz4780_pic_softc *sc, u_int irq)
{
if (irq < 32)
WRITE4(sc, JZ_ICMSR0, (1u << irq));
else
WRITE4(sc, JZ_ICMSR1, (1u << (irq - 32)));
}
static inline intptr_t
pic_xref(device_t dev)
{
return (OF_xref_from_node(ofw_bus_get_node(dev)));
}
static int
jz4780_pic_register_isrcs(struct jz4780_pic_softc *sc)
{
int error;
uint32_t irq, i;
struct intr_irqsrc *isrc;
const char *name;
name = device_get_nameunit(sc->pic_dev);
for (irq = 0; irq < sc->nirqs; irq++) {
sc->pic_irqs[irq].irq = irq;
isrc = PIC_INTR_ISRC(sc, irq);
error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s,%d",
name, irq);
if (error != 0) {
for (i = 0; i < irq; i++)
intr_isrc_deregister(PIC_INTR_ISRC(sc, irq));
device_printf(sc->pic_dev, "%s failed", __func__);
return (error);
}
}
return (0);
}
static int
jz4780_pic_attach(device_t dev)
{
struct jz4780_pic_softc *sc;
intptr_t xref;
xref = pic_xref(dev);
sc = device_get_softc(dev);
if (bus_alloc_resources(dev, jz4780_pic_spec, sc->pic_res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
}
sc->pic_dev = dev;
/* Set the number of interrupts */
sc->nirqs = nitems(sc->pic_irqs);
/* Mask all interrupts */
WRITE4(sc, JZ_ICMR0, 0xFFFFFFFF);
WRITE4(sc, JZ_ICMR1, 0xFFFFFFFF);
/* Register the interrupts */
if (jz4780_pic_register_isrcs(sc) != 0) {
device_printf(dev, "could not register PIC ISRCs\n");
goto cleanup;
}
/*
* Now, when everything is initialized, it's right time to
* register interrupt controller to interrupt framefork.
*/
if (intr_pic_register(dev, xref) == NULL) {
device_printf(dev, "could not register PIC\n");
goto cleanup;
}
if (bus_setup_intr(dev, sc->pic_res[1], INTR_TYPE_CLK,
jz4780_pic_intr, NULL, sc, &sc->pic_intrhand)) {
device_printf(dev, "could not setup irq handler\n");
intr_pic_deregister(dev, xref);
goto cleanup;
}
return (0);
cleanup:
bus_release_resources(dev, jz4780_pic_spec, sc->pic_res);
return(ENXIO);
}
static int
jz4780_pic_intr(void *arg)
{
struct jz4780_pic_softc *sc = arg;
struct intr_irqsrc *isrc;
struct thread *td;
uint32_t i, intr;
td = curthread;
/* Workaround: do not inflate intr nesting level */
td->td_intr_nesting_level--;
intr = READ4(sc, JZ_ICPR0);
while ((i = fls(intr)) != 0) {
i--;
intr &= ~(1u << i);
isrc = PIC_INTR_ISRC(sc, i);
if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i);
pic_irq_mask(sc, i);
continue;
}
}
KASSERT(i == 0, ("all interrupts handled"));
intr = READ4(sc, JZ_ICPR1);
while ((i = fls(intr)) != 0) {
i--;
intr &= ~(1u << i);
i += 32;
isrc = PIC_INTR_ISRC(sc, i);
if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) {
device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i);
pic_irq_mask(sc, i);
continue;
}
}
KASSERT(i == 0, ("all interrupts handled"));
td->td_intr_nesting_level++;
return (FILTER_HANDLED);
}
static int
jz4780_pic_map_intr(device_t dev, struct intr_map_data *data,
struct intr_irqsrc **isrcp)
{
#ifdef FDT
struct jz4780_pic_softc *sc;
struct intr_map_data_fdt *daf;
sc = device_get_softc(dev);
daf = (struct intr_map_data_fdt *)data;
if (data == NULL || data->type != INTR_MAP_DATA_FDT ||
daf->ncells != 1 || daf->cells[0] >= sc->nirqs)
return (EINVAL);
*isrcp = PIC_INTR_ISRC(sc, daf->cells[0]);
return (0);
#else
return (EINVAL);
#endif
}
static void
jz4780_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct jz4780_pic_isrc *pic_isrc;
pic_isrc = (struct jz4780_pic_isrc *)isrc;
pic_irq_unmask(device_get_softc(dev), pic_isrc->irq);
}
static void
jz4780_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct jz4780_pic_isrc *pic_isrc;
pic_isrc = (struct jz4780_pic_isrc *)isrc;
pic_irq_mask(device_get_softc(dev), pic_isrc->irq);
}
static void
jz4780_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
{
jz4780_pic_disable_intr(dev, isrc);
}
static void
jz4780_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
{
jz4780_pic_enable_intr(dev, isrc);
}
static device_method_t jz4780_pic_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_pic_probe),
DEVMETHOD(device_attach, jz4780_pic_attach),
/* Interrupt controller interface */
DEVMETHOD(pic_enable_intr, jz4780_pic_enable_intr),
DEVMETHOD(pic_disable_intr, jz4780_pic_disable_intr),
DEVMETHOD(pic_map_intr, jz4780_pic_map_intr),
DEVMETHOD(pic_post_ithread, jz4780_pic_post_ithread),
DEVMETHOD(pic_pre_ithread, jz4780_pic_pre_ithread),
{ 0, 0 }
};
static driver_t jz4780_pic_driver = {
"intc",
jz4780_pic_methods,
sizeof(struct jz4780_pic_softc),
};
static devclass_t jz4780_pic_devclass;
EARLY_DRIVER_MODULE(intc, ofwbus, jz4780_pic_driver, jz4780_pic_devclass, 0, 0,
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);

View File

@ -0,0 +1,296 @@
/*-
* Copyright (c) 2009 Oleksandr Tymoshenko
* Copyright (c) 2015 Alexander Kabaev
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_ddb.h"
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/cons.h>
#include <sys/kdb.h>
#include <sys/reboot.h>
#ifdef FDT
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
#endif
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <net/ethernet.h>
#include <machine/clock.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/hwfunc.h>
#include <machine/md_var.h>
#include <machine/trap.h>
#include <machine/vmparam.h>
#include <mips/ingenic/jz4780_regs.h>
#include <mips/ingenic/jz4780_cpuregs.h>
uint32_t * const led = (uint32_t *)0xb0010548;
extern char edata[], end[];
static char boot1_env[4096];
void
platform_cpu_init(void)
{
uint32_t reg;
/*
* Do not expect mbox interrups while writing
* mbox
*/
reg = mips_rd_xburst_reim();
reg &= ~JZ_REIM_MIRQ0M;
mips_wr_xburst_reim(reg);
/* Clean mailboxes */
mips_wr_xburst_mbox0(0);
mips_wr_xburst_mbox1(0);
mips_wr_xburst_core_sts(~JZ_CORESTS_MIRQ0P);
/* Unmask mbox interrupts */
reg |= JZ_REIM_MIRQ0M;
mips_wr_xburst_reim(reg);
}
void
platform_reset(void)
{
/*
* For now, provoke a watchdog reset in about a second, so UART buffers
* have a fighting chance to flush before we pull the plug
*/
writereg(JZ_TCU_BASE + JZ_WDOG_TCER, 0); /* disable watchdog */
writereg(JZ_TCU_BASE + JZ_WDOG_TCNT, 0); /* reset counter */
writereg(JZ_TCU_BASE + JZ_WDOG_TDR, 128); /* wait for ~1s */
writereg(JZ_TCU_BASE + JZ_WDOG_TCSR, TCSR_RTC_EN | TCSR_DIV_256);
writereg(JZ_TCU_BASE + JZ_WDOG_TCER, TCER_ENABLE); /* fire! */
/* Wait for reset */
while (1)
;
}
static void
mips_init(void)
{
int i;
#ifdef FDT
struct mem_region mr[FDT_MEM_REGIONS];
uint64_t val;
int mr_cnt;
int j;
#endif
for (i = 0; i < 10; i++) {
phys_avail[i] = 0;
}
/* The minimal amount of memory Ingenic SoC can have. */
dump_avail[0] = phys_avail[0] = MIPS_KSEG0_TO_PHYS(kernel_kseg0_end);
physmem = realmem = btoc(32 * 1024 * 1024);
/*
* X1000 mips cpu special.
* TODO: do anyone know what is this ?
*/
__asm(
"li $2, 0xa9000000 \n\t"
"mtc0 $2, $5, 4 \n\t"
"nop \n\t"
::"r"(2));
#ifdef FDT
if (fdt_get_mem_regions(mr, &mr_cnt, &val) == 0) {
physmem = realmem = btoc(val);
KASSERT((phys_avail[0] >= mr[0].mr_start) && \
(phys_avail[0] < (mr[0].mr_start + mr[0].mr_size)),
("First region is not within FDT memory range"));
/* Limit size of the first region */
phys_avail[1] = (mr[0].mr_start + MIN(mr[0].mr_size, ctob(realmem)));
dump_avail[1] = phys_avail[1];
/* Add the rest of regions */
for (i = 1, j = 2; i < mr_cnt; i++, j+=2) {
phys_avail[j] = mr[i].mr_start;
phys_avail[j+1] = (mr[i].mr_start + mr[i].mr_size);
dump_avail[j] = phys_avail[j];
dump_avail[j+1] = phys_avail[j+1];
}
}
#endif
init_param1();
init_param2(physmem);
mips_cpu_init();
pmap_bootstrap();
mips_proc0_init();
mutex_init();
kdb_init();
led[0] = 0x8000;
#ifdef KDB
if (boothowto & RB_KDB)
kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
#endif
}
static void
_parse_bootarg(char *v)
{
char *n;
if (*v == '-') {
while (*v != '\0') {
v++;
switch (*v) {
case 'a': boothowto |= RB_ASKNAME; break;
/* Someone should simulate that ;-) */
case 'C': boothowto |= RB_CDROM; break;
case 'd': boothowto |= RB_KDB; break;
case 'D': boothowto |= RB_MULTIPLE; break;
case 'm': boothowto |= RB_MUTE; break;
case 'g': boothowto |= RB_GDB; break;
case 'h': boothowto |= RB_SERIAL; break;
case 'p': boothowto |= RB_PAUSE; break;
case 'r': boothowto |= RB_DFLTROOT; break;
case 's': boothowto |= RB_SINGLE; break;
case 'v': boothowto |= RB_VERBOSE; break;
}
}
} else {
n = strsep(&v, "=");
if (v == NULL)
kern_setenv(n, "1");
else
kern_setenv(n, v);
}
}
static void
_parse_cmdline(int argc, char *argv[])
{
int i;
for (i = 1; i < argc; i++)
_parse_bootarg(argv[i]);
}
#ifdef FDT
/* Parse cmd line args as env - copied from xlp_machdep. */
/* XXX-BZ this should really be centrally provided for all (boot) code. */
static void
_parse_bootargs(char *cmdline)
{
char *v;
while ((v = strsep(&cmdline, " \n")) != NULL) {
if (*v == '\0')
continue;
_parse_bootarg(v);
}
}
#endif
void
platform_start(__register_t a0, __register_t a1,
__register_t a2 __unused, __register_t a3 __unused)
{
char **argv;
int argc;
vm_offset_t kernend;
#ifdef FDT
vm_offset_t dtbp;
phandle_t chosen;
char buf[2048]; /* early stack supposedly big enough */
#endif
/*
* clear the BSS and SBSS segments, this should be first call in
* the function
*/
kernend = (vm_offset_t)&end;
memset(&edata, 0, kernend - (vm_offset_t)(&edata));
mips_postboot_fixup();
/* Initialize pcpu stuff */
mips_pcpu0_init();
/* Something to hold kernel env until kmem is available */
init_static_kenv(boot1_env, sizeof(boot1_env));
#ifdef FDT
/*
* Find the dtb passed in by the boot loader (currently fictional).
*/
dtbp = (vm_offset_t)NULL;
#if defined(FDT_DTB_STATIC)
/*
* In case the device tree blob was not retrieved (from metadata) try
* to use the statically embedded one.
*/
if (dtbp == (vm_offset_t)NULL)
dtbp = (vm_offset_t)&fdt_static_dtb;
#else
#error "Non-static FDT not supported on JZ4780"
#endif
if (OF_install(OFW_FDT, 0) == FALSE)
while (1);
if (OF_init((void *)dtbp) != 0)
while (1);
#endif
cninit();
#ifdef FDT
/*
* Get bootargs from FDT if specified.
*/
chosen = OF_finddevice("/chosen");
if (OF_getprop(chosen, "bootargs", buf, sizeof(buf)) != -1)
_parse_bootargs(buf);
#endif
/* Parse cmdline from U-Boot */
argc = a0;
argv = (char **)a1;
_parse_cmdline(argc, argv);
mips_init();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
/*-
* Copyright (c) 2015 Alexander Kabaev <kan@FreeBSD.org>
* Copyright (c) 2004-2010 Juli Mallett <jmallett@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/smp.h>
#include <sys/systm.h>
#include <machine/cpufunc.h>
#include <machine/hwfunc.h>
#include <machine/md_var.h>
#include <machine/smp.h>
#include <mips/ingenic/jz4780_regs.h>
#include <mips/ingenic/jz4780_cpuregs.h>
void jz4780_mpentry(void);
#define JZ4780_MAXCPU 2
void
platform_ipi_send(int cpuid)
{
if (cpuid == 0)
mips_wr_xburst_mbox0(1);
else
mips_wr_xburst_mbox1(1);
}
void
platform_ipi_clear(void)
{
int cpuid = PCPU_GET(cpuid);
uint32_t action;
action = (cpuid == 0) ? mips_rd_xburst_mbox0() : mips_rd_xburst_mbox1();
KASSERT(action == 1, ("CPU %d: unexpected IPIs: %#x", cpuid, action));
mips_wr_xburst_core_sts(~(JZ_CORESTS_MIRQ0P << cpuid));
}
int
platform_processor_id(void)
{
return (mips_rd_ebase() & 7);
}
int
platform_ipi_hardintr_num(void)
{
return (1);
}
int
platform_ipi_softintr_num(void)
{
return (-1);
}
void
platform_init_ap(int cpuid)
{
unsigned reg;
/*
* Clear any pending IPIs.
*/
mips_wr_xburst_core_sts(~(JZ_CORESTS_MIRQ0P << cpuid));
/* Allow IPI mbox for this core */
reg = mips_rd_xburst_reim();
reg |= (JZ_REIM_MIRQ0M << cpuid);
mips_wr_xburst_reim(reg);
/*
* Unmask the ipi interrupts.
*/
reg = hard_int_mask(platform_ipi_hardintr_num());
set_intr_mask(reg);
}
void
platform_cpu_mask(cpuset_t *mask)
{
uint32_t i, m;
CPU_ZERO(mask);
for (i = 0, m = 1 ; i < JZ4780_MAXCPU; i++, m <<= 1)
CPU_SET(i, mask);
}
struct cpu_group *
platform_smp_topo(void)
{
return (smp_topo_none());
}
static void
jz4780_core_powerup(void)
{
uint32_t reg;
reg = readreg(JZ_CGU_BASE + JZ_LPCR);
reg &= ~LPCR_PD_SCPU;
writereg(JZ_CGU_BASE + JZ_LPCR, reg);
do {
reg = readreg(JZ_CGU_BASE + JZ_LPCR);
} while ((reg & LPCR_SCPUS) != 0);
}
/*
* Spin up the second code. The code is roughly modeled after
* similar routine in Linux.
*/
int
platform_start_ap(int cpuid)
{
uint32_t reg, addr;
if (cpuid >= JZ4780_MAXCPU)
return (EINVAL);
/* Figure out address of mpentry in KSEG1 */
addr = MIPS_PHYS_TO_KSEG1(MIPS_KSEG0_TO_PHYS(jz4780_mpentry));
KASSERT((addr & ~JZ_REIM_ENTRY_MASK) == 0,
("Unaligned mpentry"));
/* Configure core alternative entry point */
reg = mips_rd_xburst_reim();
reg &= ~JZ_REIM_ENTRY_MASK;
reg |= addr & JZ_REIM_ENTRY_MASK;
/* Allow this core to get IPIs from one being started */
reg |= JZ_REIM_MIRQ0M;
mips_wr_xburst_reim(reg);
/* Force core into reset and enable use of alternate entry point */
reg = mips_rd_xburst_core_ctl();
reg |= (JZ_CORECTL_SWRST0 << cpuid) | (JZ_CORECTL_RPC0 << cpuid);
mips_wr_xburst_core_ctl(reg);
/* Power the core up */
jz4780_core_powerup();
/* Take the core out of reset */
reg &= ~(JZ_CORECTL_SWRST0 << cpuid);
mips_wr_xburst_core_ctl(reg);
return (0);
}

View File

@ -0,0 +1,62 @@
/*-
* Copyright (c) 2015 Alexander Kabaev
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <machine/asm.h>
#include <machine/cpu.h>
#include <machine/cpuregs.h>
#include <machine/cache_r4k.h>
#include "assym.s"
#define CACHE_SIZE (32 * 1024)
#define CACHE_LINESIZE 32
.text
.set noat
.set noreorder
.section .text.mpentry_jz4780
.balign 0x10000
GLOBAL(jz4780_mpentry)
/* Initialize caches */
li t0, MIPS_KSEG0_START
ori t1, t0, CACHE_SIZE
mtc0 zero, MIPS_COP_0_TAG_LO
COP0_SYNC
1: cache CACHEOP_R4K_INDEX_STORE_TAG | CACHE_R4K_I, 0(t0)
cache CACHEOP_R4K_INDEX_STORE_TAG | CACHE_R4K_D, 0(t0)
bne t0, t1, 1b
addiu t0, t0, CACHE_LINESIZE
/* Set TLB page mask */
mtc0 zero, MIPS_COP_0_TLB_PG_MASK
COP0_SYNC
j mpentry
nop

View File

@ -0,0 +1,123 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
struct jz4780_nand_softc {
device_t dev;
struct resource *res[1];
};
static struct resource_spec jz4780_nand_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
static int jz4780_nand_probe(device_t dev);
static int jz4780_nand_attach(device_t dev);
static int jz4780_nand_detach(device_t dev);
static int
jz4780_nand_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-nand"))
return (ENXIO);
device_set_desc(dev, "Ingenic JZ4780 NAND Controller");
return (BUS_PROBE_DEFAULT);
}
static int
jz4780_nand_attach(device_t dev)
{
struct jz4780_nand_softc *sc = device_get_softc(dev);
sc->dev = dev;
if (bus_alloc_resources(dev, jz4780_nand_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
return (ENXIO);
}
return (0);
}
static int
jz4780_nand_detach(device_t dev)
{
struct jz4780_nand_softc *sc = device_get_softc(dev);
bus_release_resources(dev, jz4780_nand_spec, sc->res);
return (0);
}
static device_method_t jz4780_nand_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_nand_probe),
DEVMETHOD(device_attach, jz4780_nand_attach),
DEVMETHOD(device_detach, jz4780_nand_detach),
DEVMETHOD_END
};
static driver_t jz4780_nand_driver = {
"nand",
jz4780_nand_methods,
sizeof(struct jz4780_nand_softc),
};
static devclass_t jz4780_nand_devclass;
DRIVER_MODULE(jz4780_nand, simplebus, jz4780_nand_driver,
jz4780_nand_devclass, 0, 0);

View File

@ -0,0 +1,373 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Ingenic JZ4780 NAND and External Memory Controller (NEMC) driver.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/extres/clk/clk.h>
#include <dev/fdt/fdt_common.h>
#include <dev/fdt/simplebus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <mips/ingenic/jz4780_regs.h>
struct jz4780_nemc_devinfo {
struct simplebus_devinfo sinfo;
uint32_t bank;
};
struct jz4780_nemc_softc {
struct simplebus_softc simplebus_sc;
device_t dev;
struct resource *res[1];
uint32_t banks;
uint32_t clock_tick_psecs;
clk_t clk;
};
static struct resource_spec jz4780_nemc_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ -1, 0 }
};
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
static int jz4780_nemc_probe(device_t dev);
static int jz4780_nemc_attach(device_t dev);
static int jz4780_nemc_detach(device_t dev);
static int
jz4780_nemc_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-nemc"))
return (ENXIO);
device_set_desc(dev, "Ingenic JZ4780 NEMC");
return (BUS_PROBE_DEFAULT);
}
#define JZ4780_NEMC_NS_TO_TICKS(sc, val) howmany((val) * 1000, (sc)->clock_tick_psecs)
/* Use table from JZ4780 programmers manual to convert ticks to tBP/tAW register values */
static const uint8_t ticks_to_tBP_tAW[32] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 1:1 mapping */
11, 11, /* 12 cycles */
12, 12, 12, /* 15 cycles */
13, 13, 13, 13, 13, /* 20 cycles */
14, 14, 14, 14, 14, /* 25 cycles */
15, 15, 15, 15, 15, 15 /* 31 cycles */
};
static int
jz4780_nemc_configure_bank(struct jz4780_nemc_softc *sc,
device_t dev, u_int bank)
{
uint32_t smcr, cycles;
phandle_t node;
pcell_t val;
/* Check if bank is configured already */
if (sc->banks & (1 << bank))
return 0;
smcr = CSR_READ_4(sc, JZ_NEMC_SMCR(bank));
smcr &= ~JZ_NEMC_SMCR_SMT_MASK;
smcr |= JZ_NEMC_SMCR_SMT_NORMAL << JZ_NEMC_SMCR_SMT_SHIFT;
node = ofw_bus_get_node(dev);
if (OF_getencprop(node, "ingenic,nemc-tAS", &val, sizeof(val)) > 0) {
cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
if (cycles > 15) {
device_printf(sc->dev,
"invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
"ingenic,nemc-tAS", val, cycles, 15);
return -1;
}
smcr &= ~JZ_NEMC_SMCR_TAS_MASK;
smcr |= cycles << JZ_NEMC_SMCR_TAS_SHIFT;
}
if (OF_getencprop(node, "ingenic,nemc-tAH", &val, sizeof(val)) > 0) {
cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
if (cycles > 15) {
device_printf(sc->dev,
"invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
"ingenic,nemc-tAH", val, cycles, 15);
return -1;
}
smcr &= ~JZ_NEMC_SMCR_TAH_MASK;
smcr |= cycles << JZ_NEMC_SMCR_TAH_SHIFT;
}
if (OF_getencprop(node, "ingenic,nemc-tBP", &val, sizeof(val)) > 0) {
cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
if (cycles > 31) {
device_printf(sc->dev,
"invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
"ingenic,nemc-tBP", val, cycles, 15);
return -1;
}
smcr &= ~JZ_NEMC_SMCR_TBP_MASK;
smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TBP_SHIFT;
}
if (OF_getencprop(node, "ingenic,nemc-tAW", &val, sizeof(val)) > 0) {
cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
if (cycles > 31) {
device_printf(sc->dev,
"invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
"ingenic,nemc-tAW", val, cycles, 15);
return -1;
}
smcr &= ~JZ_NEMC_SMCR_TAW_MASK;
smcr |= ticks_to_tBP_tAW[cycles] << JZ_NEMC_SMCR_TAW_SHIFT;
}
if (OF_getencprop(node, "ingenic,nemc-tSTRV", &val, sizeof(val)) > 0) {
cycles = JZ4780_NEMC_NS_TO_TICKS(sc, val);
if (cycles > 63) {
device_printf(sc->dev,
"invalid value of %s %u (%u cycles), maximum %u cycles supported\n",
"ingenic,nemc-tSTRV", val, cycles, 15);
return -1;
}
smcr &= ~JZ_NEMC_SMCR_STRV_MASK;
smcr |= cycles << JZ_NEMC_SMCR_STRV_SHIFT;
}
CSR_WRITE_4(sc, JZ_NEMC_SMCR(bank), smcr);
sc->banks |= (1 << bank);
return 0;
}
/* Wholesale copy of simplebus routine */
static int
jz4780_nemc_fill_ranges(phandle_t node, struct simplebus_softc *sc)
{
int host_address_cells;
cell_t *base_ranges;
ssize_t nbase_ranges;
int err;
int i, j, k;
err = OF_searchencprop(OF_parent(node), "#address-cells",
&host_address_cells, sizeof(host_address_cells));
if (err <= 0)
return (-1);
nbase_ranges = OF_getproplen(node, "ranges");
if (nbase_ranges < 0)
return (-1);
sc->nranges = nbase_ranges / sizeof(cell_t) /
(sc->acells + host_address_cells + sc->scells);
if (sc->nranges == 0)
return (0);
sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
M_DEVBUF, M_WAITOK);
base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
for (i = 0, j = 0; i < sc->nranges; i++) {
sc->ranges[i].bus = 0;
for (k = 0; k < sc->acells; k++) {
sc->ranges[i].bus <<= 32;
sc->ranges[i].bus |= base_ranges[j++];
}
sc->ranges[i].host = 0;
for (k = 0; k < host_address_cells; k++) {
sc->ranges[i].host <<= 32;
sc->ranges[i].host |= base_ranges[j++];
}
sc->ranges[i].size = 0;
for (k = 0; k < sc->scells; k++) {
sc->ranges[i].size <<= 32;
sc->ranges[i].size |= base_ranges[j++];
}
}
free(base_ranges, M_DEVBUF);
return (sc->nranges);
}
static int
jz4780_nemc_attach(device_t dev)
{
struct jz4780_nemc_softc *sc = device_get_softc(dev);
phandle_t node;
uint64_t freq;
sc->dev = dev;
if (bus_alloc_resources(dev, jz4780_nemc_spec, sc->res)) {
device_printf(dev, "could not allocate resources for device\n");
return (ENXIO);
}
node = ofw_bus_get_node(dev);
/* Initialize simplebus and enumerate resources */
simplebus_init(dev, node);
if (jz4780_nemc_fill_ranges(node, &sc->simplebus_sc) < 0)
goto error;
/* Figure our underlying clock rate. */
if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) {
device_printf(dev, "could not lookup device clock\n");
goto error;
}
if (clk_enable(sc->clk) != 0) {
device_printf(dev, "could not enable device clock\n");
goto error;
}
if (clk_get_freq(sc->clk, &freq) != 0) {
device_printf(dev, "could not determine clock speed\n");
goto error;
}
/* Convert clock frequency to picoseconds-per-tick value. */
sc->clock_tick_psecs = (uint32_t)(1000000000000ULL / freq);
/*
* Allow devices to identify.
*/
bus_generic_probe(dev);
/*
* Now walk the tree and attach top level devices
*/
for (node = OF_child(node); node > 0; node = OF_peer(node))
simplebus_add_device(dev, node, 0, NULL, -1, NULL);
return (bus_generic_attach(dev));
error:
jz4780_nemc_detach(dev);
return (ENXIO);
}
static int
jz4780_nemc_detach(device_t dev)
{
struct jz4780_nemc_softc *sc = device_get_softc(dev);
bus_generic_detach(dev);
if (sc->clk != NULL)
clk_release(sc->clk);
bus_release_resources(dev, jz4780_nemc_spec, sc->res);
return (0);
}
static int
jz4780_nemc_decode_bank(struct simplebus_softc *sc, struct resource *r,
u_int *bank)
{
rman_res_t start, end;
int i;
start = rman_get_start(r);
end = rman_get_end(r);
/* Remap through ranges property */
for (i = 0; i < sc->nranges; i++) {
if (start >= sc->ranges[i].host && end <
sc->ranges[i].host + sc->ranges[i].size) {
*bank = (sc->ranges[i].bus >> 32);
return (0);
}
}
return (1);
}
static int
jz4780_nemc_activate_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
struct jz4780_nemc_softc *sc;
u_int bank;
int err;
if (type == SYS_RES_MEMORY) {
sc = device_get_softc(bus);
/* Figure out on what bank device is residing */
err = jz4780_nemc_decode_bank(&sc->simplebus_sc, r, &bank);
if (err == 0) {
/* Attempt to configure the bank if not done already */
err = jz4780_nemc_configure_bank(sc, child, bank);
if (err != 0)
return (err);
}
}
/* Call default implementation to finish the work */
return (bus_generic_activate_resource(bus, child,
type, rid, r));
}
static device_method_t jz4780_nemc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_nemc_probe),
DEVMETHOD(device_attach, jz4780_nemc_attach),
DEVMETHOD(device_detach, jz4780_nemc_detach),
/* Overrides to configure bank on resource activation */
DEVMETHOD(bus_activate_resource, jz4780_nemc_activate_resource),
DEVMETHOD_END
};
static devclass_t jz4780_nemc_devclass;
DEFINE_CLASS_1(nemc, jz4780_nemc_driver, jz4780_nemc_methods,
sizeof(struct jz4780_nemc_softc), simplebus_driver);
DRIVER_MODULE(jz4780_nemc, simplebus, jz4780_nemc_driver,
jz4780_nemc_devclass, 0, 0);

View File

@ -0,0 +1,318 @@
/*-
* Copyright (c) 2015, Alexander Kabaev <kan@FreeBSD.org>
* Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/condvar.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/gpio.h>
#include <dev/extres/clk/clk.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_util.h>
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/controller/ohci.h>
#include <dev/usb/controller/ohcireg.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/gpio/gpiobusvar.h>
#include <mips/ingenic/jz4780_clock.h>
#include <mips/ingenic/jz4780_regs.h>
static int jz4780_ohci_attach(device_t dev);
static int jz4780_ohci_detach(device_t dev);
static int jz4780_ohci_probe(device_t dev);
struct jz4780_ohci_softc
{
struct ohci_softc sc_ohci;
struct gpiobus_pin *gpio_vbus;
clk_t clk;
};
static int
jz4780_ohci_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-ohci"))
return (ENXIO);
device_set_desc(dev, "Ingenic JZ4780 OHCI");
return (BUS_PROBE_DEFAULT);
}
static int
jz4780_ohci_vbus_gpio_enable(device_t dev, struct jz4780_ohci_softc *sc)
{
struct gpiobus_pin *gpio_vbus;
int error;
error = ofw_gpiobus_parse_gpios(dev, "ingenic,vbus-gpio", &gpio_vbus);
/*
* The pin can be mapped already by other device. Assume it also has need
* activated and proceed happily.
*/
if (error <= 0)
return (0);
sc->gpio_vbus = gpio_vbus;
if (error > 1) {
device_printf(dev, "too many vbus gpios\n");
return (ENXIO);
}
if (sc->gpio_vbus != NULL) {
error = GPIO_PIN_SET(sc->gpio_vbus->dev, sc->gpio_vbus->pin, 1);
if (error != 0) {
device_printf(dev, "Cannot configure GPIO pin %d on %s\n",
sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev));
return (error);
}
error = GPIO_PIN_SETFLAGS(sc->gpio_vbus->dev, sc->gpio_vbus->pin,
GPIO_PIN_OUTPUT);
if (error != 0) {
device_printf(dev, "Cannot configure GPIO pin %d on %s\n",
sc->gpio_vbus->pin, device_get_nameunit(sc->gpio_vbus->dev));
return (error);
}
}
return (0);
}
static int
jz4780_ohci_clk_enable(device_t dev)
{
struct jz4780_ohci_softc *sc;
int err;
sc = device_get_softc(dev);
err = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
if (err != 0) {
device_printf(dev, "unable to lookup device clock\n");
return (err);
}
err = clk_enable(sc->clk);
if (err != 0) {
device_printf(dev, "unable to enable device clock\n");
return (err);
}
err = clk_set_freq(sc->clk, 48000000, 0);
if (err != 0) {
device_printf(dev, "unable to set device clock to 48 kHZ\n");
return (err);
}
return (0);
}
static int
jz4780_ohci_attach(device_t dev)
{
struct jz4780_ohci_softc *sc = device_get_softc(dev);
int err;
int rid;
/* initialize some bus fields */
sc->sc_ohci.sc_bus.parent = dev;
sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices;
sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES;
sc->sc_ohci.sc_bus.dma_bits = 32;
/* get all DMA memory */
if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus,
USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) {
return (ENOMEM);
}
sc->sc_ohci.sc_dev = dev;
/* frob vbus gpio */
err = jz4780_ohci_vbus_gpio_enable(dev, sc);
if (err)
goto error;
err = jz4780_ohci_clk_enable(dev);
if (err)
goto error;
rid = 0;
sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->sc_ohci.sc_io_res == NULL) {
err = ENOMEM;
goto error;
}
sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res);
sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res);
sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res);
rid = 0;
sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_ACTIVE);
if (sc->sc_ohci.sc_irq_res == NULL) {
err = ENOMEM;
goto error;
}
if (jz4780_ohci_enable() != 0) {
device_printf(dev, "CGU failed to enable OHCI\n");
err = ENXIO;
goto error;
}
sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
if (sc->sc_ohci.sc_bus.bdev == NULL) {
err = ENOMEM;
goto error;
}
device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus);
err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res,
INTR_TYPE_BIO | INTR_MPSAFE, NULL,
(driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl);
if (err) {
err = ENXIO;
goto error;
}
strlcpy(sc->sc_ohci.sc_vendor, "Ingenic", sizeof(sc->sc_ohci.sc_vendor));
bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0);
err = ohci_init(&sc->sc_ohci);
if (!err)
err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev);
if (err)
goto error;
return (0);
error:
if (err)
jz4780_ohci_detach(dev);
return (err);
}
static int
jz4780_ohci_detach(device_t dev)
{
struct jz4780_ohci_softc *sc = device_get_softc(dev);
device_t bdev;
if (sc->sc_ohci.sc_bus.bdev) {
bdev = sc->sc_ohci.sc_bus.bdev;
device_detach(bdev);
device_delete_child(dev, bdev);
}
/* during module unload there are lots of children leftover */
device_delete_children(dev);
/*
* Put the controller into reset, then disable clocks and do
* the MI tear down. We have to disable the clocks/hardware
* after we do the rest of the teardown. We also disable the
* clocks in the opposite order we acquire them, but that
* doesn't seem to be absolutely necessary. We free up the
* clocks after we disable them, so the system could, in
* theory, reuse them.
*/
if (sc->sc_ohci.sc_io_res != NULL) {
bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl,
OHCI_CONTROL, 0);
}
if (sc->sc_ohci.sc_intr_hdl) {
bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl);
sc->sc_ohci.sc_intr_hdl = NULL;
}
if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) {
/*
* only call ohci_detach() after ohci_init()
*/
ohci_detach(&sc->sc_ohci);
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res);
sc->sc_ohci.sc_irq_res = NULL;
}
if (sc->sc_ohci.sc_io_res) {
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.sc_io_res);
sc->sc_ohci.sc_io_res = NULL;
sc->sc_ohci.sc_io_tag = 0;
sc->sc_ohci.sc_io_hdl = 0;
}
if (sc->clk != NULL)
clk_release(sc->clk);
usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc);
free(sc->gpio_vbus, M_DEVBUF);
return (0);
}
static device_method_t ohci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_ohci_probe),
DEVMETHOD(device_attach, jz4780_ohci_attach),
DEVMETHOD(device_detach, jz4780_ohci_detach),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD_END
};
static driver_t ohci_driver = {
.name = "ohci",
.methods = ohci_methods,
.size = sizeof(struct jz4780_ohci_softc),
};
static devclass_t ohci_devclass;
DRIVER_MODULE(ohci, simplebus, ohci_driver, ohci_devclass, 0, 0);

View File

@ -0,0 +1,260 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Ingenic JZ4780 pinctrl driver.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/gpio.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fdt/fdt_common.h>
#include <dev/fdt/fdt_pinctrl.h>
#include <dev/fdt/simplebus.h>
#include <mips/ingenic/jz4780_regs.h>
#include "jz4780_gpio_if.h"
struct jz4780_pinctrl_softc {
struct simplebus_softc ssc;
device_t dev;
};
#define CHIP_REG_STRIDE 256
#define CHIP_REG_OFFSET(base, chip) ((base) + (chip) * CHIP_REG_STRIDE)
static int
jz4780_pinctrl_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-pinctrl"))
return (ENXIO);
device_set_desc(dev, "Ingenic JZ4780 GPIO");
return (BUS_PROBE_DEFAULT);
}
static int
jz4780_pinctrl_attach(device_t dev)
{
struct jz4780_pinctrl_softc *sc;
struct resource_list *rs;
struct resource_list_entry *re;
phandle_t dt_parent, dt_child;
int i, ret;
sc = device_get_softc(dev);
sc->dev = dev;
/*
* Fetch our own resource list to dole memory between children
*/
rs = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
if (rs == NULL)
return (ENXIO);
re = resource_list_find(rs, SYS_RES_MEMORY, 0);
if (re == NULL)
return (ENXIO);
simplebus_init(dev, 0);
/* Iterate over this node children, looking for pin controllers */
dt_parent = ofw_bus_get_node(dev);
i = 0;
for (dt_child = OF_child(dt_parent); dt_child != 0;
dt_child = OF_peer(dt_child)) {
struct simplebus_devinfo *ndi;
device_t child;
bus_addr_t phys;
bus_size_t size;
/* Add gpio controller child */
if (!OF_hasprop(dt_child, "gpio-controller"))
continue;
child = simplebus_add_device(dev, dt_child, 0, NULL, -1, NULL);
if (child == NULL)
break;
/* Setup child resources */
phys = CHIP_REG_OFFSET(re->start, i);
size = CHIP_REG_STRIDE;
if (phys + size - 1 <= re->end) {
ndi = device_get_ivars(child);
resource_list_add(&ndi->rl, SYS_RES_MEMORY, 0,
phys, phys + size - 1, size);
}
i++;
}
ret = bus_generic_attach(dev);
if (ret == 0) {
fdt_pinctrl_register(dev, "ingenic,pins");
fdt_pinctrl_configure_tree(dev);
}
return (ret);
}
static int
jz4780_pinctrl_detach(device_t dev)
{
bus_generic_detach(dev);
return (0);
}
struct jx4780_bias_prop {
const char *name;
uint32_t bias;
};
static struct jx4780_bias_prop jx4780_bias_table[] = {
{ "bias-disable", 0 },
{ "bias-pull-up", GPIO_PIN_PULLUP },
{ "bias-pull-down", GPIO_PIN_PULLDOWN },
};
static int
jz4780_pinctrl_parse_pincfg(phandle_t pincfgxref, uint32_t *bias_value)
{
phandle_t pincfg_node;
int i;
pincfg_node = OF_node_from_xref(pincfgxref);
for (i = 0; i < nitems(jx4780_bias_table); i++) {
if (OF_hasprop(pincfg_node, jx4780_bias_table[i].name)) {
*bias_value = jx4780_bias_table[i].bias;
return 0;
}
}
return -1;
}
static device_t
jz4780_pinctrl_chip_lookup(struct jz4780_pinctrl_softc *sc, phandle_t chipxref)
{
device_t chipdev;
chipdev = OF_device_from_xref(chipxref);
return chipdev;
}
static int
jz4780_pinctrl_configure_pins(device_t dev, phandle_t cfgxref)
{
struct jz4780_pinctrl_softc *sc = device_get_softc(dev);
device_t chip;
phandle_t node;
ssize_t i, len;
uint32_t *value, *pconf;
int result;
node = OF_node_from_xref(cfgxref);
len = OF_getencprop_alloc(node, "ingenic,pins", sizeof(uint32_t) * 4,
(void **)&value);
if (len < 0) {
device_printf(dev,
"missing ingenic,pins attribute in FDT\n");
return (ENXIO);
}
pconf = value;
result = EINVAL;
for (i = 0; i < len; i++, pconf += 4) {
uint32_t bias;
/* Lookup the chip that handles this configuration */
chip = jz4780_pinctrl_chip_lookup(sc, pconf[0]);
if (chip == NULL) {
device_printf(dev,
"invalid gpio controller reference in FDT\n");
goto done;
}
if (jz4780_pinctrl_parse_pincfg(pconf[3], &bias) != 0) {
device_printf(dev,
"invalid pin bias for pin %u on %s in FDT\n",
pconf[1], ofw_bus_get_name(chip));
goto done;
}
result = JZ4780_GPIO_CONFIGURE_PIN(chip, pconf[1], pconf[2],
bias);
if (result != 0) {
device_printf(dev,
"failed to configure pin %u on %s\n", pconf[1],
ofw_bus_get_name(chip));
goto done;
}
}
result = 0;
done:
free(value, M_OFWPROP);
return (result);
}
static device_method_t jz4780_pinctrl_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_pinctrl_probe),
DEVMETHOD(device_attach, jz4780_pinctrl_attach),
DEVMETHOD(device_detach, jz4780_pinctrl_detach),
/* fdt_pinctrl interface */
DEVMETHOD(fdt_pinctrl_configure, jz4780_pinctrl_configure_pins),
DEVMETHOD_END
};
static devclass_t jz4780_pinctrl_devclass;
DEFINE_CLASS_1(pinctrl, jz4780_pinctrl_driver, jz4780_pinctrl_methods,
sizeof(struct jz4780_pinctrl_softc), simplebus_driver);
EARLY_DRIVER_MODULE(pinctrl, simplebus, jz4780_pinctrl_driver,
jz4780_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);

View File

@ -0,0 +1,33 @@
/*-
* Copyright 2015 Alexander Kabaev <kan@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _MIPS_INGENIC_JZ4780_PINCTRL_H
#define _MIPS_INGENIC_JZ4780_PINCTRL_H
#endif /* _MIPS_INGENIC_JZ4780_PINCTRL_H */

View File

@ -0,0 +1,787 @@
/* $NetBSD: ingenic_regs.h,v 1.22 2015/10/08 17:54:30 macallan Exp $ */
/*-
* Copyright (c) 2014 Michael Lorenz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef JZ4780_REGS_H
#define JZ4780_REGS_H
/* for mips_wbflush() */
#include <machine/locore.h>
/* UARTs, mostly 16550 compatible with 32bit spaced registers */
#define JZ_UART0 0x10030000
#define JZ_UART1 0x10031000
#define JZ_UART2 0x10032000
#define JZ_UART3 0x10033000
#define JZ_UART4 0x10034000
/* LCD controller base addresses, registers are in jzfb_regs.h */
#define JZ_LCDC0_BASE 0x13050000
#define JZ_LCDC1_BASE 0x130a0000
/* TCU unit base address */
#define JZ_TCU_BASE 0x10002000
/* Watchdog */
#define JZ_WDOG_TDR 0x00000000 /* compare */
#define JZ_WDOG_TCER 0x00000004
#define TCER_ENABLE 0x01 /* enable counter */
#define JZ_WDOG_TCNT 0x00000008 /* 16bit up count */
#define JZ_WDOG_TCSR 0x0000000c
#define TCSR_PCK_EN 0x01 /* PCLK */
#define TCSR_RTC_EN 0x02 /* RTCCLK - 32.768kHz */
#define TCSR_EXT_EN 0x04 /* EXTCLK - 48MHz */
#define TCSR_PRESCALE_M 0x38
#define TCSR_DIV_1 0x00
#define TCSR_DIV_4 0x08
#define TCSR_DIV_16 0x10
#define TCSR_DIV_64 0x18
#define TCSR_DIV_256 0x20
#define TCSR_DIV_1024 0x28
/* timers and PWMs */
#define JZ_TC_TER 0x00000010 /* TC enable reg, ro */
#define JZ_TC_TESR 0x00000014 /* TC enable set reg. */
#define TESR_TCST0 0x0001 /* enable counter 0 */
#define TESR_TCST1 0x0002 /* enable counter 1 */
#define TESR_TCST2 0x0004 /* enable counter 2 */
#define TESR_TCST3 0x0008 /* enable counter 3 */
#define TESR_TCST4 0x0010 /* enable counter 4 */
#define TESR_TCST5 0x0020 /* enable counter 5 */
#define TESR_TCST6 0x0040 /* enable counter 6 */
#define TESR_TCST7 0x0080 /* enable counter 7 */
#define TESR_OST 0x8000 /* enable OST */
#define JZ_TC_TECR 0x00000018 /* TC enable clear reg. */
#define JZ_TC_TFR 0x00000020
#define TFR_FFLAG0 0x00000001 /* channel 0 */
#define TFR_FFLAG1 0x00000002 /* channel 1 */
#define TFR_FFLAG2 0x00000004 /* channel 2 */
#define TFR_FFLAG3 0x00000008 /* channel 3 */
#define TFR_FFLAG4 0x00000010 /* channel 4 */
#define TFR_FFLAG5 0x00000020 /* channel 5 */
#define TFR_FFLAG6 0x00000040 /* channel 6 */
#define TFR_FFLAG7 0x00000080 /* channel 7 */
#define TFR_OSTFLAG 0x00008000 /* OS timer */
#define JZ_TC_TFSR 0x00000024 /* timer flag set */
#define JZ_TC_TFCR 0x00000028 /* timer flag clear */
#define JZ_TC_TMR 0x00000030 /* timer flag mask */
#define TMR_FMASK(n) (1 << (n))
#define TMR_HMASK(n) (1 << ((n) + 16))
#define JZ_TC_TMSR 0x00000034 /* timer flag mask set */
#define JZ_TC_TMCR 0x00000038 /* timer flag mask clear*/
#define JZ_TC_TDFR(n) (0x00000040 + (n * 0x10)) /* FULL compare */
#define JZ_TC_TDHR(n) (0x00000044 + (n * 0x10)) /* HALF compare */
#define JZ_TC_TCNT(n) (0x00000048 + (n * 0x10)) /* count */
#define JZ_TC_TCSR(n) (0x0000004c + (n * 0x10))
/* same bits as in JZ_WDOG_TCSR */
/* operating system timer */
#define JZ_OST_DATA 0x000000e0 /* compare */
#define JZ_OST_CNT_LO 0x000000e4
#define JZ_OST_CNT_HI 0x000000e8
#define JZ_OST_CTRL 0x000000ec
#define OSTC_PCK_EN 0x0001 /* use PCLK */
#define OSTC_RTC_EN 0x0002 /* use RTCCLK */
#define OSTC_EXT_EN 0x0004 /* use EXTCLK */
#define OSTC_PRESCALE_M 0x0038
#define OSTC_DIV_1 0x0000
#define OSTC_DIV_4 0x0008
#define OSTC_DIV_16 0x0010
#define OSTC_DIV_64 0x0018
#define OSTC_DIV_256 0x0020
#define OSTC_DIV_1024 0x0028
#define OSTC_SHUTDOWN 0x0200
#define OSTC_MODE 0x8000 /* 0 - reset to 0 when = OST_DATA */
#define JZ_OST_CNT_U32 0x000000fc /* copy of CNT_HI when reading CNT_LO */
static inline void
writereg(uint32_t reg, uint32_t val)
{
*(volatile int32_t *)MIPS_PHYS_TO_KSEG1(reg) = val;
mips_wbflush();
}
static inline uint32_t
readreg(uint32_t reg)
{
mips_wbflush();
return *(volatile int32_t *)MIPS_PHYS_TO_KSEG1(reg);
}
/* Clock management */
#define JZ_CGU_BASE 0x10000000
#define JZ_CPCCR 0x00000000 /* Clock Control Register */
#define JZ_PDIV_M 0x000f0000 /* PCLK divider mask */
#define JZ_PDIV_S 16 /* PCLK divider shift */
#define JZ_CDIV_M 0x0000000f /* CPU clock divider mask */
#define JZ_CDIV_S 0 /* CPU clock divider shift */
#define JZ_CPAPCR 0x00000010 /* APLL */
#define JZ_CPMPCR 0x00000014 /* MPLL */
#define JZ_CPEPCR 0x00000018 /* EPLL */
#define JZ_CPVPCR 0x0000001C /* VPLL */
#define JZ_PLLM_S 19 /* PLL multiplier shift */
#define JZ_PLLM_M 0xfff80000 /* PLL multiplier mask */
#define JZ_PLLN_S 13 /* PLL divider shift */
#define JZ_PLLN_M 0x0007e000 /* PLL divider mask */
#define JZ_PLLP_S 9 /* PLL postdivider shift */
#define JZ_PLLP_M 0x00001700 /* PLL postdivider mask */
#define JZ_PLLON 0x00000010 /* PLL is on and stable */
#define JZ_PLLBP 0x00000002 /* PLL bypass */
#define JZ_PLLEN 0x00000001 /* PLL enable */
#define JZ_CLKGR0 0x00000020 /* Clock Gating Registers */
#define CLK_NEMC (1 << 0)
#define CLK_BCH (1 << 1)
#define CLK_OTG0 (1 << 2)
#define CLK_MSC0 (1 << 3)
#define CLK_SSI0 (1 << 4)
#define CLK_SMB0 (1 << 5)
#define CLK_SMB1 (1 << 6)
#define CLK_SCC (1 << 7)
#define CLK_AIC (1 << 8)
#define CLK_TSSI0 (1 << 9)
#define CLK_OWI (1 << 10)
#define CLK_MSC1 (1 << 11)
#define CLK_MSC2 (1 << 12)
#define CLK_KBC (1 << 13)
#define CLK_SADC (1 << 14)
#define CLK_UART0 (1 << 15)
#define CLK_UART1 (1 << 16)
#define CLK_UART2 (1 << 17)
#define CLK_UART3 (1 << 18)
#define CLK_SSI1 (1 << 19)
#define CLK_SSI2 (1 << 20)
#define CLK_PDMA (1 << 21)
#define CLK_GPS (1 << 22)
#define CLK_MAC (1 << 23)
#define CLK_UHC (1 << 24)
#define CLK_SMB2 (1 << 25)
#define CLK_CIM (1 << 26)
#define CLK_TVE (1 << 27)
#define CLK_LCD (1 << 28)
#define CLK_IPU (1 << 29)
#define CLK_DDR0 (1 << 30)
#define CLK_DDR1 (1 << 31)
#define JZ_CLKGR1 0x00000028 /* Clock Gating Registers */
#define CLK_SMB3 (1 << 0)
#define CLK_TSSI1 (1 << 1)
#define CLK_VPU (1 << 2)
#define CLK_PCM (1 << 3)
#define CLK_GPU (1 << 4)
#define CLK_COMPRESS (1 << 5)
#define CLK_AIC1 (1 << 6)
#define CLK_GPVLC (1 << 7)
#define CLK_OTG1 (1 << 8)
#define CLK_HDMI (1 << 9)
#define CLK_UART4 (1 << 10)
#define CLK_AHB_MON (1 << 11)
#define CLK_SMB4 (1 << 12)
#define CLK_DES (1 << 13)
#define CLK_X2D (1 << 14)
#define CLK_P1 (1 << 15)
#define JZ_DDCDR 0x0000002c /* DDR clock divider register */
#define JZ_VPUCDR 0x00000030 /* VPU clock divider register */
#define JZ_I2SCDR 0x00000060 /* I2S device clock divider register */
#define JZ_I2S1CDR 0x000000a0 /* I2S device clock divider register */
#define JZ_USBCDR 0x00000050 /* OTG PHY clock divider register */
#define JZ_LP0CDR 0x00000054 /* LCD0 pix clock divider register */
#define JZ_LP1CDR 0x00000064 /* LCD1 pix clock divider register */
#define JZ_MSC0CDR 0x00000068 /* MSC0 clock divider register */
#define JZ_MSC1CDR 0x000000a4 /* MSC1 clock divider register */
#define JZ_MSC2CDR 0x000000a8 /* MSC2 clock divider register */
#define MSCCDR_SCLK_A 0x40000000
#define MSCCDR_MPLL 0x80000000
#define MSCCDR_CE 0x20000000
#define MSCCDR_BUSY 0x10000000
#define MSCCDR_STOP 0x08000000
#define MSCCDR_PHASE 0x00008000 /* 0 - 90deg phase, 1 - 180 */
#define MSCCDR_DIV_M 0x000000ff /* src / ((div + 1) * 2) */
#define UHCCDR_DIV_M 0x000000ff
#define JZ_UHCCDR 0x0000006c /* UHC 48M clock divider register */
#define UHCCDR_SCLK_A 0x00000000
#define UHCCDR_MPLL 0x40000000
#define UHCCDR_EPLL 0x80000000
#define UHCCDR_OTG_PHY 0xc0000000
#define UHCCDR_CLK_MASK 0xc0000000
#define UHCCDR_CE 0x20000000
#define UHCCDR_BUSY 0x10000000
#define UHCCDR_STOP 0x08000000
#define UHCCDR_DIV_M 0x000000ff
#define UHCCDR_DIV(d) (d)
#define JZ_SSICDR 0x00000074 /* SSI clock divider register */
#define JZ_CIMCDR 0x0000007c /* CIM MCLK clock divider register */
#define JZ_PCMCDR 0x00000084 /* PCM device clock divider register */
#define JZ_GPUCDR 0x00000088 /* GPU clock divider register */
#define JZ_HDMICDR 0x0000008c /* HDMI clock divider register */
#define JZ_BCHCDR 0x000000ac /* BCH clock divider register */
#define JZ_CPM_INTR 0x000000b0 /* CPM interrupt register */
#define JZ_CPM_INTRE 0x000000b4 /* CPM interrupt enable register */
#define JZ_CPSPR 0x00000034 /* CPM scratch register */
#define JZ_CPSRPR 0x00000038 /* CPM scratch protected register */
#define JZ_USBPCR 0x0000003c /* USB parameter control register */
#define PCR_USB_MODE 0x80000000 /* 1 - otg */
#define PCR_AVLD_REG 0x40000000
#define PCR_IDPULLUP_MASK 0x30000000
#define PCR_INCR_MASK 0x08000000
#define PCR_TCRISETUNE 0x04000000
#define PCR_COMMONONN 0x02000000
#define PCR_VBUSVLDEXT 0x01000000
#define PCR_VBUSVLDEXTSEL 0x00800000
#define PCR_POR 0x00400000
#define PCR_SIDDQ 0x00200000
#define PCR_OTG_DISABLE 0x00100000
#define PCR_COMPDISTN_M 0x000e0000
#define PCR_OTGTUNE 0x0001c000
#define PCR_SQRXTUNE 0x00003800
#define PCR_TXFSLSTUNE 0x00000780
#define PCR_TXPREEMPHTUNE 0x00000040
#define PCR_TXHSXVTUNE 0x00000030
#define PCR_TXVREFTUNE 0x0000000f
#define JZ_USBRDT 0x00000040 /* Reset detect timer register */
#define USBRDT_USBRDT_SHIFT 0
#define USBRDT_USBRDT_WIDTH 23
#define USBRDT_VBFIL_LD_EN 0x01000000
#define JZ_USBVBFIL 0x00000044 /* USB jitter filter register */
#define USBVBFIL_IDDIGFIL_SHIFT 16
#define USBVBFIL_IDDIGFIL_WIDTH 16
#define USBVBFIL_USBVBFIL_SHIFT 0
#define USBVBFIL_USBVBFIL_WIDTH 16
#define JZ_USBPCR1 0x00000048 /* USB parameter control register 1 */
#define PCR_SYNOPSYS 0x10000000 /* Mentor mode otherwise */
#define PCR_REFCLK_CORE 0x08000000
#define PCR_REFCLK_XO25 0x04000000
#define PCR_REFCLK_CO 0x00000000
#define PCR_REFCLK_M 0x0c000000
#define PCR_CLK_M 0x03000000 /* clock */
#define PCR_CLK_192 0x03000000 /* 19.2MHz */
#define PCR_CLK_48 0x02000000 /* 48MHz */
#define PCR_CLK_24 0x01000000 /* 24MHz */
#define PCR_CLK_12 0x00000000 /* 12MHz */
#define PCR_DMPD1 0x00800000 /* pull down D- on port 1 */
#define PCR_DPPD1 0x00400000 /* pull down D+ on port 1 */
#define PCR_PORT0_RST 0x00200000 /* port 0 reset */
#define PCR_PORT1_RST 0x00100000 /* port 1 reset */
#define PCR_WORD_I_F0 0x00080000 /* 1: 16bit/30M, 8/60 otherw. */
#define PCR_WORD_I_F1 0x00040000 /* same for port 1 */
#define PCR_COMPDISTUNE 0x00038000 /* disconnect threshold */
#define PCR_SQRXTUNE1 0x00007000 /* squelch threshold */
#define PCR_TXFSLSTUNE1 0x00000f00 /* FS/LS impedance adj. */
#define PCR_TXPREEMPH 0x00000080 /* HS transm. pre-emphasis */
#define PCR_TXHSXVTUNE1 0x00000060 /* dp/dm voltage adj. */
#define PCR_TXVREFTUNE1 0x00000017 /* HS DC voltage adj. */
#define PCR_TXRISETUNE1 0x00000001 /* rise/fall wave adj. */
/* power manager */
#define JZ_LPCR 0x00000004
#define LPCR_PD_SCPU (1u << 31) /* CPU1 power down */
#define LPCR_PD_VPU (1u << 30) /* VPU power down */
#define LPCR_PD_GPU (1u << 29) /* GPU power down */
#define LPCR_PD_GPS (1u << 28) /* GPS power down */
#define LPCR_SCPUS (1u << 27) /* CPU1 power down status */
#define LPCR_VPUS (1u << 26) /* VPU power down status */
#define LPCR_GPUS (1u << 25) /* GPU power down status */
#define LPCR_GPSS (1u << 24) /* GPS power down status */
#define LPCR_GPU_IDLE (1u << 20) /* GPU idle status */
#define LPCR_PST_SHIFT 8 /* Power stability time */
#define LPCR_PST_MASK (0xFFFu << 8)
#define LPCR_DUTY_SHIFT 3 /* CPU clock duty */
#define LPCR_DUTY_MASK (0x1Fu << 3)
#define LPCR_DOZE (1u << 2) /* Doze mode */
#define LPCR_LPM_SHIFT 0 /* Low power mode */
#define LPCR_LPM_MASK (0x03u << 0)
#define JZ_OPCR 0x00000024 /* Oscillator Power Control Reg. */
#define OPCR_IDLE_DIS 0x80000000 /* don't stop CPU clk on idle */
#define OPCR_GPU_CLK_ST 0x40000000 /* stop GPU clock */
#define OPCR_L2CM_M 0x0c000000
#define OPCR_L2CM_ON 0x00000000 /* L2 stays on in sleep */
#define OPCR_L2CM_RET 0x04000000 /* L2 retention mode in sleep */
#define OPCR_L2CM_OFF 0x08000000 /* L2 powers down in sleep */
#define OPCR_SPENDN0 0x00000080 /* 0 - OTG port forced down */
#define OPCR_SPENDN1 0x00000040 /* 0 - UHC port forced down */
#define OPCR_BUS_MODE 0x00000020 /* 1 - bursts */
#define OPCR_O1SE 0x00000010 /* EXTCLK on in sleep */
#define OPCR_PD 0x00000008 /* P0 down in sleep */
#define OPCR_ERCS 0x00000004 /* 1 RTCCLK, 0 EXTCLK/512 */
#define OPCR_CPU_MODE 0x00000002 /* 1 access 'accelerated' */
#define OPCR_OSE 0x00000001 /* disable EXTCLK */
#define JZ_SPCR0 0x000000b8 /* SRAM Power Control Registers */
#define JZ_SPCR1 0x000000bc
#define JZ_SRBC 0x000000c4 /* Soft Reset & Bus Control */
#define SRBC_UHC_SR 0x00004000 /* UHC soft reset*/
/*
* random number generator
*
* Its function currently isn't documented by Ingenic.
* However, testing suggests that it works as expected.
*/
#define JZ_ERNG 0x000000d8
#define JZ_RNG 0x000000dc
/* Interrupt controller */
#define JZ_ICBASE 0x10001000 /* IC base address */
#define JZ_ICSR0 0x00000000 /* raw IRQ line status */
#define JZ_ICMR0 0x00000004 /* IRQ mask, 1 masks IRQ */
#define JZ_ICMSR0 0x00000008 /* sets bits in mask register */
#define JZ_ICMCR0 0x0000000c /* clears bits in mask register */
#define JZ_ICPR0 0x00000010 /* line status after masking */
#define JZ_ICSR1 0x00000020 /* raw IRQ line status */
#define JZ_ICMR1 0x00000024 /* IRQ mask, 1 masks IRQ */
#define JZ_ICMSR1 0x00000028 /* sets bits in mask register */
#define JZ_ICMCR1 0x0000002c /* clears bits in maks register */
#define JZ_ICPR1 0x00000030 /* line status after masking */
#define JZ_DSR0 0x00000034 /* source for PDMA */
#define JZ_DMR0 0x00000038 /* mask for PDMA */
#define JZ_DPR0 0x0000003c /* pending for PDMA */
#define JZ_DSR1 0x00000040 /* source for PDMA */
#define JZ_DMR1 0x00000044 /* mask for PDMA */
#define JZ_DPR1 0x00000048 /* pending for PDMA */
/* memory controller */
#define JZ_DMMAP0 0x13010024
#define JZ_DMMAP1 0x13010028
#define DMMAP_BASE 0x0000ff00 /* base PADDR of memory chunk */
#define DMMAP_MASK 0x000000ff /* mask which bits of PADDR are
* constant */
/* USB controllers */
#define JZ_EHCI_BASE 0x13490000
#define JZ_EHCI_REG_UTMI_BUS 0x000000b0
#define UTMI_BUS_WIDTH 0x00000040
#define JZ_OHCI_BASE 0x134a0000
#define JZ_DWC2_BASE 0x13500000
#define JZ_DWC2_GUSBCFG 0
/* Ethernet */
#define JZ_DME_BASE 0x16000000
#define JZ_DME_IO 0
#define JZ_DME_DATA 2
/* GPIO */
#define JZ_GPIO_A_BASE 0x10010000
#define JZ_GPIO_B_BASE 0x10010100
#define JZ_GPIO_C_BASE 0x10010200
#define JZ_GPIO_D_BASE 0x10010300
#define JZ_GPIO_E_BASE 0x10010400
#define JZ_GPIO_F_BASE 0x10010500
/* GPIO registers per port */
#define JZ_GPIO_PIN 0x00000000 /* pin level register */
/* 0 - normal gpio, 1 - interrupt */
#define JZ_GPIO_INT 0x00000010 /* interrupt register */
#define JZ_GPIO_INTS 0x00000014 /* interrupt set register */
#define JZ_GPIO_INTC 0x00000018 /* interrupt clear register */
/*
* INT == 1: 1 disables interrupt
* INT == 0: device select, see below
*/
#define JZ_GPIO_MASK 0x00000020 /* port mask register */
#define JZ_GPIO_MASKS 0x00000024 /* port mask set register */
#define JZ_GPIO_MASKC 0x00000028 /* port mask clear register */
/*
* INT == 1: 0 - level triggered, 1 - edge triggered
* INT == 0: 0 - device select, see below
*/
#define JZ_GPIO_PAT1 0x00000030 /* pattern 1 register */
#define JZ_GPIO_PAT1S 0x00000034 /* pattern 1 set register */
#define JZ_GPIO_PAT1C 0x00000038 /* pattern 1 clear register */
/*
* INT == 1:
* PAT1 == 0: 0 - trigger on low, 1 - trigger on high
* PAT0 == 1: 0 - trigger on falling edge, 1 - trigger on rising edge
* INT == 0:
* MASK == 0:
* PAT1 == 0: 0 - device 0, 1 - device 1
* PAT0 == 1: 0 - device 2, 1 - device 3
* MASK == 1:
* PAT1 == 0: set gpio output
* PAT1 == 1: pin is input
*/
#define JZ_GPIO_PAT0 0x00000040 /* pattern 0 register */
#define JZ_GPIO_PAT0S 0x00000044 /* pattern 0 set register */
#define JZ_GPIO_PAT0C 0x00000048 /* pattern 0 clear register */
/* 1 - interrupt happened */
#define JZ_GPIO_FLAG 0x00000050 /* flag register */
#define JZ_GPIO_FLAGC 0x00000058 /* flag clear register */
/* 1 - disable pull up/down resistors */
#define JZ_GPIO_DPULL 0x00000070 /* pull disable register */
#define JZ_GPIO_DPULLS 0x00000074 /* pull disable set register */
#define JZ_GPIO_DPULLC 0x00000078 /* pull disable clear register */
/* the following are uncommented in the manual */
#define JZ_GPIO_DRVL 0x00000080 /* drive low register */
#define JZ_GPIO_DRVLS 0x00000084 /* drive low set register */
#define JZ_GPIO_DRVLC 0x00000088 /* drive low clear register */
#define JZ_GPIO_DIR 0x00000090 /* direction register */
#define JZ_GPIO_DIRS 0x00000094 /* direction register */
#define JZ_GPIO_DIRC 0x00000098 /* direction register */
#define JZ_GPIO_DRVH 0x000000a0 /* drive high register */
#define JZ_GPIO_DRVHS 0x000000a4 /* drive high set register */
#define JZ_GPIO_DRVHC 0x000000a8 /* drive high clear register */
/* I2C / SMBus */
#define JZ_SMB0_BASE 0x10050000
#define JZ_SMB1_BASE 0x10051000
#define JZ_SMB2_BASE 0x10052000
#define JZ_SMB3_BASE 0x10053000
#define JZ_SMB4_BASE 0x10054000
/* SMBus register offsets, per port */
#define JZ_SMBCON 0x00 /* SMB control */
#define JZ_STPHLD 0x80 /* Stop Hold Enable bit */
#define JZ_SLVDIS 0x40 /* 1 - slave disabled */
#define JZ_REST 0x20 /* 1 - allow RESTART */
#define JZ_MATP 0x10 /* 1 - enable 10bit addr. for master */
#define JZ_SATP 0x08 /* 1 - enable 10bit addr. for slave */
#define JZ_SPD_M 0x06 /* bus speed control */
#define JZ_SPD_100KB 0x02 /* 100kBit/s mode */
#define JZ_SPD_400KB 0x04 /* 400kBit/s mode */
#define JZ_MD 0x01 /* enable master */
#define JZ_SMBTAR 0x04 /* SMB target address */
#define JZ_SMATP 0x1000 /* enable 10bit master addr */
#define JZ_SPECIAL 0x0800 /* 1 - special command */
#define JZ_START 0x0400 /* 1 - send START */
#define JZ_SMBTAR_M 0x03ff /* target address */
#define JZ_SMBSAR 0x08 /* SMB slave address */
#define JZ_SMBDC 0x10 /* SMB data buffer and command */
#define JZ_CMD 0x100 /* 1 - read, 0 - write */
#define JZ_DATA 0x0ff
#define JZ_SMBSHCNT 0x14 /* Standard speed SMB SCL high count */
#define JZ_SMBSLCNT 0x18 /* Standard speed SMB SCL low count */
#define JZ_SMBFHCNT 0x1C /* Fast speed SMB SCL high count */
#define JZ_SMBFLCNT 0x20 /* Fast speed SMB SCL low count */
#define JZ_SMBINTST 0x2C /* SMB Interrupt Status */
#define JZ_ISTT 0x400 /* START or RESTART occured */
#define JZ_ISTP 0x200 /* STOP occured */
#define JZ_TXABT 0x40 /* ABORT occured */
#define JZ_TXEMP 0x10 /* TX FIFO is low */
#define JZ_TXOF 0x08 /* TX FIFO is high */
#define JZ_RXFL 0x04 /* RX FIFO is at JZ_SMBRXTL*/
#define JZ_RXOF 0x02 /* RX FIFO is high */
#define JZ_RXUF 0x01 /* RX FIFO underflow */
#define JZ_SMBINTM 0x30 /* SMB Interrupt Mask */
#define JZ_SMBRXTL 0x38 /* SMB RxFIFO Threshold */
#define JZ_SMBTXTL 0x3C /* SMB TxFIFO Threshold */
#define JZ_SMBCINT 0x40 /* Clear Interrupts */
#define JZ_CLEARALL 0x01
#define JZ_SMBCRXUF 0x44 /* Clear RXUF Interrupt */
#define JZ_SMBCRXOF 0x48 /* Clear RX_OVER Interrupt */
#define JZ_SMBCTXOF 0x4C /* Clear TX_OVER Interrupt */
#define JZ_SMBCRXREQ 0x50 /* Clear RDREQ Interrupt */
#define JZ_SMBCTXABT 0x54 /* Clear TX_ABRT Interrupt */
#define JZ_SMBCRXDN 0x58 /* Clear RX_DONE Interrupt */
#define JZ_SMBCACT 0x5c /* Clear ACTIVITY Interrupt */
#define JZ_SMBCSTP 0x60 /* Clear STOP Interrupt */
#define JZ_SMBCSTT 0x64 /* Clear START Interrupt */
#define JZ_SMBCGC 0x68 /* Clear GEN_CALL Interrupt */
#define JZ_SMBENB 0x6C /* SMB Enable */
#define JZ_ENABLE 0x01
#define JZ_SMBST 0x70 /* SMB Status register */
#define JZ_SLVACT 0x40 /* slave is active */
#define JZ_MSTACT 0x20 /* master is active */
#define JZ_RFF 0x10 /* RX FIFO is full */
#define JZ_RFNE 0x08 /* RX FIFO not empty */
#define JZ_TFE 0x04 /* TX FIFO is empty */
#define JZ_TFNF 0x02 /* TX FIFO is not full */
#define JZ_ACT 0x01 /* JZ_SLVACT | JZ_MSTACT */
#define JZ_SMBABTSRC 0x80 /* SMB Transmit Abort Status Register */
#define JZ_SMBDMACR 0x88 /* DMA Control Register */
#define JZ_SMBDMATDL 0x8c /* DMA Transmit Data Level */
#define JZ_SMBDMARDL 0x90 /* DMA Receive Data Level */
#define JZ_SMBSDASU 0x94 /* SMB SDA Setup Register */
#define JZ_SMBACKGC 0x98 /* SMB ACK General Call Register */
#define JZ_SMBENBST 0x9C /* SMB Enable Status Register */
#define JZ_SMBSDAHD 0xD0 /* SMB SDA HolD time Register */
#define JZ_HDENB 0x100 /* enable hold time */
/* SD/MMC hosts */
#define JZ_MSC0_BASE 0x13450000
#define JZ_MSC1_BASE 0x13460000
#define JZ_MSC2_BASE 0x13470000
#define JZ_MSC_CTRL 0x00
#define JZ_SEND_CCSD 0x8000
#define JZ_SEND_AS_CCSD 0x4000
#define JZ_EXIT_MULTIPLE 0x0080
#define JZ_EXIT_TRANSFER 0x0040
#define JZ_START_READWAIT 0x0020
#define JZ_STOP_READWAIT 0x0010
#define JZ_RESET 0x0008
#define JZ_START_OP 0x0004
#define JZ_CLOCK_CTRL_M 0x0003
#define JZ_CLOCK_START 0x0002
#define JZ_CLOCK_STOP 0x0001
#define JZ_MSC_STAT 0x04
#define JZ_AUTO_CMD12_DONE 0x80000000
#define JZ_AUTO_CMD23_DONE 0x40000000
#define JZ_SVS 0x20000000
#define JZ_PIN_LEVEL_M 0x1f000000
#define JZ_BCE 0x00100000 /* boot CRC error */
#define JZ_BDE 0x00080000 /* boot data end */
#define JZ_BAE 0x00040000 /* boot acknowledge error */
#define JZ_BAR 0x00020000 /* boot ack. received */
#define JZ_DMAEND 0x00010000
#define JZ_IS_RESETTING 0x00008000
#define JZ_SDIO_INT_ACTIVE 0x00004000
#define JZ_PRG_DONE 0x00002000
#define JZ_DATA_TRAN_DONE 0x00001000
#define JZ_END_CMD_RES 0x00000800
#define JZ_DATA_FIFO_AFULL 0x00000400
#define JZ_IS_READWAIT 0x00000200
#define JZ_CLK_EN 0x00000100
#define JZ_DATA_FIFO_FULL 0x00000080
#define JZ_DATA_FIFO_EMPTY 0x00000040
#define JZ_CRC_RES_ERR 0x00000020
#define JZ_CRC_READ_ERR 0x00000010
#define JZ_CRC_WRITE_ERR_M 0x0000000c
#define JZ_CRC_WRITE_OK 0x00000000
#define JZ_CRC_CARD_ERR 0x00000004
#define JZ_CRC_NO_STATUS 0x00000008
#define JZ_TIME_OUT_RES 0x00000002
#define JZ_TIME_OUT_READ 0x00000001
#define JZ_MSC_CLKRT 0x08
#define JZ_DEV_CLK 0x0
#define JZ_DEV_CLK_2 0x1 /* DEV_CLK / 2 */
#define JZ_DEV_CLK_4 0x2 /* DEV_CLK / 4 */
#define JZ_DEV_CLK_8 0x3 /* DEV_CLK / 8 */
#define JZ_DEV_CLK_16 0x4 /* DEV_CLK / 16 */
#define JZ_DEV_CLK_32 0x5 /* DEV_CLK / 32 */
#define JZ_DEV_CLK_64 0x6 /* DEV_CLK / 64 */
#define JZ_DEV_CLK_128 0x7 /* DEV_CLK / 128 */
#define JZ_MSC_CMDAT 0x0c
#define JZ_CCS_EXPECTED 0x80000000
#define JZ_READ_CEATA 0x40000000
#define JZ_DIS_BOOT 0x08000000
#define JZ_ENA_BOOT 0x04000000
#define JZ_EXP_BOOT_ACK 0x02000000
#define JZ_BOOT_MODE 0x01000000
#define JZ_AUTO_CMD23 0x00040000
#define JZ_SDIO_PRDT 0x00020000
#define JZ_AUTO_CMD12 0x00010000
#define JZ_RTRG_M 0x0000c000 /* receive FIFO trigger */
#define JZ_RTRG_16 0x00000000 /* >= 16 */
#define JZ_RTRG_32 0x00004000 /* >= 32 */
#define JZ_RTRG_64 0x00008000 /* >= 64 */
#define JZ_RTRG_96 0x0000c000 /* >= 96 */
#define JZ_TTRG_M 0x00003000 /* transmit FIFO trigger */
#define JZ_TTRG_16 0x00000000 /* >= 16 */
#define JZ_TTRG_32 0x00001000 /* >= 32 */
#define JZ_TTRG_64 0x00002000 /* >= 64 */
#define JZ_TTRG_96 0x00003000 /* >= 96 */
#define JZ_IO_ABORT 0x00000800
#define JZ_BUS_WIDTH_M 0x00000600
#define JZ_BUS_1BIT 0x00000000
#define JZ_BUS_4BIT 0x00000400
#define JZ_BUS_8BIT 0x00000600
#define JZ_INIT 0x00000080 /* send 80 clk init before cmd */
#define JZ_BUSY 0x00000040
#define JZ_STREAM 0x00000020
#define JZ_WRITE 0x00000010 /* read otherwise */
#define JZ_DATA_EN 0x00000008
#define JZ_RESPONSE_M 0x00000007 /* response format */
#define JZ_RES_NONE 0x00000000
#define JZ_RES_R1 0x00000001 /* R1 and R1b */
#define JZ_RES_R2 0x00000002
#define JZ_RES_R3 0x00000003
#define JZ_RES_R4 0x00000004
#define JZ_RES_R5 0x00000005
#define JZ_RES_R6 0x00000006
#define JZ_RES_R7 0x00000007
#define JZ_MSC_RESTO 0x10 /* 16bit response timeout in MSC_CLK */
#define JZ_MSC_RDTO 0x14 /* 32bit read timeout in MSC_CLK */
#define JZ_MSC_BLKLEN 0x18 /* 16bit block length */
#define JZ_MSC_NOB 0x1c /* 16bit block counter */
#define JZ_MSC_SNOB 0x20 /* 16bit successful block counter */
#define JZ_MSC_IMASK 0x24 /* interrupt mask */
#define JZ_INT_AUTO_CMD23_DONE 0x40000000
#define JZ_INT_SVS 0x20000000
#define JZ_INT_PIN_LEVEL_M 0x1f000000
#define JZ_INT_BCE 0x00100000
#define JZ_INT_BDE 0x00080000
#define JZ_INT_BAE 0x00040000
#define JZ_INT_BAR 0x00020000
#define JZ_INT_DMAEND 0x00010000
#define JZ_INT_AUTO_CMD12_DONE 0x00008000
#define JZ_INT_DATA_FIFO_FULL 0x00004000
#define JZ_INT_DATA_FIFO_EMPTY 0x00002000
#define JZ_INT_CRC_RES_ERR 0x00001000
#define JZ_INT_CRC_READ_ERR 0x00000800
#define JZ_INT_CRC_WRITE_ERR 0x00000400
#define JZ_INT_TIMEOUT_RES 0x00000200
#define JZ_INT_TIMEOUT_READ 0x00000100
#define JZ_INT_SDIO 0x00000080
#define JZ_INT_TXFIFO_WR_REQ 0x00000040
#define JZ_INT_RXFIFO_RD_REQ 0x00000020
#define JZ_INT_END_CMD_RES 0x00000004
#define JZ_INT_PRG_DONE 0x00000002
#define JZ_INT_DATA_TRAN_DONE 0x00000001
#define JZ_MSC_IFLG 0x28 /* interrupt flags */
#define JZ_MSC_CMD 0x2c /* 6bit CMD index */
#define JZ_MSC_ARG 0x30 /* 32bit argument */
#define JZ_MSC_RES 0x34 /* 8x16bit response data FIFO */
#define JZ_MSC_RXFIFO 0x38
#define JZ_MSC_TXFIFO 0x3c
#define JZ_MSC_LPM 0x40
#define JZ_DRV_SEL_M 0xc0000000
#define JZ_FALLING_EDGE 0x00000000
#define JZ_RISING_1NS 0x40000000 /* 1ns delay */
#define JZ_RISING_4 0x80000000 /* 1/4 MSC_CLK delay */
#define JZ_SMP_SEL 0x20000000 /* 1 - rising edge */
#define JZ_LPM 0x00000001 /* low power mode */
#define JZ_MSC_DMAC 0x44
#define JZ_MODE_SEL 0x80 /* 1 - specify transfer length */
#define JZ_AOFST_M 0x60 /* address offset in bytes */
#define JZ_AOFST_S 6 /* addrress offset shift */
#define JZ_ALIGNEN 0x10 /* allow non-32bit-aligned transfers */
#define JZ_INCR_M 0x0c /* burst type */
#define JZ_INCR_16 0x00
#define JZ_INCR_32 0x04
#define JZ_INCR_64 0x08
#define JZ_DMASEL 0x02 /* 1 - SoC DMAC, 0 - MSC built-in */
#define JZ_DMAEN 0x01 /* enable DMA */
#define JZ_MSC_DMANDA 0x48 /* next descriptor paddr */
#define JZ_MSC_DMADA 0x4c /* current descriptor */
#define JZ_MSC_DMALEN 0x50 /* transfer tength */
#define JZ_MSC_DMACMD 0x54
#define JZ_DMA_IDI_M 0xff000000
#define JZ_DMA_ID_M 0x00ff0000
#define JZ_DMA_AOFST_M 0x00000600
#define JZ_DMA_ALIGN 0x00000100
#define JZ_DMA_ENDI 0x00000002
#define JZ_DMA_LINK 0x00000001
#define JZ_MSC_CTRL2 0x58
#define JZ_PIP 0x1f000000 /* 1 - intr trigger on high */
#define JZ_RST_EN 0x00800000
#define JZ_STPRM 0x00000010
#define JZ_SVC 0x00000008
#define JZ_SMS_M 0x00000007
#define JZ_SMS_DEF 0x00000000 /* default speed */
#define JZ_SMS_HIGH 0x00000001 /* high speed */
#define JZ_SMS_SDR12 0x00000002
#define JZ_SMS_SDR25 0x00000003
#define JZ_SMS_SDR50 0x00000004
#define JZ_MSC_RTCNT 0x5c /* RT FIFO count */
/* EFUSE Slave Interface */
#define JZ_EFUSE 0x134100D0
#define JZ_EFUCTRL 0x00
#define JZ_EFUSE_BANK 0x40000000 /* select upper 4KBit */
#define JZ_EFUSE_ADDR_M 0x3fe00000 /* in bytes */
#define JZ_EFUSE_ADDR_SHIFT 21
#define JZ_EFUSE_SIZE_M 0x001f0000 /* in bytes */
#define JZ_EFUSE_SIZE_SHIFT 16
#define JZ_EFUSE_PROG 0x00008000 /* enable programming */
#define JZ_EFUSE_WRITE 0x00000002 /* write enable */
#define JZ_EFUSE_READ 0x00000001 /* read enable */
#define JZ_EFUCFG 0x04
#define JZ_EFUSE_INT_E 0x80000000 /* which IRQ? */
#define JZ_EFUSE_RD_ADJ_M 0x00f00000
#define JZ_EFUSE_RD_STROBE 0x000f0000
#define JZ_EFUSE_WR_ADJUST 0x0000f000
#define JZ_EFUSE_WR_STROBE 0x00000fff
#define JZ_EFUSTATE 0x08
#define JZ_EFUSE_GLOBAL_P 0x00008000 /* wr protect bits */
#define JZ_EFUSE_CHIPID_P 0x00004000
#define JZ_EFUSE_CUSTID_P 0x00002000
#define JZ_EFUSE_SECWR_EN 0x00001000
#define JZ_EFUSE_PC_P 0x00000800
#define JZ_EFUSE_HDMIKEY_P 0x00000400
#define JZ_EFUSE_SECKEY_P 0x00000200
#define JZ_EFUSE_SECBOOT_EN 0x00000100
#define JZ_EFUSE_HDMI_BUSY 0x00000004
#define JZ_EFUSE_WR_DONE 0x00000002
#define JZ_EFUSE_RD_DONE 0x00000001
#define JZ_EFUDATA0 0x0C
#define JZ_EFUDATA1 0x10
#define JZ_EFUDATA2 0x14
#define JZ_EFUDATA3 0x18
#define JZ_EFUDATA4 0x1C
#define JZ_EFUDATA5 0x20
#define JZ_EFUDATA6 0x24
#define JZ_EFUDATA7 0x28
/* NEMC */
#define JZ_NEMC_BASE 0x13410000
#define JZ_NEMC_SMCR(n) (0x10 + (n) * 4)
# define JZ_NEMC_SMCR_SMT_SHIFT 0
# define JZ_NEMC_SMCR_SMT_WIDTH 1
# define JZ_NEMC_SMCR_SMT_MASK (((1 << JZ_NEMC_SMCR_SMT_WIDTH) - 1) << JZ_NEMC_SMCR_SMT_SHIFT)
# define JZ_NEMC_SMCR_SMT_NORMAL (0 << JZ_NEMC_SMCR_SMT_SHIFT)
# define JZ_NEMC_SMCR_SMT_BROM (1 << JZ_NEMC_SMCR_SMT_SHIFT)
# define JZ_NEMC_SMCR_BL_SHIFT 1
# define JZ_NEMC_SMCR_BL_WIDTH 2
# define JZ_NEMC_SMCR_BL_MASK (((1 << JZ_NEMC_SMCR_BL_WIDTH) - 1) << JZ_NEMC_SMCR_BL_SHIFT)
# define JZ_NEMC_SMCR_BL(n) (((n) << JZ_NEMC_SMCR_BL_SHIFT)
# define JZ_NEMC_SMCR_BW_SHIFT 6
# define JZ_NEMC_SMCR_BW_WIDTH 2
# define JZ_NEMC_SMCR_BW_MASK (((1 << JZ_NEMC_SMCR_BW_WIDTH) - 1) << JZ_NEMC_SMCR_BW_SHIFT)
# define JZ_NEMC_SMCR_BW_8 (0 << JZ_NEMC_SMCR_BW_SHIFT)
# define JZ_NEMC_SMCR_TAS_SHIFT 8
# define JZ_NEMC_SMCR_TAS_WIDTH 4
# define JZ_NEMC_SMCR_TAS_MASK (((1 << JZ_NEMC_SMCR_TAS_WIDTH) - 1) << JZ_NEMC_SMCR_TAS_SHIFT)
# define JZ_NEMC_SMCR_TAH_SHIFT 12
# define JZ_NEMC_SMCR_TAH_WIDTH 4
# define JZ_NEMC_SMCR_TAH_MASK (((1 << JZ_NEMC_SMCR_TAH_WIDTH) - 1) << JZ_NEMC_SMCR_TAH_SHIFT)
# define JZ_NEMC_SMCR_TBP_SHIFT 16
# define JZ_NEMC_SMCR_TBP_WIDTH 4
# define JZ_NEMC_SMCR_TBP_MASK (((1 << JZ_NEMC_SMCR_TBP_WIDTH) - 1) << JZ_NEMC_SMCR_TBP_SHIFT)
# define JZ_NEMC_SMCR_TAW_SHIFT 20
# define JZ_NEMC_SMCR_TAW_WIDTH 4
# define JZ_NEMC_SMCR_TAW_MASK (((1 << JZ_NEMC_SMCR_TAW_WIDTH) - 1) << JZ_NEMC_SMCR_TAW_SHIFT)
# define JZ_NEMC_SMCR_STRV_SHIFT 24
# define JZ_NEMC_SMCR_STRV_WIDTH 4
# define JZ_NEMC_SMCR_STRV_MASK (((1 << JZ_NEMC_SMCR_STRV_WIDTH) - 1) << JZ_NEMC_SMCR_STRV_SHIFT)
#define JZ_NEMC_SACR(n) (0x30 + (n) * 4)
# define JZ_NEMC_SACR_MASK_SHIFT 0
# define JZ_NEMC_SACR_MASK_WIDTH 8
# define JZ_NEMC_SACR_MASK_MASK (((1 << JZ_NEMC_SACR_MASK_WIDTH) - 1) << JZ_NEMC_SACR_MASK_SHIFT)
# define JZ_NEMC_SACR_ADDR_SHIFT 0
# define JZ_NEMC_SACR_ADDR_WIDTH 8
# define JZ_NEMC_SACR_ADDR_MASK (((1 << JZ_NEMC_SACR_ADDR_WIDTH) - 1) << JZ_NEMC_SACR_ADDR_SHIFT)
#define JC_NEMC_NFSCR 0x50
#endif /* JZ4780_REGS_H */

View File

@ -0,0 +1,337 @@
/*-
* Copyright 2013-2015 Alexander Kabaev <kan@FreeBSD.org>
* Copyright 2013-2015 John Wehle <john@feith.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/timetc.h>
#include <sys/timeet.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/hwfunc.h>
#include <dev/extres/clk/clk.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <mips/ingenic/jz4780_regs.h>
struct jz4780_timer_softc {
device_t dev;
struct resource * res[4];
void * ih_cookie;
struct eventtimer et;
struct timecounter tc;
};
static struct resource_spec jz4780_timer_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_IRQ, 0, RF_ACTIVE }, /* OST */
{ SYS_RES_IRQ, 1, RF_ACTIVE }, /* TC5 */
{ SYS_RES_IRQ, 2, RF_ACTIVE }, /* TC0-4,6 */
{ -1, 0 }
};
/*
* devclass_get_device / device_get_softc could be used
* to dynamically locate this, however the timers are a
* required device which can't be unloaded so there's
* no need for the overhead.
*/
static struct jz4780_timer_softc *jz4780_timer_sc = NULL;
#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
static unsigned
jz4780_get_timecount(struct timecounter *tc)
{
struct jz4780_timer_softc *sc =
(struct jz4780_timer_softc *)tc->tc_priv;
return CSR_READ_4(sc, JZ_OST_CNT_LO);
}
static int
jz4780_hardclock(void *arg)
{
struct jz4780_timer_softc *sc = (struct jz4780_timer_softc *)arg;
CSR_WRITE_4(sc, JZ_TC_TFCR, TFR_FFLAG5);
CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5);
if (sc->et.et_active)
sc->et.et_event_cb(&sc->et, sc->et.et_arg);
return (FILTER_HANDLED);
}
static int
jz4780_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
{
struct jz4780_timer_softc *sc =
(struct jz4780_timer_softc *)et->et_priv;
uint32_t ticks;
ticks = (first * et->et_frequency) / SBT_1S;
if (ticks == 0)
return (EINVAL);
CSR_WRITE_4(sc, JZ_TC_TDFR(5), ticks);
CSR_WRITE_4(sc, JZ_TC_TCNT(5), 0);
CSR_WRITE_4(sc, JZ_TC_TESR, TESR_TCST5);
return (0);
}
static int
jz4780_timer_stop(struct eventtimer *et)
{
struct jz4780_timer_softc *sc =
(struct jz4780_timer_softc *)et->et_priv;
CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5);
return (0);
}
static int
jz4780_timer_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-tcu"))
return (ENXIO);
device_set_desc(dev, "Ingenic JZ4780 timer");
return (BUS_PROBE_DEFAULT);
}
static int
jz4780_timer_attach(device_t dev)
{
struct jz4780_timer_softc *sc = device_get_softc(dev);
pcell_t counter_freq;
clk_t clk;
/* There should be exactly one instance. */
if (jz4780_timer_sc != NULL)
return (ENXIO);
sc->dev = dev;
if (bus_alloc_resources(dev, jz4780_timer_spec, sc->res)) {
device_printf(dev, "can not allocate resources for device\n");
return (ENXIO);
}
counter_freq = 0;
if (clk_get_by_name(dev, "ext", &clk) == 0) {
uint64_t clk_freq;
if (clk_get_freq(clk, &clk_freq) == 0)
counter_freq = (uint32_t)clk_freq / 16;
clk_release(clk);
}
if (counter_freq == 0) {
device_printf(dev, "unable to determine ext clock frequency\n");
/* Hardcode value we 'know' is correct */
counter_freq = 48000000 / 16;
}
/*
* Disable the timers, select the input for each timer,
* clear and then start OST.
*/
/* Stop OST, if it happens to be running */
CSR_WRITE_4(sc, JZ_TC_TECR, TESR_OST);
/* Stop all other channels as well */
CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST0 | TESR_TCST1 | TESR_TCST2 |
TESR_TCST3 | TESR_TCST4 | TESR_TCST5 | TESR_TCST6 | TESR_TCST3);
/* Clear detect mask flags */
CSR_WRITE_4(sc, JZ_TC_TFCR, 0xFFFFFFFF);
/* Mask all interrupts */
CSR_WRITE_4(sc, JZ_TC_TMSR, 0xFFFFFFFF);
/* Init counter with known data */
CSR_WRITE_4(sc, JZ_OST_CTRL, 0);
CSR_WRITE_4(sc, JZ_OST_CNT_LO, 0);
CSR_WRITE_4(sc, JZ_OST_CNT_HI, 0);
CSR_WRITE_4(sc, JZ_OST_DATA, 0xffffffff);
/* Configure counter for external clock */
CSR_WRITE_4(sc, JZ_OST_CTRL, OSTC_EXT_EN | OSTC_MODE | OSTC_DIV_16);
/* Start the counter again */
CSR_WRITE_4(sc, JZ_TC_TESR, TESR_OST);
/* Configure TCU channel 5 similarly to OST and leave it disabled */
CSR_WRITE_4(sc, JZ_TC_TCSR(5), TCSR_EXT_EN | TCSR_DIV_16);
CSR_WRITE_4(sc, JZ_TC_TMCR, TMR_FMASK(5));
if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_CLK,
jz4780_hardclock, NULL, sc, &sc->ih_cookie)) {
device_printf(dev, "could not setup interrupt handler\n");
bus_release_resources(dev, jz4780_timer_spec, sc->res);
return (ENXIO);
}
sc->et.et_name = "JZ4780 TCU5";
sc->et.et_flags = ET_FLAGS_ONESHOT;
sc->et.et_frequency = counter_freq;
sc->et.et_quality = 1000;
sc->et.et_min_period = (0x00000002LLU * SBT_1S) / sc->et.et_frequency;
sc->et.et_max_period = (0x0000fffeLLU * SBT_1S) / sc->et.et_frequency;
sc->et.et_start = jz4780_timer_start;
sc->et.et_stop = jz4780_timer_stop;
sc->et.et_priv = sc;
et_register(&sc->et);
sc->tc.tc_get_timecount = jz4780_get_timecount;
sc->tc.tc_name = "JZ4780 OST";
sc->tc.tc_frequency = counter_freq;
sc->tc.tc_counter_mask = ~0u;
sc->tc.tc_quality = 1000;
sc->tc.tc_priv = sc;
tc_init(&sc->tc);
/* Now when tc is initialized, allow DELAY to find it */
jz4780_timer_sc = sc;
return (0);
}
static int
jz4780_timer_detach(device_t dev)
{
return (EBUSY);
}
static device_method_t jz4780_timer_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_timer_probe),
DEVMETHOD(device_attach, jz4780_timer_attach),
DEVMETHOD(device_detach, jz4780_timer_detach),
DEVMETHOD_END
};
static driver_t jz4780_timer_driver = {
"timer",
jz4780_timer_methods,
sizeof(struct jz4780_timer_softc),
};
static devclass_t jz4780_timer_devclass;
EARLY_DRIVER_MODULE(timer, simplebus, jz4780_timer_driver,
jz4780_timer_devclass, 0, 0, BUS_PASS_TIMER);
void
DELAY(int usec)
{
uint32_t counter;
uint32_t delta, now, previous, remaining;
/* Timer has not yet been initialized */
if (jz4780_timer_sc == NULL) {
for (; usec > 0; usec--)
for (counter = 200; counter > 0; counter--) {
/* Prevent gcc from optimizing out the loop */
mips_rd_cause();
}
return;
}
/*
* Some of the other timers in the source tree do this calculation as:
*
* usec * ((sc->tc.tc_frequency / 1000000) + 1)
*
* which gives a fairly pessimistic result when tc_frequency is an exact
* multiple of 1000000. Given the data type and typical values for
* tc_frequency adding 999999 shouldn't overflow.
*/
remaining = usec * ((jz4780_timer_sc->tc.tc_frequency + 999999) /
1000000);
/*
* We add one since the first iteration may catch the counter just
* as it is changing.
*/
remaining += 1;
previous = jz4780_get_timecount(&jz4780_timer_sc->tc);
for ( ; ; ) {
now = jz4780_get_timecount(&jz4780_timer_sc->tc);
/*
* If the timer has rolled over, then we have the case:
*
* if (previous > now) {
* delta = (0 - previous) + now
* }
*
* which is really no different then the normal case.
* Both cases are simply:
*
* delta = now - previous.
*/
delta = now - previous;
if (delta >= remaining)
break;
previous = now;
remaining -= delta;
}
}
void
platform_initclocks(void)
{
}

View File

@ -0,0 +1,220 @@
/*-
* Copyright (c) 2013 Ian Lepore
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "opt_platform.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <machine/bus.h>
#include <dev/extres/clk/clk.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/uart/uart.h>
#include <dev/uart/uart_cpu.h>
#include <dev/uart/uart_cpu_fdt.h>
#include <dev/uart/uart_bus.h>
#include <dev/uart/uart_dev_ns8250.h>
#include <dev/ic/ns16550.h>
#include "uart_if.h"
/*
* High-level UART interface.
*/
struct jz4780_uart_softc {
struct ns8250_softc ns8250_base;
clk_t clk_mod;
clk_t clk_baud;
};
static int
jz4780_bus_attach(struct uart_softc *sc)
{
struct ns8250_softc *ns8250;
struct uart_bas *bas;
int rv;
ns8250 = (struct ns8250_softc *)sc;
bas = &sc->sc_bas;
rv = ns8250_bus_attach(sc);
if (rv != 0)
return (0);
/* Configure uart to use extra IER_RXTMOUT bit */
ns8250->ier_rxbits = IER_RXTMOUT | IER_EMSC | IER_ERLS | IER_ERXRDY;
ns8250->ier_mask = ~(ns8250->ier_rxbits);
ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask;
ns8250->ier |= ns8250->ier_rxbits;
uart_setreg(bas, REG_IER, ns8250->ier);
uart_barrier(bas);
return (0);
}
static kobj_method_t jz4780_uart_methods[] = {
KOBJMETHOD(uart_probe, ns8250_bus_probe),
KOBJMETHOD(uart_attach, jz4780_bus_attach),
KOBJMETHOD(uart_detach, ns8250_bus_detach),
KOBJMETHOD(uart_flush, ns8250_bus_flush),
KOBJMETHOD(uart_getsig, ns8250_bus_getsig),
KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl),
KOBJMETHOD(uart_ipend, ns8250_bus_ipend),
KOBJMETHOD(uart_param, ns8250_bus_param),
KOBJMETHOD(uart_receive, ns8250_bus_receive),
KOBJMETHOD(uart_setsig, ns8250_bus_setsig),
KOBJMETHOD(uart_transmit, ns8250_bus_transmit),
KOBJMETHOD(uart_grab, ns8250_bus_grab),
KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab),
KOBJMETHOD_END
};
static struct uart_class jz4780_uart_class = {
"jz4780_uart_class",
jz4780_uart_methods,
sizeof(struct jz4780_uart_softc),
.uc_ops = &uart_ns8250_ops,
.uc_range = 8,
.uc_rclk = 0,
};
/* Compatible devices. */
static struct ofw_compat_data compat_data[] = {
{"ingenic,jz4780-uart", (uintptr_t)&jz4780_uart_class},
{NULL, (uintptr_t)NULL},
};
UART_FDT_CLASS(compat_data);
/*
* UART Driver interface.
*/
static int
jz4780_uart_get_shift(device_t dev)
{
phandle_t node;
pcell_t shift;
node = ofw_bus_get_node(dev);
if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0)
shift = 2;
return ((int)shift);
}
static int
jz4780_uart_probe(device_t dev)
{
struct jz4780_uart_softc *sc;
uint64_t freq;
int shift;
int rv;
const struct ofw_compat_data *cd;
sc = device_get_softc(dev);
if (!ofw_bus_status_okay(dev))
return (ENXIO);
cd = ofw_bus_search_compatible(dev, compat_data);
if (cd->ocd_data == 0)
return (ENXIO);
/* Figure out clock setup */
rv = clk_get_by_ofw_name(dev, 0, "module", &sc->clk_mod);
if (rv != 0) {
device_printf(dev, "Cannot get UART clock: %d\n", rv);
return (ENXIO);
}
rv = clk_enable(sc->clk_mod);
if (rv != 0) {
device_printf(dev, "Cannot enable UART clock: %d\n", rv);
return (ENXIO);
}
rv = clk_get_by_ofw_name(dev, 0, "baud", &sc->clk_baud);
if (rv != 0) {
device_printf(dev, "Cannot get UART clock: %d\n", rv);
return (ENXIO);
}
rv = clk_enable(sc->clk_baud);
if (rv != 0) {
device_printf(dev, "Cannot enable UART clock: %d\n", rv);
return (ENXIO);
}
rv = clk_get_freq(sc->clk_baud, &freq);
if (rv != 0) {
device_printf(dev, "Cannot determine UART clock frequency: %d\n", rv);
return (ENXIO);
}
if (bootverbose)
device_printf(dev, "got UART clock: %lld\n", freq);
sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data;
shift = jz4780_uart_get_shift(dev);
return (uart_bus_probe(dev, shift, (int)freq, 0, 0));
}
static int
jz4780_uart_detach(device_t dev)
{
struct jz4780_uart_softc *sc;
int rv;
rv = uart_bus_detach(dev);
if (rv != 0)
return (rv);
sc = device_get_softc(dev);
if (sc->clk_mod != NULL) {
clk_release(sc->clk_mod);
}
if (sc->clk_baud != NULL) {
clk_release(sc->clk_baud);
}
return (0);
}
static device_method_t jz4780_uart_bus_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, jz4780_uart_probe),
DEVMETHOD(device_attach, uart_bus_attach),
DEVMETHOD(device_detach, jz4780_uart_detach),
{ 0, 0 }
};
static driver_t jz4780_uart_driver = {
uart_driver_name,
jz4780_uart_bus_methods,
sizeof(struct jz4780_uart_softc),
};
DRIVER_MODULE(jz4780_uart, simplebus, jz4780_uart_driver, uart_devclass,
0, 0);