Add support for the Allwinner A83T (sun8iw6p1) SoC.

Clocks, GPIO, UART, SD card / eMMC, USB, watchdog, and ethernet are
supported. Note that the A83T contains two clusters of four Cortex-A7
CPUs, and only CPUs in first cluster are started for now.

Tested on a Sinovoip Banana Pi BPI-M3.
This commit is contained in:
jmcneill 2016-05-05 09:41:57 +00:00
parent 2997dc482e
commit 2fab455b80
23 changed files with 1023 additions and 57 deletions

View File

@ -112,6 +112,7 @@ static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10-ehci", (uintptr_t)&a10_ehci_conf },
{ "allwinner,sun6i-a31-ehci", (uintptr_t)&a31_ehci_conf },
{ "allwinner,sun7i-a20-ehci", (uintptr_t)&a10_ehci_conf },
{ "allwinner,sun8i-a83t-ehci", (uintptr_t)&a31_ehci_conf },
{ NULL, (uintptr_t)NULL }
};

View File

@ -99,6 +99,12 @@ extern const struct allwinner_padconf a31s_padconf;
extern const struct allwinner_padconf a31_r_padconf;
#endif
/* Defined in a83t_padconf.c */
#ifdef SOC_ALLWINNER_A83T
extern const struct allwinner_padconf a83t_padconf;
extern const struct allwinner_padconf a83t_r_padconf;
#endif
static struct ofw_compat_data compat_data[] = {
#ifdef SOC_ALLWINNER_A10
{"allwinner,sun4i-a10-pinctrl", (uintptr_t)&a10_padconf},
@ -114,6 +120,10 @@ static struct ofw_compat_data compat_data[] = {
#endif
#if defined(SOC_ALLWINNER_A31) || defined(SOC_ALLWINNER_A31S)
{"allwinner,sun6i-a31-r-pinctrl", (uintptr_t)&a31_r_padconf},
#endif
#ifdef SOC_ALLWINNER_A83T
{"allwinner,sun8i-a83t-pinctrl", (uintptr_t)&a83t_padconf},
{"allwinner,sun8i-a83t-r-pinctrl", (uintptr_t)&a83t_r_padconf},
#endif
{NULL, 0}
};

View File

@ -0,0 +1,162 @@
/*-
* Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
* 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.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <arm/allwinner/allwinner_pinctrl.h>
#ifdef SOC_ALLWINNER_A83T
static const struct allwinner_pins a83t_pins[] = {
{ "PB0", 1, 0, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } },
{ "PB1", 1, 1, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } },
{ "PB2", 1, 2, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } },
{ "PB3", 1, 3, { "gpio_in", "gpio_out", "uart2", "jtag", NULL, NULL, "eint" } },
{ "PB4", 1, 4, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } },
{ "PB5", 1, 5, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } },
{ "PB6", 1, 6, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } },
{ "PB7", 1, 7, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } },
{ "PB8", 1, 8, { "gpio_in", "gpio_out", "i2s0", "tdm", NULL, NULL, "eint" } },
{ "PB9", 1, 9, { "gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "eint" } },
{ "PB10", 1, 10, { "gpio_in", "gpio_out", "uart0", NULL, NULL, NULL, "eint" } },
{ "PC0", 2, 0, { "gpio_in", "gpio_out", "nand", "spi0" } },
{ "PC1", 2, 1, { "gpio_in", "gpio_out", "nand", "spi0" } },
{ "PC2", 2, 2, { "gpio_in", "gpio_out", "nand", "spi0" } },
{ "PC3", 2, 3, { "gpio_in", "gpio_out", "nand", "spi0" } },
{ "PC4", 2, 4, { "gpio_in", "gpio_out", "nand" } },
{ "PC5", 2, 5, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC6", 2, 6, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC7", 2, 7, { "gpio_in", "gpio_out", "nand" } },
{ "PC8", 2, 8, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC9", 2, 9, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC10", 2, 10, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC11", 2, 11, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC12", 2, 12, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC13", 2, 13, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC14", 2, 14, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC15", 2, 15, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC16", 2, 16, { "gpio_in", "gpio_out", "nand", "mmc2" } },
{ "PC17", 2, 17, { "gpio_in", "gpio_out", "nand" } },
{ "PC18", 2, 18, { "gpio_in", "gpio_out", "nand" } },
{ "PD2", 3, 2, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD3", 3, 3, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD4", 3, 4, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD5", 3, 5, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD6", 3, 6, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD7", 3, 7, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD10", 3, 10, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD11", 3, 11, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD12", 3, 12, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD13", 3, 13, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD14", 3, 14, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD15", 3, 15, { "gpio_in", "gpio_out", "lcd", NULL, "emac" } },
{ "PD18", 3, 18, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
{ "PD19", 3, 19, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
{ "PD20", 3, 20, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
{ "PD21", 3, 21, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
{ "PD22", 3, 22, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
{ "PD23", 3, 23, { "gpio_in", "gpio_out", "lcd", "lvds", "emac" } },
{ "PD24", 3, 24, { "gpio_in", "gpio_out", "lcd", "lvds" } },
{ "PD25", 3, 25, { "gpio_in", "gpio_out", "lcd", "lvds" } },
{ "PD26", 3, 26, { "gpio_in", "gpio_out", "lcd", "lvds" } },
{ "PD27", 3, 27, { "gpio_in", "gpio_out", "lcd", "lvds" } },
{ "PD28", 3, 28, { "gpio_in", "gpio_out", "pwm" } },
{ "PD29", 3, 29, { "gpio_in", "gpio_out" } },
{ "PE0", 4, 0, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
{ "PE1", 4, 1, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
{ "PE2", 4, 2, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
{ "PE3", 4, 3, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
{ "PE4", 4, 4, { "gpio_in", "gpio_out", "csi" } },
{ "PE5", 4, 5, { "gpio_in", "gpio_out", "csi" } },
{ "PE6", 4, 6, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
{ "PE7", 4, 7, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
{ "PE8", 4, 8, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
{ "PE9", 4, 9, { "gpio_in", "gpio_out", "csi", NULL, "ccir" } },
{ "PE10", 4, 10, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } },
{ "PE11", 4, 11, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } },
{ "PE12", 4, 12, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } },
{ "PE13", 4, 13, { "gpio_in", "gpio_out", "csi", "uart4", "ccir" } },
{ "PE14", 4, 14, { "gpio_in", "gpio_out", "csi", "twi2" } },
{ "PE15", 4, 15, { "gpio_in", "gpio_out", "csi", "twi2" } },
{ "PE16", 4, 16, { "gpio_in", "gpio_out" } },
{ "PE17", 4, 17, { "gpio_in", "gpio_out" } },
{ "PE18", 4, 18, { "gpio_in", "gpio_out", NULL, "owa" } },
{ "PE19", 4, 19, { "gpio_in", "gpio_out" } },
{ "PF0", 5, 0, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
{ "PF1", 5, 1, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
{ "PF2", 5, 2, { "gpio_in", "gpio_out", "mmc0", "uart0" } },
{ "PF3", 5, 3, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
{ "PF4", 5, 4, { "gpio_in", "gpio_out", "mmc0", "uart0" } },
{ "PF5", 5, 5, { "gpio_in", "gpio_out", "mmc0", "jtag" } },
{ "PF6", 5, 6, { "gpio_in", "gpio_out" } },
{ "PG0", 6, 0, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } },
{ "PG1", 6, 1, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } },
{ "PG2", 6, 2, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } },
{ "PG3", 6, 3, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } },
{ "PG4", 6, 4, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } },
{ "PG5", 6, 5, { "gpio_in", "gpio_out", "mmc1", NULL, NULL, NULL, "eint" } },
{ "PG6", 6, 6, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } },
{ "PG7", 6, 7, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } },
{ "PG8", 6, 8, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } },
{ "PG9", 6, 9, { "gpio_in", "gpio_out", "uart1", "spi1", NULL, NULL, "eint" } },
{ "PG10", 6, 10, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } },
{ "PG11", 6, 11, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } },
{ "PG12", 6, 12, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } },
{ "PG13", 6, 13, { "gpio_in", "gpio_out", "i2s1", "uart3", NULL, NULL, "eint" } },
{ "PH0", 7, 0, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "eint" } },
{ "PH1", 7, 1, { "gpio_in", "gpio_out", "i2c0", NULL, NULL, NULL, "eint" } },
{ "PH2", 7, 2, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "eint" } },
{ "PH3", 7, 3, { "gpio_in", "gpio_out", "i2c1", NULL, NULL, NULL, "eint" } },
{ "PH4", 7, 4, { "gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, "eint" } },
{ "PH5", 7, 5, { "gpio_in", "gpio_out", "i2c2", NULL, NULL, NULL, "eint" } },
{ "PH6", 7, 6, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } },
{ "PH7", 7, 7, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } },
{ "PH8", 7, 8, { "gpio_in", "gpio_out", "hdmiddc", NULL, NULL, NULL, "eint" } },
{ "PH9", 7, 9, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } },
{ "PH10", 7, 10, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } },
{ "PH11", 7, 11, { "gpio_in", "gpio_out", NULL, NULL, NULL, NULL, "eint" } },
};
const struct allwinner_padconf a83t_padconf = {
.npins = nitems(a83t_pins),
.pins = a83t_pins,
};
#endif /* !SOC_ALLWINNER_A83T */

View File

@ -0,0 +1,62 @@
/*-
* Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
* 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.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/types.h>
#include <arm/allwinner/allwinner_pinctrl.h>
#ifdef SOC_ALLWINNER_A83T
static const struct allwinner_pins a83t_r_pins[] = {
{ "PL0", 0, 0, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "eint" } },
{ "PL1", 0, 1, { "gpio_in", "gpio_out", "s_rsb", "s_i2c", NULL, NULL, "eint" } },
{ "PL2", 0, 2, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "eint" } },
{ "PL3", 0, 3, { "gpio_in", "gpio_out", "s_uart", NULL, NULL, NULL, "eint" } },
{ "PL4", 0, 4, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } },
{ "PL5", 0, 5, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } },
{ "PL6", 0, 6, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } },
{ "PL7", 0, 7, { "gpio_in", "gpio_out", "s_jtag", NULL, NULL, NULL, "eint" } },
{ "PL8", 0, 8, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "eint" } },
{ "PL9", 0, 9, { "gpio_in", "gpio_out", "s_i2c", NULL, NULL, NULL, "eint" } },
{ "PL10", 0, 10, { "gpio_in", "gpio_out", "s_pwm", NULL, NULL, NULL, "eint" } },
{ "PL11", 0, 11, { "gpio_in", "gpio_out", NULL, NULL, NULL, "eint" } },
{ "PL12", 0, 12, { "gpio_in", "gpio_out", "s_cir", NULL, NULL, NULL, "eint" } },
};
const struct allwinner_padconf a83t_r_padconf = {
.npins = nitems(a83t_r_pins),
.pins = a83t_r_pins,
};
#endif /* !SOC_ALLWINNER_A83T */

View File

@ -0,0 +1,4 @@
# $FreeBSD$
arm/allwinner/a83t/a83t_padconf.c standard
arm/allwinner/a83t/a83t_r_padconf.c standard

View File

@ -0,0 +1,15 @@
# Allwinner A83T common options
#$FreeBSD$
cpu CPU_CORTEXA
machine arm armv6
makeoptions CONF_CFLAGS="-march=armv7a"
makeoptions KERNVIRTADDR=0xc0200000
options KERNVIRTADDR=0xc0200000
options IPI_IRQ_START=0
options IPI_IRQ_END=15
files "../allwinner/files.allwinner"
files "../allwinner/a83t/files.a83t"

View File

@ -94,6 +94,15 @@ a31s_attach(platform_t plat)
return (0);
}
static int
a83t_attach(platform_t plat)
{
soc_type = ALLWINNERSOC_A83T;
soc_family = ALLWINNERSOC_SUN8I;
return (0);
}
static vm_offset_t
allwinner_lastaddr(platform_t plat)
{
@ -196,6 +205,21 @@ static platform_method_t a31s_methods[] = {
FDT_PLATFORM_DEF(a31s, "a31s", 0, "allwinner,sun6i-a31s", 200);
#endif
#if defined(SOC_ALLWINNER_A83T)
static platform_method_t a83t_methods[] = {
PLATFORMMETHOD(platform_attach, a83t_attach),
PLATFORMMETHOD(platform_lastaddr, allwinner_lastaddr),
PLATFORMMETHOD(platform_devmap_init, allwinner_devmap_init),
#ifdef SMP
PLATFORMMETHOD(platform_mp_start_ap, a83t_mp_start_ap),
PLATFORMMETHOD(platform_mp_setmaxid, aw_mp_setmaxid),
#endif
PLATFORMMETHOD_END,
};
FDT_PLATFORM_DEF(a83t, "a83t", 0, "allwinner,sun8i-a83t", 200);
#endif
u_int
allwinner_soc_type(void)
{

View File

@ -36,11 +36,13 @@
#define ALLWINNERSOC_A20 0x20000000
#define ALLWINNERSOC_A31 0x31000000
#define ALLWINNERSOC_A31S 0x31000001
#define ALLWINNERSOC_A83T 0x83000000
#define ALLWINNERSOC_SUN4I 0x40000000
#define ALLWINNERSOC_SUN5I 0x50000000
#define ALLWINNERSOC_SUN6I 0x60000000
#define ALLWINNERSOC_SUN7I 0x70000000
#define ALLWINNERSOC_SUN8I 0x80000000
u_int allwinner_soc_type(void);
u_int allwinner_soc_family(void);

View File

@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
@ -53,40 +54,74 @@ __FBSDID("$FreeBSD$");
#define CCU_BASE 0x01c20000
#define CCU_SIZE 0x400
#define PRCM_BASE 0x01f01400
#define PRCM_SIZE 0x200
#define SYSCTRL_BASE 0x01c00000
#define SYSCTRL_SIZE 0x34
struct aw_ccu_softc {
struct simplebus_softc sc;
bus_space_tag_t bst;
bus_space_handle_t bsh;
bus_space_handle_t ccu_bsh;
bus_space_handle_t prcm_bsh;
bus_space_handle_t sysctrl_bsh;
struct mtx mtx;
int flags;
};
#define CLOCK_CCU (1 << 0)
#define CLOCK_PRCM (1 << 1)
#define CLOCK_SYSCTRL (1 << 2)
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10", 1 },
{ "allwinner,sun7i-a20", 1 },
{ "allwinner,sun6i-a31", 1 },
{ "allwinner,sun6i-a31s", 1 },
{ "allwinner,sun4i-a10", CLOCK_CCU },
{ "allwinner,sun7i-a20", CLOCK_CCU },
{ "allwinner,sun6i-a31", CLOCK_CCU },
{ "allwinner,sun6i-a31s", CLOCK_CCU },
{ "allwinner,sun8i-a83t", CLOCK_CCU|CLOCK_PRCM|CLOCK_SYSCTRL },
{ NULL, 0 }
};
static int
aw_ccu_check_addr(bus_addr_t addr)
aw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr,
bus_space_handle_t *pbsh, bus_size_t *poff)
{
if (addr < CCU_BASE || addr >= (CCU_BASE + CCU_SIZE))
return (EINVAL);
return (0);
if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE) &&
(sc->flags & CLOCK_CCU) != 0) {
*poff = addr - CCU_BASE;
*pbsh = sc->ccu_bsh;
return (0);
}
if (addr >= PRCM_BASE && addr < (PRCM_BASE + PRCM_SIZE) &&
(sc->flags & CLOCK_PRCM) != 0) {
*poff = addr - PRCM_BASE;
*pbsh = sc->prcm_bsh;
return (0);
}
if (addr >= SYSCTRL_BASE && addr < (SYSCTRL_BASE + SYSCTRL_SIZE) &&
(sc->flags & CLOCK_SYSCTRL) != 0) {
*poff = addr - SYSCTRL_BASE;
*pbsh = sc->sysctrl_bsh;
return (0);
}
return (EINVAL);
}
static int
aw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val)
{
struct aw_ccu_softc *sc;
if (aw_ccu_check_addr(addr) != 0)
return (EINVAL);
bus_space_handle_t bsh;
bus_size_t reg;
sc = device_get_softc(dev);
if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
return (EINVAL);
mtx_assert(&sc->mtx, MA_OWNED);
bus_space_write_4(sc->bst, sc->bsh, addr - CCU_BASE, val);
bus_space_write_4(sc->bst, bsh, reg, val);
return (0);
}
@ -95,13 +130,16 @@ static int
aw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
{
struct aw_ccu_softc *sc;
if (aw_ccu_check_addr(addr) != 0)
return (EINVAL);
bus_space_handle_t bsh;
bus_size_t reg;
sc = device_get_softc(dev);
if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
return (EINVAL);
mtx_assert(&sc->mtx, MA_OWNED);
*val = bus_space_read_4(sc->bst, sc->bsh, addr - CCU_BASE);
*val = bus_space_read_4(sc->bst, bsh, reg);
return (0);
}
@ -110,17 +148,20 @@ static int
aw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
{
struct aw_ccu_softc *sc;
bus_space_handle_t bsh;
bus_size_t reg;
uint32_t val;
if (aw_ccu_check_addr(addr) != 0)
sc = device_get_softc(dev);
if (aw_ccu_check_addr(sc, addr, &bsh, &reg) != 0)
return (EINVAL);
sc = device_get_softc(dev);
mtx_assert(&sc->mtx, MA_OWNED);
val = bus_space_read_4(sc->bst, sc->bsh, addr - CCU_BASE);
val = bus_space_read_4(sc->bst, bsh, reg);
val &= ~clr;
val |= set;
bus_space_write_4(sc->bst, sc->bsh, addr - CCU_BASE, val);
bus_space_write_4(sc->bst, bsh, reg, val);
return (0);
}
@ -143,20 +184,32 @@ aw_ccu_device_unlock(device_t dev)
mtx_unlock(&sc->mtx);
}
static const struct ofw_compat_data *
aw_ccu_search_compatible(void)
{
const struct ofw_compat_data *compat;
phandle_t root;
root = OF_finddevice("/");
for (compat = compat_data; compat_data->ocd_str != NULL; compat++)
if (fdt_is_compatible(root, compat->ocd_str))
break;
return (compat);
}
static int
aw_ccu_probe(device_t dev)
{
const char *name;
device_t pdev;
name = ofw_bus_get_name(dev);
if (name == NULL || strcmp(name, "clocks") != 0)
return (ENXIO);
pdev = device_get_parent(dev);
if (ofw_bus_search_compatible(pdev, compat_data)->ocd_data == 0)
return (0);
if (aw_ccu_search_compatible()->ocd_data == 0)
return (ENXIO);
device_set_desc(dev, "Allwinner Clock Control Unit");
return (BUS_PROBE_SPECIFIC);
@ -175,15 +228,37 @@ aw_ccu_attach(device_t dev)
simplebus_init(dev, node);
sc->flags = aw_ccu_search_compatible()->ocd_data;
/*
* Map CCU registers. The DT doesn't have a "reg" property for the
* /clocks node and child nodes have conflicting "reg" properties.
* Map registers. The DT doesn't have a "reg" property
* for the /clocks node and child nodes have conflicting "reg"
* properties.
*/
sc->bst = bus_get_bus_tag(dev);
error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0, &sc->bsh);
if (error != 0) {
device_printf(dev, "couldn't map CCU: %d\n", error);
return (error);
if (sc->flags & CLOCK_CCU) {
error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0,
&sc->ccu_bsh);
if (error != 0) {
device_printf(dev, "couldn't map CCU: %d\n", error);
return (error);
}
}
if (sc->flags & CLOCK_PRCM) {
error = bus_space_map(sc->bst, PRCM_BASE, PRCM_SIZE, 0,
&sc->prcm_bsh);
if (error != 0) {
device_printf(dev, "couldn't map PRCM: %d\n", error);
return (error);
}
}
if (sc->flags & CLOCK_SYSCTRL) {
error = bus_space_map(sc->bst, SYSCTRL_BASE, SYSCTRL_SIZE, 0,
&sc->sysctrl_bsh);
if (error != 0) {
device_printf(dev, "couldn't map SYSCTRL: %d\n", error);
return (error);
}
}
mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);

