diff options
author | raj <raj@FreeBSD.org> | 2008-12-18 18:27:12 +0000 |
---|---|---|
committer | raj <raj@FreeBSD.org> | 2008-12-18 18:27:12 +0000 |
commit | 188fbdbc6c6e07890800f4b1e6081abd971e1c8b (patch) | |
tree | 414398636e7b21076b7dbacfa639c59ac00f1658 /sys/powerpc/mpc85xx/lbc.c | |
parent | 021bbbd29f0cd53f8701fb27bd43a344f5bb02f7 (diff) | |
download | FreeBSD-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/mpc85xx/lbc.c')
-rw-r--r-- | sys/powerpc/mpc85xx/lbc.c | 268 |
1 files changed, 235 insertions, 33 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); } |