Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 24 May 2012 21:23:13 +0000 (UTC)
From:      Marcel Moolenaar <marcel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r235937 - head/sys/powerpc/mpc85xx
Message-ID:  <201205242123.q4OLNDv7061108@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marcel
Date: Thu May 24 21:23:13 2012
New Revision: 235937
URL: http://svn.freebsd.org/changeset/base/235937

Log:
  A few improvements:
  1.  Define all registers. These definitions are needed to support
      the FCM driver for direct-connect NAND.
  2.  Repurpose lbc_read_reg() and lbc_write_reg() for use by localbus
      attached device drivers. Use bus_space functions directly in the
      lbc driver itself.
  3.  Be smarter about programming LAWs and mapping memory. The ranges
      defined in the FDT are per bank (= chip select) and since we can
      have up to 8 banks, we could easily use more than 8 LAWs or TLB
      enrties when per-bank memory ranges need multiple LAWs or TLBs
      due to alignment or size constraints.
      We now combine all memory ranges into the fewest possible set of
      contiguous regions and program the hardware for that. Thus, a
      cleverly written FDT with 8 devices may still only need 1 LAW or
      1 TLB entry. Note that the memory ranges can be assigned randomly
      to the banks. We sort as we build to handle that.
  4.  Support the FCM when programming the OR register. This is mostly
      for documention purposes as we do not have a way to define the
      mode for a bank.
  5.  Remove Semihalf-ism: do not define DEBUG (only to undefine it
      again).

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

Modified: head/sys/powerpc/mpc85xx/lbc.c
==============================================================================
--- head/sys/powerpc/mpc85xx/lbc.c	Thu May 24 21:13:24 2012	(r235936)
+++ head/sys/powerpc/mpc85xx/lbc.c	Thu May 24 21:23:13 2012	(r235937)
@@ -56,9 +56,6 @@ __FBSDID("$FreeBSD$");
 #include "ofw_bus_if.h"
 #include "lbc.h"
 
-#define DEBUG
-#undef DEBUG
-
 #ifdef DEBUG
 #define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
     printf(fmt,##args); } while (0)
@@ -66,20 +63,6 @@ __FBSDID("$FreeBSD$");
 #define debugf(fmt, args...)
 #endif
 
-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));
-}
-
 static MALLOC_DEFINE(M_LBC, "localbus", "localbus devices information");
 
 static int lbc_probe(device_t);
