From: John Jacques <[email protected]> Add support for node 0x153 on 5500.
Support indirect accesses correctly. Signed-off-by: John Jacques <[email protected]> --- drivers/misc/lsi-ncr.c | 370 +++++++++++++++++++++++++++++++++++++++++++----- include/linux/lsi-ncr.h | 3 + 2 files changed, 335 insertions(+), 38 deletions(-) diff --git a/drivers/misc/lsi-ncr.c b/drivers/misc/lsi-ncr.c index 9885e0a..adf8d41 100644 --- a/drivers/misc/lsi-ncr.c +++ b/drivers/misc/lsi-ncr.c @@ -23,6 +23,7 @@ #include <linux/lsi-ncr.h> #include <linux/of.h> #include <linux/delay.h> +#include <linux/sizes.h> static int ncr_available; static int nca_big_endian = 1; @@ -136,6 +137,11 @@ union command_data_register_2 { } __packed bits; } __packed; +static int ncr_trace; +module_param(ncr_trace, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +MODULE_PARM_DESC(ncr_trace, "NCR Tracing"); + + /* * ncr_register_read/write * low-level access functions to Axxia registers, @@ -353,56 +359,242 @@ union ncp_apb2ser_indirect_command { /* ------------------------------------------------------------------------------ - ncr_0x115 + apb2ser_indirect_setup */ static int -ncr_0x115(unsigned int region, unsigned int offset, int write, - unsigned int *value) +apb2ser_indirect_setup(unsigned int region, + unsigned int *indirect_offset, + unsigned int *transfer_width) +{ + unsigned int node = NCP_NODE_ID(region); + unsigned int target = NCP_TARGET_ID(region); + unsigned int base; + + if (is_5600) + if ((node < 0x110) || (node > 0x11a)) + return -1; + + if (is_6700) + if ((node < 0x110) || (node > 0x11f)) + return -1; + + if (node <= 0x114) { + base = (node - 0x110) * 2; + } else if (node >= 0x116) { + base = (node - 0x111) * 2; + } else { + if (is_5600) + base = 0x14; + else + base = 0x1e; + } + + *indirect_offset = ((base + target) * 0x10000); + *transfer_width = (target > 0) ? 2 : 4; + udelay(10); + + return 0; +} + +/* + * ------------------------------------------------------------------------------ + * apb2ser_indirect_access + */ + +static int +apb2ser_indirect_access(unsigned int offset, + unsigned int indirect_offset, + unsigned int transfer_width, + int write, + unsigned int *value) { - unsigned long base; union ncp_apb2ser_indirect_command indcmd; unsigned wfc; memset(&indcmd, 0, sizeof(union ncp_apb2ser_indirect_command)); indcmd.bits.valid = 1; indcmd.bits.hwrite = (0 == write) ? 0 : 1; - indcmd.bits.tshift = 1; + indcmd.bits.tshift = 0xf; indcmd.bits.htrans = 2; + indcmd.bits.hsize = 2; indcmd.bits.haddr = offset; - if (0 == NCP_TARGET_ID(region)) - indcmd.bits.hsize = 2; - else - indcmd.bits.hsize = 1; - - if (0 != is_5600) - base = 0x10000ULL * (0x14 + NCP_TARGET_ID(region)); - else - base = 0x10000ULL * (0x1e + NCP_TARGET_ID(region)); - - mdelay(50); - if (0 != write) - writel(*value, (apb2ser0 + base)); + writel(*value, (apb2ser0 + indirect_offset)); pr_debug("ncr: indcmd.raw=0x%x\n", indcmd.raw); - writel(indcmd.raw, (apb2ser0 + base + 4)); + writel(indcmd.raw, (apb2ser0 + indirect_offset + 4)); wfc = WFC_TIMEOUT; do { --wfc; - indcmd.raw = readl(apb2ser0 + base + 4); + indcmd.raw = readl(apb2ser0 + indirect_offset + 4); } while (1 == indcmd.bits.valid && 0 < wfc); if (0 == wfc) { - printk(KERN_ERR "APB2SER Timeout: 0x%x\n", region); + pr_err("APB2SER Timeout!\n"); return -1; } if (0 == write) - *value = readl(apb2ser0 + base + 8); + *value = readl(apb2ser0 + indirect_offset + 8); + + return 0; +} + +/* + * ------------------------------------------------------------------------------ + * ncr_apb2ser + */ + +static int +ncr_apb2ser(unsigned int region, + unsigned int offset, + int write, + unsigned int *value) +{ + int rc; + unsigned int indirect_offset; + unsigned int transfer_width; + + rc = apb2ser_indirect_setup(region, &indirect_offset, &transfer_width); + + if (0 != rc) { + pr_err("APB2SER Indirect Setup Failed!\n"); + + return -1; + } + + rc = apb2ser_indirect_access(offset, indirect_offset, transfer_width, + write, value); + + if (0 != rc) { + pr_err("APB2SER Indirect Setup Failed!\n"); + + return -1; + } + + return 0; +} + +union ncp_cobalt_serdes_ctrl98 { + unsigned short raw; + + struct { +#ifdef __BIG_ENDIAN + unsigned short reserved_b53 : 13; + unsigned short cr_ack_clear : 1; + unsigned short cr_rd : 1; + unsigned short cr_wr : 1; +#else /* Little Endian */ + unsigned short cr_wr : 1; + unsigned short cr_rd : 1; + unsigned short cr_ack_clear : 1; + unsigned short reserved_b53 : 13; +#endif + } __packed bits; +} __packed; + + +union ncp_cobalt_serdes_ctrl99 { + unsigned short raw; + + struct { +#ifdef __BIG_ENDIAN + unsigned short reserved : 15; + unsigned short cr_ack : 1; +#else /* Little Endian */ + unsigned short cr_ack : 1; + unsigned short reserved : 15; +#endif + } __packed bits; +} __packed; + +/* + * ------------------------------------------------------------------------------ + * ncr_apb2ser_e12 + */ + +static int +ncr_apb2ser_e12(unsigned int region, + unsigned int offset, + int write, + unsigned int *value) +{ + unsigned int indirect_offset; + unsigned int transfer_width; + union ncp_cobalt_serdes_ctrl98 hss_cobalt_ctrl_98 = {0}; + union ncp_cobalt_serdes_ctrl99 hss_cobalt_ctrl_99 = {0}; + unsigned short e12_addr = 0; + unsigned int ctrl_96_off; + unsigned int ctrl_97_off; + unsigned int ctrl_98_off; + unsigned int ctrl_99_off; + unsigned int ctrl_224_off; + + if (0 != + apb2ser_indirect_setup(region, &indirect_offset, &transfer_width)) + return -1; + + if (is_5600) { + ctrl_96_off = 0x00c0; + ctrl_97_off = 0x00c2; + ctrl_98_off = 0x00c4; + ctrl_99_off = 0x00c6; + ctrl_224_off = 0x01c0; + } else { + ctrl_96_off = 0x0180; + ctrl_97_off = 0x0184; + ctrl_98_off = 0x0188; + ctrl_99_off = 0x018c; + ctrl_224_off = 0x0380; + offset >>= 1; + } + + if ((offset >= 0x1000) && (offset <= 0x10d0)) + e12_addr = (offset - 0x1000) / 2; + else if (offset >= 0x2000) + e12_addr = offset / 2; + + apb2ser_indirect_access(ctrl_96_off, indirect_offset, 4, + 1, (unsigned int *)&e12_addr); + + if (write) { + apb2ser_indirect_access(ctrl_97_off, indirect_offset, 4, 1, + value); + hss_cobalt_ctrl_98.bits.cr_rd = 0; /* bus read strobe */ + hss_cobalt_ctrl_98.bits.cr_wr = 1; + } else { + hss_cobalt_ctrl_98.bits.cr_rd = 1; /* bus read strobe */ + hss_cobalt_ctrl_98.bits.cr_wr = 0; + } + + hss_cobalt_ctrl_98.bits.cr_ack_clear = 0; + apb2ser_indirect_access(ctrl_98_off, indirect_offset, 4, 1, + (unsigned int *)&hss_cobalt_ctrl_98.raw); + + /* poll for cr_ack to get set */ + do { + apb2ser_indirect_access(ctrl_99_off, indirect_offset, 4, 0, + (unsigned int *) + &hss_cobalt_ctrl_99.raw); + } while (0 == hss_cobalt_ctrl_99.bits.cr_ack); + + hss_cobalt_ctrl_98.bits.cr_rd = 0; + hss_cobalt_ctrl_98.bits.cr_wr = 0; + hss_cobalt_ctrl_98.bits.cr_ack_clear = 1; + apb2ser_indirect_access(ctrl_98_off, indirect_offset, 4, 1, + (unsigned int *)&hss_cobalt_ctrl_98.raw); + + hss_cobalt_ctrl_98.bits.cr_ack_clear = 0; + apb2ser_indirect_access(ctrl_98_off, indirect_offset, 4, 1, + (unsigned int *)&hss_cobalt_ctrl_98.raw); + + if (!write) + apb2ser_indirect_access(ctrl_224_off, indirect_offset, 4, 0, + value); return 0; } @@ -493,6 +685,20 @@ ncr_axi2ser(unsigned int region, unsigned int offset, int write, switch (NCP_NODE_ID(region)) { case 0x153: + if (0 != is_5500) { + address += (offset & (~0x3)); + + /* + * Copy from buffer to the data words. + */ + + if (0 != write) + *((unsigned long *)address) = + *((unsigned long *)value); + else + *((unsigned long *)value) = + *((unsigned long *)address); + } break; case 0x155: address += 0x800000; @@ -547,6 +753,7 @@ __ncr_read(struct ncr_io_fns *io_fn, union command_data_register_0 cdr0; union command_data_register_1 cdr1; union command_data_register_2 cdr2; + unsigned char *input = buffer; if (0 == ncr_available) return -1; @@ -555,14 +762,21 @@ __ncr_read(struct ncr_io_fns *io_fn, __FILE__, __LINE__, region, NCP_NODE_ID(region), NCP_TARGET_ID(region)); - if (0x115 == NCP_NODE_ID(region)) { - if (0 != is_5500) { - if (0 != ncr_0x115_5500(region, address, 0, buffer)) - return -1; + if (0x110 <= NCP_NODE_ID(region) && + 0x11f >= NCP_NODE_ID(region)) { + int rc; + + if (is_5500) { + rc = ncr_0x115_5500(region, address, 0, buffer); + } else if ((NCP_TARGET_ID(region) != 0) && + (address >= 0x1000)) { + rc = ncr_apb2ser_e12(region, address, 0, buffer); } else { - if (0 != ncr_0x115(region, address, 0, buffer)) - return -1; + rc = ncr_apb2ser(region, address, 0, buffer); } + + if (0 != rc) + return -1; } else if (0x153 == NCP_NODE_ID(region) || 0x155 == NCP_NODE_ID(region) || 0x156 == NCP_NODE_ID(region) || @@ -632,6 +846,24 @@ _ Copy data words to the buffer. return -1; } + if (0 != ncr_trace) { + int i; + + printk("NCR: Read ["); + + for (i = 0; i < number; ++i) { + if ((i + 1) < number) + printk("0x%02x, ", *input++); + else + printk("0x%02x", *input++); + } + + printk("] from 0x%x.0x%x.0x%lx\n", + NCP_NODE_ID(region), + NCP_TARGET_ID(region), + address); + } + return 0; } @@ -717,14 +949,40 @@ __ncr_write(struct ncr_io_fns *io_fn, if (0 == ncr_available) return -1; - if (0x115 == NCP_NODE_ID(region)) { - if (0 != is_5500) { - if (0 != ncr_0x115_5500(region, address, 1, buffer)) - return -1; + if (0 != ncr_trace) { + int i; + unsigned char *input = buffer; + + printk("NCR: Writing ["); + + for (i = 0; i < number; ++i) { + if ((i + 1) < number) + printk("0x%02x, ", *input++); + else + printk("0x%02x", *input++); + } + + printk("] to 0x%x.0x%x.0x%x\n", + NCP_NODE_ID(region), + NCP_TARGET_ID(region), + address); + } + + if (0x110 <= NCP_NODE_ID(region) && + 0x11f >= NCP_NODE_ID(region)) { + int rc; + + if (is_5500) { + rc = ncr_0x115_5500(region, address, 1, buffer); + } else if ((NCP_TARGET_ID(region) != 0) && + (address >= 0x1000)) { + rc = ncr_apb2ser_e12(region, address, 1, buffer); } else { - if (0 != ncr_0x115(region, address, 1, buffer)) - return -1; + rc = ncr_apb2ser(region, address, 1, buffer); } + + if (0 != rc) + return -1; } else if (0x153 == NCP_NODE_ID(region) || 0x155 == NCP_NODE_ID(region) || 0x156 == NCP_NODE_ID(region) || @@ -860,14 +1118,40 @@ EXPORT_SYMBOL(ncr_write32); /* ------------------------------------------------------------------------------ - ncr_init + ncr_start_trace */ +void +ncr_start_trace(void) +{ + ncr_trace = 1; +} +EXPORT_SYMBOL(ncr_start_trace); + +/* + * ------------------------------------------------------------------------------ + * ncr_stop_trace + */ + +void +ncr_stop_trace(void) +{ + ncr_trace = 0; +} +EXPORT_SYMBOL(ncr_stop_trace); + +/* + * ------------------------------------------------------------------------------ + * ncr_init + */ + static int ncr_init(void) { +#ifdef CONFIG_ARCH_AXXIA default_io_fn = &ncr_io_fn_nolock; + if (of_find_compatible_node(NULL, NULL, "lsi,axm5500-amarillo")) { u32 pfuse; u32 chip_type; @@ -918,12 +1202,22 @@ ncr_init(void) is_6700 = 1; nca_big_endian = 0; /* The 6700 NCA is LE */ } else { - pr_debug("No Valid Compatible String Found for NCR!\n"); - + pr_err("No Valid Compatible String Found for NCR!\n"); return -1; } +#else + if (of_find_compatible_node(NULL, NULL, "lsi,acp3500")) { + pr_debug("Using ACP3500 Addresses\n"); + nca = ioremap(0x002000520000ULL, 0x20000); + default_io_fn = &ncr_io_fn_nolock; + } else { + pr_debug("Using ACP34xx Addresses\n"); + nca = ioremap(0x002000520000ULL, 0x20000); + default_io_fn = &ncr_io_fn_lock; + } +#endif - pr_debug("ncr: available\n"); + pr_info("ncr: available\n"); ncr_available = 1; return 0; diff --git a/include/linux/lsi-ncr.h b/include/linux/lsi-ncr.h index 354a2c4..53b4138 100644 --- a/include/linux/lsi-ncr.h +++ b/include/linux/lsi-ncr.h @@ -42,6 +42,9 @@ int ncr_write32(unsigned int, unsigned int, unsigned int); int ncr_read_nolock(unsigned int, unsigned int, int, void *); int ncr_write_nolock(unsigned int, unsigned int, int, void *); +void ncr_start_trace(void); +void ncr_stop_trace(void); + /* * when defined, the RTE driver module will set/clear * the ncr_reset_active flag to indicate when Axxia device -- 2.7.4 -- _______________________________________________ linux-yocto mailing list [email protected] https://lists.yoctoproject.org/listinfo/linux-yocto
