diff options
Diffstat (limited to 'arch/powerpc/platforms/85xx/p1022_ds.c')
-rw-r--r-- | arch/powerpc/platforms/85xx/p1022_ds.c | 230 |
1 files changed, 191 insertions, 39 deletions
diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index f700c81a1321..3c732acf331d 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -27,6 +27,7 @@ #include <sysdev/fsl_pci.h> #include <asm/udbg.h> #include <asm/fsl_guts.h> +#include <asm/fsl_lbc.h> #include "smp.h" #include "mpc85xx.h" @@ -142,17 +143,74 @@ static void p1022ds_set_gamma_table(enum fsl_diu_monitor_port port, { } +struct fsl_law { + u32 lawbar; + u32 reserved1; + u32 lawar; + u32 reserved[5]; +}; + +#define LAWBAR_MASK 0x00F00000 +#define LAWBAR_SHIFT 12 + +#define LAWAR_EN 0x80000000 +#define LAWAR_TGT_MASK 0x01F00000 +#define LAW_TRGT_IF_LBC (0x04 << 20) + +#define LAWAR_MASK (LAWAR_EN | LAWAR_TGT_MASK) +#define LAWAR_MATCH (LAWAR_EN | LAW_TRGT_IF_LBC) + +#define BR_BA 0xFFFF8000 + +/* + * Map a BRx value to a physical address + * + * The localbus BRx registers only store the lower 32 bits of the address. To + * obtain the upper four bits, we need to scan the LAW table. The entry which + * maps to the localbus will contain the upper four bits. + */ +static phys_addr_t lbc_br_to_phys(const void *ecm, unsigned int count, u32 br) +{ +#ifndef CONFIG_PHYS_64BIT + /* + * If we only have 32-bit addressing, then the BRx address *is* the + * physical address. + */ + return br & BR_BA; +#else + const struct fsl_law *law = ecm + 0xc08; + unsigned int i; + + for (i = 0; i < count; i++) { + u64 lawbar = in_be32(&law[i].lawbar); + u32 lawar = in_be32(&law[i].lawar); + + if ((lawar & LAWAR_MASK) == LAWAR_MATCH) + /* Extract the upper four bits */ + return (br & BR_BA) | ((lawbar & LAWBAR_MASK) << 12); + } + + return 0; +#endif +} + /** * p1022ds_set_monitor_port: switch the output to a different monitor port - * */ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port) { struct device_node *guts_node; - struct device_node *indirect_node = NULL; + struct device_node *lbc_node = NULL; + struct device_node *law_node = NULL; struct ccsr_guts __iomem *guts; + struct fsl_lbc_regs *lbc = NULL; + void *ecm = NULL; u8 __iomem *lbc_lcs0_ba = NULL; u8 __iomem *lbc_lcs1_ba = NULL; + phys_addr_t cs0_addr, cs1_addr; + u32 br0, or0, br1, or1; + const __be32 *iprop; + unsigned int num_laws; u8 b; /* Map the global utilities registers. */ @@ -168,22 +226,99 @@ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port) goto exit; } - indirect_node = of_find_compatible_node(NULL, NULL, - "fsl,p1022ds-indirect-pixis"); - if (!indirect_node) { - pr_err("p1022ds: missing pixis indirect mode node\n"); + lbc_node = of_find_compatible_node(NULL, NULL, "fsl,p1022-elbc"); + if (!lbc_node) { + pr_err("p1022ds: missing localbus node\n"); goto exit; } - lbc_lcs0_ba = of_iomap(indirect_node, 0); - if (!lbc_lcs0_ba) { - pr_err("p1022ds: could not map localbus chip select 0\n"); + lbc = of_iomap(lbc_node, 0); + if (!lbc) { + pr_err("p1022ds: could not map localbus node\n"); + goto exit; + } + + law_node = of_find_compatible_node(NULL, NULL, "fsl,ecm-law"); + if (!law_node) { + pr_err("p1022ds: missing local access window node\n"); + goto exit; + } + + ecm = of_iomap(law_node, 0); + if (!ecm) { + pr_err("p1022ds: could not map local access window node\n"); + goto exit; + } + + iprop = of_get_property(law_node, "fsl,num-laws", 0); + if (!iprop) { + pr_err("p1022ds: LAW node is missing fsl,num-laws property\n"); + goto exit; + } + num_laws = be32_to_cpup(iprop); + + /* + * Indirect mode requires both BR0 and BR1 to be set to "GPCM", + * otherwise writes to these addresses won't actually appear on the + * local bus, and so the PIXIS won't see them. + * + * In FCM mode, writes go to the NAND controller, which does not pass + * them to the localbus directly. So we force BR0 and BR1 into GPCM + * mode, since we don't care about what's behind the localbus any + * more. + */ + br0 = in_be32(&lbc->bank[0].br); + br1 = in_be32(&lbc->bank[1].br); + or0 = in_be32(&lbc->bank[0].or); + or1 = in_be32(&lbc->bank[1].or); + + /* Make sure CS0 and CS1 are programmed */ + if (!(br0 & BR_V) || !(br1 & BR_V)) { + pr_err("p1022ds: CS0 and/or CS1 is not programmed\n"); + goto exit; + } + + /* + * Use the existing BRx/ORx values if it's already GPCM. Otherwise, + * force the values to simple 32KB GPCM windows with the most + * conservative timing. + */ + if ((br0 & BR_MSEL) != BR_MS_GPCM) { + br0 = (br0 & BR_BA) | BR_V; + or0 = 0xFFFF8000 | 0xFF7; + out_be32(&lbc->bank[0].br, br0); + out_be32(&lbc->bank[0].or, or0); + } + if ((br1 & BR_MSEL) != BR_MS_GPCM) { + br1 = (br1 & BR_BA) | BR_V; + or1 = 0xFFFF8000 | 0xFF7; + out_be32(&lbc->bank[1].br, br1); + out_be32(&lbc->bank[1].or, or1); + } + + cs0_addr = lbc_br_to_phys(ecm, num_laws, br0); + if (!cs0_addr) { + pr_err("p1022ds: could not determine physical address for CS0" + " (BR0=%08x)\n", br0); + goto exit; + } + cs1_addr = lbc_br_to_phys(ecm, num_laws, br1); + if (!cs0_addr) { + pr_err("p1022ds: could not determine physical address for CS1" + " (BR1=%08x)\n", br1); goto exit; } - lbc_lcs1_ba = of_iomap(indirect_node, 1); + lbc_lcs0_ba = ioremap(cs0_addr, 1); + if (!lbc_lcs0_ba) { + pr_err("p1022ds: could not ioremap CS0 address %llx\n", + (unsigned long long)cs0_addr); + goto exit; + } + lbc_lcs1_ba = ioremap(cs1_addr, 1); if (!lbc_lcs1_ba) { - pr_err("p1022ds: could not map localbus chip select 1\n"); + pr_err("p1022ds: could not ioremap CS1 address %llx\n", + (unsigned long long)cs1_addr); goto exit; } @@ -254,10 +389,15 @@ exit: iounmap(lbc_lcs1_ba); if (lbc_lcs0_ba) iounmap(lbc_lcs0_ba); + if (lbc) + iounmap(lbc); + if (ecm) + iounmap(ecm); if (guts) iounmap(guts); - of_node_put(indirect_node); + of_node_put(law_node); + of_node_put(lbc_node); of_node_put(guts_node); } @@ -339,24 +479,6 @@ void __init p1022_ds_pic_init(void) #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) -/* - * Disables a node in the device tree. - * - * This function is called before kmalloc() is available, so the 'new' object - * should be allocated in the global area. The easiest way is to do that is - * to allocate one static local variable for each call to this function. - */ -static void __init disable_one_node(struct device_node *np, struct property *new) -{ - struct property *old; - - old = of_find_property(np, new->name, NULL); - if (old) - prom_update_property(np, new, old); - else - prom_add_property(np, new); -} - /* TRUE if there is a "video=fslfb" command-line parameter. */ static bool fslfb; @@ -419,28 +541,58 @@ static void __init p1022_ds_setup_arch(void) diu_ops.valid_monitor_port = p1022ds_valid_monitor_port; /* - * Disable the NOR flash node if there is video=fslfb... command-line - * parameter. When the DIU is active, NOR flash is unavailable, so we - * have to disable the node before the MTD driver loads. + * Disable the NOR and NAND flash nodes if there is video=fslfb... + * command-line parameter. When the DIU is active, the localbus is + * unavailable, so we have to disable these nodes before the MTD + * driver loads. */ if (fslfb) { struct device_node *np = of_find_compatible_node(NULL, NULL, "fsl,p1022-elbc"); if (np) { - np = of_find_compatible_node(np, NULL, "cfi-flash"); - if (np) { + struct device_node *np2; + + of_node_get(np); + np2 = of_find_compatible_node(np, NULL, "cfi-flash"); + if (np2) { static struct property nor_status = { .name = "status", .value = "disabled", .length = sizeof("disabled"), }; + /* + * prom_update_property() is called before + * kmalloc() is available, so the 'new' object + * should be allocated in the global area. + * The easiest way is to do that is to + * allocate one static local variable for each + * call to this function. + */ + pr_info("p1022ds: disabling %s node", + np2->full_name); + prom_update_property(np2, &nor_status); + of_node_put(np2); + } + + of_node_get(np); + np2 = of_find_compatible_node(np, NULL, + "fsl,elbc-fcm-nand"); + if (np2) { + static struct property nand_status = { + .name = "status", + .value = "disabled", + .length = sizeof("disabled"), + }; + pr_info("p1022ds: disabling %s node", - np->full_name); - disable_one_node(np, &nor_status); - of_node_put(np); + np2->full_name); + prom_update_property(np2, &nand_status); + of_node_put(np2); } + + of_node_put(np); } } @@ -450,7 +602,7 @@ static void __init p1022_ds_setup_arch(void) mpc85xx_smp_init(); #ifdef CONFIG_SWIOTLB - if (memblock_end_of_DRAM() > max) { + if ((memblock_end_of_DRAM() - 1) > max) { ppc_swiotlb_enable = 1; set_pci_dma_ops(&swiotlb_dma_ops); ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; |