View File

@ -55,6 +55,9 @@ __FBSDID("$FreeBSD$");
#define CPUCFG_SIZE 0x400
#define PRCM_BASE 0x01f01400
#define PRCM_SIZE 0x800
/* Register for multi-cluster SoC */
#define CPUXCFG_BASE 0x01700000
#define CPUXCFG_SIZE 0x400
#define CPU_OFFSET 0x40
#define CPU_OFFSET_CTL 0x04
@ -80,6 +83,14 @@ __FBSDID("$FreeBSD$");
#define CPUCFG_DBGCTL0 0x1e0
#define CPUCFG_DBGCTL1 0x1e4
#define CPUS_CL_RST(cl) (0x30 + (cluster) * 0x4)
#define CPUX_CL_CTRL0(cl) (0x0 + (cluster) * 0x10)
#define CPUX_CL_CTRL1(cl) (0x4 + (cluster) * 0x10)
#define CPUX_CL_CPU_STATUS(cl) (0x30 + (cluster) * 0x4)
#define CPUX_CL_RST(cl) (0x80 + (cluster) * 0x4)
#define PRCM_CL_PWROFF(cl) (0x100 + (cluster) * 0x4)
#define PRCM_CL_PWR_CLAMP(cl, cpu) (0x140 + (cluster) * 0x4 + (cpu) * 0x4)
void
aw_mp_setmaxid(platform_t plat)
{
@ -202,3 +213,89 @@ a31_mp_start_ap(platform_t plat)
bus_space_unmap(fdtbus_bs_tag, cpucfg, CPUCFG_SIZE);
bus_space_unmap(fdtbus_bs_tag, prcm, PRCM_SIZE);
}
static void
aw_mc_mp_start_cpu(bus_space_handle_t cpuscfg, bus_space_handle_t cpuxcfg,
bus_space_handle_t prcm, int cluster, int cpu)
{
uint32_t val;
int i;
/* Assert core reset */
val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster));
val &= ~(1 << cpu);
bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster), val);
/* Assert power-on reset */
val = bus_space_read_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster));
val &= ~(1 << cpu);
bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster), val);
/* Disable automatic L1 cache invalidate at reset */
val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_CTRL0(cluster));
val &= ~(1 << cpu);
bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_CTRL0(cluster), val);
/* Release power clamp */
for (i = 0; i <= CPU_PWR_CLAMP_STEPS; i++)
bus_space_write_4(fdtbus_bs_tag, prcm,
PRCM_CL_PWR_CLAMP(cluster, cpu), 0xff >> i);
while (bus_space_read_4(fdtbus_bs_tag, prcm,
PRCM_CL_PWR_CLAMP(cluster, cpu)) != 0)
;
/* Clear power-off gating */
val = bus_space_read_4(fdtbus_bs_tag, prcm, PRCM_CL_PWROFF(cluster));
val &= ~(1 << cpu);
bus_space_write_4(fdtbus_bs_tag, prcm, PRCM_CL_PWROFF(cluster), val);
/* De-assert power-on reset */
val = bus_space_read_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster));
val |= (1 << cpu);
bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUS_CL_RST(cluster), val);
/* De-assert core reset */
val = bus_space_read_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster));
val |= (1 << cpu);
bus_space_write_4(fdtbus_bs_tag, cpuxcfg, CPUX_CL_RST(cluster), val);
}
static void
aw_mc_mp_start_ap(bus_space_handle_t cpuscfg, bus_space_handle_t cpuxcfg,
bus_space_handle_t prcm)
{
int cluster, cpu;
KASSERT(mp_ncpus <= 4, ("multiple clusters not yet supported"));
dcache_wbinv_poc_all();
bus_space_write_4(fdtbus_bs_tag, cpuscfg, CPUCFG_P_REG0,
pmap_kextract((vm_offset_t)mpentry));
cluster = 0;
for (cpu = 1; cpu < mp_ncpus; cpu++)
aw_mc_mp_start_cpu(cpuscfg, cpuxcfg, prcm, cluster, cpu);
}
void
a83t_mp_start_ap(platform_t plat)
{
bus_space_handle_t cpuscfg, cpuxcfg, prcm;
if (bus_space_map(fdtbus_bs_tag, CPUCFG_BASE, CPUCFG_SIZE,
0, &cpuscfg) != 0)
panic("Couldn't map the CPUCFG\n");
if (bus_space_map(fdtbus_bs_tag, CPUXCFG_BASE, CPUXCFG_SIZE,
0, &cpuxcfg) != 0)
panic("Couldn't map the CPUXCFG\n");
if (bus_space_map(fdtbus_bs_tag, PRCM_BASE, PRCM_SIZE, 0,
&prcm) != 0)
panic("Couldn't map the PRCM\n");
aw_mc_mp_start_ap(cpuscfg, cpuxcfg, prcm);
armv7_sev();
bus_space_unmap(fdtbus_bs_tag, cpuxcfg, CPUXCFG_SIZE);
bus_space_unmap(fdtbus_bs_tag, cpuscfg, CPUCFG_SIZE);
bus_space_unmap(fdtbus_bs_tag, prcm, PRCM_SIZE);
}

