freebsd-skq/sys/arm/mv/common.c
Rafal Jaworowski 5694b144c0 Properly handle initial state of power mgmt.
Modules on Marvell SOC can be selectively PM-disabled, and we must not access
disabled devices' registers (attempt to initialize them) unconditionally, as
this leads to the system hang. This patch introduces graceful handling of the
PM state during devices init.

Submitted by:	Michal Hajduk
Obtained from:	Semihalf
2009-08-25 09:35:50 +00:00

1588 lines
35 KiB
C

/*-
* Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
* All rights reserved.
*
* Developed by Semihalf.
*
* 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.
* 3. Neither the name of MARVELL nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY 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 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 <machine/bus.h>
#include <arm/mv/mvreg.h>
#include <arm/mv/mvvar.h>
#include <arm/mv/mvwin.h>
static int win_eth_can_remap(int i);
static int decode_win_cpu_valid(void);
static int decode_win_usb_valid(void);
static int decode_win_eth_valid(void);
static int decode_win_pcie_valid(void);
static int decode_win_sata_valid(void);
static int decode_win_cesa_valid(void);
static void decode_win_cpu_setup(void);
static void decode_win_usb_setup(void);
static void decode_win_eth_setup(uint32_t base);
static void decode_win_pcie_setup(uint32_t base);
static void decode_win_sata_setup(void);
static void decode_win_cesa_setup(void);
static void decode_win_cesa_dump(void);
static void decode_win_usb_dump(void);
static uint32_t used_cpu_wins;
static __inline int
pm_is_disabled(uint32_t mask)
{
return (soc_power_ctrl_get(mask) == mask ? 0 : 1);
}
static __inline uint32_t
obio_get_pm_mask(uint32_t base)
{
struct obio_device *od;
for (od = obio_devices; od->od_name != NULL; od++)
if (od->od_base == base)
return (od->od_pwr_mask);
return (CPU_PM_CTRL_NONE);
}
/*
* Disable device using power management register.
* 1 - Device Power On
* 0 - Device Power Off
* Mask can be set in loader.
* EXAMPLE:
* loader> set hw.pm-disable-mask=0x2
*
* Common mask:
* |-------------------------------|
* | Device | Kirkwood | Discovery |
* |-------------------------------|
* | USB0 | 0x00008 | 0x020000 |
* |-------------------------------|
* | USB1 | - | 0x040000 |
* |-------------------------------|
* | USB2 | - | 0x080000 |
* |-------------------------------|
* | GE0 | 0x00001 | 0x000002 |
* |-------------------------------|
* | GE1 | - | 0x000004 |
* |-------------------------------|
* | IDMA | - | 0x100000 |
* |-------------------------------|
* | XOR | 0x10000 | 0x200000 |
* |-------------------------------|
* | CESA | 0x20000 | 0x400000 |
* |-------------------------------|
* | SATA | 0x04000 | 0x004000 |
* --------------------------------|
* This feature can be used only on Kirkwood and Discovery
* machines.
*/
static __inline void
pm_disable_device(int mask)
{
#ifdef DIAGNOSTIC
uint32_t reg;
reg = soc_power_ctrl_get(CPU_PM_CTRL_ALL);
printf("Power Management Register: 0%x\n", reg);
reg &= ~mask;
soc_power_ctrl_set(reg);
printf("Device %x is disabled\n", mask);
reg = soc_power_ctrl_get(CPU_PM_CTRL_ALL);
printf("Power Management Register: 0%x\n", reg);
#endif
}
uint32_t
read_cpu_ctrl(uint32_t reg)
{
return (bus_space_read_4(obio_tag, MV_CPU_CONTROL_BASE, reg));
}
void
write_cpu_ctrl(uint32_t reg, uint32_t val)
{
bus_space_write_4(obio_tag, MV_CPU_CONTROL_BASE, reg, val);
}
void
cpu_reset(void)
{
write_cpu_ctrl(RSTOUTn_MASK, SOFT_RST_OUT_EN);
write_cpu_ctrl(SYSTEM_SOFT_RESET, SYS_SOFT_RST);
while (1);
}
uint32_t
cpu_extra_feat(void)
{
uint32_t dev, rev;
uint32_t ef = 0;
soc_id(&dev, &rev);
if (dev == MV_DEV_88F6281 || dev == MV_DEV_MV78100_Z0 ||
dev == MV_DEV_MV78100)
__asm __volatile("mrc p15, 1, %0, c15, c1, 0" : "=r" (ef));
else if (dev == MV_DEV_88F5182 || dev == MV_DEV_88F5281)
__asm __volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (ef));
else if (bootverbose)
printf("This ARM Core does not support any extra features\n");
return (ef);
}
/*
* Get the power status of device. This feature is only supported on
* Kirkwood and Discovery SoCs.
*/
uint32_t
soc_power_ctrl_get(uint32_t mask)
{
#ifndef SOC_MV_ORION
if (mask != CPU_PM_CTRL_NONE)
mask &= read_cpu_ctrl(CPU_PM_CTRL);
return (mask);
#else
return (mask);
#endif
}
/*
* Set the power status of device. This feature is only supported on
* Kirkwood and Discovery SoCs.
*/
void
soc_power_ctrl_set(uint32_t mask)
{
#ifndef SOC_MV_ORION
if (mask != CPU_PM_CTRL_NONE)
write_cpu_ctrl(CPU_PM_CTRL, mask);
#endif
}
void
soc_id(uint32_t *dev, uint32_t *rev)
{
/*
* Notice: system identifiers are available in the registers range of
* PCIE controller, so using this function is only allowed (and
* possible) after the internal registers range has been mapped in via
* pmap_devmap_bootstrap().
*/
*dev = bus_space_read_4(obio_tag, MV_PCIE_BASE, 0) >> 16;
*rev = bus_space_read_4(obio_tag, MV_PCIE_BASE, 8) & 0xff;
}
void
soc_identify(void)
{
uint32_t d, r;
const char *dev;
const char *rev;
soc_id(&d, &r);
printf("SOC: ");
if (bootverbose)
printf("(0x%4x:0x%02x) ", d, r);
rev = "";
switch (d) {
case MV_DEV_88F5181:
dev = "Marvell 88F5181";
if (r == 3)
rev = "B1";
break;
case MV_DEV_88F5182:
dev = "Marvell 88F5182";
if (r == 2)
rev = "A2";
break;
case MV_DEV_88F5281:
dev = "Marvell 88F5281";
if (r == 4)
rev = "D0";
else if (r == 5)
rev = "D1";
else if (r == 6)
rev = "D2";
break;
case MV_DEV_88F6281:
dev = "Marvell 88F6281";
if (r == 0)
rev = "Z0";
else if (r == 2)
rev = "A0";
break;
case MV_DEV_MV78100_Z0:
dev = "Marvell MV78100 Z0";
break;
case MV_DEV_MV78100:
dev = "Marvell MV78100";
break;
default:
dev = "UNKNOWN";
break;
}
printf("%s", dev);
if (*rev != '\0')
printf(" rev %s", rev);
printf(", TClock %dMHz\n", get_tclk() / 1000 / 1000);
/* TODO add info on currently set endianess */
}
int
soc_decode_win(void)
{
uint32_t dev, rev;
int mask;
mask = 0;
TUNABLE_INT_FETCH("hw.pm-disable-mask", &mask);
if (mask != 0)
pm_disable_device(mask);
/* Retrieve our ID: some windows facilities vary between SoC models */
soc_id(&dev, &rev);
if (decode_win_cpu_valid() != 1 || decode_win_usb_valid() != 1 ||
decode_win_eth_valid() != 1 || decode_win_idma_valid() != 1 ||
decode_win_pcie_valid() != 1 || decode_win_sata_valid() != 1 ||
decode_win_cesa_valid() != 1)
return(-1);
decode_win_cpu_setup();
decode_win_usb_setup();
decode_win_eth_setup(MV_ETH0_BASE);
if (dev == MV_DEV_MV78100 || dev == MV_DEV_MV78100_Z0)
decode_win_eth_setup(MV_ETH1_BASE);
if (dev == MV_DEV_88F6281 || dev == MV_DEV_MV78100 ||
dev == MV_DEV_MV78100_Z0)
decode_win_cesa_setup();
decode_win_idma_setup();
decode_win_xor_setup();
if (dev == MV_DEV_MV78100 || dev == MV_DEV_MV78100_Z0) {
decode_win_pcie_setup(MV_PCIE00_BASE);
decode_win_pcie_setup(MV_PCIE01_BASE);
decode_win_pcie_setup(MV_PCIE02_BASE);
decode_win_pcie_setup(MV_PCIE03_BASE);
decode_win_pcie_setup(MV_PCIE10_BASE);
decode_win_pcie_setup(MV_PCIE11_BASE);
decode_win_pcie_setup(MV_PCIE12_BASE);
decode_win_pcie_setup(MV_PCIE13_BASE);
} else
decode_win_pcie_setup(MV_PCIE_BASE);
if (dev != MV_DEV_88F5281)
decode_win_sata_setup();
return (0);
}
/**************************************************************************
* Decode windows registers accessors
**************************************************************************/
WIN_REG_IDX_RD(win_cpu, cr, MV_WIN_CPU_CTRL, MV_MBUS_BRIDGE_BASE)
WIN_REG_IDX_RD(win_cpu, br, MV_WIN_CPU_BASE, MV_MBUS_BRIDGE_BASE)
WIN_REG_IDX_RD(win_cpu, remap_l, MV_WIN_CPU_REMAP_LO, MV_MBUS_BRIDGE_BASE)
WIN_REG_IDX_RD(win_cpu, remap_h, MV_WIN_CPU_REMAP_HI, MV_MBUS_BRIDGE_BASE)
WIN_REG_IDX_WR(win_cpu, cr, MV_WIN_CPU_CTRL, MV_MBUS_BRIDGE_BASE)
WIN_REG_IDX_WR(win_cpu, br, MV_WIN_CPU_BASE, MV_MBUS_BRIDGE_BASE)
WIN_REG_IDX_WR(win_cpu, remap_l, MV_WIN_CPU_REMAP_LO, MV_MBUS_BRIDGE_BASE)
WIN_REG_IDX_WR(win_cpu, remap_h, MV_WIN_CPU_REMAP_HI, MV_MBUS_BRIDGE_BASE)
WIN_REG_IDX_RD(ddr, br, MV_WIN_DDR_BASE, MV_DDR_CADR_BASE)
WIN_REG_IDX_RD(ddr, sz, MV_WIN_DDR_SIZE, MV_DDR_CADR_BASE)
WIN_REG_IDX_RD2(win_usb, cr, MV_WIN_USB_CTRL, MV_USB_AWR_BASE)
WIN_REG_IDX_RD2(win_usb, br, MV_WIN_USB_BASE, MV_USB_AWR_BASE)
WIN_REG_IDX_WR2(win_usb, cr, MV_WIN_USB_CTRL, MV_USB_AWR_BASE)
WIN_REG_IDX_WR2(win_usb, br, MV_WIN_USB_BASE, MV_USB_AWR_BASE)
WIN_REG_IDX_RD(win_cesa, cr, MV_WIN_CESA_CTRL, MV_CESA_BASE)
WIN_REG_IDX_RD(win_cesa, br, MV_WIN_CESA_BASE, MV_CESA_BASE)
WIN_REG_IDX_WR(win_cesa, cr, MV_WIN_CESA_CTRL, MV_CESA_BASE)
WIN_REG_IDX_WR(win_cesa, br, MV_WIN_CESA_BASE, MV_CESA_BASE)
WIN_REG_BASE_IDX_RD(win_eth, br, MV_WIN_ETH_BASE)
WIN_REG_BASE_IDX_RD(win_eth, sz, MV_WIN_ETH_SIZE)
WIN_REG_BASE_IDX_RD(win_eth, har, MV_WIN_ETH_REMAP)
WIN_REG_BASE_IDX_WR(win_eth, br, MV_WIN_ETH_BASE)
WIN_REG_BASE_IDX_WR(win_eth, sz, MV_WIN_ETH_SIZE)
WIN_REG_BASE_IDX_WR(win_eth, har, MV_WIN_ETH_REMAP)
WIN_REG_IDX_RD2(win_xor, br, MV_WIN_XOR_BASE, MV_XOR_BASE)
WIN_REG_IDX_RD2(win_xor, sz, MV_WIN_XOR_SIZE, MV_XOR_BASE)
WIN_REG_IDX_RD2(win_xor, har, MV_WIN_XOR_REMAP, MV_XOR_BASE)
WIN_REG_IDX_RD2(win_xor, ctrl, MV_WIN_XOR_CTRL, MV_XOR_BASE)
WIN_REG_IDX_WR2(win_xor, br, MV_WIN_XOR_BASE, MV_XOR_BASE)
WIN_REG_IDX_WR2(win_xor, sz, MV_WIN_XOR_SIZE, MV_XOR_BASE)
WIN_REG_IDX_WR2(win_xor, har, MV_WIN_XOR_REMAP, MV_XOR_BASE)
WIN_REG_IDX_WR2(win_xor, ctrl, MV_WIN_XOR_CTRL, MV_XOR_BASE)
WIN_REG_BASE_RD(win_eth, bare, 0x290)
WIN_REG_BASE_RD(win_eth, epap, 0x294)
WIN_REG_BASE_WR(win_eth, bare, 0x290)
WIN_REG_BASE_WR(win_eth, epap, 0x294)
WIN_REG_BASE_IDX_RD(win_pcie, cr, MV_WIN_PCIE_CTRL);
WIN_REG_BASE_IDX_RD(win_pcie, br, MV_WIN_PCIE_BASE);
WIN_REG_BASE_IDX_RD(win_pcie, remap, MV_WIN_PCIE_REMAP);
WIN_REG_BASE_IDX_WR(win_pcie, cr, MV_WIN_PCIE_CTRL);
WIN_REG_BASE_IDX_WR(win_pcie, br, MV_WIN_PCIE_BASE);
WIN_REG_BASE_IDX_WR(win_pcie, remap, MV_WIN_PCIE_REMAP);
WIN_REG_BASE_IDX_WR(pcie, bar, MV_PCIE_BAR);
WIN_REG_IDX_RD(win_idma, br, MV_WIN_IDMA_BASE, MV_IDMA_BASE)
WIN_REG_IDX_RD(win_idma, sz, MV_WIN_IDMA_SIZE, MV_IDMA_BASE)
WIN_REG_IDX_RD(win_idma, har, MV_WIN_IDMA_REMAP, MV_IDMA_BASE)
WIN_REG_IDX_RD(win_idma, cap, MV_WIN_IDMA_CAP, MV_IDMA_BASE)
WIN_REG_IDX_WR(win_idma, br, MV_WIN_IDMA_BASE, MV_IDMA_BASE)
WIN_REG_IDX_WR(win_idma, sz, MV_WIN_IDMA_SIZE, MV_IDMA_BASE)
WIN_REG_IDX_WR(win_idma, har, MV_WIN_IDMA_REMAP, MV_IDMA_BASE)
WIN_REG_IDX_WR(win_idma, cap, MV_WIN_IDMA_CAP, MV_IDMA_BASE)
WIN_REG_RD(win_idma, bare, 0xa80, MV_IDMA_BASE)
WIN_REG_WR(win_idma, bare, 0xa80, MV_IDMA_BASE)
WIN_REG_IDX_RD(win_sata, cr, MV_WIN_SATA_CTRL, MV_SATAHC_BASE);
WIN_REG_IDX_RD(win_sata, br, MV_WIN_SATA_BASE, MV_SATAHC_BASE);
WIN_REG_IDX_WR(win_sata, cr, MV_WIN_SATA_CTRL, MV_SATAHC_BASE);
WIN_REG_IDX_WR(win_sata, br, MV_WIN_SATA_BASE, MV_SATAHC_BASE);
/**************************************************************************
* Decode windows helper routines
**************************************************************************/
void
soc_dump_decode_win(void)
{
uint32_t dev, rev;
int i;
soc_id(&dev, &rev);
for (i = 0; i < MV_WIN_CPU_MAX; i++) {
printf("CPU window#%d: c 0x%08x, b 0x%08x", i,
win_cpu_cr_read(i),
win_cpu_br_read(i));
if (win_cpu_can_remap(i))
printf(", rl 0x%08x, rh 0x%08x",
win_cpu_remap_l_read(i),
win_cpu_remap_h_read(i));
printf("\n");
}
printf("Internal regs base: 0x%08x\n",
bus_space_read_4(obio_tag, MV_INTREGS_BASE, 0));
for (i = 0; i < MV_WIN_DDR_MAX; i++)
printf("DDR CS#%d: b 0x%08x, s 0x%08x\n", i,
ddr_br_read(i), ddr_sz_read(i));
for (i = 0; i < MV_WIN_ETH_MAX; i++) {
printf("ETH window#%d: b 0x%08x, s 0x%08x", i,
win_eth_br_read(MV_ETH0_BASE, i),
win_eth_sz_read(MV_ETH0_BASE, i));
if (win_eth_can_remap(i))
printf(", ha 0x%08x",
win_eth_har_read(MV_ETH0_BASE, i));
printf("\n");
}
printf("ETH windows: bare 0x%08x, epap 0x%08x\n",
win_eth_bare_read(MV_ETH0_BASE),
win_eth_epap_read(MV_ETH0_BASE));
decode_win_idma_dump();
decode_win_cesa_dump();
decode_win_usb_dump();
printf("\n");
}
/**************************************************************************
* CPU windows routines
**************************************************************************/
int
win_cpu_can_remap(int i)
{
uint32_t dev, rev;
soc_id(&dev, &rev);
/* Depending on the SoC certain windows have remap capability */
if ((dev == MV_DEV_88F5182 && i < 2) ||
(dev == MV_DEV_88F5281 && i < 4) ||
(dev == MV_DEV_88F6281 && i < 4) ||
(dev == MV_DEV_MV78100 && i < 8) ||
(dev == MV_DEV_MV78100_Z0 && i < 8))
return (1);
return (0);
}
/* XXX This should check for overlapping remap fields too.. */
int
decode_win_overlap(int win, int win_no, const struct decode_win *wintab)
{
const struct decode_win *tab;
int i;
tab = wintab;
for (i = 0; i < win_no; i++, tab++) {
if (i == win)
/* Skip self */
continue;
if ((tab->base + tab->size - 1) < (wintab + win)->base)
continue;
else if (((wintab + win)->base + (wintab + win)->size - 1) <
tab->base)
continue;
else
return (i);
}
return (-1);
}
static int
decode_win_cpu_valid(void)
{
int i, j, rv;
uint32_t b, e, s;
if (cpu_wins_no > MV_WIN_CPU_MAX) {
printf("CPU windows: too many entries: %d\n", cpu_wins_no);
return (-1);
}
rv = 1;
for (i = 0; i < cpu_wins_no; i++) {
if (cpu_wins[i].target == 0) {
printf("CPU window#%d: DDR target window is not "
"supposed to be reprogrammed!\n", i);
rv = 0;
}
if (cpu_wins[i].remap >= 0 && win_cpu_can_remap(i) != 1) {
printf("CPU window#%d: not capable of remapping, but "
"val 0x%08x defined\n", i, cpu_wins[i].remap);
rv = 0;
}
s = cpu_wins[i].size;
b = cpu_wins[i].base;
e = b + s - 1;
if (s > (0xFFFFFFFF - b + 1)) {
/*
* XXX this boundary check should account for 64bit
* and remapping..
*/
printf("CPU window#%d: no space for size 0x%08x at "
"0x%08x\n", i, s, b);
rv = 0;
continue;
}
j = decode_win_overlap(i, cpu_wins_no, &cpu_wins[0]);
if (j >= 0) {
printf("CPU window#%d: (0x%08x - 0x%08x) overlaps "
"with #%d (0x%08x - 0x%08x)\n", i, b, e, j,
cpu_wins[j].base,
cpu_wins[j].base + cpu_wins[j].size - 1);
rv = 0;
}
}
return (rv);
}
int
decode_win_cpu_set(int target, int attr, vm_paddr_t base, uint32_t size,
int remap)
{
uint32_t br, cr;
int win;
if (used_cpu_wins >= MV_WIN_CPU_MAX)
return (-1);
win = used_cpu_wins++;
br = base & 0xffff0000;
win_cpu_br_write(win, br);
if (win_cpu_can_remap(win)) {
if (remap >= 0) {
win_cpu_remap_l_write(win, remap & 0xffff0000);
win_cpu_remap_h_write(win, 0);
} else {
/*
* Remap function is not used for a given window
* (capable of remapping) - set remap field with the
* same value as base.
*/
win_cpu_remap_l_write(win, base & 0xffff0000);
win_cpu_remap_h_write(win, 0);
}
}
cr = ((size - 1) & 0xffff0000) | (attr << 8) | (target << 4) | 1;
win_cpu_cr_write(win, cr);
return (0);
}
static void
decode_win_cpu_setup(void)
{
int i;
used_cpu_wins = 0;
/* Disable all CPU windows */
for (i = 0; i < MV_WIN_CPU_MAX; i++) {
win_cpu_cr_write(i, 0);
win_cpu_br_write(i, 0);
if (win_cpu_can_remap(i)) {
win_cpu_remap_l_write(i, 0);
win_cpu_remap_h_write(i, 0);
}
}
for (i = 0; i < cpu_wins_no; i++)
if (cpu_wins[i].target > 0)
decode_win_cpu_set(cpu_wins[i].target,
cpu_wins[i].attr, cpu_wins[i].base,
cpu_wins[i].size, cpu_wins[i].remap);
}
/*
* Check if we're able to cover all active DDR banks.
*/
static int
decode_win_can_cover_ddr(int max)
{
int i, c;
c = 0;
for (i = 0; i < MV_WIN_DDR_MAX; i++)
if (ddr_is_active(i))
c++;
if (c > max) {
printf("Unable to cover all active DDR banks: "
"%d, available windows: %d\n", c, max);
return (0);
}
return (1);
}
/**************************************************************************
* DDR windows routines
**************************************************************************/
int
ddr_is_active(int i)
{
if (ddr_sz_read(i) & 0x1)
return (1);
return (0);
}
uint32_t
ddr_base(int i)
{
return (ddr_br_read(i) & 0xff000000);
}
uint32_t
ddr_size(int i)
{
return ((ddr_sz_read(i) | 0x00ffffff) + 1);
}
uint32_t
ddr_attr(int i)
{
return (i == 0 ? 0xe :
(i == 1 ? 0xd :
(i == 2 ? 0xb :
(i == 3 ? 0x7 : 0xff))));
}
uint32_t
ddr_target(int i)
{
/* Mbus unit ID is 0x0 for DDR SDRAM controller */
return (0);
}
/**************************************************************************
* USB windows routines
**************************************************************************/
static int
decode_win_usb_valid(void)
{
return (decode_win_can_cover_ddr(MV_WIN_USB_MAX));
}
static __inline int
usb_max_ports(void)
{
uint32_t dev, rev;
soc_id(&dev, &rev);
return ((dev == MV_DEV_MV78100 || dev == MV_DEV_MV78100_Z0) ? 3 : 1);
}
static void
decode_win_usb_dump(void)
{
int i, p, m;
m = usb_max_ports();
for (p = 0; p < m; p++)
for (i = 0; i < MV_WIN_USB_MAX; i++)
printf("USB window#%d: c 0x%08x, b 0x%08x\n", i,
win_usb_cr_read(i, p), win_usb_br_read(i, p));
}
/*
* Set USB decode windows.
*/
static void
decode_win_usb_setup(void)
{
uint32_t br, cr;
int i, j, p, m;
/* Disable and clear all USB windows for all ports */
m = usb_max_ports();
for (p = 0; p < m; p++) {
if (pm_is_disabled(CPU_PM_CTRL_USB(p)))
continue;
for (i = 0; i < MV_WIN_USB_MAX; i++) {
win_usb_cr_write(i, p, 0);
win_usb_br_write(i, p, 0);
}
/* Only access to active DRAM banks is required */
for (i = 0; i < MV_WIN_DDR_MAX; i++) {
if (ddr_is_active(i)) {
br = ddr_base(i);
/*
* XXX for 6281 we should handle Mbus write
* burst limit field in the ctrl reg
*/
cr = (((ddr_size(i) - 1) & 0xffff0000) |
(ddr_attr(i) << 8) |
(ddr_target(i) << 4) | 1);
/* Set the first free USB window */
for (j = 0; j < MV_WIN_USB_MAX; j++) {
if (win_usb_cr_read(j, p) & 0x1)
continue;
win_usb_br_write(j, p, br);
win_usb_cr_write(j, p, cr);
break;
}
}
}
}
}
/**************************************************************************
* ETH windows routines
**************************************************************************/
static int
win_eth_can_remap(int i)
{
/* ETH encode windows 0-3 have remap capability */
if (i < 4)
return (1);
return (0);
}
static int
eth_bare_read(uint32_t base, int i)
{
uint32_t v;
v = win_eth_bare_read(base);
v &= (1 << i);
return (v >> i);
}
static void
eth_bare_write(uint32_t base, int i, int val)
{
uint32_t v;
v = win_eth_bare_read(base);
v &= ~(1 << i);
v |= (val << i);
win_eth_bare_write(base, v);
}
static void
eth_epap_write(uint32_t base, int i, int val)
{
uint32_t v;
v = win_eth_epap_read(base);
v &= ~(0x3 << (i * 2));
v |= (val << (i * 2));
win_eth_epap_write(base, v);
}
static void
decode_win_eth_setup(uint32_t base)
{
uint32_t br, sz;
int i, j;
if (pm_is_disabled(obio_get_pm_mask(base)))
return;
/* Disable, clear and revoke protection for all ETH windows */
for (i = 0; i < MV_WIN_ETH_MAX; i++) {
eth_bare_write(base, i, 1);
eth_epap_write(base, i, 0);
win_eth_br_write(base, i, 0);
win_eth_sz_write(base, i, 0);
if (win_eth_can_remap(i))
win_eth_har_write(base, i, 0);
}
/* Only access to active DRAM banks is required */
for (i = 0; i < MV_WIN_DDR_MAX; i++)
if (ddr_is_active(i)) {
br = ddr_base(i) | (ddr_attr(i) << 8) | ddr_target(i);
sz = ((ddr_size(i) - 1) & 0xffff0000);
/* Set the first free ETH window */
for (j = 0; j < MV_WIN_ETH_MAX; j++) {
if (eth_bare_read(base, j) == 0)
continue;
win_eth_br_write(base, j, br);
win_eth_sz_write(base, j, sz);
/* XXX remapping ETH windows not supported */
/* Set protection RW */
eth_epap_write(base, j, 0x3);
/* Enable window */
eth_bare_write(base, j, 0);
break;
}
}
}
static int
decode_win_eth_valid(void)
{
return (decode_win_can_cover_ddr(MV_WIN_ETH_MAX));
}
/**************************************************************************
* PCIE windows routines
**************************************************************************/
static void
decode_win_pcie_setup(uint32_t base)
{
uint32_t size = 0;
uint32_t cr, br;
int i, j;
for (i = 0; i < MV_PCIE_BAR_MAX; i++)
pcie_bar_write(base, i, 0);
for (i = 0; i < MV_WIN_PCIE_MAX; i++) {
win_pcie_cr_write(base, i, 0);
win_pcie_br_write(base, i, 0);
win_pcie_remap_write(base, i, 0);
}
for (i = 0; i < MV_WIN_DDR_MAX; i++) {
if (ddr_is_active(i)) {
/* Map DDR to BAR 1 */
cr = (ddr_size(i) - 1) & 0xffff0000;
size += ddr_size(i) & 0xffff0000;
cr |= (ddr_attr(i) << 8) | (ddr_target(i) << 4) | 1;
br = ddr_base(i);
/* Use the first available PCIE window */
for (j = 0; j < MV_WIN_PCIE_MAX; j++) {
if (win_pcie_cr_read(base, j) != 0)
continue;
win_pcie_br_write(base, j, br);
win_pcie_cr_write(base, j, cr);
break;
}
}
}
/*
* Upper 16 bits in BAR register is interpreted as BAR size
* (in 64 kB units) plus 64kB, so substract 0x10000
* form value passed to register to get correct value.
*/
size -= 0x10000;
pcie_bar_write(base, 0, size | 1);
}
static int
decode_win_pcie_valid(void)
{
return (decode_win_can_cover_ddr(MV_WIN_PCIE_MAX));
}
/**************************************************************************
* IDMA windows routines
**************************************************************************/
#if defined(SOC_MV_ORION) || defined(SOC_MV_DISCOVERY)
static int
idma_bare_read(int i)
{
uint32_t v;
v = win_idma_bare_read();
v &= (1 << i);
return (v >> i);
}
static void
idma_bare_write(int i, int val)
{
uint32_t v;
v = win_idma_bare_read();
v &= ~(1 << i);
v |= (val << i);
win_idma_bare_write(v);
}
/*
* Sets channel protection 'val' for window 'w' on channel 'c'
*/
static void
idma_cap_write(int c, int w, int val)
{
uint32_t v;
v = win_idma_cap_read(c);
v &= ~(0x3 << (w * 2));
v |= (val << (w * 2));
win_idma_cap_write(c, v);
}
/*
* Set protection 'val' on all channels for window 'w'
*/
static void
idma_set_prot(int w, int val)
{
int c;
for (c = 0; c < MV_IDMA_CHAN_MAX; c++)
idma_cap_write(c, w, val);
}
static int
win_idma_can_remap(int i)
{
/* IDMA decode windows 0-3 have remap capability */
if (i < 4)
return (1);
return (0);
}
void
decode_win_idma_setup(void)
{
uint32_t br, sz;
int i, j;
if (pm_is_disabled(CPU_PM_CTRL_IDMA))
return;
/*
* Disable and clear all IDMA windows, revoke protection for all channels
*/
for (i = 0; i < MV_WIN_IDMA_MAX; i++) {
idma_bare_write(i, 1);
win_idma_br_write(i, 0);
win_idma_sz_write(i, 0);
if (win_idma_can_remap(i) == 1)
win_idma_har_write(i, 0);
}
for (i = 0; i < MV_IDMA_CHAN_MAX; i++)
win_idma_cap_write(i, 0);
/*
* Set up access to all active DRAM banks
*/
for (i = 0; i < MV_WIN_DDR_MAX; i++)
if (ddr_is_active(i)) {
br = ddr_base(i) | (ddr_attr(i) << 8) | ddr_target(i);
sz = ((ddr_size(i) - 1) & 0xffff0000);
/* Place DDR entries in non-remapped windows */
for (j = 0; j < MV_WIN_IDMA_MAX; j++)
if (win_idma_can_remap(j) != 1 &&
idma_bare_read(j) == 1) {
/* Configure window */
win_idma_br_write(j, br);
win_idma_sz_write(j, sz);
/* Set protection RW on all channels */
idma_set_prot(j, 0x3);
/* Enable window */
idma_bare_write(j, 0);
break;
}
}
/*
* Remaining targets -- from statically defined table
*/
for (i = 0; i < idma_wins_no; i++)
if (idma_wins[i].target > 0) {
br = (idma_wins[i].base & 0xffff0000) |
(idma_wins[i].attr << 8) | idma_wins[i].target;
sz = ((idma_wins[i].size - 1) & 0xffff0000);
/* Set the first free IDMA window */
for (j = 0; j < MV_WIN_IDMA_MAX; j++) {
if (idma_bare_read(j) == 0)
continue;
/* Configure window */
win_idma_br_write(j, br);
win_idma_sz_write(j, sz);
if (win_idma_can_remap(j) &&
idma_wins[j].remap >= 0)
win_idma_har_write(j, idma_wins[j].remap);
/* Set protection RW on all channels */
idma_set_prot(j, 0x3);
/* Enable window */
idma_bare_write(j, 0);
break;
}
}
}
int
decode_win_idma_valid(void)
{
const struct decode_win *wintab;
int c, i, j, rv;
uint32_t b, e, s;
if (idma_wins_no > MV_WIN_IDMA_MAX) {
printf("IDMA windows: too many entries: %d\n", idma_wins_no);
return (-1);
}
for (i = 0, c = 0; i < MV_WIN_DDR_MAX; i++)
if (ddr_is_active(i))
c++;
if (idma_wins_no > (MV_WIN_IDMA_MAX - c)) {
printf("IDMA windows: too many entries: %d, available: %d\n",
idma_wins_no, MV_WIN_IDMA_MAX - c);
return (-1);
}
wintab = idma_wins;
rv = 1;
for (i = 0; i < idma_wins_no; i++, wintab++) {
if (wintab->target == 0) {
printf("IDMA window#%d: DDR target window is not "
"supposed to be reprogrammed!\n", i);
rv = 0;
}
if (wintab->remap >= 0 && win_cpu_can_remap(i) != 1) {
printf("IDMA window#%d: not capable of remapping, but "
"val 0x%08x defined\n", i, wintab->remap);
rv = 0;
}
s = wintab->size;
b = wintab->base;
e = b + s - 1;
if (s > (0xFFFFFFFF - b + 1)) {
/* XXX this boundary check should account for 64bit and
* remapping.. */
printf("IDMA window#%d: no space for size 0x%08x at "
"0x%08x\n", i, s, b);
rv = 0;
continue;
}
j = decode_win_overlap(i, idma_wins_no, &idma_wins[0]);
if (j >= 0) {
printf("IDMA window#%d: (0x%08x - 0x%08x) overlaps "
"with #%d (0x%08x - 0x%08x)\n", i, b, e, j,
idma_wins[j].base,
idma_wins[j].base + idma_wins[j].size - 1);
rv = 0;
}
}
return (rv);
}
void
decode_win_idma_dump(void)
{
int i;
for (i = 0; i < MV_WIN_IDMA_MAX; i++) {
printf("IDMA window#%d: b 0x%08x, s 0x%08x", i,
win_idma_br_read(i), win_idma_sz_read(i));
if (win_idma_can_remap(i))
printf(", ha 0x%08x", win_idma_har_read(i));
printf("\n");
}
for (i = 0; i < MV_IDMA_CHAN_MAX; i++)
printf("IDMA channel#%d: ap 0x%08x\n", i,
win_idma_cap_read(i));
printf("IDMA windows: bare 0x%08x\n", win_idma_bare_read());
}
#else
/* Provide dummy functions to satisfy the build for SoCs not equipped with IDMA */
int
decode_win_idma_valid(void)
{
return (1);
}
void
decode_win_idma_setup(void)
{
}
void
decode_win_idma_dump(void)
{
}
#endif
/**************************************************************************
* XOR windows routines
**************************************************************************/
#if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY)
static int
xor_ctrl_read(int i, int c, int e)
{
uint32_t v;
v = win_xor_ctrl_read(c, e);
v &= (1 << i);
return (v >> i);
}
static void
xor_ctrl_write(int i, int c, int e, int val)
{
uint32_t v;
v = win_xor_ctrl_read(c, e);
v &= ~(1 << i);
v |= (val << i);
win_xor_ctrl_write(c, e, v);
}
/*
* Set channel protection 'val' for window 'w' on channel 'c'
*/
static void
xor_chan_write(int c, int e, int w, int val)
{
uint32_t v;
v = win_xor_ctrl_read(c, e);
v &= ~(0x3 << (w * 2 + 16));
v |= (val << (w * 2 + 16));
win_xor_ctrl_write(c, e, v);
}
/*
* Set protection 'val' on all channels for window 'w' on engine 'e'
*/
static void
xor_set_prot(int w, int e, int val)
{
int c;
for (c = 0; c < MV_XOR_CHAN_MAX; c++)
xor_chan_write(c, e, w, val);
}
static int
win_xor_can_remap(int i)
{
/* XOR decode windows 0-3 have remap capability */
if (i < 4)
return (1);
return (0);
}
static int
xor_max_eng(void)
{
uint32_t dev, rev;
soc_id(&dev, &rev);
if (dev == MV_DEV_88F6281)
return (2);
else if ((dev == MV_DEV_MV78100) || (dev == MV_DEV_MV78100_Z0))
return (1);
else
return (0);
}
static void
xor_active_dram(int c, int e, int *window)
{
uint32_t br, sz;
int i, m, w;
/*
* Set up access to all active DRAM banks
*/
m = xor_max_eng();
for (i = 0; i < m; i++)
if (ddr_is_active(i)) {
br = ddr_base(i) | (ddr_attr(i) << 8) |
ddr_target(i);
sz = ((ddr_size(i) - 1) & 0xffff0000);
/* Place DDR entries in non-remapped windows */
for (w = 0; w < MV_WIN_XOR_MAX; w++)
if (win_xor_can_remap(w) != 1 &&
(xor_ctrl_read(w, c, e) == 0) &&
w > *window) {
/* Configure window */
win_xor_br_write(w, e, br);
win_xor_sz_write(w, e, sz);
/* Set protection RW on all channels */
xor_set_prot(w, e, 0x3);
/* Enable window */
xor_ctrl_write(w, c, e, 1);
(*window)++;
break;
}
}
}
void
decode_win_xor_setup(void)
{
uint32_t br, sz;
int i, j, z, e = 1, m, window;
if (pm_is_disabled(CPU_PM_CTRL_XOR))
return;
/*
* Disable and clear all XOR windows, revoke protection for all
* channels
*/
m = xor_max_eng();
for (j = 0; j < m; j++, e--) {
/* Number of non-remaped windows */
window = MV_XOR_NON_REMAP - 1;
for (i = 0; i < MV_WIN_XOR_MAX; i++) {
win_xor_br_write(i, e, 0);
win_xor_sz_write(i, e, 0);
}
if (win_xor_can_remap(i) == 1)
win_xor_har_write(i, e, 0);
for (i = 0; i < MV_XOR_CHAN_MAX; i++) {
win_xor_ctrl_write(i, e, 0);
xor_active_dram(i, e, &window);
}
/*
* Remaining targets -- from a statically defined table
*/
for (i = 0; i < xor_wins_no; i++)
if (xor_wins[i].target > 0) {
br = (xor_wins[i].base & 0xffff0000) |
(xor_wins[i].attr << 8) |
xor_wins[i].target;
sz = ((xor_wins[i].size - 1) & 0xffff0000);
/* Set the first free XOR window */
for (z = 0; z < MV_WIN_XOR_MAX; z++) {
if (xor_ctrl_read(z, 0, e) &&
xor_ctrl_read(z, 1, e))
continue;
/* Configure window */
win_xor_br_write(z, e, br);
win_xor_sz_write(z, e, sz);
if (win_xor_can_remap(z) &&
xor_wins[z].remap >= 0)
win_xor_har_write(z, e,
xor_wins[z].remap);
/* Set protection RW on all channels */
xor_set_prot(z, e, 0x3);
/* Enable window */
xor_ctrl_write(z, 0, e, 1);
xor_ctrl_write(z, 1, e, 1);
break;
}
}
}
}
int
decode_win_xor_valid(void)
{
const struct decode_win *wintab;
int c, i, j, rv;
uint32_t b, e, s;
if (xor_wins_no > MV_WIN_XOR_MAX) {
printf("XOR windows: too many entries: %d\n", xor_wins_no);
return (-1);
}
for (i = 0, c = 0; i < MV_WIN_DDR_MAX; i++)
if (ddr_is_active(i))
c++;
if (xor_wins_no > (MV_WIN_XOR_MAX - c)) {
printf("XOR windows: too many entries: %d, available: %d\n",
xor_wins_no, MV_WIN_IDMA_MAX - c);
return (-1);
}
wintab = xor_wins;
rv = 1;
for (i = 0; i < xor_wins_no; i++, wintab++) {
if (wintab->target == 0) {
printf("XOR window#%d: DDR target window is not "
"supposed to be reprogrammed!\n", i);
rv = 0;
}
if (wintab->remap >= 0 && win_cpu_can_remap(i) != 1) {
printf("XOR window#%d: not capable of remapping, but "
"val 0x%08x defined\n", i, wintab->remap);
rv = 0;
}
s = wintab->size;
b = wintab->base;
e = b + s - 1;
if (s > (0xFFFFFFFF - b + 1)) {
/*
* XXX this boundary check should account for 64bit
* and remapping..
*/
printf("XOR window#%d: no space for size 0x%08x at "
"0x%08x\n", i, s, b);
rv = 0;
continue;
}
j = decode_win_overlap(i, xor_wins_no, &xor_wins[0]);
if (j >= 0) {
printf("XOR window#%d: (0x%08x - 0x%08x) overlaps "
"with #%d (0x%08x - 0x%08x)\n", i, b, e, j,
xor_wins[j].base,
xor_wins[j].base + xor_wins[j].size - 1);
rv = 0;
}
}
return (rv);
}
void
decode_win_xor_dump(void)
{
int i, j;
int e = 1;
for (j = 0; j < xor_max_eng(); j++, e--) {
for (i = 0; i < MV_WIN_XOR_MAX; i++) {
printf("XOR window#%d: b 0x%08x, s 0x%08x", i,
win_xor_br_read(i, e), win_xor_sz_read(i, e));
if (win_xor_can_remap(i))
printf(", ha 0x%08x", win_xor_har_read(i, e));
printf("\n");
}
for (i = 0; i < MV_XOR_CHAN_MAX; i++)
printf("XOR control#%d: 0x%08x\n", i,
win_xor_ctrl_read(i, e));
}
}
#else
/* Provide dummy functions to satisfy the build for SoCs not equipped with XOR */
int
decode_win_xor_valid(void)
{
return (1);
}
void
decode_win_xor_setup(void)
{
}
void
decode_win_xor_dump(void)
{
}
#endif
/**************************************************************************
* CESA TDMA windows routines
**************************************************************************/
#if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY)
/*
* Dump CESA TDMA decode windows.
*/
static void
decode_win_cesa_dump(void)
{
int i;
for (i = 0; i < MV_WIN_CESA_MAX; i++)
printf("CESA window#%d: c 0x%08x, b 0x%08x\n", i,
win_cesa_cr_read(i), win_cesa_br_read(i));
}
/*
* Set CESA TDMA decode windows.
*/
static void
decode_win_cesa_setup(void)
{
uint32_t br, cr;
int i, j;
if (pm_is_disabled(CPU_PM_CTRL_CRYPTO))
return;
/* Disable and clear all CESA windows */
for (i = 0; i < MV_WIN_CESA_MAX; i++) {
win_cesa_cr_write(i, 0);
win_cesa_br_write(i, 0);
}
/* Only access to active DRAM banks is required. */
for (i = 0; i < MV_WIN_DDR_MAX; i++)
if (ddr_is_active(i)) {
br = ddr_base(i);
cr = (((ddr_size(i) - 1) & 0xffff0000) |
(ddr_attr(i) << 8) | (ddr_target(i) << 4) | 1);
/* Set the first available CESA window */
for (j = 0; j < MV_WIN_CESA_MAX; j++) {
if (win_cesa_cr_read(j) & 0x1)
continue;
win_cesa_br_write(j, br);
win_cesa_cr_write(j, cr);
break;
}
}
}
/*
* Check CESA TDMA decode windows.
*/
static int
decode_win_cesa_valid(void)
{
return (decode_win_can_cover_ddr(MV_WIN_CESA_MAX));
}
#else
/*
* Provide dummy functions to satisfy the build for SoCs not equipped with
* CESA
*/
int
decode_win_cesa_valid(void)
{
return (1);
}
void
decode_win_cesa_setup(void)
{
}
void
decode_win_cesa_dump(void)
{
}
#endif
/**************************************************************************
* SATA windows routines
**************************************************************************/
static void
decode_win_sata_setup(void)
{
uint32_t cr, br;
int i, j;
if (pm_is_disabled(CPU_PM_CTRL_SATA))
return;
for (i = 0; i < MV_WIN_SATA_MAX; i++) {
win_sata_cr_write(i, 0);
win_sata_br_write(i, 0);
}
for (i = 0; i < MV_WIN_DDR_MAX; i++)
if (ddr_is_active(i)) {
cr = ((ddr_size(i) - 1) & 0xffff0000) |
(ddr_attr(i) << 8) | (ddr_target(i) << 4) | 1;
br = ddr_base(i);
/* Use the first available SATA window */
for (j = 0; j < MV_WIN_SATA_MAX; j++) {
if ((win_sata_cr_read(j) & 1) != 0)
continue;
win_sata_br_write(j, br);
win_sata_cr_write(j, cr);
break;
}
}
}
static int
decode_win_sata_valid(void)
{
uint32_t dev, rev;
soc_id(&dev, &rev);
if (dev == MV_DEV_88F5281)
return (1);
return (decode_win_can_cover_ddr(MV_WIN_SATA_MAX));
}