[linux-yocto] [PATCH 16/17] drivers/misc: Update the Axxia NCR Driver
Daniel Dragomir
daniel.dragomir at windriver.com
Tue May 16 11:39:08 PDT 2017
From: John Jacques <john.jacques at intel.com>
Add support for node 0x153 on 5500.
Support indirect accesses correctly.
Signed-off-by: John Jacques <john.jacques at intel.com>
---
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
More information about the linux-yocto
mailing list