View File

@ -31,5 +31,6 @@
void aw_mp_setmaxid(platform_t plat);
void a20_mp_start_ap(platform_t plat);
void a31_mp_start_ap(platform_t plat);
void a83t_mp_start_ap(platform_t plat);
#endif /* _AW_MP_H_ */

View File

@ -55,6 +55,7 @@ static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun5i-a13-usb-phy", 1 },
{ "allwinner,sun6i-a31-usb-phy", 1 },
{ "allwinner,sun7i-a20-usb-phy", 1 },
{ "allwinner,sun8i-a83t-usb-phy", 1 },
{ NULL, 0 }
};

View File

@ -63,16 +63,35 @@ __FBSDID("$FreeBSD$");
#define A31_AHB1_CLK_SRC_SEL_MAX 3
#define A31_AHB1_CLK_SRC_SEL_SHIFT 12
#define A83T_AHB1_CLK_SRC_SEL (0x3 << 12)
#define A83T_AHB1_CLK_SRC_SEL_ISPLL(x) ((x) & 0x2)
#define A83T_AHB1_CLK_SRC_SEL_MAX 3
#define A83T_AHB1_CLK_SRC_SEL_SHIFT 12
#define A83T_AHB1_PRE_DIV (0x3 << 6)
#define A83T_AHB1_PRE_DIV_SHIFT 6
#define A83T_AHB1_CLK_DIV_RATIO (0x3 << 4)
#define A83T_AHB1_CLK_DIV_RATIO_SHIFT 4
#define H3_AHB2_CLK_CFG (0x3 << 0)
#define H3_AHB2_CLK_CFG_SHIFT 0
#define H3_AHB2_CLK_CFG_AHB1 0
#define H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2 1
#define H3_AHB2_CLK_CFG_MAX 1
enum aw_ahbclk_type {
AW_A10_AHB = 1,
AW_A13_AHB,
AW_A31_AHB1,
AW_A83T_AHB1,
AW_H3_AHB2,
};
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10-ahb-clk", AW_A10_AHB },
{ "allwinner,sun5i-a13-ahb-clk", AW_A13_AHB },
{ "allwinner,sun6i-a31-ahb1-clk", AW_A31_AHB1 },
{ "allwinner,sun8i-a83t-ahb1-clk", AW_A83T_AHB1 },
{ "allwinner,sun8i-h3-ahb2-clk", AW_H3_AHB2 },
{ NULL, 0 }
};
@ -113,6 +132,19 @@ aw_ahbclk_init(struct clknode *clk, device_t dev)
index = (val & A31_AHB1_CLK_SRC_SEL) >>
A31_AHB1_CLK_SRC_SEL_SHIFT;
break;
case AW_A83T_AHB1:
DEVICE_LOCK(sc);
AHBCLK_READ(sc, &val);
DEVICE_UNLOCK(sc);
index = (val & A83T_AHB1_CLK_SRC_SEL) >>
A83T_AHB1_CLK_SRC_SEL_SHIFT;
break;
case AW_H3_AHB2:
DEVICE_LOCK(sc);
AHBCLK_READ(sc, &val);
DEVICE_UNLOCK(sc);
index = (val & H3_AHB2_CLK_CFG) >> H3_AHB2_CLK_CFG_SHIFT;
break;
default:
return (ENXIO);
}
@ -133,11 +165,10 @@ aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq)
AHBCLK_READ(sc, &val);
DEVICE_UNLOCK(sc);
div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
A10_AHB_CLK_DIV_RATIO_SHIFT);
switch (sc->type) {
case AW_A31_AHB1:
div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
A10_AHB_CLK_DIV_RATIO_SHIFT);
src_sel = (val & A31_AHB1_CLK_SRC_SEL) >>
A31_AHB1_CLK_SRC_SEL_SHIFT;
if (src_sel == A31_AHB1_CLK_SRC_SEL_PLL6)
@ -146,7 +177,28 @@ aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq)
else
pre_div = 1;
break;
case AW_A83T_AHB1:
div = 1 << ((val & A83T_AHB1_CLK_DIV_RATIO) >>
A83T_AHB1_CLK_DIV_RATIO_SHIFT);
src_sel = (val & A83T_AHB1_CLK_SRC_SEL) >>
A83T_AHB1_CLK_SRC_SEL_SHIFT;
if (A83T_AHB1_CLK_SRC_SEL_ISPLL(src_sel))
pre_div = ((val & A83T_AHB1_PRE_DIV) >>
A83T_AHB1_PRE_DIV_SHIFT) + 1;
else
pre_div = 1;
break;
case AW_H3_AHB2:
src_sel = (val & H3_AHB2_CLK_CFG) >> H3_AHB2_CLK_CFG_SHIFT;
if (src_sel == H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2)
div = 2;
else
div = 1;
pre_div = 1;
break;
default:
div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >>
A10_AHB_CLK_DIV_RATIO_SHIFT);
pre_div = 1;
break;
}
@ -179,6 +231,26 @@ aw_ahbclk_set_mux(struct clknode *clk, int index)
AHBCLK_WRITE(sc, val);
DEVICE_UNLOCK(sc);
break;
case AW_A83T_AHB1:
if (index < 0 || index > A83T_AHB1_CLK_SRC_SEL_MAX)
return (ERANGE);
DEVICE_LOCK(sc);
AHBCLK_READ(sc, &val);
val &= ~A83T_AHB1_CLK_SRC_SEL;
val |= (index << A83T_AHB1_CLK_SRC_SEL_SHIFT);
AHBCLK_WRITE(sc, val);
DEVICE_UNLOCK(sc);
break;
case AW_H3_AHB2:
if (index < 0 || index > H3_AHB2_CLK_CFG)
return (ERANGE);
DEVICE_LOCK(sc);
AHBCLK_READ(sc, &val);
val &= ~H3_AHB2_CLK_CFG;
val |= (index << H3_AHB2_CLK_CFG_SHIFT);
AHBCLK_WRITE(sc, val);
DEVICE_UNLOCK(sc);
break;
default:
return (ENXIO);
}

