diff --git a/sys/mips/mips/bus_space_generic.c b/sys/mips/mips/bus_space_generic.c index 15780a25d057..b934ec42ad13 100644 --- a/sys/mips/mips/bus_space_generic.c +++ b/sys/mips/mips/bus_space_generic.c @@ -206,6 +206,14 @@ static struct bus_space generic_space = { #define wr8(a, v) oct_write8(a, v) #define wr16(a, v) oct_write16(a, v) #define wr32(a, v) oct_write32(a, v) +#elif defined(CPU_SB1) && _BYTE_ORDER == _BIG_ENDIAN +#include +#define rd8(a) sb_big_endian_read8(a) +#define rd16(a) sb_big_endian_read16(a) +#define rd32(a) sb_big_endian_read32(a) +#define wr8(a, v) sb_big_endian_write8(a, v) +#define wr16(a, v) sb_big_endian_write16(a, v) +#define wr32(a, v) sb_big_endian_write32(a, v) #else #define rd8(a) readb(a) #define rd16(a) readw(a) diff --git a/sys/mips/sibyte/sb_asm.S b/sys/mips/sibyte/sb_asm.S index 19d00dd0bc9d..a822d79d8213 100644 --- a/sys/mips/sibyte/sb_asm.S +++ b/sys/mips/sibyte/sb_asm.S @@ -28,6 +28,7 @@ #include #include +#include /* * We compile a 32-bit kernel to run on the SB-1 processor which is a 64-bit @@ -50,7 +51,7 @@ LEAF(sb_load64) ld v1, 0(a0) /* result = *(uint64_t *)ptr */ move v0, v1 -#if defined(TARGET_BIG_ENDIAN) +#if _BYTE_ORDER == _BIG_ENDIAN dsll32 v1, v1, 0 dsrl32 v1, v1, 0 /* v1 = lower_uint32(result) */ jr ra @@ -68,7 +69,7 @@ END(sb_load64) * Return value: void */ LEAF(sb_store64) -#if defined(TARGET_BIG_ENDIAN) +#if _BYTE_ORDER == _BIG_ENDIAN dsll32 a2, a2, 0 /* a2 = upper_uint32(val) */ dsll32 a3, a3, 0 /* a3 = lower_uint32(val) */ dsrl32 a3, a3, 0 diff --git a/sys/mips/sibyte/sb_bus_space.h b/sys/mips/sibyte/sb_bus_space.h new file mode 100644 index 000000000000..f3364f07a29a --- /dev/null +++ b/sys/mips/sibyte/sb_bus_space.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2010 Neelkanth Natu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SB_BUS_SPACE_H_ +#define _SB_BUS_SPACE_H_ + +#include + +#if _BYTE_ORDER == _BIG_ENDIAN +uint8_t sb_big_endian_read8(bus_addr_t addr); +uint16_t sb_big_endian_read16(bus_addr_t addr); +uint32_t sb_big_endian_read32(bus_addr_t addr); +void sb_big_endian_write8(bus_addr_t addr, uint8_t val); +void sb_big_endian_write16(bus_addr_t addr, uint16_t val); +void sb_big_endian_write32(bus_addr_t addr, uint32_t val); +#endif + +#endif diff --git a/sys/mips/sibyte/sb_zbpci.c b/sys/mips/sibyte/sb_zbpci.c index 6107b36cc414..42fed5e161f2 100644 --- a/sys/mips/sibyte/sb_zbpci.c +++ b/sys/mips/sibyte/sb_zbpci.c @@ -50,6 +50,7 @@ #include "pcib_if.h" +#include "sb_bus_space.h" #include "sb_scd.h" __FBSDID("$FreeBSD$"); @@ -63,6 +64,15 @@ static const vm_paddr_t CFG_PADDR_BASE = 0xFE000000; static const u_long PCI_IOSPACE_ADDR = 0xFC000000; static const u_long PCI_IOSPACE_SIZE = 0x02000000; +#define PCI_MATCH_BYTE_LANES_START 0x40000000 +#define PCI_MATCH_BYTE_LANES_END 0x5FFFFFFF +#define PCI_MATCH_BYTE_LANES_SIZE 0x20000000 + +#define PCI_MATCH_BIT_LANES_MASK (1 << 29) +#define PCI_MATCH_BIT_LANES_START 0x60000000 +#define PCI_MATCH_BIT_LANES_END 0x7FFFFFFF +#define PCI_MATCH_BIT_LANES_SIZE 0x20000000 + static struct rman port_rman; static int @@ -111,6 +121,19 @@ zbpci_attach(device_t dev) if (res == NULL) panic("Cannot allocate resource for config space accesses."); + /* + * Allocate the entire "match bit lanes" address space. + */ +#if _BYTE_ORDER == _BIG_ENDIAN + rid = 2; + res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + PCI_MATCH_BIT_LANES_START, + PCI_MATCH_BIT_LANES_END, + PCI_MATCH_BIT_LANES_SIZE, 0); + if (res == NULL) + panic("Cannot allocate resource for pci match bit lanes."); +#endif /* _BYTE_ORDER ==_BIG_ENDIAN */ + /* * Allocate KVA for accessing PCI config space. */ @@ -177,21 +200,61 @@ static int zbpci_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { + int error; void *vaddr; - u_long paddr, psize; + u_long orig_paddr, paddr, psize; + + paddr = rman_get_start(res); + psize = rman_get_size(res); + orig_paddr = paddr; + +#if _BYTE_ORDER == _BIG_ENDIAN + /* + * The CFE allocates PCI memory resources that map to the + * "match byte lanes" address space. This address space works + * best for DMA transfers because it does not do any automatic + * byte swaps when data crosses the pci-cpu interface. + * + * This also makes it sub-optimal for accesses to PCI device + * registers because it exposes the little-endian nature of + * the PCI bus to the big-endian CPU. The Sibyte has another + * address window called the "match bit lanes" window which + * automatically swaps bytes when data crosses the pci-cpu + * interface. + * + * We "assume" that any bus_space memory accesses done by the + * CPU to a PCI device are register/configuration accesses and + * are done through the "match bit lanes" window. Any DMA + * transfers will continue to be through the "match byte lanes" + * window because the PCI BAR registers will not be changed. + */ + if (type == SYS_RES_MEMORY) { + if (paddr >= PCI_MATCH_BYTE_LANES_START && + paddr + psize - 1 <= PCI_MATCH_BYTE_LANES_END) { + paddr |= PCI_MATCH_BIT_LANES_MASK; + rman_set_start(res, paddr); + rman_set_end(res, paddr + psize - 1); + } + } +#endif if (type != SYS_RES_IOPORT) { - return (bus_generic_activate_resource(bus, child, type, - rid, res)); + error = bus_generic_activate_resource(bus, child, type, + rid, res); +#if _BYTE_ORDER == _BIG_ENDIAN + if (type == SYS_RES_MEMORY) { + rman_set_start(res, orig_paddr); + rman_set_end(res, orig_paddr + psize - 1); + } +#endif + return (error); } /* * Map the I/O space resource through the memory window starting * at PCI_IOSPACE_ADDR. */ - paddr = rman_get_start(res) + PCI_IOSPACE_ADDR; - psize = rman_get_size(res); - vaddr = pmap_mapdev(paddr, psize); + vaddr = pmap_mapdev(paddr + PCI_IOSPACE_ADDR, psize); rman_set_virtual(res, vaddr); rman_set_bustag(res, mips_bus_space_generic); @@ -280,6 +343,9 @@ zbpci_config_space_va(int bus, int slot, int func, int reg, int bytes) va_page = zbpci_config_space[cpu].vaddr; pa = CFG_PADDR_BASE | (bus << 16) | (slot << 11) | (func << 8) | reg; +#if _BYTE_ORDER == _BIG_ENDIAN + pa = pa ^ (4 - bytes); +#endif pa_page = pa & ~(PAGE_SIZE - 1); if (zbpci_config_space[cpu].paddr != pa_page) { pmap_kremove(va_page); @@ -397,3 +463,82 @@ DEFINE_CLASS_1(zbpci, zbpci_driver, zbpci_methods, 0, pcib_driver); static devclass_t zbpci_devclass; DRIVER_MODULE(zbpci, zbbus, zbpci_driver, zbpci_devclass, 0, 0); + +/* + * Big endian bus space routines + */ +#if _BYTE_ORDER == _BIG_ENDIAN + +/* + * The CPU correctly deals with the big-endian to little-endian swap if + * we are accessing 4 bytes at a time. However if we want to read 1 or 2 + * bytes then we need to fudge the address generated by the CPU such that + * it generates the right byte enables on the PCI bus. + */ +static bus_addr_t +sb_match_bit_lane_addr(bus_addr_t addr, int bytes) +{ + vm_offset_t pa; + + pa = vtophys(addr); + + if (pa >= PCI_MATCH_BIT_LANES_START && pa <= PCI_MATCH_BIT_LANES_END) + return (addr ^ (4 - bytes)); + else + return (addr); +} + +uint8_t +sb_big_endian_read8(bus_addr_t addr) +{ + bus_addr_t addr2; + + addr2 = sb_match_bit_lane_addr(addr, 1); + return (readb(addr2)); +} + +uint16_t +sb_big_endian_read16(bus_addr_t addr) +{ + bus_addr_t addr2; + + addr2 = sb_match_bit_lane_addr(addr, 2); + return (readw(addr2)); +} + +uint32_t +sb_big_endian_read32(bus_addr_t addr) +{ + bus_addr_t addr2; + + addr2 = sb_match_bit_lane_addr(addr, 4); + return (readl(addr2)); +} + +void +sb_big_endian_write8(bus_addr_t addr, uint8_t val) +{ + bus_addr_t addr2; + + addr2 = sb_match_bit_lane_addr(addr, 1); + writeb(addr2, val); +} + +void +sb_big_endian_write16(bus_addr_t addr, uint16_t val) +{ + bus_addr_t addr2; + + addr2 = sb_match_bit_lane_addr(addr, 2); + writew(addr2, val); +} + +void +sb_big_endian_write32(bus_addr_t addr, uint32_t val) +{ + bus_addr_t addr2; + + addr2 = sb_match_bit_lane_addr(addr, 4); + writel(addr2, val); +} +#endif /* _BIG_ENDIAN */