diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /arch/powerpc/sysdev/fsl_rio.c | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/powerpc/sysdev/fsl_rio.c')
-rw-r--r-- | arch/powerpc/sysdev/fsl_rio.c | 797 |
1 files changed, 797 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c new file mode 100644 index 000000000..3f9f78621 --- /dev/null +++ b/arch/powerpc/sysdev/fsl_rio.c @@ -0,0 +1,797 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Freescale MPC85xx/MPC86xx RapidIO support + * + * Copyright 2009 Sysgo AG + * Thomas Moll <thomas.moll@sysgo.com> + * - fixed maintenance access routines, check for aligned access + * + * Copyright 2009 Integrated Device Technology, Inc. + * Alex Bounine <alexandre.bounine@idt.com> + * - Added Port-Write message handling + * - Added Machine Check exception handling + * + * Copyright (C) 2007, 2008, 2010, 2011 Freescale Semiconductor, Inc. + * Zhang Wei <wei.zhang@freescale.com> + * + * Copyright 2005 MontaVista Software, Inc. + * Matt Porter <mporter@kernel.crashing.org> + */ + +#include <linux/init.h> +#include <linux/extable.h> +#include <linux/types.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include <linux/io.h> +#include <linux/uaccess.h> +#include <asm/machdep.h> + +#include "fsl_rio.h" + +#undef DEBUG_PW /* Port-Write debugging */ + +#define RIO_PORT1_EDCSR 0x0640 +#define RIO_PORT2_EDCSR 0x0680 +#define RIO_PORT1_IECSR 0x10130 +#define RIO_PORT2_IECSR 0x101B0 + +#define RIO_GCCSR 0x13c +#define RIO_ESCSR 0x158 +#define ESCSR_CLEAR 0x07120204 +#define RIO_PORT2_ESCSR 0x178 +#define RIO_CCSR 0x15c +#define RIO_LTLEDCSR_IER 0x80000000 +#define RIO_LTLEDCSR_PRT 0x01000000 +#define IECSR_CLEAR 0x80000000 +#define RIO_ISR_AACR 0x10120 +#define RIO_ISR_AACR_AA 0x1 /* Accept All ID */ + +#define RIWTAR_TRAD_VAL_SHIFT 12 +#define RIWTAR_TRAD_MASK 0x00FFFFFF +#define RIWBAR_BADD_VAL_SHIFT 12 +#define RIWBAR_BADD_MASK 0x003FFFFF +#define RIWAR_ENABLE 0x80000000 +#define RIWAR_TGINT_LOCAL 0x00F00000 +#define RIWAR_RDTYP_NO_SNOOP 0x00040000 +#define RIWAR_RDTYP_SNOOP 0x00050000 +#define RIWAR_WRTYP_NO_SNOOP 0x00004000 +#define RIWAR_WRTYP_SNOOP 0x00005000 +#define RIWAR_WRTYP_ALLOC 0x00006000 +#define RIWAR_SIZE_MASK 0x0000003F + +static DEFINE_SPINLOCK(fsl_rio_config_lock); + +#define __fsl_read_rio_config(x, addr, err, op) \ + __asm__ __volatile__( \ + "1: "op" %1,0(%2)\n" \ + " eieio\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: li %1,-1\n" \ + " li %0,%3\n" \ + " b 2b\n" \ + ".previous\n" \ + EX_TABLE(1b, 3b) \ + : "=r" (err), "=r" (x) \ + : "b" (addr), "i" (-EFAULT), "0" (err)) + +void __iomem *rio_regs_win; +void __iomem *rmu_regs_win; +resource_size_t rio_law_start; + +struct fsl_rio_dbell *dbell; +struct fsl_rio_pw *pw; + +#ifdef CONFIG_E500 +int fsl_rio_mcheck_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *entry; + unsigned long reason; + + if (!rio_regs_win) + return 0; + + reason = in_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR)); + if (reason & (RIO_LTLEDCSR_IER | RIO_LTLEDCSR_PRT)) { + /* Check if we are prepared to handle this fault */ + entry = search_exception_tables(regs->nip); + if (entry) { + pr_debug("RIO: %s - MC Exception handled\n", + __func__); + out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), + 0); + regs->msr |= MSR_RI; + regs->nip = extable_fixup(entry); + return 1; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(fsl_rio_mcheck_exception); +#endif + +/** + * fsl_local_config_read - Generate a MPC85xx local config space read + * @mport: RapidIO master port info + * @index: ID of RapdiIO interface + * @offset: Offset into configuration space + * @len: Length (in bytes) of the maintenance transaction + * @data: Value to be read into + * + * Generates a MPC85xx local configuration space read. Returns %0 on + * success or %-EINVAL on failure. + */ +static int fsl_local_config_read(struct rio_mport *mport, + int index, u32 offset, int len, u32 *data) +{ + struct rio_priv *priv = mport->priv; + pr_debug("fsl_local_config_read: index %d offset %8.8x\n", index, + offset); + *data = in_be32(priv->regs_win + offset); + + return 0; +} + +/** + * fsl_local_config_write - Generate a MPC85xx local config space write + * @mport: RapidIO master port info + * @index: ID of RapdiIO interface + * @offset: Offset into configuration space + * @len: Length (in bytes) of the maintenance transaction + * @data: Value to be written + * + * Generates a MPC85xx local configuration space write. Returns %0 on + * success or %-EINVAL on failure. + */ +static int fsl_local_config_write(struct rio_mport *mport, + int index, u32 offset, int len, u32 data) +{ + struct rio_priv *priv = mport->priv; + pr_debug + ("fsl_local_config_write: index %d offset %8.8x data %8.8x\n", + index, offset, data); + out_be32(priv->regs_win + offset, data); + + return 0; +} + +/** + * fsl_rio_config_read - Generate a MPC85xx read maintenance transaction + * @mport: RapidIO master port info + * @index: ID of RapdiIO interface + * @destid: Destination ID of transaction + * @hopcount: Number of hops to target device + * @offset: Offset into configuration space + * @len: Length (in bytes) of the maintenance transaction + * @val: Location to be read into + * + * Generates a MPC85xx read maintenance transaction. Returns %0 on + * success or %-EINVAL on failure. + */ +static int +fsl_rio_config_read(struct rio_mport *mport, int index, u16 destid, + u8 hopcount, u32 offset, int len, u32 *val) +{ + struct rio_priv *priv = mport->priv; + unsigned long flags; + u8 *data; + u32 rval, err = 0; + + pr_debug + ("fsl_rio_config_read:" + " index %d destid %d hopcount %d offset %8.8x len %d\n", + index, destid, hopcount, offset, len); + + /* 16MB maintenance window possible */ + /* allow only aligned access to maintenance registers */ + if (offset > (0x1000000 - len) || !IS_ALIGNED(offset, len)) + return -EINVAL; + + spin_lock_irqsave(&fsl_rio_config_lock, flags); + + out_be32(&priv->maint_atmu_regs->rowtar, + (destid << 22) | (hopcount << 12) | (offset >> 12)); + out_be32(&priv->maint_atmu_regs->rowtear, (destid >> 10)); + + data = (u8 *) priv->maint_win + (offset & (RIO_MAINT_WIN_SIZE - 1)); + switch (len) { + case 1: + __fsl_read_rio_config(rval, data, err, "lbz"); + break; + case 2: + __fsl_read_rio_config(rval, data, err, "lhz"); + break; + case 4: + __fsl_read_rio_config(rval, data, err, "lwz"); + break; + default: + spin_unlock_irqrestore(&fsl_rio_config_lock, flags); + return -EINVAL; + } + + if (err) { + pr_debug("RIO: cfg_read error %d for %x:%x:%x\n", + err, destid, hopcount, offset); + } + + spin_unlock_irqrestore(&fsl_rio_config_lock, flags); + *val = rval; + + return err; +} + +/** + * fsl_rio_config_write - Generate a MPC85xx write maintenance transaction + * @mport: RapidIO master port info + * @index: ID of RapdiIO interface + * @destid: Destination ID of transaction + * @hopcount: Number of hops to target device + * @offset: Offset into configuration space + * @len: Length (in bytes) of the maintenance transaction + * @val: Value to be written + * + * Generates an MPC85xx write maintenance transaction. Returns %0 on + * success or %-EINVAL on failure. + */ +static int +fsl_rio_config_write(struct rio_mport *mport, int index, u16 destid, + u8 hopcount, u32 offset, int len, u32 val) +{ + struct rio_priv *priv = mport->priv; + unsigned long flags; + u8 *data; + int ret = 0; + + pr_debug + ("fsl_rio_config_write:" + " index %d destid %d hopcount %d offset %8.8x len %d val %8.8x\n", + index, destid, hopcount, offset, len, val); + + /* 16MB maintenance windows possible */ + /* allow only aligned access to maintenance registers */ + if (offset > (0x1000000 - len) || !IS_ALIGNED(offset, len)) + return -EINVAL; + + spin_lock_irqsave(&fsl_rio_config_lock, flags); + + out_be32(&priv->maint_atmu_regs->rowtar, + (destid << 22) | (hopcount << 12) | (offset >> 12)); + out_be32(&priv->maint_atmu_regs->rowtear, (destid >> 10)); + + data = (u8 *) priv->maint_win + (offset & (RIO_MAINT_WIN_SIZE - 1)); + switch (len) { + case 1: + out_8((u8 *) data, val); + break; + case 2: + out_be16((u16 *) data, val); + break; + case 4: + out_be32((u32 *) data, val); + break; + default: + ret = -EINVAL; + } + spin_unlock_irqrestore(&fsl_rio_config_lock, flags); + + return ret; +} + +static void fsl_rio_inbound_mem_init(struct rio_priv *priv) +{ + int i; + + /* close inbound windows */ + for (i = 0; i < RIO_INB_ATMU_COUNT; i++) + out_be32(&priv->inb_atmu_regs[i].riwar, 0); +} + +int fsl_map_inb_mem(struct rio_mport *mport, dma_addr_t lstart, + u64 rstart, u64 size, u32 flags) +{ + struct rio_priv *priv = mport->priv; + u32 base_size; + unsigned int base_size_log; + u64 win_start, win_end; + u32 riwar; + int i; + + if ((size & (size - 1)) != 0 || size > 0x400000000ULL) + return -EINVAL; + + base_size_log = ilog2(size); + base_size = 1 << base_size_log; + + /* check if addresses are aligned with the window size */ + if (lstart & (base_size - 1)) + return -EINVAL; + if (rstart & (base_size - 1)) + return -EINVAL; + + /* check for conflicting ranges */ + for (i = 0; i < RIO_INB_ATMU_COUNT; i++) { + riwar = in_be32(&priv->inb_atmu_regs[i].riwar); + if ((riwar & RIWAR_ENABLE) == 0) + continue; + win_start = ((u64)(in_be32(&priv->inb_atmu_regs[i].riwbar) & RIWBAR_BADD_MASK)) + << RIWBAR_BADD_VAL_SHIFT; + win_end = win_start + ((1 << ((riwar & RIWAR_SIZE_MASK) + 1)) - 1); + if (rstart < win_end && (rstart + size) > win_start) + return -EINVAL; + } + + /* find unused atmu */ + for (i = 0; i < RIO_INB_ATMU_COUNT; i++) { + riwar = in_be32(&priv->inb_atmu_regs[i].riwar); + if ((riwar & RIWAR_ENABLE) == 0) + break; + } + if (i >= RIO_INB_ATMU_COUNT) + return -ENOMEM; + + out_be32(&priv->inb_atmu_regs[i].riwtar, lstart >> RIWTAR_TRAD_VAL_SHIFT); + out_be32(&priv->inb_atmu_regs[i].riwbar, rstart >> RIWBAR_BADD_VAL_SHIFT); + out_be32(&priv->inb_atmu_regs[i].riwar, RIWAR_ENABLE | RIWAR_TGINT_LOCAL | + RIWAR_RDTYP_SNOOP | RIWAR_WRTYP_SNOOP | (base_size_log - 1)); + + return 0; +} + +void fsl_unmap_inb_mem(struct rio_mport *mport, dma_addr_t lstart) +{ + u32 win_start_shift, base_start_shift; + struct rio_priv *priv = mport->priv; + u32 riwar, riwtar; + int i; + + /* skip default window */ + base_start_shift = lstart >> RIWTAR_TRAD_VAL_SHIFT; + for (i = 0; i < RIO_INB_ATMU_COUNT; i++) { + riwar = in_be32(&priv->inb_atmu_regs[i].riwar); + if ((riwar & RIWAR_ENABLE) == 0) + continue; + + riwtar = in_be32(&priv->inb_atmu_regs[i].riwtar); + win_start_shift = riwtar & RIWTAR_TRAD_MASK; + if (win_start_shift == base_start_shift) { + out_be32(&priv->inb_atmu_regs[i].riwar, riwar & ~RIWAR_ENABLE); + return; + } + } +} + +void fsl_rio_port_error_handler(int offset) +{ + /*XXX: Error recovery is not implemented, we just clear errors */ + out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), 0); + + if (offset == 0) { + out_be32((u32 *)(rio_regs_win + RIO_PORT1_EDCSR), 0); + out_be32((u32 *)(rio_regs_win + RIO_PORT1_IECSR), IECSR_CLEAR); + out_be32((u32 *)(rio_regs_win + RIO_ESCSR), ESCSR_CLEAR); + } else { + out_be32((u32 *)(rio_regs_win + RIO_PORT2_EDCSR), 0); + out_be32((u32 *)(rio_regs_win + RIO_PORT2_IECSR), IECSR_CLEAR); + out_be32((u32 *)(rio_regs_win + RIO_PORT2_ESCSR), ESCSR_CLEAR); + } +} +static inline void fsl_rio_info(struct device *dev, u32 ccsr) +{ + const char *str; + if (ccsr & 1) { + /* Serial phy */ + switch (ccsr >> 30) { + case 0: + str = "1"; + break; + case 1: + str = "4"; + break; + default: + str = "Unknown"; + break; + } + dev_info(dev, "Hardware port width: %s\n", str); + + switch ((ccsr >> 27) & 7) { + case 0: + str = "Single-lane 0"; + break; + case 1: + str = "Single-lane 2"; + break; + case 2: + str = "Four-lane"; + break; + default: + str = "Unknown"; + break; + } + dev_info(dev, "Training connection status: %s\n", str); + } else { + /* Parallel phy */ + if (!(ccsr & 0x80000000)) + dev_info(dev, "Output port operating in 8-bit mode\n"); + if (!(ccsr & 0x08000000)) + dev_info(dev, "Input port operating in 8-bit mode\n"); + } +} + +/** + * fsl_rio_setup - Setup Freescale PowerPC RapidIO interface + * @dev: platform_device pointer + * + * Initializes MPC85xx RapidIO hardware interface, configures + * master port with system-specific info, and registers the + * master port with the RapidIO subsystem. + */ +int fsl_rio_setup(struct platform_device *dev) +{ + struct rio_ops *ops; + struct rio_mport *port; + struct rio_priv *priv; + int rc = 0; + const u32 *dt_range, *cell, *port_index; + u32 active_ports = 0; + struct resource regs, rmu_regs; + struct device_node *np, *rmu_node; + int rlen; + u32 ccsr; + u64 range_start, range_size; + int paw, aw, sw; + u32 i; + static int tmp; + struct device_node *rmu_np[MAX_MSG_UNIT_NUM] = {NULL}; + + if (!dev->dev.of_node) { + dev_err(&dev->dev, "Device OF-Node is NULL"); + return -ENODEV; + } + + rc = of_address_to_resource(dev->dev.of_node, 0, ®s); + if (rc) { + dev_err(&dev->dev, "Can't get %pOF property 'reg'\n", + dev->dev.of_node); + return -EFAULT; + } + dev_info(&dev->dev, "Of-device full name %pOF\n", + dev->dev.of_node); + dev_info(&dev->dev, "Regs: %pR\n", ®s); + + rio_regs_win = ioremap(regs.start, resource_size(®s)); + if (!rio_regs_win) { + dev_err(&dev->dev, "Unable to map rio register window\n"); + rc = -ENOMEM; + goto err_rio_regs; + } + + ops = kzalloc(sizeof(struct rio_ops), GFP_KERNEL); + if (!ops) { + rc = -ENOMEM; + goto err_ops; + } + ops->lcread = fsl_local_config_read; + ops->lcwrite = fsl_local_config_write; + ops->cread = fsl_rio_config_read; + ops->cwrite = fsl_rio_config_write; + ops->dsend = fsl_rio_doorbell_send; + ops->pwenable = fsl_rio_pw_enable; + ops->open_outb_mbox = fsl_open_outb_mbox; + ops->open_inb_mbox = fsl_open_inb_mbox; + ops->close_outb_mbox = fsl_close_outb_mbox; + ops->close_inb_mbox = fsl_close_inb_mbox; + ops->add_outb_message = fsl_add_outb_message; + ops->add_inb_buffer = fsl_add_inb_buffer; + ops->get_inb_message = fsl_get_inb_message; + ops->map_inb = fsl_map_inb_mem; + ops->unmap_inb = fsl_unmap_inb_mem; + + rmu_node = of_parse_phandle(dev->dev.of_node, "fsl,srio-rmu-handle", 0); + if (!rmu_node) { + dev_err(&dev->dev, "No valid fsl,srio-rmu-handle property\n"); + rc = -ENOENT; + goto err_rmu; + } + rc = of_address_to_resource(rmu_node, 0, &rmu_regs); + if (rc) { + dev_err(&dev->dev, "Can't get %pOF property 'reg'\n", + rmu_node); + of_node_put(rmu_node); + goto err_rmu; + } + of_node_put(rmu_node); + rmu_regs_win = ioremap(rmu_regs.start, resource_size(&rmu_regs)); + if (!rmu_regs_win) { + dev_err(&dev->dev, "Unable to map rmu register window\n"); + rc = -ENOMEM; + goto err_rmu; + } + for_each_compatible_node(np, NULL, "fsl,srio-msg-unit") { + rmu_np[tmp] = np; + tmp++; + } + + /*set up doobell node*/ + np = of_find_compatible_node(NULL, NULL, "fsl,srio-dbell-unit"); + if (!np) { + dev_err(&dev->dev, "No fsl,srio-dbell-unit node\n"); + rc = -ENODEV; + goto err_dbell; + } + dbell = kzalloc(sizeof(struct fsl_rio_dbell), GFP_KERNEL); + if (!(dbell)) { + dev_err(&dev->dev, "Can't alloc memory for 'fsl_rio_dbell'\n"); + rc = -ENOMEM; + goto err_dbell; + } + dbell->dev = &dev->dev; + dbell->bellirq = irq_of_parse_and_map(np, 1); + dev_info(&dev->dev, "bellirq: %d\n", dbell->bellirq); + + aw = of_n_addr_cells(np); + dt_range = of_get_property(np, "reg", &rlen); + if (!dt_range) { + pr_err("%pOF: unable to find 'reg' property\n", + np); + rc = -ENOMEM; + goto err_pw; + } + range_start = of_read_number(dt_range, aw); + dbell->dbell_regs = (struct rio_dbell_regs *)(rmu_regs_win + + (u32)range_start); + + /*set up port write node*/ + np = of_find_compatible_node(NULL, NULL, "fsl,srio-port-write-unit"); + if (!np) { + dev_err(&dev->dev, "No fsl,srio-port-write-unit node\n"); + rc = -ENODEV; + goto err_pw; + } + pw = kzalloc(sizeof(struct fsl_rio_pw), GFP_KERNEL); + if (!(pw)) { + dev_err(&dev->dev, "Can't alloc memory for 'fsl_rio_pw'\n"); + rc = -ENOMEM; + goto err_pw; + } + pw->dev = &dev->dev; + pw->pwirq = irq_of_parse_and_map(np, 0); + dev_info(&dev->dev, "pwirq: %d\n", pw->pwirq); + aw = of_n_addr_cells(np); + dt_range = of_get_property(np, "reg", &rlen); + if (!dt_range) { + pr_err("%pOF: unable to find 'reg' property\n", + np); + rc = -ENOMEM; + goto err; + } + range_start = of_read_number(dt_range, aw); + pw->pw_regs = (struct rio_pw_regs *)(rmu_regs_win + (u32)range_start); + + /*set up ports node*/ + for_each_child_of_node(dev->dev.of_node, np) { + port_index = of_get_property(np, "cell-index", NULL); + if (!port_index) { + dev_err(&dev->dev, "Can't get %pOF property 'cell-index'\n", + np); + continue; + } + + dt_range = of_get_property(np, "ranges", &rlen); + if (!dt_range) { + dev_err(&dev->dev, "Can't get %pOF property 'ranges'\n", + np); + continue; + } + + /* Get node address wide */ + cell = of_get_property(np, "#address-cells", NULL); + if (cell) + aw = *cell; + else + aw = of_n_addr_cells(np); + /* Get node size wide */ + cell = of_get_property(np, "#size-cells", NULL); + if (cell) + sw = *cell; + else + sw = of_n_size_cells(np); + /* Get parent address wide wide */ + paw = of_n_addr_cells(np); + range_start = of_read_number(dt_range + aw, paw); + range_size = of_read_number(dt_range + aw + paw, sw); + + dev_info(&dev->dev, "%pOF: LAW start 0x%016llx, size 0x%016llx.\n", + np, range_start, range_size); + + port = kzalloc(sizeof(struct rio_mport), GFP_KERNEL); + if (!port) + continue; + + rc = rio_mport_initialize(port); + if (rc) { + kfree(port); + continue; + } + + i = *port_index - 1; + port->index = (unsigned char)i; + + priv = kzalloc(sizeof(struct rio_priv), GFP_KERNEL); + if (!priv) { + dev_err(&dev->dev, "Can't alloc memory for 'priv'\n"); + kfree(port); + continue; + } + + INIT_LIST_HEAD(&port->dbells); + port->iores.start = range_start; + port->iores.end = port->iores.start + range_size - 1; + port->iores.flags = IORESOURCE_MEM; + port->iores.name = "rio_io_win"; + + if (request_resource(&iomem_resource, &port->iores) < 0) { + dev_err(&dev->dev, "RIO: Error requesting master port region" + " 0x%016llx-0x%016llx\n", + (u64)port->iores.start, (u64)port->iores.end); + kfree(priv); + kfree(port); + continue; + } + sprintf(port->name, "RIO mport %d", i); + + priv->dev = &dev->dev; + port->dev.parent = &dev->dev; + port->ops = ops; + port->priv = priv; + port->phys_efptr = 0x100; + port->phys_rmap = 1; + priv->regs_win = rio_regs_win; + + ccsr = in_be32(priv->regs_win + RIO_CCSR + i*0x20); + + /* Checking the port training status */ + if (in_be32((priv->regs_win + RIO_ESCSR + i*0x20)) & 1) { + dev_err(&dev->dev, "Port %d is not ready. " + "Try to restart connection...\n", i); + /* Disable ports */ + out_be32(priv->regs_win + + RIO_CCSR + i*0x20, 0); + /* Set 1x lane */ + setbits32(priv->regs_win + + RIO_CCSR + i*0x20, 0x02000000); + /* Enable ports */ + setbits32(priv->regs_win + + RIO_CCSR + i*0x20, 0x00600000); + msleep(100); + if (in_be32((priv->regs_win + + RIO_ESCSR + i*0x20)) & 1) { + dev_err(&dev->dev, + "Port %d restart failed.\n", i); + release_resource(&port->iores); + kfree(priv); + kfree(port); + continue; + } + dev_info(&dev->dev, "Port %d restart success!\n", i); + } + fsl_rio_info(&dev->dev, ccsr); + + port->sys_size = (in_be32((priv->regs_win + RIO_PEF_CAR)) + & RIO_PEF_CTLS) >> 4; + dev_info(&dev->dev, "RapidIO Common Transport System size: %d\n", + port->sys_size ? 65536 : 256); + + if (port->host_deviceid >= 0) + out_be32(priv->regs_win + RIO_GCCSR, RIO_PORT_GEN_HOST | + RIO_PORT_GEN_MASTER | RIO_PORT_GEN_DISCOVERED); + else + out_be32(priv->regs_win + RIO_GCCSR, + RIO_PORT_GEN_MASTER); + + priv->atmu_regs = (struct rio_atmu_regs *)(priv->regs_win + + ((i == 0) ? RIO_ATMU_REGS_PORT1_OFFSET : + RIO_ATMU_REGS_PORT2_OFFSET)); + + priv->maint_atmu_regs = priv->atmu_regs + 1; + priv->inb_atmu_regs = (struct rio_inb_atmu_regs __iomem *) + (priv->regs_win + + ((i == 0) ? RIO_INB_ATMU_REGS_PORT1_OFFSET : + RIO_INB_ATMU_REGS_PORT2_OFFSET)); + + /* Set to receive packets with any dest ID */ + out_be32((priv->regs_win + RIO_ISR_AACR + i*0x80), + RIO_ISR_AACR_AA); + + /* Configure maintenance transaction window */ + out_be32(&priv->maint_atmu_regs->rowbar, + port->iores.start >> 12); + out_be32(&priv->maint_atmu_regs->rowar, + 0x80077000 | (ilog2(RIO_MAINT_WIN_SIZE) - 1)); + + priv->maint_win = ioremap(port->iores.start, + RIO_MAINT_WIN_SIZE); + + rio_law_start = range_start; + + fsl_rio_setup_rmu(port, rmu_np[i]); + fsl_rio_inbound_mem_init(priv); + + dbell->mport[i] = port; + pw->mport[i] = port; + + if (rio_register_mport(port)) { + release_resource(&port->iores); + kfree(priv); + kfree(port); + continue; + } + active_ports++; + } + + if (!active_ports) { + rc = -ENOLINK; + goto err; + } + + fsl_rio_doorbell_init(dbell); + fsl_rio_port_write_init(pw); + + return 0; +err: + kfree(pw); + pw = NULL; +err_pw: + kfree(dbell); + dbell = NULL; +err_dbell: + iounmap(rmu_regs_win); + rmu_regs_win = NULL; +err_rmu: + kfree(ops); +err_ops: + iounmap(rio_regs_win); + rio_regs_win = NULL; +err_rio_regs: + return rc; +} + +/* The probe function for RapidIO peer-to-peer network. + */ +static int fsl_of_rio_rpn_probe(struct platform_device *dev) +{ + printk(KERN_INFO "Setting up RapidIO peer-to-peer network %pOF\n", + dev->dev.of_node); + + return fsl_rio_setup(dev); +}; + +static const struct of_device_id fsl_of_rio_rpn_ids[] = { + { + .compatible = "fsl,srio", + }, + {}, +}; + +static struct platform_driver fsl_of_rio_rpn_driver = { + .driver = { + .name = "fsl-of-rio", + .of_match_table = fsl_of_rio_rpn_ids, + }, + .probe = fsl_of_rio_rpn_probe, +}; + +static __init int fsl_of_rio_rpn_init(void) +{ + return platform_driver_register(&fsl_of_rio_rpn_driver); +} + +subsys_initcall(fsl_of_rio_rpn_init); |