View File

@ -49,24 +49,32 @@ __FBSDID("$FreeBSD$");
#include "clkdev_if.h"
#define APB0_CLK_RATIO (0x3 << 8)
#define APB0_CLK_RATIO_SHIFT 8
#define APB1_CLK_SRC_SEL (0x3 << 24)
#define APB1_CLK_SRC_SEL_SHIFT 24
#define APB1_CLK_SRC_SEL_MAX 0x3
#define APB1_CLK_RAT_N (0x3 << 16)
#define APB1_CLK_RAT_N_SHIFT 16
#define APB1_CLK_RAT_M (0x1f << 0)
#define APB1_CLK_RAT_M_SHIFT 0
#define A10_APB0_CLK_RATIO (0x3 << 8)
#define A10_APB0_CLK_RATIO_SHIFT 8
#define A10_APB1_CLK_SRC_SEL (0x3 << 24)
#define A10_APB1_CLK_SRC_SEL_SHIFT 24
#define A10_APB1_CLK_SRC_SEL_MAX 0x3
#define A10_APB1_CLK_RAT_N (0x3 << 16)
#define A10_APB1_CLK_RAT_N_SHIFT 16
#define A10_APB1_CLK_RAT_M (0x1f << 0)
#define A10_APB1_CLK_RAT_M_SHIFT 0
#define A23_APB0_CLK_RATIO (0x3 << 0)
#define A23_APB0_CLK_RATIO_SHIFT 0
#define A83T_APB1_CLK_RATIO (0x3 << 8)
#define A83T_APB1_CLK_RATIO_SHIFT 8
enum aw_apbclk_type {
AW_A10_APB0 = 1,
AW_A10_APB1,
AW_A23_APB0,
AW_A83T_APB1,
};
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10-apb0-clk", AW_A10_APB0 },
{ "allwinner,sun4i-a10-apb1-clk", AW_A10_APB1 },
{ "allwinner,sun8i-a23-apb0-clk", AW_A23_APB0 },
{ "allwinner,sun8i-a83t-apb1-clk", AW_A83T_APB1 },
{ NULL, 0 }
};
@ -91,13 +99,16 @@ aw_apbclk_init(struct clknode *clk, device_t dev)
switch (sc->type) {
case AW_A10_APB0:
case AW_A23_APB0:
case AW_A83T_APB1:
index = 0;
break;
case AW_A10_APB1:
DEVICE_LOCK(sc);
APBCLK_READ(sc, &val);
DEVICE_UNLOCK(sc);
index = (val & APB1_CLK_SRC_SEL) >> APB1_CLK_SRC_SEL_SHIFT;
index = (val & A10_APB1_CLK_SRC_SEL) >>
A10_APB1_CLK_SRC_SEL_SHIFT;
break;
default:
return (ENXIO);
@ -121,16 +132,29 @@ aw_apbclk_recalc_freq(struct clknode *clk, uint64_t *freq)
switch (sc->type) {
case AW_A10_APB0:
div = 1 << ((val & APB0_CLK_RATIO) >> APB0_CLK_RATIO_SHIFT);
div = 1 << ((val & A10_APB0_CLK_RATIO) >>
A10_APB0_CLK_RATIO_SHIFT);
if (div == 1)
div = 2;
*freq = *freq / div;
break;
case AW_A10_APB1:
n = 1 << ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_N_SHIFT);
m = ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_M_SHIFT) + 1;
n = 1 << ((val & A10_APB1_CLK_RAT_N) >>
A10_APB1_CLK_RAT_N_SHIFT);
m = ((val & A10_APB1_CLK_RAT_N) >>
A10_APB1_CLK_RAT_M_SHIFT) + 1;
*freq = *freq / n / m;
break;
case AW_A23_APB0:
div = 1 << ((val & A23_APB0_CLK_RATIO) >>
A23_APB0_CLK_RATIO_SHIFT);
*freq = *freq / div;
break;
case AW_A83T_APB1:
div = ((val & A83T_APB1_CLK_RATIO) >>
A83T_APB1_CLK_RATIO_SHIFT) + 1;
*freq = *freq / div;
break;
default:
return (ENXIO);
}
@ -149,13 +173,13 @@ aw_apbclk_set_mux(struct clknode *clk, int index)
if (sc->type != AW_A10_APB1)
return (ENXIO);
if (index < 0 || index > APB1_CLK_SRC_SEL_MAX)
if (index < 0 || index > A10_APB1_CLK_SRC_SEL_MAX)
return (ERANGE);
DEVICE_LOCK(sc);
APBCLK_READ(sc, &val);
val &= ~APB1_CLK_SRC_SEL;
val |= (index << APB1_CLK_SRC_SEL_SHIFT);
val &= ~A10_APB1_CLK_SRC_SEL;
val |= (index << A10_APB1_CLK_SRC_SEL_SHIFT);
APBCLK_WRITE(sc, val);
DEVICE_UNLOCK(sc);

