aboutsummaryrefslogtreecommitdiff
path: root/sys/powerpc/mpc85xx
diff options
context:
space:
mode:
authorMarcel Moolenaar <marcel@FreeBSD.org>2012-05-24 21:23:13 +0000
committerMarcel Moolenaar <marcel@FreeBSD.org>2012-05-24 21:23:13 +0000
commitf6703dd295bd57ef56b7a437eda24dca2ffa7e88 (patch)
treed0be012a064397629839e629bf501e7eadfd4487 /sys/powerpc/mpc85xx
parent20b796126754ca7077c2cc34364294e53d70868b (diff)
downloadsrc-f6703dd295bd57ef56b7a437eda24dca2ffa7e88.tar.gz
src-f6703dd295bd57ef56b7a437eda24dca2ffa7e88.zip
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).
Notes
Notes: svn path=/head/; revision=235937
Diffstat (limited to 'sys/powerpc/mpc85xx')
-rw-r--r--sys/powerpc/mpc85xx/lbc.c194
-rw-r--r--sys/powerpc/mpc85xx/lbc.h49
2 files changed, 179 insertions, 64 deletions
diff --git a/sys/powerpc/mpc85xx/lbc.c b/sys/powerpc/mpc85xx/lbc.c
index e993bced9ae0..9737215e787e 100644
--- a/sys/powerpc/mpc85xx/lbc.c
+++ b/sys/powerpc/mpc85xx/lbc.c
@@ -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;
+ int r;
- for (i = 0; i < LBC_DEV_MAX; i++) {
- if (sc->sc_banks[i].size == 0)
- continue;
+ r = 0;
+ while (r < LBC_DEV_MAX) {
+ if (sc->sc_range[r].size == 0)
+ return;
- 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);
+ 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, struct lbc_softc *sc,
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 child)
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));
+}
diff --git a/sys/powerpc/mpc85xx/lbc.h b/sys/powerpc/mpc85xx/lbc.h
index ee58607661d5..53d9e1c71a76 100644
--- a/sys/powerpc/mpc85xx/lbc.h
+++ b/sys/powerpc/mpc85xx/lbc.h
@@ -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_ */