diff options
author | raj <raj@FreeBSD.org> | 2008-10-13 20:07:13 +0000 |
---|---|---|
committer | raj <raj@FreeBSD.org> | 2008-10-13 20:07:13 +0000 |
commit | 3226c137788c6c0c6b82bd1b88fd42a2a8ea4cb8 (patch) | |
tree | d96b4a1062d4a3a8f75bdb30e300bd1a50f23e4f /sys/arm | |
parent | 000539b88803ce11f549bc8d24154dc9a6b1c386 (diff) | |
download | FreeBSD-src-3226c137788c6c0c6b82bd1b88fd42a2a8ea4cb8.zip FreeBSD-src-3226c137788c6c0c6b82bd1b88fd42a2a8ea4cb8.tar.gz |
Introduce basic support for Marvell families of system-on-chip ARM devices:
* Orion
- 88F5181
- 88F5182
- 88F5281
* Kirkwood
- 88F6281
* Discovery
- MV78100
The above families of SOCs are built around CPU cores compliant with ARMv5TE
instruction set architecture definition. They share a number of integrated
peripherals. This commit brings support for the following basic elements:
* GPIO
* Interrupt controller
* L1, L2 cache
* Timers, watchdog, RTC
* TWSI (I2C)
* UART
Other peripherals drivers will be introduced separately.
Reviewed by: imp, marcel, stass (Thanks guys!)
Obtained from: Marvell, Semihalf
Diffstat (limited to 'sys/arm')
28 files changed, 5698 insertions, 1 deletions
diff --git a/sys/arm/arm/elf_trampoline.c b/sys/arm/arm/elf_trampoline.c index 9902546..98e76fe 100644 --- a/sys/arm/arm/elf_trampoline.c +++ b/sys/arm/arm/elf_trampoline.c @@ -73,6 +73,8 @@ void __startC(void); #endif #ifdef CPU_XSCALE_81342 #define cpu_l2cache_wbinv_all xscalec3_l2cache_purge +#elif defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY) +#define cpu_l2cache_wbinv_all feroceon_l2cache_wbinv_all #else #define cpu_l2cache_wbinv_all() #endif diff --git a/sys/arm/include/intr.h b/sys/arm/include/intr.h index bf2bf6d..d75f14e 100644 --- a/sys/arm/include/intr.h +++ b/sys/arm/include/intr.h @@ -44,7 +44,9 @@ #elif defined(CPU_XSCALE_PXA2X0) #include <arm/xscale/pxa/pxareg.h> #define NIRQ IRQ_GPIO_MAX -#elif defined(CPU_ARM9) +#elif defined(SOC_MV_DISCOVERY) +#define NIRQ 96 +#elif defined(CPU_ARM9) || defined(SOC_MV_KIRKWOOD) #define NIRQ 64 #else #define NIRQ 32 diff --git a/sys/arm/include/resource.h b/sys/arm/include/resource.h index 229d14f..90dd5cd 100644 --- a/sys/arm/include/resource.h +++ b/sys/arm/include/resource.h @@ -41,5 +41,6 @@ #define SYS_RES_DRQ 2 /* isa dma lines */ #define SYS_RES_MEMORY 3 /* i/o memory */ #define SYS_RES_IOPORT 4 /* i/o ports */ +#define SYS_RES_GPIO 5 /* general purpose i/o */ #endif /* !_MACHINE_RESOURCE_H_ */ diff --git a/sys/arm/mv/bus_space.c b/sys/arm/mv/bus_space.c new file mode 100644 index 0000000..2cdc454 --- /dev/null +++ b/sys/arm/mv/bus_space.c @@ -0,0 +1,162 @@ +/*- + * 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 <sys/malloc.h> + +#include <machine/bus.h> + +/* + * Bus space functions for Marvell SoC family + */ + +/* Prototypes for all the bus_space structure functions */ +bs_protos(generic); +bs_protos(generic_armv4); + +/* + * The obio bus space tag. This is constant for all instances, so + * we never have to explicitly "create" it. + */ +static struct bus_space _base_tag = { + /* cookie */ + (void *) 0, + + /* mapping/unmapping */ + generic_bs_map, + generic_bs_unmap, + generic_bs_subregion, + + /* allocation/deallocation */ + generic_bs_alloc, + generic_bs_free, + + /* barrier */ + generic_bs_barrier, + + /* read (single) */ + generic_bs_r_1, + generic_armv4_bs_r_2, + generic_bs_r_4, + NULL, + + /* read multiple */ + generic_bs_rm_1, + generic_armv4_bs_rm_2, + generic_bs_rm_4, + NULL, + + /* read region */ + generic_bs_rr_1, + generic_armv4_bs_rr_2, + generic_bs_rr_4, + NULL, + + /* write (single) */ + generic_bs_w_1, + generic_armv4_bs_w_2, + generic_bs_w_4, + NULL, + + /* write multiple */ + generic_bs_wm_1, + generic_armv4_bs_wm_2, + generic_bs_wm_4, + NULL, + + /* write region */ + NULL, + NULL, + NULL, + NULL, + + /* set multiple */ + NULL, + NULL, + NULL, + NULL, + + /* set region */ + NULL, + NULL, + NULL, + NULL, + + /* copy */ + NULL, + NULL, + NULL, + NULL, + + /* read stream (single) */ + NULL, + NULL, + NULL, + NULL, + + /* read multiple stream */ + NULL, + generic_armv4_bs_rm_2, /* bus_space_read_multi_stream_2 */ + NULL, + NULL, + + /* read region stream */ + NULL, + NULL, + NULL, + NULL, + + /* write stream (single) */ + NULL, + NULL, + NULL, + NULL, + + /* write multiple stream */ + NULL, + generic_armv4_bs_wm_2, /* bus_space_write_multi_stream_2 */ + NULL, + NULL, + + /* write region stream */ + NULL, + NULL, + NULL, + NULL +}; + +bus_space_tag_t obio_tag = &_base_tag; diff --git a/sys/arm/mv/common.c b/sys/arm/mv/common.c new file mode 100644 index 0000000..5489874 --- /dev/null +++ b/sys/arm/mv/common.c @@ -0,0 +1,965 @@ +/*- + * 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/systm.h> +#include <sys/bus.h> + +#include <machine/bus.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.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 void decode_win_cpu_setup(void); +static void decode_win_usb_setup(uint32_t ctrl); +static void decode_win_eth_setup(uint32_t base); +static void decode_win_pcie_setup(uint32_t base); + +static uint32_t dev, rev; + +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 ef = 0; + + soc_id(&dev, &rev); + if (dev == MV_DEV_88F6281 || 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); +} + +uint32_t +soc_power_ctrl_get(uint32_t mask) +{ + + if (mask != CPU_PM_CTRL_NONE) + mask &= read_cpu_ctrl(CPU_PM_CTRL); + + return (mask); +} + +uint32_t +get_tclk(void) +{ + +#if defined(SOC_MV_DISCOVERY) + return (TCLK_200MHZ); +#else + return (TCLK_166MHZ); +#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"; + 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) +{ + + /* 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) + return(-1); + + decode_win_cpu_setup(); + decode_win_usb_setup(MV_USB0_BASE); + decode_win_eth_setup(MV_ETH0_BASE); + if (dev == MV_DEV_MV78100) + decode_win_eth_setup(MV_ETH1_BASE); + decode_win_idma_setup(); + decode_win_pcie_setup(MV_PCIE_BASE); + + /* TODO set up decode wins for SATA */ + + 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_RD(win_usb, cr, MV_WIN_USB_CTRL, MV_USB_AWR_BASE) +WIN_REG_IDX_RD(win_usb, br, MV_WIN_USB_BASE, MV_USB_AWR_BASE) +WIN_REG_IDX_WR(win_usb, cr, MV_WIN_USB_CTRL, MV_USB_AWR_BASE) +WIN_REG_IDX_WR(win_usb, br, MV_WIN_USB_BASE, MV_USB_AWR_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_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) + +/************************************************************************** + * Decode windows helper routines + **************************************************************************/ +void +soc_dump_decode_win(void) +{ + 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_USB_MAX; i++) + printf("USB window#%d: c 0x%08x, b 0x%08x\n", i, + win_usb_cr_read(i), win_usb_br_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(); + printf("\n"); +} + +/************************************************************************** + * CPU windows routines + **************************************************************************/ +int +win_cpu_can_remap(int i) +{ + + /* 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)) + 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); +} + +static void +decode_win_cpu_setup(void) +{ + uint32_t br, cr; + int i; + + /* 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) { + + br = cpu_wins[i].base & 0xffff0000; + win_cpu_br_write(i, br); + + if (win_cpu_can_remap(i)) { + if (cpu_wins[i].remap >= 0) { + win_cpu_remap_l_write(i, + cpu_wins[i].remap & 0xffff0000); + win_cpu_remap_h_write(i, 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(i, + cpu_wins[i].base & 0xffff0000); + win_cpu_remap_h_write(i, 0); + } + } + + cr = ((cpu_wins[i].size - 1) & 0xffff0000) | + (cpu_wins[i].attr << 8) | + (cpu_wins[i].target << 4) | 1; + + win_cpu_cr_write(i, cr); + } +} + +/* + * 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)); +} + +/* + * Set USB decode windows. + */ +static void +decode_win_usb_setup(uint32_t ctrl) +{ + uint32_t br, cr; + int i, j; + + /* Disable and clear all USB windows */ + for (i = 0; i < MV_WIN_USB_MAX; i++) { + win_usb_cr_write(i, 0); + win_usb_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); + /* + * 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) & 0x1) + continue; + + win_usb_br_write(j, br); + win_usb_cr_write(j, 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; + + /* 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; + + /* + * 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 accont 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 diff --git a/sys/arm/mv/discovery/db78xxx.c b/sys/arm/mv/discovery/db78xxx.c new file mode 100644 index 0000000..f472793 --- /dev/null +++ b/sys/arm/mv/discovery/db78xxx.c @@ -0,0 +1,110 @@ +/*- + * 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 <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/pte.h> +#include <machine/pmap.h> +#include <machine/vmparam.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.h> + +/* + * Virtual address space layout: + * ----------------------------- + * 0x0000_0000 - 0xbfff_ffff : user process + * + * 0xc040_0000 - virtual_avail : kernel reserved (text, data, page tables + * : structures, ARM stacks etc.) + * virtual_avail - 0xefff_ffff : KVA (virtual_avail is typically < 0xc0a0_0000) + * 0xf000_0000 - 0xf0ff_ffff : no-cache allocation area (16MB) + * 0xf100_0000 - 0xf10f_ffff : SoC integrated devices registers range (1MB) + * 0xf110_0000 - 0xfffe_ffff : PCIE (MEM+IO) outbound windows (~238MB) + * 0xffff_0000 - 0xffff_0fff : 'high' vectors page (4KB) + * 0xffff_1000 - 0xffff_1fff : ARM_TP_ADDRESS/RAS page (4KB) + * 0xffff_2000 - 0xffff_ffff : unused (~55KB) + */ + +const struct pmap_devmap *pmap_devmap_bootstrap_table; +vm_offset_t pmap_bootstrap_lastaddr; + +/* Static device mappings. */ +static const struct pmap_devmap pmap_devmap[] = { + /* + * Map the on-board devices VA == PA so that we can access them + * with the MMU on or off. + */ + { /* SoC integrated peripherals registers range */ + MV_BASE, + MV_PHYS_BASE, + MV_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { 0, 0, 0, 0, 0, } +}; + +int +platform_pmap_init(void) +{ + + pmap_bootstrap_lastaddr = MV_BASE - ARM_NOCACHE_KVA_SIZE; + pmap_devmap_bootstrap_table = &pmap_devmap[0]; + + return (0); +} + +static void +platform_identify(void *dummy) +{ + + soc_identify(); + + /* + * XXX Board identification e.g. read out from FPGA or similar should + * go here + */ +} +SYSINIT(platform_identify, SI_SUB_CPU, SI_ORDER_SECOND, platform_identify, NULL); + +/* + * TODO routine setting GPIO/MPP pins + */ diff --git a/sys/arm/mv/discovery/discovery.c b/sys/arm/mv/discovery/discovery.c new file mode 100644 index 0000000..33f0bf5 --- /dev/null +++ b/sys/arm/mv/discovery/discovery.c @@ -0,0 +1,160 @@ +/*- + * 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 <machine/bus.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.h> + +struct obio_device obio_devices[] = { + { "ic", MV_IC_BASE, MV_IC_SIZE, + { -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "timer", MV_TIMERS_BASE, MV_TIMERS_SIZE, + { MV_INT_TIMER0, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "gpio", MV_GPIO_BASE, MV_GPIO_SIZE, + { MV_INT_GPIO7_0, MV_INT_GPIO15_8, + MV_INT_GPIO23_16, MV_INT_GPIO31_24, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "uart", MV_UART0_BASE, MV_UART_SIZE, + { MV_INT_UART0, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "uart", MV_UART1_BASE, MV_UART_SIZE, + { MV_INT_UART1, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "idma", MV_IDMA_BASE, MV_IDMA_SIZE, + { MV_INT_IDMA_ERR, MV_INT_IDMA0, MV_INT_IDMA1, + MV_INT_IDMA2, MV_INT_IDMA3, -1 }, + { -1 }, + CPU_PM_CTRL_IDMA + }, + { "xor", MV_XOR_BASE, MV_XOR_SIZE, + { MV_INT_XOR0, MV_INT_XOR1, + MV_INT_XOR_ERR, -1 }, + { -1 }, + CPU_PM_CTRL_XOR + }, + { "ehci", MV_USB0_BASE, MV_USB_SIZE, + { MV_INT_USB_ERR, MV_INT_USB0, -1 }, + { -1 }, + CPU_PM_CTRL_USB0 | CPU_PM_CTRL_USB1 | CPU_PM_CTRL_USB2 + }, + { "mge", MV_ETH0_BASE, MV_ETH_SIZE, + { MV_INT_GBERX, MV_INT_GBETX, MV_INT_GBEMISC, + MV_INT_GBESUM, MV_INT_GBE_ERR, -1 }, + { -1 }, + CPU_PM_CTRL_GE0 + }, + { "mge", MV_ETH1_BASE, MV_ETH_SIZE, + { MV_INT_GBE1RX, MV_INT_GBE1TX, MV_INT_GBE1MISC, + MV_INT_GBE1SUM, MV_INT_GBE_ERR, -1 }, + { -1 }, + CPU_PM_CTRL_GE1 + }, + { "twsi", MV_TWSI_BASE, MV_TWSI_SIZE, + { -1 }, { -1 }, + CPU_PM_CTRL_NONE + }, + { NULL, 0, 0, { 0 }, { 0 }, 0 } +}; + +struct resource_spec mv_gpio_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { SYS_RES_IRQ, 3, RF_ACTIVE }, + { -1, 0 } +}; + +struct resource_spec mv_xor_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { -1, 0 } +}; + +const struct decode_win cpu_win_tbl[] = { + /* PCIE IO */ + { 4, 0x51, MV_PCIE_IO_PHYS_BASE, MV_PCIE_IO_SIZE, -1 }, + + /* PCIE MEM */ + { 4, 0x59, MV_PCIE_MEM_PHYS_BASE, MV_PCIE_MEM_SIZE, -1 }, + + /* Device bus BOOT */ + { 1, 0x2f, MV_DEV_BOOT_PHYS_BASE, MV_DEV_BOOT_SIZE, -1 }, + + /* Device bus CS0 */ + { 1, 0x3e, MV_DEV_CS0_PHYS_BASE, MV_DEV_CS0_SIZE, -1 }, + + /* Device bus CS1 */ + { 1, 0x3d, MV_DEV_CS1_PHYS_BASE, MV_DEV_CS1_SIZE, -1 }, + + /* Device bus CS2 */ + { 1, 0x3b, MV_DEV_CS2_PHYS_BASE, MV_DEV_CS2_SIZE, -1 }, +}; +const struct decode_win *cpu_wins = cpu_win_tbl; +int cpu_wins_no = sizeof(cpu_win_tbl) / sizeof(struct decode_win); + +/* + * Note: the decode windows table for IDMA does not explicitly have DRAM + * entries, which are not statically defined: active DDR banks (== windows) + * are established in run time from actual DDR windows settings. All active + * DDR banks are mapped into IDMA decode windows, so at least one IDMA decode + * window is occupied by the DDR bank; in case when all (MV_WIN_DDR_MAX) + * DDR banks are active, the remaining available IDMA decode windows for other + * targets is only MV_WIN_IDMA_MAX - MV_WIN_DDR_MAX. + */ +const struct decode_win idma_win_tbl[] = { + /* PCIE MEM */ + { 4, 0x59, MV_PCIE_MEM_PHYS_BASE, MV_PCIE_MEM_SIZE, -1 }, +}; +const struct decode_win *idma_wins = idma_win_tbl; +int idma_wins_no = sizeof(idma_win_tbl) / sizeof(struct decode_win); diff --git a/sys/arm/mv/discovery/files.db78xxx b/sys/arm/mv/discovery/files.db78xxx new file mode 100644 index 0000000..b01285f --- /dev/null +++ b/sys/arm/mv/discovery/files.db78xxx @@ -0,0 +1,4 @@ +# $FreeBSD$ + +arm/mv/discovery/discovery.c standard +arm/mv/discovery/db78xxx.c standard diff --git a/sys/arm/mv/discovery/std.db78xxx b/sys/arm/mv/discovery/std.db78xxx new file mode 100644 index 0000000..b2f6049 --- /dev/null +++ b/sys/arm/mv/discovery/std.db78xxx @@ -0,0 +1,13 @@ +# $FreeBSD$ + +include "../mv/std.mv" +files "../mv/discovery/files.db78xxx" + +makeoptions KERNPHYSADDR=0x00900000 +makeoptions KERNVIRTADDR=0xc0900000 + +options KERNPHYSADDR=0x00900000 +options KERNVIRTADDR=0xc0900000 +options PHYSADDR=0x00000000 +options PHYSMEM_SIZE=0x20000000 +options STARTUP_PAGETABLE_ADDR=0x00100000 diff --git a/sys/arm/mv/files.mv b/sys/arm/mv/files.mv new file mode 100644 index 0000000..bba8c83 --- /dev/null +++ b/sys/arm/mv/files.mv @@ -0,0 +1,34 @@ +# $FreeBSD$ +# +# The Marvell CPU cores +# - Compliant with V5TE architecture +# - Super scalar dual issue CPU +# - Big/Little Endian +# - MMU/MPU +# - L1 Cache: Supports streaming and write allocate +# - Variable pipeline stages +# - Out-of-order execution +# - Branch Prediction +# - JTAG/ICE +# - Vector Floating Point (VFP) unit +# +arm/arm/bus_space_generic.c standard +arm/arm/cpufunc_asm_arm10.S standard +arm/arm/cpufunc_asm_armv5_ec.S standard +arm/arm/cpufunc_asm_feroceon.S standard +arm/arm/irq_dispatch.S standard + +arm/mv/bus_space.c standard +arm/mv/common.c standard +arm/mv/gpio.c standard +arm/mv/ic.c standard +arm/mv/mv_machdep.c standard +arm/mv/obio.c standard +arm/mv/timer.c standard +arm/mv/twsi.c optional iicbus + +dev/mge/if_mge.c optional mge +dev/uart/uart_bus_mbus.c optional uart +dev/uart/uart_cpu_mv.c optional uart +dev/uart/uart_dev_ns8250.c optional uart +dev/usb/ehci_mbus.c optional ehci diff --git a/sys/arm/mv/gpio.c b/sys/arm/mv/gpio.c new file mode 100644 index 0000000..bb7f04b --- /dev/null +++ b/sys/arm/mv/gpio.c @@ -0,0 +1,516 @@ +/*- + * Copyright (c) 2006 Benno Rice. + * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. + * All rights reserved. + * + * Adapted and extended for Marvell SoCs 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. + * + * 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. + * + * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_gpio.c, rev 1 + */ + +#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/interrupt.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/rman.h> +#include <sys/queue.h> +#include <sys/timetc.h> +#include <machine/bus.h> +#include <machine/intr.h> + +#include <arm/mv/mvvar.h> +#include <arm/mv/mvreg.h> + +#define GPIO_MAX_INTR_COUNT 8 +#define GPIO_PINS_PER_REG 32 + +struct mv_gpio_softc { + struct resource * res[GPIO_MAX_INTR_COUNT + 1]; + void *ih_cookie[GPIO_MAX_INTR_COUNT]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + uint8_t pin_num; /* number of GPIO pins */ + uint8_t irq_num; /* number of real IRQs occupied by GPIO controller */ + uint8_t use_high; +}; + +extern struct resource_spec mv_gpio_spec[]; + +static struct mv_gpio_softc *mv_gpio_softc = NULL; +static uint32_t gpio_setup[MV_GPIO_MAX_NPINS]; + +static int mv_gpio_probe(device_t); +static int mv_gpio_attach(device_t); +static void mv_gpio_intr(void *); + +static void mv_gpio_intr_handler(int pin); +static uint32_t mv_gpio_reg_read(uint32_t reg); +static void mv_gpio_reg_write(uint32_t reg, uint32_t val); +static void mv_gpio_reg_set(uint32_t reg, uint32_t val); +static void mv_gpio_reg_clear(uint32_t reg, uint32_t val); + +static void mv_gpio_blink(uint32_t pin, uint8_t enable); +static void mv_gpio_polarity(uint32_t pin, uint8_t enable); +static void mv_gpio_level(uint32_t pin, uint8_t enable); +static void mv_gpio_edge(uint32_t pin, uint8_t enable); +static void mv_gpio_out_en(uint32_t pin, uint8_t enable); +static void mv_gpio_int_ack(uint32_t pin); +static void mv_gpio_value_set(uint32_t pin, uint8_t val); +static uint32_t mv_gpio_value_get(uint32_t pin); + +static device_method_t mv_gpio_methods[] = { + DEVMETHOD(device_probe, mv_gpio_probe), + DEVMETHOD(device_attach, mv_gpio_attach), + { 0, 0 } +}; + +static driver_t mv_gpio_driver = { + "gpio", + mv_gpio_methods, + sizeof(struct mv_gpio_softc), +}; + +static devclass_t mv_gpio_devclass; + +DRIVER_MODULE(gpio, mbus, mv_gpio_driver, mv_gpio_devclass, 0, 0); + +static int +mv_gpio_probe(device_t dev) +{ + + device_set_desc(dev, "Marvell Integrated GPIO Controller"); + return (0); +} + +static int +mv_gpio_attach(device_t dev) +{ + int error, i; + struct mv_gpio_softc *sc; + uint32_t dev_id, rev_id; + + sc = (struct mv_gpio_softc *)device_get_softc(dev); + + if (mv_gpio_softc != NULL) + return (ENXIO); + mv_gpio_softc = sc; + + /* Get board id and revision */ + soc_id(&dev_id, &rev_id); + + if (dev_id == MV_DEV_88F5182 || + dev_id == MV_DEV_88F5281 || + dev_id == MV_DEV_MV78100) { + sc->pin_num = 32; + sc->irq_num = 4; + sc->use_high = 0; + + } else if (dev_id == MV_DEV_88F6281) { + sc->pin_num = 50; + sc->irq_num = 7; + sc->use_high = 1; + + } else { + device_printf(dev, "unknown board id=0x%x\n", dev_id); + return (ENXIO); + } + + error = bus_alloc_resources(dev, mv_gpio_spec, sc->res); + if (error) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + /* Disable and clear all interrupts */ + bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_EDGE_MASK, 0); + bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_LEV_MASK, 0); + bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_CAUSE, 0); + + if (sc->use_high) { + bus_space_write_4(sc->bst, sc->bsh, + GPIO_HI_INT_EDGE_MASK, 0); + bus_space_write_4(sc->bst, sc->bsh, + GPIO_HI_INT_LEV_MASK, 0); + bus_space_write_4(sc->bst, sc->bsh, + GPIO_HI_INT_CAUSE, 0); + } + + for (i = 0; i < sc->irq_num; i++) { + if (bus_setup_intr(dev, sc->res[1 + i], + INTR_TYPE_MISC | INTR_FAST, + (driver_filter_t *)mv_gpio_intr, NULL, + sc, &sc->ih_cookie[i]) != 0) { + bus_release_resources(dev, mv_gpio_spec, sc->res); + device_printf(dev, "could not set up intr %d\n", i); + return (ENXIO); + } + } + + return (0); +} + +static void +mv_gpio_intr(void *arg) +{ + uint32_t int_cause, gpio_val; + uint32_t int_cause_hi, gpio_val_hi = 0; + int i; + + int_cause = mv_gpio_reg_read(GPIO_INT_CAUSE); + gpio_val = mv_gpio_reg_read(GPIO_DATA_IN); + gpio_val &= int_cause; + if (mv_gpio_softc->use_high) { + int_cause_hi = mv_gpio_reg_read(GPIO_HI_INT_CAUSE); + gpio_val_hi = mv_gpio_reg_read(GPIO_HI_DATA_IN); + gpio_val_hi &= int_cause_hi; + } + + i = 0; + while (gpio_val != 0) { + if (gpio_val & 1) + mv_gpio_intr_handler(i); + gpio_val >>= 1; + i++; + } + + if (mv_gpio_softc->use_high) { + i = 0; + while (gpio_val_hi != 0) { + if (gpio_val_hi & 1) + mv_gpio_intr_handler(i + GPIO_PINS_PER_REG); + gpio_val_hi >>= 1; + i++; + } + } +} + +/* + * GPIO interrupt handling + */ + +static struct intr_event *gpio_events[MV_GPIO_MAX_NPINS]; + +int +mv_gpio_setup_intrhandler(const char *name, driver_filter_t *filt, + void (*hand)(void *), void *arg, int pin, int flags, void **cookiep) +{ + struct intr_event *event; + int error; + + if (pin < 0 || pin >= mv_gpio_softc->pin_num) + return (ENXIO); + event = gpio_events[pin]; + if (event == NULL) { + error = intr_event_create(&event, (void *)pin, 0, pin, + (void (*)(void *))mv_gpio_intr_mask, + (void (*)(void *))mv_gpio_intr_unmask, + (void (*)(void *))mv_gpio_int_ack, + NULL, + "gpio%d:", pin); + if (error != 0) + return (error); + gpio_events[pin] = event; + } + + intr_event_add_handler(event, name, filt, hand, arg, intr_priority(flags), + flags, cookiep); + return (0); +} + +void +mv_gpio_intr_mask(int pin) +{ + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (gpio_setup[pin] & MV_GPIO_EDGE) + mv_gpio_edge(pin, 0); + else + mv_gpio_level(pin, 0); +} + +void +mv_gpio_intr_unmask(int pin) +{ + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (gpio_setup[pin] & MV_GPIO_EDGE) + mv_gpio_edge(pin, 1); + else + mv_gpio_level(pin, 1); +} + +static void +mv_gpio_intr_handler(int pin) +{ + struct intr_event *event; + + event = gpio_events[pin]; + if (event == NULL || TAILQ_EMPTY(&event->ie_handlers)) + return; + + intr_event_handle(event, NULL); +} + +int +mv_gpio_configure(uint32_t pin, uint32_t flags, uint32_t mask) +{ + + if (pin >= mv_gpio_softc->pin_num) + return (EINVAL); + + if (mask & MV_GPIO_BLINK) + mv_gpio_blink(pin, flags & MV_GPIO_BLINK); + if (mask & MV_GPIO_POLARITY) + mv_gpio_polarity(pin, flags & MV_GPIO_POLARITY); + if (mask & MV_GPIO_EDGE) + mv_gpio_edge(pin, flags & MV_GPIO_EDGE); + if (mask & MV_GPIO_LEVEL) + mv_gpio_level(pin, flags & MV_GPIO_LEVEL); + + gpio_setup[pin] &= ~(mask); + gpio_setup[pin] |= (flags & mask); + + return (0); +} + +void +mv_gpio_out(uint32_t pin, uint8_t val, uint8_t enable) +{ + + mv_gpio_value_set(pin, val); + mv_gpio_out_en(pin, enable); +} + +uint8_t +mv_gpio_in(uint32_t pin) +{ + + return (mv_gpio_value_get(pin)); +} + +static uint32_t +mv_gpio_reg_read(uint32_t reg) +{ + + return (bus_space_read_4(mv_gpio_softc->bst, + mv_gpio_softc->bsh, reg)); +} + +static void +mv_gpio_reg_write(uint32_t reg, uint32_t val) +{ + + bus_space_write_4(mv_gpio_softc->bst, + mv_gpio_softc->bsh, reg, val); +} + +static void +mv_gpio_reg_set(uint32_t reg, uint32_t pin) +{ + uint32_t reg_val; + + reg_val = mv_gpio_reg_read(reg); + reg_val |= GPIO(pin); + mv_gpio_reg_write(reg, reg_val); +} + +static void +mv_gpio_reg_clear(uint32_t reg, uint32_t pin) +{ + uint32_t reg_val; + + reg_val = mv_gpio_reg_read(reg); + reg_val &= ~(GPIO(pin)); + mv_gpio_reg_write(reg, reg_val); +} + +static void +mv_gpio_out_en(uint32_t pin, uint8_t enable) +{ + uint32_t reg; + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (pin >= GPIO_PINS_PER_REG) { + reg = GPIO_HI_DATA_OUT_EN_CTRL; + pin -= GPIO_PINS_PER_REG; + } else + reg = GPIO_DATA_OUT_EN_CTRL; + + if (enable) + mv_gpio_reg_clear(reg, pin); + else + mv_gpio_reg_set(reg, pin); +} + +static void +mv_gpio_blink(uint32_t pin, uint8_t enable) +{ + uint32_t reg; + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (pin >= GPIO_PINS_PER_REG) { + reg = GPIO_HI_BLINK_EN; + pin -= GPIO_PINS_PER_REG; + } else + reg = GPIO_BLINK_EN; + + if (enable) + mv_gpio_reg_set(reg, pin); + else + mv_gpio_reg_clear(reg, pin); +} + +static void +mv_gpio_polarity(uint32_t pin, uint8_t enable) +{ + uint32_t reg; + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (pin >= GPIO_PINS_PER_REG) { + reg = GPIO_HI_DATA_IN_POLAR; + pin -= GPIO_PINS_PER_REG; + } else + reg = GPIO_DATA_IN_POLAR; + + if (enable) + mv_gpio_reg_set(reg, pin); + else + mv_gpio_reg_clear(reg, pin); +} + +static void +mv_gpio_level(uint32_t pin, uint8_t enable) +{ + uint32_t reg; + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (pin >= GPIO_PINS_PER_REG) { + reg = GPIO_HI_INT_LEV_MASK; + pin -= GPIO_PINS_PER_REG; + } else + reg = GPIO_INT_LEV_MASK; + + if (enable) + mv_gpio_reg_set(reg, pin); + else + mv_gpio_reg_clear(reg, pin); +} + +static void +mv_gpio_edge(uint32_t pin, uint8_t enable) +{ + uint32_t reg; + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (pin >= GPIO_PINS_PER_REG) { + reg = GPIO_HI_INT_EDGE_MASK; + pin -= GPIO_PINS_PER_REG; + } else + reg = GPIO_INT_EDGE_MASK; + + if (enable) + mv_gpio_reg_set(reg, pin); + else + mv_gpio_reg_clear(reg, pin); +} + +static void +mv_gpio_int_ack(uint32_t pin) +{ + uint32_t reg; + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (pin >= GPIO_PINS_PER_REG) { + reg = GPIO_HI_INT_CAUSE; + pin -= GPIO_PINS_PER_REG; + } else + reg = GPIO_INT_CAUSE; + + mv_gpio_reg_clear(reg, pin); +} + +static uint32_t +mv_gpio_value_get(uint32_t pin) +{ + uint32_t reg, reg_val; + + if (pin >= mv_gpio_softc->pin_num) + return (0); + + if (pin >= GPIO_PINS_PER_REG) { + reg = GPIO_HI_DATA_IN; + pin -= GPIO_PINS_PER_REG; + } else + reg = GPIO_DATA_IN; + + reg_val = mv_gpio_reg_read(reg); + + return (reg_val & GPIO(pin)); +} + +static void +mv_gpio_value_set(uint32_t pin, uint8_t val) +{ + uint32_t reg; + + if (pin >= mv_gpio_softc->pin_num) + return; + + if (pin >= GPIO_PINS_PER_REG) { + reg = GPIO_HI_DATA_OUT; + pin -= GPIO_PINS_PER_REG; + } else + reg = GPIO_DATA_OUT; + + if (val) + mv_gpio_reg_set(reg, pin); + else + mv_gpio_reg_clear(reg, pin); +} diff --git a/sys/arm/mv/ic.c b/sys/arm/mv/ic.c new file mode 100644 index 0000000..47db7c4 --- /dev/null +++ b/sys/arm/mv/ic.c @@ -0,0 +1,289 @@ +/*- + * Copyright (c) 2006 Benno Rice. + * Copyright (C) 2007-2008 MARVELL INTERNATIONAL LTD. + * All rights reserved. + * + * Adapted and extended to Marvell SoCs 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. + * + * 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. + * + * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_icu.c, rev 1 + */ + +#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 <machine/bus.h> +#include <machine/intr.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.h> + +struct mv_ic_softc { + struct resource * ic_res[1]; + bus_space_tag_t ic_bst; + bus_space_handle_t ic_bsh; + int ic_high_regs; + int ic_error_regs; +}; + +static struct resource_spec mv_ic_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static struct mv_ic_softc *mv_ic_sc = NULL; + +static int mv_ic_probe(device_t); +static int mv_ic_attach(device_t); + +uint32_t mv_ic_get_cause(void); +uint32_t mv_ic_get_mask(void); +void mv_ic_set_mask(uint32_t); +uint32_t mv_ic_get_cause_hi(void); +uint32_t mv_ic_get_mask_hi(void); +void mv_ic_set_mask_hi(uint32_t); +uint32_t mv_ic_get_cause_error(void); +uint32_t mv_ic_get_mask_error(void); +void mv_ic_set_mask_error(uint32_t); +static void arm_mask_irq_all(void); + +static int +mv_ic_probe(device_t dev) +{ + + device_set_desc(dev, "Marvell Integrated Interrupt Controller"); + return (0); +} + +static int +mv_ic_attach(device_t dev) +{ + struct mv_ic_softc *sc; + uint32_t dev_id, rev_id; + int error; + + sc = (struct mv_ic_softc *)device_get_softc(dev); + + if (mv_ic_sc != NULL) + return (ENXIO); + mv_ic_sc = sc; + + soc_id(&dev_id, &rev_id); + + sc->ic_high_regs = 0; + sc->ic_error_regs = 0; + + if (dev_id == MV_DEV_88F6281 || dev_id == MV_DEV_MV78100) + sc->ic_high_regs = 1; + + if (dev_id == MV_DEV_MV78100) + sc->ic_error_regs = 1; + + error = bus_alloc_resources(dev, mv_ic_spec, sc->ic_res); + if (error) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->ic_bst = rman_get_bustag(sc->ic_res[0]); + sc->ic_bsh = rman_get_bushandle(sc->ic_res[0]); + + /* Mask all interrupts */ + arm_mask_irq_all(); + + return (0); +} + +static device_method_t mv_ic_methods[] = { + DEVMETHOD(device_probe, mv_ic_probe), + DEVMETHOD(device_attach, mv_ic_attach), + { 0, 0 } +}; + +static driver_t mv_ic_driver = { + "ic", + mv_ic_methods, + sizeof(struct mv_ic_softc), +}; + +static devclass_t mv_ic_devclass; + +DRIVER_MODULE(ic, mbus, mv_ic_driver, mv_ic_devclass, 0, 0); + +int +arm_get_next_irq(void) +{ + int irq; + + irq = mv_ic_get_cause() & mv_ic_get_mask(); + if (irq) + return (ffs(irq) - 1); + + if (mv_ic_sc->ic_high_regs) { + irq = mv_ic_get_cause_hi() & mv_ic_get_mask_hi(); + if (irq) + return (ffs(irq) + 31); + } + + if (mv_ic_sc->ic_error_regs) { + irq = mv_ic_get_cause_error() & mv_ic_get_mask_error(); + if (irq) + return (ffs(irq) + 63); + } + + return (-1); +} + +static void +arm_mask_irq_all(void) +{ + + mv_ic_set_mask(0); + + if (mv_ic_sc->ic_high_regs) + mv_ic_set_mask_hi(0); + + if (mv_ic_sc->ic_error_regs) + mv_ic_set_mask_error(0); +} + +void +arm_mask_irq(uintptr_t nb) +{ + uint32_t mr; + + if (nb < 32) { + mr = mv_ic_get_mask(); + mr &= ~(1 << nb); + mv_ic_set_mask(mr); + + } else if ((nb < 64) && mv_ic_sc->ic_high_regs) { + mr = mv_ic_get_mask_hi(); + mr &= ~(1 << (nb - 32)); + mv_ic_set_mask_hi(mr); + + } else if ((nb < 96) && mv_ic_sc->ic_error_regs) { + mr = mv_ic_get_mask_error(); + mr &= ~(1 << (nb - 64)); + mv_ic_set_mask_error(mr); + } +} + +void +arm_unmask_irq(uintptr_t nb) +{ + uint32_t mr; + + if (nb < 32) { + mr = mv_ic_get_mask(); + mr |= (1 << nb); + mv_ic_set_mask(mr); + + } else if ((nb < 64) && mv_ic_sc->ic_high_regs) { + mr = mv_ic_get_mask_hi(); + mr |= (1 << (nb - 32)); + mv_ic_set_mask_hi(mr); + + } else if ((nb < 96) && mv_ic_sc->ic_error_regs) { + mr = mv_ic_get_mask_error(); + mr |= (1 << (nb - 64)); + mv_ic_set_mask_error(mr); + } +} + +void +mv_ic_set_mask(uint32_t val) +{ + + bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh, + IRQ_MASK, val); +} + +uint32_t +mv_ic_get_mask(void) +{ + + return (bus_space_read_4(mv_ic_sc->ic_bst, + mv_ic_sc->ic_bsh, IRQ_MASK)); +} + +uint32_t +mv_ic_get_cause(void) +{ + + return (bus_space_read_4(mv_ic_sc->ic_bst, + mv_ic_sc->ic_bsh, IRQ_CAUSE)); +} + +void +mv_ic_set_mask_hi(uint32_t val) +{ + + bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh, + IRQ_MASK_HI, val); +} + +uint32_t +mv_ic_get_mask_hi(void) +{ + + return (bus_space_read_4(mv_ic_sc->ic_bst, + mv_ic_sc->ic_bsh, IRQ_MASK_HI)); +} + +uint32_t +mv_ic_get_cause_hi(void) +{ + + return (bus_space_read_4(mv_ic_sc->ic_bst, + mv_ic_sc->ic_bsh, IRQ_CAUSE_HI)); +} + +void +mv_ic_set_mask_error(uint32_t val) +{ + + bus_space_write_4(mv_ic_sc->ic_bst, mv_ic_sc->ic_bsh, + IRQ_MASK_ERROR, val); +} + +uint32_t +mv_ic_get_mask_error(void) +{ + + return (bus_space_read_4(mv_ic_sc->ic_bst, + mv_ic_sc->ic_bsh, IRQ_MASK_ERROR)); +} + +uint32_t +mv_ic_get_cause_error(void) +{ + + return (bus_space_read_4(mv_ic_sc->ic_bst, + mv_ic_sc->ic_bsh, IRQ_CAUSE_ERROR)); +} diff --git a/sys/arm/mv/kirkwood/db88f6xxx.c b/sys/arm/mv/kirkwood/db88f6xxx.c new file mode 100644 index 0000000..8db9258 --- /dev/null +++ b/sys/arm/mv/kirkwood/db88f6xxx.c @@ -0,0 +1,124 @@ +/*- + * 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 <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/pte.h> +#include <machine/pmap.h> +#include <machine/vmparam.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.h> + +/* + * Virtual address space layout: + * ----------------------------- + * 0x0000_0000 - 0xbfff_ffff : user process + * + * 0xc040_0000 - virtual_avail : kernel reserved (text, data, page tables + * : structures, ARM stacks etc.) + * virtual_avail - 0xefff_ffff : KVA (virtual_avail is typically < 0xc0a0_0000) + * 0xf000_0000 - 0xf0ff_ffff : no-cache allocation area (16MB) + * 0xf100_0000 - 0xf10f_ffff : SoC integrated devices registers range (1MB) + * 0xf110_0000 - 0xfffe_ffff : PCIE (MEM+IO) outbound windows (~238MB) + * 0xffff_0000 - 0xffff_0fff : 'high' vectors page (4KB) + * 0xffff_1000 - 0xffff_1fff : ARM_TP_ADDRESS/RAS page (4KB) + * 0xffff_2000 - 0xffff_ffff : unused (~55KB) + */ + +const struct pmap_devmap *pmap_devmap_bootstrap_table; +vm_offset_t pmap_bootstrap_lastaddr; + +/* Static device mappings. */ +static const struct pmap_devmap pmap_devmap[] = { + /* + * Map the on-board devices VA == PA so that we can access them + * with the MMU on or off. + */ + { /* SoC integrated peripherals registers range */ + MV_BASE, + MV_PHYS_BASE, + MV_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { /* PCIE I/O */ + MV_PCIE_IO_BASE, + MV_PCIE_IO_PHYS_BASE, + MV_PCIE_IO_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { /* PCIE Memory */ + MV_PCIE_MEM_BASE, + MV_PCIE_MEM_PHYS_BASE, + MV_PCIE_MEM_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { 0, 0, 0, 0, 0, } +}; + +int +platform_pmap_init(void) +{ + + pmap_bootstrap_lastaddr = MV_BASE - ARM_NOCACHE_KVA_SIZE; + pmap_devmap_bootstrap_table = &pmap_devmap[0]; + + return (0); +} + +static void +platform_identify(void *dummy) +{ + + soc_identify(); + + /* + * XXX Board identification e.g. read out from FPGA or similar should + * go here + */ +} +SYSINIT(platform_identify, SI_SUB_CPU, SI_ORDER_SECOND, platform_identify, NULL); + +/* + * TODO routine setting GPIO/MPP pins + */ diff --git a/sys/arm/mv/kirkwood/files.db88f6xxx b/sys/arm/mv/kirkwood/files.db88f6xxx new file mode 100644 index 0000000..c0de03c --- /dev/null +++ b/sys/arm/mv/kirkwood/files.db88f6xxx @@ -0,0 +1,5 @@ +# $FreeBSD$ + +arm/mv/rtc.c standard +arm/mv/kirkwood/kirkwood.c standard +arm/mv/kirkwood/db88f6xxx.c standard diff --git a/sys/arm/mv/kirkwood/kirkwood.c b/sys/arm/mv/kirkwood/kirkwood.c new file mode 100644 index 0000000..416c141 --- /dev/null +++ b/sys/arm/mv/kirkwood/kirkwood.c @@ -0,0 +1,161 @@ +/*- + * 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 <machine/bus.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.h> + +struct obio_device obio_devices[] = { + { "ic", MV_IC_BASE, MV_IC_SIZE, + { -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "timer", MV_TIMERS_BASE, MV_TIMERS_SIZE, + { MV_INT_BRIDGE, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "rtc", MV_RTC_BASE, MV_RTC_SIZE, + { -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "gpio", MV_GPIO_BASE, MV_GPIO_SIZE, + { MV_INT_GPIO7_0, MV_INT_GPIO15_8, + MV_INT_GPIO23_16, MV_INT_GPIO31_24, + MV_INT_GPIOHI7_0, MV_INT_GPIOHI15_8, + MV_INT_GPIOHI23_16, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "uart", MV_UART0_BASE, MV_UART_SIZE, + { MV_INT_UART0, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "uart", MV_UART1_BASE, MV_UART_SIZE, + { MV_INT_UART1, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "xor", MV_XOR_BASE, MV_XOR_SIZE, + { MV_INT_XOR0_CHAN0, MV_INT_XOR0_CHAN1, + MV_INT_XOR1_CHAN0, MV_INT_XOR1_CHAN1, + MV_INT_XOR0_ERR, MV_INT_XOR1_ERR, + -1 }, + { -1 }, + CPU_PM_CTRL_XOR0 | CPU_PM_CTRL_XOR1 + }, + { "ehci", MV_USB0_BASE, MV_USB_SIZE, + { MV_INT_USB_BERR, MV_INT_USB_CI, -1 }, + { -1 }, + CPU_PM_CTRL_USB0 + }, + { "mge", MV_ETH0_BASE, MV_ETH_SIZE, + { MV_INT_GBERX, MV_INT_GBETX, MV_INT_GBEMISC, + MV_INT_GBESUM, MV_INT_GBEERR, -1 }, + { -1 }, + CPU_PM_CTRL_GE0 + }, + { "twsi", MV_TWSI_BASE, MV_TWSI_SIZE, + { -1 }, { -1 }, + CPU_PM_CTRL_NONE + }, + { "pcib", MV_PCIE_BASE, MV_PCIE_SIZE, + { MV_INT_PEX0_ERR, -1 }, + { -1 }, + CPU_PM_CTRL_PEX0 + }, + { NULL, 0, 0, { 0 }, { 0 }, 0 } +}; + +#if 0 +const struct mv_pci_info pci_info[] = { + { 1, MV_PCIE_IO_BASE, MV_PCIE_IO_SIZE, + MV_PCIE_MEM_BASE, MV_PCIE_MEM_SIZE, + NULL, MV_INT_PEX0 + } +}; +#endif + +struct resource_spec mv_gpio_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { SYS_RES_IRQ, 3, RF_ACTIVE }, + { SYS_RES_IRQ, 4, RF_ACTIVE }, + { SYS_RES_IRQ, 5, RF_ACTIVE }, + { SYS_RES_IRQ, 6, RF_ACTIVE }, + { -1, 0 } +}; + +struct resource_spec mv_xor_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { SYS_RES_IRQ, 3, RF_ACTIVE }, + { SYS_RES_IRQ, 4, RF_ACTIVE }, + { SYS_RES_IRQ, 5, RF_ACTIVE }, + { -1, 0 } +}; + +const struct decode_win cpu_win_tbl[] = { + /* PCIE IO */ + { 4, 0xE0, MV_PCIE_IO_PHYS_BASE, MV_PCIE_IO_SIZE, -1 }, + + /* PCIE MEM */ + { 4, 0xE8, MV_PCIE_MEM_PHYS_BASE, MV_PCIE_MEM_SIZE, -1 }, + + /* Device bus BOOT */ + { 1, 0x0f, MV_DEV_BOOT_PHYS_BASE, MV_DEV_BOOT_SIZE, -1 }, + + /* Device bus CS0 */ + { 1, 0x1e, MV_DEV_CS0_PHYS_BASE, MV_DEV_CS0_SIZE, -1 }, + + /* Device bus CS1 */ + { 1, 0x1d, MV_DEV_CS1_PHYS_BASE, MV_DEV_CS1_SIZE, -1 }, + + /* Device bus CS2 */ + { 1, 0x1b, MV_DEV_CS2_PHYS_BASE, MV_DEV_CS2_SIZE, -1 }, +}; +const struct decode_win *cpu_wins = cpu_win_tbl; +int cpu_wins_no = sizeof(cpu_win_tbl) / sizeof(struct decode_win); diff --git a/sys/arm/mv/kirkwood/std.db88f6xxx b/sys/arm/mv/kirkwood/std.db88f6xxx new file mode 100644 index 0000000..84af354 --- /dev/null +++ b/sys/arm/mv/kirkwood/std.db88f6xxx @@ -0,0 +1,13 @@ +# $FreeBSD$ + +include "../mv/std.mv" +files "../mv/kirkwood/files.db88f6xxx" + +makeoptions KERNPHYSADDR=0x00900000 +makeoptions KERNVIRTADDR=0xc0900000 + +options KERNPHYSADDR=0x00900000 +options KERNVIRTADDR=0xc0900000 +options PHYSADDR=0x00000000 +options PHYSMEM_SIZE=0x20000000 +options STARTUP_PAGETABLE_ADDR=0x00100000 diff --git a/sys/arm/mv/mv_machdep.c b/sys/arm/mv/mv_machdep.c new file mode 100644 index 0000000..fa5ddbd --- /dev/null +++ b/sys/arm/mv/mv_machdep.c @@ -0,0 +1,643 @@ +/*- + * Copyright (c) 1994-1998 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * from: FreeBSD: //depot/projects/arm/src/sys/arm/at91/kb920x_machdep.c, rev 45 + */ + +#include "opt_msgbuf.h" +#include "opt_ddb.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#define _ARM32_BUS_DMA_PRIVATE +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sysproto.h> +#include <sys/signalvar.h> +#include <sys/imgact.h> +#include <sys/kernel.h> +#include <sys/ktr.h> +#include <sys/linker.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/pcpu.h> +#include <sys/proc.h> +#include <sys/ptrace.h> +#include <sys/cons.h> +#include <sys/bio.h> +#include <sys/bus.h> +#include <sys/buf.h> +#include <sys/exec.h> +#include <sys/kdb.h> +#include <sys/msgbuf.h> +#include <machine/reg.h> +#include <machine/cpu.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_object.h> +#include <vm/vm_page.h> +#include <vm/vm_pager.h> +#include <vm/vm_map.h> +#include <vm/vnode_pager.h> +#include <machine/pte.h> +#include <machine/pmap.h> +#include <machine/vmparam.h> +#include <machine/pcb.h> +#include <machine/undefined.h> +#include <machine/machdep.h> +#include <machine/metadata.h> +#include <machine/armreg.h> +#include <machine/bus.h> +#include <sys/reboot.h> +#include <machine/bootinfo.h> + +#include <arm/mv/mvvar.h> /* XXX eventually this should be eliminated */ + +#ifdef DEBUG +#define debugf(fmt, args...) printf(fmt, ##args) +#else +#define debugf(fmt, args...) +#endif + +#define KERNEL_PT_SYS 0 /* Page table for mapping proc0 zero page */ +#define KERNEL_PT_KERN 1 + +/* + * This is the number of L2 page tables required for covering max + * (hypothetical) memsize of 4GB and all kernel mappings (vectors, msgbuf, + * stacks etc.), uprounded to be divisible by 4. + */ +#define KERNEL_PT_MAX 78 + +/* Define various stack sizes in pages */ +#define IRQ_STACK_SIZE 1 +#define ABT_STACK_SIZE 1 +#define UND_STACK_SIZE 1 + +/* Maximum number of memory regions */ +#define MEM_REGIONS 8 + +extern unsigned char kernbase[]; +extern unsigned char _etext[]; +extern unsigned char _edata[]; +extern unsigned char __bss_start[]; +extern unsigned char _end[]; + +extern u_int data_abort_handler_address; +extern u_int prefetch_abort_handler_address; +extern u_int undefined_handler_address; + +extern const struct pmap_devmap *pmap_devmap_bootstrap_table; +extern vm_offset_t pmap_bootstrap_lastaddr; + +struct pv_addr kernel_pt_table[KERNEL_PT_MAX]; + +extern int *end; + +struct pcpu __pcpu; +struct pcpu *pcpup = &__pcpu; + +/* Physical and virtual addresses for some global pages */ + +vm_paddr_t phys_avail[10]; +vm_paddr_t dump_avail[4]; +vm_offset_t physical_pages; + +struct pv_addr systempage; +struct pv_addr msgbufpv; +struct pv_addr irqstack; +struct pv_addr undstack; +struct pv_addr abtstack; +struct pv_addr kernelstack; + +static struct trapframe proc0_tf; + +struct mem_region { + vm_offset_t mr_start; + vm_size_t mr_size; +}; + +static struct mem_region availmem_regions[MEM_REGIONS]; +static int availmem_regions_sz; + +struct bootinfo *bootinfo; + +static void print_kenv(void); +static void print_kernel_section_addr(void); +static void print_bootinfo(void); + +static void physmap_init(int); + +static char * +kenv_next(char *cp) +{ + + if (cp != NULL) { + while (*cp != 0) + cp++; + cp++; + if (*cp == 0) + cp = NULL; + } + return (cp); +} + +static void +print_kenv(void) +{ + int len; + char *cp; + + debugf("loader passed (static) kenv:\n"); + if (kern_envp == NULL) { + debugf(" no env, null ptr\n"); + return; + } + debugf(" kern_envp = 0x%08x\n", (uint32_t)kern_envp); + + len = 0; + for (cp = kern_envp; cp != NULL; cp = kenv_next(cp)) + debugf(" %x %s\n", (uint32_t)cp, cp); +} + +static void +print_bootinfo(void) +{ + struct bi_mem_region *mr; + struct bi_eth_addr *eth; + int i, j; + + debugf("bootinfo:\n"); + if (bootinfo == NULL) { + debugf(" no bootinfo, null ptr\n"); + return; + } + + debugf(" version = 0x%08x\n", bootinfo->bi_version); + debugf(" ccsrbar = 0x%08x\n", bootinfo->bi_bar_base); + debugf(" cpu_clk = 0x%08x\n", bootinfo->bi_cpu_clk); + debugf(" bus_clk = 0x%08x\n", bootinfo->bi_bus_clk); + + debugf(" mem regions:\n"); + mr = (struct bi_mem_region *)bootinfo->bi_data; + for (i = 0; i < bootinfo->bi_mem_reg_no; i++, mr++) + debugf(" #%d, base = 0x%08x, size = 0x%08x\n", i, + mr->mem_base, mr->mem_size); + + debugf(" eth addresses:\n"); + eth = (struct bi_eth_addr *)mr; + for (i = 0; i < bootinfo->bi_eth_addr_no; i++, eth++) { + debugf(" #%d, addr = ", i); + for (j = 0; j < 6; j++) + debugf("%02x ", eth->mac_addr[j]); + debugf("\n"); + } +} + +static void +print_kernel_section_addr(void) +{ + + debugf("kernel image addresses:\n"); + debugf(" kernbase = 0x%08x\n", (uint32_t)kernbase); + debugf(" _etext (sdata) = 0x%08x\n", (uint32_t)_etext); + debugf(" _edata = 0x%08x\n", (uint32_t)_edata); + debugf(" __bss_start = 0x%08x\n", (uint32_t)__bss_start); + debugf(" _end = 0x%08x\n", (uint32_t)_end); +} + +struct bi_mem_region * +bootinfo_mr(void) +{ + + return ((struct bi_mem_region *)bootinfo->bi_data); +} + +static void +physmap_init(int hardcoded) +{ + int i, j, cnt; + vm_offset_t phys_kernelend, kernload; + uint32_t s, e, sz; + struct mem_region *mp, *mp1; + + phys_kernelend = KERNPHYSADDR + (virtual_avail - KERNVIRTADDR); + kernload = KERNPHYSADDR; + + /* + * Use hardcoded physical addresses if we don't use memory regions + * from metadata. + */ + if (hardcoded) { + phys_avail[0] = 0; + phys_avail[1] = kernload; + + phys_avail[2] = phys_kernelend; + phys_avail[3] = PHYSMEM_SIZE; + + phys_avail[4] = 0; + phys_avail[5] = 0; + return; + } + + /* + * Remove kernel physical address range from avail + * regions list. Page align all regions. + * Non-page aligned memory isn't very interesting to us. + * Also, sort the entries for ascending addresses. + */ + sz = 0; + cnt = availmem_regions_sz; + debugf("processing avail regions:\n"); + for (mp = availmem_regions; mp->mr_size; mp++) { + s = mp->mr_start; + e = mp->mr_start + mp->mr_size; + debugf(" %08x-%08x -> ", s, e); + /* Check whether this region holds all of the kernel. */ + if (s < kernload && e > phys_kernelend) { + availmem_regions[cnt].mr_start = phys_kernelend; + availmem_regions[cnt++].mr_size = e - phys_kernelend; + e = kernload; + } + /* Look whether this regions starts within the kernel. */ + if (s >= kernload && s < phys_kernelend) { + if (e <= phys_kernelend) + goto empty; + s = phys_kernelend; + } + /* Now look whether this region ends within the kernel. */ + if (e > kernload && e <= phys_kernelend) { + if (s >= kernload) { + goto empty; + } + e = kernload; + } + /* Now page align the start and size of the region. */ + s = round_page(s); + e = trunc_page(e); + if (e < s) + e = s; + sz = e - s; + debugf("%08x-%08x = %x\n", s, e, sz); + + /* Check whether some memory is left here. */ + if (sz == 0) { + empty: + printf("skipping\n"); + bcopy(mp + 1, mp, + (cnt - (mp - availmem_regions)) * sizeof(*mp)); + cnt--; + mp--; + continue; + } + + /* Do an insertion sort. */ + for (mp1 = availmem_regions; mp1 < mp; mp1++) + if (s < mp1->mr_start) + break; + if (mp1 < mp) { + bcopy(mp1, mp1 + 1, (char *)mp - (char *)mp1); + mp1->mr_start = s; + mp1->mr_size = sz; + } else { + mp->mr_start = s; + mp->mr_size = sz; + } + } + availmem_regions_sz = cnt; + + /* Fill in phys_avail table, based on availmem_regions */ + debugf("fill in phys_avail:\n"); + for (i = 0, j = 0; i < availmem_regions_sz; i++, j += 2) { + + debugf(" region: 0x%08x - 0x%08x (0x%08x)\n", + availmem_regions[i].mr_start, + availmem_regions[i].mr_start + availmem_regions[i].mr_size, + availmem_regions[i].mr_size); + + phys_avail[j] = availmem_regions[i].mr_start; + phys_avail[j + 1] = availmem_regions[i].mr_start + + availmem_regions[i].mr_size; + } + phys_avail[j] = 0; + phys_avail[j + 1] = 0; +} + +void * +initarm(void *mdp, void *unused __unused) +{ + struct pv_addr kernel_l1pt; + vm_offset_t freemempos, l2_start, lastaddr; + uint32_t memsize, l2size; + struct bi_mem_region *mr; + void *kmdp; + u_int l1pagetable; + int i = 0; + + kmdp = NULL; + lastaddr = 0; + memsize = 0; + + set_cpufuncs(); + + /* + * Mask metadata pointer: it is supposed to be on page boundary. If + * the first argument (mdp) doesn't point to a valid address the + * bootloader must have passed us something else than the metadata + * ptr... In this case we want to fall back to some built-in settings. + */ + mdp = (void *)((uint32_t)mdp & ~PAGE_MASK); + + /* Parse metadata and fetch parameters */ + if (mdp != NULL) { + preload_metadata = mdp; + kmdp = preload_search_by_type("elf kernel"); + if (kmdp != NULL) { + bootinfo = (struct bootinfo *)preload_search_info(kmdp, + MODINFO_METADATA|MODINFOMD_BOOTINFO); + + boothowto = MD_FETCH(kmdp, MODINFOMD_HOWTO, int); + kern_envp = MD_FETCH(kmdp, MODINFOMD_ENVP, char *); + lastaddr = MD_FETCH(kmdp, MODINFOMD_KERNEND, vm_offset_t); + } + + /* Initialize memory regions table */ + mr = bootinfo_mr(); + for (i = 0; i < bootinfo->bi_mem_reg_no; i++, mr++) { + if (i == MEM_REGIONS) + break; + availmem_regions[i].mr_start = mr->mem_base; + availmem_regions[i].mr_size = mr->mem_size; + memsize += mr->mem_size; + } + availmem_regions_sz = i; + } else { + /* Fall back to hardcoded boothowto flags and metadata. */ + boothowto = RB_VERBOSE | RB_SINGLE; + lastaddr = fake_preload_metadata(); + + /* + * Assume a single memory region of size specified in board + * configuration file. + */ + memsize = PHYSMEM_SIZE; + } + + /* + * If memsize is invalid, we can neither proceed nor panic (too + * early for console output). + */ + if (memsize == 0) + while (1); + + /* Platform-specific initialisation */ + if (platform_pmap_init() != 0) + return (NULL); + + pcpu_init(pcpup, 0, sizeof(struct pcpu)); + PCPU_SET(curthread, &thread0); + + /* Calculate number of L2 tables needed for mapping vm_page_array */ + l2size = (memsize / PAGE_SIZE) * sizeof(struct vm_page); + l2size = (l2size >> L1_S_SHIFT) + 1; + + /* + * Add one table for end of kernel map, one for stacks, msgbuf and + * L1 and L2 tables map and one for vectors map. + */ + l2size += 3; + + /* Make it divisible by 4 */ + l2size = (l2size + 3) & ~3; + +#define KERNEL_TEXT_BASE (KERNBASE) + freemempos = (lastaddr + PAGE_MASK) & ~PAGE_MASK; + + /* Define a macro to simplify memory allocation */ +#define valloc_pages(var, np) \ + alloc_pages((var).pv_va, (np)); \ + (var).pv_pa = (var).pv_va + (KERNPHYSADDR - KERNVIRTADDR); + +#define alloc_pages(var, np) \ + (var) = freemempos; \ + freemempos += (np * PAGE_SIZE); \ + memset((char *)(var), 0, ((np) * PAGE_SIZE)); + + while (((freemempos - L1_TABLE_SIZE) & (L1_TABLE_SIZE - 1)) != 0) + freemempos += PAGE_SIZE; + valloc_pages(kernel_l1pt, L1_TABLE_SIZE / PAGE_SIZE); + + for (i = 0; i < l2size; ++i) { + if (!(i % (PAGE_SIZE / L2_TABLE_SIZE_REAL))) { + valloc_pages(kernel_pt_table[i], + L2_TABLE_SIZE / PAGE_SIZE); + } else { + kernel_pt_table[i].pv_va = freemempos - + (i % (PAGE_SIZE / L2_TABLE_SIZE_REAL)) * + L2_TABLE_SIZE_REAL; + kernel_pt_table[i].pv_pa = + kernel_pt_table[i].pv_va - KERNVIRTADDR + + KERNPHYSADDR; + } + } + /* + * Allocate a page for the system page mapped to 0x00000000 + * or 0xffff0000. This page will just contain the system vectors + * and can be shared by all processes. + */ + valloc_pages(systempage, 1); + + /* Allocate stacks for all modes */ + valloc_pages(irqstack, IRQ_STACK_SIZE); + valloc_pages(abtstack, ABT_STACK_SIZE); + valloc_pages(undstack, UND_STACK_SIZE); + valloc_pages(kernelstack, KSTACK_PAGES); + valloc_pages(msgbufpv, round_page(MSGBUF_SIZE) / PAGE_SIZE); + + /* + * Now we start construction of the L1 page table + * We start by mapping the L2 page tables into the L1. + * This means that we can replace L1 mappings later on if necessary + */ + l1pagetable = kernel_l1pt.pv_va; + + /* + * Try to map as much as possible of kernel text and data using + * 1MB section mapping and for the rest of initial kernel address + * space use L2 coarse tables. + * + * Link L2 tables for mapping remainder of kernel (modulo 1MB) + * and kernel structures + */ + l2_start = lastaddr & ~(L1_S_OFFSET); + for (i = 0 ; i < l2size - 1; i++) + pmap_link_l2pt(l1pagetable, l2_start + i * L1_S_SIZE, + &kernel_pt_table[KERNEL_PT_KERN + i]); + + pmap_curmaxkvaddr = l2_start + (l2size - 1) * L1_S_SIZE; + + /* Map kernel code and data */ + pmap_map_chunk(l1pagetable, KERNVIRTADDR, KERNPHYSADDR, + (((uint32_t)(lastaddr) - KERNVIRTADDR) + PAGE_MASK) & ~PAGE_MASK, + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + + + /* Map L1 directory and allocated L2 page tables */ + pmap_map_chunk(l1pagetable, kernel_l1pt.pv_va, kernel_l1pt.pv_pa, + L1_TABLE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); + + pmap_map_chunk(l1pagetable, kernel_pt_table[0].pv_va, + kernel_pt_table[0].pv_pa, + L2_TABLE_SIZE_REAL * l2size, + VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); + + /* Map allocated stacks and msgbuf */ + pmap_map_chunk(l1pagetable, irqstack.pv_va, irqstack.pv_pa, + freemempos - irqstack.pv_va, + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + + /* Link and map the vector page */ + pmap_link_l2pt(l1pagetable, ARM_VECTORS_HIGH, + &kernel_pt_table[KERNEL_PT_SYS]); + pmap_map_entry(l1pagetable, ARM_VECTORS_HIGH, systempage.pv_pa, + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + + pmap_devmap_bootstrap(l1pagetable, pmap_devmap_bootstrap_table); + cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL * 2)) | + DOMAIN_CLIENT); + setttb(kernel_l1pt.pv_pa); + cpu_tlb_flushID(); + cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL * 2)); + cninit(); + physmem = memsize / PAGE_SIZE; + + debugf("initarm: console initialized\n"); + debugf(" arg1 mdp = 0x%08x\n", (uint32_t)mdp); + debugf(" boothowto = 0x%08x\n", boothowto); + print_bootinfo(); + print_kernel_section_addr(); + print_kenv(); + + /* + * Re-initialise decode windows + */ + if (soc_decode_win() != 0) + printf("WARNING: could not re-initialise decode windows! " + "Running with existing settings...\n"); + /* + * Pages were allocated during the secondary bootstrap for the + * stacks for different CPU modes. + * We must now set the r13 registers in the different CPU modes to + * point to these stacks. + * Since the ARM stacks use STMFD etc. we must set r13 to the top end + * of the stack memory. + */ + cpu_control(CPU_CONTROL_MMU_ENABLE, CPU_CONTROL_MMU_ENABLE); + set_stackptr(PSR_IRQ32_MODE, + irqstack.pv_va + IRQ_STACK_SIZE * PAGE_SIZE); + set_stackptr(PSR_ABT32_MODE, + abtstack.pv_va + ABT_STACK_SIZE * PAGE_SIZE); + set_stackptr(PSR_UND32_MODE, + undstack.pv_va + UND_STACK_SIZE * PAGE_SIZE); + + /* + * We must now clean the cache again.... + * Cleaning may be done by reading new data to displace any + * dirty data in the cache. This will have happened in setttb() + * but since we are boot strapping the addresses used for the read + * may have just been remapped and thus the cache could be out + * of sync. A re-clean after the switch will cure this. + * After booting there are no gross reloations of the kernel thus + * this problem will not occur after initarm(). + */ + cpu_idcache_wbinv_all(); + + /* Set stack for exception handlers */ + data_abort_handler_address = (u_int)data_abort_handler; + prefetch_abort_handler_address = (u_int)prefetch_abort_handler; + undefined_handler_address = (u_int)undefinedinstruction_bounce; + undefined_init(); + + proc_linkup0(&proc0, &thread0); + thread0.td_kstack = kernelstack.pv_va; + thread0.td_kstack_pages = KSTACK_PAGES; + thread0.td_pcb = (struct pcb *) + (thread0.td_kstack + KSTACK_PAGES * PAGE_SIZE) - 1; + thread0.td_pcb->pcb_flags = 0; + thread0.td_frame = &proc0_tf; + pcpup->pc_curpcb = thread0.td_pcb; + + arm_vector_init(ARM_VECTORS_HIGH, ARM_VEC_ALL); + + dump_avail[0] = KERNPHYSADDR; + dump_avail[1] = KERNPHYSADDR + memsize; + dump_avail[2] = 0; + dump_avail[3] = 0; + + pmap_bootstrap(freemempos, pmap_bootstrap_lastaddr, &kernel_l1pt); + msgbufp = (void *)msgbufpv.pv_va; + msgbufinit(msgbufp, MSGBUF_SIZE); + mutex_init(); + + /* + * Prepare map of physical memory regions available to vm subsystem. + * If metadata pointer doesn't point to a valid address, use hardcoded + * values. + */ + physmap_init((mdp != NULL) ? 0 : 1); + + /* Do basic tuning, hz etc */ + init_param1(); + init_param2(physmem); + kdb_init(); + return ((void *)(kernelstack.pv_va + USPACE_SVC_STACK_TOP - + sizeof(struct pcb))); +} + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + + return (0); +} diff --git a/sys/arm/mv/mvreg.h b/sys/arm/mv/mvreg.h new file mode 100644 index 0000000..68c2314 --- /dev/null +++ b/sys/arm/mv/mvreg.h @@ -0,0 +1,540 @@ +/*- + * Copyright (C) 2007-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. + * + * $FreeBSD$ + */ + +#ifndef _MVREG_H_ +#define _MVREG_H_ + +#include <machine/intr.h> + +/* + * Physical addresses of integrated SoC peripherals + */ +#define MV_PHYS_BASE 0xF1000000 +#define MV_SIZE 0x100000 + +/* + * Decode windows addresses (physical) + */ +#define MV_PCIE_IO_PHYS_BASE (MV_PHYS_BASE + MV_SIZE) +#define MV_PCIE_IO_BASE MV_PCIE_IO_PHYS_BASE +#define MV_PCIE_IO_SIZE (1024 * 1024) +#define MV_PCI_IO_PHYS_BASE (MV_PCIE_IO_PHYS_BASE + MV_PCIE_IO_SIZE) +#define MV_PCI_IO_BASE MV_PCI_IO_PHYS_BASE +#define MV_PCI_IO_SIZE (1024 * 1024) + +#define MV_PCIE_MEM_PHYS_BASE (MV_PCI_IO_PHYS_BASE + MV_PCI_IO_SIZE) +#define MV_PCIE_MEM_BASE MV_PCIE_MEM_PHYS_BASE +#define MV_PCIE_MEM_SIZE (64 * 1024 * 1024) +#define MV_PCI_MEM_PHYS_BASE (MV_PCIE_MEM_PHYS_BASE + MV_PCIE_MEM_SIZE) +#define MV_PCI_MEM_BASE MV_PCI_MEM_PHYS_BASE +#define MV_PCI_MEM_SIZE (64 * 1024 * 1024) + +/* XXX DEV_BOOT, CSx are board specific, should be defined per platform */ + +/* 512KB NOR FLASH */ +#define MV_DEV_BOOT_PHYS_BASE (MV_PCI_MEM_PHYS_BASE + MV_PCI_MEM_SIZE) +#define MV_DEV_BOOT_SIZE (512 * 1024) +/* CS0: 7-seg LED */ +#define MV_DEV_CS0_PHYS_BASE 0xFA000000 +#define MV_DEV_CS0_SIZE (1024 * 1024) /* XXX u-boot has 2MB */ +/* CS1: 32MB NOR FLASH */ +#define MV_DEV_CS1_PHYS_BASE (MV_DEV_CS0_PHYS_BASE + MV_DEV_CS0_SIZE) +#define MV_DEV_CS1_SIZE (32 * 1024 * 1024) +/* CS2: 32MB NAND FLASH */ +#define MV_DEV_CS2_PHYS_BASE (MV_DEV_CS1_PHYS_BASE + MV_DEV_CS1_SIZE) +#define MV_DEV_CS2_SIZE 1024 /* XXX u-boot has 1MB */ + +/* XXX this is probably not robust against wraparounds... */ +#if ((MV_DEV_CS2_PHYS_BASE + MV_DEV_CS2_SIZE) > 0xFFFEFFFF) +#error Devices memory layout overlaps reset vectors range! +#endif + +/* + * Integrated SoC peripherals addresses + */ +#define MV_BASE MV_PHYS_BASE /* VA == PA mapping */ +#define MV_DDR_CADR_BASE (MV_BASE + 0x1500) +#define MV_MPP_BASE (MV_BASE + 0x10000) +#define MV_GPIO_BASE (MV_BASE + 0x10100) +#define MV_GPIO_SIZE 0x20 +#define MV_RTC_BASE (MV_BASE + 0x10300) +#define MV_RTC_SIZE 0x08 +#define MV_TWSI_BASE (MV_BASE + 0x11000) +#define MV_TWSI_SIZE 0x20 +#define MV_UART0_BASE (MV_BASE + 0x12000) +#define MV_UART1_BASE (MV_BASE + 0x12100) +#define MV_UART_SIZE 0x20 +#define MV_MBUS_BRIDGE_BASE (MV_BASE + 0x20000) +#define MV_INTREGS_BASE (MV_MBUS_BRIDGE_BASE + 0x80) +#define MV_CPU_CONTROL_BASE (MV_MBUS_BRIDGE_BASE + 0x100) +#define MV_IC_BASE (MV_MBUS_BRIDGE_BASE + 0x200) +#define MV_IC_SIZE 0x3C +#define MV_TIMERS_BASE (MV_MBUS_BRIDGE_BASE + 0x300) +#define MV_TIMERS_SIZE 0x30 +#define MV_PCI_BASE (MV_BASE + 0x30000) +#define MV_PCI_SIZE 0x2000 +#define MV_PCIE_BASE (MV_BASE + 0x40000) +#define MV_PCIE_SIZE 0x2000 +#define MV_USB0_BASE (MV_BASE + 0x50000) +#define MV_USB1_BASE (MV_USB0_BASE + 0x1000) +#define MV_USB2_BASE (MV_USB0_BASE + 0x2000) +#define MV_USB_SIZE 0x1000 +#define MV_USB_HOST_OFST 0x0100 /* EHCI HC regs start at this offset within USB range */ +#define MV_USB_AWR_BASE (MV_USB0_BASE + 0x320) +#define MV_IDMA_BASE (MV_BASE + 0x60000) +#define MV_IDMA_SIZE 0x1000 +#define MV_XOR_BASE (MV_BASE + 0x60000) +#define MV_XOR_SIZE 0x1000 +#define MV_ETH0_BASE (MV_BASE + 0x72000) +#define MV_ETH1_BASE (MV_BASE + 0x76000) +#define MV_ETH_SIZE 0x2000 + +#define MV_DEV_CS0_BASE MV_DEV_CS0_PHYS_BASE + +/* + * Interrupt sources + */ +#if defined(SOC_MV_ORION) + +#define MV_INT_BRIDGE 0 /* AHB-MBus Bridge Interrupt */ +#define MV_INT_UART0 3 /* UART0 Interrupt */ +#define MV_INT_UART1 4 +#define MV_INT_GPIO7_0 6 /* GPIO[7:0] Interrupt */ +#define MV_INT_GPIO15_8 7 /* GPIO[15:8] Interrupt */ +#define MV_INT_GPIO23_16 8 /* GPIO[23:16] Interrupt */ +#define MV_INT_GPIO31_24 9 /* GPIO[31:24] Interrupt */ +#define MV_INT_PEX0_ERR 10 /* PCI Express Error */ +#define MV_INT_PEX0 11 /* PCI Express INTA,B,C,D Message */ +#define MV_INT_PCI_ERR 15 /* PCI Error */ +#define MV_INT_USB_BERR 16 /* USB Bridge Error */ +#define MV_INT_USB_CI 17 /* USB Controller interrupt */ +#define MV_INT_GBERX 18 /* GbE receive interrupt */ +#define MV_INT_GBETX 19 /* GbE transmit interrupt */ +#define MV_INT_GBEMISC 20 /* GbE misc. interrupt */ +#define MV_INT_GBESUM 21 /* GbE summary interrupt */ +#define MV_INT_GBEERR 22 /* GbE error interrupt */ +#define MV_INT_IDMA_ERR 23 /* DMA error interrupt */ +#define MV_INT_IDMA0 24 /* IDMA chan. 0 completion interrupt */ +#define MV_INT_IDMA1 25 /* IDMA chan. 1 completion interrupt */ +#define MV_INT_IDMA2 26 /* IDMA chan. 2 completion interrupt */ +#define MV_INT_IDMA3 27 /* IDMA chan. 3 completion interrupt */ +#define MV_INT_SATA 29 /* Serial-ATA Interrupt */ + +#elif defined(SOC_MV_KIRKWOOD) + +#define MV_INT_BRIDGE 1 /* AHB-MBus Bridge Interrupt */ +#define MV_INT_XOR0_CHAN0 5 /* XOR engine 0 channel 0 Interrupt */ +#define MV_INT_XOR0_CHAN1 6 /* XOR engine 0 channel 1 Interrupt */ +#define MV_INT_XOR1_CHAN0 7 /* XOR engine 1 channel 0 Interrupt */ +#define MV_INT_XOR1_CHAN1 8 /* XOR engine 1 channel 1 Interrupt */ +#define MV_INT_PEX0 9 /* PCI Express INTA,B,C,D Message */ +#define MV_INT_GBESUM 11 /* GbE0 summary interrupt */ +#define MV_INT_GBERX 12 /* GbE0 receive interrupt */ +#define MV_INT_GBETX 13 /* GbE0 transmit interrupt */ +#define MV_INT_GBEMISC 14 /* GbE0 misc. interrupt */ +#define MV_INT_GBE1SUM 15 /* GbE1 summary interrupt */ +#define MV_INT_GBE1RX 16 /* GbE1 receive interrupt */ +#define MV_INT_GBE1TX 17 /* GbE1 transmit interrupt */ +#define MV_INT_GBE1MISC 18 /* GbE1 misc. interrupt */ +#define MV_INT_USB_CI 19 /* USB Controller interrupt */ +#define MV_INT_SATA 21 /* Serial-ATA Interrupt */ +#define MV_INT_IDMA_ERR 23 /* DMA error interrupt */ +#define MV_INT_UART0 33 /* UART0 Interrupt */ +#define MV_INT_UART1 34 +#define MV_INT_GPIO7_0 35 /* GPIO[7:0] Interrupt */ +#define MV_INT_GPIO15_8 36 /* GPIO[15:8] Interrupt */ +#define MV_INT_GPIO23_16 37 /* GPIO[23:16] Interrupt */ +#define MV_INT_GPIO31_24 38 /* GPIO[31:24] Interrupt */ +#define MV_INT_GPIOHI7_0 39 /* GPIOHI[7:0] Interrupt */ +#define MV_INT_GPIOHI15_8 40 /* GPIOHI[15:8] Interrupt */ +#define MV_INT_GPIOHI23_16 41 /* GPIOHI[23:16] Interrupt */ +#define MV_INT_XOR0_ERR 42 /* XOR engine 0 error Interrupt */ +#define MV_INT_XOR1_ERR 43 /* XOR engine 1 error Interrupt */ +#define MV_INT_PEX0_ERR 44 /* PCI Express Error */ +#define MV_INT_GBEERR 46 /* GbE0 error interrupt */ +#define MV_INT_GBE1ERR 47 /* GbE1 error interrupt */ +#define MV_INT_USB_BERR 48 /* USB Bridge Error */ + +#elif defined(SOC_MV_DISCOVERY) + +#define MV_INT_ERRSUM 0 /* Summary of error interrupts */ +#define MV_INT_SPI 1 /* SPI interrupt */ +#define MV_INT_TWSI0 2 /* TWSI0 interrupt */ +#define MV_INT_TWSI1 3 /* TWSI1 interrupt */ +#define MV_INT_IDMA0 4 /* IDMA Channel0 completion */ +#define MV_INT_IDMA1 5 /* IDMA Channel0 completion */ +#define MV_INT_IDMA2 6 /* IDMA Channel0 completion */ +#define MV_INT_IDMA3 7 /* IDMA Channel0 completion */ +#define MV_INT_TIMER0 8 /* Timer0 interrupt */ +#define MV_INT_TIMER1 9 /* Timer1 interrupt */ +#define MV_INT_TIMER2 10 /* Timer2 interrupt */ +#define MV_INT_TIMER3 11 /* Timer3 interrupt */ +#define MV_INT_UART0 12 /* UART0 interrupt */ +#define MV_INT_UART1 13 /* UART1 interrupt */ +#define MV_INT_UART2 14 /* UART2 interrupt */ +#define MV_INT_UART3 15 /* UART3 interrupt */ +#define MV_INT_USB0 16 /* USB0 interrupt */ +#define MV_INT_USB1 17 /* USB1 interrupt */ +#define MV_INT_USB2 18 /* USB2 interrupt */ +#define MV_INT_CRYPTO 19 /* Crypto engine completion interrupt */ +#define MV_INT_XOR0 22 /* XOR engine 0 completion interrupt */ +#define MV_INT_XOR1 23 /* XOR engine 1 completion interrupt */ +#define MV_INT_SATA 26 /* SATA interrupt */ +#define MV_INT_PEX00 32 /* PCI Express port 0.0 INTA/B/C/D */ +#define MV_INT_PEX01 33 /* PCI Express port 0.1 INTA/B/C/D */ +#define MV_INT_PEX02 34 /* PCI Express port 0.2 INTA/B/C/D */ +#define MV_INT_PEX03 35 /* PCI Express port 0.3 INTA/B/C/D */ +#define MV_INT_PEX10 36 /* PCI Express port 1.0 INTA/B/C/D */ +#define MV_INT_PEX11 37 /* PCI Express port 1.1 INTA/B/C/D */ +#define MV_INT_PEX12 38 /* PCI Express port 1.2 INTA/B/C/D */ +#define MV_INT_PEX13 39 /* PCI Express port 1.3 INTA/B/C/D */ +#define MV_INT_GBESUM 40 /* Gigabit Ethernet Port 0 summary */ +#define MV_INT_GBERX 41 /* Gigabit Ethernet Port 0 Rx summary */ +#define MV_INT_GBETX 42 /* Gigabit Ethernet Port 0 Tx summary */ +#define MV_INT_GBEMISC 43 /* Gigabit Ethernet Port 0 Misc summ. */ +#define MV_INT_GBE1SUM 44 /* Gigabit Ethernet Port 1 summary */ +#define MV_INT_GBE1RX 45 /* Gigabit Ethernet Port 1 Rx summary */ +#define MV_INT_GBE1TX 46 /* Gigabit Ethernet Port 1 Tx summary */ +#define MV_INT_GBE1MISC 47 /* Gigabit Ethernet Port 1 Misc summ. */ +#define MV_INT_GPIO7_0 56 /* GPIO[7:0] Interrupt */ +#define MV_INT_GPIO15_8 57 /* GPIO[15:8] Interrupt */ +#define MV_INT_GPIO23_16 58 /* GPIO[23:16] Interrupt */ +#define MV_INT_GPIO31_24 59 /* GPIO[31:24] Interrupt */ +#define MV_INT_DB_IN 60 /* Inbound Doorbell Cause reg Summary */ +#define MV_INT_DB_OUT 61 /* Outbound Doorbell Cause reg Summ. */ +#define MV_INT_CRYPT_ERR 64 /* Crypto engine error */ +#define MV_INT_DEV_ERR 65 /* Device bus error */ +#define MV_INT_IDMA_ERR 66 /* DMA error */ +#define MV_INT_CPU_ERR 67 /* CPU error */ +#define MV_INT_PEX0_ERR 68 /* PCI-Express port0 error */ +#define MV_INT_PEX1_ERR 69 /* PCI-Express port1 error */ +#define MV_INT_GBE_ERR 70 /* Gigabit Ethernet error */ +#define MV_INT_USB_ERR 72 /* USB error */ +#define MV_INT_DRAM_ERR 73 /* DRAM ECC error */ +#define MV_INT_XOR_ERR 74 /* XOR engine error */ +#define MV_INT_WD 79 /* WD Timer interrupt */ + +#endif /* SOC_MV_ORION */ + +#define BRIDGE_IRQ_CAUSE 0x10 +#define BRIGDE_IRQ_MASK 0x14 + +#if defined(SOC_MV_DISCOVERY) +#define IRQ_CAUSE_ERROR 0x0 +#define IRQ_CAUSE 0x4 +#define IRQ_CAUSE_HI 0x8 +#define IRQ_MASK_ERROR 0xC +#define IRQ_MASK 0x10 +#define IRQ_MASK_HI 0x14 +#define IRQ_CAUSE_SELECT 0x18 +#define FIQ_MASK_ERROR 0x1C +#define FIQ_MASK 0x20 +#define FIQ_MASK_HI 0x24 +#define FIQ_CAUSE_SELECT 0x28 +#define ENDPOINT_IRQ_MASK_ERROR 0x2C +#define ENDPOINT_IRQ_MASK 0x30 +#define ENDPOINT_IRQ_MASK_HI 0x34 +#define ENDPOINT_IRQ_CAUSE_SELECT 0x38 +#else /* !SOC_MV_DISCOVERY */ +#define IRQ_CAUSE 0x0 +#define IRQ_MASK 0x4 +#define FIQ_MASK 0x8 +#define ENDPOINT_IRQ_MASK 0xC +#define IRQ_CAUSE_HI 0x10 +#define IRQ_MASK_HI 0x14 +#define FIQ_MASK_HI 0x18 +#define ENDPOINT_IRQ_MASK_HI 0x1C +#define IRQ_CAUSE_ERROR (-1) /* Fake defines for unified */ +#define IRQ_MASK_ERROR (-1) /* interrupt controller code */ +#endif + +#define BRIDGE_IRQ_CAUSE 0x10 +#define IRQ_CPU_SELF 0x00000001 +#define IRQ_TIMER0 0x00000002 +#define IRQ_TIMER1 0x00000004 +#define IRQ_TIMER_WD 0x00000008 + +#define BRIDGE_IRQ_MASK 0x14 +#define IRQ_CPU_MASK 0x00000001 +#define IRQ_TIMER0_MASK 0x00000002 +#define IRQ_TIMER1_MASK 0x00000004 +#define IRQ_TIMER_WD_MASK 0x00000008 + +/* + * System reset + */ +#define RSTOUTn_MASK 0x8 +#define WD_RST_OUT_EN 0x00000002 +#define SOFT_RST_OUT_EN 0x00000004 +#define SYSTEM_SOFT_RESET 0xc +#define SYS_SOFT_RST 0x00000001 + +/* + * Power Control + */ +#define CPU_PM_CTRL 0x1C +#define CPU_PM_CTRL_NONE 0 + +#if defined(SOC_MV_KIRKWOOD) +#define CPU_PM_CTRL_GE0 (1 << 0) +#define CPU_PM_CTRL_PEX0_PHY (1 << 1) +#define CPU_PM_CTRL_PEX0 (1 << 2) +#define CPU_PM_CTRL_USB0 (1 << 3) +#define CPU_PM_CTRL_SDIO (1 << 4) +#define CPU_PM_CTRL_TSU (1 << 5) +#define CPU_PM_CTRL_DUNIT (1 << 6) +#define CPU_PM_CTRL_RUNIT (1 << 7) +#define CPU_PM_CTRL_XOR0 (1 << 8) +#define CPU_PM_CTRL_AUDIO (1 << 9) +#define CPU_PM_CTRL_SATA0 (1 << 14) +#define CPU_PM_CTRL_SATA1 (1 << 15) +#define CPU_PM_CTRL_XOR1 (1 << 16) +#define CPU_PM_CTRL_CRYPTO (1 << 17) +#define CPU_PM_CTRL_GE1 (1 << 18) +#define CPU_PM_CTRL_TDM (1 << 19) +#elif defined(SOC_MV_DISCOVERY) +#define CPU_PM_CTRL_GE0 (1 << 1) +#define CPU_PM_CTRL_GE1 (1 << 2) +#define CPU_PM_CTRL_PEX00 (1 << 5) +#define CPU_PM_CTRL_PEX01 (1 << 6) +#define CPU_PM_CTRL_PEX02 (1 << 7) +#define CPU_PM_CTRL_PEX03 (1 << 8) +#define CPU_PM_CTRL_PEX10 (1 << 9) +#define CPU_PM_CTRL_PEX11 (1 << 10) +#define CPU_PM_CTRL_PEX12 (1 << 11) +#define CPU_PM_CTRL_PEX13 (1 << 12) +#define CPU_PM_CTRL_SATA0_PHY (1 << 13) +#define CPU_PM_CTRL_SATA0 (1 << 14) +#define CPU_PM_CTRL_SATA1_PHY (1 << 15) +#define CPU_PM_CTRL_SATA1 (1 << 16) +#define CPU_PM_CTRL_USB0 (1 << 17) +#define CPU_PM_CTRL_USB1 (1 << 18) +#define CPU_PM_CTRL_USB2 (1 << 19) +#define CPU_PM_CTRL_IDMA (1 << 20) +#define CPU_PM_CTRL_XOR (1 << 21) +#define CPU_PM_CTRL_CRYPTO (1 << 22) +#define CPU_PM_CTRL_DEVICE (1 << 23) +#endif + +/* + * Timers + */ +#define CPU_TIMER_CONTROL 0x0 +#define CPU_TIMER0_EN 0x00000001 +#define CPU_TIMER0_AUTO 0x00000002 +#define CPU_TIMER1_EN 0x00000004 +#define CPU_TIMER1_AUTO 0x00000008 +#define CPU_TIMER_WD_EN 0x00000010 +#define CPU_TIMER_WD_AUTO 0x00000020 +#define CPU_TIMER0_REL 0x10 +#define CPU_TIMER0 0x14 + +/* + * GPIO + */ +#define GPIO_DATA_OUT 0x00 +#define GPIO_DATA_OUT_EN_CTRL 0x04 +#define GPIO_BLINK_EN 0x08 +#define GPIO_DATA_IN_POLAR 0x0c +#define GPIO_DATA_IN 0x10 +#define GPIO_INT_CAUSE 0x14 +#define GPIO_INT_EDGE_MASK 0x18 +#define GPIO_INT_LEV_MASK 0x1c + +#define GPIO_HI_DATA_OUT 0x40 +#define GPIO_HI_DATA_OUT_EN_CTRL 0x44 +#define GPIO_HI_BLINK_EN 0x48 +#define GPIO_HI_DATA_IN_POLAR 0x4c +#define GPIO_HI_DATA_IN 0x50 +#define GPIO_HI_INT_CAUSE 0x54 +#define GPIO_HI_INT_EDGE_MASK 0x58 +#define GPIO_HI_INT_LEV_MASK 0x5c + +#define GPIO(n) (1 << (n)) +#define MV_GPIO_MAX_NPINS 64 + +#define MV_GPIO_BLINK 0x1 +#define MV_GPIO_POLARITY 0x2 +#define MV_GPIO_EDGE 0x4 +#define MV_GPIO_LEVEL 0x8 + +#define IS_GPIO_IRQ(irq) ((irq) >= NIRQ && (irq) < NIRQ + MV_GPIO_MAX_NPINS) +#define GPIO2IRQ(gpio) ((gpio) + NIRQ) +#define IRQ2GPIO(irq) ((irq) - NIRQ) + +/* + * MPP + */ +#define MPP_CONTROL0 0x00 +#define MPP_CONTROL1 0x04 +#define MPP_CONTROL2 0x50 +#define DEVICE_MULTIPLEX 0x08 + +#if defined(SOC_MV_ORION) +#define SAMPLE_AT_RESET 0x10 +#elif defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY) +#define SAMPLE_AT_RESET 0x30 +#else +#error SOC_MV_XX not defined +#endif + +/* + * Clocks + */ +#ifdef SOC_MV_ORION +#define TCLK_MASK 0x300 +#define TCLK_SHIFT 0x8 +#elif defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY) +#define TCLK_MASK 0x30000 +#define TCLK_SHIFT 0x10 +#else +#error SOC_MV_XX not defined +#endif + +#define TCLK_100MHZ 100000000 +#define TCLK_125MHZ 125000000 +#define TCLK_133MHZ 133333333 +#define TCLK_150MHZ 150000000 +#define TCLK_166MHZ 166666667 +#define TCLK_200MHZ 200000000 + +/* + * Chip ID + */ +#define MV_DEV_88F5181 0x5181 +#define MV_DEV_88F5182 0x5182 +#define MV_DEV_88F5281 0x5281 +#define MV_DEV_88F6281 0x6281 +#define MV_DEV_MV78100 0x6381 + +/* + * Decode windows definitions and macros + */ +#define MV_WIN_CPU_CTRL(n) (0x10 * (n) + (((n) < 8) ? 0x000 : 0x880)) +#define MV_WIN_CPU_BASE(n) (0x10 * (n) + (((n) < 8) ? 0x004 : 0x884)) +#define MV_WIN_CPU_REMAP_LO(n) (0x10 * (n) + (((n) < 8) ? 0x008 : 0x888)) +#define MV_WIN_CPU_REMAP_HI(n) (0x10 * (n) + (((n) < 8) ? 0x00C : 0x88C)) +#if defined(SOC_MV_DISCOVERY) +#define MV_WIN_CPU_MAX 14 +#else +#define MV_WIN_CPU_MAX 8 +#endif + +#define MV_WIN_DDR_BASE(n) (0x8 * (n) + 0x0) +#define MV_WIN_DDR_SIZE(n) (0x8 * (n) + 0x4) +#define MV_WIN_DDR_MAX 4 + +#define MV_WIN_USB_CTRL(n) (0x10 * (n) + 0x0) +#define MV_WIN_USB_BASE(n) (0x10 * (n) + 0x4) +#define MV_WIN_USB_MAX 4 + +#define MV_WIN_ETH_BASE(n) (0x8 * (n) + 0x200) +#define MV_WIN_ETH_SIZE(n) (0x8 * (n) + 0x204) +#define MV_WIN_ETH_REMAP(n) (0x4 * (n) + 0x280) +#define MV_WIN_ETH_MAX 6 + +#define MV_WIN_IDMA_BASE(n) (0x8 * (n) + 0xa00) +#define MV_WIN_IDMA_SIZE(n) (0x8 * (n) + 0xa04) +#define MV_WIN_IDMA_REMAP(n) (0x4 * (n) + 0xa60) +#define MV_WIN_IDMA_CAP(n) (0x4 * (n) + 0xa70) +#define MV_WIN_IDMA_MAX 8 +#define MV_IDMA_CHAN_MAX 4 + +#define MV_WIN_PCIE_CTRL(n) (0x10 * (((n) < 5) ? (n) : \ + (n) + 1) + 0x1820) +#define MV_WIN_PCIE_BASE(n) (0x10 * (((n) < 5) ? (n) : \ + (n) + 1) + 0x1824) +#define MV_WIN_PCIE_REMAP(n) (0x10 * (((n) < 5) ? (n) : \ + (n) + 1) + 0x182C) +#define MV_WIN_PCIE_MAX 6 + +#define MV_PCIE_BAR(n) (0x04 * (n) + 0x1804) +#define MV_PCIE_BAR_MAX 3 + +#define WIN_REG_IDX_RD(pre,reg,off,base) \ + static __inline uint32_t \ + pre ## _ ## reg ## _read(int i) \ + { \ + return (bus_space_read_4(obio_tag, base, off(i))); \ + } + +#define WIN_REG_BASE_IDX_RD(pre,reg,off) \ + static __inline uint32_t \ + pre ## _ ## reg ## _read(uint32_t base, int i) \ + { \ + return (bus_space_read_4(obio_tag, base, off(i))); \ + } + +#define WIN_REG_IDX_WR(pre,reg,off,base) \ + static __inline void \ + pre ## _ ## reg ## _write(int i, uint32_t val) \ + { \ + bus_space_write_4(obio_tag, base, off(i), val); \ + } + +#define WIN_REG_BASE_IDX_WR(pre,reg,off) \ + static __inline void \ + pre ## _ ## reg ## _write(uint32_t base, int i, uint32_t val) \ + { \ + bus_space_write_4(obio_tag, base, off(i), val); \ + } + +#define WIN_REG_RD(pre,reg,off,base) \ + static __inline uint32_t \ + pre ## _ ## reg ## _read(void) \ + { \ + return (bus_space_read_4(obio_tag, base, off)); \ + } + +#define WIN_REG_BASE_RD(pre,reg,off) \ + static __inline uint32_t \ + pre ## _ ## reg ## _read(uint32_t base) \ + { \ + return (bus_space_read_4(obio_tag, base, off)); \ + } + +#define WIN_REG_WR(pre,reg,off,base) \ + static __inline void \ + pre ## _ ## reg ## _write(uint32_t val) \ + { \ + bus_space_write_4(obio_tag, base, off, val); \ + } + +#define WIN_REG_BASE_WR(pre,reg,off) \ + static __inline void \ + pre ## _ ## reg ## _write(uint32_t base, uint32_t val) \ + { \ + bus_space_write_4(obio_tag, base, off, val); \ + } + +#endif /* _MVREG_H_ */ diff --git a/sys/arm/mv/mvvar.h b/sys/arm/mv/mvvar.h new file mode 100644 index 0000000..cdf87c9 --- /dev/null +++ b/sys/arm/mv/mvvar.h @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 2002, 2003 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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. + * + * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0var.h, rev 1 + * + * $FreeBSD$ + */ + +#ifndef _MVVAR_H_ +#define _MVVAR_H_ + +#include <sys/rman.h> + +struct obio_softc { + bus_space_tag_t obio_bst; /* bus space tag */ + struct rman obio_mem; + struct rman obio_irq; + struct rman obio_gpio; +}; + +struct obio_device { + const char *od_name; + u_long od_base; + u_long od_size; + u_int od_irqs[7 + 1]; /* keep additional entry for -1 sentinel */ + u_int od_gpio[2 + 1]; /* as above for IRQ */ + u_int od_pwr_mask; + struct resource_list od_resources; +}; + +struct decode_win { + int target; /* Mbus unit ID */ + int attr; /* Attributes of the target interface */ + vm_paddr_t base; /* Physical base addr */ + uint32_t size; + int remap; +}; + +extern bus_space_tag_t obio_tag; +extern struct obio_device obio_devices[]; +extern const struct decode_win *cpu_wins; +extern const struct decode_win *idma_wins; +extern int cpu_wins_no; +extern int idma_wins_no; + +/* Function prototypes */ +int mv_gpio_setup_intrhandler(const char *name, driver_filter_t *filt, + void (*hand)(void *), void *arg, int pin, int flags, void **cookiep); +void mv_gpio_intr_mask(int pin); +void mv_gpio_intr_unmask(int pin); +int mv_gpio_configure(uint32_t pin, uint32_t flags, uint32_t mask); +void mv_gpio_out(uint32_t pin, uint8_t val, uint8_t enable); +uint8_t mv_gpio_in(uint32_t pin); + +int platform_pmap_init(void); +int soc_decode_win(void); +void soc_id(uint32_t *dev, uint32_t *rev); +void soc_identify(void); +void soc_dump_decode_win(void); +uint32_t soc_power_ctrl_get(uint32_t mask); + +int decode_win_overlap(int, int, const struct decode_win *); +int win_cpu_can_remap(int); + +void decode_win_idma_dump(void); +void decode_win_idma_setup(void); +int decode_win_idma_valid(void); + +int ddr_is_active(int i); +uint32_t ddr_base(int i); +uint32_t ddr_size(int i); +uint32_t ddr_attr(int i); +uint32_t ddr_target(int i); + +uint32_t cpu_extra_feat(void); +uint32_t get_tclk(void); +uint32_t read_cpu_ctrl(uint32_t); +void write_cpu_ctrl(uint32_t, uint32_t); + +enum mbus_device_ivars { + MBUS_IVAR_BASE, +}; + +#define MBUS_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(mbus, var, MBUS, ivar, type) + +MBUS_ACCESSOR(base, BASE, u_long) + +#undef MBUS_ACCESSOR + +#endif /* _MVVAR_H_ */ diff --git a/sys/arm/mv/obio.c b/sys/arm/mv/obio.c new file mode 100644 index 0000000..bd757a5 --- /dev/null +++ b/sys/arm/mv/obio.c @@ -0,0 +1,355 @@ +/*- + * Copyright (c) 2006 Benno Rice. 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. + * + * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_obio.c, rev 1 + */ + +#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/malloc.h> +#include <sys/rman.h> +#include <machine/bus.h> +#include <machine/intr.h> + +#include <arm/mv/mvvar.h> +#include <arm/mv/mvreg.h> + +static void mbus_identify(driver_t *, device_t); +static int mbus_probe(device_t); +static int mbus_attach(device_t); + +static void +mbus_identify(driver_t *driver, device_t parent) +{ + + BUS_ADD_CHILD(parent, 0, "mbus", 0); +} + +static int +mbus_probe(device_t dev) +{ + + device_set_desc(dev, "Marvell Internal Bus (Mbus)"); + return (0); +} + +static int +mbus_attach(device_t dev) +{ + struct obio_softc *sc; + struct obio_device *od; + int i; + device_t child; + + sc = device_get_softc(dev); + + sc->obio_bst = obio_tag; + + sc->obio_mem.rm_type = RMAN_ARRAY; + sc->obio_mem.rm_descr = "Marvell OBIO Memory"; + if (rman_init(&sc->obio_mem) != 0) + panic("mbus_attach: failed to init obio mem rman"); + if (rman_manage_region(&sc->obio_mem, 0, ~0) != 0) + panic("mbus_attach: failed to set up obio mem rman"); + + sc->obio_irq.rm_type = RMAN_ARRAY; + sc->obio_irq.rm_descr = "Marvell OBIO IRQ"; + if (rman_init(&sc->obio_irq) != 0) + panic("mbus_attach: failed to init obio irq rman"); + if (rman_manage_region(&sc->obio_irq, 0, NIRQ - 1) != 0) + panic("mbus_attach: failed to set up obio irq rman"); + + sc->obio_gpio.rm_type = RMAN_ARRAY; + sc->obio_gpio.rm_descr = "Marvell GPIO"; + if (rman_init(&sc->obio_gpio) != 0) + panic("mbus_attach: failed to init gpio rman"); + if (rman_manage_region(&sc->obio_gpio, 0, MV_GPIO_MAX_NPINS - 1) != 0) + panic("mbus_attach: failed to set up gpio rman"); + + for (od = obio_devices; od->od_name != NULL; od++) { + if (soc_power_ctrl_get(od->od_pwr_mask) != od->od_pwr_mask) + continue; + + resource_list_init(&od->od_resources); + + resource_list_add(&od->od_resources, SYS_RES_MEMORY, 0, + od->od_base, od->od_base + od->od_size, od->od_size); + + for (i = 0; od->od_irqs[i] != -1; i++) { + resource_list_add(&od->od_resources, SYS_RES_IRQ, i, + od->od_irqs[i], od->od_irqs[i], 1); + } + + for (i = 0; od->od_gpio[i] != -1; i++) { + resource_list_add(&od->od_resources, SYS_RES_GPIO, i, + od->od_gpio[i], od->od_gpio[i], 1); + } + + child = device_add_child(dev, od->od_name, -1); + device_set_ivars(child, od); + } + + bus_generic_probe(dev); + bus_generic_attach(dev); + + return (0); +} + +static int +mbus_print_child(device_t dev, device_t child) +{ + struct obio_device *od; + int rv; + + od = (struct obio_device *)device_get_ivars(child); + if (od == NULL) + panic("Unknown device on %s", device_get_nameunit(dev)); + + rv = 0; + + rv += bus_print_child_header(dev, child); + + rv += resource_list_print_type(&od->od_resources, "at mem", + SYS_RES_MEMORY, "0x%08lx"); + rv += resource_list_print_type(&od->od_resources, "irq", + SYS_RES_IRQ, "%ld"); + rv += resource_list_print_type(&od->od_resources, "gpio", + SYS_RES_GPIO, "%ld"); + + if (device_get_flags(child)) + rv += printf(" flags %#x", device_get_flags(child)); + rv += bus_print_child_footer(dev, child); + + return (rv); +} + +static int +mbus_setup_intr(device_t dev, device_t child, struct resource *ires, int flags, + driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) +{ + struct obio_softc *sc; + int error; + + sc = (struct obio_softc *)device_get_softc(dev); + + if (rman_is_region_manager(ires, &sc->obio_gpio)) { + error = mv_gpio_setup_intrhandler( + device_get_nameunit(child), filt, intr, arg, + rman_get_start(ires), flags, cookiep); + + if (error != 0) + return (error); + + mv_gpio_intr_unmask(rman_get_start(ires)); + return (0); + } + + BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags, filt, intr, arg, + cookiep); + arm_unmask_irq(rman_get_start(ires)); + return (0); +} + +static int +mbus_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) +{ + + return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, ires, cookie)); +} + +static int +mbus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct obio_device *od; + + od = (struct obio_device *)device_get_ivars(child); + + switch (which) { + case MBUS_IVAR_BASE: + *((u_long *)result) = od->od_base; + break; + + default: + return (ENOENT); + } + + return (0); +} + +static struct resource_list * +mbus_get_resource_list(device_t dev, device_t child) +{ + struct obio_device *od; + + od = (struct obio_device *)device_get_ivars(child); + + if (od == NULL) + return (NULL); + + return (&od->od_resources); +} + +static struct resource * +mbus_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct obio_softc *sc; + struct obio_device *od; + struct resource *rv; + struct resource_list *rl; + struct resource_list_entry *rle = NULL; + struct rman *rm; + int needactivate; + + sc = (struct obio_softc *)device_get_softc(dev); + + if (type == SYS_RES_IRQ && IS_GPIO_IRQ(start)) { + type = SYS_RES_GPIO; + start = IRQ2GPIO(start); + } + + switch (type) { + case SYS_RES_IRQ: + rm = &sc->obio_irq; + break; + case SYS_RES_MEMORY: + rm = &sc->obio_mem; + break; + case SYS_RES_GPIO: + rm = &sc->obio_gpio; + break; + default: + return (NULL); + } + + if (device_get_parent(child) == dev) { + od = (struct obio_device *)device_get_ivars(child); + rl = &od->od_resources; + rle = resource_list_find(rl, type, *rid); + + if (rle->res != NULL) + panic("mbus_alloc_resource: resource is busy"); + } + + if (rle) { + start = rle->start; + end = rle->end; + count = rle->count; + } + + needactivate = flags & RF_ACTIVE; + flags &= ~RF_ACTIVE; + rv = rman_reserve_resource(rm, start, end, count, flags, child); + + if (rv == NULL) + return (NULL); + + if (rle) + rle->res = rv; + + rman_set_rid(rv, *rid); + + if (type == SYS_RES_MEMORY) { + rman_set_bustag(rv, sc->obio_bst); + rman_set_bushandle(rv, start); + } + + if (needactivate) + if (bus_activate_resource(child, type, *rid, rv)) { + rman_release_resource(rv); + return (NULL); + } + + return (rv); +} + +static int +mbus_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + struct obio_device *od; + struct resource_list *rl; + struct resource_list_entry *rle; + + if (device_get_parent(child) == dev) { + od = (struct obio_device *)device_get_ivars(child); + rl = &od->od_resources; + rle = resource_list_find(rl, type, rid); + + if (!rle) + panic("mbus_release_resource: can't find resource"); + + if (!rle->res) + panic("mbus_release_resource: resource entry is not busy"); + + r = rle->res; + rle->res = NULL; + } + + rman_release_resource(r); + + return (0); +} + +static int +mbus_activate_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + + return (rman_activate_resource(r)); +} + +static device_method_t mbus_methods[] = { + DEVMETHOD(device_identify, mbus_identify), + DEVMETHOD(device_probe, mbus_probe), + DEVMETHOD(device_attach, mbus_attach), + + DEVMETHOD(bus_print_child, mbus_print_child), + + DEVMETHOD(bus_read_ivar, mbus_read_ivar), + DEVMETHOD(bus_setup_intr, mbus_setup_intr), + DEVMETHOD(bus_teardown_intr, mbus_teardown_intr), + + DEVMETHOD(bus_get_resource_list, mbus_get_resource_list), + DEVMETHOD(bus_alloc_resource, mbus_alloc_resource), + DEVMETHOD(bus_release_resource, mbus_release_resource), + DEVMETHOD(bus_activate_resource, mbus_activate_resource), + + {0, 0} +}; + +static driver_t mbus_driver = { + "mbus", + mbus_methods, + sizeof(struct obio_softc), +}; + +static devclass_t mbus_devclass; + +DRIVER_MODULE(mbus, nexus, mbus_driver, mbus_devclass, 0, 0); diff --git a/sys/arm/mv/orion/db88f5xxx.c b/sys/arm/mv/orion/db88f5xxx.c new file mode 100644 index 0000000..5c333b4 --- /dev/null +++ b/sys/arm/mv/orion/db88f5xxx.c @@ -0,0 +1,177 @@ +/*- + * 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 <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/pte.h> +#include <machine/pmap.h> +#include <machine/vmparam.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.h> + +/* + * Virtual address space layout: + * ----------------------------- + * 0x0000_0000 - 0xbfff_ffff : user process + * + * 0xc040_0000 - virtual_avail : kernel reserved (text, data, page tables + * : structures, ARM stacks etc.) + * virtual_avail - 0xefff_ffff : KVA (virtual_avail is typically < 0xc0a0_0000) + * 0xf000_0000 - 0xf0ff_ffff : no-cache allocation area (16MB) + * 0xf100_0000 - 0xf10f_ffff : SoC integrated devices registers range (1MB) + * 0xf110_0000 - 0xfffe_ffff : PCI, PCIE (MEM+IO) outbound windows (~238MB) + * 0xffff_0000 - 0xffff_0fff : 'high' vectors page (4KB) + * 0xffff_1000 - 0xffff_1fff : ARM_TP_ADDRESS/RAS page (4KB) + * 0xffff_2000 - 0xffff_ffff : unused (~55KB) + */ + +const struct pmap_devmap *pmap_devmap_bootstrap_table; +vm_offset_t pmap_bootstrap_lastaddr; + +/* Static device mappings. */ +static const struct pmap_devmap pmap_devmap[] = { + /* + * Map the on-board devices VA == PA so that we can access them + * with the MMU on or off. + */ + { /* SoC integrated peripherals registers range */ + MV_BASE, + MV_PHYS_BASE, + MV_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { /* PCIE I/O */ + MV_PCIE_IO_BASE, + MV_PCIE_IO_PHYS_BASE, + MV_PCIE_IO_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { /* PCIE Memory */ + MV_PCIE_MEM_BASE, + MV_PCIE_MEM_PHYS_BASE, + MV_PCIE_MEM_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { /* PCI I/O */ + MV_PCI_IO_BASE, + MV_PCI_IO_PHYS_BASE, + MV_PCI_IO_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { /* PCI Memory */ + MV_PCI_MEM_BASE, + MV_PCI_MEM_PHYS_BASE, + MV_PCI_MEM_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { /* 7-seg LED */ + MV_DEV_CS0_BASE, + MV_DEV_CS0_PHYS_BASE, + MV_DEV_CS0_SIZE, + VM_PROT_READ | VM_PROT_WRITE, + PTE_NOCACHE, + }, + { 0, 0, 0, 0, 0, } +}; + +#if 0 +int platform_pci_get_irq(u_int bus, u_int slot, u_int func, u_int pin) +{ + int irq; + + switch (slot) { + case 7: + irq = GPIO2IRQ(12); /* GPIO 0 for DB-88F5182 */ + break; /* GPIO 12 for DB-88F5281 */ + case 8: + case 9: + irq = GPIO2IRQ(13); /* GPIO 1 for DB-88F5182 */ + break; /* GPIO 13 for DB-88F5281 */ + default: + irq = -1; + break; + }; + + /* + * XXX This isn't the right place to setup GPIO, but it makes sure + * that PCI works on 5XXX targets where U-Boot doesn't set up the GPIO + * correctly to handle PCI IRQs (e.g., on 5182). This code will go + * away once we set up GPIO in a generic way in a proper place (TBD). + */ + if (irq >= 0) + mv_gpio_configure(IRQ2GPIO(irq), MV_GPIO_POLARITY | + MV_GPIO_LEVEL, ~0u); + + return(irq); +} +#endif + +int +platform_pmap_init(void) +{ + + pmap_bootstrap_lastaddr = MV_BASE - ARM_NOCACHE_KVA_SIZE; + pmap_devmap_bootstrap_table = &pmap_devmap[0]; + + return (0); +} + +static void +platform_identify(void *dummy) +{ + + soc_identify(); + + /* + * XXX Board identification e.g. read out from FPGA or similar should + * go here + */ +} +SYSINIT(platform_identify, SI_SUB_CPU, SI_ORDER_SECOND, platform_identify, NULL); + +/* + * TODO routine setting GPIO/MPP pins + */ diff --git a/sys/arm/mv/orion/files.db88f5xxx b/sys/arm/mv/orion/files.db88f5xxx new file mode 100644 index 0000000..9bdbc3b --- /dev/null +++ b/sys/arm/mv/orion/files.db88f5xxx @@ -0,0 +1,4 @@ +# $FreeBSD$ + +arm/mv/orion/orion.c standard +arm/mv/orion/db88f5xxx.c standard diff --git a/sys/arm/mv/orion/orion.c b/sys/arm/mv/orion/orion.c new file mode 100644 index 0000000..8eb4908a --- /dev/null +++ b/sys/arm/mv/orion/orion.c @@ -0,0 +1,189 @@ +/*- + * 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 <machine/bus.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.h> + +#if 0 +#include <arm/mv/mv_pci.h> +extern int platform_pci_get_irq(u_int bus, u_int slot, u_int func, u_int pin); +#endif + +struct obio_device obio_devices[] = { + { "ic", MV_IC_BASE, MV_IC_SIZE, + { -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "timer", MV_TIMERS_BASE, MV_TIMERS_SIZE, + { MV_INT_BRIDGE, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "gpio", MV_GPIO_BASE, MV_GPIO_SIZE, + { MV_INT_GPIO7_0, MV_INT_GPIO15_8, + MV_INT_GPIO23_16, MV_INT_GPIO31_24, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "uart", MV_UART0_BASE, MV_UART_SIZE, + { MV_INT_UART0, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "uart", MV_UART1_BASE, MV_UART_SIZE, + { MV_INT_UART1, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "idma", MV_IDMA_BASE, MV_IDMA_SIZE, + { MV_INT_IDMA_ERR, MV_INT_IDMA0, MV_INT_IDMA1, + MV_INT_IDMA2, MV_INT_IDMA3, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "ehci", MV_USB0_BASE, MV_USB_SIZE, + { MV_INT_USB_BERR, MV_INT_USB_CI, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "mge", MV_ETH0_BASE, MV_ETH_SIZE, + { MV_INT_GBERX, MV_INT_GBETX, MV_INT_GBEMISC, + MV_INT_GBESUM, MV_INT_GBEERR, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "twsi", MV_TWSI_BASE, MV_TWSI_SIZE, + { -1 }, { -1 }, + CPU_PM_CTRL_NONE + }, + { "pcib", MV_PCIE_BASE, MV_PCIE_SIZE, + { MV_INT_PEX0_ERR, -1 }, + { -1 }, + CPU_PM_CTRL_NONE + }, + { "pcib", MV_PCI_BASE, MV_PCI_SIZE, + { MV_INT_PCI_ERR, -1 }, + {-1 }, + CPU_PM_CTRL_NONE + }, + { NULL, 0, 0, { 0 } } +}; + +#if 0 +const struct mv_pci_info pci_info[] = { + { 1, MV_PCIE_IO_BASE, MV_PCIE_IO_SIZE, + MV_PCIE_MEM_BASE, MV_PCIE_MEM_SIZE, + NULL, MV_INT_PEX0 + }, + { 0, MV_PCI_IO_BASE, MV_PCI_IO_SIZE, + MV_PCI_MEM_BASE, MV_PCI_MEM_SIZE, + platform_pci_get_irq, -1 + }, +}; +#endif + +struct resource_spec mv_gpio_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { SYS_RES_IRQ, 3, RF_ACTIVE }, + { -1, 0 } +}; + +const struct decode_win cpu_win_tbl[] = { + /* PCIE IO */ + { 4, 0x51, MV_PCIE_IO_PHYS_BASE, MV_PCIE_IO_SIZE, -1 }, + + /* PCI IO */ + { 3, 0x51, MV_PCI_IO_PHYS_BASE, MV_PCI_IO_SIZE, -1 }, + + /* PCIE MEM */ + { 4, 0x59, MV_PCIE_MEM_PHYS_BASE, MV_PCIE_MEM_SIZE, -1 }, + + /* PCI MEM */ + { 3, 0x59, MV_PCI_MEM_PHYS_BASE, MV_PCI_MEM_SIZE, -1 }, + + /* Device bus BOOT */ + { 1, 0x0f, MV_DEV_BOOT_PHYS_BASE, MV_DEV_BOOT_SIZE, -1 }, + + /* Device bus CS0 */ + { 1, 0x1e, MV_DEV_CS0_PHYS_BASE, MV_DEV_CS0_SIZE, -1 }, + + /* Device bus CS1 */ + { 1, 0x1d, MV_DEV_CS1_PHYS_BASE, MV_DEV_CS1_SIZE, -1 }, + + /* Device bus CS2 */ + { 1, 0x1b, MV_DEV_CS2_PHYS_BASE, MV_DEV_CS2_SIZE, -1 }, +}; +const struct decode_win *cpu_wins = cpu_win_tbl; +int cpu_wins_no = sizeof(cpu_win_tbl) / sizeof(struct decode_win); + +/* + * Note: the decode windows table for IDMA does not explicitly have DRAM + * entries, which are not statically defined: active DDR banks (== windows) + * are established in run time from actual DDR windows settings. All active + * DDR banks are mapped into IDMA decode windows, so at least one IDMA decode + * window is occupied by the DDR bank; in case when all (MV_WIN_DDR_MAX) + * DDR banks are active, the remaining available IDMA decode windows for other + * targets is only MV_WIN_IDMA_MAX - MV_WIN_DDR_MAX. + */ +const struct decode_win idma_win_tbl[] = { + /* PCIE MEM */ + { 4, 0x59, MV_PCIE_MEM_PHYS_BASE, MV_PCIE_MEM_SIZE, -1 }, + + /* PCI MEM */ + { 3, 0x59, MV_PCI_MEM_PHYS_BASE, MV_PCI_MEM_SIZE, -1 }, + + /* Device bus BOOT */ + { 1, 0x0f, MV_DEV_BOOT_PHYS_BASE, MV_DEV_BOOT_SIZE, -1 }, + + /* Device bus CS0 */ + { 1, 0x1e, MV_DEV_CS0_PHYS_BASE, MV_DEV_CS0_SIZE, -1 }, + + /* Device bus CS1 */ + { 1, 0x1d, MV_DEV_CS1_PHYS_BASE, MV_DEV_CS1_SIZE, -1 }, + + /* Device bus CS2 */ + { 1, 0x1b, MV_DEV_CS2_PHYS_BASE, MV_DEV_CS2_SIZE, -1 }, +}; +const struct decode_win *idma_wins = idma_win_tbl; +int idma_wins_no = sizeof(idma_win_tbl) / sizeof(struct decode_win); diff --git a/sys/arm/mv/orion/std.db88f5xxx b/sys/arm/mv/orion/std.db88f5xxx new file mode 100644 index 0000000..e5a0dd2 --- /dev/null +++ b/sys/arm/mv/orion/std.db88f5xxx @@ -0,0 +1,13 @@ +# $FreeBSD$ + +include "../mv/std.mv" +files "../mv/orion/files.db88f5xxx" + +makeoptions KERNPHYSADDR=0x00400000 +makeoptions KERNVIRTADDR=0xc0400000 + +options KERNPHYSADDR=0x00400000 +options KERNVIRTADDR=0xc0400000 +options PHYSADDR=0x00000000 +options PHYSMEM_SIZE=0x08000000 +options STARTUP_PAGETABLE_ADDR=0x00100000 diff --git a/sys/arm/mv/rtc.c b/sys/arm/mv/rtc.c new file mode 100644 index 0000000..80b8343 --- /dev/null +++ b/sys/arm/mv/rtc.c @@ -0,0 +1,184 @@ +/*- + * 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/bus.h> +#include <sys/lock.h> +#include <sys/time.h> +#include <sys/clock.h> +#include <sys/resource.h> +#include <sys/systm.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include "clock_if.h" + +#define MV_RTC_TIME_REG 0x00 +#define MV_RTC_DATE_REG 0x04 +#define YEAR_BASE 2000 + +struct mv_rtc_softc { + device_t dev; + struct resource *res[1]; +}; + +static struct resource_spec res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int mv_rtc_probe(device_t dev); +static int mv_rtc_attach(device_t dev); + +static int mv_rtc_gettime(device_t dev, struct timespec *ts); +static int mv_rtc_settime(device_t dev, struct timespec *ts); + +static uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off); +static int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, + uint32_t val); + +static device_method_t mv_rtc_methods[] = { + DEVMETHOD(device_probe, mv_rtc_probe), + DEVMETHOD(device_attach, mv_rtc_attach), + + DEVMETHOD(clock_gettime, mv_rtc_gettime), + DEVMETHOD(clock_settime, mv_rtc_settime), + + { 0, 0 }, +}; + +static driver_t mv_rtc_driver = { + "rtc", + mv_rtc_methods, + sizeof(struct mv_rtc_softc), +}; +static devclass_t mv_rtc_devclass; + +DRIVER_MODULE(mv_rtc, mbus, mv_rtc_driver, mv_rtc_devclass, 0, 0); + +static int +mv_rtc_probe(device_t dev) +{ + + device_set_desc(dev, "Marvell Integrated RTC"); + return (0); +} + +static int +mv_rtc_attach(device_t dev) +{ + struct mv_rtc_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + clock_register(dev, 1000000); + + if (bus_alloc_resources(dev, res_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + return (0); +} + +static int +mv_rtc_gettime(device_t dev, struct timespec *ts) +{ + struct clocktime ct; + struct mv_rtc_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + val = mv_rtc_reg_read(sc, MV_RTC_TIME_REG); + + ct.nsec = 0; + ct.sec = FROMBCD(val & 0x7f); + ct.min = FROMBCD((val & 0x7f00) >> 8); + ct.hour = FROMBCD((val & 0x3f0000) >> 16); + ct.dow = FROMBCD((val & 0x7000000) >> 24) - 1; + + val = mv_rtc_reg_read(sc, MV_RTC_DATE_REG); + + ct.day = FROMBCD(val & 0x7f); + ct.mon = FROMBCD((val & 0x1f00) >> 8); + ct.year = YEAR_BASE + FROMBCD((val & 0xff0000) >> 16); + + return (clock_ct_to_ts(&ct, ts)); +} + +static int +mv_rtc_settime(device_t dev, struct timespec *ts) +{ + struct clocktime ct; + struct mv_rtc_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + /* Resolution: 1 sec */ + if (ts->tv_nsec >= 500000000) + ts->tv_sec++; + ts->tv_nsec = 0; + clock_ts_to_ct(ts, &ct); + + val = TOBCD(ct.sec) | (TOBCD(ct.min) << 8) | + (TOBCD(ct.hour) << 16) | (TOBCD( ct.dow + 1) << 24); + mv_rtc_reg_write(sc, MV_RTC_TIME_REG, val); + + val = TOBCD(ct.day) | (TOBCD(ct.mon) << 8) | + (TOBCD(ct.year - YEAR_BASE) << 16); + mv_rtc_reg_write(sc, MV_RTC_DATE_REG, val); + + return (0); +} + +static uint32_t +mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off) +{ + + return (bus_read_4(sc->res[0], off)); +} + +static int +mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val) +{ + + bus_write_4(sc->res[0], off, val); + return (0); +} diff --git a/sys/arm/mv/std.mv b/sys/arm/mv/std.mv new file mode 100644 index 0000000..6210102 --- /dev/null +++ b/sys/arm/mv/std.mv @@ -0,0 +1,5 @@ +# $FreeBSD$ + +files "../mv/files.mv" +cpu CPU_ARM9E +makeoptions CONF_CFLAGS="-march=armv5te" diff --git a/sys/arm/mv/timer.c b/sys/arm/mv/timer.c new file mode 100644 index 0000000..d239b9a --- /dev/null +++ b/sys/arm/mv/timer.c @@ -0,0 +1,381 @@ +/*- + * Copyright (c) 2006 Benno Rice. + * Copyright (C) 2007-2008 MARVELL INTERNATIONAL LTD. + * All rights reserved. + * + * Adapted to Marvell SoC 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. + * + * 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. + * + * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_timer.c, rev 1 + */ + +#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/malloc.h> +#include <sys/rman.h> +#include <sys/timetc.h> +#include <sys/watchdog.h> +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/frame.h> +#include <machine/intr.h> + +#include <arm/mv/mvreg.h> +#include <arm/mv/mvvar.h> + +#define MV_TIMER_TICK (get_tclk() / hz) +#define INITIAL_TIMECOUNTER (0xffffffff) +#define MAX_WATCHDOG_TICKS (0xffffffff) + +struct mv_timer_softc { + struct resource * timer_res[2]; + bus_space_tag_t timer_bst; + bus_space_handle_t timer_bsh; + struct mtx timer_mtx; +}; + +static struct resource_spec mv_timer_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static struct mv_timer_softc *timer_softc = NULL; +static int timers_initialized = 0; + +static int mv_timer_probe(device_t); +static int mv_timer_attach(device_t); + +static int mv_hardclock(void *); +static unsigned mv_timer_get_timecount(struct timecounter *); + +static uint32_t mv_get_timer_control(void); +static void mv_set_timer_control(uint32_t); +static uint32_t mv_get_timer(uint32_t); +static void mv_set_timer(uint32_t, uint32_t); +static void mv_set_timer_rel(uint32_t, uint32_t); +static void mv_watchdog_enable(void); +static void mv_watchdog_disable(void); +static void mv_watchdog_event(void *, unsigned int, int *); +static void mv_setup_timer(void); +static void mv_setup_timercount(void); + +static struct timecounter mv_timer_timecounter = { + .tc_get_timecount = mv_timer_get_timecount, + .tc_name = "CPU Timer", + .tc_frequency = 0, /* This is assigned on the fly in the init sequence */ + .tc_counter_mask = ~0u, + .tc_quality = 1000, +}; + +static int +mv_timer_probe(device_t dev) +{ + + device_set_desc(dev, "Marvell CPU Timer"); + return (0); +} + +static int +mv_timer_attach(device_t dev) +{ + int error; + void *ihl; + struct mv_timer_softc *sc; + + if (timer_softc != NULL) + return (ENXIO); + + sc = (struct mv_timer_softc *)device_get_softc(dev); + timer_softc = sc; + + error = bus_alloc_resources(dev, mv_timer_spec, sc->timer_res); + if (error) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + sc->timer_bst = rman_get_bustag(sc->timer_res[0]); + sc->timer_bsh = rman_get_bushandle(sc->timer_res[0]); + + mtx_init(&timer_softc->timer_mtx, "watchdog", NULL, MTX_DEF); + mv_watchdog_disable(); + EVENTHANDLER_REGISTER(watchdog_list, mv_watchdog_event, sc, 0); + + if (bus_setup_intr(dev, sc->timer_res[1], INTR_TYPE_CLK, + mv_hardclock, NULL, NULL, &ihl) != 0) { + bus_release_resources(dev, mv_timer_spec, sc->timer_res); + device_printf(dev, "could not setup hardclock interrupt\n"); + return (ENXIO); + } + + mv_setup_timercount(); + timers_initialized = 1; + + return (0); +} + +static int +mv_hardclock(void *arg) +{ + uint32_t irq_cause; + struct trapframe *frame; + + frame = (struct trapframe *)arg; + hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); + + irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE); + irq_cause &= ~(IRQ_TIMER0); + write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause); + + return (FILTER_HANDLED); +} + +static device_method_t mv_timer_methods[] = { + DEVMETHOD(device_probe, mv_timer_probe), + DEVMETHOD(device_attach, mv_timer_attach), + + { 0, 0 } +}; + +static driver_t mv_timer_driver = { + "timer", + mv_timer_methods, + sizeof(struct mv_timer_softc), +}; + +static devclass_t mv_timer_devclass; + +DRIVER_MODULE(timer, mbus, mv_timer_driver, mv_timer_devclass, 0, 0); + +static unsigned +mv_timer_get_timecount(struct timecounter *tc) +{ + + return (INITIAL_TIMECOUNTER - mv_get_timer(1)); +} + +void +cpu_initclocks(void) +{ + uint32_t irq_cause, irq_mask; + + mv_setup_timer(); + + irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE); + irq_cause &= ~(IRQ_TIMER0); + write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause); + + irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK); + irq_mask |= IRQ_TIMER0_MASK; + write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask); + + mv_timer_timecounter.tc_frequency = get_tclk(); + tc_init(&mv_timer_timecounter); +} + +void +cpu_startprofclock(void) +{ + +} + +void +cpu_stopprofclock(void) +{ + +} + +void +DELAY(int usec) +{ + uint32_t val, val_temp; + int32_t nticks; + + if (!timers_initialized) { + for (; usec > 0; usec--) + for (val = 100; val > 0; val--) + ; + return; + } + + val = mv_get_timer(1); + nticks = ((get_tclk() / 1000000 + 1) * usec); + + while (nticks > 0) { + val_temp = mv_get_timer(1); + if (val > val_temp) + nticks -= (val - val_temp); + else + nticks -= (val + (INITIAL_TIMECOUNTER - val_temp)); + + val = val_temp; + } +} + +static uint32_t +mv_get_timer_control(void) +{ + + return (bus_space_read_4(timer_softc->timer_bst, + timer_softc->timer_bsh, CPU_TIMER_CONTROL)); +} + +static void +mv_set_timer_control(uint32_t val) +{ + + bus_space_write_4(timer_softc->timer_bst, + timer_softc->timer_bsh, CPU_TIMER_CONTROL, val); +} + +static uint32_t +mv_get_timer(uint32_t timer) +{ + + return (bus_space_read_4(timer_softc->timer_bst, + timer_softc->timer_bsh, CPU_TIMER0 + timer * 0x8)); +} + +static void +mv_set_timer(uint32_t timer, uint32_t val) +{ + + bus_space_write_4(timer_softc->timer_bst, + timer_softc->timer_bsh, CPU_TIMER0 + timer * 0x8, val); +} + +static void +mv_set_timer_rel(uint32_t timer, uint32_t val) +{ + + bus_space_write_4(timer_softc->timer_bst, + timer_softc->timer_bsh, CPU_TIMER0_REL + timer * 0x8, val); +} + +static void +mv_watchdog_enable(void) +{ + uint32_t val; + uint32_t irq_cause, irq_mask; + + irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE); + irq_cause &= ~(IRQ_TIMER_WD); + write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause); + + irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK); + irq_mask |= IRQ_TIMER_WD_MASK; + write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask); + + val = read_cpu_ctrl(RSTOUTn_MASK); + val |= WD_RST_OUT_EN; + write_cpu_ctrl(RSTOUTn_MASK, val); + + val = mv_get_timer_control(); + val |= CPU_TIMER_WD_EN | CPU_TIMER_WD_AUTO; + mv_set_timer_control(val); +} + +static void +mv_watchdog_disable(void) +{ + uint32_t val; + uint32_t irq_cause, irq_mask; + + val = mv_get_timer_control(); + val &= ~(CPU_TIMER_WD_EN | CPU_TIMER_WD_AUTO); + mv_set_timer_control(val); + + val = read_cpu_ctrl(RSTOUTn_MASK); + val &= ~WD_RST_OUT_EN; + write_cpu_ctrl(RSTOUTn_MASK, val); + + irq_mask = read_cpu_ctrl(BRIDGE_IRQ_MASK); + irq_mask &= ~(IRQ_TIMER_WD_MASK); + write_cpu_ctrl(BRIDGE_IRQ_MASK, irq_mask); + + irq_cause = read_cpu_ctrl(BRIDGE_IRQ_CAUSE); + irq_cause &= ~(IRQ_TIMER_WD); + write_cpu_ctrl(BRIDGE_IRQ_CAUSE, irq_cause); +} + + +/* + * Watchdog event handler. + */ +static void +mv_watchdog_event(void *arg, unsigned int cmd, int *error) +{ + uint64_t ns; + uint64_t ticks; + + mtx_lock(&timer_softc->timer_mtx); + if (cmd == 0) + mv_watchdog_disable(); + else { + /* + * Watchdog timeout is in nanosecs, calculation according to + * watchdog(9) + */ + ns = (uint64_t)1 << (cmd & WD_INTERVAL); + ticks = (uint64_t)(ns * get_tclk()) / 1000000000; + if (ticks > MAX_WATCHDOG_TICKS) + mv_watchdog_disable(); + else { + /* Timer 2 is the watchdog */ + mv_set_timer(2, ticks); + mv_watchdog_enable(); + *error = 0; + } + } + mtx_unlock(&timer_softc->timer_mtx); +} + +static void +mv_setup_timer(void) +{ + uint32_t val; + + mv_set_timer_rel(0, MV_TIMER_TICK); + mv_set_timer(0, MV_TIMER_TICK); + val = mv_get_timer_control(); + val |= CPU_TIMER0_EN | CPU_TIMER0_AUTO; + mv_set_timer_control(val); +} + +static void +mv_setup_timercount(void) +{ + uint32_t val; + + mv_set_timer_rel(1, INITIAL_TIMECOUNTER); + mv_set_timer(1, INITIAL_TIMECOUNTER); + val = mv_get_timer_control(); + val |= CPU_TIMER1_EN | CPU_TIMER1_AUTO; + mv_set_timer_control(val); +} diff --git a/sys/arm/mv/twsi.c b/sys/arm/mv/twsi.c new file mode 100644 index 0000000..15ec415 --- /dev/null +++ b/sys/arm/mv/twsi.c @@ -0,0 +1,523 @@ +/*- + * 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. + */ + +/* + * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell + * SoCs. Supports master operation only, and works in polling mode. + * + * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software + * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices". + */ + +#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/resource.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <sys/rman.h> + +#include <sys/lock.h> +#include <sys/mutex.h> + +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> +#include "iicbus_if.h" + +#define MV_TWSI_NAME "twsi" + +#define TWSI_SLAVE_ADDR 0x00 +#define TWSI_EXT_SLAVE_ADDR 0x10 +#define TWSI_DATA 0x04 + +#define TWSI_CONTROL 0x08 +#define TWSI_CONTROL_ACK (1 << 2) +#define TWSI_CONTROL_IFLG (1 << 3) +#define TWSI_CONTROL_STOP (1 << 4) +#define TWSI_CONTROL_START (1 << 5) +#define TWSI_CONTROL_TWSIEN (1 << 6) +#define TWSI_CONTROL_INTEN (1 << 7) + +#define TWSI_STATUS 0x0c +#define TWSI_STATUS_START 0x08 +#define TWSI_STATUS_RPTD_START 0x10 +#define TWSI_STATUS_ADDR_W_ACK 0x18 +#define TWSI_STATUS_DATA_WR_ACK 0x28 +#define TWSI_STATUS_ADDR_R_ACK 0x40 +#define TWSI_STATUS_DATA_RD_ACK 0x50 +#define TWSI_STATUS_DATA_RD_NOACK 0x58 + +#define TWSI_BAUD_RATE 0x0c +#define TWSI_BAUD_RATE_94DOT3 0x53 /* N=3, M=10 */ +#define TWSI_BAUD_RATE_74DOT1 0x6b /* N=3, M=13 */ +#define TWSI_BAUD_RATE_51DOT8 0x4c /* N=4, M=9 */ +#define TWSI_BAUD_RATE_99DOT7 0x66 /* N=6, M=12 */ + +#define TWSI_SOFT_RESET 0x1c + +#define TWSI_DEBUG +#undef TWSI_DEBUG + +#ifdef TWSI_DEBUG +#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +struct mv_twsi_softc { + device_t dev; + struct resource *res[1]; /* SYS_RES_MEMORY */ + struct mtx mutex; + device_t iicbus; +}; + +static int mv_twsi_probe(device_t); +static int mv_twsi_attach(device_t); +static int mv_twsi_detach(device_t); + +static int mv_twsi_reset(device_t dev, u_char speed, u_char addr, + u_char *oldaddr); +static int mv_twsi_repeated_start(device_t dev, u_char slave, int timeout); +static int mv_twsi_start(device_t dev, u_char slave, int timeout); +static int mv_twsi_stop(device_t dev); +static int mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, + int delay); +static int mv_twsi_write(device_t dev, char *buf, int len, int *sent, + int timeout); + +static struct resource_spec res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static device_method_t mv_twsi_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, mv_twsi_probe), + DEVMETHOD(device_attach, mv_twsi_attach), + DEVMETHOD(device_detach, mv_twsi_detach), + + /* iicbus interface */ + DEVMETHOD(iicbus_callback, iicbus_null_callback), + DEVMETHOD(iicbus_repeated_start, mv_twsi_repeated_start), + DEVMETHOD(iicbus_start, mv_twsi_start), + DEVMETHOD(iicbus_stop, mv_twsi_stop), + DEVMETHOD(iicbus_write, mv_twsi_write), + DEVMETHOD(iicbus_read, mv_twsi_read), + DEVMETHOD(iicbus_reset, mv_twsi_reset), + DEVMETHOD(iicbus_transfer, iicbus_transfer_gen), + { 0, 0 } +}; + +static devclass_t mv_twsi_devclass; + +static driver_t mv_twsi_driver = { + MV_TWSI_NAME, + mv_twsi_methods, + sizeof(struct mv_twsi_softc), +}; + +DRIVER_MODULE(twsi, mbus, mv_twsi_driver, mv_twsi_devclass, 0, 0); +DRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0); +MODULE_DEPEND(twsi, iicbus, 1, 1, 1); + +static __inline uint32_t +TWSI_READ(struct mv_twsi_softc *sc, bus_size_t off) +{ + + return (bus_read_4(sc->res[0], off)); +} + +static __inline void +TWSI_WRITE(struct mv_twsi_softc *sc, bus_size_t off, uint32_t val) +{ + + bus_write_4(sc->res[0], off, val); +} + +static __inline void +twsi_control_clear(struct mv_twsi_softc *sc, uint32_t mask) +{ + uint32_t val; + + val = TWSI_READ(sc, TWSI_CONTROL); + val &= ~mask; + TWSI_WRITE(sc, TWSI_CONTROL, val); +} + +static __inline void +twsi_control_set(struct mv_twsi_softc *sc, uint32_t mask) +{ + uint32_t val; + + val = TWSI_READ(sc, TWSI_CONTROL); + val |= mask; + TWSI_WRITE(sc, TWSI_CONTROL, val); +} + +static __inline void +twsi_clear_iflg(struct mv_twsi_softc *sc) +{ + + DELAY(1000); + twsi_control_clear(sc, TWSI_CONTROL_IFLG); + DELAY(1000); +} + + +/* + * timeout given in us + * returns + * 0 on sucessfull mask change + * non-zero on timeout + */ +static int +twsi_poll_ctrl(struct mv_twsi_softc *sc, int timeout, uint32_t mask) +{ + + timeout /= 10; + while (!(TWSI_READ(sc, TWSI_CONTROL) & mask)) { + DELAY(10); + if (--timeout < 0) + return (timeout); + } + return (0); +} + + +/* + * 'timeout' is given in us. Note also that timeout handling is not exact -- + * twsi_locked_start() total wait can be more than 2 x timeout + * (twsi_poll_ctrl() is called twice). 'mask' can be either TWSI_STATUS_START + * or TWSI_STATUS_RPTD_START + */ +static int +twsi_locked_start(device_t dev, struct mv_twsi_softc *sc, int32_t mask, + u_char slave, int timeout) +{ + int read_access, iflg_set = 0; + uint32_t status; + + mtx_assert(&sc->mutex, MA_OWNED); + + if (mask == TWSI_STATUS_RPTD_START) + /* read IFLG to know if it should be cleared later; from NBSD */ + iflg_set = TWSI_READ(sc, TWSI_CONTROL) & TWSI_CONTROL_IFLG; + + twsi_control_set(sc, TWSI_CONTROL_START); + + if (mask == TWSI_STATUS_RPTD_START && iflg_set) { + debugf("IFLG set, clearing\n"); + twsi_clear_iflg(sc); + } + + /* + * Without this delay we timeout checking IFLG if the timeout is 0. + * NBSD driver always waits here too. + */ + DELAY(1000); + + if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { + debugf("timeout sending %sSTART condition\n", + mask == TWSI_STATUS_START ? "" : "repeated "); + return (IIC_ETIMEOUT); + } + + status = TWSI_READ(sc, TWSI_STATUS); + if (status != mask) { + debugf("wrong status (%02x) after sending %sSTART condition\n", + status, mask == TWSI_STATUS_START ? "" : "repeated "); + return (IIC_ESTATUS); + } + + TWSI_WRITE(sc, TWSI_DATA, slave); + DELAY(1000); + twsi_clear_iflg(sc); + + if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { + debugf("timeout sending slave address\n"); + return (IIC_ETIMEOUT); + } + + read_access = (slave & 0x1) ? 1 : 0; + status = TWSI_READ(sc, TWSI_STATUS); + if (status != (read_access ? + TWSI_STATUS_ADDR_R_ACK : TWSI_STATUS_ADDR_W_ACK)) { + debugf("no ACK (status: %02x) after sending slave address\n", + status); + return (IIC_ENOACK); + } + + return (IIC_NOERR); +} + +static int +mv_twsi_probe(device_t dev) +{ + + device_set_desc(dev, "Marvell Integrated I2C Bus Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +mv_twsi_attach(device_t dev) +{ + struct mv_twsi_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + mtx_init(&sc->mutex, device_get_nameunit(dev), MV_TWSI_NAME, MTX_DEF); + + /* Allocate IO resources */ + if (bus_alloc_resources(dev, res_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + mv_twsi_detach(dev); + return (ENXIO); + } + + sc->iicbus = device_add_child(dev, "iicbus", -1); + if (sc->iicbus == NULL) { + device_printf(dev, "could not add iicbus child\n"); + mv_twsi_detach(dev); + return (ENXIO); + } + + bus_generic_attach(dev); + return (0); +} + +static int +mv_twsi_detach(device_t dev) +{ + struct mv_twsi_softc *sc; + int rv; + + sc = device_get_softc(dev); + + if ((rv = bus_generic_detach(dev)) != 0) + return (rv); + + if (sc->iicbus != NULL) + if ((rv = device_delete_child(dev, sc->iicbus)) != 0) + return (rv); + + bus_release_resources(dev, res_spec, sc->res); + + mtx_destroy(&sc->mutex); + return (0); +} + +/* + * Only slave mode supported, disregard [old]addr + */ +static int +mv_twsi_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct mv_twsi_softc *sc; + uint32_t baud_rate; + + sc = device_get_softc(dev); + + switch (speed) { + case IIC_SLOW: + baud_rate = TWSI_BAUD_RATE_51DOT8; + break; + case IIC_FAST: + baud_rate = TWSI_BAUD_RATE_51DOT8; + break; + case IIC_UNKNOWN: + case IIC_FASTEST: + default: + baud_rate = TWSI_BAUD_RATE_99DOT7; + break; + } + + mtx_lock(&sc->mutex); + TWSI_WRITE(sc, TWSI_SOFT_RESET, 0x0); + DELAY(2000); + TWSI_WRITE(sc, TWSI_BAUD_RATE, baud_rate); + TWSI_WRITE(sc, TWSI_CONTROL, TWSI_CONTROL_TWSIEN | TWSI_CONTROL_ACK); + DELAY(1000); + mtx_unlock(&sc->mutex); + + return (0); +} + +/* + * timeout is given in us + */ +static int +mv_twsi_repeated_start(device_t dev, u_char slave, int timeout) +{ + struct mv_twsi_softc *sc; + int rv; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mutex); + rv = twsi_locked_start(dev, sc, TWSI_STATUS_RPTD_START, slave, + timeout); + mtx_unlock(&sc->mutex); + + if (rv) { + mv_twsi_stop(dev); + return (rv); + } else + return (IIC_NOERR); +} + +/* + * timeout is given in us + */ +static int +mv_twsi_start(device_t dev, u_char slave, int timeout) +{ + struct mv_twsi_softc *sc; + int rv; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mutex); + rv = twsi_locked_start(dev, sc, TWSI_STATUS_START, slave, timeout); + mtx_unlock(&sc->mutex); + + if (rv) { + mv_twsi_stop(dev); + return (rv); + } else + return (IIC_NOERR); +} + +static int +mv_twsi_stop(device_t dev) +{ + struct mv_twsi_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mutex); + twsi_control_set(sc, TWSI_CONTROL_STOP); + DELAY(1000); + twsi_clear_iflg(sc); + mtx_unlock(&sc->mutex); + + return (IIC_NOERR); +} + +static int +mv_twsi_read(device_t dev, char *buf, int len, int *read, int last, int delay) +{ + struct mv_twsi_softc *sc; + uint32_t status; + int last_byte, rv; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mutex); + *read = 0; + while (*read < len) { + /* + * Check if we are reading last byte of the last buffer, + * do not send ACK then, per I2C specs + */ + last_byte = ((*read == len - 1) && last) ? 1 : 0; + if (last_byte) + twsi_control_clear(sc, TWSI_CONTROL_ACK); + else + twsi_control_set(sc, TWSI_CONTROL_ACK); + + DELAY (1000); + twsi_clear_iflg(sc); + + if (twsi_poll_ctrl(sc, delay, TWSI_CONTROL_IFLG)) { + debugf("timeout reading data\n"); + rv = IIC_ETIMEOUT; + goto out; + } + + status = TWSI_READ(sc, TWSI_STATUS); + if (status != (last_byte ? + TWSI_STATUS_DATA_RD_NOACK : TWSI_STATUS_DATA_RD_ACK)) { + debugf("wrong status (%02x) while reading\n", status); + rv = IIC_ESTATUS; + goto out; + } + + *buf++ = TWSI_READ(sc, TWSI_DATA); + (*read)++; + } + rv = IIC_NOERR; +out: + mtx_unlock(&sc->mutex); + return (rv); +} + +static int +mv_twsi_write(device_t dev, char *buf, int len, int *sent, int timeout) +{ + struct mv_twsi_softc *sc; + uint32_t status; + int rv; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mutex); + *sent = 0; + while (*sent < len) { + TWSI_WRITE(sc, TWSI_DATA, *buf++); + + twsi_clear_iflg(sc); + if (twsi_poll_ctrl(sc, timeout, TWSI_CONTROL_IFLG)) { + debugf("timeout writing data\n"); + rv = IIC_ETIMEOUT; + goto out; + } + + status = TWSI_READ(sc, TWSI_STATUS); + if (status != TWSI_STATUS_DATA_WR_ACK) { + debugf("wrong status (%02x) while writing\n", status); + rv = IIC_ESTATUS; + goto out; + } + (*sent)++; + } + rv = IIC_NOERR; +out: + mtx_unlock(&sc->mutex); + return (rv); +} |