982e7e3df8
This defines a new bhnd_erom_if API, providing a common interface to device enumeration on siba(4) and bcma(4) devices, for use both in the bhndb bridge and SoC early boot contexts, and migrates mips/broadcom over to the new API. This also replaces the previous adhoc device enumeration support implemented for mips/broadcom. Migration of bhndb to the new API will be implemented in a follow-up commit. - Defined new bhnd_erom_if interface for bhnd(4) device enumeration, along with bcma(4) and siba(4)-specific implementations. - Fixed a minor bug in bhndb that logged an error when we attempted to map the full siba(4) bus space (18000000-17FFFFFF) in the siba EROM parser. - Reverted use of the resource's start address as the ChipCommon enum_addr in bhnd_read_chipid(). When called from bhndb, this address is found within the host address space, resulting in an invalid bridged enum_addr. - Added support for falling back on standard bus_activate_resource() in bhnd_bus_generic_activate_resource(), enabling allocation of the bhnd_erom's bhnd_resource directly from a nexus-attached bhnd(4) device. - Removed BHND_BUS_GET_CORE_TABLE(); it has been replaced by the erom API. - Added support for statically initializing bhnd_erom instances, for use prior to malloc availability. The statically allocated buffer size is verified both at runtime, and via a compile-time assertion (see BHND_EROM_STATIC_BYTES). - bhnd_erom classes are registered within a module via a linker set, allowing mips/broadcom to probe available EROM parser instances without creating a strong reference to bcma/siba-specific symbols. - Migrated mips/broadcom to bhnd_erom_if, replacing the previous MIPS-specific device enumeration implementation. Approved by: adrian (mentor) Differential Revision: https://reviews.freebsd.org/D7748
299 lines
7.6 KiB
C
299 lines
7.6 KiB
C
/*-
|
|
* Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <dev/bhnd/bhnd.h>
|
|
|
|
#include <dev/bhnd/cores/chipc/chipcreg.h>
|
|
|
|
#include <dev/bhnd/cores/chipc/pwrctl/bhnd_pwrctlvar.h>
|
|
|
|
#include <dev/bhnd/cores/pmu/bhnd_pmureg.h>
|
|
#include <dev/bhnd/cores/pmu/bhnd_pmuvar.h>
|
|
|
|
#include "bcm_machdep.h"
|
|
|
|
static struct bhnd_pmu_query *bcm_get_pmu(struct bcm_platform *bp);
|
|
static bool bcm_has_pmu(struct bcm_platform *bp);
|
|
|
|
static uint32_t bcm_pmu_read4(bus_size_t reg, void *ctx);
|
|
static void bcm_pmu_write4(bus_size_t reg, uint32_t val,
|
|
void *ctx);
|
|
static uint32_t bcm_pmu_read_chipst(void *ctx);
|
|
|
|
const struct bhnd_pmu_io bcm_pmu_soc_io = {
|
|
.rd4 = bcm_pmu_read4,
|
|
.wr4 = bcm_pmu_write4,
|
|
.rd_chipst = bcm_pmu_read_chipst
|
|
};
|
|
|
|
/**
|
|
* Supported UART clock sources.
|
|
*/
|
|
typedef enum {
|
|
BCM_UART_RCLK_PLL_T1 = 0, /**< UART uses PLL m2 (mii/uart/mipsref) with no divisor */
|
|
BCM_UART_RCLK_ALP = 1, /**< UART uses ALP rclk with no divisor */
|
|
BCM_UART_RCLK_EXT = 2, /**< UART uses 1.8423 MHz external clock */
|
|
BCM_UART_RCLK_SI = 3, /**< UART uses backplane clock with divisor of two */
|
|
BCM_UART_RCLK_FIXED = 4, /**< UART uses fixed 88Mhz backplane clock with a divisor of 48 */
|
|
} bcm_uart_clksrc;
|
|
|
|
/**
|
|
* UART clock configuration.
|
|
*/
|
|
struct bcm_uart_clkcfg {
|
|
bcm_uart_clksrc src; /**< clock source */
|
|
uint32_t div; /**< clock divisor */
|
|
uint32_t freq; /**< clock frequency (Hz) */
|
|
};
|
|
|
|
#define BCM_UART_RCLK_PLL_T1_DIV 1
|
|
#define BCM_UART_RCLK_ALP_DIV 1
|
|
#define BCM_UART_RCLK_EXT_HZ 1842300 /* 1.8423MHz */
|
|
#define BCM_UART_RCLK_EXT_DIV 1
|
|
#define BCM_UART_RCLK_FIXED_HZ 88000000 /* 88MHz */
|
|
#define BCM_UART_RCLK_FIXED_DIV 48
|
|
|
|
/* Fetch PLL type from ChipCommon capability flags */
|
|
#define BCM_PMU_PLL_TYPE(_bp) \
|
|
CHIPC_GET_BITS(_bp->cc_caps, CHIPC_CAP_PLL)
|
|
|
|
/**
|
|
* Return the PMU instance, or NULL if no PMU.
|
|
*/
|
|
static struct bhnd_pmu_query *
|
|
bcm_get_pmu(struct bcm_platform *bp)
|
|
{
|
|
if (!bcm_has_pmu(bp))
|
|
return (NULL);
|
|
return (&bp->pmu);
|
|
}
|
|
|
|
/**
|
|
* Return true if a PMU is available, false otherwise.
|
|
*/
|
|
static bool
|
|
bcm_has_pmu(struct bcm_platform *bp)
|
|
{
|
|
return (bp->pmu_addr != 0);
|
|
}
|
|
|
|
/**
|
|
* Determine the UART clock source for @p bp and return the
|
|
* corresponding clock configuration, if any.
|
|
*/
|
|
static struct bcm_uart_clkcfg
|
|
bcm_get_uart_clkcfg(struct bcm_platform *bp)
|
|
{
|
|
struct bcm_uart_clkcfg cfg;
|
|
struct bhnd_core_info *cc_id;
|
|
|
|
cc_id = &bp->cc_id;
|
|
|
|
/* These tests are ordered by precedence. */
|
|
|
|
/* PLL M2 clock source? */
|
|
if (!bcm_has_pmu(bp) && BCM_PMU_PLL_TYPE(bp) == CHIPC_PLL_TYPE1) {
|
|
uint32_t n, m;
|
|
|
|
n = BCM_CHIPC_READ_4(bp, CHIPC_CLKC_N);
|
|
m = BCM_CHIPC_READ_4(bp, CHIPC_CLKC_M2);
|
|
|
|
cfg = (struct bcm_uart_clkcfg) {
|
|
BCM_UART_RCLK_PLL_T1,
|
|
BCM_UART_RCLK_PLL_T1_DIV,
|
|
bhnd_pwrctl_clock_rate(BCM_PMU_PLL_TYPE(bp), n, m)
|
|
};
|
|
|
|
return (cfg);
|
|
}
|
|
|
|
/* ALP clock source? */
|
|
if (cc_id->hwrev != 15 && cc_id->hwrev >= 11) {
|
|
cfg = (struct bcm_uart_clkcfg) {
|
|
BCM_UART_RCLK_ALP,
|
|
BCM_UART_RCLK_ALP_DIV,
|
|
bcm_get_alpfreq(bp)
|
|
};
|
|
return (cfg);
|
|
}
|
|
|
|
/* External clock? */
|
|
if (CHIPC_HWREV_HAS_CORECTRL(cc_id->hwrev)) {
|
|
uint32_t corectrl, uclksel;
|
|
bool uintclk0;
|
|
|
|
/* Fetch UART clock support flag */
|
|
uclksel = CHIPC_GET_BITS(bp->cc_caps, CHIPC_CAP_UCLKSEL);
|
|
|
|
/* Is UART using internal clock? */
|
|
corectrl = BCM_CHIPC_READ_4(bp, CHIPC_CORECTRL);
|
|
uintclk0 = CHIPC_GET_FLAG(corectrl, CHIPC_UARTCLKO);
|
|
|
|
if (uintclk0 && uclksel == CHIPC_CAP_UCLKSEL_UINTCLK) {
|
|
cfg = (struct bcm_uart_clkcfg) {
|
|
BCM_UART_RCLK_EXT,
|
|
BCM_UART_RCLK_EXT_DIV,
|
|
BCM_UART_RCLK_EXT_HZ
|
|
};
|
|
return (cfg);
|
|
}
|
|
}
|
|
|
|
/* UART uses backplane clock? */
|
|
if (cc_id->hwrev == 15 || (cc_id->hwrev >= 3 && cc_id->hwrev <= 10)) {
|
|
cfg = (struct bcm_uart_clkcfg) {
|
|
BCM_UART_RCLK_SI,
|
|
BCM_CHIPC_READ_4(bp, CHIPC_CLKDIV) & CHIPC_CLKD_UART,
|
|
bcm_get_sifreq(bp)
|
|
};
|
|
|
|
return (cfg);
|
|
}
|
|
|
|
/* UART uses fixed clock? */
|
|
if (cc_id->hwrev <= 2) {
|
|
cfg = (struct bcm_uart_clkcfg) {
|
|
BCM_UART_RCLK_FIXED,
|
|
BCM_UART_RCLK_FIXED_DIV,
|
|
BCM_UART_RCLK_FIXED_HZ
|
|
};
|
|
|
|
return (cfg);
|
|
}
|
|
|
|
/* All cases must be accounted for above */
|
|
panic("unreachable - no clock config");
|
|
}
|
|
|
|
/**
|
|
* Return the UART reference clock frequency (in Hz).
|
|
*/
|
|
u_int
|
|
bcm_get_uart_rclk(struct bcm_platform *bp)
|
|
{
|
|
struct bcm_uart_clkcfg cfg;
|
|
|
|
cfg = bcm_get_uart_clkcfg(bp);
|
|
return (cfg.freq / cfg.div);
|
|
}
|
|
|
|
/** ALP clock frequency (in Hz) */
|
|
uint64_t
|
|
bcm_get_alpfreq(struct bcm_platform *bp) {
|
|
if (!bcm_has_pmu(bp))
|
|
panic("%s requires PMU\n", __FUNCTION__);
|
|
|
|
return (bhnd_pmu_alp_clock(bcm_get_pmu(bp)));
|
|
}
|
|
|
|
/** ILP clock frequency (in Hz) */
|
|
uint64_t
|
|
bcm_get_ilpfreq(struct bcm_platform *bp) {
|
|
if (!bcm_has_pmu(bp))
|
|
panic("%s requires PMU\n", __FUNCTION__);
|
|
|
|
return (bhnd_pmu_ilp_clock(bcm_get_pmu(bp)));
|
|
}
|
|
|
|
/** CPU clock frequency (in Hz) */
|
|
uint64_t
|
|
bcm_get_cpufreq(struct bcm_platform *bp)
|
|
{
|
|
uint32_t fixed_hz;
|
|
uint32_t n, m;
|
|
bus_size_t mreg;
|
|
uint8_t pll_type;
|
|
|
|
/* PMU support */
|
|
if (bcm_has_pmu(bp))
|
|
return (bhnd_pmu_cpu_clock(bcm_get_pmu(bp)));
|
|
|
|
/*
|
|
* PWRCTL support
|
|
*/
|
|
pll_type = CHIPC_GET_BITS(bp->cc_caps, CHIPC_CAP_PLL);
|
|
mreg = bhnd_pwrctl_cpu_clkreg_m(&bp->cid, pll_type, &fixed_hz);
|
|
if (mreg == 0)
|
|
return (fixed_hz);
|
|
|
|
n = BCM_CHIPC_READ_4(bp, CHIPC_CLKC_N);
|
|
m = BCM_CHIPC_READ_4(bp, mreg);
|
|
|
|
return (bhnd_pwrctl_cpu_clock_rate(&bp->cid, pll_type, n, m));
|
|
|
|
}
|
|
|
|
/** Backplane clock frequency (in Hz) */
|
|
uint64_t
|
|
bcm_get_sifreq(struct bcm_platform *bp)
|
|
{
|
|
uint32_t fixed_hz;
|
|
uint32_t n, m;
|
|
bus_size_t mreg;
|
|
uint8_t pll_type;
|
|
|
|
/* PMU support */
|
|
if (bcm_has_pmu(bp))
|
|
return (bhnd_pmu_si_clock(bcm_get_pmu(bp)));
|
|
|
|
/*
|
|
* PWRCTL support
|
|
*/
|
|
pll_type = CHIPC_GET_BITS(bp->cc_caps, CHIPC_CAP_PLL);
|
|
mreg = bhnd_pwrctl_si_clkreg_m(&bp->cid, pll_type, &fixed_hz);
|
|
if (mreg == 0)
|
|
return (fixed_hz);
|
|
|
|
n = BCM_CHIPC_READ_4(bp, CHIPC_CLKC_N);
|
|
m = BCM_CHIPC_READ_4(bp, mreg);
|
|
|
|
return (bhnd_pwrctl_si_clock_rate(&bp->cid, pll_type, n, m));
|
|
}
|
|
|
|
|
|
static uint32_t
|
|
bcm_pmu_read4(bus_size_t reg, void *ctx) {
|
|
struct bcm_platform *bp = ctx;
|
|
return (readl(BCM_SOC_ADDR(bp->pmu_addr, reg)));
|
|
}
|
|
|
|
static void
|
|
bcm_pmu_write4(bus_size_t reg, uint32_t val, void *ctx) {
|
|
struct bcm_platform *bp = ctx;
|
|
writel(BCM_SOC_ADDR(bp->pmu_addr, reg), val);
|
|
}
|
|
|
|
static uint32_t
|
|
bcm_pmu_read_chipst(void *ctx)
|
|
{
|
|
struct bcm_platform *bp = ctx;
|
|
return (readl(BCM_SOC_ADDR(bp->cc_addr, CHIPC_CHIPST)));
|
|
}
|