@@ -161,46 +144,123 @@ lbc_address_mask(uint32_t size)
 static void
 lbc_banks_unmap(struct lbc_softc *sc)
 {
-	int i;
-
-	for (i = 0; i < LBC_DEV_MAX; i++) {
-		if (sc->sc_banks[i].size == 0)
-			continue;
+	int r;
 
-		law_disable(OCP85XX_TGTIF_LBC, sc->sc_banks[i].pa,
-		    sc->sc_banks[i].size);
-		pmap_unmapdev(sc->sc_banks[i].va, sc->sc_banks[i].size);
+	r = 0;
+	while (r < LBC_DEV_MAX) {
+		if (sc->sc_range[r].size == 0)
+			return;
+
+		pmap_unmapdev(sc->sc_range[r].kva, sc->sc_range[r].size);
+		law_disable(OCP85XX_TGTIF_LBC, sc->sc_range[r].addr,
+		    sc->sc_range[r].size);
+		r++;
 	}
 }
 
 static int
 lbc_banks_map(struct lbc_softc *sc)
 {
-	u_long start, size;
-	int error, i;
+	vm_paddr_t end, start;
+	vm_size_t size;
+	u_int i, r, ranges, s;
+	int error;
 
+	bzero(sc->sc_range, sizeof(sc->sc_range));
+
+	/*
+	 * Determine number of discontiguous address ranges to program.
+	 */
+	ranges = 0;
 	for (i = 0; i < LBC_DEV_MAX; i++) {
-		if (sc->sc_banks[i].size == 0)
+		size = sc->sc_banks[i].size;
+		if (size == 0)
 			continue;
 
-		/* Physical address start/size. */
-		start = sc->sc_banks[i].pa;
-		size = sc->sc_banks[i].size;
+		start = sc->sc_banks[i].addr;
+		for (r = 0; r < ranges; r++) {
+			/* Avoid wrap-around bugs. */
+			end = sc->sc_range[r].addr - 1 + sc->sc_range[r].size;
+			if (start > 0 && end == start - 1) {
+				sc->sc_range[r].size += size;
+				break;
+			}
+			/* Avoid wrap-around bugs. */
+			end = start - 1 + size;
+			if (sc->sc_range[r].addr > 0 &&
+			    end == sc->sc_range[r].addr - 1) {
+				sc->sc_range[r].addr = start;
+				sc->sc_range[r].size += size;
+				break;
+			}
+		}
+		if (r == ranges) {
+			/* New range; add using insertion sort */
+			r = 0;
+			while (r < ranges && sc->sc_range[r].addr < start)
+				r++;
+			for (s = ranges; s > r; s--)
+				sc->sc_range[s] = sc->sc_range[s-1];
+			sc->sc_range[r].addr = start;
+			sc->sc_range[r].size = size;
+			ranges++;
+		}
+	}
 
-		/*
-		 * Configure LAW for this LBC bank (CS) and map its physical
-		 * memory region into KVA.
-		 */
+	/*
+	 * Ranges are sorted so quickly go over the list to merge ranges
+	 * that grew toward each other while building the ranges.
+	 */
+	r = 0;
+	while (r < ranges - 1) {
+		end = sc->sc_range[r].addr + sc->sc_range[r].size;
+		if (end != sc->sc_range[r+1].addr) {
+			r++;
+			continue;
+		}
+		sc->sc_range[r].size += sc->sc_range[r+1].size;
+		for (s = r + 1; s < ranges - 1; s++)
+			sc->sc_range[s] = sc->sc_range[s+1];
+		bzero(&sc->sc_range[s], sizeof(sc->sc_range[s]));
+		ranges--;
+	}
+
+	/*
+	 * Configure LAW for the LBC ranges and map the physical memory
+	 * range into KVA.
+	 */
+	for (r = 0; r < ranges; r++) {
+		start = sc->sc_range[r].addr;
+		size = sc->sc_range[r].size;
 		error = law_enable(OCP85XX_TGTIF_LBC, start, size);
 		if (error)
 			return (error);
+		sc->sc_range[r].kva = (vm_offset_t)pmap_mapdev(start, size);
+	}
+
+	/* XXX: need something better here? */
+	if (ranges == 0)
+		return (EINVAL);
+
+	/* Assign KVA to banks based on the enclosing range. */
+	for (i = 0; i < LBC_DEV_MAX; i++) {
+		size = sc->sc_banks[i].size;
+		if (size == 0)
+			continue;
 
-		sc->sc_banks[i].va = (vm_offset_t)pmap_mapdev(start, size);
-		if (sc->sc_banks[i].va == 0) {
-			lbc_banks_unmap(sc);
-			return (ENOSPC);
+		start = sc->sc_banks[i].addr;
+		for (r = 0; r < ranges; r++) {
+			end = sc->sc_range[r].addr - 1 + sc->sc_range[r].size;
+			if (start >= sc->sc_range[r].addr &&
+			    start - 1 + size <= end)
+				break;
+		}
+		if (r < ranges) {
+			sc->sc_banks[i].kva = sc->sc_range[r].kva +
+			    (start - sc->sc_range[r].addr);
 		}
 	}
+
 	return (0);
 }
 
@@ -213,14 +273,18 @@ lbc_banks_enable(struct lbc_softc *sc)
 
 	for (i = 0; i < LBC_DEV_MAX; i++) {
 		size = sc->sc_banks[i].size;
-		if (size == 0)
+		if (size == 0) {
+			bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+			    LBC85XX_BR(i), 0);
+			bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+			    LBC85XX_OR(i), 0);
 			continue;
+		}
+
 		/*
 		 * Compute and program BR value.
 		 */
-		regval = 0;
-		regval |= sc->sc_banks[i].pa;
-
+		regval = sc->sc_banks[i].addr;
 		switch (sc->sc_banks[i].width) {
 		case 8:
 			regval |= (1 << 11);
@@ -240,24 +304,22 @@ lbc_banks_enable(struct lbc_softc *sc)
 		regval |= (sc->sc_banks[i].msel << 5);
 		regval |= (sc->sc_banks[i].atom << 2);
 		regval |= 1;
-
-		lbc_write_reg(sc, LBC85XX_BR(i), regval);
+		bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+		    LBC85XX_BR(i), regval);
 
 		/*
 		 * Compute and program OR value.
 		 */
-		regval = 0;
-		regval |= lbc_address_mask(size);
-
+		regval = lbc_address_mask(size);
 		switch (sc->sc_banks[i].msel) {
 		case LBCRES_MSEL_GPCM:
 			/* TODO Add flag support for option registers */
-			regval |= 0x00000ff7;
+			regval |= 0x0ff7;
 			break;
 		case LBCRES_MSEL_FCM:
-			printf("FCM mode not supported yet!");
-			error = ENOSYS;
-			goto fail;
+			/* TODO Add flag support for options register */
+			regval |= 0x0796;
+			break;
 		case LBCRES_MSEL_UPMA:
 		case LBCRES_MSEL_UPMB:
 		case LBCRES_MSEL_UPMC:
@@ -265,7 +327,8 @@ lbc_banks_enable(struct lbc_softc *sc)
 			error = ENOSYS;
 			goto fail;
 		}
-		lbc_write_reg(sc, LBC85XX_OR(i), regval);
+		bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+		    LBC85XX_OR(i), regval);
 	}
 
 	/*
@@ -276,7 +339,7 @@ lbc_banks_enable(struct lbc_softc *sc)
 	 * - set ECC parity type
 	 * - set bus monitor timing and timer prescale
 	 */
