diff options
Diffstat (limited to 'linux/drivers/soc/fsl/qbman/qman_portal.c')
-rw-r--r-- | linux/drivers/soc/fsl/qbman/qman_portal.c | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/linux/drivers/soc/fsl/qbman/qman_portal.c b/linux/drivers/soc/fsl/qbman/qman_portal.c new file mode 100644 index 00000000..c74059b1 --- /dev/null +++ b/linux/drivers/soc/fsl/qbman/qman_portal.c @@ -0,0 +1,796 @@ +#include <machine/rtems-bsd-kernel-space.h> + +#include <rtems/bsd/local/opt_dpaa.h> + +/* Copyright 2008 - 2015 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "qman_priv.h" +#ifdef __rtems__ +#include <bsp/qoriq.h> +#endif /* __rtems__ */ + +/* Enable portal interupts (as opposed to polling mode) */ +#define CONFIG_FSL_DPA_PIRQ_SLOW 1 +#define CONFIG_FSL_DPA_PIRQ_FAST 1 + +/* Global variable containing revision id (even on non-control plane systems + * where CCSR isn't available) */ +u16 qman_ip_rev; +EXPORT_SYMBOL(qman_ip_rev); +u16 qm_channel_pool1 = QMAN_CHANNEL_POOL1; +EXPORT_SYMBOL(qm_channel_pool1); +u16 qm_channel_caam = QMAN_CHANNEL_CAAM; +EXPORT_SYMBOL(qm_channel_caam); +u16 qm_channel_pme = QMAN_CHANNEL_PME; +EXPORT_SYMBOL(qm_channel_pme); +u16 qm_channel_dce = QMAN_CHANNEL_DCE; +EXPORT_SYMBOL(qm_channel_dce); +u16 qman_portal_max; +EXPORT_SYMBOL(qman_portal_max); + +#ifndef __rtems__ +/* For these variables, and the portal-initialisation logic, the + * comments in bman_driver.c apply here so won't be repeated. */ +static struct qman_portal *shared_portals[NR_CPUS]; +static int num_shared_portals; +static int shared_portals_idx; +static LIST_HEAD(unused_pcfgs); +#endif /* __rtems__ */ + +/* A SDQCR mask comprising all the available/visible pool channels */ +static u32 pools_sdqcr; + +#define STR_ERR_NOPROP "No '%s' property in node %s\n" +#define STR_ERR_CELL "'%s' is not a %d-cell range in node %s\n" +#define STR_FQID_RANGE "fsl,fqid-range" +#define STR_POOL_CHAN_RANGE "fsl,pool-channel-range" +#define STR_CGRID_RANGE "fsl,cgrid-range" + +/* A "fsl,fqid-range" node; release the given range to the allocator */ +static __init int fsl_fqid_range_init(struct device_node *node) +{ + int ret; + const u32 *range = of_get_property(node, STR_FQID_RANGE, &ret); + + if (!range) { + pr_err(STR_ERR_NOPROP, STR_FQID_RANGE, node->full_name); + return -EINVAL; + } + if (ret != 8) { + pr_err(STR_ERR_CELL, STR_FQID_RANGE, 2, node->full_name); + return -EINVAL; + } + qman_seed_fqid_range(range[0], range[1]); + pr_info("FQID allocator includes range %d:%d\n", + range[0], range[1]); + return 0; +} + +/* A "fsl,pool-channel-range" node; add to the SDQCR mask only */ +static __init int fsl_pool_channel_range_sdqcr(struct device_node *node) +{ + int ret; + const u32 *chanid = of_get_property(node, STR_POOL_CHAN_RANGE, &ret); + + if (!chanid) { + pr_err(STR_ERR_NOPROP, STR_POOL_CHAN_RANGE, node->full_name); + return -EINVAL; + } + if (ret != 8) { + pr_err(STR_ERR_CELL, STR_POOL_CHAN_RANGE, 1, node->full_name); + return -EINVAL; + } + for (ret = 0; ret < chanid[1]; ret++) + pools_sdqcr |= QM_SDQCR_CHANNELS_POOL_CONV(chanid[0] + ret); + return 0; +} + +/* A "fsl,pool-channel-range" node; release the given range to the allocator */ +static __init int fsl_pool_channel_range_init(struct device_node *node) +{ + int ret; + const u32 *chanid = of_get_property(node, STR_POOL_CHAN_RANGE, &ret); + + if (!chanid) { + pr_err(STR_ERR_NOPROP, STR_POOL_CHAN_RANGE, node->full_name); + return -EINVAL; + } + if (ret != 8) { + pr_err(STR_ERR_CELL, STR_POOL_CHAN_RANGE, 1, node->full_name); + return -EINVAL; + } + qman_seed_pool_range(chanid[0], chanid[1]); + pr_info("Pool channel allocator includes range %d:%d\n", + chanid[0], chanid[1]); + return 0; +} + +/* A "fsl,cgrid-range" node; release the given range to the allocator */ +static __init int fsl_cgrid_range_init(struct device_node *node) +{ + struct qman_cgr cgr; + int ret, errors = 0; + const u32 *range = of_get_property(node, STR_CGRID_RANGE, &ret); + + if (!range) { + pr_err(STR_ERR_NOPROP, STR_CGRID_RANGE, node->full_name); + return -EINVAL; + } + if (ret != 8) { + pr_err(STR_ERR_CELL, STR_CGRID_RANGE, 2, node->full_name); + return -EINVAL; + } + qman_seed_cgrid_range(range[0], range[1]); + pr_info("CGRID allocator includes range %d:%d\n", + range[0], range[1]); + for (cgr.cgrid = 0; cgr.cgrid < __CGR_NUM; cgr.cgrid++) { + ret = qman_modify_cgr(&cgr, QMAN_CGR_FLAG_USE_INIT, NULL); + if (ret) + errors++; + } + if (errors) + pr_err("Warning: %d error%s while initialising CGRs %d:%d\n", + errors, (errors > 1) ? "s" : "", range[0], range[1]); + return 0; +} + +static void qman_get_ip_revision(struct device_node *dn) +{ +#ifdef __rtems__ + struct device_node of_dns; +#endif /* __rtems__ */ + u16 ip_rev = 0; + + for_each_compatible_node(dn, NULL, "fsl,qman-portal") { + if (!of_device_is_available(dn)) + continue; + if (of_device_is_compatible(dn, "fsl,qman-portal-1.0") || + of_device_is_compatible(dn, "fsl,qman-portal-1.0.0")) { + pr_err("Rev1.0 on P4080 rev1 is not supported!\n"); + BUG_ON(1); + } else if (of_device_is_compatible(dn, "fsl,qman-portal-1.1") || + of_device_is_compatible(dn, "fsl,qman-portal-1.1.0")) { + ip_rev = QMAN_REV11; + qman_portal_max = 10; + } else if (of_device_is_compatible(dn, "fsl,qman-portal-1.2") || + of_device_is_compatible(dn, "fsl,qman-portal-1.2.0")) { + ip_rev = QMAN_REV12; + qman_portal_max = 10; + } else if (of_device_is_compatible(dn, "fsl,qman-portal-2.0") || + of_device_is_compatible(dn, "fsl,qman-portal-2.0.0")) { + ip_rev = QMAN_REV20; + qman_portal_max = 3; + } else if (of_device_is_compatible(dn, + "fsl,qman-portal-3.0.0")) { + ip_rev = QMAN_REV30; + qman_portal_max = 50; + } else if (of_device_is_compatible(dn, + "fsl,qman-portal-3.0.1")) { + ip_rev = QMAN_REV30; + qman_portal_max = 25; + } else if (of_device_is_compatible(dn, + "fsl,qman-portal-3.1.0")) { + ip_rev = QMAN_REV31; + qman_portal_max = 50; + } else if (of_device_is_compatible(dn, + "fsl,qman-portal-3.1.1")) { + ip_rev = QMAN_REV31; + qman_portal_max = 25; + } else if (of_device_is_compatible(dn, + "fsl,qman-portal-3.1.2")) { + ip_rev = QMAN_REV31; + qman_portal_max = 18; + } else if (of_device_is_compatible(dn, + "fsl,qman-portal-3.1.3")) { + ip_rev = QMAN_REV31; + qman_portal_max = 10; + } else { + pr_warn("Unknown version in portal node, default to rev1.1\n"); + ip_rev = QMAN_REV11; + qman_portal_max = 10; + } + + if (!qman_ip_rev) { + if (ip_rev) { + qman_ip_rev = ip_rev; + } else { + pr_warn("Unknown version, default to rev1.1\n"); + qman_ip_rev = QMAN_REV11; + } + } else if (ip_rev && (qman_ip_rev != ip_rev)) + pr_warn("Revision = 0x%04x, but portal '%s' has 0x%04x\n", + qman_ip_rev, dn->full_name, ip_rev); + if (qman_ip_rev == ip_rev) + break; + } +} + +#ifndef __rtems__ +/* Parse a portal node, perform generic mapping duties and return the config. It + * is not known at this stage for what purpose (or even if) the portal will be + * used. */ +static struct qm_portal_config * __init parse_pcfg(struct device_node *node) +{ + struct qm_portal_config *pcfg; + const u32 *channel; + int irq, ret; + struct resource res; + + pcfg = kzalloc(sizeof(*pcfg), GFP_KERNEL); + if (!pcfg) + return NULL; + + /* + * This is a *horrible hack*, but the IOMMU/PAMU driver needs a + * 'struct device' in order to get the PAMU stashing setup and the QMan + * portal [driver] won't function at all without ring stashing + * + * Making the QMan portal driver nice and proper is part of the + * upstreaming effort + */ + pcfg->dev.bus = &platform_bus_type; + pcfg->dev.of_node = node; +#ifdef CONFIG_IOMMU_API + pcfg->dev.archdata.iommu_domain = NULL; +#endif + + ret = of_address_to_resource(node, DPA_PORTAL_CE, + &pcfg->addr_phys[DPA_PORTAL_CE]); + if (ret) { + pr_err("Can't get %s property 'reg::CE'\n", node->full_name); + goto err; + } + ret = of_address_to_resource(node, DPA_PORTAL_CI, + &pcfg->addr_phys[DPA_PORTAL_CI]); + if (ret) { + pr_err("Can't get %s property 'reg::CI'\n", node->full_name); + goto err; + } + + channel = of_get_property(node, "fsl,qman-channel-id", &ret); + if (!channel || (ret != 4)) { + pr_err("Can't get %s property 'fsl,qman-channel-id'\n", + node->full_name); + goto err; + } + pcfg->public_cfg.channel = *channel; + pcfg->public_cfg.cpu = -1; + irq = irq_of_parse_and_map(node, 0); + if (irq == NO_IRQ) { + pr_err("Can't get %s property 'interrupts'\n", node->full_name); + goto err; + } + pcfg->public_cfg.irq = irq; +#ifdef CONFIG_FSL_QMAN_CONFIG + /* We need the same LIODN offset for all portals */ + qman_liodn_fixup(pcfg->public_cfg.channel); +#endif + + pcfg->addr_virt[DPA_PORTAL_CE] = ioremap_prot( + pcfg->addr_phys[DPA_PORTAL_CE].start, + resource_size(&pcfg->addr_phys[DPA_PORTAL_CE]), + 0); + pcfg->addr_virt[DPA_PORTAL_CI] = ioremap_prot( + pcfg->addr_phys[DPA_PORTAL_CI].start, + resource_size(&pcfg->addr_phys[DPA_PORTAL_CI]), + _PAGE_GUARDED | _PAGE_NO_CACHE); + + return pcfg; +err: + kfree(pcfg); + return NULL; +} + +static struct qm_portal_config *get_pcfg(struct list_head *list) +{ + struct qm_portal_config *pcfg; + + if (list_empty(list)) + return NULL; + pcfg = list_entry(list->prev, struct qm_portal_config, list); + list_del(&pcfg->list); + return pcfg; +} +#endif /* __rtems__ */ + +static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu) +{ +#ifdef CONFIG_FSL_PAMU + int ret; + int window_count = 1; + struct iommu_domain_geometry geom_attr; + struct pamu_stash_attribute stash_attr; + + pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type); + if (!pcfg->iommu_domain) { + pr_err("%s(): iommu_domain_alloc() failed", __func__); + goto _no_iommu; + } + geom_attr.aperture_start = 0; + geom_attr.aperture_end = + ((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1; + geom_attr.force_aperture = true; + ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY, + &geom_attr); + if (ret < 0) { + pr_err("%s(): iommu_domain_set_attr() = %d", __func__, ret); + goto _iommu_domain_free; + } + ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS, + &window_count); + if (ret < 0) { + pr_err("%s(): iommu_domain_set_attr() = %d", __func__, ret); + goto _iommu_domain_free; + } + stash_attr.cpu = cpu; + stash_attr.cache = PAMU_ATTR_CACHE_L1; + ret = iommu_domain_set_attr(pcfg->iommu_domain, + DOMAIN_ATTR_FSL_PAMU_STASH, + &stash_attr); + if (ret < 0) { + pr_err("%s(): iommu_domain_set_attr() = %d", + __func__, ret); + goto _iommu_domain_free; + } + ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36, + IOMMU_READ | IOMMU_WRITE); + if (ret < 0) { + pr_err("%s(): iommu_domain_window_enable() = %d", + __func__, ret); + goto _iommu_domain_free; + } + ret = iommu_attach_device(pcfg->iommu_domain, &pcfg->dev); + if (ret < 0) { + pr_err("%s(): iommu_device_attach() = %d", + __func__, ret); + goto _iommu_domain_free; + } + ret = iommu_domain_set_attr(pcfg->iommu_domain, + DOMAIN_ATTR_FSL_PAMU_ENABLE, + &window_count); + if (ret < 0) { + pr_err("%s(): iommu_domain_set_attr() = %d", + __func__, ret); + goto _iommu_detach_device; + } + +_no_iommu: +#endif +#ifdef CONFIG_FSL_QMAN_CONFIG + if (qman_set_sdest(pcfg->public_cfg.channel, cpu)) +#endif + pr_warn("Failed to set the stash request queue\n"); + + return; + +#ifdef CONFIG_FSL_PAMU +_iommu_detach_device: + iommu_detach_device(pcfg->iommu_domain, NULL); +_iommu_domain_free: + iommu_domain_free(pcfg->iommu_domain); + pcfg->iommu_domain = NULL; +#endif +} + +static struct qman_portal *init_pcfg(struct qm_portal_config *pcfg) +{ + struct qman_portal *p; + +#ifndef __rtems__ + pcfg->iommu_domain = NULL; +#endif /* __rtems__ */ + portal_set_cpu(pcfg, pcfg->public_cfg.cpu); + p = qman_create_affine_portal(pcfg, NULL); + if (p) { + u32 irq_sources = 0; + /* Determine what should be interrupt-vs-poll driven */ +#ifdef CONFIG_FSL_DPA_PIRQ_SLOW + irq_sources |= QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI | + QM_PIRQ_CSCI; +#endif +#ifdef CONFIG_FSL_DPA_PIRQ_FAST + irq_sources |= QM_PIRQ_DQRI; +#endif + qman_p_irqsource_add(p, irq_sources); + pr_info("Portal %sinitialised, cpu %d\n", +#ifndef __rtems__ + pcfg->public_cfg.is_shared ? "(shared) " : "", +#else /* __rtems__ */ + "", +#endif /* __rtems__ */ + pcfg->public_cfg.cpu); + } else + pr_crit("Portal failure on cpu %d\n", pcfg->public_cfg.cpu); + return p; +} + +#ifndef __rtems__ +static void init_slave(int cpu) +{ + struct qman_portal *p; + struct cpumask oldmask = *tsk_cpus_allowed(current); + + set_cpus_allowed_ptr(current, get_cpu_mask(cpu)); + p = qman_create_affine_slave(shared_portals[shared_portals_idx++], cpu); + if (!p) + pr_err("Slave portal failure on cpu %d\n", cpu); + else + pr_info("Portal (slave) initialised, cpu %d\n", cpu); + set_cpus_allowed_ptr(current, &oldmask); + if (shared_portals_idx >= num_shared_portals) + shared_portals_idx = 0; +} + +static struct cpumask want_unshared __initdata; +static struct cpumask want_shared __initdata; + +static int __init parse_qportals(char *str) +{ + return parse_portals_bootarg(str, &want_shared, &want_unshared, + "qportals"); +} +__setup("qportals=", parse_qportals); + +static void qman_portal_update_sdest(const struct qm_portal_config *pcfg, + unsigned int cpu) +{ + struct pamu_stash_attribute stash_attr; + int ret; + + if (pcfg->iommu_domain) { + stash_attr.cpu = cpu; + stash_attr.cache = PAMU_ATTR_CACHE_L1; + ret = iommu_domain_set_attr(pcfg->iommu_domain, + DOMAIN_ATTR_FSL_PAMU_STASH, &stash_attr); + if (ret < 0) { + pr_err("Failed to update pamu stash setting\n"); + return; + } + } +#ifdef CONFIG_FSL_QMAN_CONFIG + if (qman_set_sdest(pcfg->public_cfg.channel, cpu)) +#endif + pr_warn("Failed to update portal's stash request queue\n"); +} + +static void qman_offline_cpu(unsigned int cpu) +{ + struct qman_portal *p; + const struct qm_portal_config *pcfg; + + p = (struct qman_portal *)affine_portals[cpu]; + if (p) { + pcfg = qman_get_qm_portal_config(p); + if (pcfg) { + irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(0)); + qman_portal_update_sdest(pcfg, 0); + } + } +} +#endif /* __rtems__ */ + +#ifdef CONFIG_HOTPLUG_CPU +static void qman_online_cpu(unsigned int cpu) +{ + struct qman_portal *p; + const struct qm_portal_config *pcfg; + + p = (struct qman_portal *)affine_portals[cpu]; + if (p) { + pcfg = qman_get_qm_portal_config(p); + if (pcfg) { + irq_set_affinity(pcfg->public_cfg.irq, cpumask_of(cpu)); + qman_portal_update_sdest(pcfg, cpu); + } + } +} + +static int qman_hotplug_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + qman_online_cpu(cpu); + break; + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + qman_offline_cpu(cpu); + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block qman_hotplug_cpu_notifier = { + .notifier_call = qman_hotplug_cpu_callback, +}; +#endif /* CONFIG_HOTPLUG_CPU */ + +#ifdef __rtems__ +#include <bsp/fdt.h> +static struct qm_portal_config qman_configs[NR_CPUS]; +static void +qman_init_portals(void) +{ + const char *fdt = bsp_fdt_get(); + struct device_node dn; + const char *name; + int cpu_count = (int)rtems_get_processor_count(); + int cpu; + int ret; + int node; + int parent; + + memset(&dn, 0, sizeof(dn)); + + name = "fsl,qman-portal"; + node = fdt_node_offset_by_compatible(fdt, 0, name); + if (node < 0) + panic("qman: no portals in FDT"); + parent = fdt_parent_offset(fdt, node); + if (parent < 0) + panic("qman: no parent of portals in FDT"); + node = fdt_first_subnode(fdt, parent); + + dn.full_name = name; + dn.offset = node; + + qoriq_clear_ce_portal(&qoriq_qman_portal[0][0], + sizeof(qoriq_qman_portal[0])); + qoriq_clear_ci_portal(&qoriq_qman_portal[1][0], + sizeof(qoriq_qman_portal[1])); + + for (cpu = 0; cpu < cpu_count; ++cpu) { + struct qm_portal_config *pcfg = &qman_configs[cpu]; + struct qman_portal *portal; + struct resource res; + const u32 *channel; + + if (node < 0) + panic("qman: missing portal in FDT"); + + ret = of_address_to_resource(&dn, 0, &res); + if (ret != 0) + panic("qman: no portal CE address"); + pcfg->addr_virt[0] = (__iomem void *) + ((uintptr_t)&qoriq_qman_portal[0][0] + (uintptr_t)res.start); + BSD_ASSERT((uintptr_t)pcfg->addr_virt[0] >= + (uintptr_t)&qoriq_qman_portal[0][0]); + BSD_ASSERT((uintptr_t)pcfg->addr_virt[0] < + (uintptr_t)&qoriq_qman_portal[1][0]); + + ret = of_address_to_resource(&dn, 1, &res); + if (ret != 0) + panic("qman: no portal CI address"); + pcfg->addr_virt[1] = (__iomem void *) + ((uintptr_t)&qoriq_qman_portal[0][0] + (uintptr_t)res.start); + BSD_ASSERT((uintptr_t)pcfg->addr_virt[1] >= + (uintptr_t)&qoriq_qman_portal[1][0]); + BSD_ASSERT((uintptr_t)pcfg->addr_virt[1] < + (uintptr_t)&qoriq_qman_portal[2][0]); + + pcfg->public_cfg.irq = of_irq_to_resource(&dn, 0, NULL); + if (pcfg->public_cfg.irq == NO_IRQ) + panic("qman: no portal interrupt"); + + channel = of_get_property(&dn, "fsl,qman-channel-id", &ret); + if (channel == NULL || ret != 4) + panic("qman: no portal channel ID"); + pcfg->public_cfg.channel = *channel; + + pcfg->public_cfg.cpu = cpu; + pcfg->public_cfg.pools = pools_sdqcr; + + portal = init_pcfg(pcfg); + if (portal == NULL) + panic("qman: cannot create portal"); + + node = fdt_next_subnode(fdt, node); + dn.offset = node; + } +} +#endif /* __rtems__ */ +#ifndef __rtems__ +__init int qman_init(void) +{ + struct cpumask slave_cpus; + struct cpumask unshared_cpus = *cpu_none_mask; + struct cpumask shared_cpus = *cpu_none_mask; + LIST_HEAD(unshared_pcfgs); + LIST_HEAD(shared_pcfgs); + struct device_node *dn; + struct qm_portal_config *pcfg; + struct qman_portal *p; + int cpu, ret; + struct cpumask offline_cpus; + + /* Initialise the QMan (CCSR) device */ + for_each_compatible_node(dn, NULL, "fsl,qman") { + if (!qman_init_ccsr(dn)) + pr_info("Err interrupt handler present\n"); + else + pr_err("CCSR setup failed\n"); + } +#else /* __rtems__ */ +int +qman_init(struct device_node *dn) +{ + struct device_node of_dns; + int ret; +#endif /* __rtems__ */ +#ifdef CONFIG_FSL_QMAN_FQ_LOOKUP + /* Setup lookup table for FQ demux */ + ret = qman_setup_fq_lookup_table(qman_fqd_size()/64); + if (ret) + return ret; +#endif + + /* Get qman ip revision */ + qman_get_ip_revision(dn); + if ((qman_ip_rev & 0xff00) >= QMAN_REV30) { + qm_channel_pool1 = QMAN_CHANNEL_POOL1_REV3; + qm_channel_caam = QMAN_CHANNEL_CAAM_REV3; + qm_channel_pme = QMAN_CHANNEL_PME_REV3; + } + + /* Parse pool channels into the SDQCR mask. (Must happen before portals + * are initialised.) */ + for_each_compatible_node(dn, NULL, "fsl,pool-channel-range") { + ret = fsl_pool_channel_range_sdqcr(dn); + if (ret) + return ret; + } + +#ifndef __rtems__ + memset(affine_portals, 0, sizeof(void *) * num_possible_cpus()); + /* Initialise portals. See bman_driver.c for comments */ + for_each_compatible_node(dn, NULL, "fsl,qman-portal") { + if (!of_device_is_available(dn)) + continue; + pcfg = parse_pcfg(dn); + if (pcfg) { + pcfg->public_cfg.pools = pools_sdqcr; + list_add_tail(&pcfg->list, &unused_pcfgs); + } + } + for_each_possible_cpu(cpu) { + if (cpumask_test_cpu(cpu, &want_shared)) { + pcfg = get_pcfg(&unused_pcfgs); + if (!pcfg) + break; + pcfg->public_cfg.cpu = cpu; + list_add_tail(&pcfg->list, &shared_pcfgs); + cpumask_set_cpu(cpu, &shared_cpus); + } + if (cpumask_test_cpu(cpu, &want_unshared)) { + if (cpumask_test_cpu(cpu, &shared_cpus)) + continue; + pcfg = get_pcfg(&unused_pcfgs); + if (!pcfg) + break; + pcfg->public_cfg.cpu = cpu; + list_add_tail(&pcfg->list, &unshared_pcfgs); + cpumask_set_cpu(cpu, &unshared_cpus); + } + } + if (list_empty(&shared_pcfgs) && list_empty(&unshared_pcfgs)) { + for_each_possible_cpu(cpu) { + pcfg = get_pcfg(&unused_pcfgs); + if (!pcfg) + break; + pcfg->public_cfg.cpu = cpu; + list_add_tail(&pcfg->list, &unshared_pcfgs); + cpumask_set_cpu(cpu, &unshared_cpus); + } + } + cpumask_andnot(&slave_cpus, cpu_possible_mask, &shared_cpus); + cpumask_andnot(&slave_cpus, &slave_cpus, &unshared_cpus); + if (cpumask_empty(&slave_cpus)) { + if (!list_empty(&shared_pcfgs)) { + cpumask_or(&unshared_cpus, &unshared_cpus, + &shared_cpus); + cpumask_clear(&shared_cpus); + list_splice_tail(&shared_pcfgs, &unshared_pcfgs); + INIT_LIST_HEAD(&shared_pcfgs); + } + } else { + if (list_empty(&shared_pcfgs)) { + pcfg = get_pcfg(&unshared_pcfgs); + if (!pcfg) { + pr_crit("No portals available!\n"); + return 0; + } + cpumask_clear_cpu(pcfg->public_cfg.cpu, &unshared_cpus); + cpumask_set_cpu(pcfg->public_cfg.cpu, &shared_cpus); + list_add_tail(&pcfg->list, &shared_pcfgs); + } + } + list_for_each_entry(pcfg, &unshared_pcfgs, list) { + pcfg->public_cfg.is_shared = 0; + p = init_pcfg(pcfg); + } + list_for_each_entry(pcfg, &shared_pcfgs, list) { + pcfg->public_cfg.is_shared = 1; + p = init_pcfg(pcfg); + if (p) + shared_portals[num_shared_portals++] = p; + } + if (!cpumask_empty(&slave_cpus)) + for_each_cpu(cpu, &slave_cpus) + init_slave(cpu); +#else /* __rtems__ */ + qman_init_portals(); +#endif /* __rtems__ */ + pr_info("Portals initialised\n"); +#ifndef __rtems__ + cpumask_andnot(&offline_cpus, cpu_possible_mask, cpu_online_mask); + for_each_cpu(cpu, &offline_cpus) + qman_offline_cpu(cpu); +#endif /* __rtems__ */ +#ifdef CONFIG_HOTPLUG_CPU + register_hotcpu_notifier(&qman_hotplug_cpu_notifier); +#endif + return 0; +} + +__init int qman_resource_init(void) +{ +#ifdef __rtems__ + struct device_node of_dns; +#endif /* __rtems__ */ + struct device_node *dn; + int ret; + + /* Initialise FQID allocation ranges */ + for_each_compatible_node(dn, NULL, "fsl,fqid-range") { + ret = fsl_fqid_range_init(dn); + if (ret) + return ret; + } + /* Initialise CGRID allocation ranges */ + for_each_compatible_node(dn, NULL, "fsl,cgrid-range") { + ret = fsl_cgrid_range_init(dn); + if (ret) + return ret; + } + /* Parse pool channels into the allocator. (Must happen after portals + * are initialised.) */ + for_each_compatible_node(dn, NULL, "fsl,pool-channel-range") { + ret = fsl_pool_channel_range_init(dn); + if (ret) + return ret; + } + + return 0; +} |