Index: head/sys/arm/rockchip/files.rk32xx =================================================================== --- head/sys/arm/rockchip/files.rk32xx (nonexistent) +++ head/sys/arm/rockchip/files.rk32xx (revision 368340) @@ -0,0 +1,31 @@ +# $FreeBSD$ +kern/kern_clocksource.c standard + +arm/rockchip/rk32xx_machdep.c standard +arm/rockchip/rk32xx_mp.c optional smp +arm64/rockchip/if_dwc_rk.c standard +arm64/rockchip/rk_i2c.c standard +arm64/rockchip/rk_iodomain.c standard +arm64/rockchip/rk_gpio.c standard +arm64/rockchip/rk_grf.c standard +arm64/rockchip/rk_pinctrl.c standard +arm64/rockchip/rk_pmu.c standard +arm64/rockchip/rk_pwm.c standard +arm64/rockchip/rk_tsadc.c standard +arm64/rockchip/rk_tsadc_if.m standard +arm64/rockchip/rk_usbphy.c standard +arm64/rockchip/clk/rk_clk_armclk.c standard +arm64/rockchip/clk/rk_clk_composite.c standard +arm64/rockchip/clk/rk_clk_fract.c standard +arm64/rockchip/clk/rk_clk_gate.c standard +arm64/rockchip/clk/rk_clk_mux.c standard +arm64/rockchip/clk/rk_clk_pll.c standard +arm64/rockchip/clk/rk_cru.c standard +arm64/rockchip/clk/rk3288_cru.c standard + +dev/iicbus/pmic/act8846.c standard +dev/iicbus/pmic/act8846_regulator.c standard +dev/iicbus/pmic/fan53555.c standard +dev/iicbus/rtc/hym8563.c standard +dev/mmc/host/dwmmc.c optional dwmmc +dev/mmc/host/dwmmc_rockchip.c optional dwmmc Property changes on: head/sys/arm/rockchip/files.rk32xx ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/arm/rockchip/rk32xx_machdep.c =================================================================== --- head/sys/arm/rockchip/rk32xx_machdep.c (nonexistent) +++ head/sys/arm/rockchip/rk32xx_machdep.c (revision 368340) @@ -0,0 +1,126 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> + * + * 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 "opt_platform.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/devmap.h> +#include <sys/lock.h> +#include <sys/reboot.h> + +#include <vm/vm.h> + +#include <machine/bus.h> +#include <machine/fdt.h> +#include <machine/intr.h> +#include <machine/machdep.h> +#include <machine/platformvar.h> + +#include <dev/ofw/openfirm.h> + +#include <arm/rockchip/rk32xx_mp.h> + +#include "platform_if.h" +#define CRU_PHYSBASE 0xFF760000 +#define CRU_SIZE 0x00010000 +#define CRU_GLB_SRST_FST_VALUE 0x1B0 + +static platform_def_t rk3288w_platform; + +static void +rk32xx_late_init(platform_t plat) +{ + +} + +/* + * Set up static device mappings. + */ +static int +rk32xx_devmap_init(platform_t plat) +{ + + devmap_add_entry(0xFF000000, 0x00E00000); + return (0); +} + +static void +rk32xx_cpu_reset(platform_t plat) +{ + bus_space_handle_t cru; + + printf("Resetting...\n"); + bus_space_map(fdtbus_bs_tag, CRU_PHYSBASE, CRU_SIZE, 0, &cru); + + spinlock_enter(); + dsb(); + /* Generate 'first global software reset' */ + bus_space_write_4(fdtbus_bs_tag, cru, CRU_GLB_SRST_FST_VALUE, 0xfdb9); + while(1) + ; +} + +/* + * Early putc routine for EARLY_PRINTF support. To use, add to kernel config: + * option SOCDEV_PA=0xFF600000 + * option SOCDEV_VA=0x70000000 + * option EARLY_PRINTF + */ +#if 0 +#ifdef EARLY_PRINTF +static void +rk32xx_early_putc(int c) +{ + + volatile uint32_t * UART_STAT_REG = (uint32_t *)(0x7009007C); + volatile uint32_t * UART_TX_REG = (uint32_t *)(0x70090000); + const uint32_t UART_TXRDY = (1 << 2); + while ((*UART_STAT_REG & UART_TXRDY) == 0) + continue; + *UART_TX_REG = c; +} +early_putc_t *early_putc = rk32xx_early_putc; +#endif +#endif +static platform_method_t rk32xx_methods[] = { + PLATFORMMETHOD(platform_devmap_init, rk32xx_devmap_init), + PLATFORMMETHOD(platform_late_init, rk32xx_late_init), + PLATFORMMETHOD(platform_cpu_reset, rk32xx_cpu_reset), + +#ifdef SMP + PLATFORMMETHOD(platform_mp_start_ap, rk32xx_mp_start_ap), + PLATFORMMETHOD(platform_mp_setmaxid, rk32xx_mp_setmaxid), +#endif + PLATFORMMETHOD_END, +}; +FDT_PLATFORM_DEF2(rk32xx, rk3288, "RK3288", 0, "rockchip,rk3288", 200); +FDT_PLATFORM_DEF2(rk32xx, rk3288w, "RK3288W", 0, "rockchip,rk3288w", 200); Property changes on: head/sys/arm/rockchip/rk32xx_machdep.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/rockchip/rk32xx_mp.c =================================================================== --- head/sys/arm/rockchip/rk32xx_mp.c (nonexistent) +++ head/sys/arm/rockchip/rk32xx_mp.c (revision 368340) @@ -0,0 +1,174 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/smp.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/cpu.h> +#include <machine/fdt.h> +#include <machine/smp.h> +#include <machine/platformvar.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_cpu.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/psci/psci.h> + +#include <arm/rockchip/rk32xx_mp.h> + +#define IMEM_PHYSBASE 0xFF700000 +#define IMEM_SIZE 0x00018000 + +#define PMU_PHYSBASE 0xFF730000 +#define PMU_SIZE 0x00010000 +#define PMU_PWRDN_CON 0x08 + +static int running_cpus; +static uint32_t psci_mask, pmu_mask; +void +rk32xx_mp_setmaxid(platform_t plat) +{ + int ncpu; + + /* If we've already set the global vars don't bother to do it again. */ + if (mp_ncpus != 0) + return; + + /* Read current CP15 Cache Size ID Register */ + ncpu = cp15_l2ctlr_get(); + ncpu = CPUV7_L2CTLR_NPROC(ncpu); + + mp_ncpus = ncpu; + mp_maxid = ncpu - 1; +} + +static void +rk32xx_mp_start_pmu(uint32_t mask) +{ + bus_space_handle_t imem; + bus_space_handle_t pmu; + uint32_t val; + int i, rv; + + rv = bus_space_map(fdtbus_bs_tag, IMEM_PHYSBASE, IMEM_SIZE, 0, &imem); + if (rv != 0) + panic("Couldn't map the IMEM\n"); + rv = bus_space_map(fdtbus_bs_tag, PMU_PHYSBASE, PMU_SIZE, 0, &pmu); + if (rv != 0) + panic("Couldn't map the PMU\n"); + + /* Power off all secondary cores first */ + val = bus_space_read_4(fdtbus_bs_tag, pmu, PMU_PWRDN_CON); + for (i = 1; i < mp_ncpus; i++) + val |= 1 << i; + bus_space_write_4(fdtbus_bs_tag, pmu, PMU_PWRDN_CON, val); + DELAY(5000); + + /* Power up all secondary cores */ + val = bus_space_read_4(fdtbus_bs_tag, pmu, PMU_PWRDN_CON); + for (i = 1; i < mp_ncpus; i++) + val &= ~(1 << i); + bus_space_write_4(fdtbus_bs_tag, pmu, PMU_PWRDN_CON, val); + DELAY(5000); + + /* Copy mpentry address then magic to sram */ + val = pmap_kextract((vm_offset_t)mpentry); + bus_space_write_4(fdtbus_bs_tag, imem, 8, val); + dsb(); + bus_space_write_4(fdtbus_bs_tag, imem, 4, 0xDEADBEAF); + dsb(); + + sev(); + + bus_space_unmap(fdtbus_bs_tag, imem, IMEM_SIZE); + bus_space_unmap(fdtbus_bs_tag, pmu, PMU_SIZE); +} + +static boolean_t +rk32xx_start_ap(u_int id, phandle_t node, u_int addr_cells, pcell_t *reg) +{ + int rv; + char method[16]; + uint32_t mask; + + if (!ofw_bus_node_status_okay(node)) + return(false); + + /* Skip boot CPU. */ + if (id == 0) + return (true); + + if (running_cpus >= mp_ncpus) + return (false); + running_cpus++; + + mask = 1 << (*reg & 0x0f); + +#ifdef INVARIANTS + if ((mask & pmu_mask) || (mask & psci_mask)) + printf("CPU: Duplicated register value: 0x%X for CPU(%d)\n", + *reg, id); +#endif + rv = OF_getprop(node, "enable-method", method, sizeof(method)); + if (rv > 0 && strcmp(method, "psci") == 0) { + psci_mask |= mask; + rv = psci_cpu_on(*reg, pmap_kextract((vm_offset_t)mpentry), id); + if (rv != PSCI_RETVAL_SUCCESS) { + printf("Failed to start CPU(%d)\n", id); + return (false); + } + return (true); + } + + pmu_mask |= mask; + return (true); +} + +void +rk32xx_mp_start_ap(platform_t plat) +{ + + ofw_cpu_early_foreach(rk32xx_start_ap, true); + if (pmu_mask != 0 && psci_mask != 0) { + printf("Inconsistent CPUs startup methods detected.\n"); + printf("Only PSCI enabled cores will be started.\n"); + return; + } + if (pmu_mask != 0) + rk32xx_mp_start_pmu(pmu_mask); +} Property changes on: head/sys/arm/rockchip/rk32xx_mp.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/rockchip/rk32xx_mp.h =================================================================== --- head/sys/arm/rockchip/rk32xx_mp.h (nonexistent) +++ head/sys/arm/rockchip/rk32xx_mp.h (revision 368340) @@ -0,0 +1,36 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> + * + * 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 _RK32XX_MP_H_ +#define _RK32XX_MP_H_ + +void rk32xx_mp_setmaxid(platform_t plat); +void rk32xx_mp_start_ap(platform_t plat); + +#endif /* _RK32XX_MP_H_ */ Property changes on: head/sys/arm/rockchip/rk32xx_mp.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/rockchip/std.rk32xx =================================================================== --- head/sys/arm/rockchip/std.rk32xx (nonexistent) +++ head/sys/arm/rockchip/std.rk32xx (revision 368340) @@ -0,0 +1,8 @@ +# Rockchip rk32xx common options +#$FreeBSD$ + +cpu CPU_CORTEXA +machine arm armv7 +makeoptions CONF_CFLAGS="-mcpu=cortex-a17" + +files "../rockchip/files.rk32xx" Property changes on: head/sys/arm/rockchip/std.rk32xx ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/arm64/rockchip/clk/rk3288_cru.c =================================================================== --- head/sys/arm64/rockchip/clk/rk3288_cru.c (nonexistent) +++ head/sys/arm64/rockchip/clk/rk3288_cru.c (revision 368340) @@ -0,0 +1,1026 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot <manu@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. + * + * $FreeBSD$ + */ + +#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/fdt/simplebus.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/clk/clk_div.h> +#include <dev/extres/clk/clk_fixed.h> +#include <dev/extres/clk/clk_mux.h> + +#include <arm64/rockchip/clk/rk_cru.h> + +#include <gnu/dts/include/dt-bindings/clock/rk3288-cru.h> + +#define CRU_SOFTRST_SIZE 12 + +#define CRU_APLL_CON(x) (0x000 + (x) * 0x4) +#define CRU_DPLL_CON(x) (0x010 + (x) * 0x4) +#define CRU_CPLL_CON(x) (0x020 + (x) * 0x4) +#define CRU_GPLL_CON(x) (0x030 + (x) * 0x4) +#define CRU_NPLL_CON(x) (0x040 + (x) * 0x4) +#define CRU_MODE_CON 0x050 +#define CRU_CLKSEL_CON(x) (0x060 + (x) * 0x4) +#define CRU_CLKGATE_CON(x) (0x160 + (x) * 0x4) +#define CRU_GLB_SRST_FST_VALUE 0x1b0 +#define CRU_GLB_SRST_SND_VALUE 0x1b4 +#define CRU_SOFTRST_CON(x) (0x1b8 + (x) * 0x4) +#define CRU_MISC_CON 0x1e8 +#define CRU_GLB_CNT_TH 0x1ec +#define CRU_GLB_RST_CON 0x1f0 +#define CRU_GLB_RST_ST 0x1f8 +#define CRU_SDMMC_CON0 0x200 +#define CRU_SDMMC_CON1 0x204 +#define CRU_SDIO0_CON0 0x208 +#define CRU_SDIO0_CON1 0x20c +#define CRU_SDIO1_CON0 0x210 +#define CRU_SDIO1_CON1 0x214 +#define CRU_EMMC_CON0 0x218 +#define CRU_EMMC_CON1 0x21c + +/* GATES */ +#define GATE(_idx, _clkname, _pname, _o, _s) \ +{ \ + .id = _idx, \ + .name = _clkname, \ + .parent_name = _pname, \ + .offset = CRU_CLKGATE_CON(_o), \ + .shift = _s, \ +} + +static struct rk_cru_gate rk3288_gates[] = { + /* CRU_CLKGATE_CON0 */ + GATE(0, "sclk_acc_efuse", "xin24m", 0, 12), + GATE(0, "cpll_aclk_cpu", "cpll", 0, 11), + GATE(0, "gpll_aclk_cpu", "gpll", 0, 10), + GATE(0, "gpll_ddr", "gpll", 0, 9), + GATE(0, "dpll_ddr", "dpll", 0, 8), + GATE(0, "aclk_bus_2pmu", "aclk_cpu_pre", 0, 7), + GATE(PCLK_CPU, "pclk_cpu", "pclk_cpu_s", 0, 5), + GATE(HCLK_CPU, "hclk_cpu", "hclk_cpu_s", 0, 4), + GATE(ACLK_CPU, "aclk_cpu", "aclk_cpu_pre", 0, 3), + GATE(0, "gpll_core", "gpll", 0, 2), + GATE(0, "apll_core", "apll", 0, 1), + + + /* CRU_CLKGATE_CON1 */ + GATE(0, "uart3_frac", "uart3_frac_s", 1, 15), + GATE(0, "uart3_src", "uart3_src_s", 1, 14), + GATE(0, "uart2_frac", "uart2_frac_s", 1, 13), + GATE(0, "uart2_src", "uart2_src_s", 1, 12), + GATE(0, "uart1_frac", "uart1_frac_s", 1, 11), + GATE(0, "uart1_src", "uart1_src_s", 1, 10), + GATE(0, "uart0_frac", "uart0_frac_s", 1, 9), + GATE(0, "uart0_src", "uart0_src_s", 1, 8), + GATE(SCLK_TIMER5, "sclk_timer5", "xin24m", 1, 5), + GATE(SCLK_TIMER4, "sclk_timer4", "xin24m", 1, 4), + GATE(SCLK_TIMER3, "sclk_timer3", "xin24m", 1, 3), + GATE(SCLK_TIMER2, "sclk_timer2", "xin24m", 1, 2), + GATE(SCLK_TIMER1, "sclk_timer1", "xin24m", 1, 1), + GATE(SCLK_TIMER0, "sclk_timer0", "xin24m", 1, 0), + + /* CRU_CLKGATE_CON2 */ + GATE(0, "uart4_frac", "uart4_frac_s", 2, 13), + GATE(0, "uart4_src", "uart4_src_s", 2, 12), + GATE(SCLK_SPI2, "sclk_spi2", "sclk_spi2_s", 2, 11), + GATE(SCLK_SPI1, "sclk_spi1", "sclk_spi1_s", 2, 10), + GATE(SCLK_SPI0, "sclk_spi0", "sclk_spi0_s", 2, 9), + GATE(SCLK_SARADC, "sclk_saradc", "sclk_saradc_s", 2, 8), + GATE(SCLK_TSADC, "sclk_tsadc", "sclk_tsadc_s", 2, 7), + GATE(0, "hsadc_src", "hsadc_src_s", 2, 6), + GATE(0, "mac_pll_src", "mac_pll_src_s", 2, 5), + GATE(PCLK_PERI, "pclk_peri", "pclk_peri_s", 2, 3), + GATE(HCLK_PERI, "hclk_peri", "hclk_peri_s", 2, 2), + GATE(ACLK_PERI, "aclk_peri", "aclk_peri_src", 2, 1), + GATE(0, "aclk_peri_src", "aclk_peri_src_s", 2, 0), + + /* CRU_CLKGATE_CON3 */ + GATE(SCLK_ISP_JPE, "sclk_isp_jpe", "sclk_isp_jpe_s", 3, 15), + GATE(SCLK_ISP, "sclk_isp", "sclk_isp_s", 3, 14), + GATE(SCLK_EDP, "sclk_edp", "sclk_edp_s", 3, 13), + GATE(SCLK_EDP_24M, "sclk_edp_24m", "sclk_edp_24m_s", 3, 12), + GATE(0, "aclk_vdpu", "aclk_vdpu_s", 3, 11), + GATE(0, "hclk_vcodec_pre", "hclk_vcodec_pre_s", 3, 10), + GATE(0, "aclk_vepu", "aclk_vepu_s", 3, 9), + GATE(0, "vip_src", "vip_src_s", 3, 7), +/* 6 - Not in TRM, sclk_hsicphy480m in Linux */ + GATE(0, "aclk_rga_pre", "aclk_rga_pre_s", 3, 5), + GATE(SCLK_RGA, "sclk_rga", "sclk_rga_s", 3, 4), + GATE(DCLK_VOP1, "dclk_vop1", "dclk_vop1_s", 3, 3), + GATE(0, "aclk_vio1", "aclk_vio1_s", 3, 2), + GATE(DCLK_VOP0, "dclk_vop0", "dclk_vop0_s", 3, 1), + GATE(0, "aclk_vio0", "aclk_vio0_s", 3, 0), + + /* CRU_CLKGATE_CON4 */ +/* 15 - Test clock generator */ + GATE(0, "jtag", "ext_jtag", 4, 14), + GATE(0, "sclk_ddrphy1", "ddrphy", 4, 13), + GATE(0, "sclk_ddrphy0", "ddrphy", 4, 12), + GATE(0, "sclk_tspout", "sclk_tspout_s", 4, 11), + GATE(0, "sclk_tsp", "sclk_tsp_s", 4, 10), + GATE(SCLK_SPDIF8CH, "sclk_spdif_8ch", "spdif_8ch_mux", 4, 9), + GATE(0, "spdif_8ch_frac", "spdif_8ch_frac_s", 4, 8), + GATE(0, "spdif_8ch_pre", "spdif_8ch_pre_s", 4, 7), + GATE(SCLK_SPDIF, "sclk_spdif", "spdif_mux", 4, 6), + GATE(0, "spdif_frac", "spdif_frac_s", 4, 5), + GATE(0, "spdif_pre", "spdif_pre_s", 4, 4), + GATE(SCLK_I2S0, "sclk_i2s0", "i2s_pre", 4, 3), + GATE(0, "i2s_frac", "i2s_frac_s", 4, 2), + GATE(0, "i2s_src", "i2s_src_s", 4, 1), + GATE(SCLK_I2S0_OUT, "i2s0_clkout", "i2s0_clkout_s", 4, 1), + + /* CRU_CLKGATE_CON5 */ + GATE(SCLK_MIPIDSI_24M, "sclk_mipidsi_24m", "xin24m", 5, 15), + GATE(SCLK_USBPHY480M_SRC, "usbphy480m_src", "usbphy480m_src_s", 5, 14), + GATE(SCLK_PS2C, "sclk_ps2c", "xin24m", 5, 13), + GATE(SCLK_HDMI_HDCP, "sclk_hdmi_hdcp", "xin24m", 5, 12), + GATE(SCLK_HDMI_CEC, "sclk_hdmi_cec", "xin32k", 5, 11), + GATE(SCLK_PVTM_GPU, "sclk_pvtm_gpu", "xin24m", 5, 10), + GATE(SCLK_PVTM_CORE, "sclk_pvtm_core", "xin24m", 5, 9), + GATE(0, "pclk_pd_pmu", "pclk_pd_pmu_s", 5, 8), + GATE(SCLK_GPU, "sclk_gpu", "sclk_gpu_s", 5, 7), + GATE(SCLK_NANDC1, "sclk_nandc1", "sclk_nandc1_s", 5, 6), + GATE(SCLK_NANDC0, "sclk_nandc0", "sclk_nandc0_s", 5, 5), + GATE(SCLK_CRYPTO, "crypto", "crypto_s", 5, 4), + GATE(SCLK_MACREF_OUT, "sclk_macref_out", "mac_clk", 5, 3), + GATE(SCLK_MACREF, "sclk_macref", "mac_clk", 5, 2), + GATE(SCLK_MAC_TX, "sclk_mac_tx", "mac_clk", 5, 1), + GATE(SCLK_MAC_RX, "sclk_mac_rx", "mac_clk", 5, 0), + + + /* CRU_CLKGATE_CON6 */ + GATE(PCLK_I2C4, "pclk_i2c4", "pclk_peri", 6, 15), + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_peri", 6, 14), + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_peri", 6, 13), + GATE(PCLK_UART4, "pclk_uart4", "pclk_peri", 6, 12), + GATE(PCLK_UART3, "pclk_uart3", "pclk_peri", 6, 11), + GATE(PCLK_UART1, "pclk_uart1", "pclk_peri", 6, 9), + GATE(PCLK_UART0, "pclk_uart0", "pclk_peri", 6, 8), + GATE(PCLK_PS2C, "pclk_ps2c", "pclk_peri", 6, 7), + GATE(PCLK_SPI2, "pclk_spi2", "pclk_peri", 6, 6), + GATE(PCLK_SPI1, "pclk_spi1", "pclk_peri", 6, 5), + GATE(PCLK_SPI0, "pclk_spi0", "pclk_peri", 6, 4), + GATE(ACLK_DMAC2, "aclk_dmac2", "aclk_peri", 6, 3), + GATE(0, "aclk_peri_axi_matrix", "aclk_peri", 6, 2), + GATE(0, "pclk_peri_matrix", "pclk_peri", 6, 1), + GATE(0, "hclk_peri_matrix", "hclk_peri", 6, 0), + + + /* CRU_CLKGATE_CON7 */ + GATE(HCLK_NANDC1, "hclk_nandc1", "hclk_peri", 7, 15), + GATE(HCLK_NANDC0, "hclk_nandc0", "hclk_peri", 7, 14), + GATE(0, "hclk_mem", "hclk_peri", 7, 13), + GATE(0, "hclk_emem", "hclk_peri", 7, 12), + GATE(0, "aclk_peri_niu", "aclk_peri", 7, 11), + GATE(0, "hclk_peri_ahb_arbi", "hclk_peri", 7, 10), + GATE(0, "hclk_usb_peri", "hclk_peri", 7, 9), +/* 8 - Not in TRM - hclk_hsic in Linux */ + GATE(HCLK_USBHOST1, "hclk_host1", "hclk_peri", 7, 7), + GATE(HCLK_USBHOST0, "hclk_host0", "hclk_peri", 7, 6), + GATE(0, "pmu_hclk_otg0", "hclk_peri", 7, 5), + GATE(HCLK_OTG0, "hclk_otg0", "hclk_peri", 7, 4), + GATE(PCLK_SIM, "pclk_sim", "pclk_peri", 7, 3), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_peri", 7, 2), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_peri", 7, 1), + GATE(PCLK_I2C5, "pclk_i2c5", "pclk_peri", 7, 0), + + /* CRU_CLKGATE_CON8 */ + GATE(ACLK_MMU, "aclk_mmu", "aclk_peri", 8, 12), +/* 11 - 9 27m_tsp, hsadc_1_tsp, hsadc_1_tsp */ + GATE(HCLK_TSP, "hclk_tsp", "hclk_peri", 8, 8), + GATE(HCLK_HSADC, "hclk_hsadc", "hclk_peri", 8, 7), + GATE(HCLK_EMMC, "hclk_emmc", "hclk_peri", 8, 6), + GATE(HCLK_SDIO1, "hclk_sdio1", "hclk_peri", 8, 5), + GATE(HCLK_SDIO0, "hclk_sdio0", "hclk_peri", 8, 4), + GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 8, 3), + GATE(HCLK_GPS, "hclk_gps", "aclk_peri", 8, 2), + GATE(PCLK_GMAC, "pclk_gmac", "pclk_peri", 8, 1), + GATE(ACLK_GMAC, "aclk_gmac", "aclk_peri", 8, 0), + + /* CRU_CLKGATE_CON9 */ + GATE(HCLK_VCODEC, "hclk_vcodec", "hclk_vcodec_pre", 9, 1), + GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vcodec_pre", 9, 0), + + /* CRU_CLKGATE_CON10 */ + GATE(PCLK_PUBL0, "pclk_publ0", "pclk_cpu", 10, 15), + GATE(PCLK_DDRUPCTL0, "pclk_ddrupctl0", "pclk_cpu", 10, 14), + GATE(0, "aclk_strc_sys", "aclk_cpu", 10, 13), + GATE(ACLK_DMAC1, "aclk_dmac1", "aclk_cpu", 10, 12), + GATE(HCLK_SPDIF8CH, "hclk_spdif_8ch", "hclk_cpu", 10, 11), + GATE(HCLK_SPDIF, "hclk_spdif", "hclk_cpu", 10, 10), + GATE(HCLK_ROM, "hclk_rom", "hclk_cpu", 10, 9), + GATE(HCLK_I2S0, "hclk_i2s0", "hclk_cpu", 10, 8), + GATE(0, "sclk_intmem2", "aclk_cpu", 10, 7), + GATE(0, "sclk_intmem1", "aclk_cpu", 10, 6), + GATE(0, "sclk_intmem0", "aclk_cpu", 10, 5), + GATE(0, "aclk_intmem", "aclk_cpu", 10, 4), + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_cpu", 10, 3), + GATE(PCLK_I2C0, "pclk_i2c0", "pclk_cpu", 10, 2), + GATE(PCLK_TIMER, "pclk_timer", "pclk_cpu", 10, 1), + GATE(PCLK_PWM, "pclk_pwm", "pclk_cpu", 10, 0), + + /* CRU_CLKGATE_CON11 */ + GATE(PCLK_RKPWM, "pclk_rkpwm", "pclk_cpu", 11, 11), + GATE(PCLK_EFUSE256, "pclk_efuse_256", "pclk_cpu", 11, 10), + GATE(PCLK_UART2, "pclk_uart2", "pclk_cpu", 11, 9), + GATE(0, "aclk_ccp", "aclk_cpu", 11, 8), + GATE(HCLK_CRYPTO, "hclk_crypto", "hclk_cpu", 11, 7), + GATE(ACLK_CRYPTO, "aclk_crypto", "aclk_cpu", 11, 6), + GATE(0, "nclk_ddrupctl1", "ddrphy", 11, 5), + GATE(0, "nclk_ddrupctl0", "ddrphy", 11, 4), + GATE(PCLK_TZPC, "pclk_tzpc", "pclk_cpu", 11, 3), + GATE(PCLK_EFUSE1024, "pclk_efuse_1024", "pclk_cpu", 11, 2), + GATE(PCLK_PUBL1, "pclk_publ1", "pclk_cpu", 11, 1), + GATE(PCLK_DDRUPCTL1, "pclk_ddrupctl1", "pclk_cpu", 11, 0), + + /* CRU_CLKGATE_CON12 */ + GATE(0, "pclk_core_niu", "pclk_dbg_pre", 12, 11), + GATE(0, "cs_dbg", "pclk_dbg_pre", 12, 10), + GATE(0, "pclk_dbg", "pclk_dbg_pre", 12, 9), + GATE(0, "armcore0", "armcore0_s", 12, 8), + GATE(0, "armcore1", "armcore1_s", 12, 7), + GATE(0, "armcore2", "armcore2_s", 12, 6), + GATE(0, "armcore3", "armcore3_s", 12, 5), + GATE(0, "l2ram", "l2ram_s", 12, 4), + GATE(0, "aclk_core_m0", "aclk_core_m0_s", 12, 3), + GATE(0, "aclk_core_mp", "aclk_core_mp_s", 12, 2), + GATE(0, "atclk", "atclk_s", 12, 1), + GATE(0, "pclk_dbg_pre", "pclk_dbg_pre_s", 12, 0), + + /* CRU_CLKGATE_CON13 */ + GATE(SCLK_HEVC_CORE, "sclk_hevc_core", "sclk_hevc_core_s", 13, 15), + GATE(SCLK_HEVC_CABAC, "sclk_hevc_cabac", "sclk_hevc_cabac_s", 13, 14), + GATE(ACLK_HEVC, "aclk_hevc", "aclk_hevc_s", 13, 13), + GATE(0, "wii", "wifi_frac_s", 13, 12), + GATE(SCLK_LCDC_PWM1, "sclk_lcdc_pwm1", "xin24m", 13, 11), + GATE(SCLK_LCDC_PWM0, "sclk_lcdc_pwm0", "xin24m", 13, 10), +/* 9 - Not in TRM - hsicphy12m_xin12m in Linux */ + GATE(0, "c2c_host", "aclk_cpu_src", 13, 8), + GATE(SCLK_OTG_ADP, "sclk_otg_adp", "xin32k", 13, 7), + GATE(SCLK_OTGPHY2, "sclk_otgphy2", "xin24m", 13, 6), + GATE(SCLK_OTGPHY1, "sclk_otgphy1", "xin24m", 13, 5), + GATE(SCLK_OTGPHY0, "sclk_otgphy0", "xin24m", 13, 4), + GATE(SCLK_EMMC, "sclk_emmc", "sclk_emmc_s", 13, 3), + GATE(SCLK_SDIO1, "sclk_sdio1", "sclk_sdio1_s", 13, 2), + GATE(SCLK_SDIO0, "sclk_sdio0", "sclk_sdio0_s", 13, 1), + GATE(SCLK_SDMMC, "sclk_sdmmc", "sclk_sdmmc_s", 13, 0), + + /* CRU_CLKGATE_CON14 */ + GATE(0, "pclk_alive_niu", "pclk_pd_alive", 14, 12), + GATE(PCLK_GRF, "pclk_grf", "pclk_pd_alive", 14, 11), + GATE(PCLK_GPIO8, "pclk_gpio8", "pclk_pd_alive", 14, 8), + GATE(PCLK_GPIO7, "pclk_gpio7", "pclk_pd_alive", 14, 7), + GATE(PCLK_GPIO6, "pclk_gpio6", "pclk_pd_alive", 14, 6), + GATE(PCLK_GPIO5, "pclk_gpio5", "pclk_pd_alive", 14, 5), + GATE(PCLK_GPIO4, "pclk_gpio4", "pclk_pd_alive", 14, 4), + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_pd_alive", 14, 3), + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_pd_alive", 14, 2), + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_pd_alive", 14, 1), + + /* CRU_CLKGATE_CON15*/ + GATE(HCLK_VIP, "hclk_vip", "hclk_vio", 15, 15), + GATE(ACLK_VIP, "aclk_vip", "aclk_vio0", 15, 14), + GATE(ACLK_RGA_NIU, "aclk_rga_niu", "aclk_rga_pre", 15, 13), + GATE(ACLK_VIO1_NIU, "aclk_vio1_niu", "aclk_vio1", 15, 12), + GATE(ACLK_VIO0_NIU, "aclk_vio0_niu", "aclk_vio0", 15, 11), + GATE(HCLK_VIO_NIU, "hclk_vio_niu", "hclk_vio", 15, 10), + GATE(HCLK_VIO_AHB_ARBI, "hclk_vio_ahb_arbi", "hclk_vio",15, 9), + GATE(HCLK_VOP1, "hclk_vop1", "hclk_vio", 15, 8), + GATE(ACLK_VOP1, "aclk_vop1", "aclk_vio1", 15, 7), + GATE(HCLK_VOP0, "hclk_vop0", "hclk_vio", 15, 6), + GATE(ACLK_VOP0, "aclk_vop0", "aclk_vio0", 15, 5), +/* 4 - aclk_lcdc_iep */ + GATE(HCLK_IEP, "hclk_iep", "hclk_vio", 15, 3), + GATE(ACLK_IEP, "aclk_iep", "aclk_vio0", 15, 2), + GATE(HCLK_RGA, "hclk_rga", "hclk_vio", 15, 1), + GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 15, 0), + + /* CRU_CLKGATE_CON16 */ + GATE(PCLK_VIO2_H2P, "pclk_vio2_h2p", "hclk_vio", 16, 11), + GATE(HCLK_VIO2_H2P, "hclk_vio2_h2p", "hclk_vio", 16, 10), + GATE(PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "hclk_vio", 16, 9), + GATE(PCLK_EDP_CTRL, "pclk_edp_ctrl", "hclk_vio", 16, 8), + GATE(PCLK_LVDS_PHY, "pclk_lvds_phy", "hclk_vio", 16, 7), + GATE(PCLK_MIPI_CSI, "pclk_mipi_csi", "hclk_vio", 16, 6), + GATE(PCLK_MIPI_DSI1, "pclk_mipi_dsi1", "hclk_vio", 16, 5), + GATE(PCLK_MIPI_DSI0, "pclk_mipi_dsi0", "hclk_vio", 16, 4), + GATE(PCLK_ISP_IN, "pclk_isp_in", "ext_isp", 16, 3), + GATE(ACLK_ISP, "aclk_isp", "aclk_vio1", 16, 2), + GATE(HCLK_ISP, "hclk_isp", "hclk_vio", 16, 1), + GATE(0, "pclk_vip_in", "ext_vip", 16, 0), + + /* CRU_CLKGATE_CON17 */ + GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_pd_pmu", 17, 4), + GATE(PCLK_SGRF, "pclk_sgrf", "pclk_pd_pmu", 17, 3), + GATE(0, "pclk_pmu_niu", "pclk_pd_pmu", 17, 2), + GATE(0, "pclk_intmem1", "pclk_pd_pmu", 17, 1), + GATE(PCLK_PMU, "pclk_pmu", "pclk_pd_pmu", 17, 0), + + /* CRU_CLKGATE_CON18 */ + GATE(ACLK_GPU, "aclk_gpu", "sclk_gpu", 18, 0), +}; + +/* + * PLLs + */ +#define PLL_RATE_BA(_hz, _ref, _fb, _post, _ba) \ +{ \ + .freq = _hz, \ + .refdiv = _ref, \ + .fbdiv = _fb, \ + .postdiv1 = _post, \ + .bwadj = _ba, \ +} + +#define PLL_RATE(_mhz, _ref, _fb, _post) \ + PLL_RATE_BA(_mhz, _ref, _fb, _post, ((_fb < 2) ? 1 : _fb >> 1)) + +static struct rk_clk_pll_rate rk3288_pll_rates[] = { + PLL_RATE( 2208000000, 1, 92, 1), + PLL_RATE( 2184000000, 1, 91, 1), + PLL_RATE( 2160000000, 1, 90, 1), + PLL_RATE( 2136000000, 1, 89, 1), + PLL_RATE( 2112000000, 1, 88, 1), + PLL_RATE( 2088000000, 1, 87, 1), + PLL_RATE( 2064000000, 1, 86, 1), + PLL_RATE( 2040000000, 1, 85, 1), + PLL_RATE( 2016000000, 1, 84, 1), + PLL_RATE( 1992000000, 1, 83, 1), + PLL_RATE( 1968000000, 1, 82, 1), + PLL_RATE( 1944000000, 1, 81, 1), + PLL_RATE( 1920000000, 1, 80, 1), + PLL_RATE( 1896000000, 1, 79, 1), + PLL_RATE( 1872000000, 1, 78, 1), + PLL_RATE( 1848000000, 1, 77, 1), + PLL_RATE( 1824000000, 1, 76, 1), + PLL_RATE( 1800000000, 1, 75, 1), + PLL_RATE( 1776000000, 1, 74, 1), + PLL_RATE( 1752000000, 1, 73, 1), + PLL_RATE( 1728000000, 1, 72, 1), + PLL_RATE( 1704000000, 1, 71, 1), + PLL_RATE( 1680000000, 1, 70, 1), + PLL_RATE( 1656000000, 1, 69, 1), + PLL_RATE( 1632000000, 1, 68, 1), + PLL_RATE( 1608000000, 1, 67, 1), + PLL_RATE( 1560000000, 1, 65, 1), + PLL_RATE( 1512000000, 1, 63, 1), + PLL_RATE( 1488000000, 1, 62, 1), + PLL_RATE( 1464000000, 1, 61, 1), + PLL_RATE( 1440000000, 1, 60, 1), + PLL_RATE( 1416000000, 1, 59, 1), + PLL_RATE( 1392000000, 1, 58, 1), + PLL_RATE( 1368000000, 1, 57, 1), + PLL_RATE( 1344000000, 1, 56, 1), + PLL_RATE( 1320000000, 1, 55, 1), + PLL_RATE( 1296000000, 1, 54, 1), + PLL_RATE( 1272000000, 1, 53, 1), + PLL_RATE( 1248000000, 1, 52, 1), + PLL_RATE( 1224000000, 1, 51, 1), + PLL_RATE( 1200000000, 1, 50, 1), + PLL_RATE( 1188000000, 2, 99, 1), + PLL_RATE( 1176000000, 1, 49, 1), + PLL_RATE( 1128000000, 1, 47, 1), + PLL_RATE( 1104000000, 1, 46, 1), + PLL_RATE( 1008000000, 1, 84, 2), + PLL_RATE( 912000000, 1, 76, 2), + PLL_RATE( 891000000, 8, 594, 2), + PLL_RATE( 888000000, 1, 74, 2), + PLL_RATE( 816000000, 1, 68, 2), + PLL_RATE( 798000000, 2, 133, 2), + PLL_RATE( 792000000, 1, 66, 2), + PLL_RATE( 768000000, 1, 64, 2), + PLL_RATE( 742500000, 8, 495, 2), + PLL_RATE( 696000000, 1, 58, 2), + PLL_RATE_BA( 621000000, 1, 207, 8, 1), + PLL_RATE( 600000000, 1, 50, 2), + PLL_RATE_BA( 594000000, 1, 198, 8, 1), + PLL_RATE( 552000000, 1, 46, 2), + PLL_RATE( 504000000, 1, 84, 4), + PLL_RATE( 500000000, 3, 125, 2), + PLL_RATE( 456000000, 1, 76, 4), + PLL_RATE( 428000000, 1, 107, 6), + PLL_RATE( 408000000, 1, 68, 4), + PLL_RATE( 400000000, 3, 100, 2), + PLL_RATE_BA( 394000000, 1, 197, 12, 1), + PLL_RATE( 384000000, 2, 128, 4), + PLL_RATE( 360000000, 1, 60, 4), + PLL_RATE_BA( 356000000, 1, 178, 12, 1), + PLL_RATE_BA( 324000000, 1, 189, 14, 1), + PLL_RATE( 312000000, 1, 52, 4), + PLL_RATE_BA( 308000000, 1, 154, 12, 1), + PLL_RATE_BA( 303000000, 1, 202, 16, 1), + PLL_RATE( 300000000, 1, 75, 6), + PLL_RATE_BA( 297750000, 2, 397, 16, 1), + PLL_RATE_BA( 293250000, 2, 391, 16, 1), + PLL_RATE_BA( 292500000, 1, 195, 16, 1), + PLL_RATE( 273600000, 1, 114, 10), + PLL_RATE_BA( 273000000, 1, 182, 16, 1), + PLL_RATE_BA( 270000000, 1, 180, 16, 1), + PLL_RATE_BA( 266250000, 2, 355, 16, 1), + PLL_RATE_BA( 256500000, 1, 171, 16, 1), + PLL_RATE( 252000000, 1, 84, 8), + PLL_RATE_BA( 250500000, 1, 167, 16, 1), + PLL_RATE_BA( 243428571, 1, 142, 14, 1), + PLL_RATE( 238000000, 1, 119, 12), + PLL_RATE_BA( 219750000, 2, 293, 16, 1), + PLL_RATE_BA( 216000000, 1, 144, 16, 1), + PLL_RATE_BA( 213000000, 1, 142, 16, 1), + PLL_RATE( 195428571, 1, 114, 14), + PLL_RATE( 160000000, 1, 80, 12), + PLL_RATE( 157500000, 1, 105, 16), + PLL_RATE( 126000000, 1, 84, 16), + PLL_RATE( 48000000, 1, 64, 32), + {}, +}; + +static struct rk_clk_armclk_rates rk3288_armclk_rates[] = { + { 1800000000, 1}, + { 1704000000, 1}, + { 1608000000, 1}, + { 1512000000, 1}, + { 1416000000, 1}, + { 1200000000, 1}, + { 1008000000, 1}, + { 816000000, 1}, + { 696000000, 1}, + { 600000000, 1}, + { 408000000, 1}, + { 312000000, 1}, + { 216000000, 1}, + { 126000000, 1}, +}; + + +/* Fixed rate clock. */ +#define FRATE(_id, _name, _freq) \ +{ \ + .type = RK_CLK_FIXED, \ + .clk.fixed = &(struct clk_fixed_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = NULL, \ + .clkdef.parent_cnt = 0, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .freq = _freq, \ + }, \ +} + +/* Fixed rate multipier/divider. */ +#define FACT(_id, _name, _pname, _mult, _div) \ +{ \ + .type = RK_CLK_FIXED, \ + .clk.fixed = &(struct clk_fixed_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = (const char *[]){_pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .mult = _mult, \ + .div = _div, \ + }, \ +} + +/* Standard PLL. */ +#define PLL(_id, _name, _base, _shift) \ +{ \ + .type = RK3066_CLK_PLL, \ + .clk.pll = &(struct rk_clk_pll_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = pll_src_p, \ + .clkdef.parent_cnt = nitems(pll_src_p), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .base_offset = _base, \ + .mode_reg = CRU_MODE_CON, \ + .mode_shift = _shift, \ + .rates = rk3288_pll_rates, \ + }, \ +} + +/* Multiplexer. */ +#define MUX(_id, _name, _pn, _f, _mo, _ms, _mw) \ +{ \ + .type = RK_CLK_MUX, \ + .clk.mux = &(struct rk_clk_mux_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = _pn, \ + .clkdef.parent_cnt = nitems(_pn), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = CRU_CLKSEL_CON(_mo), \ + .shift = _ms, \ + .width = _mw, \ + .mux_flags = _f, \ + }, \ +} + +#define ARMDIV(_id, _name, _pn, _r, _o, _ds, _dw, _ms, _mw, _mp, _ap) \ +{ \ + .type = RK_CLK_ARMCLK, \ + .clk.armclk = &(struct rk_clk_armclk_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = _pn, \ + .clkdef.parent_cnt = nitems(_pn), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .muxdiv_offset = CRU_CLKSEL_CON(_o), \ + .mux_shift = _ms, \ + .mux_width = _mw, \ + .div_shift = _ds, \ + .div_width = _dw, \ + .main_parent = _mp, \ + .alt_parent = _ap, \ + .rates = _r, \ + .nrates = nitems(_r), \ + }, \ +} + +/* Fixed rate multipier/divider. */ +#define FRACT(_id, _name, _pname, _f, _o) \ +{ \ + .type = RK_CLK_FRACT, \ + .clk.fract = &(struct rk_clk_fract_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = (const char *[]){_pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = CRU_CLKSEL_CON(_o), \ + .flags = _f, \ + }, \ +} + +/* Full composite clock. */ +#define COMP(_id, _name, _pnames, _f, _o, _ds, _dw, _ms, _mw) \ +{ \ + .type = RK_CLK_COMPOSITE, \ + .clk.composite = &(struct rk_clk_composite_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = _pnames, \ + .clkdef.parent_cnt = nitems(_pnames), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .muxdiv_offset = CRU_CLKSEL_CON(_o), \ + .mux_shift = _ms, \ + .mux_width = _mw, \ + .div_shift = _ds, \ + .div_width = _dw, \ + .flags = RK_CLK_COMPOSITE_HAVE_MUX | _f, \ + }, \ +} + +/* Composite clock without mux (divider olnly). */ +#define CDIV(_id, _name, _pname, _f, _o, _ds, _dw) \ +{ \ + .type = RK_CLK_COMPOSITE, \ + .clk.composite = &(struct rk_clk_composite_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = (const char *[]){_pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .muxdiv_offset = CRU_CLKSEL_CON(_o), \ + .div_shift = _ds, \ + .div_width = _dw, \ + .flags = _f, \ + }, \ +} + + +#define PLIST(_name) static const char *_name[] +PLIST(pll_src_p) = {"xin24m", "xin24m", "xin32k"}; +PLIST(armclk_p)= {"apll_core", "gpll_core"}; +PLIST(ddrphy_p) = {"dpll_ddr", "gpll_ddr"}; +PLIST(aclk_cpu_p) = {"cpll_aclk_cpu", "gpll_aclk_cpu"}; + +PLIST(cpll_gpll_p) = {"cpll", "gpll"}; +PLIST(npll_cpll_gpll_p) = {"npll", "cpll", "gpll"}; +PLIST(cpll_gpll_npll_p) = {"cpll", "gpll", "npll"}; +PLIST(cpll_gpll_usb480m_p)= {"cpll", "gpll", "usbphy480m_src"}; +PLIST(cpll_gpll_usb480m_npll_p) = {"cpll", "gpll", "usbphy480m_src", "npll"}; + +PLIST(mmc_p) = {"cpll", "gpll", "xin24m", "xin24m"}; +PLIST(i2s_pre_p) = {"i2s_src", "i2s_frac", "ext_i2s", "xin12m"}; +PLIST(i2s_clkout_p) = {"i2s_pre", "xin12m"}; +PLIST(spdif_p) = {"spdif_pre", "spdif_frac", "xin12m"}; +PLIST(spdif_8ch_p) = {"spdif_8ch_pre", "spdif_8ch_frac", "xin12m"}; +PLIST(uart0_p) = {"uart0_src", "uart0_frac", "xin24m"}; +PLIST(uart1_p) = {"uart1_src", "uart1_frac", "xin24m"}; +PLIST(uart2_p) = {"uart2_src", "uart2_frac", "xin24m"}; +PLIST(uart3_p) = {"uart3_src", "uart3_frac", "xin24m"}; +PLIST(uart4_p) = {"uart4_src", "uart4_frac", "xin24m"}; +PLIST(vip_out_p) = {"vip_src", "xin24m"}; +PLIST(mac_p) = {"mac_pll_src", "ext_gmac"}; +PLIST(hsadcout_p) = {"hsadc_src", "ext_hsadc"}; +PLIST(edp_24m_p) = {"ext_edp_24m", "xin24m"}; +PLIST(tspout_p) = {"cpll", "gpll", "npll", "xin27m"}; +PLIST(wifi_p) = {"cpll", "gpll"}; +PLIST(usbphy480m_p) = {"sclk_otgphy1_480m", "sclk_otgphy2_480m", "sclk_otgphy0_480m"}; + +/* PLIST(aclk_vcodec_pre_p) = {"aclk_vepu", "aclk_vdpu"}; */ + + +static struct rk_clk rk3288_clks[] = { + /* External clocks */ + LINK("xin24m"), + FRATE(0, "xin32k", 32000), + FRATE(0, "xin27m", 27000000), + FRATE(0, "ext_hsadc", 0), + FRATE(0, "ext_jtag", 0), + FRATE(0, "ext_isp", 0), + FRATE(0, "ext_vip", 0), + FRATE(0, "ext_i2s", 0), + FRATE(0, "ext_edp_24m", 0), + + FRATE(0, "sclk_otgphy0_480m", 0), + FRATE(0, "sclk_otgphy1_480m", 0), + FRATE(0, "sclk_otgphy2_480m", 0), + + FRATE(0, "aclk_vcodec_pre", 0), + + /* Fixed dividers */ + FACT(0, "xin12m", "xin24m", 1, 2), + FACT(0, "hclk_vcodec_pre_s", "aclk_vcodec_pre", 1, 4), + + PLL(PLL_APLL, "apll", CRU_APLL_CON(0), 0), + PLL(PLL_DPLL, "dpll", CRU_DPLL_CON(0), 4), + PLL(PLL_CPLL, "cpll", CRU_CPLL_CON(0), 8), + PLL(PLL_GPLL, "gpll", CRU_GPLL_CON(0), 12), + PLL(PLL_NPLL, "npll", CRU_NPLL_CON(0), 14), + + /* CRU_CLKSEL0_CON */ + ARMDIV(ARMCLK, "armclk", armclk_p, rk3288_armclk_rates, + 0, 8, 5, 15, 1, 0, 1), + CDIV(0, "aclk_core_mp_s", "armclk", 0, + 0, 4, 4), + CDIV(0, "aclk_core_m0_s", "armclk", 0, + 0, 0, 4), + + /* CRU_CLKSEL1_CON */ + CDIV(0, "pclk_cpu_s", "aclk_cpu_pre", 0, + 1, 12, 3), + CDIV(0, "hclk_cpu_s", "aclk_cpu_pre", RK_CLK_COMPOSITE_DIV_EXP, + 1, 8, 2), + COMP(0, "aclk_cpu_src", aclk_cpu_p, 0, + 1, 3, 5, 15, 1), + CDIV(0, "aclk_cpu_pre", "aclk_cpu_src", 0, + 1, 0, 3), + + /* CRU_CLKSEL2_CON */ +/* 12:8 testout_div */ + CDIV(0, "sclk_tsadc_s", "xin32k", 0, + 2, 0, 6), + + /* CRU_CLKSEL3_CON */ + MUX(SCLK_UART4, "sclk_uart4", uart4_p, 0, + 3, 8, 2), + CDIV(0, "uart4_src_s", "uart_src", 0, + 3, 0, 7), + + /* CRU_CLKSEL4_CON */ + MUX(0, "i2s_pre", i2s_pre_p, 0, + 4, 8, 2), + MUX(0, "i2s0_clkout_s", i2s_clkout_p, 0, + 4, 12, 1), + COMP(0, "i2s_src_s", cpll_gpll_p, 0, + 4, 0, 7, 15, 1), + + /* CRU_CLKSEL5_CON */ + MUX(0, "spdif_src", cpll_gpll_p, 0, + 5, 15, 1), + MUX(0, "spdif_mux", spdif_p, 0, + 5, 8, 2), + CDIV(0, "spdif_pre_s", "spdif_src", 0, + 5, 0, 7), + + /* CRU_CLKSEL6_CON */ + COMP(0, "sclk_isp_jpe_s", cpll_gpll_npll_p, 0, + 6, 8, 6, 14, 2), + COMP(0, "sclk_isp_s", cpll_gpll_npll_p, 0, + 6, 0, 6, 6, 2), + + /* CRU_CLKSEL7_CON */ + FRACT(0, "uart4_frac_s", "uart4_src", 0, + 7), + + /* CRU_CLKSEL8_CON */ + FRACT(0, "i2s_frac_s", "i2s_src", 0, + 8), + + /* CRU_CLKSEL9_CON */ + FRACT(0, "spdif_frac_s", "spdif_src", 0, + 9), + + /* CRU_CLKSEL10_CON */ + CDIV(0, "pclk_peri_s", "aclk_peri_src", RK_CLK_COMPOSITE_DIV_EXP, + 10, 12, 2), + CDIV(0, "hclk_peri_s", "aclk_peri_src", RK_CLK_COMPOSITE_DIV_EXP, + 10, 8, 2), + COMP(0, "aclk_peri_src_s", cpll_gpll_p, 0, + 10, 0, 5, 15, 1), + + /* CRU_CLKSEL11_CON */ + COMP(0, "sclk_sdmmc_s", mmc_p, 0, + 11, 0, 6, 6, 2), + + /* CRU_CLKSEL12_CON */ + COMP(0, "sclk_emmc_s", mmc_p, 0, + 12, 8, 6, 14, 2), + COMP(0, "sclk_sdio0_s", mmc_p, 0, + 12, 0, 6, 6, 2), + + /* CRU_CLKSEL13_CON */ + MUX(0, "uart_src", cpll_gpll_p, 0, + 13, 15, 1), + MUX(0, "usbphy480m_src_s", usbphy480m_p, 0, + 13, 11, 2), + MUX(SCLK_UART0, "sclk_uart0", uart0_p, 0, + 13, 8, 2), + COMP(0, "uart0_src_s", cpll_gpll_usb480m_npll_p, 0, + 13, 0, 7, 13, 2), + + /* CRU_CLKSEL14_CON */ + MUX(SCLK_UART1, "sclk_uart1", uart1_p, 0, + 14, 8, 2), + CDIV(0, "uart1_src_s", "uart_src", 0, + 14, 0, 7), + + + /* CRU_CLKSEL15_CON */ + MUX(SCLK_UART2, "sclk_uart2", uart2_p, 0, + 15, 8, 2), + CDIV(0, "uart2_src_s", "uart_src", 0, + 15, 0, 7), + + /* CRU_CLKSEL16_CON */ + MUX(SCLK_UART3, "sclk_uart3", uart3_p, 0, + 16, 8, 2), + CDIV(0, "uart3_src_s", "uart_src", 0, + 16, 0, 7), + + /* CRU_CLKSEL17_CON */ + FRACT(0, "uart0_frac_s", "uart0_src", 0, + 17), + + /* CRU_CLKSEL18_CON */ + FRACT(0, "uart1_frac_s", "uart1_src", 0, + 18), + + /* CRU_CLKSEL19_CON */ + FRACT(0, "uart2_frac_s", "uart2_src", 0, + 19), + + /* CRU_CLKSEL20_CON */ + FRACT(0, "uart3_frac_s", "uart3_src", 0, + 20), + + /* CRU_CLKSEL21_CON */ + COMP(0, "mac_pll_src_s", npll_cpll_gpll_p, 0, + 21, 8, 5, 0, 2), + MUX(SCLK_MAC, "mac_clk", mac_p, 0, + 21, 4, 1), + + /* CRU_CLKSEL22_CON */ + MUX(0, "sclk_hsadc_out", hsadcout_p, 0, + 22, 4, 1), + COMP(0, "hsadc_src_s", cpll_gpll_p, 0, + 22, 8, 8, 0, 1), + MUX(0, "wifi_src", wifi_p, 0, + 22, 1, 1), +/* 7 - inverter "sclk_hsadc", "sclk_hsadc_out" */ + + /* CRU_CLKSEL23_CON */ + FRACT(0, "wifi_frac_s", "wifi_src", 0, + 23), + + /* CRU_CLKSEL24_CON */ + CDIV(0, "sclk_saradc_s", "xin24m", 0, + 24, 8, 8), + + /* CRU_CLKSEL25_CON */ + COMP(0, "sclk_spi1_s", cpll_gpll_p, 0, + 25, 8, 7, 15, 1), + COMP(0, "sclk_spi0_s", cpll_gpll_p, 0, + 25, 0, 7, 7, 1), + + /* CRU_CLKSEL26_CON */ + COMP(SCLK_VIP_OUT, "sclk_vip_out", vip_out_p, 0, + 26, 9, 5, 15, 1), + MUX(0, "vip_src_s", cpll_gpll_p, 0, + 26, 8, 1), + CDIV(0, "crypto_s", "aclk_cpu_pre", 0, + 26, 6, 2), + COMP(0, "ddrphy", ddrphy_p, RK_CLK_COMPOSITE_DIV_EXP, + 26, 0, 2, 2, 1), + + /* CRU_CLKSEL27_CON */ + COMP(0, "dclk_vop0_s", cpll_gpll_npll_p, 0, + 27, 8, 8, 0, 2), + + MUX(0, "sclk_edp_24m_s", edp_24m_p, 0, + 28, 15, 1), + CDIV(0, "hclk_vio", "aclk_vio0", 0, + 28, 8, 5), + COMP(0, "sclk_edp_s", cpll_gpll_npll_p, 0, + 28, 0, 6, 6, 2), + + /* CRU_CLKSEL29_CON */ + COMP(0, "dclk_vop1_s", cpll_gpll_npll_p, 0, + 29, 8, 8, 6, 2), +/* 4 - inverter "pclk_vip" "pclk_vip_in" */ +/* 3 - inverter "pclk_isp", "pclk_isp_in" */ + + /* CRU_CLKSEL30_CON */ + COMP(0, "sclk_rga_s", cpll_gpll_usb480m_p, 0, + 30, 8, 5, 14, 2), + COMP(0, "aclk_rga_pre_s", cpll_gpll_usb480m_p, 0, + 30, 0, 5, 6, 2), + + /* CRU_CLKSEL31_CON */ + COMP(0, "aclk_vio1_s", cpll_gpll_usb480m_p, 0, + 31, 8, 5, 14, 2), + COMP(0, "aclk_vio0_s", cpll_gpll_usb480m_p, 0, + 31, 0, 5, 6, 2), + + /* CRU_CLKSEL32_CON */ + COMP(0, "aclk_vdpu_s", cpll_gpll_usb480m_p, 0, + 32, 8, 5, 14, 2), + COMP(0, "aclk_vepu_s", cpll_gpll_usb480m_p, 0, + 32, 0, 5, 6, 2), + + /* CRU_CLKSEL33_CON */ + CDIV(0, "pclk_pd_alive", "gpll", 0, + 33, 8, 5), + CDIV(0, "pclk_pd_pmu_s", "gpll", 0, + 33, 0, 5), + + /* CRU_CLKSEL34_CON */ + COMP(0, "sclk_sdio1_s", mmc_p, 0, + 34, 8, 6, 14, 2), + COMP(0, "sclk_gpu_s", cpll_gpll_usb480m_npll_p, 0, + 34, 0, 5, 6, 2), + + /* CRU_CLKSEL35_CON */ + COMP(0, "sclk_tspout_s", tspout_p, 0, + 35, 8, 5, 14, 2), + COMP(0, "sclk_tsp_s", cpll_gpll_npll_p, 0, + 35, 0, 5, 6, 2), + + /* CRU_CLKSEL36_CON */ + CDIV(0, "armcore3_s", "armclk", 0, + 36, 12, 3), + CDIV(0, "armcore2_s", "armclk", 0, + 36, 8, 3), + CDIV(0, "armcore1_s", "armclk", 0, + 36, 4, 3), + CDIV(0, "armcore0_s", "armclk", 0, + 36, 0, 3), + + /* CRU_CLKSEL37_CON */ + CDIV(0, "pclk_dbg_pre_s", "armclk", 0, + 37, 9, 5), + CDIV(0, "atclk_s", "armclk", 0, + 37, 4, 5), + CDIV(0, "l2ram_s", "armclk", 0, + 37, 0, 3), + + /* CRU_CLKSEL38_CON */ + COMP(0, "sclk_nandc1_s", cpll_gpll_p, 0, + 38, 8, 5, 15, 1), + COMP(0, "sclk_nandc0_s", cpll_gpll_p, 0, + 38, 0, 5, 7, 1), + + /* CRU_CLKSEL39_CON */ + COMP(0, "aclk_hevc_s", cpll_gpll_npll_p, 0, + 39, 8, 5, 14, 2), + COMP(0, "sclk_spi2_s", cpll_gpll_p, 0, + 39, 0, 7, 7, 1), + + /* CRU_CLKSEL40_CON */ + CDIV(HCLK_HEVC, "hclk_hevc", "aclk_hevc", 0, + 40, 12, 2), + MUX(0, "spdif_8ch_mux", spdif_8ch_p, 0, + 40, 8, 2), + CDIV(0, "spdif_8ch_pre_s", "spdif_src", 0, + 40, 0, 7), + + /* CRU_CLKSEL41_CON */ + FRACT(0, "spdif_8ch_frac_s", "spdif_8ch_pre", 0, + 41), + + /* CRU_CLKSEL42_CON */ + COMP(0, "sclk_hevc_core_s", cpll_gpll_npll_p, 0, + 42, 8, 5, 14, 2), + COMP(0, "sclk_hevc_cabac_s", cpll_gpll_npll_p, 0, + 42, 0, 5, 6, 2), +/* + * not yet implemented MMC clocks + * id name src reg + * SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", RK3288_SDMMC_CON0 + * SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK3288_SDMMC_CON1, + + * SCLK_SDIO0_DRV, "sdio0_drv", "sclk_sdio0", RK3288_SDIO0_CON0, 1), + * SCLK_SDIO0_SAMPLE, "sdio0_sample", "sclk_sdio0", RK3288_SDIO0_CON1, 0), + + * SCLK_SDIO1_DRV, "sdio1_drv", "sclk_sdio1", RK3288_SDIO1_CON0, 1), + * SCLK_SDIO1_SAMPLE, "sdio1_sample", "sclk_sdio1", RK3288_SDIO1_CON1, 0), + + * SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc", RK3288_EMMC_CON0, 1), + * SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", RK3288_EMMC_CON1, 0), + * + * and GFR based mux for "aclk_vcodec_pre" + */ + +}; + +static int +rk3288_cru_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "rockchip,rk3288-cru")) { + device_set_desc(dev, "Rockchip RK3288 Clock and Reset Unit"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +rk3288_cru_attach(device_t dev) +{ + struct rk_cru_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + sc->gates = rk3288_gates; + sc->ngates = nitems(rk3288_gates); + + sc->clks = rk3288_clks; + sc->nclks = nitems(rk3288_clks); + + sc->reset_num = CRU_SOFTRST_SIZE * 16; + sc->reset_offset = CRU_SOFTRST_CON(0); + + return (rk_cru_attach(dev)); +} + +static device_method_t rk3288_cru_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rk3288_cru_probe), + DEVMETHOD(device_attach, rk3288_cru_attach), + + DEVMETHOD_END +}; + +static devclass_t rk3288_cru_devclass; + +DEFINE_CLASS_1(rk3288_cru, rk3288_cru_driver, rk3288_cru_methods, + sizeof(struct rk_cru_softc), rk_cru_driver); + +EARLY_DRIVER_MODULE(rk3288_cru, simplebus, rk3288_cru_driver, + rk3288_cru_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE + 1); Property changes on: head/sys/arm64/rockchip/clk/rk3288_cru.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm64/rockchip/clk/rk_clk_pll.c =================================================================== --- head/sys/arm64/rockchip/clk/rk_clk_pll.c (revision 368339) +++ head/sys/arm64/rockchip/clk/rk_clk_pll.c (revision 368340) @@ -1,543 +1,786 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org> * * 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/bus.h> #include <dev/extres/clk/clk.h> #include <arm64/rockchip/clk/rk_clk_pll.h> #include "clkdev_if.h" struct rk_clk_pll_sc { uint32_t base_offset; uint32_t gate_offset; uint32_t gate_shift; uint32_t mode_reg; uint32_t mode_shift; uint32_t flags; struct rk_clk_pll_rate *rates; struct rk_clk_pll_rate *frac_rates; }; #define WRITE4(_clk, off, val) \ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) #define READ4(_clk, off, val) \ CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define DEVICE_LOCK(_clk) \ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) #define DEVICE_UNLOCK(_clk) \ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) #define RK_CLK_PLL_MASK_SHIFT 16 #if 0 #define dprintf(format, arg...) \ printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) #else #define dprintf(format, arg...) #endif static int rk_clk_pll_set_gate(struct clknode *clk, bool enable) { struct rk_clk_pll_sc *sc; uint32_t val = 0; sc = clknode_get_softc(clk); if ((sc->flags & RK_CLK_PLL_HAVE_GATE) == 0) return (0); dprintf("%sabling gate\n", enable ? "En" : "Dis"); if (!enable) val |= 1 << sc->gate_shift; dprintf("sc->gate_shift: %x\n", sc->gate_shift); val |= (1 << sc->gate_shift) << RK_CLK_PLL_MASK_SHIFT; dprintf("Write: gate_offset=%x, val=%x\n", sc->gate_offset, val); DEVICE_LOCK(clk); WRITE4(clk, sc->gate_offset, val); DEVICE_UNLOCK(clk); return (0); } +/* CON0 */ +#define RK3066_CLK_PLL_REFDIV_SHIFT 8 +#define RK3066_CLK_PLL_REFDIV_MASK 0x3F00 +#define RK3066_CLK_PLL_POSTDIV_SHIFT 0 +#define RK3066_CLK_PLL_POSTDIV_MASK 0x000F +/* CON1 */ +#define RK3066_CLK_PLL_LOCK_MASK (1U << 31) +#define RK3066_CLK_PLL_FBDIV_SHIFT 0 +#define RK3066_CLK_PLL_FBDIV_MASK 0x0FFF +/* CON2 */ + +/* CON3 */ +#define RK3066_CLK_PLL_RESET (1 << 5) +#define RK3066_CLK_PLL_TEST (1 << 4) +#define RK3066_CLK_PLL_ENSAT (1 << 3) +#define RK3066_CLK_PLL_FASTEN (1 << 2) +#define RK3066_CLK_PLL_POWER_DOWN (1 << 1) +#define RK3066_CLK_PLL_BYPASS (1 << 0) + +#define RK3066_CLK_PLL_MODE_SLOW 0 +#define RK3066_CLK_PLL_MODE_NORMAL 1 +#define RK3066_CLK_PLL_MODE_DEEP_SLOW 2 +#define RK3066_CLK_PLL_MODE_MASK 0x3 + +static int +rk3066_clk_pll_init(struct clknode *clk, device_t dev) +{ + struct rk_clk_pll_sc *sc; + uint32_t reg; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->mode_reg, ®); + DEVICE_UNLOCK(clk); + + reg = (reg >> sc->mode_shift) & RK3066_CLK_PLL_MODE_MASK; + clknode_init_parent_idx(clk, reg); + + return (0); +} + +static int +rk3066_clk_pll_set_mux(struct clknode *clk, int idx) +{ + uint32_t reg; + struct rk_clk_pll_sc *sc; + + sc = clknode_get_softc(clk); + + reg = (idx & RK3066_CLK_PLL_MODE_MASK) << sc->mode_shift; + reg |= (RK3066_CLK_PLL_MODE_MASK << sc->mode_shift) << + RK_CLK_PLL_MASK_SHIFT; + + DEVICE_LOCK(clk); + WRITE4(clk, sc->mode_reg, reg); + DEVICE_UNLOCK(clk); + return(0); +} + +static int +rk3066_clk_pll_recalc(struct clknode *clk, uint64_t *freq) +{ + struct rk_clk_pll_sc *sc; + uint64_t rate; + uint32_t refdiv, fbdiv, postdiv; + uint32_t raw0, raw1, raw2, reg; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + + READ4(clk, sc->base_offset, &raw0); + READ4(clk, sc->base_offset + 4, &raw1); + READ4(clk, sc->base_offset + 8, &raw2); + READ4(clk, sc->mode_reg, ®); + + DEVICE_UNLOCK(clk); + + reg = (reg >> sc->mode_shift) & RK3066_CLK_PLL_MODE_MASK; + + if (reg != RK3066_CLK_PLL_MODE_NORMAL) + return (0); + + if (!(raw1 & RK3066_CLK_PLL_LOCK_MASK)) { + *freq = 0; + return (0); + } + + /* TODO MUX */ + refdiv = (raw0 & RK3066_CLK_PLL_REFDIV_MASK) >> + RK3066_CLK_PLL_REFDIV_SHIFT; + refdiv += 1; + postdiv = (raw0 & RK3066_CLK_PLL_POSTDIV_MASK) >> + RK3066_CLK_PLL_POSTDIV_SHIFT; + postdiv += 1; + fbdiv = (raw1 & RK3066_CLK_PLL_FBDIV_MASK) >> + RK3066_CLK_PLL_FBDIV_SHIFT; + fbdiv += 1; + + rate = *freq * fbdiv; + rate /= refdiv; + *freq = rate / postdiv; + + return (0); +} + +static int +rk3066_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + struct rk_clk_pll_rate *rates; + struct rk_clk_pll_sc *sc; + uint32_t reg; + int rv, timeout; + + sc = clknode_get_softc(clk); + + if (sc->rates == NULL) + return (EINVAL); + + for (rates = sc->rates; rates->freq; rates++) { + if (rates->freq == *fout) + break; + } + if (rates->freq == 0) { + *stop = 1; + return (EINVAL); + } + + DEVICE_LOCK(clk); + + /* Setting to slow mode during frequency change */ + reg = (RK3066_CLK_PLL_MODE_MASK << sc->mode_shift) << + RK_CLK_PLL_MASK_SHIFT; + dprintf("Set PLL_MODEREG to %x\n", reg); + WRITE4(clk, sc->mode_reg, reg); + + /* Reset PLL */ + WRITE4(clk, sc->base_offset + 12, RK3066_CLK_PLL_RESET | + RK3066_CLK_PLL_RESET << RK_CLK_PLL_MASK_SHIFT); + + /* Setting postdiv and refdiv */ + reg = 0; + reg |= RK3066_CLK_PLL_POSTDIV_MASK << 16; + reg |= (rates->postdiv1 - 1) << RK3066_CLK_PLL_POSTDIV_SHIFT; + + reg |= RK3066_CLK_PLL_REFDIV_MASK << 16; + reg |= (rates->refdiv - 1)<< RK3066_CLK_PLL_REFDIV_SHIFT; + + dprintf("Set PLL_CON0 to %x\n", reg); + WRITE4(clk, sc->base_offset, reg); + + + /* Setting fbdiv (no write mask)*/ + READ4(clk, sc->base_offset + 4, ®); + reg &= ~RK3066_CLK_PLL_FBDIV_MASK; + reg |= RK3066_CLK_PLL_FBDIV_MASK << 16; + reg = (rates->fbdiv - 1) << RK3066_CLK_PLL_FBDIV_SHIFT; + + dprintf("Set PLL_CON1 to %x\n", reg); + WRITE4(clk, sc->base_offset + 0x4, reg); + + /* PLL loop bandwidth adjust */ + reg = rates->bwadj - 1; + dprintf("Set PLL_CON2 to %x (%x)\n", reg, rates->bwadj); + WRITE4(clk, sc->base_offset + 0x8, reg); + + /* Clear reset */ + WRITE4(clk, sc->base_offset + 12, + RK3066_CLK_PLL_RESET << RK_CLK_PLL_MASK_SHIFT); + DELAY(100000); + + /* Reading lock */ + for (timeout = 1000; timeout >= 0; timeout--) { + READ4(clk, sc->base_offset + 0x4, ®); + if ((reg & RK3066_CLK_PLL_LOCK_MASK) != 0) + break; + DELAY(1); + } + + rv = 0; + if (timeout < 0) { + device_printf(clknode_get_device(clk), + "%s - Timedout while waiting for lock.\n", + clknode_get_name(clk)); + dprintf("PLL_CON1: %x\n", reg); + rv = ETIMEDOUT; + } + + /* Set back to normal mode */ + reg = (RK3066_CLK_PLL_MODE_NORMAL << sc->mode_shift); + reg |= (RK3066_CLK_PLL_MODE_MASK << sc->mode_shift) << + RK_CLK_PLL_MASK_SHIFT; + dprintf("Set PLL_MODEREG to %x\n", reg); + WRITE4(clk, sc->mode_reg, reg); + + DEVICE_UNLOCK(clk); + *stop = 1; + rv = clknode_set_parent_by_idx(clk, 1); + return (rv); +} + +static clknode_method_t rk3066_clk_pll_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, rk3066_clk_pll_init), + CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, rk3066_clk_pll_recalc), + CLKNODEMETHOD(clknode_set_freq, rk3066_clk_pll_set_freq), + CLKNODEMETHOD(clknode_set_mux, rk3066_clk_pll_set_mux), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(rk3066_clk_pll_clknode, rk3066_clk_pll_clknode_class, + rk3066_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class); + +int +rk3066_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef) +{ + struct clknode *clk; + struct rk_clk_pll_sc *sc; + + clk = clknode_create(clkdom, &rk3066_clk_pll_clknode_class, + &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + + sc->base_offset = clkdef->base_offset; + sc->gate_offset = clkdef->gate_offset; + sc->gate_shift = clkdef->gate_shift; + sc->mode_reg = clkdef->mode_reg; + sc->mode_shift = clkdef->mode_shift; + sc->flags = clkdef->flags; + sc->rates = clkdef->rates; + sc->frac_rates = clkdef->frac_rates; + + clknode_register(clkdom, clk); + + return (0); +} + #define RK3328_CLK_PLL_FBDIV_OFFSET 0 #define RK3328_CLK_PLL_FBDIV_SHIFT 0 #define RK3328_CLK_PLL_FBDIV_MASK 0xFFF #define RK3328_CLK_PLL_POSTDIV1_OFFSET 0 #define RK3328_CLK_PLL_POSTDIV1_SHIFT 12 #define RK3328_CLK_PLL_POSTDIV1_MASK 0x7000 #define RK3328_CLK_PLL_DSMPD_OFFSET 4 #define RK3328_CLK_PLL_DSMPD_SHIFT 12 #define RK3328_CLK_PLL_DSMPD_MASK 0x1000 #define RK3328_CLK_PLL_REFDIV_OFFSET 4 #define RK3328_CLK_PLL_REFDIV_SHIFT 0 #define RK3328_CLK_PLL_REFDIV_MASK 0x3F #define RK3328_CLK_PLL_POSTDIV2_OFFSET 4 #define RK3328_CLK_PLL_POSTDIV2_SHIFT 6 #define RK3328_CLK_PLL_POSTDIV2_MASK 0x1C0 #define RK3328_CLK_PLL_FRAC_OFFSET 8 #define RK3328_CLK_PLL_FRAC_SHIFT 0 #define RK3328_CLK_PLL_FRAC_MASK 0xFFFFFF #define RK3328_CLK_PLL_LOCK_MASK 0x400 #define RK3328_CLK_PLL_MODE_SLOW 0 #define RK3328_CLK_PLL_MODE_NORMAL 1 #define RK3328_CLK_PLL_MODE_MASK 0x1 static int rk3328_clk_pll_init(struct clknode *clk, device_t dev) { struct rk_clk_pll_sc *sc; sc = clknode_get_softc(clk); clknode_init_parent_idx(clk, 0); return (0); } static int rk3328_clk_pll_recalc(struct clknode *clk, uint64_t *freq) { struct rk_clk_pll_sc *sc; uint64_t rate; uint32_t dsmpd, refdiv, fbdiv; uint32_t postdiv1, postdiv2, frac; uint32_t raw1, raw2, raw3; sc = clknode_get_softc(clk); DEVICE_LOCK(clk); READ4(clk, sc->base_offset, &raw1); READ4(clk, sc->base_offset + 4, &raw2); READ4(clk, sc->base_offset + 8, &raw3); fbdiv = (raw1 & RK3328_CLK_PLL_FBDIV_MASK) >> RK3328_CLK_PLL_FBDIV_SHIFT; postdiv1 = (raw1 & RK3328_CLK_PLL_POSTDIV1_MASK) >> RK3328_CLK_PLL_POSTDIV1_SHIFT; dsmpd = (raw2 & RK3328_CLK_PLL_DSMPD_MASK) >> RK3328_CLK_PLL_DSMPD_SHIFT; refdiv = (raw2 & RK3328_CLK_PLL_REFDIV_MASK) >> RK3328_CLK_PLL_REFDIV_SHIFT; postdiv2 = (raw2 & RK3328_CLK_PLL_POSTDIV2_MASK) >> RK3328_CLK_PLL_POSTDIV2_SHIFT; frac = (raw3 & RK3328_CLK_PLL_FRAC_MASK) >> RK3328_CLK_PLL_FRAC_SHIFT; DEVICE_UNLOCK(clk); rate = *freq * fbdiv / refdiv; if (dsmpd == 0) { /* Fractional mode */ uint64_t frac_rate; frac_rate = *freq * frac / refdiv; rate += frac_rate >> 24; } *freq = rate / postdiv1 / postdiv2; if (*freq % 2) *freq = *freq + 1; return (0); } static int rk3328_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, int flags, int *stop) { struct rk_clk_pll_rate *rates; struct rk_clk_pll_sc *sc; uint32_t reg; int timeout; sc = clknode_get_softc(clk); if (sc->rates) rates = sc->rates; else if (sc->frac_rates) rates = sc->frac_rates; else return (EINVAL); for (; rates->freq; rates++) { if (rates->freq == *fout) break; } if (rates->freq == 0) { *stop = 1; return (EINVAL); } DEVICE_LOCK(clk); /* Setting to slow mode during frequency change */ reg = (RK3328_CLK_PLL_MODE_MASK << sc->mode_shift) << RK_CLK_PLL_MASK_SHIFT; dprintf("Set PLL_MODEREG to %x\n", reg); WRITE4(clk, sc->mode_reg, reg); /* Setting postdiv1 and fbdiv */ reg = (rates->postdiv1 << RK3328_CLK_PLL_POSTDIV1_SHIFT) | (rates->fbdiv << RK3328_CLK_PLL_FBDIV_SHIFT); reg |= (RK3328_CLK_PLL_POSTDIV1_MASK | RK3328_CLK_PLL_FBDIV_MASK) << 16; dprintf("Set PLL_CON0 to %x\n", reg); WRITE4(clk, sc->base_offset, reg); /* Setting dsmpd, postdiv2 and refdiv */ reg = (rates->dsmpd << RK3328_CLK_PLL_DSMPD_SHIFT) | (rates->postdiv2 << RK3328_CLK_PLL_POSTDIV2_SHIFT) | (rates->refdiv << RK3328_CLK_PLL_REFDIV_SHIFT); reg |= (RK3328_CLK_PLL_DSMPD_MASK | RK3328_CLK_PLL_POSTDIV2_MASK | RK3328_CLK_PLL_REFDIV_MASK) << RK_CLK_PLL_MASK_SHIFT; dprintf("Set PLL_CON1 to %x\n", reg); WRITE4(clk, sc->base_offset + 0x4, reg); /* Setting frac */ READ4(clk, sc->base_offset + 0x8, ®); reg &= ~RK3328_CLK_PLL_FRAC_MASK; reg |= rates->frac << RK3328_CLK_PLL_FRAC_SHIFT; dprintf("Set PLL_CON2 to %x\n", reg); WRITE4(clk, sc->base_offset + 0x8, reg); /* Reading lock */ for (timeout = 1000; timeout; timeout--) { READ4(clk, sc->base_offset + 0x4, ®); if ((reg & RK3328_CLK_PLL_LOCK_MASK) == 0) break; DELAY(1); } /* Set back to normal mode */ reg = (RK3328_CLK_PLL_MODE_NORMAL << sc->mode_shift); reg |= (RK3328_CLK_PLL_MODE_MASK << sc->mode_shift) << RK_CLK_PLL_MASK_SHIFT; dprintf("Set PLL_MODEREG to %x\n", reg); WRITE4(clk, sc->mode_reg, reg); DEVICE_UNLOCK(clk); *stop = 1; return (0); } static clknode_method_t rk3328_clk_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, rk3328_clk_pll_init), CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, rk3328_clk_pll_recalc), CLKNODEMETHOD(clknode_set_freq, rk3328_clk_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(rk3328_clk_pll_clknode, rk3328_clk_pll_clknode_class, rk3328_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class); int rk3328_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef) { struct clknode *clk; struct rk_clk_pll_sc *sc; clk = clknode_create(clkdom, &rk3328_clk_pll_clknode_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->base_offset = clkdef->base_offset; sc->gate_offset = clkdef->gate_offset; sc->gate_shift = clkdef->gate_shift; sc->mode_reg = clkdef->mode_reg; sc->mode_shift = clkdef->mode_shift; sc->flags = clkdef->flags; sc->rates = clkdef->rates; sc->frac_rates = clkdef->frac_rates; clknode_register(clkdom, clk); return (0); } #define RK3399_CLK_PLL_FBDIV_OFFSET 0 #define RK3399_CLK_PLL_FBDIV_SHIFT 0 #define RK3399_CLK_PLL_FBDIV_MASK 0xFFF #define RK3399_CLK_PLL_POSTDIV2_OFFSET 4 #define RK3399_CLK_PLL_POSTDIV2_SHIFT 12 #define RK3399_CLK_PLL_POSTDIV2_MASK 0x7000 #define RK3399_CLK_PLL_POSTDIV1_OFFSET 4 #define RK3399_CLK_PLL_POSTDIV1_SHIFT 8 #define RK3399_CLK_PLL_POSTDIV1_MASK 0x700 #define RK3399_CLK_PLL_REFDIV_OFFSET 4 #define RK3399_CLK_PLL_REFDIV_SHIFT 0 #define RK3399_CLK_PLL_REFDIV_MASK 0x3F #define RK3399_CLK_PLL_FRAC_OFFSET 8 #define RK3399_CLK_PLL_FRAC_SHIFT 0 #define RK3399_CLK_PLL_FRAC_MASK 0xFFFFFF #define RK3399_CLK_PLL_DSMPD_OFFSET 0xC #define RK3399_CLK_PLL_DSMPD_SHIFT 3 #define RK3399_CLK_PLL_DSMPD_MASK 0x8 #define RK3399_CLK_PLL_LOCK_OFFSET 8 #define RK3399_CLK_PLL_LOCK_MASK 0x400 #define RK3399_CLK_PLL_MODE_OFFSET 0xC #define RK3399_CLK_PLL_MODE_MASK 0x300 #define RK3399_CLK_PLL_MODE_SLOW 0 #define RK3399_CLK_PLL_MODE_NORMAL 1 #define RK3399_CLK_PLL_MODE_DEEPSLOW 2 #define RK3399_CLK_PLL_MODE_SHIFT 8 #define RK3399_CLK_PLL_WRITE_MASK 0xFFFF0000 static int rk3399_clk_pll_init(struct clknode *clk, device_t dev) { struct rk_clk_pll_sc *sc; sc = clknode_get_softc(clk); clknode_init_parent_idx(clk, 0); return (0); } static int rk3399_clk_pll_recalc(struct clknode *clk, uint64_t *freq) { struct rk_clk_pll_sc *sc; uint32_t dsmpd, refdiv, fbdiv; uint32_t postdiv1, postdiv2, fracdiv; uint32_t con1, con2, con3, con4; uint64_t foutvco; uint32_t mode; sc = clknode_get_softc(clk); DEVICE_LOCK(clk); READ4(clk, sc->base_offset, &con1); READ4(clk, sc->base_offset + 4, &con2); READ4(clk, sc->base_offset + 8, &con3); READ4(clk, sc->base_offset + 0xC, &con4); DEVICE_UNLOCK(clk); /* * if we are in slow mode the output freq * is the parent one, the 24Mhz external oscillator * if we are in deep mode the output freq is 32.768khz */ mode = (con4 & RK3399_CLK_PLL_MODE_MASK) >> RK3399_CLK_PLL_MODE_SHIFT; if (mode == RK3399_CLK_PLL_MODE_SLOW) { dprintf("pll in slow mode, con4=%x\n", con4); return (0); } else if (mode == RK3399_CLK_PLL_MODE_DEEPSLOW) { dprintf("pll in deep slow, con4=%x\n", con4); *freq = 32768; return (0); } dprintf("con0: %x\n", con1); dprintf("con1: %x\n", con2); dprintf("con2: %x\n", con3); dprintf("con3: %x\n", con4); fbdiv = (con1 & RK3399_CLK_PLL_FBDIV_MASK) >> RK3399_CLK_PLL_FBDIV_SHIFT; postdiv1 = (con2 & RK3399_CLK_PLL_POSTDIV1_MASK) >> RK3399_CLK_PLL_POSTDIV1_SHIFT; postdiv2 = (con2 & RK3399_CLK_PLL_POSTDIV2_MASK) >> RK3399_CLK_PLL_POSTDIV2_SHIFT; refdiv = (con2 & RK3399_CLK_PLL_REFDIV_MASK) >> RK3399_CLK_PLL_REFDIV_SHIFT; fracdiv = (con3 & RK3399_CLK_PLL_FRAC_MASK) >> RK3399_CLK_PLL_FRAC_SHIFT; fracdiv >>= 24; dsmpd = (con4 & RK3399_CLK_PLL_DSMPD_MASK) >> RK3399_CLK_PLL_DSMPD_SHIFT; dprintf("fbdiv: %d\n", fbdiv); dprintf("postdiv1: %d\n", postdiv1); dprintf("postdiv2: %d\n", postdiv2); dprintf("refdiv: %d\n", refdiv); dprintf("fracdiv: %d\n", fracdiv); dprintf("dsmpd: %d\n", dsmpd); dprintf("parent freq=%ju\n", *freq); if (dsmpd == 0) { /* Fractional mode */ foutvco = *freq / refdiv * (fbdiv + fracdiv); } else { /* Integer mode */ foutvco = *freq / refdiv * fbdiv; } dprintf("foutvco: %ju\n", foutvco); *freq = foutvco / postdiv1 / postdiv2; dprintf("freq: %ju\n", *freq); return (0); } static int rk3399_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, int flags, int *stop) { struct rk_clk_pll_rate *rates; struct rk_clk_pll_sc *sc; uint32_t reg; int timeout; sc = clknode_get_softc(clk); if (sc->rates) rates = sc->rates; else if (sc->frac_rates) rates = sc->frac_rates; else return (EINVAL); for (; rates->freq; rates++) { if (rates->freq == *fout) break; } if (rates->freq == 0) { *stop = 1; return (EINVAL); } DEVICE_LOCK(clk); /* Set to slow mode during frequency change */ reg = RK3399_CLK_PLL_MODE_SLOW << RK3399_CLK_PLL_MODE_SHIFT; reg |= RK3399_CLK_PLL_MODE_MASK << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset + 0xC, reg); /* Setting fbdiv */ reg = rates->fbdiv << RK3399_CLK_PLL_FBDIV_SHIFT; reg |= RK3399_CLK_PLL_FBDIV_MASK << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset, reg); /* Setting postdiv1, postdiv2 and refdiv */ reg = rates->postdiv1 << RK3399_CLK_PLL_POSTDIV1_SHIFT; reg |= rates->postdiv2 << RK3399_CLK_PLL_POSTDIV2_SHIFT; reg |= rates->refdiv << RK3399_CLK_PLL_REFDIV_SHIFT; reg |= (RK3399_CLK_PLL_POSTDIV1_MASK | RK3399_CLK_PLL_POSTDIV2_MASK | RK3399_CLK_PLL_REFDIV_MASK) << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset + 0x4, reg); /* Setting frac */ READ4(clk, sc->base_offset + 0x8, ®); reg &= ~RK3399_CLK_PLL_FRAC_MASK; reg |= rates->frac << RK3399_CLK_PLL_FRAC_SHIFT; WRITE4(clk, sc->base_offset + 0x8, reg | RK3399_CLK_PLL_WRITE_MASK); /* Set dsmpd */ reg = rates->dsmpd << RK3399_CLK_PLL_DSMPD_SHIFT; reg |= RK3399_CLK_PLL_DSMPD_MASK << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset + 0xC, reg); /* Reading lock */ for (timeout = 1000; timeout; timeout--) { READ4(clk, sc->base_offset + RK3399_CLK_PLL_LOCK_OFFSET, ®); if ((reg & RK3399_CLK_PLL_LOCK_MASK) == 0) break; DELAY(1); } /* Set back to normal mode */ reg = RK3399_CLK_PLL_MODE_NORMAL << RK3399_CLK_PLL_MODE_SHIFT; reg |= RK3399_CLK_PLL_MODE_MASK << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset + 0xC, reg); DEVICE_UNLOCK(clk); *stop = 1; return (0); } static clknode_method_t rk3399_clk_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, rk3399_clk_pll_init), CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, rk3399_clk_pll_recalc), CLKNODEMETHOD(clknode_set_freq, rk3399_clk_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(rk3399_clk_pll_clknode, rk3399_clk_pll_clknode_class, rk3399_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class); int rk3399_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef) { struct clknode *clk; struct rk_clk_pll_sc *sc; clk = clknode_create(clkdom, &rk3399_clk_pll_clknode_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->base_offset = clkdef->base_offset; sc->gate_offset = clkdef->gate_offset; sc->gate_shift = clkdef->gate_shift; sc->flags = clkdef->flags; sc->rates = clkdef->rates; sc->frac_rates = clkdef->frac_rates; clknode_register(clkdom, clk); return (0); } Index: head/sys/arm64/rockchip/clk/rk_clk_pll.h =================================================================== --- head/sys/arm64/rockchip/clk/rk_clk_pll.h (revision 368339) +++ head/sys/arm64/rockchip/clk/rk_clk_pll.h (revision 368340) @@ -1,66 +1,68 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2018 Emmanuel Vadot <manu@FreeBSD.org> * * 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 _RK_CLK_PLL_H_ #define _RK_CLK_PLL_H_ #include <dev/extres/clk/clk.h> struct rk_clk_pll_rate { uint32_t freq; uint32_t refdiv; uint32_t fbdiv; uint32_t postdiv1; uint32_t postdiv2; uint32_t dsmpd; uint32_t frac; + uint32_t bwadj; }; struct rk_clk_pll_def { struct clknode_init_def clkdef; uint32_t base_offset; uint32_t gate_offset; uint32_t gate_shift; uint32_t mode_reg; uint32_t mode_shift; uint32_t flags; struct rk_clk_pll_rate *rates; struct rk_clk_pll_rate *frac_rates; }; #define RK_CLK_PLL_HAVE_GATE 0x1 +int rk3066_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef); int rk3328_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef); int rk3399_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef); #endif /* _RK_CLK_PLL_H_ */ Index: head/sys/arm64/rockchip/clk/rk_cru.c =================================================================== --- head/sys/arm64/rockchip/clk/rk_cru.c (revision 368339) +++ head/sys/arm64/rockchip/clk/rk_cru.c (revision 368340) @@ -1,306 +1,310 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org> * * 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$ */ /* * RockChip Clock and Reset Unit */ #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/lock.h> #include <sys/module.h> #include <sys/mutex.h> #include <machine/bus.h> #include <dev/fdt/simplebus.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> #include <dev/extres/clk/clk.h> #include <dev/extres/clk/clk_gate.h> #include <dev/extres/clk/clk_fixed.h> #include <dev/extres/clk/clk_link.h> #include <dev/extres/hwreset/hwreset.h> #include <arm64/rockchip/clk/rk_clk_composite.h> #include <arm64/rockchip/clk/rk_clk_gate.h> #include <arm64/rockchip/clk/rk_clk_mux.h> #include <arm64/rockchip/clk/rk_clk_pll.h> #include <arm64/rockchip/clk/rk_cru.h> #include "clkdev_if.h" #include "hwreset_if.h" static struct resource_spec rk_cru_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; #define CCU_READ4(sc, reg) bus_read_4((sc)->res, (reg)) #define CCU_WRITE4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) void rk3328_cru_register_clocks(struct rk_cru_softc *sc); static int rk_cru_write_4(device_t dev, bus_addr_t addr, uint32_t val) { struct rk_cru_softc *sc; sc = device_get_softc(dev); CCU_WRITE4(sc, addr, val); return (0); } static int rk_cru_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { struct rk_cru_softc *sc; sc = device_get_softc(dev); *val = CCU_READ4(sc, addr); return (0); } static int rk_cru_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) { struct rk_cru_softc *sc; uint32_t reg; sc = device_get_softc(dev); reg = CCU_READ4(sc, addr); reg &= ~clr; reg |= set; CCU_WRITE4(sc, addr, reg); return (0); } static int rk_cru_reset_assert(device_t dev, intptr_t id, bool reset) { struct rk_cru_softc *sc; uint32_t reg; int bit; uint32_t val; sc = device_get_softc(dev); if (id > sc->reset_num) return (ENXIO); reg = sc->reset_offset + id / 16 * 4; bit = id % 16; mtx_lock(&sc->mtx); val = 0; if (reset) val = (1 << bit); CCU_WRITE4(sc, reg, val | ((1 << bit) << 16)); mtx_unlock(&sc->mtx); return (0); } static int rk_cru_reset_is_asserted(device_t dev, intptr_t id, bool *reset) { struct rk_cru_softc *sc; uint32_t reg; int bit; uint32_t val; sc = device_get_softc(dev); if (id > sc->reset_num) return (ENXIO); reg = sc->reset_offset + id / 16 * 4; bit = id % 16; mtx_lock(&sc->mtx); val = CCU_READ4(sc, reg); mtx_unlock(&sc->mtx); *reset = false; if (val & (1 << bit)) *reset = true; return (0); } static void rk_cru_device_lock(device_t dev) { struct rk_cru_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->mtx); } static void rk_cru_device_unlock(device_t dev) { struct rk_cru_softc *sc; sc = device_get_softc(dev); mtx_unlock(&sc->mtx); } static int rk_cru_register_gates(struct rk_cru_softc *sc) { struct rk_clk_gate_def def; int i; for (i = 0; i < sc->ngates; i++) { if (sc->gates[i].name == NULL) continue; memset(&def, 0, sizeof(def)); def.clkdef.id = sc->gates[i].id; def.clkdef.name = sc->gates[i].name; def.clkdef.parent_names = &sc->gates[i].parent_name; def.clkdef.parent_cnt = 1; def.offset = sc->gates[i].offset; def.shift = sc->gates[i].shift; def.mask = 1; def.on_value = 0; def.off_value = 1; rk_clk_gate_register(sc->clkdom, &def); } return (0); } int rk_cru_attach(device_t dev) { struct rk_cru_softc *sc; phandle_t node; int i; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); if (bus_alloc_resources(dev, rk_cru_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); sc->clkdom = clkdom_create(dev); if (sc->clkdom == NULL) panic("Cannot create clkdom\n"); for (i = 0; i < sc->nclks; i++) { switch (sc->clks[i].type) { case RK_CLK_UNDEFINED: break; + case RK3066_CLK_PLL: + rk3066_clk_pll_register(sc->clkdom, + sc->clks[i].clk.pll); + break; case RK3328_CLK_PLL: rk3328_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll); break; case RK3399_CLK_PLL: rk3399_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll); break; case RK_CLK_COMPOSITE: rk_clk_composite_register(sc->clkdom, sc->clks[i].clk.composite); break; case RK_CLK_MUX: rk_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux); break; case RK_CLK_ARMCLK: rk_clk_armclk_register(sc->clkdom, sc->clks[i].clk.armclk); break; case RK_CLK_FIXED: clknode_fixed_register(sc->clkdom, sc->clks[i].clk.fixed); break; case RK_CLK_FRACT: rk_clk_fract_register(sc->clkdom, sc->clks[i].clk.fract); break; case RK_CLK_LINK: clknode_link_register(sc->clkdom, sc->clks[i].clk.link); break; default: device_printf(dev, "Unknown clock type\n"); return (ENXIO); } } if (sc->gates) rk_cru_register_gates(sc); if (clkdom_finit(sc->clkdom) != 0) panic("cannot finalize clkdom initialization\n"); if (bootverbose) clkdom_dump(sc->clkdom); clk_set_assigned(dev, node); /* register our self as a reset provider */ hwreset_register_ofw_provider(dev); return (0); } static device_method_t rk_cru_methods[] = { /* clkdev interface */ DEVMETHOD(clkdev_write_4, rk_cru_write_4), DEVMETHOD(clkdev_read_4, rk_cru_read_4), DEVMETHOD(clkdev_modify_4, rk_cru_modify_4), DEVMETHOD(clkdev_device_lock, rk_cru_device_lock), DEVMETHOD(clkdev_device_unlock, rk_cru_device_unlock), /* Reset interface */ DEVMETHOD(hwreset_assert, rk_cru_reset_assert), DEVMETHOD(hwreset_is_asserted, rk_cru_reset_is_asserted), DEVMETHOD_END }; DEFINE_CLASS_0(rk_cru, rk_cru_driver, rk_cru_methods, sizeof(struct rk_cru_softc)); Index: head/sys/arm64/rockchip/clk/rk_cru.h =================================================================== --- head/sys/arm64/rockchip/clk/rk_cru.h (revision 368339) +++ head/sys/arm64/rockchip/clk/rk_cru.h (revision 368340) @@ -1,252 +1,253 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org> * * 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$ */ #ifndef __RK_CRU_H__ #define __RK_CRU_H__ #include <dev/extres/clk/clk.h> #include <dev/extres/clk/clk_div.h> #include <dev/extres/clk/clk_gate.h> #include <dev/extres/clk/clk_fixed.h> #include <dev/extres/clk/clk_link.h> #include <arm64/rockchip/clk/rk_clk_armclk.h> #include <arm64/rockchip/clk/rk_clk_composite.h> #include <arm64/rockchip/clk/rk_clk_fract.h> #include <arm64/rockchip/clk/rk_clk_gate.h> #include <arm64/rockchip/clk/rk_clk_mux.h> #include <arm64/rockchip/clk/rk_clk_pll.h> /* Macro for defining various types of clocks. */ /* Pure gate */ #define GATE(_idx, _clkname, _pname, _o, _s) \ { \ .id = _idx, \ .name = _clkname, \ .parent_name = _pname, \ .offset = CRU_CLKGATE_CON(_o), \ .shift = _s, \ } /* Fixed rate clock. */ #define FRATE(_id, _name, _freq) \ { \ .type = RK_CLK_FIXED, \ .clk.fixed = &(struct clk_fixed_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = NULL, \ .clkdef.parent_cnt = 0, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .freq = _freq, \ }, \ } /* Fixed factor multipier/divider. */ #define FFACT(_id, _name, _pname, _mult, _div) \ { \ .type = RK_CLK_FIXED, \ .clk.fixed = &(struct clk_fixed_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = (const char *[]){_pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .mult = _mult, \ .div = _div, \ }, \ } /* Linked clock. */ #define LINK(_name) \ { \ .type = RK_CLK_LINK, \ .clk.link = &(struct clk_link_def) { \ .clkdef.id = 0, \ .clkdef.name = _name, \ .clkdef.parent_names = NULL, \ .clkdef.parent_cnt = 0, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ }, \ } /* Complex clock fo ARM cores. */ #define ARMDIV(_id, _name, _pn, _r, _o, _ds, _dw, _ms, _mw, _mp, _ap) \ { \ .type = RK_CLK_ARMCLK, \ .clk.armclk = &(struct rk_clk_armclk_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = _pn, \ .clkdef.parent_cnt = nitems(_pn), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .muxdiv_offset = CRU_CLKSEL_CON(_o), \ .mux_shift = _ms, \ .mux_width = _mw, \ .div_shift = _ds, \ .div_width = _dw, \ .main_parent = _mp, \ .alt_parent = _ap, \ .rates = _r, \ .nrates = nitems(_r), \ }, \ } /* Fractional rate multipier/divider. */ #define FRACT(_id, _name, _pname, _f, _o) \ { \ .type = RK_CLK_FRACT, \ .clk.fract = &(struct rk_clk_fract_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = (const char *[]){_pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = CRU_CLKSEL_CON(_o), \ .flags = _f, \ }, \ } /* Full composite clock. */ #define COMP(_id, _name, _pnames, _f, _o, _ds, _dw, _ms, _mw) \ { \ .type = RK_CLK_COMPOSITE, \ .clk.composite = &(struct rk_clk_composite_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = _pnames, \ .clkdef.parent_cnt = nitems(_pnames), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .muxdiv_offset = CRU_CLKSEL_CON(_o), \ .mux_shift = _ms, \ .mux_width = _mw, \ .div_shift = _ds, \ .div_width = _dw, \ .flags = RK_CLK_COMPOSITE_HAVE_MUX | _f, \ }, \ } /* Composite clock without mux (divider only). */ #define CDIV(_id, _name, _pname, _f, _o, _ds, _dw) \ { \ .type = RK_CLK_COMPOSITE, \ .clk.composite = &(struct rk_clk_composite_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = (const char *[]){_pname}, \ .clkdef.parent_cnt = 1, \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .muxdiv_offset = CRU_CLKSEL_CON(_o), \ .div_shift = _ds, \ .div_width = _dw, \ .flags = _f, \ }, \ } /* Complex clock without divider (multiplexer only). */ #define MUX(_id, _name, _pn, _f, _mo, _ms, _mw) \ { \ .type = RK_CLK_MUX, \ .clk.mux = &(struct rk_clk_mux_def) { \ .clkdef.id = _id, \ .clkdef.name = _name, \ .clkdef.parent_names = _pn, \ .clkdef.parent_cnt = nitems(_pn), \ .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ .offset = CRU_CLKSEL_CON(_mo), \ .shift = _ms, \ .width = _mw, \ .mux_flags = _f, \ }, \ } struct rk_cru_gate { const char *name; const char *parent_name; uint32_t id; uint32_t offset; uint32_t shift; }; #define CRU_GATE(idx, clkname, pname, o, s) \ { \ .id = idx, \ .name = clkname, \ .parent_name = pname, \ .offset = o, \ .shift = s, \ }, enum rk_clk_type { RK_CLK_UNDEFINED = 0, + RK3066_CLK_PLL, RK3328_CLK_PLL, RK3399_CLK_PLL, RK_CLK_COMPOSITE, RK_CLK_FIXED, RK_CLK_FRACT, RK_CLK_MUX, RK_CLK_ARMCLK, RK_CLK_LINK, }; struct rk_clk { enum rk_clk_type type; union { struct rk_clk_pll_def *pll; struct rk_clk_composite_def *composite; struct rk_clk_mux_def *mux; struct rk_clk_armclk_def *armclk; struct clk_fixed_def *fixed; struct rk_clk_fract_def *fract; struct clk_link_def *link; } clk; }; struct rk_cru_softc { device_t dev; struct resource *res; struct clkdom *clkdom; struct mtx mtx; int type; uint32_t reset_offset; uint32_t reset_num; struct rk_cru_gate *gates; int ngates; struct rk_clk *clks; int nclks; struct rk_clk_armclk_def *armclk; struct rk_clk_armclk_rates *armclk_rates; int narmclk_rates; }; DECLARE_CLASS(rk_cru_driver); int rk_cru_attach(device_t dev); #endif /* __RK_CRU_H__ */ Index: head/sys/arm64/rockchip/rk_pmu.c =================================================================== --- head/sys/arm64/rockchip/rk_pmu.c (nonexistent) +++ head/sys/arm64/rockchip/rk_pmu.c (revision 368340) @@ -0,0 +1,77 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> + * + * 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/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/rman.h> +#include <machine/bus.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/extres/syscon/syscon.h> +#include <dev/fdt/simple_mfd.h> + +static struct ofw_compat_data compat_data[] = { + {"rockchip,rk3288-pmu", 1}, + {NULL, 0} +}; + +static int +rk_pmu_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, "RockChip Power Management Unit"); + return (BUS_PROBE_DEFAULT); +} + +static device_method_t rk_pmu_methods[] = { + DEVMETHOD(device_probe, rk_pmu_probe), + + DEVMETHOD_END +}; + + +DEFINE_CLASS_1(rk_pmu, rk_pmu_driver, rk_pmu_methods, + sizeof(struct simple_mfd_softc), simple_mfd_driver); + +static devclass_t rk_pmu_devclass; +EARLY_DRIVER_MODULE(rk_pmu, simplebus, rk_pmu_driver, rk_pmu_devclass, + 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(rk_pmu, 1); Property changes on: head/sys/arm64/rockchip/rk_pmu.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm64/rockchip/rk_pwm.c =================================================================== --- head/sys/arm64/rockchip/rk_pwm.c (revision 368339) +++ head/sys/arm64/rockchip/rk_pwm.c (revision 368340) @@ -1,403 +1,404 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot <manu@FreeBSD.org> * Copyright (c) 2019 Brandon Bergren <git@bdragon.rtk0.net> * * 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/bus.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/rman.h> #include <sys/resource.h> #include <machine/bus.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> #include <dev/extres/clk/clk.h> #include "pwmbus_if.h" /* Register offsets. */ #define RK_PWM_COUNTER 0x00 #define RK_PWM_PERIOD 0x04 #define RK_PWM_DUTY 0x08 #define RK_PWM_CTRL 0x0c #define SET(reg,mask,val) reg = ((reg & ~mask) | val) #define RK_PWM_CTRL_ENABLE_MASK (1 << 0) #define RK_PWM_CTRL_ENABLED (1 << 0) #define RK_PWM_CTRL_DISABLED (0) #define RK_PWM_CTRL_MODE_MASK (3 << 1) #define RK_PWM_CTRL_MODE_ONESHOT (0) #define RK_PWM_CTRL_MODE_CONTINUOUS (1 << 1) #define RK_PWM_CTRL_MODE_CAPTURE (1 << 2) #define RK_PWM_CTRL_DUTY_MASK (1 << 3) #define RK_PWM_CTRL_DUTY_POSITIVE (1 << 3) #define RK_PWM_CTRL_DUTY_NEGATIVE (0) #define RK_PWM_CTRL_INACTIVE_MASK (1 << 4) #define RK_PWM_CTRL_INACTIVE_POSITIVE (1 << 4) #define RK_PWM_CTRL_INACTIVE_NEGATIVE (0) /* PWM Output Alignment */ #define RK_PWM_CTRL_ALIGN_MASK (1 << 5) #define RK_PWM_CTRL_ALIGN_CENTER (1 << 5) #define RK_PWM_CTRL_ALIGN_LEFT (0) /* Low power mode: disable prescaler when inactive */ #define RK_PWM_CTRL_LP_MASK (1 << 8) #define RK_PWM_CTRL_LP_ENABLE (1 << 8) #define RK_PWM_CTRL_LP_DISABLE (0) /* Clock source: bypass the scaler or not */ #define RK_PWM_CTRL_CLOCKSRC_MASK (1 << 9) #define RK_PWM_CTRL_CLOCKSRC_NONSCALED (0) #define RK_PWM_CTRL_CLOCKSRC_SCALED (1 << 9) #define RK_PWM_CTRL_PRESCALE_MASK (7 << 12) #define RK_PWM_CTRL_PRESCALE_SHIFT 12 #define RK_PWM_CTRL_SCALE_MASK (0xFF << 16) #define RK_PWM_CTRL_SCALE_SHIFT 16 #define RK_PWM_CTRL_REPEAT_MASK (0xFF << 24) #define RK_PWM_CTRL_REPEAT_SHIFT 24 #define NS_PER_SEC 1000000000 static struct ofw_compat_data compat_data[] = { + { "rockchip,rk3288-pwm", 1 }, { "rockchip,rk3399-pwm", 1 }, { NULL, 0 } }; static struct resource_spec rk_pwm_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; struct rk_pwm_softc { device_t dev; device_t busdev; clk_t clk; struct resource *res; uint64_t clk_freq; unsigned int period; unsigned int duty; uint32_t flags; uint8_t prescaler; uint8_t scaler; bool using_scaler; bool enabled; }; #define RK_PWM_READ(sc, reg) bus_read_4((sc)->res, (reg)) #define RK_PWM_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) static int rk_pwm_probe(device_t dev); static int rk_pwm_attach(device_t dev); static int rk_pwm_detach(device_t dev); static int rk_pwm_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, "Rockchip PWM"); return (BUS_PROBE_DEFAULT); } static int rk_pwm_attach(device_t dev) { struct rk_pwm_softc *sc; phandle_t node; uint64_t clk_freq; uint32_t reg; int error; sc = device_get_softc(dev); sc->dev = dev; error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); if (error != 0) { device_printf(dev, "cannot get clock\n"); goto fail; } error = clk_enable(sc->clk); if (error != 0) { device_printf(dev, "cannot enable clock\n"); goto fail; } error = clk_get_freq(sc->clk, &sc->clk_freq); if (error != 0) { device_printf(dev, "cannot get base frequency\n"); goto fail; } if (bus_alloc_resources(dev, rk_pwm_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } /* Read the configuration left by U-Boot */ reg = RK_PWM_READ(sc, RK_PWM_CTRL); if ((reg & RK_PWM_CTRL_ENABLE_MASK) == RK_PWM_CTRL_ENABLED) sc->enabled = true; reg = RK_PWM_READ(sc, RK_PWM_CTRL); reg &= RK_PWM_CTRL_PRESCALE_MASK; sc->prescaler = reg >> RK_PWM_CTRL_PRESCALE_SHIFT; reg = RK_PWM_READ(sc, RK_PWM_CTRL); reg &= RK_PWM_CTRL_SCALE_MASK; sc->scaler = reg >> RK_PWM_CTRL_SCALE_SHIFT; reg = RK_PWM_READ(sc, RK_PWM_CTRL); if ((reg & RK_PWM_CTRL_CLOCKSRC_MASK) == RK_PWM_CTRL_CLOCKSRC_SCALED) sc->using_scaler = true; else sc->using_scaler = false; clk_freq = sc->clk_freq / (2 ^ sc->prescaler); if (sc->using_scaler) { if (sc->scaler == 0) clk_freq /= 512; else clk_freq /= (sc->scaler * 2); } reg = RK_PWM_READ(sc, RK_PWM_PERIOD); sc->period = NS_PER_SEC / (clk_freq / reg); reg = RK_PWM_READ(sc, RK_PWM_DUTY); sc->duty = NS_PER_SEC / (clk_freq / reg); node = ofw_bus_get_node(dev); OF_device_register_xref(OF_xref_from_node(node), dev); sc->busdev = device_add_child(dev, "pwmbus", -1); return (bus_generic_attach(dev)); fail: rk_pwm_detach(dev); return (error); } static int rk_pwm_detach(device_t dev) { struct rk_pwm_softc *sc; sc = device_get_softc(dev); bus_generic_detach(sc->dev); bus_release_resources(dev, rk_pwm_spec, &sc->res); return (0); } static phandle_t aw_pwm_get_node(device_t bus, device_t dev) { /* * Share our controller node with our pwmbus child; it instantiates * devices by walking the children contained within our node. */ return ofw_bus_get_node(bus); } static int rk_pwm_channel_count(device_t dev, u_int *nchannel) { /* The device supports 4 channels, but attaches multiple times in the * device tree. This interferes with advanced usage though, as * the interrupt capability and channel 3 FIFO register offsets * don't work right in this situation. * But since we don't support those yet, pretend we are singlechannel. */ *nchannel = 1; return (0); } static int rk_pwm_channel_config(device_t dev, u_int channel, u_int period, u_int duty) { struct rk_pwm_softc *sc; uint64_t period_freq, duty_freq; uint32_t reg; uint32_t period_out; uint32_t duty_out; uint8_t prescaler; uint8_t scaler; bool using_scaler; sc = device_get_softc(dev); period_freq = NS_PER_SEC / period; /* Datasheet doesn't define, so use Nyquist frequency. */ if (period_freq > (sc->clk_freq / 2)) return (EINVAL); duty_freq = NS_PER_SEC / duty; if (duty_freq < period_freq) { device_printf(sc->dev, "duty < period\n"); return (EINVAL); } /* Assuming 24 MHz reference, we should never actually have to use the divider due to pwm API limitations. */ prescaler = 0; scaler = 0; using_scaler = false; /* XXX Expand API to allow for 64 bit period/duty. */ period_out = (sc->clk_freq * period) / NS_PER_SEC; duty_out = (sc->clk_freq * duty) / NS_PER_SEC; reg = RK_PWM_READ(sc, RK_PWM_CTRL); if ((reg & RK_PWM_CTRL_MODE_MASK) != RK_PWM_CTRL_MODE_CONTINUOUS) { /* Switching modes, disable just in case. */ SET(reg, RK_PWM_CTRL_ENABLE_MASK, RK_PWM_CTRL_DISABLED); RK_PWM_WRITE(sc, RK_PWM_CTRL, reg); } RK_PWM_WRITE(sc, RK_PWM_PERIOD, period_out); RK_PWM_WRITE(sc, RK_PWM_DUTY, duty_out); SET(reg, RK_PWM_CTRL_ENABLE_MASK, RK_PWM_CTRL_ENABLED); SET(reg, RK_PWM_CTRL_MODE_MASK, RK_PWM_CTRL_MODE_CONTINUOUS); SET(reg, RK_PWM_CTRL_ALIGN_MASK, RK_PWM_CTRL_ALIGN_LEFT); SET(reg, RK_PWM_CTRL_CLOCKSRC_MASK, using_scaler); SET(reg, RK_PWM_CTRL_PRESCALE_MASK, prescaler << RK_PWM_CTRL_PRESCALE_SHIFT); SET(reg, RK_PWM_CTRL_SCALE_MASK, scaler << RK_PWM_CTRL_SCALE_SHIFT); RK_PWM_WRITE(sc, RK_PWM_CTRL, reg); sc->period = period; sc->duty = duty; return (0); } static int rk_pwm_channel_get_config(device_t dev, u_int channel, u_int *period, u_int *duty) { struct rk_pwm_softc *sc; sc = device_get_softc(dev); *period = sc->period; *duty = sc->duty; return (0); } static int rk_pwm_channel_enable(device_t dev, u_int channel, bool enable) { struct rk_pwm_softc *sc; uint32_t reg; sc = device_get_softc(dev); if (enable && sc->enabled) return (0); reg = RK_PWM_READ(sc, RK_PWM_CTRL); SET(reg, RK_PWM_CTRL_ENABLE_MASK, enable); RK_PWM_WRITE(sc, RK_PWM_CTRL, reg); sc->enabled = enable; return (0); } static int rk_pwm_channel_is_enabled(device_t dev, u_int channel, bool *enabled) { struct rk_pwm_softc *sc; sc = device_get_softc(dev); *enabled = sc->enabled; return (0); } static device_method_t rk_pwm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_pwm_probe), DEVMETHOD(device_attach, rk_pwm_attach), DEVMETHOD(device_detach, rk_pwm_detach), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, aw_pwm_get_node), /* pwm interface */ DEVMETHOD(pwmbus_channel_count, rk_pwm_channel_count), DEVMETHOD(pwmbus_channel_config, rk_pwm_channel_config), DEVMETHOD(pwmbus_channel_get_config, rk_pwm_channel_get_config), DEVMETHOD(pwmbus_channel_enable, rk_pwm_channel_enable), DEVMETHOD(pwmbus_channel_is_enabled, rk_pwm_channel_is_enabled), DEVMETHOD_END }; static driver_t rk_pwm_driver = { "pwm", rk_pwm_methods, sizeof(struct rk_pwm_softc), }; static devclass_t rk_pwm_devclass; DRIVER_MODULE(rk_pwm, simplebus, rk_pwm_driver, rk_pwm_devclass, 0, 0); SIMPLEBUS_PNP_INFO(compat_data); Index: head/sys/arm64/rockchip/rk_tsadc.c =================================================================== --- head/sys/arm64/rockchip/rk_tsadc.c (revision 368339) +++ head/sys/arm64/rockchip/rk_tsadc.c (revision 368340) @@ -1,792 +1,793 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> * * 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$"); /* * Thermometer and thermal zones driver for RockChip SoCs. * Calibration data are taken from Linux, because this part of SoC * is undocumented in TRM. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/gpio.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/malloc.h> #include <sys/rman.h> #include <sys/sysctl.h> #include <machine/bus.h> #include <dev/extres/clk/clk.h> #include <dev/extres/hwreset/hwreset.h> #include <dev/extres/syscon/syscon.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> #include "syscon_if.h" #include "rk_tsadc_if.h" /* Global registers */ #define TSADC_USER_CON 0x000 #define TSADC_AUTO_CON 0x004 #define TSADC_AUTO_CON_POL_HI (1 << 8) #define TSADC_AUTO_SRC_EN(x) (1 << (4 + (x))) #define TSADC_AUTO_Q_SEL (1 << 1) /* V3 only */ #define TSADC_AUTO_CON_AUTO (1 << 0) #define TSADC_INT_EN 0x008 #define TSADC_INT_EN_2CRU_EN_SRC(x) (1 << (8 + (x))) #define TSADC_INT_EN_2GPIO_EN_SRC(x) (1 << (4 + (x))) #define TSADC_INT_PD 0x00c #define TSADC_DATA(x) (0x20 + (x) * 0x04) #define TSADC_COMP_INT(x) (0x30 + (x) * 0x04) #define TSADC_COMP_INT_SRC_EN(x) (1 << (0 + (x))) #define TSADC_COMP_SHUT(x) (0x40 + (x) * 0x04) #define TSADC_HIGHT_INT_DEBOUNCE 0x060 #define TSADC_HIGHT_TSHUT_DEBOUNCE 0x064 #define TSADC_AUTO_PERIOD 0x068 #define TSADC_AUTO_PERIOD_HT 0x06c #define TSADC_COMP0_LOW_INT 0x080 /* V3 only */ #define TSADC_COMP1_LOW_INT 0x084 /* V3 only */ /* GFR Bits */ #define GRF_SARADC_TESTBIT 0x0e644 #define GRF_SARADC_TESTBIT_ON (0x10001 << 2) #define GRF_TSADC_TESTBIT_L 0x0e648 #define GRF_TSADC_VCM_EN_L (0x10001 << 7) #define GRF_TSADC_TESTBIT_H 0x0e64c #define GRF_TSADC_VCM_EN_H (0x10001 << 7) #define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2) #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) static struct sysctl_ctx_list tsadc_sysctl_ctx; struct tsensor { char *name; int id; int channel; }; struct rk_calib_entry { uint32_t raw; int temp; }; struct tsadc_calib_info { struct rk_calib_entry *table; int nentries; }; struct tsadc_conf { int use_syscon; int q_sel_ntc; int shutdown_temp; int shutdown_mode; int shutdown_pol; struct tsensor *tsensors; int ntsensors; struct tsadc_calib_info calib_info; }; struct tsadc_softc { device_t dev; struct resource *mem_res; struct resource *irq_res; void *irq_ih; clk_t tsadc_clk; clk_t apb_pclk_clk; hwreset_t hwreset; struct syscon *grf; struct tsadc_conf *conf; int shutdown_temp; int shutdown_mode; int shutdown_pol; int alarm_temp; }; static struct rk_calib_entry rk3288_calib_data[] = { {3800, -40000}, {3792, -35000}, {3783, -30000}, {3774, -25000}, {3765, -20000}, {3756, -15000}, {3747, -10000}, {3737, -5000}, {3728, 0}, {3718, 5000}, {3708, 10000}, {3698, 15000}, {3688, 20000}, {3678, 25000}, {3667, 30000}, {3656, 35000}, {3645, 40000}, {3634, 45000}, {3623, 50000}, {3611, 55000}, {3600, 60000}, {3588, 65000}, {3575, 70000}, {3563, 75000}, {3550, 80000}, {3537, 85000}, {3524, 90000}, {3510, 95000}, {3496, 100000}, {3482, 105000}, {3467, 110000}, {3452, 115000}, {3437, 120000}, {3421, 125000}, }; struct tsensor rk3288_tsensors[] = { { .channel = 0, .id = 2, .name = "reserved"}, { .channel = 1, .id = 0, .name = "CPU"}, { .channel = 2, .id = 1, .name = "GPU"}, }; struct tsadc_conf rk3288_tsadc_conf = { .use_syscon = 0, .q_sel_ntc = 0, .shutdown_temp = 95000, .shutdown_mode = 1, /* GPIO */ .shutdown_pol = 0, /* Low */ .tsensors = rk3288_tsensors, .ntsensors = nitems(rk3288_tsensors), .calib_info = { .table = rk3288_calib_data, .nentries = nitems(rk3288_calib_data), } }; static struct rk_calib_entry rk3328_calib_data[] = { {296, -40000}, {304, -35000}, {313, -30000}, {331, -20000}, {340, -15000}, {349, -10000}, {359, -5000}, {368, 0}, {378, 5000}, {388, 10000}, {398, 15000}, {408, 20000}, {418, 25000}, {429, 30000}, {440, 35000}, {451, 40000}, {462, 45000}, {473, 50000}, {485, 55000}, {496, 60000}, {508, 65000}, {521, 70000}, {533, 75000}, {546, 80000}, {559, 85000}, {572, 90000}, {586, 95000}, {600, 100000}, {614, 105000}, {629, 110000}, {644, 115000}, {659, 120000}, {675, 125000}, }; static struct tsensor rk3328_tsensors[] = { { .channel = 0, .id = 0, .name = "CPU"}, }; static struct tsadc_conf rk3328_tsadc_conf = { .use_syscon = 0, .q_sel_ntc = 1, .shutdown_temp = 95000, .shutdown_mode = 0, /* CRU */ .shutdown_pol = 0, /* Low */ .tsensors = rk3328_tsensors, .ntsensors = nitems(rk3328_tsensors), .calib_info = { .table = rk3328_calib_data, .nentries = nitems(rk3328_calib_data), } }; static struct rk_calib_entry rk3399_calib_data[] = { {402, -40000}, {410, -35000}, {419, -30000}, {427, -25000}, {436, -20000}, {444, -15000}, {453, -10000}, {461, -5000}, {470, 0}, {478, 5000}, {487, 10000}, {496, 15000}, {504, 20000}, {513, 25000}, {521, 30000}, {530, 35000}, {538, 40000}, {547, 45000}, {555, 50000}, {564, 55000}, {573, 60000}, {581, 65000}, {590, 70000}, {599, 75000}, {607, 80000}, {616, 85000}, {624, 90000}, {633, 95000}, {642, 100000}, {650, 105000}, {659, 110000}, {668, 115000}, {677, 120000}, {685, 125000}, }; static struct tsensor rk3399_tsensors[] = { { .channel = 0, .id = 0, .name = "CPU"}, { .channel = 1, .id = 1, .name = "GPU"}, }; static struct tsadc_conf rk3399_tsadc_conf = { .use_syscon = 1, .q_sel_ntc = 1, .shutdown_temp = 95000, .shutdown_mode = 1, /* GPIO */ .shutdown_pol = 0, /* Low */ .tsensors = rk3399_tsensors, .ntsensors = nitems(rk3399_tsensors), .calib_info = { .table = rk3399_calib_data, .nentries = nitems(rk3399_calib_data), } }; static struct ofw_compat_data compat_data[] = { {"rockchip,rk3288-tsadc", (uintptr_t)&rk3288_tsadc_conf}, {"rockchip,rk3328-tsadc", (uintptr_t)&rk3328_tsadc_conf}, {"rockchip,rk3399-tsadc", (uintptr_t)&rk3399_tsadc_conf}, {NULL, 0} }; static uint32_t tsadc_temp_to_raw(struct tsadc_softc *sc, int temp) { struct rk_calib_entry *tbl; int denom, ntbl, raw, i; tbl = sc->conf->calib_info.table; ntbl = sc->conf->calib_info.nentries; if (temp <= tbl[0].temp) return (tbl[0].raw); if (temp >= tbl[ntbl - 1].temp) return (tbl[ntbl - 1].raw); for (i = 1; i < (ntbl - 1); i++) { /* Exact match */ if (temp == tbl[i].temp) return (tbl[i].raw); if (temp < tbl[i].temp) break; } /* * Translated value is between i and i - 1 table entries. * Do linear interpolation for it. */ raw = (int)tbl[i - 1].raw - (int)tbl[i].raw; raw *= temp - tbl[i - 1].temp; denom = tbl[i - 1].temp - tbl[i].temp; raw = tbl[i - 1].raw + raw / denom; return (raw); } static int tsadc_raw_to_temp(struct tsadc_softc *sc, uint32_t raw) { struct rk_calib_entry *tbl; int denom, ntbl, temp, i; bool descending; tbl = sc->conf->calib_info.table; ntbl = sc->conf->calib_info.nentries; descending = tbl[0].raw > tbl[1].raw; if (descending) { /* Raw column is in descending order. */ if (raw >= tbl[0].raw) return (tbl[0].temp); if (raw <= tbl[ntbl - 1].raw) return (tbl[ntbl - 1].temp); for (i = ntbl - 2; i > 0; i--) { /* Exact match */ if (raw == tbl[i].raw) return (tbl[i].temp); if (raw < tbl[i].raw) break; } } else { /* Raw column is in ascending order. */ if (raw <= tbl[0].raw) return (tbl[0].temp); if (raw >= tbl[ntbl - 1].raw) return (tbl[ntbl - 1].temp); for (i = 1; i < (ntbl - 1); i++) { /* Exact match */ if (raw == tbl[i].raw) return (tbl[i].temp); if (raw < tbl[i].raw) break; } } /* * Translated value is between i and i - 1 table entries. * Do linear interpolation for it. */ temp = (int)tbl[i - 1].temp - (int)tbl[i].temp; temp *= raw - tbl[i - 1].raw; denom = tbl[i - 1].raw - tbl[i].raw; temp = tbl[i - 1].temp + temp / denom; return (temp); } static void tsadc_init_tsensor(struct tsadc_softc *sc, struct tsensor *sensor) { uint32_t val; /* Shutdown mode */ val = RD4(sc, TSADC_INT_EN); if (sc->shutdown_mode != 0) { /* Signal shutdown of GPIO pin */ val &= ~TSADC_INT_EN_2CRU_EN_SRC(sensor->channel); val |= TSADC_INT_EN_2GPIO_EN_SRC(sensor->channel); } else { val |= TSADC_INT_EN_2CRU_EN_SRC(sensor->channel); val &= ~TSADC_INT_EN_2GPIO_EN_SRC(sensor->channel); } WR4(sc, TSADC_INT_EN, val); /* Shutdown temperature */ val = tsadc_raw_to_temp(sc, sc->shutdown_temp); WR4(sc, TSADC_COMP_SHUT(sensor->channel), val); val = RD4(sc, TSADC_AUTO_CON); val |= TSADC_AUTO_SRC_EN(sensor->channel); WR4(sc, TSADC_AUTO_CON, val); /* Alarm temperature */ val = tsadc_temp_to_raw(sc, sc->alarm_temp); WR4(sc, TSADC_COMP_INT(sensor->channel), val); val = RD4(sc, TSADC_INT_EN); val |= TSADC_COMP_INT_SRC_EN(sensor->channel); WR4(sc, TSADC_INT_EN, val); } static void tsadc_init(struct tsadc_softc *sc) { uint32_t val; /* Common part */ val = 0; /* XXX Is this right? */ if (sc->shutdown_pol != 0) val |= TSADC_AUTO_CON_POL_HI; else val &= ~TSADC_AUTO_CON_POL_HI; if (sc->conf->q_sel_ntc) val |= TSADC_AUTO_Q_SEL; WR4(sc, TSADC_AUTO_CON, val); if (!sc->conf->use_syscon) { /* V2 init */ WR4(sc, TSADC_AUTO_PERIOD, 250); /* 250 ms */ WR4(sc, TSADC_AUTO_PERIOD_HT, 50); /* 50 ms */ WR4(sc, TSADC_HIGHT_INT_DEBOUNCE, 4); WR4(sc, TSADC_HIGHT_TSHUT_DEBOUNCE, 4); } else { /* V3 init */ if (sc->grf == NULL) { /* Errata: adjust interleave to working value */ WR4(sc, TSADC_USER_CON, 13 << 6); /* 13 clks */ } else { SYSCON_WRITE_4(sc->grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L); SYSCON_WRITE_4(sc->grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H); DELAY(30); /* 15 usec min */ SYSCON_WRITE_4(sc->grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON); SYSCON_WRITE_4(sc->grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON); DELAY(180); /* 90 usec min */ } WR4(sc, TSADC_AUTO_PERIOD, 1875); /* 2.5 ms */ WR4(sc, TSADC_AUTO_PERIOD_HT, 1875); /* 2.5 ms */ WR4(sc, TSADC_HIGHT_INT_DEBOUNCE, 4); WR4(sc, TSADC_HIGHT_TSHUT_DEBOUNCE, 4); } } static int tsadc_read_temp(struct tsadc_softc *sc, struct tsensor *sensor, int *temp) { uint32_t val; val = RD4(sc, TSADC_DATA(sensor->channel)); *temp = tsadc_raw_to_temp(sc, val); #ifdef DEBUG printf("%s: Sensor(id: %d, ch: %d), temp: %d\n", __func__, sensor->id, sensor->channel, *temp); printf(" status: 0x%08X, 0x%08X\n", RD4(sc, TSADC_USER_CON), RD4(sc, TSADC_AUTO_CON)); printf(" Data: 0x%08X, 0x%08X, 0x%08X\n", RD4(sc, TSADC_DATA(sensor->channel)), RD4(sc, TSADC_COMP_INT(sensor->channel)), RD4(sc, TSADC_COMP_SHUT(sensor->channel))); #endif return (0); } static int tsadc_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val) { struct tsadc_softc *sc; int i, rv; sc = device_get_softc(dev); if (id >= sc->conf->ntsensors) return (ERANGE); for (i = 0; i < sc->conf->ntsensors; i++) { if (sc->conf->tsensors->id == id) { rv =tsadc_read_temp(sc, sc->conf->tsensors + id, val); return (rv); } } return (ERANGE); } static int tsadc_sysctl_temperature(SYSCTL_HANDLER_ARGS) { struct tsadc_softc *sc; int val; int rv; int id; /* Write request */ if (req->newptr != NULL) return (EINVAL); sc = arg1; id = arg2; if (id >= sc->conf->ntsensors) return (ERANGE); rv = tsadc_read_temp(sc, sc->conf->tsensors + id, &val); if (rv != 0) return (rv); val = val / 100; val += 2731; rv = sysctl_handle_int(oidp, &val, 0, req); return (rv); } static int tsadc_init_sysctl(struct tsadc_softc *sc) { int i; struct sysctl_oid *oid, *tmp; sysctl_ctx_init(&tsadc_sysctl_ctx); /* create node for hw.temp */ oid = SYSCTL_ADD_NODE(&tsadc_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, ""); if (oid == NULL) return (ENXIO); /* Add sensors */ for (i = sc->conf->ntsensors - 1; i >= 0; i--) { tmp = SYSCTL_ADD_PROC(&tsadc_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, sc->conf->tsensors[i].name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i, tsadc_sysctl_temperature, "IK", "SoC Temperature"); if (tmp == NULL) return (ENXIO); } return (0); } static int tsadc_intr(void *arg) { struct tsadc_softc *sc; uint32_t val; sc = (struct tsadc_softc *)arg; val = RD4(sc, TSADC_INT_PD); WR4(sc, TSADC_INT_PD, val); /* XXX Handle shutdown and alarm interrupts. */ if (val & 0x00F0) { device_printf(sc->dev, "Alarm: device temperature " "is above of shutdown level.\n"); } else if (val & 0x000F) { device_printf(sc->dev, "Alarm: device temperature " "is above of alarm level.\n"); } return (FILTER_HANDLED); } static int tsadc_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, "RockChip temperature sensors"); return (BUS_PROBE_DEFAULT); } static int tsadc_attach(device_t dev) { struct tsadc_softc *sc; phandle_t node; uint32_t val; int i, rid, rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(sc->dev); sc->conf = (struct tsadc_conf *) ofw_bus_search_compatible(dev, compat_data)->ocd_data; sc->alarm_temp = 90000; rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); goto fail; } if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, tsadc_intr, NULL, sc, &sc->irq_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); goto fail; } /* FDT resources */ rv = hwreset_get_by_ofw_name(dev, 0, "tsadc-apb", &sc->hwreset); if (rv != 0) { device_printf(dev, "Cannot get 'tsadc-apb' reset\n"); goto fail; } rv = clk_get_by_ofw_name(dev, 0, "tsadc", &sc->tsadc_clk); if (rv != 0) { device_printf(dev, "Cannot get 'tsadc' clock: %d\n", rv); goto fail; } rv = clk_get_by_ofw_name(dev, 0, "apb_pclk", &sc->apb_pclk_clk); if (rv != 0) { device_printf(dev, "Cannot get 'apb_pclk' clock: %d\n", rv); goto fail; } /* grf is optional */ rv = syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf); if (rv != 0 && rv != ENOENT) { device_printf(dev, "Cannot get 'grf' syscon: %d\n", rv); goto fail; } rv = OF_getencprop(node, "rockchip,hw-tshut-temp", &sc->shutdown_temp, sizeof(sc->shutdown_temp)); if (rv <= 0) sc->shutdown_temp = sc->conf->shutdown_temp; rv = OF_getencprop(node, "rockchip,hw-tshut-mode", &sc->shutdown_mode, sizeof(sc->shutdown_mode)); if (rv <= 0) sc->shutdown_mode = sc->conf->shutdown_mode; rv = OF_getencprop(node, "rockchip,hw-tshut-polarity", &sc->shutdown_pol, sizeof(sc->shutdown_pol)); if (rv <= 0) sc->shutdown_pol = sc->conf->shutdown_pol; /* Wakeup controller */ rv = hwreset_assert(sc->hwreset); if (rv != 0) { device_printf(dev, "Cannot assert reset\n"); goto fail; } /* Set the assigned clocks parent and freq */ - if (clk_set_assigned(sc->dev, node) != 0) { + rv = clk_set_assigned(sc->dev, node); + if (rv != 0 && rv != ENOENT) { device_printf(dev, "clk_set_assigned failed\n"); goto fail; } rv = clk_enable(sc->tsadc_clk); if (rv != 0) { device_printf(dev, "Cannot enable 'tsadc_clk' clock: %d\n", rv); goto fail; } rv = clk_enable(sc->apb_pclk_clk); if (rv != 0) { device_printf(dev, "Cannot enable 'apb_pclk' clock: %d\n", rv); goto fail; } rv = hwreset_deassert(sc->hwreset); if (rv != 0) { device_printf(dev, "Cannot deassert reset\n"); goto fail; } tsadc_init(sc); for (i = 0; i < sc->conf->ntsensors; i++) tsadc_init_tsensor(sc, sc->conf->tsensors + i); /* Enable auto mode */ val = RD4(sc, TSADC_AUTO_CON); val |= TSADC_AUTO_CON_AUTO; WR4(sc, TSADC_AUTO_CON, val); rv = tsadc_init_sysctl(sc); if (rv != 0) { device_printf(sc->dev, "Cannot initialize sysctls\n"); goto fail_sysctl; } OF_device_register_xref(OF_xref_from_node(node), dev); return (bus_generic_attach(dev)); fail_sysctl: sysctl_ctx_free(&tsadc_sysctl_ctx); fail: if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); if (sc->tsadc_clk != NULL) clk_release(sc->tsadc_clk); if (sc->apb_pclk_clk != NULL) clk_release(sc->apb_pclk_clk); if (sc->hwreset != NULL) hwreset_release(sc->hwreset); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (ENXIO); } static int tsadc_detach(device_t dev) { struct tsadc_softc *sc; sc = device_get_softc(dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); sysctl_ctx_free(&tsadc_sysctl_ctx); if (sc->tsadc_clk != NULL) clk_release(sc->tsadc_clk); if (sc->apb_pclk_clk != NULL) clk_release(sc->apb_pclk_clk); if (sc->hwreset != NULL) hwreset_release(sc->hwreset); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (ENXIO); } static device_method_t rk_tsadc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tsadc_probe), DEVMETHOD(device_attach, tsadc_attach), DEVMETHOD(device_detach, tsadc_detach), /* TSADC interface */ DEVMETHOD(rk_tsadc_get_temperature, tsadc_get_temp), DEVMETHOD_END }; static devclass_t rk_tsadc_devclass; static DEFINE_CLASS_0(rk_tsadc, rk_tsadc_driver, rk_tsadc_methods, sizeof(struct tsadc_softc)); EARLY_DRIVER_MODULE(rk_tsadc, simplebus, rk_tsadc_driver, rk_tsadc_devclass, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); Index: head/sys/arm64/rockchip/rk_usbphy.c =================================================================== --- head/sys/arm64/rockchip/rk_usbphy.c (nonexistent) +++ head/sys/arm64/rockchip/rk_usbphy.c (revision 368340) @@ -0,0 +1,307 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> + * + * 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/gpio.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> + +#include <machine/bus.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> +#include <dev/extres/phy/phy_usb.h> +#include <dev/extres/regulator/regulator.h> +#include <dev/extres/syscon/syscon.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/extres/syscon/syscon.h> +#include <dev/fdt/simple_mfd.h> +#include "phynode_if.h" +#include "phynode_usb_if.h" +#include "syscon_if.h" + + + +/* Phy registers */ +#define UOC_CON0 0x00 +#define UOC_CON0_SIDDQ (1 << 13) +#define UOC_CON0_DISABLE (1 << 4) +#define UOC_CON0_COMMON_ON_N (1 << 0) + +#define UOC_CON2 0x08 +#define UOC_CON2_SOFT_CON_SEL (1 << 2) + +#define UOC_CON3 0x0c + + +#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) +#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) + + +static struct ofw_compat_data compat_data[] = { + {"rockchip,rk3288-usb-phy", 1}, + {NULL, 0}, +}; + +struct rk_usbphy_softc { + device_t dev; +}; + +struct rk_phynode_sc { + struct phynode_usb_sc usb_sc; + uint32_t base; + int mode; + clk_t clk; + hwreset_t hwreset; + regulator_t supply_vbus; + struct syscon *syscon; +}; + +static int +rk_phynode_phy_enable(struct phynode *phy, bool enable) +{ + struct rk_phynode_sc *sc; + int rv; + + sc = phynode_get_softc(phy); + + rv = SYSCON_MODIFY_4(sc->syscon, + sc->base + UOC_CON0, + UOC_CON0_SIDDQ << 16 | UOC_CON0_SIDDQ, + enable ? 0 : UOC_CON0_SIDDQ); + + return (rv); + +} + +static int +rk_phynode_get_mode(struct phynode *phynode, int *mode) +{ + struct rk_phynode_sc *sc; + + sc = phynode_get_softc(phynode); + *mode = sc->mode; + return (0); +} + +static int +rk_phynode_set_mode(struct phynode *phynode, int mode) +{ + struct rk_phynode_sc *sc; + + sc = phynode_get_softc(phynode); + sc->mode = mode; + + return (0); +} + + + /* Phy controller class and methods. */ +static phynode_method_t rk_phynode_methods[] = { + PHYNODEUSBMETHOD(phynode_enable, rk_phynode_phy_enable), + PHYNODEMETHOD(phynode_usb_get_mode, rk_phynode_get_mode), + PHYNODEMETHOD(phynode_usb_set_mode, rk_phynode_set_mode), + PHYNODEUSBMETHOD_END +}; +DEFINE_CLASS_1(rk_phynode, rk_phynode_class, rk_phynode_methods, + sizeof(struct rk_phynode_sc), phynode_usb_class); + +static int +rk_usbphy_init_phy(struct rk_usbphy_softc *sc, phandle_t node) +{ + struct phynode *phynode; + struct phynode_init_def phy_init; + struct rk_phynode_sc *phy_sc; + int rv; + uint32_t base; + clk_t clk; + hwreset_t hwreset; + regulator_t supply_vbus; + struct syscon *syscon; + + clk = NULL; + hwreset = NULL; + supply_vbus = NULL; + + rv = OF_getencprop(node, "reg", &base, sizeof(base)); + if (rv <= 0) { + device_printf(sc->dev, "cannot get 'reg' property.\n"); + goto fail; + } + + /* FDT resources. All are optional. */ + rv = clk_get_by_ofw_name(sc->dev, node, "phyclk", &clk); + if (rv != 0 && rv != ENOENT) { + device_printf(sc->dev, "cannot get 'phyclk' clock.\n"); + goto fail; + } + rv = hwreset_get_by_ofw_name(sc->dev, node, "phy-reset", &hwreset); + if (rv != 0 && rv != ENOENT) { + device_printf(sc->dev, "Cannot get 'phy-reset' reset\n"); + goto fail; + } + rv = regulator_get_by_ofw_property(sc->dev, node, "vbus-supply", + &supply_vbus); + if (rv != 0 && rv != ENOENT) { + device_printf(sc->dev, "Cannot get 'vbus' regulator.\n"); + goto fail; + } + + rv = SYSCON_GET_HANDLE(sc->dev, &syscon); + if (rv != 0) { + device_printf(sc->dev, "Cannot get parent syscon\n"); + goto fail; + } + + /* Init HW resources */ + if (hwreset != NULL) { + rv = hwreset_assert(hwreset); + if (rv != 0) { + device_printf(sc->dev, "Cannot assert reset\n"); + goto fail; + } + } + if (clk != NULL) { + rv = clk_enable(clk); + if (rv != 0) { + device_printf(sc->dev, + "Cannot enable 'phyclk' clock.\n"); + goto fail; + } + } + + if (hwreset != NULL) { + rv = hwreset_deassert(hwreset); + if (rv != 0) { + device_printf(sc->dev, "Cannot deassert reset\n"); + goto fail; + } + } + + /* Create and register phy. */ + bzero(&phy_init, sizeof(phy_init)); + phy_init.id = 1; + phy_init.ofw_node = node; + phynode = phynode_create(sc->dev, &rk_phynode_class, &phy_init); + if (phynode == NULL) { + device_printf(sc->dev, "Cannot create phy.\n"); + return (ENXIO); + } + + phy_sc = phynode_get_softc(phynode); + phy_sc->base = base; + phy_sc->clk = clk; + phy_sc->hwreset = hwreset; + phy_sc->supply_vbus = supply_vbus; + phy_sc->syscon = syscon; + if (phynode_register(phynode) == NULL) { + device_printf(sc->dev, "Cannot register phy.\n"); + return (ENXIO); + } + /* XXX It breaks boot */ + /* rk_phynode_phy_enable(phynode, 1); */ + return (0); + +fail: + if (supply_vbus != NULL) + regulator_release(supply_vbus); + if (clk != NULL) + clk_release(clk); + if (hwreset != NULL) + hwreset_release(hwreset); + + return (ENXIO); +} + +static int +rk_usbphy_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, "RockChip USB Phy"); + return (BUS_PROBE_DEFAULT); +} + +static int +rk_usbphy_attach(device_t dev) +{ + struct rk_usbphy_softc *sc; + phandle_t node, child; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(sc->dev); + + /* Attach child devices */ + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + rv = rk_usbphy_init_phy(sc, child); + if (rv != 0) + goto fail; + } + return (bus_generic_attach(dev)); + +fail: + return (ENXIO); +} + +static int +rk_usbphy_detach(device_t dev) +{ + struct rk_usbphy_softc *sc; + sc = device_get_softc(dev); + + return (0); +} + +static device_method_t rk_usbphy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rk_usbphy_probe), + DEVMETHOD(device_attach, rk_usbphy_attach), + DEVMETHOD(device_detach, rk_usbphy_detach), + DEVMETHOD_END +}; + +static devclass_t rk_usbphy_devclass; +static DEFINE_CLASS_0(rk_usbphy, rk_usbphy_driver, rk_usbphy_methods, + sizeof(struct rk_usbphy_softc)); +EARLY_DRIVER_MODULE(rk_usbphy, simplebus, rk_usbphy_driver, + rk_usbphy_devclass, NULL, NULL, BUS_PASS_TIMER + BUS_PASS_ORDER_LAST); Property changes on: head/sys/arm64/rockchip/rk_usbphy.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property