summaryrefslogtreecommitdiffstats
path: root/sys/powerpc
diff options
context:
space:
mode:
authorraj <raj@FreeBSD.org>2008-12-18 18:27:12 +0000
committerraj <raj@FreeBSD.org>2008-12-18 18:27:12 +0000
commit188fbdbc6c6e07890800f4b1e6081abd971e1c8b (patch)
tree414398636e7b21076b7dbacfa639c59ac00f1658 /sys/powerpc
parent021bbbd29f0cd53f8701fb27bd43a344f5bb02f7 (diff)
downloadFreeBSD-src-188fbdbc6c6e07890800f4b1e6081abd971e1c8b.zip
FreeBSD-src-188fbdbc6c6e07890800f4b1e6081abd971e1c8b.tar.gz
Extend and improve MPC85XX Local Bus management.
- Make LBC resources management self-contained: introduce explicit LBC resources definition (much like the OCP), provide dedicated rman for LB mem space. - Full configuration of an LB chip select device: program LAW and BR/OR, map into KVA, handle all LB attributes (bus width, machine select, ecc, write protect etc). - Factor out LAW manipulation routines into shared code, adjust OCP area accordingly. - Other LBC fixes and clean-ups. Obtained from: Semihalf
Diffstat (limited to 'sys/powerpc')
-rw-r--r--sys/powerpc/mpc85xx/lbc.c268
-rw-r--r--sys/powerpc/mpc85xx/lbc.h44
-rw-r--r--sys/powerpc/mpc85xx/mpc85xx.c69
-rw-r--r--sys/powerpc/mpc85xx/mpc85xx.h2
-rw-r--r--sys/powerpc/mpc85xx/ocpbus.c32
5 files changed, 346 insertions, 69 deletions
diff --git a/sys/powerpc/mpc85xx/lbc.c b/sys/powerpc/mpc85xx/lbc.c
index 4b168b8..2267245 100644
--- a/sys/powerpc/mpc85xx/lbc.c
+++ b/sys/powerpc/mpc85xx/lbc.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2006-2008, Juniper Networks, Inc.
+ * Copyright (c) 2008 Semihalf, Rafal Czubak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,6 +45,8 @@ __FBSDID("$FreeBSD$");
#include <vm/pmap.h>
#include <powerpc/mpc85xx/lbc.h>
+#include <powerpc/mpc85xx/mpc85xx.h>
+#include <powerpc/mpc85xx/ocpbus.h>
struct lbc_softc {
device_t sc_dev;
@@ -54,16 +57,41 @@ struct lbc_softc {
int sc_rid;
struct rman sc_rman;
- uintptr_t sc_kva;
+ vm_offset_t sc_kva[LBC_DEV_MAX];
};
struct lbc_devinfo {
int lbc_devtype;
- int lbc_memtype;
- /* Also the BAR number */
+ /* LBC child unit. It also represents resource table entry number */
int lbc_unit;
};
+/* Resources for MPC8555CDS system */
+const struct lbc_resource mpc85xx_lbc_resources[] = {
+ /* Boot flash bank */
+ {
+ LBC_DEVTYPE_CFI, 0, 0xff800000, 0x00800000, 16,
+ LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
+ LBCRES_ATOM_DISABLED, 0
+ },
+
+ /* Second flash bank */
+ {
+ LBC_DEVTYPE_CFI, 1, 0xff000000, 0x00800000, 16,
+ LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
+ LBCRES_ATOM_DISABLED, 0
+ },
+
+ /* DS1553 RTC/NVRAM */
+ {
+ LBC_DEVTYPE_RTC, 2, 0xf8000000, 0x8000, 8,
+ LBCRES_MSEL_GPCM, LBCRES_DECC_DISABLED,
+ LBCRES_ATOM_DISABLED, 0
+ },
+
+ {0}
+};
+
static int lbc_probe(device_t);
static int lbc_attach(device_t);
static int lbc_shutdown(device_t);
@@ -108,26 +136,170 @@ static driver_t lbc_driver = {
devclass_t lbc_devclass;
DRIVER_MODULE(lbc, ocpbus, lbc_driver, lbc_devclass, 0, 0);
+static __inline void
+lbc_write_reg(struct lbc_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
+}
+
+static __inline uint32_t
+lbc_read_reg(struct lbc_softc *sc, bus_size_t off)
+{
+
+ return (bus_space_read_4(sc->sc_bst, sc->sc_bsh, off));
+}
+
+/*
+ * Calculate address mask used by OR(n) registers. Use memory region size to
+ * determine mask value. The size must be a power of two and within the range
+ * of 32KB - 4GB. Otherwise error code is returned. Value representing
+ * 4GB size can be passed as 0xffffffff.
+ */
+static uint32_t
+lbc_address_mask(uint32_t size)
+{
+ int n = 15;
+
+ if (size == ~0UL)
+ return (0);
+
+ while (n < 32) {
+ if (size == (1UL << n))
+ break;
+ n++;
+ }
+
+ if (n == 32)
+ return (EINVAL);
+
+ return (0xffff8000 << (n - 15));
+}
+
static device_t
-lbc_mk_child(device_t dev, int type, int mtype, int unit)
+lbc_mk_child(device_t dev, const struct lbc_resource *lbcres)
{
struct lbc_devinfo *dinfo;
device_t child;
+ if (lbcres->lbr_unit > LBC_DEV_MAX - 1)
+ return (NULL);
+
child = device_add_child(dev, NULL, -1);
if (child == NULL) {
- device_printf(dev, "could not add child device\n");
+ device_printf(dev, "could not add LBC child device\n");
return (NULL);
}
dinfo = malloc(sizeof(struct lbc_devinfo), M_DEVBUF, M_WAITOK | M_ZERO);
- dinfo->lbc_devtype = type;
- dinfo->lbc_memtype = mtype;
- dinfo->lbc_unit = unit;
+ dinfo->lbc_devtype = lbcres->lbr_devtype;
+ dinfo->lbc_unit = lbcres->lbr_unit;
device_set_ivars(child, dinfo);
return (child);
}
static int
+lbc_init_child(device_t dev, device_t child)
+{
+ struct lbc_softc *sc;
+ struct lbc_devinfo *dinfo;
+ const struct lbc_resource *res;
+ u_long start, size;
+ uint32_t regbuff;
+ int error, unit;
+
+ sc = device_get_softc(dev);
+ dinfo = device_get_ivars(child);
+
+ res = mpc85xx_lbc_resources;
+
+ regbuff = 0;
+ unit = -1;
+ for (; res->lbr_devtype; res++) {
+ if (res->lbr_unit != dinfo->lbc_unit)
+ continue;
+
+ start = res->lbr_base_addr;
+ size = res->lbr_size;
+ unit = res->lbr_unit;
+
+ /*
+ * Configure LAW for this LBC device and map its physical
+ * memory region into KVA
+ */
+ error = law_enable(OCP85XX_TGTIF_LBC, start, size);
+ if (error)
+ return (error);
+
+ sc->sc_kva[unit] = (vm_offset_t)pmap_mapdev(start, size);
+ if (sc->sc_kva[unit] == 0) {
+ law_disable(OCP85XX_TGTIF_LBC, start, size);
+ return (ENOSPC);
+ }
+
+ /*
+ * Compute and program BR value
+ */
+ regbuff |= start;
+
+ switch (res->lbr_port_size) {
+ case 8:
+ regbuff |= (1 << 11);
+ break;
+ case 16:
+ regbuff |= (2 << 11);
+ break;
+ case 32:
+ regbuff |= (3 << 11);
+ break;
+ default:
+ error = EINVAL;
+ goto fail;
+ }
+ regbuff |= (res->lbr_decc << 9);
+ regbuff |= (res->lbr_wp << 8);
+ regbuff |= (res->lbr_msel << 5);
+ regbuff |= (res->lbr_atom << 2);
+ regbuff |= 1;
+
+ lbc_write_reg(sc, LBC85XX_BR(unit), regbuff);
+
+ /*
+ * Compute and program OR value
+ */
+ regbuff = 0;
+ regbuff |= lbc_address_mask(size);
+
+ switch (res->lbr_msel) {
+ case LBCRES_MSEL_GPCM:
+ /* TODO Add flag support for option registers */
+ regbuff |= 0x00000ff7;
+ break;
+ case LBCRES_MSEL_FCM:
+ printf("FCM mode not supported yet!");
+ error = ENOSYS;
+ goto fail;
+ case LBCRES_MSEL_UPMA:
+ case LBCRES_MSEL_UPMB:
+ case LBCRES_MSEL_UPMC:
+ printf("UPM mode not supported yet!");
+ error = ENOSYS;
+ goto fail;
+ }
+
+ lbc_write_reg(sc, LBC85XX_OR(unit), regbuff);
+
+ return (0);
+ }
+fail:
+ if (unit != -1) {
+ law_disable(OCP85XX_TGTIF_LBC, start, size);
+ pmap_unmapdev(sc->sc_kva[unit], size);
+ return (error);
+ } else
+ return (ENOENT);
+}
+
+static int
lbc_probe(device_t dev)
{
device_t parent;
@@ -150,7 +322,7 @@ lbc_attach(device_t dev)
{
struct lbc_softc *sc;
struct rman *rm;
- u_long start, size;
+ const struct lbc_resource *lbcres;
int error;
sc = device_get_softc(dev);
@@ -165,15 +337,11 @@ lbc_attach(device_t dev)
sc->sc_bst = rman_get_bustag(sc->sc_res);
sc->sc_bsh = rman_get_bushandle(sc->sc_res);
- error = bus_get_resource(dev, SYS_RES_MEMORY, 1, &start, &size);
- if (error)
- goto fail;
-
rm = &sc->sc_rman;
rm->rm_type = RMAN_ARRAY;
rm->rm_descr = "MPC85XX Local Bus Space";
- rm->rm_start = start;
- rm->rm_end = start + size - 1;
+ rm->rm_start = 0UL;
+ rm->rm_end = ~0UL;
error = rman_init(rm);
if (error)
goto fail;
@@ -184,13 +352,35 @@ lbc_attach(device_t dev)
goto fail;
}
- sc->sc_kva = (uintptr_t)pmap_mapdev(start, size);
-
- lbc_mk_child(dev, LBC_DEVTYPE_CFI, 0, 0);
+ /*
+ * Initialize configuration register:
+ * - enable Local Bus
+ * - set data buffer control signal function
+ * - disable parity byte select
+ * - set ECC parity type
+ * - set bus monitor timing and timer prescale
+ */
+ lbc_write_reg(sc, LBC85XX_LBCR, 0x00000000);
+
+ /*
+ * Initialize clock ratio register:
+ * - disable PLL bypass mode
+ * - configure LCLK delay cycles for the assertion of LALE
+ * - set system clock divider
+ */
+ lbc_write_reg(sc, LBC85XX_LCRR, 0x00030008);
+
+ lbcres = mpc85xx_lbc_resources;
+
+ for (; lbcres->lbr_devtype; lbcres++)
+ if (!lbc_mk_child(dev, lbcres)) {
+ error = ENXIO;
+ goto fail;
+ }
return (bus_generic_attach(dev));
- fail:
+fail:
bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
return (error);
}
@@ -208,11 +398,13 @@ lbc_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 lbc_softc *sc;
+ struct lbc_devinfo *dinfo;
struct resource *rv;
struct rman *rm;
int error;
sc = device_get_softc(dev);
+ dinfo = device_get_ivars(child);
if (type != SYS_RES_MEMORY && type != SYS_RES_IRQ)
return (NULL);
@@ -225,6 +417,12 @@ lbc_alloc_resource(device_t dev, device_t child, int type, int *rid,
return (bus_alloc_resource(dev, type, rid, start, end, count,
flags));
+ if (!sc->sc_kva[dinfo->lbc_unit]) {
+ error = lbc_init_child(dev, child);
+ if (error)
+ return (NULL);
+ }
+
error = lbc_get_resource(dev, child, type, *rid, &start, &count);
if (error)
return (NULL);
@@ -234,8 +432,7 @@ lbc_alloc_resource(device_t dev, device_t child, int type, int *rid,
rv = rman_reserve_resource(rm, start, end, count, flags, child);
if (rv != NULL) {
rman_set_bustag(rv, &bs_be_tag);
- rman_set_bushandle(rv, sc->sc_kva + rman_get_start(rv) -
- rm->rm_start);
+ rman_set_bushandle(rv, rman_get_start(rv));
}
return (rv);
}
@@ -279,12 +476,6 @@ lbc_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
case LBC_IVAR_DEVTYPE:
*result = dinfo->lbc_devtype;
return (0);
- case LBC_IVAR_CLOCK:
- *result = 1843200;
- return (0);
- case LBC_IVAR_REGSHIFT:
- *result = 0;
- return (0);
default:
break;
}
@@ -305,24 +496,35 @@ lbc_get_resource(device_t dev, device_t child, int type, int rid,
{
struct lbc_softc *sc;
struct lbc_devinfo *dinfo;
+ const struct lbc_resource *lbcres;
if (type != SYS_RES_MEMORY)
return (ENOENT);
- /* Currently all devices have a single RID per type. */
+ /* Currently all LBC devices have a single RID per type. */
if (rid != 0)
return (ENOENT);
sc = device_get_softc(dev);
dinfo = device_get_ivars(child);
+ if ((dinfo->lbc_unit < 0) || (dinfo->lbc_unit > (LBC_DEV_MAX - 1)))
+ return (EINVAL);
+
+ lbcres = mpc85xx_lbc_resources;
+
switch (dinfo->lbc_devtype) {
case LBC_DEVTYPE_CFI:
- *startp = sc->sc_rman.rm_start;
- *countp = sc->sc_rman.rm_end - sc->sc_rman.rm_start + 1;
- break;
+ case LBC_DEVTYPE_RTC:
+ for (; lbcres->lbr_devtype; lbcres++) {
+ if (dinfo->lbc_unit == lbcres->lbr_unit) {
+ *startp = sc->sc_kva[lbcres->lbr_unit];
+ *countp = lbcres->lbr_size;
+ return (0);
+ }
+ }
default:
- return(EDOOFUS);
+ return (EDOOFUS);
}
- return(0);
+ return (0);
}
diff --git a/sys/powerpc/mpc85xx/lbc.h b/sys/powerpc/mpc85xx/lbc.h
index 8bc7843..24e741a 100644
--- a/sys/powerpc/mpc85xx/lbc.h
+++ b/sys/powerpc/mpc85xx/lbc.h
@@ -30,11 +30,49 @@
#define _MACHINE_LBC_H_
#define LBC_IVAR_DEVTYPE 1
-#define LBC_IVAR_CLOCK 2
-#define LBC_IVAR_REGSHIFT 3
+
+/* Maximum number of devices on Local Bus */
+#define LBC_DEV_MAX 8
/* Device types. */
#define LBC_DEVTYPE_CFI 1
-#define LBC_DEVTYPE_UART 2
+#define LBC_DEVTYPE_RTC 2
+
+/* Local access registers */
+#define LBC85XX_BR(n) (OCP85XX_LBC_OFF + (8 * n))
+#define LBC85XX_OR(n) (OCP85XX_LBC_OFF + 4 + (8 * n))
+#define LBC85XX_LBCR (OCP85XX_LBC_OFF + 0xd0)
+#define LBC85XX_LCRR (OCP85XX_LBC_OFF + 0xd4)
+
+/* LBC machine select */
+#define LBCRES_MSEL_GPCM 0
+#define LBCRES_MSEL_FCM 1
+#define LBCRES_MSEL_UPMA 8
+#define LBCRES_MSEL_UPMB 9
+#define LBCRES_MSEL_UPMC 10
+
+/* LBC data error checking modes */
+#define LBCRES_DECC_DISABLED 0
+#define LBCRES_DECC_NORMAL 1
+#define LBCRES_DECC_RMW 2
+
+/* LBC atomic operation modes */
+#define LBCRES_ATOM_DISABLED 0
+#define LBCRES_ATOM_RAWA 1
+#define LBCRES_ATOM_WARA 2
+
+struct lbc_resource {
+ int lbr_devtype; /* LBC device type */
+ int lbr_unit; /* Resource table entry number */
+ vm_paddr_t lbr_base_addr; /* Device mem region base address */
+ size_t lbr_size; /* Device mem region size */
+ int lbr_port_size; /* Data bus width */
+ uint8_t lbr_msel; /* LBC machine select */
+ uint8_t lbr_decc; /* Data error checking mode */
+ uint8_t lbr_atom; /* Atomic operation mode */
+ uint8_t lbr_wp; /* Write protect */
+};
+
+extern const struct lbc_resource mpc85xx_lbc_resources[];
#endif /* _MACHINE_LBC_H_ */
diff --git a/sys/powerpc/mpc85xx/mpc85xx.c b/sys/powerpc/mpc85xx/mpc85xx.c
index b75e301..de9c771 100644
--- a/sys/powerpc/mpc85xx/mpc85xx.c
+++ b/sys/powerpc/mpc85xx/mpc85xx.c
@@ -10,9 +10,6 @@
* 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 the author 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
@@ -64,6 +61,72 @@ ccsr_write4(uintptr_t addr, uint32_t val)
__asm __volatile("eieio; sync");
}
+static __inline int
+law_getmax(void)
+{
+ uint32_t ver;
+
+ ver = SVR_VER(mfspr(SPR_SVR));
+ if (ver == SVR_MPC8572E || ver == SVR_MPC8572)
+ return (12);
+ else
+ return (8);
+}
+
+#define _LAW_SR(trgt,size) (0x80000000 | (trgt << 20) | (ffsl(size) - 2))
+#define _LAW_BAR(addr) (addr >> 12)
+
+int
+law_enable(int trgt, u_long addr, u_long size)
+{
+ uint32_t bar, sr;
+ int i, law_max;
+
+ law_max = law_getmax();
+ bar = _LAW_BAR(addr);
+ sr = _LAW_SR(trgt, size);
+
+ /* Bail if already programmed. */
+ for (i = 0; i < law_max; i++)
+ if (sr == ccsr_read4(OCP85XX_LAWSR(i)) &&
+ bar == ccsr_read4(OCP85XX_LAWBAR(i)))
+ return (0);
+
+ /* Find an unused access window. */
+ for (i = 0; i < law_max; i++)
+ if ((ccsr_read4(OCP85XX_LAWSR(i)) & 0x80000000) == 0)
+ break;
+
+ if (i == law_max)
+ return (ENOSPC);
+
+ ccsr_write4(OCP85XX_LAWBAR(i), bar);
+ ccsr_write4(OCP85XX_LAWSR(i), sr);
+ return (0);
+}
+
+int
+law_disable(int trgt, u_long addr, u_long size)
+{
+ uint32_t bar, sr;
+ int i, law_max;
+
+ law_max = law_getmax();
+ bar = _LAW_BAR(addr);
+ sr = _LAW_SR(trgt, size);
+
+ /* Find and disable requested LAW. */
+ for (i = 0; i < law_max; i++)
+ if (sr == ccsr_read4(OCP85XX_LAWSR(i)) &&
+ bar == ccsr_read4(OCP85XX_LAWBAR(i))) {
+ ccsr_write4(OCP85XX_LAWBAR(i), 0);
+ ccsr_write4(OCP85XX_LAWSR(i), 0);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
void
cpu_reset(void)
{
diff --git a/sys/powerpc/mpc85xx/mpc85xx.h b/sys/powerpc/mpc85xx/mpc85xx.h
index 525783a..4d31b58 100644
--- a/sys/powerpc/mpc85xx/mpc85xx.h
+++ b/sys/powerpc/mpc85xx/mpc85xx.h
@@ -31,5 +31,7 @@
uint32_t ccsr_read4(uintptr_t addr);
void ccsr_write4(uintptr_t addr, uint32_t val);
+int law_enable(int trgt, u_long addr, u_long size);
+int law_disable(int trgt, u_long addr, u_long size);
#endif /* _MPC85XX_H_ */
diff --git a/sys/powerpc/mpc85xx/ocpbus.c b/sys/powerpc/mpc85xx/ocpbus.c
index 74af18c..ae56fb0 100644
--- a/sys/powerpc/mpc85xx/ocpbus.c
+++ b/sys/powerpc/mpc85xx/ocpbus.c
@@ -138,8 +138,6 @@ static int
ocpbus_write_law(int trgt, int type, u_long *startp, u_long *countp)
{
u_long addr, size;
- int i;
- uint32_t bar, sr;
switch (type) {
case SYS_RES_MEMORY:
@@ -156,10 +154,6 @@ ocpbus_write_law(int trgt, int type, u_long *startp, u_long *countp)
addr = 0xA0000000;
size = 0x10000000;
break;
- case OCP85XX_TGTIF_LBC:
- addr = 0xff800000;
- size = 0x00800000;
- break;
default:
return (EINVAL);
}
@@ -189,28 +183,7 @@ ocpbus_write_law(int trgt, int type, u_long *startp, u_long *countp)
*startp = addr;
*countp = size;
- /* Program the LAWs for this memory range. */
- bar = addr >> 12;
- sr = 0x80000000 | (trgt << 20) | (ffsl(size) - 2);
-
- /* Check if already programmed. */
- for (i = 0; i < law_max; i++) {
- if (sr == ccsr_read4(OCP85XX_LAWSR(i)) &&
- bar == ccsr_read4(OCP85XX_LAWBAR(i)))
- return (0);
- }
-
- /* Find an unused access window .*/
- for (i = 0; i < law_max; i++) {
- if ((ccsr_read4(OCP85XX_LAWSR(i)) & 0x80000000) == 0)
- break;
- }
- if (i == law_max)
- return (ENOSPC);
-
- ccsr_write4(OCP85XX_LAWBAR(i), bar);
- ccsr_write4(OCP85XX_LAWSR(i), sr);
- return (0);
+ return (law_enable(trgt, *startp, *countp));
}
static int
@@ -232,7 +205,7 @@ ocpbus_probe(device_t dev)
}
static int
-ocpbus_attach (device_t dev)
+ocpbus_attach(device_t dev)
{
struct ocpbus_softc *sc;
int error, i, tgt;
@@ -377,7 +350,6 @@ const struct ocp_resource mpc8555_resources[] = {
{OCPBUS_DEVTYPE_LBC, 0, SYS_RES_MEMORY, 0, OCP85XX_LBC_OFF,
OCP85XX_LBC_SIZE},
- {OCPBUS_DEVTYPE_LBC, 0, SYS_RES_MEMORY, 1, 0, OCP85XX_TGTIF_LBC},
{OCPBUS_DEVTYPE_I2C, 0, SYS_RES_MEMORY, 0, OCP85XX_I2C0_OFF,
OCP85XX_I2C_SIZE},
OpenPOWER on IntegriCloud