From: John Jacques <[email protected]> The Axxia hardware makes it possible to reset the system without losing the contents of memory. This commit adds the ability to initiate a reset in such a way that memory contents are not lost.
Cleanup of the DDR Retention feature for Linux 3.10. Shutdown devices, and disable hotplug before shuting down the cores. Signed-off-by: John Jacques <[email protected]> --- arch/arm/mach-axxia/Makefile | 1 + arch/arm/mach-axxia/axxia.c | 3 + arch/arm/mach-axxia/ddr_retention.c | 290 ++++++++++++------- arch/arm/mach-axxia/include/mach/ncr.h | 44 +++ arch/arm/mach-axxia/ncr.c | 486 ++++++++++++++++++++++++++++++++ drivers/misc/Kconfig | 2 +- drivers/misc/lsi-smmon.c | 2 +- drivers/net/ethernet/lsi/lsi_acp_net.c | 9 +- 8 files changed, 725 insertions(+), 112 deletions(-) create mode 100644 arch/arm/mach-axxia/include/mach/ncr.h create mode 100644 arch/arm/mach-axxia/ncr.c diff --git a/arch/arm/mach-axxia/Makefile b/arch/arm/mach-axxia/Makefile index e49adf1..79053c8 100644 --- a/arch/arm/mach-axxia/Makefile +++ b/arch/arm/mach-axxia/Makefile @@ -5,6 +5,7 @@ obj-y += wrappers.o obj-y += axxia.o obj-y += clock.o obj-y += io.o +obj-y += ncr.o obj-y += timers.o obj-y += pci.o obj-y += ddr_retention.o diff --git a/arch/arm/mach-axxia/axxia.c b/arch/arm/mach-axxia/axxia.c index ef87fb2..421b050 100644 --- a/arch/arm/mach-axxia/axxia.c +++ b/arch/arm/mach-axxia/axxia.c @@ -49,6 +49,7 @@ #include <mach/timers.h> #include <mach/axxia-gic.h> #include <linux/irqchip/arm-gic.h> +#include <mach/ncr.h> #include "axxia.h" #include "pci.h" #include "i2c.h" @@ -325,6 +326,8 @@ void __init axxia_dt_init(void) axxia_auxdata_lookup, NULL); pm_power_off = NULL; /* TBD */ + ncr_init(); + spi_register_board_info(spi_devs, ARRAY_SIZE(spi_devs)); /* diff --git a/arch/arm/mach-axxia/ddr_retention.c b/arch/arm/mach-axxia/ddr_retention.c index ca426c2..e409774 100644 --- a/arch/arm/mach-axxia/ddr_retention.c +++ b/arch/arm/mach-axxia/ddr_retention.c @@ -21,69 +21,124 @@ */ #include <linux/module.h> - -#ifndef CONFIG_ARCH_AXXIA_SIM - #include <linux/cpu.h> #include <linux/reboot.h> #include <linux/syscore_ops.h> - #include <linux/proc_fs.h> +#include <linux/delay.h> +#include <linux/of.h> #include <asm/io.h> #include <asm/cacheflush.h> -#include <../../../drivers/misc/lsi-ncr.h> +#include <mach/ncr.h> -static void __iomem *nca_address; -static void __iomem *apb_base; - -static inline void kill_time(int cnt) -{ - while (cnt--) - ; -} +static void __iomem *nca; +static void __iomem *apb; +static void __iomem *dickens; unsigned long ncp_caal_regions_acp55xx[] = { - NCP_REGION_ID(0x0b, 0x05), /* SPPV2 */ - NCP_REGION_ID(0x0c, 0x05), /* SED */ - NCP_REGION_ID(0x0e, 0x05), /* DPI_HFA */ - NCP_REGION_ID(0x14, 0x05), /* MTM */ - NCP_REGION_ID(0x14, 0x0a), /* MTM2 */ - NCP_REGION_ID(0x15, 0x00), /* MME */ - NCP_REGION_ID(0x16, 0x05), /* NCAV2 */ - NCP_REGION_ID(0x16, 0x10), /* NCAV22 */ - NCP_REGION_ID(0x17, 0x05), /* EIOAM1 */ - NCP_REGION_ID(0x19, 0x05), /* TMGR */ - NCP_REGION_ID(0x1a, 0x05), /* MPPY */ - NCP_REGION_ID(0x1a, 0x23), /* MPPY2 */ - NCP_REGION_ID(0x1a, 0x21), /* MPPY3 */ - NCP_REGION_ID(0x1b, 0x05), /* PIC */ - NCP_REGION_ID(0x1c, 0x05), /* PAB */ - NCP_REGION_ID(0x1f, 0x05), /* EIOAM0 */ - NCP_REGION_ID(0x31, 0x05), /* ISB */ - NCP_REGION_ID(0x28, 0x05), /* EIOASM0 */ - NCP_REGION_ID(0x29, 0x05), /* EIOASM1 */ - NCP_REGION_ID(0x2a, 0x05), /* EIOAS2 */ - NCP_REGION_ID(0x2b, 0x05), /* EIOAS3 */ - NCP_REGION_ID(0x2c, 0x05), /* EIOAS4 */ - NCP_REGION_ID(0x2d, 0x05), /* EIOAS5 */ - NCP_REGION_ID(0x32, 0x05), /* ISBS */ + NCP_REGION_ID(0x0b, 0x05), /* SPPV2 */ + NCP_REGION_ID(0x0c, 0x05), /* SED */ + NCP_REGION_ID(0x0e, 0x05), /* DPI_HFA */ + NCP_REGION_ID(0x14, 0x05), /* MTM */ + NCP_REGION_ID(0x14, 0x0a), /* MTM2 */ + NCP_REGION_ID(0x15, 0x00), /* MME */ + NCP_REGION_ID(0x16, 0x05), /* NCAV2 */ + NCP_REGION_ID(0x16, 0x10), /* NCAV22 */ + NCP_REGION_ID(0x17, 0x05), /* EIOAM1 */ + NCP_REGION_ID(0x19, 0x05), /* TMGR */ + NCP_REGION_ID(0x1a, 0x05), /* MPPY */ + NCP_REGION_ID(0x1a, 0x23), /* MPPY2 */ + NCP_REGION_ID(0x1a, 0x21), /* MPPY3 */ + NCP_REGION_ID(0x1b, 0x05), /* PIC */ + NCP_REGION_ID(0x1c, 0x05), /* PAB */ + NCP_REGION_ID(0x1f, 0x05), /* EIOAM0 */ + NCP_REGION_ID(0x31, 0x05), /* ISB */ + NCP_REGION_ID(0x28, 0x05), /* EIOASM0 */ + NCP_REGION_ID(0x29, 0x05), /* EIOASM1 */ + NCP_REGION_ID(0x2a, 0x05), /* EIOAS2 */ + NCP_REGION_ID(0x2b, 0x05), /* EIOAS3 */ + NCP_REGION_ID(0x2c, 0x05), /* EIOAS4 */ + NCP_REGION_ID(0x2d, 0x05), /* EIOAS5 */ + NCP_REGION_ID(0x32, 0x05), /* ISBS */ NCP_REGION_ID(0xff, 0xff) }; -static void quiesce_vp_engine(void) +/* + ------------------------------------------------------------------------------ + flush_l3 + + This is NOT a general function to flush the L3 cache. There are a number of + assumptions that are not usually true... + + 1) All other cores are " quiesced". + 2) There is no need to worry about preemption or interrupts. +*/ + +static void +flush_l3(void) +{ + + unsigned long hnf_offsets[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + }; + + int i; + unsigned long status; + int retries; + + for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i) + writel(0x0, dickens + (0x10000 * hnf_offsets[i]) + 0x10); + + for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i) { + retries = 10000; + + do { + status = readl(dickens + + (0x10000 * hnf_offsets[i]) + 0x18); + udelay(1); + } while ((0 < --retries) && (0x0 != (status & 0xf))); + + if (0 == retries) + BUG(); + } + + for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i) + writel(0x3, dickens + (0x10000 * hnf_offsets[i]) + 0x10); + + for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i) { + retries = 10000; + + do { + status = readl(dickens + + (0x10000 * hnf_offsets[i]) + 0x18); + udelay(1); + } while ((0 < --retries) && (0xc != (status & 0xf))); + + if (0 == retries) + BUG(); + } + + asm volatile ("dsb" : : : "memory"); + asm volatile ("dmb" : : : "memory"); + + return; +} + +static void +quiesce_vp_engine(void) { - unsigned long *pCnalRegions = ncp_caal_regions_acp55xx; - unsigned long *pRegion; + unsigned long *pCnalRegions = ncp_caal_regions_acp55xx; + unsigned long *pRegion; unsigned ort, owt; - unsigned long buf = 0; - unsigned short node, target; - int loop; + unsigned long buf = 0; + unsigned short node, target; + int loop; pr_info("quiescing VP engines...\n"); pRegion = pCnalRegions; - while (*pRegion != NCP_REGION_ID(0xff, 0xff)) { + while (*pRegion != NCP_REGION_ID(0xff, 0xff)) { /* set read/write transaction limits to zero */ ncr_write(*pRegion, 0x8, 4, &buf); ncr_write(*pRegion, 0xc, 4, &buf); @@ -92,6 +147,7 @@ static void quiesce_vp_engine(void) pRegion = pCnalRegions; loop = 0; + while (*pRegion != NCP_REGION_ID(0xff, 0xff)) { node = (*pRegion & 0xffff0000) >> 16; target = *pRegion & 0x0000ffff; @@ -102,12 +158,12 @@ static void quiesce_vp_engine(void) if ((ort == 0) && (owt == 0)) { /* this engine has been quiesced, move on to the next */ pr_info("quiesced region 0x%02x.0x%02x\n", - node, target); + node, target); pRegion++; } else { if (loop++ > 10000) { pr_info("Unable to quiesce region 0x%02x.0x%02x ort=0x%x, owt=0x%x\n", - node, target, ort, owt); + node, target, ort, owt); pRegion++; loop = 0; continue; @@ -118,13 +174,15 @@ static void quiesce_vp_engine(void) return; } -static inline void ncp_ddr_shutdown(void) +static inline void +ncp_ddr_shutdown(void) { unsigned long value; int loop = 1; - unsigned long cdr2[2] = { 0x00002200, 0x00000f00 }; + unsigned long cdr2[2] = {0x00002200, 0x00000f00}; int smId; + /* * Most of the PIO command has already been set up. * issue config ring write - enter DDR self-refresh mode @@ -132,76 +190,88 @@ static inline void ncp_ddr_shutdown(void) for (smId = 0; smId < 2; smId++) { /* CDR2 - Node.target */ - ncr_register_write(cdr2[smId], - (unsigned *)(nca_address + 0xf8)); + ncr_register_write(cdr2[smId], (unsigned *) (nca + 0xf8)); /* CDR0 - */ - ncr_register_write(0x80050003, - (unsigned *)(nca_address + 0xf0)); + ncr_register_write(0x80050003, (unsigned *) (nca + 0xf0)); do { - kill_time(100000); - value = - ncr_register_read((unsigned *)(nca_address + 0xf0)); + value = ncr_register_read((unsigned *) + (nca + 0xf0)); } while ((0x80000000UL & value)); } /* check interrupt status for completion */ /* CDR1 - word offset 0x104 (byte offset 0x410) */ - ncr_register_write(0x00000104, (unsigned *)(nca_address + 0xf4)); + ncr_register_write(0x00000104, (unsigned *) (nca + 0xf4)); for (smId = 0; smId < 2; smId++) { /* CDR2 - Node.target */ - ncr_register_write(cdr2[smId], - (unsigned *)(nca_address + 0xf8)); + ncr_register_write(cdr2[smId], (unsigned *) (nca + 0xf8)); do { - ncr_register_write(loop, - (unsigned *)(nca_address + 0x11f0)); + ncr_register_write(loop, (unsigned *) + (nca + 0x11f0)); /* issue config ring read */ - ncr_register_write(0x80040003, - (unsigned *)(nca_address + 0xf0)); + ncr_register_write(0x80040003, (unsigned *) + (nca + 0xf0)); do { - kill_time(100000); - value = - ncr_register_read((unsigned *)(nca_address + - 0xf0)); + value = ncr_register_read((unsigned *) + (nca + 0xf0)); } while ((0x80000000UL & value)); - value = - ncr_register_read((unsigned *)(nca_address + - 0x1000)); - ncr_register_write(value, - (unsigned *)(nca_address + 0x1200)); + value = ncr_register_read((unsigned *) + (nca + 0x1000)); + ncr_register_write(value, (unsigned *) + (nca + 0x1200)); loop++; } while ((value & 0x0200) == 0); } - /* indicate DDR retention reset */ - writel(0x00000001, apb_base + 0x300dc); /* set persist_scratch bit 0 */ + /* + Indicate DDR Retention Reset + */ + + /* set bit 0 of persist_scratch */ + writel(0x00000001, apb + 0x300dc); - /* issue chip reset */ - writel(0x00000040, apb_base + 0x31004); /* Internal Boot, 0xffff0000 - Target */ - writel(0x80000000, apb_base + 0x3180c); /* Set ResetReadDone */ - writel(0x00080802, apb_base + 0x31008); /* Chip Reset */ + /* + Issue Chip Reset + */ + + /* Intrnl Boot, 0xffff0000 Target */ + writel(0x00000040, apb + 0x31004); + /* Set ResetReadDone */ + writel(0x80000000, apb + 0x3180c); + /* Chip Reset */ + writel(0x00080802, apb + 0x31008); + + return; } -void initiate_retention_reset(void) +void +initiate_retention_reset(void) { unsigned long ctl_244 = 0; unsigned long value; + unsigned long delay; - if (NULL == nca_address) - nca_address = ioremap(0x002020100000ULL, 0x20000); + if (NULL == nca || NULL == apb || NULL == dickens) + BUG(); - /* send stop message to other CPUs */ - local_irq_disable(); + system_state = SYSTEM_RESTART; asm volatile ("dsb" : : : "memory"); asm volatile ("dmb" : : : "memory"); - system_state = SYSTEM_RESTART; + usermodehelper_disable(); + device_shutdown(); + cpu_hotplug_disable(); + syscore_shutdown(); smp_send_stop(); - kill_time(1000000); + for (delay = 0; delay < 10000; ++delay) + udelay(1000); + + flush_cache_all(); + flush_l3(); /* TODO - quiesce VP engines */ quiesce_vp_engine(); @@ -213,8 +283,7 @@ void initiate_retention_reset(void) ncr_write(NCP_REGION_ID(15, 0), 0x414, 4, &value); /* unlock reset register for later */ - apb_base = ioremap(0x2010000000, 0x40000); - writel(0x000000ab, apb_base + 0x31000); /* Access Key */ + writel(0x000000ab, apb + 0x31000); /* Access Key */ /* prepare to put DDR in self refresh power-down mode */ /* first read the CTL_244 register and OR in the LP_CMD value */ @@ -224,14 +293,15 @@ void initiate_retention_reset(void) /* * set up for CRBW operation */ + /* write register value into CDAR[0] */ - ncr_register_write(ctl_244, (unsigned *)(nca_address + 0x1000)); + ncr_register_write(ctl_244, (unsigned *) (nca + 0x1000)); /* CDR2 - Node.target = 34.0 */ - ncr_register_write(0x00002200, (unsigned *)(nca_address + 0xf8)); + ncr_register_write(0x00002200, (unsigned *) (nca + 0xf8)); /* CDR1 - word offset 0xf4 (byte offset 0x3d0) */ - ncr_register_write(0x000000f4, (unsigned *)(nca_address + 0xf4)); + ncr_register_write(0x000000f4, (unsigned *) (nca + 0xf4)); /* * issue instruction barrier @@ -242,34 +312,38 @@ void initiate_retention_reset(void) prefetch(ncp_ddr_shutdown); ncp_ddr_shutdown(); + + return; } +EXPORT_SYMBOL(initiate_retention_reset); -static ssize_t axxia_ddr_retention_trigger(struct file *file, - const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t +axxia_ddr_retention_trigger(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) { initiate_retention_reset(); return 0; } -static const struct file_operations axxia_ddr_retention_proc_ops = { - .write = axxia_ddr_retention_trigger, - .llseek = noop_llseek, +static const struct file_operations proc_ops = { + .write = axxia_ddr_retention_trigger, + .llseek = noop_llseek, }; -void axxia_ddr_retention_init(void) +#define PROC_PATH "driver/axxia_ddr_retention_reset" + +void +axxia_ddr_retention_init(void) { - if (!proc_create("driver/axxia_ddr_retention_reset", S_IWUSR, NULL, - &axxia_ddr_retention_proc_ops)) - pr_info("Failed to register DDR retention proc interface\n"); -} -EXPORT_SYMBOL(initiate_retention_reset); + if (!of_find_compatible_node(NULL, NULL, "lsi,axm5516")) + return; -#else + if (!proc_create(PROC_PATH, S_IWUSR, NULL, &proc_ops)) { + pr_err("Failed to register DDR retention proc interface\n"); + return; + } -void axxia_ddr_retention_init(void) -{ - return; + apb = ioremap(0x2010000000, 0x40000); + nca = ioremap(0x002020100000ULL, 0x20000); + dickens = ioremap(0x2000000000, 0x1000000); } - -#endif /* CONFIG_ARCH_AXXIA_SIM */ diff --git a/arch/arm/mach-axxia/include/mach/ncr.h b/arch/arm/mach-axxia/include/mach/ncr.h new file mode 100644 index 0000000..926d366 --- /dev/null +++ b/arch/arm/mach-axxia/include/mach/ncr.h @@ -0,0 +1,44 @@ +/* + * arch/arm/mach-axxia/include/mach/ncr.h + * + * Copyright (C) 2010 LSI + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ARCH_ARM_MACH_AXXIA_NCR_H +#define __ARCH_ARM_MACH_AXXIA_NCR_H + +#ifndef NCP_REGION_ID +#define NCP_REGION_ID(node, target) \ +((unsigned long) ((((node) & 0xffff) << 16) | ((target) & 0xffff))) +#endif + +#ifndef NCP_NODE_ID +#define NCP_NODE_ID(region) (((region) >> 16) & 0xffff) +#endif + +#ifndef NCP_TARGET_ID +#define NCP_TARGET_ID(region) ((region) & 0xffff) +#endif + +unsigned long ncr_register_read(unsigned *); +void ncr_register_write(const unsigned, unsigned *); +int ncr_read(unsigned long, unsigned long, int, void *); +int ncr_write(unsigned long, unsigned long, int, void *); +int ncr_init(void); +void ncr_exit(void); + +#endif /* __ARCH_ARM_MACH_AXXIA_NCR_H */ diff --git a/arch/arm/mach-axxia/ncr.c b/arch/arm/mach-axxia/ncr.c new file mode 100644 index 0000000..58b959a --- /dev/null +++ b/arch/arm/mach-axxia/ncr.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2009 LSI Corporation + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <linux/module.h> + +#include <asm/io.h> + +#include <mach/ncr.h> + +#ifdef CONFIG_ARCH_AXXIA +#define NCA_PHYS_ADDRESS 0x002020100000ULL +#define APB2SER_PHY_PHYS_ADDRESS 0x002010000000ULL +#else +#define NCA_PHYS_ADDRESS 0x002000520000ULL +#endif + +static void __iomem *nca_address; +#ifdef APB2SER_PHY_PHYS_ADDRESS +static void __iomem *apb2ser0_address; +#endif /* APB2SER_PHY_PHYS_ADDRESS */ + +#define WFC_TIMEOUT (400000) + +#define LOCK_DOMAIN 0 + +typedef union { + unsigned long raw; + struct { +#ifdef __BIG_ENDIAN + unsigned long start_done:1; + unsigned long unused:6; + unsigned long local_bit:1; + unsigned long status:2; + unsigned long byte_swap_enable:1; + unsigned long cfg_cmpl_int_enable:1; + unsigned long cmd_type:4; + unsigned long dbs:16; +#else + unsigned long dbs:16; + unsigned long cmd_type:4; + unsigned long cfg_cmpl_int_enable:1; + unsigned long byte_swap_enable:1; + unsigned long status:2; + unsigned long local_bit:1; + unsigned long unused:6; + unsigned long start_done:1; +#endif +} __attribute__ ((packed)) bits; +} __attribute__ ((packed)) command_data_register_0_t; + +typedef union { + unsigned long raw; + struct { + unsigned long target_address:32; + } __attribute__ ((packed)) bits; +} __attribute__ ((packed)) command_data_register_1_t; + +typedef union { + unsigned long raw; + struct { +#ifdef __BIG_ENDIAN + unsigned long unused:16; + unsigned long target_node_id:8; + unsigned long target_id_address_upper:8; +#else + unsigned long target_id_address_upper:8; + unsigned long target_node_id:8; + unsigned long unused:16; +#endif + } __attribute__ ((packed)) bits; +} __attribute__ ((packed)) command_data_register_2_t; + +#ifdef CONFIG_ARM + +/* + ---------------------------------------------------------------------- + ncr_register_read +*/ + +unsigned long +ncr_register_read(unsigned *address) +{ + unsigned long value; + + value = ioread32be(address); + + return value; +} + +/* + ---------------------------------------------------------------------- + ncr_register_write +*/ + +void +ncr_register_write(const unsigned value, unsigned *address) +{ + iowrite32be(value, address); + + return; +} + +#else + +/* + ---------------------------------------------------------------------- + ncr_register_read +*/ + +unsigned long +ncr_register_read(unsigned *address) +{ + unsigned long value; + + value = in_be32((unsigned *)address); + + return value; +} + +/* + ---------------------------------------------------------------------- + ncr_register_write +*/ + +void +ncr_register_write(const unsigned value, unsigned *address) +{ + out_be32(address, value); + + return; +} + +#endif + +/* + ------------------------------------------------------------------------------ + ncr_lock +*/ + +static int +ncr_lock(int domain) +{ + unsigned long offset; + unsigned long value; + int loops = 10000; + + offset = (0xff80 + (domain * 4)); + + do { + value = ncr_register_read((unsigned *)(nca_address + offset)); + } while ((0 != value) && (0 < --loops)); + + if (0 == loops) + return -1; + + return 0; +} + +/* + ------------------------------------------------------------------------------ + ncr_unlock +*/ + +static void +ncr_unlock(int domain) +{ + unsigned long offset; + + offset = (0xff80 + (domain * 4)); + ncr_register_write(0, (unsigned *)(nca_address + offset)); + + return; +} + +/* + ====================================================================== + ====================================================================== + Public Interface + ====================================================================== + ====================================================================== +*/ + +/* + ---------------------------------------------------------------------- + ncr_read +*/ + +int +ncr_read(unsigned long region, unsigned long address, int number, + void *buffer) +{ + command_data_register_0_t cdr0; + command_data_register_1_t cdr1; + command_data_register_2_t cdr2; + int wfc_timeout = WFC_TIMEOUT; + + if (NULL == nca_address) + return -1; + +#ifdef APB2SER_PHY_PHYS_ADDRESS + if (NULL == apb2ser0_address) + return -1; +#endif /* APB2SER_PHY_PHYS_ADDRESS */ + + if (0 != ncr_lock(LOCK_DOMAIN)) + return -1; + + if (NCP_NODE_ID(region) != 0x0153) { + /* + Set up the read command. + */ + + cdr2.raw = 0; + cdr2.bits.target_node_id = NCP_NODE_ID(region); + cdr2.bits.target_id_address_upper = NCP_TARGET_ID(region); + ncr_register_write(cdr2.raw, (unsigned *) (nca_address + 0xf8)); + + cdr1.raw = 0; + cdr1.bits.target_address = (address >> 2); + ncr_register_write(cdr1.raw, (unsigned *) (nca_address + 0xf4)); + + cdr0.raw = 0; + cdr0.bits.start_done = 1; + + if (0xff == cdr2.bits.target_id_address_upper) + cdr0.bits.local_bit = 1; + + cdr0.bits.cmd_type = 4; + /* TODO: Verify number... */ + cdr0.bits.dbs = (number - 1); + ncr_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0)); + mb(); + + /* + Wait for completion. + */ + + do { + --wfc_timeout; + } while ((0x80000000UL == + ncr_register_read((unsigned *)(nca_address + 0xf0))) && + 0 < wfc_timeout); + + if (0 == wfc_timeout) { + ncr_unlock(LOCK_DOMAIN); + return -1; + } + + /* + Copy data words to the buffer. + */ + + address = (unsigned long)(nca_address + 0x1000); + while (4 <= number) { + *((unsigned long *) buffer) = + ncr_register_read((unsigned *) address); + address += 4; + number -= 4; + } + + if (0 < number) { + unsigned long temp = + ncr_register_read((unsigned *) address); + memcpy((void *) buffer, &temp, number); + } + } else { +#ifdef APB2SER_PHY_PHYS_ADDRESS + void __iomem *targ_address = apb2ser0_address + + (address & (~0x3)); + /* + Copy data words to the buffer. + */ + + while (4 <= number) { + *((unsigned long *) buffer) = + *((unsigned long *) targ_address); + targ_address += 4; + number -= 4; + } +#else + ncr_unlock(LOCK_DOMAIN); + return -1; +#endif /* APB2SER_PHY_PHYS_ADDRESS */ + } + + ncr_unlock(LOCK_DOMAIN); + + return 0; +} +EXPORT_SYMBOL(ncr_read); + +/* + ---------------------------------------------------------------------- + ncr_write +*/ + +int +ncr_write(unsigned long region, unsigned long address, int number, + void *buffer) +{ + command_data_register_0_t cdr0; + command_data_register_1_t cdr1; + command_data_register_2_t cdr2; + unsigned long data_word_base; + int dbs = (number - 1); + int wfc_timeout = WFC_TIMEOUT; + + if (NULL == nca_address) + return -1; + +#ifdef APB2SER_PHY_PHYS_ADDRESS + if (NULL == apb2ser0_address) + return -1; +#endif /* APB2SER_PHY_PHYS_ADDRESS */ + + if (0 != ncr_lock(LOCK_DOMAIN)) + return -1; + + if (NCP_NODE_ID(region) != 0x0153) { + /* + Set up the write. + */ + + cdr2.raw = 0; + cdr2.bits.target_node_id = NCP_NODE_ID(region); + cdr2.bits.target_id_address_upper = NCP_TARGET_ID(region); + ncr_register_write(cdr2.raw, (unsigned *) (nca_address + 0xf8)); + + cdr1.raw = 0; + cdr1.bits.target_address = (address >> 2); + ncr_register_write(cdr1.raw, (unsigned *) (nca_address + 0xf4)); + + /* + Copy from buffer to the data words. + */ + + data_word_base = (unsigned long)(nca_address + 0x1000); + + while (4 <= number) { + ncr_register_write(*((unsigned long *) buffer), + (unsigned *) data_word_base); + data_word_base += 4; + buffer += 4; + number -= 4; + } + + if (0 < number) { + unsigned long temp = 0; + + memcpy((void *) &temp, (void *) buffer, number); + ncr_register_write(temp, (unsigned *) data_word_base); + data_word_base += number; + buffer += number; + number = 0; + } + + cdr0.raw = 0; + cdr0.bits.start_done = 1; + + if (0xff == cdr2.bits.target_id_address_upper) + cdr0.bits.local_bit = 1; + + cdr0.bits.cmd_type = 5; + /* TODO: Verify number... */ + cdr0.bits.dbs = dbs; + ncr_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0)); + mb(); + + /* + Wait for completion. + */ + + do { + --wfc_timeout; + } while ((0x80000000UL == + ncr_register_read((unsigned *)(nca_address + 0xf0))) && + 0 < wfc_timeout); + + if (0 == wfc_timeout) { + ncr_unlock(LOCK_DOMAIN); + return -1; + } + + /* + Check status. + */ + + if (0x3 != ((ncr_register_read((unsigned *) (nca_address + 0xf0)) & + 0x00c00000) >> 22)) { + unsigned long status; + + status = ncr_register_read((unsigned *)(nca_address + + 0xe4)); + ncr_unlock(LOCK_DOMAIN); + + return status; + } + } else { +#ifdef APB2SER_PHY_PHYS_ADDRESS + void __iomem *targ_address = apb2ser0_address + + (address & (~0x3)); + /* + Copy from buffer to the data words. + */ + + while (4 <= number) { + *((unsigned long *) targ_address) = + *((unsigned long *) buffer); + targ_address += 4; + number -= 4; + } +#else + ncr_unlock(LOCK_DOMAIN); + return -1; +#endif /* APB2SER_PHY_PHYS_ADDRESS */ + } + + ncr_unlock(LOCK_DOMAIN); + + return 0; +} +EXPORT_SYMBOL(ncr_write); + +/* + ---------------------------------------------------------------------- + ncr_init +*/ + +int +ncr_init(void) +{ + nca_address = ioremap(NCA_PHYS_ADDRESS, 0x20000); + +#ifdef APB2SER_PHY_PHYS_ADDRESS + apb2ser0_address = ioremap(APB2SER_PHY_PHYS_ADDRESS, 0x10000); +#endif /* APB2SER_PHY_PHYS_ADDRESS */ + + pr_info("ncr: available\n"); + + return 0; +} +EXPORT_SYMBOL(ncr_init); + +/* + ---------------------------------------------------------------------- + ncr_exit +*/ + +void __exit +ncr_exit(void) +{ + /* Unmap the NCA. */ + if (NULL != nca_address) + iounmap(nca_address); + +#ifdef APB2SER_PHY_PHYS_ADDRESS + /* Unmap the APB2SER0 PHY. */ + if (NULL != apb2ser0_address) + iounmap(apb2ser0_address); +#endif /* APB2SER_PHY_PHYS_ADDRESS */ + + return; +} +EXPORT_SYMBOL(ncr_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Register Ring access for LSI's ACP board"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 62457bc..6272797 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -537,7 +537,7 @@ config LSI_MTC config LSI_NCR tristate "LSI NCR Access" - depends on ARCH_AXXIA || ACP + depends on ACP help Provides access to the LSI Axxia NCR bus. diff --git a/drivers/misc/lsi-smmon.c b/drivers/misc/lsi-smmon.c index 0c0db5d..5ab644b 100644 --- a/drivers/misc/lsi-smmon.c +++ b/drivers/misc/lsi-smmon.c @@ -17,7 +17,7 @@ #include <linux/spinlock.h> #include <linux/slab.h> -#include "lsi-ncr.h" +#include <mach/ncr.h> #ifndef CONFIG_ARCH_AXXIA #error "Only AXM55xx is Supported At Present!" diff --git a/drivers/net/ethernet/lsi/lsi_acp_net.c b/drivers/net/ethernet/lsi/lsi_acp_net.c index 03bb287..8924b19 100644 --- a/drivers/net/ethernet/lsi/lsi_acp_net.c +++ b/drivers/net/ethernet/lsi/lsi_acp_net.c @@ -82,13 +82,18 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/dma-mapping.h> - #include <linux/uaccess.h> #include <linux/io.h> + #include <asm/dma.h> -#include "lsi_acp_net.h" +#ifdef CONFIG_AXXIA +#include <mach/ncr.h> +#else #include "../../../misc/lsi-ncr.h" +#endif + +#include "lsi_acp_net.h" /* Define to disable full duplex mode on Amarillo boards */ #undef AMARILLO_WA -- 1.7.9.5 _______________________________________________ linux-yocto mailing list [email protected] https://lists.yoctoproject.org/listinfo/linux-yocto