-	lbc_write_reg(sc, LBC85XX_LBCR, 0);
+	bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LBCR, 0);
 
 	/*
 	 * Initialize clock ratio register:
@@ -284,8 +347,7 @@ lbc_banks_enable(struct lbc_softc *sc)
 	 * - configure LCLK delay cycles for the assertion of LALE
 	 * - set system clock divider
 	 */
-	lbc_write_reg(sc, LBC85XX_LCRR, 0x00030008);
-
+	bus_space_write_4(sc->sc_bst, sc->sc_bsh, LBC85XX_LCRR, 0x00030008);
 	return (0);
 
 fail:
@@ -348,7 +410,7 @@ fdt_lbc_reg_decode(phandle_t node, struc
 		reg += addr_cells - 1 + size_cells;
 
 		/* Calculate address range relative to VA base. */
-		start = sc->sc_banks[bank].va + start;
+		start = sc->sc_banks[bank].kva + start;
 		end = start + count - 1;
 
 		debugf("reg addr bank = %d, start = %lx, end = %lx, "
@@ -479,7 +541,7 @@ lbc_attach(device_t dev)
 		debugf("bank = %d, start = %lx, size = %lx\n", bank,
 		    start, size);
 
-		sc->sc_banks[bank].pa = start + offset;
+		sc->sc_banks[bank].addr = start + offset;
 		sc->sc_banks[bank].size = size;
 
 		/*
@@ -676,3 +738,21 @@ lbc_get_devinfo(device_t bus, device_t c
 	di = device_get_ivars(child);
 	return (&di->di_ofw);
 }
+
+void
+lbc_write_reg(device_t child, u_int off, uint32_t val)
+{
+	struct lbc_softc *sc;
+
+	sc = device_get_softc(device_get_parent(child));
+	bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);
+}
+
+uint32_t
+lbc_read_reg(device_t child, u_int off)
+{
+	struct lbc_softc *sc;
+
+	sc = device_get_softc(device_get_parent(child));
+	return (bus_space_read_4(sc->sc_bst, sc->sc_bsh, off));
+}

Modified: head/sys/powerpc/mpc85xx/lbc.h
==============================================================================
--- head/sys/powerpc/mpc85xx/lbc.h	Thu May 24 21:13:24 2012	(r235936)
+++ head/sys/powerpc/mpc85xx/lbc.h	Thu May 24 21:23:13 2012	(r235937)
@@ -33,10 +33,35 @@
 #define	LBC_DEV_MAX	8
 
 /* Local access registers */
-#define	LBC85XX_BR(n)	(8 * n)
-#define	LBC85XX_OR(n)	(4 + (8 * n))
-#define	LBC85XX_LBCR	(0xd0)
-#define	LBC85XX_LCRR	(0xd4)
+#define	LBC85XX_BR(n)	(0x0 + (8 * n))	/* Base register 0-7 */
+#define	LBC85XX_OR(n)	(0x4 + (8 * n))	/* Options register 0-7 */
+#define	LBC85XX_MAR	0x068		/* UPM address register */
+#define	LBC85XX_MAMR	0x070		/* UPMA mode register */
+#define	LBC85XX_MBMR	0x074		/* UPMB mode register */
+#define	LBC85XX_MCMR	0x078		/* UPMC mode register */
+#define	LBC85XX_MRTPR	0x084		/* Memory refresh timer prescaler */
+#define	LBC85XX_MDR	0x088		/* UPM data register */
+#define	LBC85XX_LSOR	0x090		/* Special operation initiation */
+#define	LBC85XX_LURT	0x0a0		/* UPM refresh timer */
+#define	LBC85XX_LSRT	0x0a4		/* SDRAM refresh timer */
+#define	LBC85XX_LTESR	0x0b0		/* Transfer error status register */
+#define	LBC85XX_LTEDR	0x0b4		/* Transfer error disable register */
+#define	LBC85XX_LTEIR	0x0b8		/* Transfer error interrupt register */
+#define	LBC85XX_LTEATR	0x0bc		/* Transfer error attributes register */
+#define	LBC85XX_LTEAR	0x0c0		/* Transfer error address register */
+#define	LBC85XX_LTECCR	0x0c4		/* Transfer error ECC register */
+#define	LBC85XX_LBCR	0x0d0		/* Configuration register */
+#define	LBC85XX_LCRR	0x0d4		/* Clock ratio register */
+#define	LBC85XX_FMR	0x0e0		/* Flash mode register */
+#define	LBC85XX_FIR	0x0e4		/* Flash instruction register */
+#define	LBC85XX_FCR	0x0e8		/* Flash command register */
+#define	LBC85XX_FBAR	0x0ec		/* Flash block address register */
+#define	LBC85XX_FPAR	0x0f0		/* Flash page address register */
+#define	LBC85XX_FBCR	0x0f4		/* Flash byte count register */
+#define	LBC85XX_FECC0	0x100		/* Flash ECC block 0 register */
+#define	LBC85XX_FECC1	0x104		/* Flash ECC block 0 register */
+#define	LBC85XX_FECC2	0x108		/* Flash ECC block 0 register */
+#define	LBC85XX_FECC3	0x10c		/* Flash ECC block 0 register */
 
 /* LBC machine select */
 #define	LBCRES_MSEL_GPCM	0
@@ -55,10 +80,16 @@
 #define	LBCRES_ATOM_RAWA	1
 #define	LBCRES_ATOM_WARA	2
 
+struct lbc_memrange {
+	vm_paddr_t	addr;
+	vm_size_t	size;
+	vm_offset_t	kva;
+};
+
 struct lbc_bank {
-	u_long		pa;		/* physical addr of the bank */
-	u_long		size;		/* bank size */
-	vm_offset_t	va;		/* VA of the bank */
+	vm_paddr_t	addr;		/* physical addr of the bank */
+	vm_size_t	size;		/* bank size */
+	vm_offset_t	kva;		/* VA of the bank */
 
 	/*
 	 * XXX the following bank attributes do not have properties specified
@@ -84,6 +115,7 @@ struct lbc_softc {
 	int			sc_addr_cells;
 	int			sc_size_cells;
 
+	struct lbc_memrange	sc_range[LBC_DEV_MAX];
 	struct lbc_bank		sc_banks[LBC_DEV_MAX];
 };
 
@@ -93,4 +125,7 @@ struct lbc_devinfo {
 	int			di_bank;
 };
 
+uint32_t	lbc_read_reg(device_t child, u_int off);
+void		lbc_write_reg(device_t child, u_int off, uint32_t val);
+
 #endif /* _MACHINE_LBC_H_ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201205242123.q4OLNDv7061108>