630 lines
16 KiB
C
630 lines
16 KiB
C
/* $NetBSD: awin_board.c,v 1.35 2015/04/20 01:33:22 matt Exp $ */
|
|
/*-
|
|
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Matt Thomas of 3am Software Foundry.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "opt_allwinner.h"
|
|
#include "opt_arm_debug.h"
|
|
#include "opt_multiprocessor.h"
|
|
|
|
#define _ARM32_BUS_DMA_PRIVATE
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__KERNEL_RCSID(1, "$NetBSD: awin_board.c,v 1.35 2015/04/20 01:33:22 matt Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/cpu.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <prop/proplib.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_ether.h>
|
|
|
|
#include <arm/locore.h>
|
|
|
|
#include <arm/mainbus/mainbus.h>
|
|
|
|
#include <arm/allwinner/awin_reg.h>
|
|
#include <arm/allwinner/awin_var.h>
|
|
|
|
#include <arm/cortex/gtmr_var.h>
|
|
|
|
bus_space_handle_t awin_core_bsh;
|
|
#if defined(ALLWINNER_A80)
|
|
bus_space_handle_t awin_core2_bsh;
|
|
bus_space_handle_t awin_rcpus_bsh;
|
|
#endif
|
|
|
|
struct arm32_bus_dma_tag awin_dma_tag = {
|
|
_BUS_DMAMAP_FUNCS,
|
|
_BUS_DMAMEM_FUNCS,
|
|
_BUS_DMATAG_FUNCS,
|
|
};
|
|
|
|
struct arm32_dma_range awin_coherent_dma_ranges[] = {
|
|
[0] = {
|
|
.dr_sysbase = AWIN_SDRAM_PBASE,
|
|
.dr_busbase = AWIN_SDRAM_PBASE,
|
|
.dr_flags = _BUS_DMAMAP_COHERENT,
|
|
},
|
|
};
|
|
|
|
|
|
struct arm32_bus_dma_tag awin_coherent_dma_tag = {
|
|
._ranges = awin_coherent_dma_ranges,
|
|
._nranges = __arraycount(awin_coherent_dma_ranges),
|
|
_BUS_DMAMAP_FUNCS,
|
|
_BUS_DMAMEM_FUNCS,
|
|
_BUS_DMATAG_FUNCS,
|
|
};
|
|
|
|
#ifdef AWIN_CONSOLE_EARLY
|
|
#include <dev/ic/ns16550reg.h>
|
|
#include <dev/ic/comreg.h>
|
|
#include <dev/cons.h>
|
|
|
|
static volatile uint32_t *uart_base;
|
|
|
|
static int
|
|
awin_cngetc(dev_t dv)
|
|
{
|
|
if ((le32toh(uart_base[com_lsr]) & LSR_RXRDY) == 0)
|
|
return -1;
|
|
|
|
return le32toh(uart_base[com_data]) & 0xff;
|
|
}
|
|
|
|
static void
|
|
awin_cnputc(dev_t dv, int c)
|
|
{
|
|
int timo = 150000;
|
|
|
|
while ((le32toh(uart_base[com_lsr]) & LSR_TXRDY) == 0 && --timo > 0)
|
|
;
|
|
|
|
uart_base[com_data] = htole32(c & 0xff);
|
|
|
|
timo = 150000;
|
|
while ((le32toh(uart_base[com_lsr]) & LSR_TSRE) == 0 && --timo > 0)
|
|
;
|
|
}
|
|
|
|
static struct consdev awin_earlycons = {
|
|
.cn_putc = awin_cnputc,
|
|
.cn_getc = awin_cngetc,
|
|
.cn_pollc = nullcnpollc,
|
|
};
|
|
#endif /* AWIN_CONSOLE_EARLY */
|
|
|
|
static void
|
|
awin_cpu_clk(void)
|
|
{
|
|
struct cpu_info * const ci = curcpu();
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
|
|
#if defined(ALLWINNER_A80)
|
|
const uint32_t c0cpux = bus_space_read_4(bst, awin_core_bsh,
|
|
AWIN_A80_CCU_OFFSET + AWIN_A80_CCU_PLL_C0CPUX_CTRL_REG);
|
|
const u_int p = (c0cpux & AWIN_A80_CCU_PLL_CxCPUX_OUT_EXT_DIVP) ? 4 : 1;
|
|
const u_int n = __SHIFTOUT(c0cpux, AWIN_A80_CCU_PLL_CxCPUX_FACTOR_N);
|
|
|
|
ci->ci_data.cpu_cc_freq = ((uint64_t)AWIN_REF_FREQ * n) / p;
|
|
#else
|
|
u_int reg = awin_chip_id() == AWIN_CHIP_ID_A31 ?
|
|
AWIN_A31_CPU_AXI_CFG_REG :
|
|
AWIN_CPU_AHB_APB0_CFG_REG;
|
|
const uint32_t cpu0_cfg = bus_space_read_4(bst, awin_core_bsh,
|
|
AWIN_CCM_OFFSET + reg);
|
|
const u_int cpu_clk_sel = __SHIFTIN(cpu0_cfg, AWIN_CPU_CLK_SRC_SEL);
|
|
switch (__SHIFTOUT(cpu_clk_sel, AWIN_CPU_CLK_SRC_SEL)) {
|
|
case AWIN_CPU_CLK_SRC_SEL_LOSC:
|
|
ci->ci_data.cpu_cc_freq = 32768;
|
|
break;
|
|
case AWIN_CPU_CLK_SRC_SEL_OSC24M:
|
|
ci->ci_data.cpu_cc_freq = AWIN_REF_FREQ;
|
|
break;
|
|
case AWIN_CPU_CLK_SRC_SEL_PLL1: {
|
|
const uint32_t pll1_cfg = bus_space_read_4(bst,
|
|
awin_core_bsh, AWIN_CCM_OFFSET + AWIN_PLL1_CFG_REG);
|
|
u_int p, n, k, m;
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
p = 0;
|
|
n = __SHIFTOUT(pll1_cfg, AWIN_PLL_CFG_FACTOR_N) + 1;
|
|
k = __SHIFTOUT(pll1_cfg, AWIN_PLL_CFG_FACTOR_K) + 1;
|
|
m = __SHIFTOUT(pll1_cfg, AWIN_PLL_CFG_FACTOR_M) + 1;
|
|
} else {
|
|
p = __SHIFTOUT(pll1_cfg, AWIN_PLL_CFG_OUT_EXP_DIVP);
|
|
n = __SHIFTOUT(pll1_cfg, AWIN_PLL_CFG_FACTOR_N);
|
|
k = __SHIFTOUT(pll1_cfg, AWIN_PLL_CFG_FACTOR_K) + 1;
|
|
m = __SHIFTOUT(pll1_cfg, AWIN_PLL_CFG_FACTOR_M) + 1;
|
|
}
|
|
ci->ci_data.cpu_cc_freq =
|
|
((uint64_t)AWIN_REF_FREQ * (n ? n : 1) * k / m) >> p;
|
|
break;
|
|
}
|
|
case AWIN_CPU_CLK_SRC_SEL_200MHZ:
|
|
ci->ci_data.cpu_cc_freq = 200000000;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
awin_bootstrap(vaddr_t iobase, vaddr_t uartbase)
|
|
{
|
|
int error;
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
|
|
#ifdef AWIN_CONSOLE_EARLY
|
|
uart_base = (volatile uint32_t *)uartbase;
|
|
cn_tab = &awin_earlycons;
|
|
printf("Early console started\n");
|
|
#endif
|
|
|
|
error = bus_space_map(bst, AWIN_CORE_PBASE, AWIN_CORE_SIZE,
|
|
0, &awin_core_bsh);
|
|
if (error)
|
|
panic("%s: failed to map awin %s registers: %d",
|
|
__func__, "io", error);
|
|
KASSERT(awin_core_bsh == iobase);
|
|
|
|
#ifdef ALLWINNER_A80
|
|
error = bus_space_map(bst, AWIN_A80_CORE2_PBASE, AWIN_A80_CORE2_SIZE,
|
|
0, &awin_core2_bsh);
|
|
if (error)
|
|
panic("%s: failed to map awin %s registers: %d",
|
|
__func__, "core2", error);
|
|
error = bus_space_map(bst, AWIN_A80_RCPUS_PBASE, AWIN_A80_RCPUS_SIZE,
|
|
0, &awin_rcpus_bsh);
|
|
if (error)
|
|
panic("%s: failed to map awin %s registers: %d",
|
|
__func__, "rcpus", error);
|
|
#endif
|
|
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf("CPU Speed is");
|
|
#endif
|
|
awin_cpu_clk();
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf(" %"PRIu64"\n", curcpu()->ci_data.cpu_cc_freq);
|
|
#endif
|
|
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf("Determining GPIO configuration");
|
|
#endif
|
|
awin_gpio_init();
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf("\n");
|
|
#endif
|
|
|
|
#ifdef VERBOSE_INIT_ARM
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
uint32_t s[4];
|
|
unsigned int cpuno;
|
|
for (cpuno = 0; cpuno < 4; cpuno++) {
|
|
s[cpuno] = bus_space_read_4(bst, awin_core_bsh,
|
|
AWIN_A31_CPUCFG_OFFSET +
|
|
AWIN_A31_CPUCFG_STATUS_REG(cpuno));
|
|
}
|
|
printf("%s: cpu status: 0=%#x 1=%#x 2=%#x 3=%#x\n", __func__,
|
|
s[0], s[1], s[2], s[3]);
|
|
} else if (awin_chip_id() == AWIN_CHIP_ID_A20) {
|
|
uint32_t s0 = bus_space_read_4(bst, awin_core_bsh,
|
|
AWIN_CPUCFG_OFFSET + AWIN_CPUCFG_CPU0_STATUS_REG);
|
|
uint32_t s1 = bus_space_read_4(bst, awin_core_bsh,
|
|
AWIN_CPUCFG_OFFSET + AWIN_CPUCFG_CPU1_STATUS_REG);
|
|
printf("%s: cpu status: 0=%#x 1=%#x\n", __func__, s0, s1);
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MULTIPROCESSOR) && defined(VERBOSE_INIT_ARM)
|
|
u_int arm_cpu_max;
|
|
#endif
|
|
#if defined(MULTIPROCESSOR) || defined(VERBOSE_INIT_ARM)
|
|
arm_cpu_max = 1 + __SHIFTOUT(armreg_l2ctrl_read(), L2CTRL_NUMCPU);
|
|
#endif
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf("%s: %d cpus present\n", __func__, arm_cpu_max);
|
|
#endif
|
|
#if defined(MULTIPROCESSOR)
|
|
for (size_t i = 1; i < arm_cpu_max; i++) {
|
|
if ((arm_cpu_hatched & (1 << i)) == 0) {
|
|
printf("%s: warning: cpu%zu failed to hatch\n",
|
|
__func__, i);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
awin_dma_bootstrap(psize_t psize)
|
|
{
|
|
awin_coherent_dma_ranges[0].dr_len = psize;
|
|
}
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
void
|
|
awin_cpu_hatch(struct cpu_info *ci)
|
|
{
|
|
gtmr_init_cpu_clock(ci);
|
|
}
|
|
#endif
|
|
|
|
psize_t
|
|
awin_memprobe(void)
|
|
{
|
|
psize_t memsize;
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf("memprobe not supported on A31\n");
|
|
#endif
|
|
memsize = 0;
|
|
} else {
|
|
const uint32_t dcr = bus_space_read_4(&armv7_generic_bs_tag,
|
|
awin_core_bsh,
|
|
AWIN_DRAM_OFFSET + AWIN_DRAM_DCR_REG);
|
|
|
|
memsize = (__SHIFTOUT(dcr, AWIN_DRAM_DCR_BUS_WIDTH) + 1)
|
|
/ __SHIFTOUT(dcr, AWIN_DRAM_DCR_IO_WIDTH);
|
|
memsize *= 1 << (__SHIFTOUT(dcr, AWIN_DRAM_DCR_CHIP_DENSITY)
|
|
+ 28 - 3);
|
|
#ifdef VERBOSE_INIT_ARM
|
|
printf("sdram_config = %#x, memsize = %uMB\n", dcr,
|
|
(u_int)(memsize >> 20));
|
|
#endif
|
|
}
|
|
return memsize;
|
|
}
|
|
|
|
uint16_t
|
|
awin_chip_id(void)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
#if defined(ALLWINNER_A80)
|
|
bus_space_handle_t bsh = awin_core2_bsh;
|
|
#else
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
#endif
|
|
static uint16_t chip_id = 0;
|
|
uint32_t ver;
|
|
|
|
if (!chip_id) {
|
|
ver = bus_space_read_4(bst, bsh,
|
|
AWIN_SRAM_OFFSET + AWIN_SRAM_VER_REG);
|
|
ver |= AWIN_SRAM_VER_R_EN;
|
|
bus_space_write_4(bst, bsh,
|
|
AWIN_SRAM_OFFSET + AWIN_SRAM_VER_REG, ver);
|
|
ver = bus_space_read_4(bst, bsh,
|
|
AWIN_SRAM_OFFSET + AWIN_SRAM_VER_REG);
|
|
|
|
chip_id = __SHIFTOUT(ver, AWIN_SRAM_VER_KEY_FIELD);
|
|
}
|
|
|
|
return chip_id;
|
|
}
|
|
|
|
const char *
|
|
awin_chip_name(void)
|
|
{
|
|
uint16_t chip_id = awin_chip_id();
|
|
|
|
switch (chip_id) {
|
|
case AWIN_CHIP_ID_A10: return "A10";
|
|
case AWIN_CHIP_ID_A13: return "A13";
|
|
case AWIN_CHIP_ID_A20: return "A20";
|
|
case AWIN_CHIP_ID_A23: return "A23";
|
|
case AWIN_CHIP_ID_A31: return "A31";
|
|
case AWIN_CHIP_ID_A80: return "A80";
|
|
default: return "unknown chip";
|
|
}
|
|
}
|
|
|
|
void
|
|
awin_pll6_enable(void)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
|
|
KASSERT(awin_chip_id() != AWIN_CHIP_ID_A80);
|
|
|
|
/*
|
|
* SATA needs PLL6 to be a 100MHz clock.
|
|
*/
|
|
const uint32_t ocfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL6_CFG_REG);
|
|
|
|
/*
|
|
* Output freq is 24MHz * n * k / m / 6.
|
|
* To get to 100MHz, k & m must be equal and n must be 25.
|
|
*/
|
|
uint32_t ncfg = ocfg;
|
|
ncfg &= ~(AWIN_PLL_CFG_BYPASS);
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
ncfg &= ~(AWIN_PLL_CFG_FACTOR_N|AWIN_PLL_CFG_FACTOR_K);
|
|
ncfg |= __SHIFTIN(1, AWIN_PLL_CFG_FACTOR_K);
|
|
ncfg |= __SHIFTIN(24, AWIN_PLL_CFG_FACTOR_N);
|
|
} else {
|
|
const u_int k = __SHIFTOUT(ocfg, AWIN_PLL_CFG_FACTOR_K);
|
|
ncfg &= ~(AWIN_PLL_CFG_FACTOR_M|AWIN_PLL_CFG_FACTOR_N);
|
|
ncfg |= __SHIFTIN(k, AWIN_PLL_CFG_FACTOR_M);
|
|
ncfg |= __SHIFTIN(25, AWIN_PLL_CFG_FACTOR_N);
|
|
ncfg |= AWIN_PLL6_CFG_SATA_CLK_EN;
|
|
}
|
|
ncfg |= AWIN_PLL_CFG_ENABLE;
|
|
if (ncfg != ocfg) {
|
|
bus_space_write_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL6_CFG_REG, ncfg);
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
do {
|
|
ncfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL6_CFG_REG);
|
|
} while ((ncfg & AWIN_A31_PLL6_CFG_LOCK) == 0);
|
|
}
|
|
}
|
|
#if 0
|
|
printf(" [pll6=%#x->%#x:n=%ju k=%ju m=%ju] ",
|
|
ocfg, ncfg,
|
|
__SHIFTOUT(ncfg, AWIN_PLL_CFG_FACTOR_N),
|
|
__SHIFTOUT(ncfg, AWIN_PLL_CFG_FACTOR_K),
|
|
__SHIFTOUT(ncfg, AWIN_PLL_CFG_FACTOR_M));
|
|
#endif
|
|
}
|
|
|
|
void
|
|
awin_pll2_enable(void)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
|
|
/*
|
|
* AC (at 48kHz) needs PLL2 to be 24576000 Hz
|
|
*/
|
|
const uint32_t ocfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL2_CFG_REG);
|
|
|
|
uint32_t ncfg = ocfg;
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
ncfg &= ~(AWIN_A31_PLL2_CFG_PREVDIV_M|
|
|
AWIN_A31_PLL2_CFG_FACTOR_N|
|
|
AWIN_A31_PLL2_CFG_POSTDIV_P);
|
|
ncfg |= __SHIFTIN(20, AWIN_A31_PLL2_CFG_PREVDIV_M);
|
|
ncfg |= __SHIFTIN(85, AWIN_A31_PLL2_CFG_FACTOR_N);
|
|
ncfg |= __SHIFTIN(3, AWIN_A31_PLL2_CFG_POSTDIV_P);
|
|
ncfg |= AWIN_PLL_CFG_ENABLE;
|
|
} else {
|
|
ncfg &= ~(AWIN_PLL2_CFG_PREVDIV|
|
|
AWIN_PLL2_CFG_FACTOR_N|
|
|
AWIN_PLL2_CFG_POSTDIV);
|
|
ncfg |= __SHIFTIN(21, AWIN_PLL2_CFG_PREVDIV);
|
|
ncfg |= __SHIFTIN(86, AWIN_PLL2_CFG_FACTOR_N);
|
|
ncfg |= __SHIFTIN(4, AWIN_PLL2_CFG_POSTDIV);
|
|
ncfg |= AWIN_PLL_CFG_ENABLE;
|
|
}
|
|
|
|
if (ncfg != ocfg) {
|
|
bus_space_write_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL2_CFG_REG, ncfg);
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
do {
|
|
ncfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL2_CFG_REG);
|
|
} while ((ncfg & AWIN_A31_PLL2_CFG_LOCK) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
awin_pll3_enable(void)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
|
|
/*
|
|
* HDMI needs PLL3 to be 29700000 Hz
|
|
*/
|
|
const uint32_t ocfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG);
|
|
|
|
uint32_t ncfg = ocfg;
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
ncfg &= ~AWIN_A31_PLL3_CFG_MODE;
|
|
ncfg &= ~AWIN_A31_PLL3_CFG_MODE_SEL;
|
|
ncfg |= AWIN_A31_PLL3_CFG_FRAC_CLK_OUT;
|
|
ncfg |= AWIN_PLL_CFG_ENABLE;
|
|
} else {
|
|
ncfg &= ~AWIN_PLL3_MODE_SEL;
|
|
ncfg |= AWIN_PLL3_FRAC_SET;
|
|
ncfg |= AWIN_PLL_CFG_ENABLE;
|
|
}
|
|
|
|
if (ncfg != ocfg) {
|
|
bus_space_write_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG, ncfg);
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
do {
|
|
ncfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG);
|
|
} while ((ncfg & AWIN_A31_PLL3_CFG_LOCK) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
awin_pll7_enable(void)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
|
|
/*
|
|
* HDMI needs PLL7 to be 29700000 Hz
|
|
*/
|
|
const uint32_t ocfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL7_CFG_REG);
|
|
|
|
uint32_t ncfg = ocfg;
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
ncfg &= ~AWIN_A31_PLL7_CFG_MODE;
|
|
ncfg &= ~AWIN_A31_PLL7_CFG_MODE_SEL;
|
|
ncfg |= AWIN_A31_PLL7_CFG_FRAC_CLK_OUT;
|
|
ncfg |= AWIN_PLL_CFG_ENABLE;
|
|
} else {
|
|
ncfg &= ~AWIN_PLL7_MODE_SEL;
|
|
ncfg |= AWIN_PLL7_FRAC_SET;
|
|
ncfg |= AWIN_PLL_CFG_ENABLE;
|
|
}
|
|
|
|
if (ncfg != ocfg) {
|
|
bus_space_write_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL7_CFG_REG, ncfg);
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
do {
|
|
ncfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL7_CFG_REG);
|
|
} while ((ncfg & AWIN_A31_PLL7_CFG_LOCK) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
awin_pll3_set_rate(uint32_t rate)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
|
|
const uint32_t ocfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG);
|
|
|
|
uint32_t ncfg = ocfg;
|
|
if (rate == 0) {
|
|
ncfg &= ~AWIN_PLL_CFG_ENABLE;
|
|
} else {
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
unsigned int m = 8;
|
|
unsigned int n = rate / (AWIN_REF_FREQ / m);
|
|
ncfg |= AWIN_A31_PLL3_CFG_MODE_SEL;
|
|
ncfg &= ~AWIN_A31_PLL3_CFG_FACTOR_N;
|
|
ncfg |= __SHIFTIN(n - 1, AWIN_A31_PLL3_CFG_FACTOR_N);
|
|
ncfg &= ~AWIN_A31_PLL3_CFG_PREDIV_M;
|
|
ncfg |= __SHIFTIN(m - 1, AWIN_A31_PLL3_CFG_PREDIV_M);
|
|
} else {
|
|
unsigned int m = rate / 3000000;
|
|
ncfg |= AWIN_PLL3_MODE_SEL;
|
|
ncfg &= ~AWIN_PLL3_FACTOR_M;
|
|
ncfg |= __SHIFTIN(m, AWIN_PLL3_FACTOR_M);
|
|
}
|
|
ncfg |= AWIN_PLL_CFG_ENABLE;
|
|
}
|
|
|
|
if (ncfg != ocfg) {
|
|
bus_space_write_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG, ncfg);
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
do {
|
|
ncfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL3_CFG_REG);
|
|
} while ((ncfg & AWIN_A31_PLL3_CFG_LOCK) == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
awin_pll5x_get_rate(void)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
unsigned int n, k, p;
|
|
|
|
KASSERT(awin_chip_id() != AWIN_CHIP_ID_A31);
|
|
KASSERT(awin_chip_id() != AWIN_CHIP_ID_A80);
|
|
|
|
const uint32_t cfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL5_CFG_REG);
|
|
|
|
n = __SHIFTOUT(cfg, AWIN_PLL_CFG_FACTOR_N);
|
|
k = __SHIFTOUT(cfg, AWIN_PLL_CFG_FACTOR_K) + 1;
|
|
p = __SHIFTOUT(cfg, AWIN_PLL5_OUT_EXT_DIV_P);
|
|
|
|
return (AWIN_REF_FREQ * n * k) >> p;
|
|
}
|
|
|
|
uint32_t
|
|
awin_pll6_get_rate(void)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
unsigned int n, k, m;
|
|
|
|
KASSERT(awin_chip_id() != AWIN_CHIP_ID_A80);
|
|
|
|
const uint32_t cfg = bus_space_read_4(bst, bsh,
|
|
AWIN_CCM_OFFSET + AWIN_PLL6_CFG_REG);
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
n = __SHIFTOUT(cfg, AWIN_PLL_CFG_FACTOR_N) + 1;
|
|
k = __SHIFTOUT(cfg, AWIN_PLL_CFG_FACTOR_K) + 1;
|
|
m = 2;
|
|
} else {
|
|
n = __SHIFTOUT(cfg, AWIN_PLL_CFG_FACTOR_N);
|
|
k = __SHIFTOUT(cfg, AWIN_PLL_CFG_FACTOR_K) + 1;
|
|
m = __SHIFTOUT(cfg, AWIN_PLL_CFG_FACTOR_M) + 1;
|
|
}
|
|
|
|
return (AWIN_REF_FREQ * n * k) / m;
|
|
}
|
|
|
|
uint32_t
|
|
awin_periph0_get_rate(void)
|
|
{
|
|
bus_space_tag_t bst = &armv7_generic_bs_tag;
|
|
bus_space_handle_t bsh = awin_core_bsh;
|
|
unsigned int n, idiv, odiv;
|
|
|
|
KASSERT(awin_chip_id() == AWIN_CHIP_ID_A80);
|
|
|
|
const uint32_t cfg = bus_space_read_4(bst, bsh,
|
|
AWIN_A80_CCU_OFFSET + AWIN_A80_CCU_PLL_PERIPH0_CTRL_REG);
|
|
|
|
n = __SHIFTOUT(cfg, AWIN_A80_CCU_PLL_PERIPH0_FACTOR_N);
|
|
idiv = __SHIFTOUT(cfg, AWIN_A80_CCU_PLL_PERIPH0_INPUT_DIV) + 1;
|
|
odiv = __SHIFTOUT(cfg, AWIN_A80_CCU_PLL_PERIPH0_OUTPUT_DIV) + 1;
|
|
|
|
return ((AWIN_REF_FREQ * n) / idiv) / odiv;
|
|
}
|