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_ */
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to