openwrt/target/linux/brcm-2.4/files/arch/mips/bcm947xx/hndchipc.c

159 lines
4.1 KiB
C

/*
* BCM47XX support code for some chipcommon (old extif) facilities (uart)
*
* Copyright 2006, Broadcom Corporation
* All Rights Reserved.
*
* THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
*
* $Id: hndchipc.c,v 1.1.1.1 2006/02/27 03:43:16 honor Exp $
*/
#include <typedefs.h>
#include <bcmdefs.h>
#include <osl.h>
#include <bcmutils.h>
#include <sbutils.h>
#include <bcmdevs.h>
#include <bcmnvram.h>
#include <sbconfig.h>
#include <sbextif.h>
#include <sbchipc.h>
#include <hndcpu.h>
/*
* Returns TRUE if an external UART exists at the given base
* register.
*/
static bool
BCMINITFN(serial_exists)(osl_t *osh, uint8 *regs)
{
uint8 save_mcr, status1;
save_mcr = R_REG(osh, &regs[UART_MCR]);
W_REG(osh, &regs[UART_MCR], UART_MCR_LOOP | 0x0a);
status1 = R_REG(osh, &regs[UART_MSR]) & 0xf0;
W_REG(osh, &regs[UART_MCR], save_mcr);
return (status1 == 0x90);
}
/*
* Initializes UART access. The callback function will be called once
* per found UART.
*/
void
BCMINITFN(sb_serial_init)(sb_t *sbh, void (*add)(void *regs, uint irq, uint baud_base,
uint reg_shift))
{
osl_t *osh;
void *regs;
ulong base;
uint irq;
int i, n;
osh = sb_osh(sbh);
if ((regs = sb_setcore(sbh, SB_EXTIF, 0))) {
extifregs_t *eir = (extifregs_t *) regs;
sbconfig_t *sb;
/* Determine external UART register base */
sb = (sbconfig_t *)((ulong) eir + SBCONFIGOFF);
base = EXTIF_CFGIF_BASE(sb_base(R_REG(osh, &sb->sbadmatch1)));
/* Determine IRQ */
irq = sb_irq(sbh);
/* Disable GPIO interrupt initially */
W_REG(osh, &eir->gpiointpolarity, 0);
W_REG(osh, &eir->gpiointmask, 0);
/* Search for external UARTs */
n = 2;
for (i = 0; i < 2; i++) {
regs = (void *) REG_MAP(base + (i * 8), 8);
if (serial_exists(osh, regs)) {
/* Set GPIO 1 to be the external UART IRQ */
W_REG(osh, &eir->gpiointmask, 2);
/* XXXDetermine external UART clock */
if (add)
add(regs, irq, 13500000, 0);
}
}
/* Add internal UART if enabled */
if (R_REG(osh, &eir->corecontrol) & CC_UE)
if (add)
add((void *) &eir->uartdata, irq, sb_clock(sbh), 2);
} else if ((regs = sb_setcore(sbh, SB_CC, 0))) {
chipcregs_t *cc = (chipcregs_t *) regs;
uint32 rev, cap, pll, baud_base, div;
/* Determine core revision and capabilities */
rev = sb_corerev(sbh);
cap = R_REG(osh, &cc->capabilities);
pll = cap & CAP_PLL_MASK;
/* Determine IRQ */
irq = sb_irq(sbh);
if (pll == PLL_TYPE1) {
/* PLL clock */
baud_base = sb_clock_rate(pll,
R_REG(osh, &cc->clockcontrol_n),
R_REG(osh, &cc->clockcontrol_m2));
div = 1;
} else {
/* Fixed ALP clock */
if (rev >= 11 && rev != 15) {
baud_base = 20000000;
div = 1;
/* Set the override bit so we don't divide it */
W_REG(osh, &cc->corecontrol, CC_UARTCLKO);
}
/* Internal backplane clock */
else if (rev >= 3) {
baud_base = sb_clock(sbh);
div = 2; /* Minimum divisor */
W_REG(osh, &cc->clkdiv,
((R_REG(osh, &cc->clkdiv) & ~CLKD_UART) | div));
}
/* Fixed internal backplane clock */
else {
baud_base = 88000000;
div = 48;
}
/* Clock source depends on strapping if UartClkOverride is unset */
if ((rev > 0) &&
((R_REG(osh, &cc->corecontrol) & CC_UARTCLKO) == 0)) {
if ((cap & CAP_UCLKSEL) == CAP_UINTCLK) {
/* Internal divided backplane clock */
baud_base /= div;
} else {
/* Assume external clock of 1.8432 MHz */
baud_base = 1843200;
}
}
}
/* Add internal UARTs */
n = cap & CAP_UARTS_MASK;
for (i = 0; i < n; i++) {
/* Register offset changed after revision 0 */
if (rev)
regs = (void *)((ulong) &cc->uart0data + (i * 256));
else
regs = (void *)((ulong) &cc->uart0data + (i * 8));
if (add)
add(regs, irq, baud_base, 0);
}
}
}