svn commit: r186288 - head/sys/powerpc/mpc85xx

Rafal Jaworowski raj at FreeBSD.org
Thu Dec 18 18:27:13 UTC 2008


Author: raj
Date: Thu Dec 18 18:27:12 2008
New Revision: 186288
URL: http://svn.freebsd.org/changeset/base/186288

Log:
  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

Modified:
  head/sys/powerpc/mpc85xx/lbc.c
  head/sys/powerpc/mpc85xx/lbc.h
  head/sys/powerpc/mpc85xx/mpc85xx.c
  head/sys/powerpc/mpc85xx/mpc85xx.h
  head/sys/powerpc/mpc85xx/ocpbus.c

Modified: head/sys/powerpc/mpc85xx/lbc.c
==============================================================================
--- head/sys/powerpc/mpc85xx/lbc.c	Thu Dec 18 15:56:12 2008	(r186287)
+++ head/sys/powerpc/mpc85xx/lbc.c	Thu Dec 18 18:27:12 2008	(r186288)
@@ -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_
     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_
 		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_
 	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 chi
 	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 
 {
 	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);
 }

Modified: head/sys/powerpc/mpc85xx/lbc.h
==============================================================================
--- head/sys/powerpc/mpc85xx/lbc.h	Thu Dec 18 15:56:12 2008	(r186287)
+++ head/sys/powerpc/mpc85xx/lbc.h	Thu Dec 18 18:27:12 2008	(r186288)
@@ -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_ */

Modified: head/sys/powerpc/mpc85xx/mpc85xx.c
==============================================================================
--- head/sys/powerpc/mpc85xx/mpc85xx.c	Thu Dec 18 15:56:12 2008	(r186287)
+++ head/sys/powerpc/mpc85xx/mpc85xx.c	Thu Dec 18 18:27:12 2008	(r186288)
@@ -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)
 {

Modified: head/sys/powerpc/mpc85xx/mpc85xx.h
==============================================================================
--- head/sys/powerpc/mpc85xx/mpc85xx.h	Thu Dec 18 15:56:12 2008	(r186287)
+++ head/sys/powerpc/mpc85xx/mpc85xx.h	Thu Dec 18 18:27:12 2008	(r186288)
@@ -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_ */

Modified: head/sys/powerpc/mpc85xx/ocpbus.c
==============================================================================
--- head/sys/powerpc/mpc85xx/ocpbus.c	Thu Dec 18 15:56:12 2008	(r186287)
+++ head/sys/powerpc/mpc85xx/ocpbus.c	Thu Dec 18 18:27:12 2008	(r186288)
@@ -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_l
 			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_l
 	*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_resour
 
 	{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},


More information about the svn-src-all mailing list