Various fixes to get the SWARM config working on a big-endian Sibyte CPU.

Getting the little-endian PCI bus working on the big-endian CPU proved to be
quite challenging. We let the PCI devices be mapped in the "match byte lanes"
address window. This is where they are mapped by the CFE and DMA transfers
generated to or from addresses within this window are not subject to automatic
byte-swapping.

However any access by the driver to memory-mapped pci space is redirected
via the "match bit lanes" address window. We get the benefit of automatic
byte swapping through this address window and drivers don't need to change
to deal with CPU big-endianness.
This commit is contained in:
Neel Natu 2010-02-17 06:43:37 +00:00
parent f77a51d7a8
commit f7bb996d92
4 changed files with 205 additions and 8 deletions

View File

@ -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 <mips/sibyte/sb_bus_space.h>
#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)

View File

@ -28,6 +28,7 @@
#include <machine/asm.h>
#include <machine/cpuregs.h>
#include <machine/endian.h>
/*
* 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

View File

@ -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 <machine/endian.h>
#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

View File

@ -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 */