From 188fbdbc6c6e07890800f4b1e6081abd971e1c8b Mon Sep 17 00:00:00 2001 From: raj Date: Thu, 18 Dec 2008 18:27:12 +0000 Subject: 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 --- sys/powerpc/mpc85xx/lbc.c | 268 ++++++++++++++++++++++++++++++++++++------ sys/powerpc/mpc85xx/lbc.h | 44 ++++++- sys/powerpc/mpc85xx/mpc85xx.c | 69 ++++++++++- sys/powerpc/mpc85xx/mpc85xx.h | 2 + sys/powerpc/mpc85xx/ocpbus.c | 32 +---- 5 files changed, 346 insertions(+), 69 deletions(-) (limited to 'sys/powerpc') 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 #include +#include +#include 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}, -- cgit v1.1