View File

@ -0,0 +1,320 @@
/*-
* Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
* 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.
*
* $FreeBSD$
*/
/*
* Allwinner CPUS clock
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <machine/bus.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/ofw_subr.h>
#include <dev/extres/clk/clk.h>
#include "clkdev_if.h"
#define A80_CPUS_CLK_SRC_SEL (0x3 << 16)
#define A80_CPUS_CLK_SRC_SEL_SHIFT 16
#define A80_CPUS_CLK_SRC_SEL_X32KI 0
#define A80_CPUS_CLK_SRC_SEL_OSC24M 1
#define A80_CPUS_CLK_SRC_SEL_PLL_PERIPH 2
#define A80_CPUS_CLK_SRC_SEL_PLL_AUDIO 3
#define A80_CPUS_POST_DIV (0x1f << 8)
#define A80_CPUS_POST_DIV_SHIFT 8
#define A80_CPUS_CLK_RATIO (0x3 << 4)
#define A80_CPUS_CLK_RATIO_SHIFT 4
#define A83T_CPUS_CLK_SRC_SEL (0x3 << 16)
#define A83T_CPUS_CLK_SRC_SEL_SHIFT 16
#define A83T_CPUS_CLK_SRC_SEL_X32KI 0
#define A83T_CPUS_CLK_SRC_SEL_OSC24M 1
#define A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH 2
#define A83T_CPUS_CLK_SRC_SEL_INTERNAL_OSC 3
#define A83T_CPUS_POST_DIV (0x1f << 8)
#define A83T_CPUS_POST_DIV_SHIFT 8
#define A83T_CPUS_CLK_RATIO (0x3 << 4)
#define A83T_CPUS_CLK_RATIO_SHIFT 4
enum aw_cpusclk_type {
AW_A80_CPUS = 1,
AW_A83T_CPUS,
};
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun9i-a80-cpus-clk", AW_A80_CPUS },
{ "allwinner,sun8i-a83t-cpus-clk", AW_A83T_CPUS },
{ NULL, 0 }
};
struct aw_cpusclk_sc {
device_t clkdev;
bus_addr_t reg;
enum aw_cpusclk_type type;
};
#define CPUSCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
#define CPUSCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val))
#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev)
#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev)
static int
aw_cpusclk_init(struct clknode *clk, device_t dev)
{
struct aw_cpusclk_sc *sc;
uint32_t val, mask, shift, index;
sc = clknode_get_softc(clk);
switch (sc->type) {
case AW_A80_CPUS:
mask = A80_CPUS_CLK_SRC_SEL;
shift = A80_CPUS_CLK_SRC_SEL_SHIFT;
break;
case AW_A83T_CPUS:
mask = A83T_CPUS_CLK_SRC_SEL;
shift = A83T_CPUS_CLK_SRC_SEL_SHIFT;
break;
default:
return (ENXIO);
}
DEVICE_LOCK(sc);
CPUSCLK_READ(sc, &val);
DEVICE_UNLOCK(sc);
index = (val & mask) >> shift;
clknode_init_parent_idx(clk, index);
return (0);
}
static int
aw_cpusclk_recalc_freq(struct clknode *clk, uint64_t *freq)
{
struct aw_cpusclk_sc *sc;
uint32_t val, src_sel, post_div, clk_ratio;
sc = clknode_get_softc(clk);
DEVICE_LOCK(sc);
CPUSCLK_READ(sc, &val);
DEVICE_UNLOCK(sc);
switch (sc->type) {
case AW_A80_CPUS:
src_sel = (val & A80_CPUS_CLK_SRC_SEL) >>
A80_CPUS_CLK_SRC_SEL_SHIFT;
post_div = ((val & A80_CPUS_POST_DIV) >>
A80_CPUS_POST_DIV_SHIFT) + 1;
clk_ratio = ((val & A80_CPUS_CLK_RATIO) >>
A80_CPUS_CLK_RATIO_SHIFT) + 1;
if (src_sel == A80_CPUS_CLK_SRC_SEL_PLL_PERIPH)
*freq = *freq / post_div / clk_ratio;
else
*freq = *freq / clk_ratio;
break;
case AW_A83T_CPUS:
src_sel = (val & A83T_CPUS_CLK_SRC_SEL) >>
A83T_CPUS_CLK_SRC_SEL_SHIFT;
post_div = ((val & A83T_CPUS_POST_DIV) >>
A83T_CPUS_POST_DIV_SHIFT) + 1;
clk_ratio = 1 << ((val & A83T_CPUS_CLK_RATIO) >>
A83T_CPUS_CLK_RATIO_SHIFT);
if (src_sel == A83T_CPUS_CLK_SRC_SEL_PLL_PERIPH)
*freq = *freq / post_div / clk_ratio;
else
*freq = *freq / clk_ratio;
break;
default:
return (EINVAL);
}
return (0);
}
static int
aw_cpusclk_set_mux(struct clknode *clk, int index)
{
struct aw_cpusclk_sc *sc;
uint32_t mask, shift, val;
sc = clknode_get_softc(clk);
switch (sc->type) {
case AW_A80_CPUS:
mask = A80_CPUS_CLK_SRC_SEL;
shift = A80_CPUS_CLK_SRC_SEL_SHIFT;
break;
case AW_A83T_CPUS:
mask = A83T_CPUS_CLK_SRC_SEL;
shift = A83T_CPUS_CLK_SRC_SEL_SHIFT;
break;
default:
return (ENXIO);
}
DEVICE_LOCK(sc);
CPUSCLK_READ(sc, &val);
val &= ~mask;
val |= (index << shift);
CPUSCLK_WRITE(sc, val);
DEVICE_UNLOCK(sc);
return (0);
}
static clknode_method_t aw_cpusclk_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, aw_cpusclk_init),
CLKNODEMETHOD(clknode_recalc_freq, aw_cpusclk_recalc_freq),
CLKNODEMETHOD(clknode_set_mux, aw_cpusclk_set_mux),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(aw_cpusclk_clknode, aw_cpusclk_clknode_class,
aw_cpusclk_clknode_methods, sizeof(struct aw_cpusclk_sc), clknode_class);
static int
aw_cpusclk_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);
device_set_desc(dev, "Allwinner CPUS Clock");
return (BUS_PROBE_DEFAULT);
}
static int
aw_cpusclk_attach(device_t dev)
{
struct clknode_init_def def;
struct aw_cpusclk_sc *sc;
struct clkdom *clkdom;
struct clknode *clk;
clk_t clk_parent;
bus_addr_t paddr;
bus_size_t psize;
phandle_t node;
int error, ncells, i;
node = ofw_bus_get_node(dev);
if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) {
device_printf(dev, "cannot parse 'reg' property\n");
return (ENXIO);
}
error = ofw_bus_parse_xref_list_get_length(node, "clocks",
"#clock-cells", &ncells);
if (error != 0) {
device_printf(dev, "cannot get clock count\n");
return (error);
}
clkdom = clkdom_create(dev);
memset(&def, 0, sizeof(def));
def.id = 1;
def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP,
M_WAITOK);
for (i = 0; i < ncells; i++) {
error = clk_get_by_ofw_index(dev, i, &clk_parent);
if (error != 0) {
device_printf(dev, "cannot get clock %d\n", i);
goto fail;
}
def.parent_names[i] = clk_get_name(clk_parent);
clk_release(clk_parent);
}
def.parent_cnt = ncells;
error = clk_parse_ofw_clk_name(dev, node, &def.name);
if (error != 0) {
device_printf(dev, "cannot parse clock name\n");
error = ENXIO;
goto fail;
}
clk = clknode_create(clkdom, &aw_cpusclk_clknode_class, &def);
if (clk == NULL) {
device_printf(dev, "cannot create clknode\n");
error = ENXIO;
goto fail;
}
sc = clknode_get_softc(clk);
sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
sc->reg = paddr;
sc->clkdev = device_get_parent(dev);
clknode_register(clkdom, clk);
if (clkdom_finit(clkdom) != 0) {
device_printf(dev, "cannot finalize clkdom initialization\n");
error = ENXIO;
goto fail;
}
if (bootverbose)
clkdom_dump(clkdom);
return (0);
fail:
return (error);
}
static device_method_t aw_cpusclk_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, aw_cpusclk_probe),
DEVMETHOD(device_attach, aw_cpusclk_attach),
DEVMETHOD_END
};
static driver_t aw_cpusclk_driver = {
"aw_cpusclk",
aw_cpusclk_methods,
0
};
static devclass_t aw_cpusclk_devclass;
EARLY_DRIVER_MODULE(aw_cpusclk, simplebus, aw_cpusclk_driver,
aw_cpusclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);

View File

@ -76,6 +76,14 @@ static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun6i-a31-apb2-gates-clk",
(uintptr_t)"Allwinner APB2 Clock Gates" },
{ "allwinner,sun8i-a83t-bus-gates-clk",
(uintptr_t)"Allwinner Bus Clock Gates" },
{ "allwinner,sun8i-a83t-apb0-gates-clk",
(uintptr_t)"Allwinner APB0 Clock Gates" },
{ "allwinner,sun9i-a80-apbs-gates-clk",
(uintptr_t)"Allwinner APBS Clock Gates" },
{ NULL, 0 }
};

View File

@ -60,18 +60,41 @@ __FBSDID("$FreeBSD$");
#define GMAC_CLK_SRC_EXT_RGMII 1
#define GMAC_CLK_SRC_RGMII 2
#define EMAC_TXC_DIV_CFG (1 << 15)
#define EMAC_TXC_DIV_CFG_SHIFT 15
#define EMAC_TXC_DIV_CFG_125MHZ 0
#define EMAC_TXC_DIV_CFG_25MHZ 1
#define EMAC_PHY_SELECT (1 << 16)
#define EMAC_PHY_SELECT_SHIFT 16
#define EMAC_PHY_SELECT_INT 0
#define EMAC_PHY_SELECT_EXT 1
#define EMAC_ETXDC (0x7 << 10)
#define EMAC_ETXDC_SHIFT 10
#define EMAC_ERXDC (0x1f << 5)
#define EMAC_ERXDC_SHIFT 5
#define CLK_IDX_MII 0
#define CLK_IDX_RGMII 1
#define CLK_IDX_COUNT 2
enum aw_gmacclk_type {
GMACCLK_A20 = 1,
GMACCLK_A83T,
};
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun7i-a20-gmac-clk", 1 },
{ "allwinner,sun7i-a20-gmac-clk", GMACCLK_A20 },
{ "allwinner,sun8i-a83t-emac-clk", GMACCLK_A83T },
{ NULL, 0 }
};
struct aw_gmacclk_sc {
device_t clkdev;
bus_addr_t reg;
enum aw_gmacclk_type type;
int rx_delay;
int tx_delay;
};
#define GMACCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val))
@ -110,7 +133,7 @@ static int
aw_gmacclk_set_mux(struct clknode *clk, int index)
{
struct aw_gmacclk_sc *sc;
uint32_t val, clk_src, pit;
uint32_t val, clk_src, pit, txc_div;
int error;
sc = clknode_get_softc(clk);
@ -120,10 +143,12 @@ aw_gmacclk_set_mux(struct clknode *clk, int index)
case CLK_IDX_MII:
clk_src = GMAC_CLK_SRC_MII;
pit = GMAC_CLK_PIT_MII;
txc_div = EMAC_TXC_DIV_CFG_25MHZ;
break;
case CLK_IDX_RGMII:
clk_src = GMAC_CLK_SRC_RGMII;
pit = GMAC_CLK_PIT_RGMII;
txc_div = EMAC_TXC_DIV_CFG_125MHZ;
break;
default:
return (ENXIO);
@ -134,6 +159,20 @@ aw_gmacclk_set_mux(struct clknode *clk, int index)
val &= ~(GMAC_CLK_SRC | GMAC_CLK_PIT);
val |= (clk_src << GMAC_CLK_SRC_SHIFT);
val |= (pit << GMAC_CLK_PIT_SHIFT);
if (sc->type == GMACCLK_A83T) {
val &= ~EMAC_TXC_DIV_CFG;
val |= (txc_div << EMAC_TXC_DIV_CFG_SHIFT);
val &= ~EMAC_PHY_SELECT;
val |= (EMAC_PHY_SELECT_EXT << EMAC_PHY_SELECT_SHIFT);
if (sc->tx_delay >= 0) {
val &= ~EMAC_ETXDC;
val |= (sc->tx_delay << EMAC_ETXDC_SHIFT);
}
if (sc->rx_delay >= 0) {
val &= ~EMAC_ERXDC;
val |= (sc->rx_delay << EMAC_ERXDC_SHIFT);
}
}
GMACCLK_WRITE(sc, val);
DEVICE_UNLOCK(sc);
@ -158,7 +197,7 @@ aw_gmacclk_probe(device_t dev)
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
device_set_desc(dev, "Allwinner Module Clock");
device_set_desc(dev, "Allwinner GMAC Clock");
return (BUS_PROBE_DEFAULT);
}
@ -221,6 +260,10 @@ aw_gmacclk_attach(device_t dev)
sc = clknode_get_softc(clk);
sc->reg = paddr;
sc->clkdev = device_get_parent(dev);
sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
sc->tx_delay = sc->rx_delay = -1;
OF_getencprop(node, "tx-delay", &sc->tx_delay, sizeof(sc->tx_delay));
OF_getencprop(node, "rx-delay", &sc->rx_delay, sizeof(sc->rx_delay));
clknode_register(clkdom, clk);

View File

@ -124,6 +124,12 @@ __FBSDID("$FreeBSD$");
#define A31_PLL6_DEFAULT_K 0x1
#define A31_PLL6_TIMEOUT 10
#define A80_PLL4_CLK_OUT_EN (1 << 20)
#define A80_PLL4_PLL_DIV2 (1 << 18)
#define A80_PLL4_PLL_DIV1 (1 << 16)
#define A80_PLL4_FACTOR_N (0xff << 8)
#define A80_PLL4_FACTOR_N_SHIFT 8
#define CLKID_A10_PLL3_1X 0
#define CLKID_A10_PLL3_2X 1
@ -146,6 +152,7 @@ enum aw_pll_type {
AWPLL_A10_PLL6,
AWPLL_A31_PLL1,
AWPLL_A31_PLL6,
AWPLL_A80_PLL4,
};
struct aw_pll_sc {
@ -524,6 +531,24 @@ a31_pll6_recalc(struct aw_pll_sc *sc, uint64_t *freq)
return (0);
}
static int
a80_pll4_recalc(struct aw_pll_sc *sc, uint64_t *freq)
{
uint32_t val, n, div1, div2;
DEVICE_LOCK(sc);
PLL_READ(sc, &val);
DEVICE_UNLOCK(sc);
n = (val & A80_PLL4_FACTOR_N) >> A80_PLL4_FACTOR_N_SHIFT;
div1 = (val & A80_PLL4_PLL_DIV1) == 0 ? 1 : 2;
div2 = (val & A80_PLL4_PLL_DIV2) == 0 ? 1 : 2;
*freq = (*freq * n) / div1 / div2;
return (0);
}
#define PLL(_type, _recalc, _set_freq, _init) \
[(_type)] = { \
.recalc = (_recalc), \
@ -539,6 +564,7 @@ static struct aw_pll_funcs aw_pll_func[] = {
PLL(AWPLL_A10_PLL6, a10_pll6_recalc, a10_pll6_set_freq, a10_pll6_init),
PLL(AWPLL_A31_PLL1, a31_pll1_recalc, NULL, NULL),
PLL(AWPLL_A31_PLL6, a31_pll6_recalc, NULL, a31_pll6_init),
PLL(AWPLL_A80_PLL4, a80_pll4_recalc, NULL, NULL),
};
static struct ofw_compat_data compat_data[] = {
@ -549,6 +575,7 @@ static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10-pll6-clk", AWPLL_A10_PLL6 },
{ "allwinner,sun6i-a31-pll1-clk", AWPLL_A31_PLL1 },
{ "allwinner,sun6i-a31-pll6-clk", AWPLL_A31_PLL6 },
{ "allwinner,sun9i-a80-pll4-clk", AWPLL_A80_PLL4 },
{ NULL, 0 }
};

View File

@ -62,11 +62,13 @@ __FBSDID("$FreeBSD$");
enum aw_usbclk_type {
AW_A10_USBCLK = 1,
AW_A31_USBCLK,
AW_A83T_USBCLK,
};
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10-usb-clk", AW_A10_USBCLK },
{ "allwinner,sun6i-a31-usb-clk", AW_A31_USBCLK },
{ "allwinner,sun8i-a83t-usb-clk", AW_A83T_USBCLK },
{ NULL, 0 }
};
@ -162,10 +164,11 @@ aw_usbclk_attach(device_t dev)
struct aw_usbclk_softc *sc;
struct clkdom *clkdom;
const char **names;
const char *pname;
int index, nout, error;
enum aw_usbclk_type type;
uint32_t *indices;
clk_t clk_parent;
clk_t clk_parent, clk_parent_pll;
bus_size_t psize;
phandle_t node;
@ -196,11 +199,21 @@ aw_usbclk_attach(device_t dev)
device_printf(dev, "cannot parse clock parent\n");
return (ENXIO);
}
if (type == AW_A83T_USBCLK) {
error = clk_get_by_ofw_index(dev, 1, &clk_parent_pll);
if (error != 0) {
device_printf(dev, "cannot parse pll clock parent\n");
return (ENXIO);
}
}
for (index = 0; index < nout; index++) {
error = aw_usbclk_create(dev, sc->reg, clkdom,
clk_get_name(clk_parent), names[index],
indices != NULL ? indices[index] : index);
if (strcmp(names[index], "usb_hsic_pll") == 0)
pname = clk_get_name(clk_parent_pll);
else
pname = clk_get_name(clk_parent);
error = aw_usbclk_create(dev, sc->reg, clkdom, pname,
names[index], indices != NULL ? indices[index] : index);
if (error)
goto fail;
}

View File

@ -36,6 +36,7 @@ arm/allwinner/clk/aw_apbclk.c standard
arm/allwinner/clk/aw_axiclk.c standard
arm/allwinner/clk/aw_codecclk.c standard
arm/allwinner/clk/aw_cpuclk.c standard
arm/allwinner/clk/aw_cpusclk.c standard
arm/allwinner/clk/aw_debeclk.c standard
arm/allwinner/clk/aw_gate.c standard
arm/allwinner/clk/aw_gmacclk.c standard

View File

@ -14,3 +14,4 @@ options IPI_IRQ_END=15
files "../allwinner/files.allwinner"
files "../allwinner/a20/files.a20"
files "../allwinner/a31/files.a31"
files "../allwinner/a83t/files.a83t"

View File

@ -28,6 +28,7 @@ options INTRNG
options SOC_ALLWINNER_A20
options SOC_ALLWINNER_A31
options SOC_ALLWINNER_A31S
options SOC_ALLWINNER_A83T
options HZ=100
options SCHED_ULE # ULE scheduler
@ -121,6 +122,7 @@ device bpf
#device emac # 10/100 integrated EMAC controller
device dwc # 10/100/1000 integrated GMAC controller
device awg # 10/100/1000 integrated EMAC controller
# USB ethernet support, requires miibus
device miibus

View File

@ -44,6 +44,7 @@ SOC_ALLWINNER_A10 opt_global.h
SOC_ALLWINNER_A20 opt_global.h
SOC_ALLWINNER_A31 opt_global.h
SOC_ALLWINNER_A31S opt_global.h
SOC_ALLWINNER_A83T opt_global.h
SOC_BCM2835 opt_global.h
SOC_BCM2836 opt_global.h
SOC_IMX51 opt_global.h