@@ -713,6 +713,11 @@ config HAS_FSL_QBMAN
help
Datapath Acceleration Queue and Buffer management
+config HAS_FSL_PME
+ bool
+ depends on HAS_FSL_QBMAN
+ default n
+
# Yes MCA RS/6000s exist but Linux-PPC does not currently support any
config MCA
bool
@@ -222,6 +222,7 @@ config P3041_DS
select PPC_EPAPR_HV_PIC
select HAS_FSL_PAMU
select HAS_FSL_QBMAN
+ select HAS_FSL_PME
help
This option enables support for the P3041 DS board
@@ -250,6 +251,7 @@ config P4080_DS
select PPC_EPAPR_HV_PIC
select HAS_FSL_PAMU
select HAS_FSL_QBMAN
+ select HAS_FSL_PME
help
This option enables support for the P4080 DS board
@@ -268,6 +270,7 @@ config P5020_DS
select PPC_EPAPR_HV_PIC
select HAS_FSL_PAMU
select HAS_FSL_QBMAN
+ select HAS_FSL_PME
help
This option enables support for the P5020 DS board
@@ -134,4 +134,6 @@ source "drivers/staging/gdm72xx/Kconfig"
source "drivers/staging/fsl_qbman/Kconfig"
+source "drivers/staging/fsl_pme2/Kconfig"
+
endif # STAGING
@@ -59,3 +59,4 @@ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
obj-$(CONFIG_USB_G_CCG) += ccg/
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
obj-$(CONFIG_FSL_DPA) += fsl_qbman/
+obj-$(CONFIG_FSL_PME2) += fsl_pme2/
new file mode 100644
@@ -0,0 +1,215 @@
+config FSL_PME2
+ bool "Freescale Datapath Pattern Matcher support"
+ depends on HAS_FSL_PME && FSL_QMAN_PORTAL
+ default y
+
+menu "Freescale Datapath PME options"
+ depends on FSL_PME2
+
+config FSL_PME2_CTRL
+ bool "Freescale PME2 (p4080, etc) device control"
+ default y
+ ---help---
+ This compiles device support for the Freescale PME2 pattern matching
+ part contained in datapath-enabled SoCs (ie. accessed via Qman and
+ Bman portal functionality). At least one guest operating system must
+ have this driver support, together with the appropriate device-tree
+ entry, for PME2 functionality to be available. It is responsible for
+ allocating system memory to the device and configuring it for
+ operation. For this reason, it must be built into the kernel and will
+ initialise during early kernel boot.
+
+config FSL_PME2_PDSRSIZE
+ int "Pattern Description and Stateful Rule default table size"
+ depends on FSL_PME2_CTRL
+ range 74240 1048573
+ default 131072
+ help
+ Select the default size of the Pattern Description and Stateful Rule
+ table as the number of 128 byte entries. This only takes effect if
+ the device tree node doesn't have the 'fsl,pme-pdsr' property.
+ range 74240-1048573 (9.5MB-134MB)
+ default 131072 (16MB)
+
+if FSL_PME2_CTRL
+comment "Statefule Rule Engine"
+endif
+
+config FSL_PME2_SRESIZE
+ int "SRE Session Context Entries table default table size"
+ depends on FSL_PME2_CTRL
+ range 0 134217727
+ default 327680
+ help
+ Select the default size of the SRE Context Table as the number of 32
+ byte entries. This only takes effect if the device tree node doesn't
+ have the 'fsl,pme-sre' property.
+ range 0-134217727 (0-4GB)
+ default 327680 (10MB)
+
+config FSL_PME2_SRE_AIM
+ bool "Alternate Inconclusive Mode"
+ depends on FSL_PME2_CTRL
+ default n
+ help
+ Select the inconclusive match mode treatment. When true the
+ “alternate” inconclusive mode is used. When false the “default”
+ inconclusive mode is used.
+
+config FSL_PME2_SRE_ESR
+ bool "End of SUI Simple Report"
+ depends on FSL_PME2_CTRL
+ default n
+ help
+ Select if an End of SUI will produce a Simple End of SUI report.
+
+config FSL_PME2_SRE_CTX_SIZE_PER_SESSION
+ int "Default SRE Context Size per Session (16 => 64KB, 17 => 128KB)"
+ depends on FSL_PME2_CTRL
+ range 5 17
+ default 17
+ help
+ Select SRE context size per session as a power of 2.
+ range 5-17
+ Examples:
+ 5 => 32 B
+ 6 => 64 B
+ 7 => 128 B
+ 8 => 256 B
+ 9 => 512 B
+ 10 => 1 KB
+ 11 => 2 KB
+ 12 => 4 KB
+ 13 => 8 KB
+ 14 => 16 KB
+ 15 => 32 KB
+ 16 => 64 KB
+ 17 => 128 KB
+
+config FSL_PME2_SRE_CNR
+ int "Configured Number of Stateful Rules as a multiple of 256 (128 => 32768 )"
+ depends on FSL_PME2_CTRL
+ range 0 128
+ default 128
+ help
+ Select number of stateful rules as a multiple of 256.
+ range 0-128
+ Examples:
+ 0 => 0
+ 1 => 256
+ 2 => 512
+ ...
+ 127 => 32512
+ 128 => 32768
+
+config FSL_PME2_SRE_MAX_INSTRUCTION_LIMIT
+ int "Maximum number of SRE instructions to be executed per reaction."
+ depends on FSL_PME2_CTRL
+ range 0 65535
+ default 65535
+ help
+ Select the maximum number of SRE instructions to be executed per
+ reaction.
+ range 0 65535
+
+config FSL_PME2_SRE_MAX_BLOCK_NUMBER
+ int "Maximum number of Reaction Head blocks to be traversed per pattern match event"
+ depends on FSL_PME2_CTRL
+ range 0 32767
+ default 32767
+ help
+ Select the maximum number of reaction head blocks to be traversed per
+ pattern match event (e.g. a matched pattern or an End of SUI event).
+ range 0-32767
+
+config FSL_PME2_PORTAL
+ tristate "Freescale PME2 (p4080, etc) device usage"
+ default y
+ ---help---
+ This compiles I/O support for the Freescale PME2 pattern matching
+ part contained in datapath-enabled SoCs (ie. accessed via Qman and
+ Bman portal functionality).
+
+if FSL_PME2_PORTAL
+
+config FSL_PME2_TEST_HIGH
+ tristate "PME2 high-level self-test"
+ default n
+ ---help---
+ This uses the high-level Qman driver (and the cpu-affine portals it
+ manages) to perform high-level PME2 API testing with it.
+
+config FSL_PME2_TEST_SCAN
+ tristate "PME2 scan self-test"
+ default n
+ ---help---
+ This uses the high-level Qman driver (and the cpu-affine portals it
+ manages) to perform scan PME2 API testing with it.
+
+config FSL_PME2_TEST_SCAN_WITH_BPID
+ bool "PME2 scan self-test with buffer pool"
+ depends on FSL_PME2_TEST_SCAN && FSL_BMAN_PORTAL
+ default y
+ ---help---
+ This uses a buffer pool id for scan test
+
+config FSL_PME2_TEST_SCAN_WITH_BPID_SIZE
+ int "Buffer Pool size."
+ depends on FSL_PME2_TEST_SCAN_WITH_BPID
+ range 0 11
+ default 3
+ ---help---
+ This uses the specified buffer pool size.
+
+config FSL_PME2_DB
+ tristate "PME2 Database support"
+ depends on FSL_PME2_CTRL
+ default y
+ ---help---
+ This compiles the database driver for PME2.
+
+config FSL_PME2_DB_QOSOUT_PRIORITY
+ int "PME DB output frame queue priority."
+ depends on FSL_PME2_DB
+ range 0 7
+ default 2
+ ---help---
+ The PME DB has a scheduled output frame queue. The qos priority level is configurable.
+ range 0-7
+ 0 => High Priority 0
+ 1 => High Priority 1
+ 2 => Medium Priority
+ 3 => Medium Priority
+ 4 => Medium Priority
+ 5 => Low Priority
+ 6 => Low Priority
+ 7 => Low Priority
+
+config FSL_PME2_SCAN
+ tristate "PME2 Scan support"
+ default y
+ ---help---
+ This compiles the scan driver for PME2.
+
+config FSL_PME2_SCAN_DEBUG
+ bool "Debug Statements"
+ default n
+ depends on FSL_PME2_SCAN
+ ---help---
+ The PME2_SCAN driver can optionally trace with more verbosity
+ of verbosity.
+
+endif
+
+config FSL_PME2_STAT_ACCUMULATOR_UPDATE_INTERVAL
+ int "Configure the pme2 statistics update interval in milliseconds"
+ depends on FSL_PME2_CTRL
+ range 0 10000
+ default 3400
+ help
+ The pme accumulator reads the current device statistics and add it
+ to a running counter. The frequency of these updates may be
+ controlled. If 0 is specified, no automatic updates is done.
+ range 0-10000
+
+endmenu
new file mode 100644
@@ -0,0 +1,10 @@
+# PME
+obj-$(CONFIG_FSL_PME2_CTRL) += pme2_ctrl.o pme2_sysfs.o
+obj-$(CONFIG_FSL_PME2_PORTAL) += pme2.o
+pme2-y := pme2_low.o pme2_high.o
+obj-$(CONFIG_FSL_PME2_TEST_HIGH) += pme2_test_high.o
+obj-$(CONFIG_FSL_PME2_TEST_SCAN) += pme2_test_scanning.o
+pme2_test_scanning-y = pme2_test_scan.o pme2_sample_db.o
+obj-$(CONFIG_FSL_PME2_DB) += pme2_db.o
+obj-$(CONFIG_FSL_PME2_SCAN) += pme2_scan.o
+
new file mode 100644
@@ -0,0 +1,1332 @@
+/* Copyright 2008-2011 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 "pme2_private.h"
+#include "pme2_regs.h"
+
+/* PME HW Revision */
+#define PME_REV(rev1_reg) (rev1_reg & 0x0000FFFF)
+#define PME_REV_2_0 0x00000200
+#define PME_REV_2_1 0x00000201
+#define DEC1_MAX_REV_2_0 0x000FFFFC
+#define DEC1_MAX_REV_2_1 0x0007FFFC
+
+
+/* Driver Name is used in naming the sysfs directory
+ * /sys/bus/of_platform/drivers/DRV_NAME
+ */
+#define DRV_NAME "fsl-pme"
+
+#define DEFAULT_PDSR_SZ (CONFIG_FSL_PME2_PDSRSIZE << 7)
+#define DEFAULT_SRE_SZ (CONFIG_FSL_PME2_SRESIZE << 5)
+#define PDSR_TBL_ALIGN (1 << 7)
+#define SRE_TBL_ALIGN (1 << 5)
+#define DEFAULT_SRFCC 400
+
+/* Defaults */
+#define DEFAULT_DEC0_MTE 0x3FFF
+#define DEFAULT_DLC_MPM 0xFFFF
+#define DEFAULT_DLC_MPE 0xFFFF
+/* Boot parameters */
+DECLARE_GLOBAL(max_test_line_per_pat, unsigned int, uint,
+ DEFAULT_DEC0_MTE,
+ "Maximum allowed Test Line Executions per pattern, "
+ "scaled by a factor of 8");
+DECLARE_GLOBAL(max_pat_eval_per_sui, unsigned int, uint,
+ DEFAULT_DLC_MPE,
+ "Maximum Pattern Evaluations per SUI, scaled by a factor of 8")
+DECLARE_GLOBAL(max_pat_matches_per_sui, unsigned int, uint,
+ DEFAULT_DLC_MPM,
+ "Maximum Pattern Matches per SUI");
+/* SRE */
+DECLARE_GLOBAL(sre_rule_num, unsigned int, uint,
+ CONFIG_FSL_PME2_SRE_CNR,
+ "Configured Number of Stateful Rules");
+DECLARE_GLOBAL(sre_session_ctx_size, unsigned int, uint,
+ 1 << CONFIG_FSL_PME2_SRE_CTX_SIZE_PER_SESSION,
+ "SRE Context Size per Session");
+
+/************
+ * Section 1
+ ************
+ * This code is called during kernel early-boot and could never be made
+ * loadable.
+ */
+static dma_addr_t dxe_a, sre_a;
+static size_t dxe_sz = DEFAULT_PDSR_SZ, sre_sz = DEFAULT_SRE_SZ;
+
+/* Parse the <name> property to extract the memory location and size and
+ * memblock_reserve() it. If it isn't supplied, memblock_alloc() the default size. */
+static __init int parse_mem_property(struct device_node *node, const char *name,
+ dma_addr_t *addr, size_t *sz, u64 align, int zero)
+{
+ const u32 *pint;
+ int ret;
+
+ pint = of_get_property(node, name, &ret);
+ if (!pint || (ret != 16)) {
+ pr_info("pme: No %s property '%s', using memblock_alloc(0x%016zx)\n",
+ node->full_name, name, *sz);
+ *addr = memblock_alloc(*sz, align);
+ if (zero)
+ memset(phys_to_virt(*addr), 0, *sz);
+ return 0;
+ }
+ pr_info("pme: Using %s property '%s'\n", node->full_name, name);
+ /* If using a "zero-pma", don't try to zero it, even if you asked */
+ if (zero && of_find_property(node, "zero-pma", &ret)) {
+ pr_info(" it's a 'zero-pma', not zeroing from s/w\n");
+ zero = 0;
+ }
+ *addr = ((u64)pint[0] << 32) | (u64)pint[1];
+ *sz = ((u64)pint[2] << 32) | (u64)pint[3];
+ if((u64)*addr & (align - 1)) {
+ pr_err("pme: Invalid alignment, address %016llx\n",(u64)*addr);
+ return -EINVAL;
+ }
+ /* Keep things simple, it's either all in the DRAM range or it's all
+ * outside. */
+ if (*addr < memblock_end_of_DRAM()) {
+ if ((u64)*addr + (u64)*sz > memblock_end_of_DRAM()){
+ pr_err("pme: outside DRAM range\n");
+ return -EINVAL;
+ }
+ if (memblock_reserve(*addr, *sz) < 0) {
+ pr_err("pme: Failed to reserve %s\n", name);
+ return -ENOMEM;
+ }
+ if (zero)
+ memset(phys_to_virt(*addr), 0, *sz);
+ } else if (zero) {
+ /* map as cacheable, non-guarded */
+ void *tmpp = ioremap_prot(*addr, *sz, 0);
+ memset(tmpp, 0, *sz);
+ iounmap(tmpp);
+ }
+ return 0;
+}
+
+/* No errors/interrupts. Physical addresses are assumed <= 32bits. */
+static int __init fsl_pme2_init(struct device_node *node)
+{
+ const char *s;
+ int ret = 0;
+
+ s = of_get_property(node, "fsl,hv-claimable", &ret);
+ if (s && !strcmp(s, "standby")) {
+ pr_info(" -> in standby mode\n");
+ return 0;
+ }
+ /* Check if pdsr memory already allocated */
+ if (dxe_a) {
+ pr_err("pme: Error fsl_pme2_init already done\n");
+ return -EINVAL;
+ }
+ ret = parse_mem_property(node, "fsl,pme-pdsr", &dxe_a, &dxe_sz,
+ PDSR_TBL_ALIGN, 0);
+ if (ret)
+ return ret;
+ ret = parse_mem_property(node, "fsl,pme-sre", &sre_a, &sre_sz,
+ SRE_TBL_ALIGN, 0);
+ return ret;
+}
+
+__init void pme2_init_early(void)
+{
+ struct device_node *dn;
+ int ret;
+ for_each_compatible_node(dn, NULL, "fsl,pme") {
+ ret = fsl_pme2_init(dn);
+ if (ret)
+ pr_err("pme: Error fsl_pme2_init\n");
+ }
+}
+
+/************
+ * Section 2
+ ***********
+ * This code is called during driver initialisation. It doesn't do anything with
+ * the device-tree entries nor the PME device, it simply creates the sysfs stuff
+ * and gives the user something to hold. This could be made loadable, if there
+ * was any benefit to doing so - but as the device is already "bound" by static
+ * code, there's little point to hiding the fact.
+ */
+
+MODULE_AUTHOR("Geoff Thorpe");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL PME2 (p4080) device control");
+
+/* Opaque pointer target used to represent the PME CCSR map, ... */
+struct pme;
+
+/* ... and the instance of it. */
+static struct pme *global_pme;
+static int pme_err_irq;
+
+static inline void __pme_out(struct pme *p, u32 offset, u32 val)
+{
+ u32 __iomem *regs = (void *)p;
+ out_be32(regs + (offset >> 2), val);
+}
+#define pme_out(p, r, v) __pme_out(p, PME_REG_##r, v)
+static inline u32 __pme_in(struct pme *p, u32 offset)
+{
+ u32 __iomem *regs = (void *)p;
+ return in_be32(regs + (offset >> 2));
+}
+#define pme_in(p, r) __pme_in(p, PME_REG_##r)
+
+#define PME_EFQC(en, fq) \
+ ({ \
+ /* Assume a default delay of 64 cycles */ \
+ u8 __i419 = 0x1; \
+ u32 __fq419 = (fq) & 0x00ffffff; \
+ ((en) ? 0x80000000 : 0) | (__i419 << 28) | __fq419; \
+ })
+
+#define PME_FACONF_ENABLE 0x00000002
+#define PME_FACONF_RESET 0x00000001
+
+/* pme stats accumulator work */
+static void accumulator_update(struct work_struct *work);
+void accumulator_update_interval(u32 interval);
+static DECLARE_DELAYED_WORK(accumulator_work, accumulator_update);
+u32 pme_stat_interval = CONFIG_FSL_PME2_STAT_ACCUMULATOR_UPDATE_INTERVAL;
+#define PME_SBE_ERR 0x01000000
+#define PME_DBE_ERR 0x00080000
+#define PME_PME_ERR 0x00000100
+#define PME_ALL_ERR (PME_SBE_ERR | PME_DBE_ERR | PME_PME_ERR)
+
+static struct of_device_id of_fsl_pme_ids[] = {
+ {
+ .compatible = "fsl,pme",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, of_fsl_pme_ids);
+
+/* Pme interrupt handler */
+static irqreturn_t pme_isr(int irq, void *ptr)
+{
+ static u32 last_isrstate;
+ u32 isrstate = pme_in(global_pme, ISR) ^ last_isrstate;
+
+ /* What new ISR state has been raise */
+ if (!isrstate)
+ return IRQ_NONE;
+ if (isrstate & PME_SBE_ERR)
+ pr_crit("PME: SBE detected\n");
+ if (isrstate & PME_DBE_ERR)
+ pr_crit("PME: DBE detected\n");
+ if (isrstate & PME_PME_ERR)
+ pr_crit("PME: PME serious detected\n");
+ /* Clear the ier interrupt bit */
+ last_isrstate |= isrstate;
+ pme_out(global_pme, IER, ~last_isrstate);
+ return IRQ_HANDLED;
+}
+
+static int of_fsl_pme_remove(struct platform_device *ofdev)
+{
+ /* Cancel pme accumulator */
+ accumulator_update_interval(0);
+ cancel_delayed_work_sync(&accumulator_work);
+ /* Disable PME..TODO need to wait till it's quiet */
+ pme_out(global_pme, FACONF, PME_FACONF_RESET);
+ /* Release interrupt */
+ if (likely(pme_err_irq != NO_IRQ))
+ free_irq(pme_err_irq, &ofdev->dev);
+ /* Remove sysfs attribute */
+ pme2_remove_sysfs_dev_files(ofdev);
+ /* Unmap controller region */
+ iounmap(global_pme);
+ global_pme = NULL;
+ return 0;
+}
+
+static int __devinit of_fsl_pme_probe(struct platform_device *ofdev)
+{
+ int ret, err = 0;
+ void __iomem *regs;
+ struct device *dev = &ofdev->dev;
+ struct device_node *nprop = dev->of_node;
+ u32 clkfreq = DEFAULT_SRFCC * 1000000;
+ const u32 *value;
+ const char *s;
+ int srec_aim = 0, srec_esr = 0;
+ u32 srecontextsize_code;
+ u32 dec1;
+
+ /* TODO: This standby handling won't work properly after failover, it's
+ * just to allow bring up for now. */
+ s = of_get_property(nprop, "fsl,hv-claimable", &ret);
+ if (s && !strcmp(s, "standby"))
+ return 0;
+ pme_err_irq = of_irq_to_resource(nprop, 0, NULL);
+ if (unlikely(pme_err_irq == NO_IRQ))
+ dev_warn(dev, "Can't get %s property '%s'\n", nprop->full_name,
+ "interrupts");
+
+ /* Get configuration properties from device tree */
+ /* First, get register page */
+ regs = of_iomap(nprop, 0);
+ if (regs == NULL) {
+ dev_err(dev, "of_iomap() failed\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Global configuration, leave pme disabled */
+ global_pme = (struct pme *)regs;
+ pme_out(global_pme, FACONF, 0);
+ pme_out(global_pme, EFQC, PME_EFQC(0, 0));
+
+ /* TODO: these coherency settings for PMFA, DXE, and SRE force all
+ * transactions to snoop, as the kernel does not yet support flushing in
+ * dma_map_***() APIs (ie. h/w can not treat otherwise coherent memory
+ * in a non-coherent manner, temporarily or otherwise). When the kernel
+ * supports this, we should tune these settings back to;
+ * FAMCR = 0x00010001
+ * DMCR = 0x00000000
+ * SMCR = 0x00000000
+ */
+ /* PME HW rev 2.1: Added TWC field in FAMCR */
+ pme_out(global_pme, FAMCR, 0x11010101);
+ pme_out(global_pme, DMCR, 0x00000001);
+ pme_out(global_pme, SMCR, 0x00000211);
+
+ if (likely(pme_err_irq != NO_IRQ)) {
+ /* Register the pme ISR handler */
+ err = request_irq(pme_err_irq, pme_isr, IRQF_SHARED, "pme-err",
+ dev);
+ if (err) {
+ dev_err(dev, "request_irq() failed\n");
+ goto out_unmap_ctrl_region;
+ }
+ }
+
+#ifdef CONFIG_FSL_PME2_SRE_AIM
+ srec_aim = 1;
+#endif
+#ifdef CONFIG_FSL_PME2_SRE_ESR
+ srec_esr = 1;
+#endif
+ /* Validate some parameters */
+ if (!sre_session_ctx_size || !is_power_of_2(sre_session_ctx_size) ||
+ (sre_session_ctx_size < 32) ||
+ (sre_session_ctx_size > (131072))) {
+ dev_err(dev, "invalid sre_session_ctx_size\n");
+ err = -EINVAL;
+ goto out_free_irq;
+ }
+ srecontextsize_code = ilog2(sre_session_ctx_size);
+ srecontextsize_code -= 4;
+
+ /* Configure Clock Frequency */
+ value = of_get_property(nprop, "clock-frequency", NULL);
+ if (value)
+ clkfreq = *value;
+ pme_out(global_pme, SFRCC, DIV_ROUND_UP(clkfreq, 1000000));
+
+ pme_out(global_pme, PDSRBAH, upper_32_bits(dxe_a));
+ pme_out(global_pme, PDSRBAL, lower_32_bits(dxe_a));
+ pme_out(global_pme, SCBARH, upper_32_bits(sre_a));
+ pme_out(global_pme, SCBARL, lower_32_bits(sre_a));
+ /* Maximum allocated index into the PDSR table available to the DXE
+ * Rev 2.0: Max 0xF_FFFC
+ * Rev 2.1: Max 0x7_FFFC
+ */
+ if (PME_REV(pme_in(global_pme, PM_IP_REV1)) == PME_REV_2_0) {
+ if (((dxe_sz/PDSR_TBL_ALIGN)-1) > DEC1_MAX_REV_2_0)
+ dec1 = DEC1_MAX_REV_2_0;
+ else
+ dec1 = (dxe_sz/PDSR_TBL_ALIGN)-1;
+ } else {
+ if (((dxe_sz/PDSR_TBL_ALIGN)-1) > DEC1_MAX_REV_2_1)
+ dec1 = DEC1_MAX_REV_2_1;
+ else
+ dec1 = (dxe_sz/PDSR_TBL_ALIGN)-1;
+ }
+ pme_out(global_pme, DEC1, dec1);
+ /* Maximum allocated index into the PDSR table available to the SRE */
+ pme_out(global_pme, SEC2, dec1);
+ /* Maximum allocated 32-byte offset into SRE Context Table.*/
+ if (sre_sz)
+ pme_out(global_pme, SEC3, (sre_sz/SRE_TBL_ALIGN)-1);
+ /* Max test line execution */
+ pme_out(global_pme, DEC0, max_test_line_per_pat);
+ pme_out(global_pme, DLC,
+ (max_pat_eval_per_sui << 16) | max_pat_matches_per_sui);
+
+ /* SREC - SRE Config */
+ pme_out(global_pme, SREC,
+ /* Number of rules in database */
+ (sre_rule_num << 0) |
+ /* Simple Report Enabled */
+ ((srec_esr ? 1 : 0) << 18) |
+ /* Context Size per Session */
+ (srecontextsize_code << 19) |
+ /* Alternate Inclusive Mode */
+ ((srec_aim ? 1 : 0) << 29));
+ pme_out(global_pme, SEC1,
+ (CONFIG_FSL_PME2_SRE_MAX_INSTRUCTION_LIMIT << 16) |
+ CONFIG_FSL_PME2_SRE_MAX_BLOCK_NUMBER);
+
+ /* Setup Accumulator */
+ if (pme_stat_interval)
+ schedule_delayed_work(&accumulator_work,
+ msecs_to_jiffies(pme_stat_interval));
+ /* Create sysfs entries */
+ err = pme2_create_sysfs_dev_files(ofdev);
+ if (err)
+ goto out_stop_accumulator;
+
+ /* Enable interrupts */
+ pme_out(global_pme, IER, PME_ALL_ERR);
+ dev_info(dev, "ver: 0x%08x\n", pme_in(global_pme, PM_IP_REV1));
+
+ /* Enable pme */
+ pme_out(global_pme, FACONF, PME_FACONF_ENABLE);
+ return 0;
+
+out_stop_accumulator:
+ if (pme_stat_interval) {
+ accumulator_update_interval(0);
+ cancel_delayed_work_sync(&accumulator_work);
+ }
+out_free_irq:
+ if (likely(pme_err_irq != NO_IRQ))
+ free_irq(pme_err_irq, &ofdev->dev);
+out_unmap_ctrl_region:
+ pme_out(global_pme, FACONF, PME_FACONF_RESET);
+ iounmap(global_pme);
+ global_pme = NULL;
+out:
+ return err;
+}
+
+static struct platform_driver of_fsl_pme_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ .of_match_table = of_fsl_pme_ids,
+ },
+ .probe = of_fsl_pme_probe,
+ .remove = __devexit_p(of_fsl_pme_remove),
+};
+
+static int pme2_ctrl_init(void)
+{
+ return platform_driver_register(&of_fsl_pme_driver);
+}
+
+static void pme2_ctrl_exit(void)
+{
+ platform_driver_unregister(&of_fsl_pme_driver);
+}
+
+module_init(pme2_ctrl_init);
+module_exit(pme2_ctrl_exit);
+
+/************
+ * Section 3
+ ************
+ * These APIs are the only functional hooks into the control driver, besides the
+ * sysfs attributes.
+ */
+
+int pme2_have_control(void)
+{
+ return global_pme ? 1 : 0;
+}
+EXPORT_SYMBOL(pme2_have_control);
+
+int pme2_exclusive_set(struct qman_fq *fq)
+{
+ if (!pme2_have_control())
+ return -ENODEV;
+ pme_out(global_pme, EFQC, PME_EFQC(1, qman_fq_fqid(fq)));
+ return 0;
+}
+EXPORT_SYMBOL(pme2_exclusive_set);
+
+int pme2_exclusive_unset(void)
+{
+ if (!pme2_have_control())
+ return -ENODEV;
+ pme_out(global_pme, EFQC, PME_EFQC(0, 0));
+ return 0;
+}
+EXPORT_SYMBOL(pme2_exclusive_unset);
+
+int pme_attr_set(enum pme_attr attr, u32 val)
+{
+ u32 mask;
+ u32 attr_val;
+
+ if (!pme2_have_control())
+ return -ENODEV;
+
+ /* Check if Buffer size configuration */
+ if (attr >= pme_attr_bsc_first && attr <= pme_attr_bsc_last) {
+ u32 bsc_pool_id = attr - pme_attr_bsc_first;
+ u32 bsc_pool_offset = bsc_pool_id % 8;
+ u32 bsc_pool_mask = ~(0xF << ((7-bsc_pool_offset)*4));
+ /* range for val 0..0xB */
+ if (val > 0xb)
+ return -EINVAL;
+ /* calculate which sky-blue reg */
+ /* 0..7 -> bsc_(0..7), PME_REG_BSC0 */
+ /* 8..15 -> bsc_(8..15) PME_REG_BSC1*/
+ /* ... */
+ /* 56..63 -> bsc_(56..63) PME_REG_BSC7*/
+ attr_val = pme_in(global_pme, BSC0 + ((bsc_pool_id/8)*4));
+ /* Now mask in the new value */
+ attr_val = attr_val & bsc_pool_mask;
+ attr_val = attr_val | (val << ((7-bsc_pool_offset)*4));
+ pme_out(global_pme, BSC0 + ((bsc_pool_id/8)*4), attr_val);
+ return 0;
+ }
+
+ switch (attr) {
+ case pme_attr_efqc_int:
+ if (val > 4)
+ return -EINVAL;
+ mask = 0x8FFFFFFF;
+ attr_val = pme_in(global_pme, EFQC);
+ /* clear efqc_int */
+ attr_val &= mask;
+ val <<= 28;
+ val |= attr_val;
+ pme_out(global_pme, EFQC, val);
+ break;
+
+ case pme_attr_sw_db:
+ pme_out(global_pme, SWDB, val);
+ break;
+
+ case pme_attr_dmcr:
+ pme_out(global_pme, DMCR, val);
+ break;
+
+ case pme_attr_smcr:
+ pme_out(global_pme, SMCR, val);
+ break;
+
+ case pme_attr_famcr:
+ pme_out(global_pme, FAMCR, val);
+ break;
+
+ case pme_attr_kvlts:
+ if (val < 2 || val > 16)
+ return -EINVAL;
+ /* HW range: 1..15, SW range: 2..16 */
+ pme_out(global_pme, KVLTS, --val);
+ break;
+
+ case pme_attr_max_chain_length:
+ if (val > 0x7FFF)
+ val = 0x7FFF;
+ pme_out(global_pme, KEC, val);
+ break;
+
+ case pme_attr_pattern_range_counter_idx:
+ if (val > 0x1FFFF)
+ val = 0x1FFFF;
+ pme_out(global_pme, DRCIC, val);
+ break;
+
+ case pme_attr_pattern_range_counter_mask:
+ if (val > 0x1FFFF)
+ val = 0x1FFFF;
+ pme_out(global_pme, DRCMC, val);
+ break;
+
+ case pme_attr_max_allowed_test_line_per_pattern:
+ if (val > 0x3FFF)
+ val = 0x3FFF;
+ pme_out(global_pme, DEC0, val);
+ break;
+
+ case pme_attr_max_pattern_matches_per_sui:
+ /* mpe, mpm */
+ if (val > 0xFFFF)
+ val = 0xFFFF;
+ mask = 0xFFFF0000;
+ attr_val = pme_in(global_pme, DLC);
+ /* clear mpm */
+ attr_val &= mask;
+ val &= ~mask;
+ val |= attr_val;
+ pme_out(global_pme, DLC, val);
+ break;
+
+ case pme_attr_max_pattern_evaluations_per_sui:
+ /* mpe, mpm */
+ if (val > 0xFFFF)
+ val = 0xFFFF;
+ mask = 0x0000FFFF;
+ attr_val = pme_in(global_pme, DLC);
+ /* clear mpe */
+ attr_val &= mask;
+ /* clear unwanted bits in val*/
+ val &= mask;
+ val <<= 16;
+ val |= attr_val;
+ pme_out(global_pme, DLC, val);
+ break;
+
+ case pme_attr_report_length_limit:
+ if (val > 0xFFFF)
+ val = 0xFFFF;
+ pme_out(global_pme, RLL, val);
+ break;
+
+ case pme_attr_end_of_simple_sui_report:
+ /* bit 13 */
+ mask = 0x00040000;
+ attr_val = pme_in(global_pme, SREC);
+ if (val)
+ attr_val |= mask;
+ else
+ attr_val &= ~mask;
+ pme_out(global_pme, SREC, attr_val);
+ break;
+
+ case pme_attr_aim:
+ /* bit 2 */
+ mask = 0x20000000;
+ attr_val = pme_in(global_pme, SREC);
+ if (val)
+ attr_val |= mask;
+ else
+ attr_val &= ~mask;
+ pme_out(global_pme, SREC, attr_val);
+ break;
+
+ case pme_attr_end_of_sui_reaction_ptr:
+ if (val > 0xFFFFF)
+ val = 0xFFFFF;
+ pme_out(global_pme, ESRP, val);
+ break;
+
+ case pme_attr_sre_pscl:
+ pme_out(global_pme, SFRCC, val);
+ break;
+
+ case pme_attr_sre_max_block_num:
+ /* bits 17..31 */
+ if (val > 0x7FFF)
+ val = 0x7FFF;
+ mask = 0xFFFF8000;
+ attr_val = pme_in(global_pme, SEC1);
+ /* clear mbn */
+ attr_val &= mask;
+ /* clear unwanted bits in val*/
+ val &= ~mask;
+ val |= attr_val;
+ pme_out(global_pme, SEC1, val);
+ break;
+
+ case pme_attr_sre_max_instruction_limit:
+ /* bits 0..15 */
+ if (val > 0xFFFF)
+ val = 0xFFFF;
+ mask = 0x0000FFFF;
+ attr_val = pme_in(global_pme, SEC1);
+ /* clear mil */
+ attr_val &= mask;
+ /* clear unwanted bits in val*/
+ val &= mask;
+ val <<= 16;
+ val |= attr_val;
+ pme_out(global_pme, SEC1, val);
+ break;
+
+ case pme_attr_srrv0:
+ pme_out(global_pme, SRRV0, val);
+ break;
+ case pme_attr_srrv1:
+ pme_out(global_pme, SRRV1, val);
+ break;
+ case pme_attr_srrv2:
+ pme_out(global_pme, SRRV2, val);
+ break;
+ case pme_attr_srrv3:
+ pme_out(global_pme, SRRV3, val);
+ break;
+ case pme_attr_srrv4:
+ pme_out(global_pme, SRRV4, val);
+ break;
+ case pme_attr_srrv5:
+ pme_out(global_pme, SRRV5, val);
+ break;
+ case pme_attr_srrv6:
+ pme_out(global_pme, SRRV6, val);
+ break;
+ case pme_attr_srrv7:
+ pme_out(global_pme, SRRV7, val);
+ break;
+ case pme_attr_srrfi:
+ pme_out(global_pme, SRRFI, val);
+ break;
+ case pme_attr_srri:
+ pme_out(global_pme, SRRI, val);
+ break;
+ case pme_attr_srrwc:
+ pme_out(global_pme, SRRWC, val);
+ break;
+ case pme_attr_srrr:
+ pme_out(global_pme, SRRR, val);
+ break;
+ case pme_attr_tbt0ecc1th:
+ pme_out(global_pme, TBT0ECC1TH, val);
+ break;
+ case pme_attr_tbt1ecc1th:
+ pme_out(global_pme, TBT1ECC1TH, val);
+ break;
+ case pme_attr_vlt0ecc1th:
+ pme_out(global_pme, VLT0ECC1TH, val);
+ break;
+ case pme_attr_vlt1ecc1th:
+ pme_out(global_pme, VLT1ECC1TH, val);
+ break;
+ case pme_attr_cmecc1th:
+ pme_out(global_pme, CMECC1TH, val);
+ break;
+ case pme_attr_dxcmecc1th:
+ pme_out(global_pme, DXCMECC1TH, val);
+ break;
+ case pme_attr_dxemecc1th:
+ pme_out(global_pme, DXEMECC1TH, val);
+ break;
+ case pme_attr_esr:
+ pme_out(global_pme, ESR, val);
+ break;
+ case pme_attr_pehd:
+ pme_out(global_pme, PEHD, val);
+ break;
+ case pme_attr_ecc1bes:
+ pme_out(global_pme, ECC1BES, val);
+ break;
+ case pme_attr_ecc2bes:
+ pme_out(global_pme, ECC2BES, val);
+ break;
+ case pme_attr_miace:
+ pme_out(global_pme, MIA_CE, val);
+ break;
+ case pme_attr_miacr:
+ pme_out(global_pme, MIA_CR, val);
+ break;
+ case pme_attr_cdcr:
+ pme_out(global_pme, CDCR, val);
+ break;
+ case pme_attr_pmtr:
+ pme_out(global_pme, PMTR, val);
+ break;
+
+ default:
+ pr_err("pme: Unknown attr %u\n", attr);
+ return -EINVAL;
+ };
+ return 0;
+}
+EXPORT_SYMBOL(pme_attr_set);
+
+int pme_attr_get(enum pme_attr attr, u32 *val)
+{
+ u32 mask;
+ u32 attr_val;
+
+ if (!pme2_have_control())
+ return -ENODEV;
+
+ /* Check if Buffer size configuration */
+ if (attr >= pme_attr_bsc_first && attr <= pme_attr_bsc_last) {
+ u32 bsc_pool_id = attr - pme_attr_bsc_first;
+ u32 bsc_pool_offset = bsc_pool_id % 8;
+ /* calculate which sky-blue reg */
+ /* 0..7 -> bsc_(0..7), PME_REG_BSC0 */
+ /* 8..15 -> bsc_(8..15) PME_REG_BSC1*/
+ /* ... */
+ /* 56..63 -> bsc_(56..63) PME_REG_BSC7*/
+ attr_val = pme_in(global_pme, BSC0 + ((bsc_pool_id/8)*4));
+ attr_val = attr_val >> ((7-bsc_pool_offset)*4);
+ attr_val = attr_val & 0x0000000F;
+ *val = attr_val;
+ return 0;
+ }
+
+ switch (attr) {
+ case pme_attr_efqc_int:
+ mask = 0x8FFFFFFF;
+ attr_val = pme_in(global_pme, EFQC);
+ attr_val &= ~mask;
+ attr_val >>= 28;
+ break;
+
+ case pme_attr_sw_db:
+ attr_val = pme_in(global_pme, SWDB);
+ break;
+
+ case pme_attr_dmcr:
+ attr_val = pme_in(global_pme, DMCR);
+ break;
+
+ case pme_attr_smcr:
+ attr_val = pme_in(global_pme, SMCR);
+ break;
+
+ case pme_attr_famcr:
+ attr_val = pme_in(global_pme, FAMCR);
+ break;
+
+ case pme_attr_kvlts:
+ /* bit 28-31 */
+ attr_val = pme_in(global_pme, KVLTS);
+ attr_val &= 0x0000000F;
+ /* HW range: 1..15, SW range: 2..16 */
+ attr_val += 1;
+ break;
+
+ case pme_attr_max_chain_length:
+ /* bit 17-31 */
+ attr_val = pme_in(global_pme, KEC);
+ attr_val &= 0x00007FFF;
+ break;
+
+ case pme_attr_pattern_range_counter_idx:
+ /* bit 15-31 */
+ attr_val = pme_in(global_pme, DRCIC);
+ attr_val &= 0x0001FFFF;
+ break;
+
+ case pme_attr_pattern_range_counter_mask:
+ /* bit 15-31 */
+ attr_val = pme_in(global_pme, DRCMC);
+ attr_val &= 0x0001FFFF;
+ break;
+
+ case pme_attr_max_allowed_test_line_per_pattern:
+ /* bit 18-31 */
+ attr_val = pme_in(global_pme, DEC0);
+ attr_val &= 0x00003FFF;
+ break;
+
+ case pme_attr_max_pdsr_index:
+ /* bit 12-31 */
+ attr_val = pme_in(global_pme, DEC1);
+ attr_val &= 0x000FFFFF;
+ break;
+
+ case pme_attr_max_pattern_matches_per_sui:
+ attr_val = pme_in(global_pme, DLC);
+ attr_val &= 0x0000FFFF;
+ break;
+
+ case pme_attr_max_pattern_evaluations_per_sui:
+ attr_val = pme_in(global_pme, DLC);
+ attr_val >>= 16;
+ break;
+
+ case pme_attr_report_length_limit:
+ attr_val = pme_in(global_pme, RLL);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x0000FFFF;
+ break;
+
+ case pme_attr_end_of_simple_sui_report:
+ /* bit 13 */
+ attr_val = pme_in(global_pme, SREC);
+ attr_val >>= 18;
+ /* clear unwanted bits in val*/
+ attr_val &= 0x00000001;
+ break;
+
+ case pme_attr_aim:
+ /* bit 2 */
+ attr_val = pme_in(global_pme, SREC);
+ attr_val >>= 29;
+ /* clear unwanted bits in val*/
+ attr_val &= 0x00000001;
+ break;
+
+ case pme_attr_sre_context_size:
+ /* bits 9..12 */
+ attr_val = pme_in(global_pme, SREC);
+ attr_val >>= 19;
+ /* clear unwanted bits in val*/
+ attr_val &= 0x0000000F;
+ attr_val += 4;
+ attr_val = 1 << attr_val;
+ break;
+
+ case pme_attr_sre_rule_num:
+ /* bits 24..31 */
+ attr_val = pme_in(global_pme, SREC);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x000000FF;
+ /* Multiply by 256 */
+ attr_val <<= 8;
+ break;
+
+ case pme_attr_sre_session_ctx_num: {
+ u32 ctx_sz = 0;
+ /* = sre_table_size / sre_session_ctx_size */
+ attr_val = pme_in(global_pme, SEC3);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x07FFFFFF;
+ attr_val += 1;
+ attr_val *= 32;
+ ctx_sz = pme_in(global_pme, SREC);
+ ctx_sz >>= 19;
+ /* clear unwanted bits in val*/
+ ctx_sz &= 0x0000000F;
+ ctx_sz += 4;
+ attr_val /= (1 << ctx_sz);
+ }
+ break;
+
+ case pme_attr_end_of_sui_reaction_ptr:
+ /* bits 12..31 */
+ attr_val = pme_in(global_pme, ESRP);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x000FFFFF;
+ break;
+
+ case pme_attr_sre_pscl:
+ /* bits 22..31 */
+ attr_val = pme_in(global_pme, SFRCC);
+ break;
+
+ case pme_attr_sre_max_block_num:
+ /* bits 17..31 */
+ attr_val = pme_in(global_pme, SEC1);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x00007FFF;
+ break;
+
+ case pme_attr_sre_max_instruction_limit:
+ /* bits 0..15 */
+ attr_val = pme_in(global_pme, SEC1);
+ attr_val >>= 16;
+ break;
+
+ case pme_attr_sre_max_index_size:
+ /* bits 12..31 */
+ attr_val = pme_in(global_pme, SEC2);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x000FFFFF;
+ break;
+
+ case pme_attr_sre_max_offset_ctrl:
+ /* bits 5..31 */
+ attr_val = pme_in(global_pme, SEC3);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x07FFFFFF;
+ break;
+
+ case pme_attr_src_id:
+ /* bits 24..31 */
+ attr_val = pme_in(global_pme, SRCIDR);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x000000FF;
+ break;
+
+ case pme_attr_liodnr:
+ /* bits 20..31 */
+ attr_val = pme_in(global_pme, LIODNR);
+ /* clear unwanted bits in val*/
+ attr_val &= 0x00000FFF;
+ break;
+
+ case pme_attr_rev1:
+ /* bits 0..31 */
+ attr_val = pme_in(global_pme, PM_IP_REV1);
+ break;
+
+ case pme_attr_rev2:
+ /* bits 0..31 */
+ attr_val = pme_in(global_pme, PM_IP_REV2);
+ break;
+
+ case pme_attr_srrr:
+ attr_val = pme_in(global_pme, SRRR);
+ break;
+
+ case pme_attr_trunci:
+ attr_val = pme_in(global_pme, TRUNCI);
+ break;
+
+ case pme_attr_rbc:
+ attr_val = pme_in(global_pme, RBC);
+ break;
+
+ case pme_attr_tbt0ecc1ec:
+ attr_val = pme_in(global_pme, TBT0ECC1EC);
+ break;
+
+ case pme_attr_tbt1ecc1ec:
+ attr_val = pme_in(global_pme, TBT1ECC1EC);
+ break;
+
+ case pme_attr_vlt0ecc1ec:
+ attr_val = pme_in(global_pme, VLT0ECC1EC);
+ break;
+
+ case pme_attr_vlt1ecc1ec:
+ attr_val = pme_in(global_pme, VLT1ECC1EC);
+ break;
+
+ case pme_attr_cmecc1ec:
+ attr_val = pme_in(global_pme, CMECC1EC);
+ break;
+
+ case pme_attr_dxcmecc1ec:
+ attr_val = pme_in(global_pme, DXCMECC1EC);
+ break;
+
+ case pme_attr_dxemecc1ec:
+ attr_val = pme_in(global_pme, DXEMECC1EC);
+ break;
+
+ case pme_attr_tbt0ecc1th:
+ attr_val = pme_in(global_pme, TBT0ECC1TH);
+ break;
+
+ case pme_attr_tbt1ecc1th:
+ attr_val = pme_in(global_pme, TBT1ECC1TH);
+ break;
+
+ case pme_attr_vlt0ecc1th:
+ attr_val = pme_in(global_pme, VLT0ECC1TH);
+ break;
+
+ case pme_attr_vlt1ecc1th:
+ attr_val = pme_in(global_pme, VLT1ECC1TH);
+ break;
+
+ case pme_attr_cmecc1th:
+ attr_val = pme_in(global_pme, CMECC1TH);
+ break;
+
+ case pme_attr_dxcmecc1th:
+ attr_val = pme_in(global_pme, DXCMECC1TH);
+ break;
+
+ case pme_attr_dxemecc1th:
+ attr_val = pme_in(global_pme, DXEMECC1TH);
+ break;
+
+ case pme_attr_stnib:
+ attr_val = pme_in(global_pme, STNIB);
+ break;
+
+ case pme_attr_stnis:
+ attr_val = pme_in(global_pme, STNIS);
+ break;
+
+ case pme_attr_stnth1:
+ attr_val = pme_in(global_pme, STNTH1);
+ break;
+
+ case pme_attr_stnth2:
+ attr_val = pme_in(global_pme, STNTH2);
+ break;
+
+ case pme_attr_stnthv:
+ attr_val = pme_in(global_pme, STNTHV);
+ break;
+
+ case pme_attr_stnths:
+ attr_val = pme_in(global_pme, STNTHS);
+ break;
+
+ case pme_attr_stnch:
+ attr_val = pme_in(global_pme, STNCH);
+ break;
+
+ case pme_attr_stnpm:
+ attr_val = pme_in(global_pme, STNPM);
+ break;
+
+ case pme_attr_stns1m:
+ attr_val = pme_in(global_pme, STNS1M);
+ break;
+
+ case pme_attr_stnpmr:
+ attr_val = pme_in(global_pme, STNPMR);
+ break;
+
+ case pme_attr_stndsr:
+ attr_val = pme_in(global_pme, STNDSR);
+ break;
+
+ case pme_attr_stnesr:
+ attr_val = pme_in(global_pme, STNESR);
+ break;
+
+ case pme_attr_stns1r:
+ attr_val = pme_in(global_pme, STNS1R);
+ break;
+
+ case pme_attr_stnob:
+ attr_val = pme_in(global_pme, STNOB);
+ break;
+
+ case pme_attr_mia_byc:
+ attr_val = pme_in(global_pme, MIA_BYC);
+ break;
+
+ case pme_attr_mia_blc:
+ attr_val = pme_in(global_pme, MIA_BLC);
+ break;
+
+ case pme_attr_isr:
+ attr_val = pme_in(global_pme, ISR);
+ break;
+
+ case pme_attr_ecr0:
+ attr_val = pme_in(global_pme, ECR0);
+ break;
+
+ case pme_attr_ecr1:
+ attr_val = pme_in(global_pme, ECR1);
+ break;
+
+ case pme_attr_esr:
+ attr_val = pme_in(global_pme, ESR);
+ break;
+
+ case pme_attr_pmstat:
+ attr_val = pme_in(global_pme, PMSTAT);
+ break;
+
+ case pme_attr_pehd:
+ attr_val = pme_in(global_pme, PEHD);
+ break;
+
+ case pme_attr_ecc1bes:
+ attr_val = pme_in(global_pme, ECC1BES);
+ break;
+
+ case pme_attr_ecc2bes:
+ attr_val = pme_in(global_pme, ECC2BES);
+ break;
+
+ case pme_attr_eccaddr:
+ attr_val = pme_in(global_pme, ECCADDR);
+ break;
+
+ case pme_attr_ecccode:
+ attr_val = pme_in(global_pme, ECCCODE);
+ break;
+
+ case pme_attr_miace:
+ attr_val = pme_in(global_pme, MIA_CE);
+ break;
+
+ case pme_attr_miacr:
+ attr_val = pme_in(global_pme, MIA_CR);
+ break;
+
+ case pme_attr_cdcr:
+ attr_val = pme_in(global_pme, CDCR);
+ break;
+
+ case pme_attr_pmtr:
+ attr_val = pme_in(global_pme, PMTR);
+ break;
+
+ case pme_attr_faconf:
+ attr_val = pme_in(global_pme, FACONF);
+ break;
+
+ case pme_attr_pdsrbah:
+ attr_val = pme_in(global_pme, PDSRBAH);
+ break;
+
+ case pme_attr_pdsrbal:
+ attr_val = pme_in(global_pme, PDSRBAL);
+ break;
+
+ case pme_attr_scbarh:
+ attr_val = pme_in(global_pme, SCBARH);
+ break;
+
+ case pme_attr_scbarl:
+ attr_val = pme_in(global_pme, SCBARL);
+ break;
+
+ case pme_attr_srrv0:
+ attr_val = pme_in(global_pme, SRRV0);
+ break;
+
+ case pme_attr_srrv1:
+ attr_val = pme_in(global_pme, SRRV1);
+ break;
+
+ case pme_attr_srrv2:
+ attr_val = pme_in(global_pme, SRRV2);
+ break;
+
+ case pme_attr_srrv3:
+ attr_val = pme_in(global_pme, SRRV3);
+ break;
+
+ case pme_attr_srrv4:
+ attr_val = pme_in(global_pme, SRRV4);
+ break;
+
+ case pme_attr_srrv5:
+ attr_val = pme_in(global_pme, SRRV5);
+ break;
+
+ case pme_attr_srrv6:
+ attr_val = pme_in(global_pme, SRRV6);
+ break;
+
+ case pme_attr_srrv7:
+ attr_val = pme_in(global_pme, SRRV7);
+ break;
+
+ case pme_attr_srrfi:
+ attr_val = pme_in(global_pme, SRRFI);
+ break;
+
+ case pme_attr_srri:
+ attr_val = pme_in(global_pme, SRRI);
+ break;
+
+ case pme_attr_srrwc:
+ attr_val = pme_in(global_pme, SRRWC);
+ break;
+
+ default:
+ pr_err("pme: Unknown attr %u\n", attr);
+ return -EINVAL;
+ };
+ *val = attr_val;
+ return 0;
+}
+EXPORT_SYMBOL(pme_attr_get);
+
+static enum pme_attr stat_list[] = {
+ pme_attr_trunci,
+ pme_attr_rbc,
+ pme_attr_tbt0ecc1ec,
+ pme_attr_tbt1ecc1ec,
+ pme_attr_vlt0ecc1ec,
+ pme_attr_vlt1ecc1ec,
+ pme_attr_cmecc1ec,
+ pme_attr_dxcmecc1ec,
+ pme_attr_dxemecc1ec,
+ pme_attr_stnib,
+ pme_attr_stnis,
+ pme_attr_stnth1,
+ pme_attr_stnth2,
+ pme_attr_stnthv,
+ pme_attr_stnths,
+ pme_attr_stnch,
+ pme_attr_stnpm,
+ pme_attr_stns1m,
+ pme_attr_stnpmr,
+ pme_attr_stndsr,
+ pme_attr_stnesr,
+ pme_attr_stns1r,
+ pme_attr_stnob,
+ pme_attr_mia_byc,
+ pme_attr_mia_blc
+};
+
+static u64 pme_stats[sizeof(stat_list)/sizeof(enum pme_attr)];
+static DEFINE_SPINLOCK(stat_lock);
+
+int pme_stat_get(enum pme_attr stat, u64 *value, int reset)
+{
+ int i, ret = 0;
+ int value_set = 0;
+ u32 val;
+
+ spin_lock_irq(&stat_lock);
+ for (i = 0; i < sizeof(stat_list)/sizeof(enum pme_attr); i++) {
+ if (stat_list[i] == stat) {
+ ret = pme_attr_get(stat_list[i], &val);
+ /* Do I need to check ret */
+ pme_stats[i] += val;
+ *value = pme_stats[i];
+ value_set = 1;
+ if (reset)
+ pme_stats[i] = 0;
+ break;
+ }
+ }
+ if (!value_set) {
+ pr_err("pme: Invalid stat request %d\n", stat);
+ ret = -EINVAL;
+ }
+ spin_unlock_irq(&stat_lock);
+ return ret;
+}
+EXPORT_SYMBOL(pme_stat_get);
+
+void accumulator_update_interval(u32 interval)
+{
+ int schedule = 0;
+
+ spin_lock_irq(&stat_lock);
+ if (!pme_stat_interval && interval)
+ schedule = 1;
+ pme_stat_interval = interval;
+ spin_unlock_irq(&stat_lock);
+ if (schedule)
+ schedule_delayed_work(&accumulator_work,
+ msecs_to_jiffies(interval));
+}
+
+static void accumulator_update(struct work_struct *work)
+{
+ int i, ret;
+ u32 local_interval;
+ u32 val;
+
+ spin_lock_irq(&stat_lock);
+ local_interval = pme_stat_interval;
+ for (i = 0; i < sizeof(stat_list)/sizeof(enum pme_attr); i++) {
+ ret = pme_attr_get(stat_list[i], &val);
+ pme_stats[i] += val;
+ }
+ spin_unlock_irq(&stat_lock);
+ if (local_interval)
+ schedule_delayed_work(&accumulator_work,
+ msecs_to_jiffies(local_interval));
+}
+
new file mode 100644
@@ -0,0 +1,572 @@
+/* Copyright 2009-2011 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 "pme2_private.h"
+#include <linux/compat.h>
+
+/* Forward declaration */
+static struct miscdevice fsl_pme2_db_dev;
+
+/* Global spinlock for handling exclusive inc/dec */
+static DEFINE_SPINLOCK(exclusive_lock);
+
+/* Private structure that is allocated for each open that is done on the
+ * pme_db device. This is used to maintain the state of a database session */
+struct db_session {
+ /* The ctx that is needed to communicate with the pme high level */
+ struct pme_ctx ctx;
+ /* Used to track the EXCLUSIVE_INC and EXCLUSIVE_DEC ioctls */
+ unsigned int exclusive_counter;
+};
+
+struct cmd_token {
+ /* pme high level token */
+ struct pme_ctx_token hl_token;
+ /* data */
+ struct qm_fd rx_fd;
+ /* Completion interface */
+ struct completion cb_done;
+ u8 ern;
+};
+
+#ifdef CONFIG_COMPAT
+static void compat_to_db(struct pme_db *dst, struct compat_pme_db *src)
+{
+ dst->flags = src->flags;
+ dst->status = src->status;
+ dst->input.data = compat_ptr(src->input.data);
+ dst->input.size = src->input.size;
+ dst->output.data = compat_ptr(src->output.data);
+ dst->output.size = src->output.size;
+}
+
+static void db_to_compat(struct compat_pme_db *dst, struct pme_db *src)
+{
+ dst->flags = src->flags;
+ dst->status = src->status;
+ dst->output.data = ptr_to_compat(src->output.data);
+ dst->output.size = src->output.size;
+ dst->input.data = ptr_to_compat(src->input.data);
+ dst->input.size = src->input.size;
+}
+#endif
+
+/* PME Compound Frame Index */
+#define INPUT_FRM 1
+#define OUTPUT_FRM 0
+
+/* Callback for database operations */
+static void db_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_token *ctx_token)
+{
+ struct cmd_token *token = (struct cmd_token *)ctx_token;
+ token->rx_fd = *fd;
+ complete(&token->cb_done);
+}
+
+static void db_ern_cb(struct pme_ctx *ctx, const struct qm_mr_entry *mr,
+ struct pme_ctx_token *ctx_token)
+{
+ struct cmd_token *token = (struct cmd_token *)ctx_token;
+ token->ern = 1;
+ token->rx_fd = mr->ern.fd;
+ complete(&token->cb_done);
+}
+
+struct ctrl_op {
+ struct pme_ctx_ctrl_token ctx_ctr;
+ struct completion cb_done;
+ enum pme_status cmd_status;
+ u8 res_flag;
+ u8 ern;
+};
+
+static void ctrl_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_ctrl_token *token)
+{
+ struct ctrl_op *ctrl = (struct ctrl_op *)token;
+ ctrl->cmd_status = pme_fd_res_status(fd);
+ ctrl->res_flag = pme_fd_res_flags(fd) & PME_STATUS_UNRELIABLE;
+ complete(&ctrl->cb_done);
+}
+
+static void ctrl_ern_cb(struct pme_ctx *ctx, const struct qm_mr_entry *mr,
+ struct pme_ctx_ctrl_token *token)
+{
+ struct ctrl_op *ctrl = (struct ctrl_op *)token;
+ ctrl->ern = 1;
+ complete(&ctrl->cb_done);
+}
+
+static int exclusive_inc(struct file *fp, struct db_session *db)
+{
+ int ret;
+
+ BUG_ON(!db);
+ BUG_ON(!(db->ctx.flags & PME_CTX_FLAG_EXCLUSIVE));
+ spin_lock(&exclusive_lock);
+ ret = pme_ctx_exclusive_inc(&db->ctx,
+ (PME_CTX_OP_WAIT | PME_CTX_OP_WAIT_INT));
+ if (!ret)
+ db->exclusive_counter++;
+ spin_unlock(&exclusive_lock);
+ return ret;
+}
+
+static int exclusive_dec(struct file *fp, struct db_session *db)
+{
+ int ret = 0;
+
+ BUG_ON(!db);
+ BUG_ON(!(db->ctx.flags & PME_CTX_FLAG_EXCLUSIVE));
+ spin_lock(&exclusive_lock);
+ if (!db->exclusive_counter) {
+ PMEPRERR("exclusivity counter already zero\n");
+ ret = -EINVAL;
+ } else {
+ pme_ctx_exclusive_dec(&db->ctx);
+ db->exclusive_counter--;
+ }
+ spin_unlock(&exclusive_lock);
+ return ret;
+}
+
+static int execute_cmd(struct file *fp, struct db_session *db,
+ struct pme_db *db_cmd)
+{
+ int ret = 0;
+ struct cmd_token token;
+ struct qm_sg_entry tx_comp[2];
+ struct qm_fd tx_fd;
+ void *tx_data = NULL;
+ void *rx_data = NULL;
+ u32 src_sz, dst_sz;
+ dma_addr_t dma_addr;
+
+ memset(&token, 0, sizeof(struct cmd_token));
+ memset(tx_comp, 0, sizeof(tx_comp));
+ memset(&tx_fd, 0, sizeof(struct qm_fd));
+ init_completion(&token.cb_done);
+
+ PMEPRINFO("Received User Space Contiguous mem\n");
+ PMEPRINFO("length = %d\n", db_cmd->input.size);
+ tx_data = kmalloc(db_cmd->input.size, GFP_KERNEL);
+ if (!tx_data) {
+ PMEPRERR("Err alloc %zd byte\n", db_cmd->input.size);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(tx_data,
+ (void __user *)db_cmd->input.data,
+ db_cmd->input.size)) {
+ PMEPRERR("Error copying contigous user data\n");
+ ret = -EFAULT;
+ goto free_tx_data;
+ }
+
+ /* Setup input frame */
+ tx_comp[INPUT_FRM].final = 1;
+ tx_comp[INPUT_FRM].length = db_cmd->input.size;
+ dma_addr = pme_map(tx_data);
+ if (pme_map_error(dma_addr)) {
+ PMEPRERR("Error pme_map_error\n");
+ ret = -EIO;
+ goto free_tx_data;
+ }
+ set_sg_addr(&tx_comp[INPUT_FRM], dma_addr);
+ /* setup output frame, if output is expected */
+ if (db_cmd->output.size) {
+ PMEPRINFO("expect output %d\n", db_cmd->output.size);
+ rx_data = kmalloc(db_cmd->output.size, GFP_KERNEL);
+ if (!rx_data) {
+ PMEPRERR("Err alloc %zd byte", db_cmd->output.size);
+ ret = -ENOMEM;
+ goto unmap_input_frame;
+ }
+ /* Setup output frame */
+ tx_comp[OUTPUT_FRM].length = db_cmd->output.size;
+ dma_addr = pme_map(rx_data);
+ if (pme_map_error(dma_addr)) {
+ PMEPRERR("Error pme_map_error\n");
+ ret = -EIO;
+ goto comp_frame_free_rx;
+ }
+ set_sg_addr(&tx_comp[OUTPUT_FRM], dma_addr);
+ tx_fd.format = qm_fd_compound;
+ /* Build compound frame */
+ dma_addr = pme_map(tx_comp);
+ if (pme_map_error(dma_addr)) {
+ PMEPRERR("Error pme_map_error\n");
+ ret = -EIO;
+ goto comp_frame_unmap_output;
+ }
+ set_fd_addr(&tx_fd, dma_addr);
+ } else {
+ tx_fd.format = qm_fd_sg_big;
+ tx_fd.length29 = db_cmd->input.size;
+ /* Build sg frame */
+ dma_addr = pme_map(&tx_comp[INPUT_FRM]);
+ if (pme_map_error(dma_addr)) {
+ PMEPRERR("Error pme_map_error\n");
+ ret = -EIO;
+ goto unmap_input_frame;
+ }
+ set_fd_addr(&tx_fd, dma_addr);
+ }
+ ret = pme_ctx_pmtcc(&db->ctx, PME_CTX_OP_WAIT, &tx_fd,
+ (struct pme_ctx_token *)&token);
+ if (unlikely(ret)) {
+ PMEPRINFO("pme_ctx_pmtcc error %d\n", ret);
+ goto unmap_frame;
+ }
+ PMEPRINFO("Wait for completion\n");
+ /* Wait for the command to complete */
+ wait_for_completion(&token.cb_done);
+
+ if (token.ern) {
+ ret = -EIO;
+ goto unmap_frame;
+ }
+
+ PMEPRINFO("pme2_db: process_completed_token\n");
+ PMEPRINFO("pme2_db: received %d frame type\n", token.rx_fd.format);
+ if (token.rx_fd.format == qm_fd_compound) {
+ /* Need to copy output */
+ src_sz = tx_comp[OUTPUT_FRM].length;
+ dst_sz = db_cmd->output.size;
+ PMEPRINFO("pme gen %u data, have space for %u\n",
+ src_sz, dst_sz);
+ db_cmd->output.size = min(dst_sz, src_sz);
+ /* Doesn't make sense we generated more than available space
+ * should have got truncation.
+ */
+ BUG_ON(dst_sz < src_sz);
+ if (copy_to_user((void __user *)db_cmd->output.data, rx_data,
+ db_cmd->output.size)) {
+ PMEPRERR("Error copying to user data\n");
+ ret = -EFAULT;
+ goto comp_frame_unmap_cf;
+ }
+ } else if (token.rx_fd.format == qm_fd_sg_big)
+ db_cmd->output.size = 0;
+ else
+ panic("unexpected frame type received %d\n",
+ token.rx_fd.format);
+
+ db_cmd->flags = pme_fd_res_flags(&token.rx_fd);
+ db_cmd->status = pme_fd_res_status(&token.rx_fd);
+
+unmap_frame:
+ if (token.rx_fd.format == qm_fd_sg_big)
+ goto single_frame_unmap_frame;
+
+comp_frame_unmap_cf:
+comp_frame_unmap_output:
+comp_frame_free_rx:
+ kfree(rx_data);
+ goto unmap_input_frame;
+single_frame_unmap_frame:
+unmap_input_frame:
+free_tx_data:
+ kfree(tx_data);
+
+ return ret;
+}
+
+static int execute_nop(struct file *fp, struct db_session *db)
+{
+ int ret = 0;
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .ctx_ctr.ern_cb = ctrl_ern_cb
+ };
+ init_completion(&ctx_ctrl.cb_done);
+
+ ret = pme_ctx_ctrl_nop(&db->ctx, PME_CTX_OP_WAIT|PME_CTX_OP_WAIT_INT,
+ &ctx_ctrl.ctx_ctr);
+ if (!ret)
+ wait_for_completion(&ctx_ctrl.cb_done);
+
+ if (ctx_ctrl.ern)
+ ret = -EIO;
+ return ret;
+}
+
+static atomic_t sre_reset_lock = ATOMIC_INIT(1);
+static int ioctl_sre_reset(unsigned long arg)
+{
+ struct pme_db_sre_reset reset_vals;
+ int i;
+ u32 srrr_val;
+ int ret = 0;
+
+ if (copy_from_user(&reset_vals, (struct pme_db_sre_reset __user *)arg,
+ sizeof(struct pme_db_sre_reset)))
+ return -EFAULT;
+ PMEPRINFO("sre_reset:\n");
+ PMEPRINFO(" rule_index = 0x%x:\n", reset_vals.rule_index);
+ PMEPRINFO(" rule_increment = 0x%x:\n", reset_vals.rule_increment);
+ PMEPRINFO(" rule_repetitions = 0x%x:\n", reset_vals.rule_repetitions);
+ PMEPRINFO(" rule_reset_interval = 0x%x:\n",
+ reset_vals.rule_reset_interval);
+ PMEPRINFO(" rule_reset_priority = 0x%x:\n",
+ reset_vals.rule_reset_priority);
+
+ /* Validate ranges */
+ if ((reset_vals.rule_index >= PME_PMFA_SRE_INDEX_MAX) ||
+ (reset_vals.rule_increment > PME_PMFA_SRE_INC_MAX) ||
+ (reset_vals.rule_repetitions >= PME_PMFA_SRE_REP_MAX) ||
+ (reset_vals.rule_reset_interval >=
+ PME_PMFA_SRE_INTERVAL_MAX))
+ return -ERANGE;
+ /* Check and make sure only one caller is present */
+ if (!atomic_dec_and_test(&sre_reset_lock)) {
+ /* Someone else is already in this call */
+ atomic_inc(&sre_reset_lock);
+ return -EBUSY;
+ };
+ /* All validated. Run the command */
+ for (i = 0; i < PME_SRE_RULE_VECTOR_SIZE; i++)
+ pme_attr_set(pme_attr_srrv0 + i, reset_vals.rule_vector[i]);
+ pme_attr_set(pme_attr_srrfi, reset_vals.rule_index);
+ pme_attr_set(pme_attr_srri, reset_vals.rule_increment);
+ pme_attr_set(pme_attr_srrwc,
+ (0xFFF & reset_vals.rule_reset_interval) << 1 |
+ (reset_vals.rule_reset_priority ? 1 : 0));
+ /* Need to set SRRR last */
+ pme_attr_set(pme_attr_srrr, reset_vals.rule_repetitions);
+ do {
+ mdelay(PME_PMFA_SRE_POLL_MS);
+ ret = pme_attr_get(pme_attr_srrr, &srrr_val);
+ if (ret) {
+ PMEPRCRIT("pme2: Error reading srrr\n");
+ /* bail */
+ break;
+ }
+ /* Check for error */
+ else if (srrr_val & 0x10000000) {
+ PMEPRERR("pme2: Error in SRRR\n");
+ ret = -EIO;
+ }
+ PMEPRINFO("pme2: srrr count %d\n", srrr_val);
+ } while (srrr_val);
+ atomic_inc(&sre_reset_lock);
+ return ret;
+}
+
+/**
+ * fsl_pme2_db_open - open the driver
+ *
+ * Open the driver and prepare for requests.
+ *
+ * Every time an application opens the driver, we create a db_session object
+ * for that file handle.
+ */
+static int fsl_pme2_db_open(struct inode *node, struct file *fp)
+{
+ int ret;
+ struct db_session *db = NULL;
+
+ db = kzalloc(sizeof(struct db_session), GFP_KERNEL);
+ if (!db)
+ return -ENOMEM;
+ fp->private_data = db;
+ db->ctx.cb = db_cb;
+ db->ctx.ern_cb = db_ern_cb;
+
+ ret = pme_ctx_init(&db->ctx,
+ PME_CTX_FLAG_EXCLUSIVE |
+ PME_CTX_FLAG_PMTCC |
+ PME_CTX_FLAG_DIRECT|
+ PME_CTX_FLAG_LOCAL,
+ 0, 4, CONFIG_FSL_PME2_DB_QOSOUT_PRIORITY, 0, NULL);
+ if (ret) {
+ PMEPRERR("pme_ctx_init %d\n", ret);
+ goto free_data;
+ }
+
+ /* enable the context */
+ ret = pme_ctx_enable(&db->ctx);
+ if (ret) {
+ PMEPRERR("error enabling ctx %d\n", ret);
+ pme_ctx_finish(&db->ctx);
+ goto free_data;
+ }
+ PMEPRINFO("pme2_db: Finish pme_db open %d\n", smp_processor_id());
+ return 0;
+free_data:
+ kfree(fp->private_data);
+ fp->private_data = NULL;
+ return ret;
+}
+
+static int fsl_pme2_db_close(struct inode *node, struct file *fp)
+{
+ int ret = 0;
+ struct db_session *db = fp->private_data;
+
+ PMEPRINFO("Start pme_db close\n");
+ while (db->exclusive_counter) {
+ pme_ctx_exclusive_dec(&db->ctx);
+ db->exclusive_counter--;
+ }
+
+ /* Disable context. */
+ ret = pme_ctx_disable(&db->ctx, PME_CTX_OP_WAIT, NULL);
+ if (ret)
+ PMEPRCRIT("Error disabling ctx %d\n", ret);
+ pme_ctx_finish(&db->ctx);
+ kfree(db);
+ PMEPRINFO("Finish pme_db close\n");
+ return 0;
+}
+
+/* Main switch loop for ioctl operations */
+static long fsl_pme2_db_ioctl(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct db_session *db = fp->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+
+ case PMEIO_PMTCC: {
+ int ret;
+ struct pme_db db_cmd;
+
+ /* Copy the command to kernel space */
+ if (copy_from_user(&db_cmd, (void __user *)arg,
+ sizeof(db_cmd)))
+ return -EFAULT;
+ ret = execute_cmd(fp, db, &db_cmd);
+ if (!ret)
+ ret = copy_to_user((struct pme_db __user *)arg,
+ &db_cmd, sizeof(db_cmd));
+ return ret;
+ }
+ break;
+
+ case PMEIO_EXL_INC:
+ return exclusive_inc(fp, db);
+ case PMEIO_EXL_DEC:
+ return exclusive_dec(fp, db);
+ case PMEIO_EXL_GET:
+ BUG_ON(!db);
+ BUG_ON(!(db->ctx.flags & PME_CTX_FLAG_EXCLUSIVE));
+ if (copy_to_user((void __user *)arg,
+ &db->exclusive_counter,
+ sizeof(db->exclusive_counter)))
+ ret = -EFAULT;
+ return ret;
+ case PMEIO_NOP:
+ return execute_nop(fp, db);
+ case PMEIO_SRE_RESET:
+ return ioctl_sre_reset(arg);
+
+#ifdef CONFIG_COMPAT
+ case PMEIO_PMTCC32: {
+ int ret;
+ struct pme_db db_cmd;
+ struct compat_pme_db db_cmd32;
+ struct compat_pme_db __user *user_db_cmd = compat_ptr(arg);
+
+ /* Copy the command to kernel space */
+ if (copy_from_user(&db_cmd32, user_db_cmd, sizeof(db_cmd32)))
+ return -EFAULT;
+ /* Convert to 64-bit struct */
+ compat_to_db(&db_cmd, &db_cmd32);
+ ret = execute_cmd(fp, db, &db_cmd);
+ if (!ret) {
+ /* Convert to compat struct */
+ db_to_compat(&db_cmd32, &db_cmd);
+ ret = copy_to_user(user_db_cmd, &db_cmd32,
+ sizeof(*user_db_cmd));
+ }
+ return ret;
+ }
+ break;
+#endif
+ }
+ pr_info("Unknown pme_db ioctl cmd %u\n", cmd);
+ return -EINVAL;
+}
+
+static const struct file_operations fsl_pme2_db_fops = {
+ .owner = THIS_MODULE,
+ .open = fsl_pme2_db_open,
+ .release = fsl_pme2_db_close,
+ .unlocked_ioctl = fsl_pme2_db_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = fsl_pme2_db_ioctl,
+#endif
+};
+
+static struct miscdevice fsl_pme2_db_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = PME_DEV_DB_NODE,
+ .fops = &fsl_pme2_db_fops
+};
+
+static int __init fsl_pme2_db_init(void)
+{
+ int err = 0;
+
+ pr_info("Freescale pme2 db driver\n");
+ if (!pme2_have_control()) {
+ PMEPRERR("not on ctrl-plane\n");
+ return -ENODEV;
+ }
+ err = misc_register(&fsl_pme2_db_dev);
+ if (err) {
+ PMEPRERR("cannot register device\n");
+ return err;
+ }
+ PMEPRINFO("device %s registered\n", fsl_pme2_db_dev.name);
+ return 0;
+}
+
+static void __exit fsl_pme2_db_exit(void)
+{
+ int err = misc_deregister(&fsl_pme2_db_dev);
+ if (err) {
+ PMEPRERR("Failed to deregister device %s, "
+ "code %d\n", fsl_pme2_db_dev.name, err);
+ return;
+ }
+ PMEPRINFO("device %s deregistered\n", fsl_pme2_db_dev.name);
+}
+
+module_init(fsl_pme2_db_init);
+module_exit(fsl_pme2_db_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor - OTC");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL PME2 db driver");
new file mode 100644
@@ -0,0 +1,944 @@
+/* Copyright 2008-2011 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 "pme2_private.h"
+
+/* The pme_ctx state machine is described via the following list of
+ * internal PME_CTX_FLAG_*** bits and cross-referenced to the APIs (and
+ * functionality) they track.
+ *
+ * DEAD: set at any point, an error has been hit, doesn't "cause" disabling or
+ * any autonomous ref-decrement (been there, hit the gotchas, won't do it
+ * again).
+ *
+ * DISABLING: set by pme_ctx_disable() at any point that is not already
+ * disabling, disabled, or in ctrl, and the ref is decremented. DISABLING is
+ * unset by pme_ctx_enable().
+ *
+ * DISABLED: once pme_ctx_disable() has set DISABLING and refs==0, DISABLED is
+ * set before returning. (Any failure will clear DISABLING and increment the ref
+ * count.) DISABLING is unset by pme_ctx_enable().
+ *
+ * ENABLING: set by pme_ctx_enable() provided the context is disabled, not dead,
+ * not in RECONFIG, and not already enabling. Once set, the ref is incremented
+ * and the tx FQ is scheduled (for non-exclusive flows). If this fails, the ref
+ * is decremented and the context is re-disabled. ENABLING is unset once
+ * pme_ctx_enable() completes.
+ *
+ * RECONFIG: set by pme_ctx_reconfigure_[rt]x() provided the context is
+ * disabled, not dead, and not already in reconfig. RECONFIG is cleared prior to
+ * the function returning.
+ *
+ * Simplifications: the do_flag() wrapper provides synchronised modifications of
+ * the ctx 'flags', and callers can rely on the following implications to reduce
+ * the number of flags in the masks being passed in;
+ * DISABLED implies DISABLING (and enable will clear both)
+ */
+
+/* Internal-only ctx flags, mustn't conflict with exported ones */
+#define PME_CTX_FLAG_DEAD 0x80000000
+#define PME_CTX_FLAG_DISABLING 0x40000000
+#define PME_CTX_FLAG_DISABLED 0x20000000
+#define PME_CTX_FLAG_ENABLING 0x10000000
+#define PME_CTX_FLAG_RECONFIG 0x08000000
+#define PME_CTX_FLAG_PRIVATE 0xf8000000 /* mask of them all */
+
+/* Internal-only cmd flags, musn't conflict with exported ones */
+#define PME_CTX_OP_INSIDE_DISABLE 0x80000000
+#define PME_CTX_OP_PRIVATE 0x80000000 /* mask of them all */
+
+struct pme_nostash {
+ struct qman_fq fqin;
+ struct pme_ctx *parent;
+};
+
+/* This wrapper simplifies conditional (and locked) read-modify-writes to
+ * 'flags'. Inlining should allow the compiler to optimise it based on the
+ * parameters, eg. if 'must_be_set'/'must_not_be_set' are zero it will
+ * degenerate to an unconditional read-modify-write, if 'to_set'/'to_unset' are
+ * zero it will degenerate to a read-only flag-check, etc. */
+static inline int do_flags(struct pme_ctx *ctx,
+ u32 must_be_set, u32 must_not_be_set,
+ u32 to_set, u32 to_unset)
+{
+ int err = -EBUSY;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&ctx->lock, irqflags);
+ if (((ctx->flags & must_be_set) == must_be_set) &&
+ !(ctx->flags & must_not_be_set)) {
+ ctx->flags |= to_set;
+ ctx->flags &= ~to_unset;
+ err = 0;
+ }
+ spin_unlock_irqrestore(&ctx->lock, irqflags);
+ return err;
+}
+
+static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *, struct qman_fq *,
+ const struct qm_dqrr_entry *);
+static void cb_ern(struct qman_portal *, struct qman_fq *,
+ const struct qm_mr_entry *);
+static void cb_dc_ern(struct qman_portal *, struct qman_fq *,
+ const struct qm_mr_entry *);
+static void cb_fqs(struct qman_portal *, struct qman_fq *,
+ const struct qm_mr_entry *);
+static const struct qman_fq_cb pme_fq_base_in = {
+ .fqs = cb_fqs,
+ .ern = cb_ern
+};
+static const struct qman_fq_cb pme_fq_base_out = {
+ .dqrr = cb_dqrr,
+ .dc_ern = cb_dc_ern,
+ .fqs = cb_fqs
+};
+
+/* Globals related to competition for PME_EFQC, ie. exclusivity */
+static DECLARE_WAIT_QUEUE_HEAD(exclusive_queue);
+static spinlock_t exclusive_lock = __SPIN_LOCK_UNLOCKED(exclusive_lock);
+static unsigned int exclusive_refs;
+static struct pme_ctx *exclusive_ctx;
+
+/* Index 0..255, bools do indicated which errors are serious
+ * 0x40, 0x41, 0x48, 0x49, 0x4c, 0x4e, 0x4f, 0x50, 0x51, 0x59, 0x5a, 0x5b,
+ * 0x5c, 0x5d, 0x5f, 0x60, 0x80, 0xc0, 0xc1, 0xc2, 0xc4, 0xd2,
+ * 0xd4, 0xd5, 0xd7, 0xd9, 0xda, 0xe0, 0xe7
+ */
+static u8 serious_error_vec[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* TODO: this is hitting the rx FQ with a large blunt instrument, ie. park()
+ * does a retire, query, oos, and (re)init. It's possible to force-eligible the
+ * rx FQ instead, then use a DCA_PK within the cb_dqrr() callback to park it.
+ * Implement this optimisation later if it's an issue (and incur the additional
+ * complexity in the state-machine). */
+static int park(struct qman_fq *fq, struct qm_mcc_initfq *initfq)
+{
+ int ret;
+ u32 flags;
+
+ ret = qman_retire_fq(fq, &flags);
+ if (ret)
+ return ret;
+ BUG_ON(flags & QMAN_FQ_STATE_BLOCKOOS);
+ /* We can't revert from now on */
+ ret = qman_query_fq(fq, &initfq->fqd);
+ BUG_ON(ret);
+ ret = qman_oos_fq(fq);
+ BUG_ON(ret);
+ /* can't set QM_INITFQ_WE_OAC and QM_INITFQ_WE_TDTHRESH
+ * at the same time */
+ initfq->we_mask = QM_INITFQ_WE_MASK & ~QM_INITFQ_WE_TDTHRESH;
+ ret = qman_init_fq(fq, 0, initfq);
+ BUG_ON(ret);
+ initfq->we_mask = QM_INITFQ_WE_TDTHRESH;
+ ret = qman_init_fq(fq, 0, initfq);
+ BUG_ON(ret);
+ return 0;
+}
+
+static inline int reconfigure_rx(struct pme_ctx *ctx, int to_park, u8 qosout,
+ enum qm_channel dest,
+ const struct qm_fqd_stashing *stashing)
+{
+ struct qm_mcc_initfq initfq;
+ u32 flags = QMAN_INITFQ_FLAG_SCHED;
+ int ret;
+
+ ret = do_flags(ctx, PME_CTX_FLAG_DISABLED,
+ PME_CTX_FLAG_DEAD | PME_CTX_FLAG_RECONFIG,
+ PME_CTX_FLAG_RECONFIG, 0);
+ if (ret)
+ return ret;
+ if (to_park) {
+ ret = park(&ctx->fq, &initfq);
+ if (ret)
+ goto done;
+ }
+ initfq.we_mask = QM_INITFQ_WE_DESTWQ | QM_INITFQ_WE_FQCTRL;
+ initfq.fqd.dest.wq = qosout;
+ if (stashing) {
+ initfq.we_mask |= QM_INITFQ_WE_CONTEXTA;
+ initfq.fqd.context_a.stashing = *stashing;
+ initfq.fqd.fq_ctrl = QM_FQCTRL_CTXASTASHING;
+ } else
+ initfq.fqd.fq_ctrl = 0; /* disable stashing */
+ if (ctx->flags & PME_CTX_FLAG_LOCAL)
+ flags |= QMAN_INITFQ_FLAG_LOCAL;
+ else {
+ initfq.fqd.dest.channel = dest;
+ /* Set hold-active *IFF* it's a pool channel */
+ if (dest >= qm_channel_pool1)
+ initfq.fqd.fq_ctrl |= QM_FQCTRL_HOLDACTIVE;
+ }
+ ret = qman_init_fq(&ctx->fq, flags, &initfq);
+done:
+ do_flags(ctx, 0, 0, 0, PME_CTX_FLAG_RECONFIG);
+ return ret;
+}
+
+/* this code is factored out of pme_ctx_disable() and get_ctrl() */
+static int empty_pipeline(struct pme_ctx *ctx, __maybe_unused u32 flags)
+{
+ int ret;
+#ifdef CONFIG_FSL_DPA_CAN_WAIT
+ if (flags & PME_CTX_OP_WAIT) {
+ if (flags & PME_CTX_OP_WAIT_INT) {
+ ret = -EINTR;
+ wait_event_interruptible(ctx->queue,
+ !(ret = atomic_read(&ctx->refs)));
+ } else
+ wait_event(ctx->queue,
+ !(ret = atomic_read(&ctx->refs)));
+ } else
+#endif
+ ret = atomic_read(&ctx->refs);
+ if (ret)
+ /* convert a +ve ref-count to a -ve error code */
+ ret = -EBUSY;
+ return ret;
+}
+
+int pme_ctx_init(struct pme_ctx *ctx, u32 flags, u32 bpid, u8 qosin,
+ u8 qosout, enum qm_channel dest,
+ const struct qm_fqd_stashing *stashing)
+{
+ u32 fqid_rx = 0, fqid_tx = 0;
+ int rxinit = 0, ret = -ENOMEM, fqin_inited = 0;
+
+ ctx->fq.cb = pme_fq_base_out;
+ atomic_set(&ctx->refs, 0);
+ ctx->flags = (flags & ~PME_CTX_FLAG_PRIVATE) | PME_CTX_FLAG_DISABLED |
+ PME_CTX_FLAG_DISABLING;
+ if (ctx->flags & PME_CTX_FLAG_PMTCC)
+ ctx->flags |= PME_CTX_FLAG_DIRECT | PME_CTX_FLAG_EXCLUSIVE;
+ spin_lock_init(&ctx->lock);
+ init_waitqueue_head(&ctx->queue);
+ INIT_LIST_HEAD(&ctx->tokens);
+ ctx->hw_flow = NULL;
+ ctx->hw_residue = NULL;
+
+ ctx->us_data = kzalloc(sizeof(struct pme_nostash), GFP_KERNEL);
+ if (!ctx->us_data)
+ goto err;
+ ctx->us_data->parent = ctx;
+ fqid_rx = qm_fq_new();
+ fqid_tx = qm_fq_new();
+ if (!fqid_rx || !fqid_tx || !ctx->us_data)
+ goto err;
+ ctx->us_data->fqin.cb = pme_fq_base_in;
+ if (qman_create_fq(fqid_rx, QMAN_FQ_FLAG_TO_DCPORTAL |
+ ((flags & PME_CTX_FLAG_LOCKED) ?
+ QMAN_FQ_FLAG_LOCKED : 0),
+ &ctx->us_data->fqin))
+ goto err;
+ fqin_inited = 1;
+ if (qman_create_fq(fqid_tx, QMAN_FQ_FLAG_NO_ENQUEUE |
+ ((flags & PME_CTX_FLAG_LOCKED) ?
+ QMAN_FQ_FLAG_LOCKED : 0), &ctx->fq))
+ goto err;
+ rxinit = 1;
+ /* Input FQ */
+ if (!(flags & PME_CTX_FLAG_DIRECT)) {
+ ctx->hw_flow = pme_hw_flow_new();
+ if (!ctx->hw_flow)
+ goto err;
+ }
+ ret = pme_ctx_reconfigure_tx(ctx, bpid, qosin);
+ if (ret)
+ goto err;
+ /* Output FQ */
+ ret = reconfigure_rx(ctx, 0, qosout, dest, stashing);
+ if (ret) {
+ /* Need to OOS the FQ before it gets free'd */
+ ret = qman_oos_fq(&ctx->us_data->fqin);
+ BUG_ON(ret);
+ goto err;
+ }
+ return 0;
+err:
+ if (fqid_rx)
+ qm_fq_free(fqid_rx);
+ if (fqid_tx)
+ qm_fq_free(fqid_tx);
+ if (ctx->hw_flow)
+ pme_hw_flow_free(ctx->hw_flow);
+ if (ctx->us_data) {
+ if (fqin_inited)
+ qman_destroy_fq(&ctx->us_data->fqin, 0);
+ kfree(ctx->us_data);
+ }
+ if (rxinit)
+ qman_destroy_fq(&ctx->fq, 0);
+ return ret;
+}
+EXPORT_SYMBOL(pme_ctx_init);
+
+/* NB, we don't lock here because there must be no other callers (even if we
+ * locked, what does the loser do after we win?) */
+void pme_ctx_finish(struct pme_ctx *ctx)
+{
+ u32 flags, fqid_rx, fqid_tx;
+ int ret;
+
+ ret = do_flags(ctx, PME_CTX_FLAG_DISABLED, PME_CTX_FLAG_RECONFIG, 0, 0);
+ BUG_ON(ret);
+ /* Rx/Tx are empty (coz ctx is disabled) so retirement should be
+ * immediate */
+ ret = qman_retire_fq(&ctx->us_data->fqin, &flags);
+ BUG_ON(ret);
+ BUG_ON(flags & QMAN_FQ_STATE_BLOCKOOS);
+ ret = qman_retire_fq(&ctx->fq, &flags);
+ BUG_ON(ret);
+ BUG_ON(flags & QMAN_FQ_STATE_BLOCKOOS);
+ /* OOS and free (don't kfree fq, it's a static ctx member) */
+ ret = qman_oos_fq(&ctx->us_data->fqin);
+ BUG_ON(ret);
+ ret = qman_oos_fq(&ctx->fq);
+ BUG_ON(ret);
+ fqid_rx = qman_fq_fqid(&ctx->us_data->fqin);
+ fqid_tx = qman_fq_fqid(&ctx->fq);
+ qman_destroy_fq(&ctx->us_data->fqin, 0);
+ qman_destroy_fq(&ctx->fq, 0);
+ qm_fq_free(fqid_rx);
+ qm_fq_free(fqid_tx);
+ kfree(ctx->us_data);
+ if (ctx->hw_flow)
+ pme_hw_flow_free(ctx->hw_flow);
+ if (ctx->hw_residue)
+ pme_hw_residue_free(ctx->hw_residue);
+}
+EXPORT_SYMBOL(pme_ctx_finish);
+
+int pme_ctx_is_disabled(struct pme_ctx *ctx)
+{
+ return (ctx->flags & PME_CTX_FLAG_DISABLED);
+}
+EXPORT_SYMBOL(pme_ctx_is_disabled);
+
+int pme_ctx_is_dead(struct pme_ctx *ctx)
+{
+ return (ctx->flags & PME_CTX_FLAG_DEAD);
+}
+EXPORT_SYMBOL(pme_ctx_is_dead);
+
+/* predeclare this here because pme_ctx_disable() may invoke it in "privileged
+ * mode". The code is down with the other ctrl commands, where it belongs. */
+static inline int __update_flow(struct pme_ctx *ctx, u32 flags,
+ struct pme_flow *params, struct pme_ctx_ctrl_token *token,
+ int is_disabling);
+
+/* This gets invoked by pme_ctx_disable() if it runs to completion, otherwise
+ * it's called from cb_helper. */
+static inline void __disable_done(struct pme_ctx *ctx)
+{
+ struct qm_mcc_initfq initfq;
+ int ret = 0;
+ if (!(ctx->flags & PME_CTX_FLAG_EXCLUSIVE)) {
+ /* Park fqin (exclusive is always parked) */
+ ret = park(&ctx->us_data->fqin, &initfq);
+ /* All the conditions for park() to succeed should be met. If
+ * this fails, there's a bug (s/w or h/w). */
+ if (ret)
+ pr_crit("pme2: park() should never fail! (%d)\n", ret);
+ }
+ do_flags(ctx, 0, 0, PME_CTX_FLAG_DISABLED, 0);
+}
+
+int pme_ctx_disable(struct pme_ctx *ctx, u32 flags,
+ struct pme_ctx_ctrl_token *token)
+{
+ int ret;
+
+ /* We must not (already) be DISABLING */
+ ret = do_flags(ctx, 0, PME_CTX_FLAG_DISABLING,
+ PME_CTX_FLAG_DISABLING, 0);
+ if (ret)
+ return ret;
+ /* Make sure the pipeline is empty */
+ atomic_dec(&ctx->refs);
+ ret = empty_pipeline(ctx, flags);
+ if (ret)
+ goto err;
+ /* We're idle, but is the flow context flushed from PME onboard cache?
+ * If it's not flushed when the system deallocates it, that 32 bytes
+ * could be in use later when PME decides to flush a write to it. Need
+ * to make it coherent again... */
+ if (!(ctx->flags & PME_CTX_FLAG_DIRECT)) {
+ /* Pass on wait flags (if any) but cancel any flow-context field
+ * writes (this is not the pme_ctx_ctrl_update_flow() API). */
+ ret = __update_flow(ctx, flags & ~PME_CMD_FCW_ALL, NULL,
+ token, 1);
+ if (ret)
+ goto err;
+ return 1;
+ }
+ __disable_done(ctx);
+ return 0;
+err:
+ atomic_inc(&ctx->refs);
+ do_flags(ctx, 0, 0, 0, PME_CTX_FLAG_DISABLING);
+ wake_up(&ctx->queue);
+ return ret;
+}
+EXPORT_SYMBOL(pme_ctx_disable);
+
+int pme_ctx_enable(struct pme_ctx *ctx)
+{
+ int ret;
+ ret = do_flags(ctx, PME_CTX_FLAG_DISABLED,
+ PME_CTX_FLAG_DEAD | PME_CTX_FLAG_RECONFIG |
+ PME_CTX_FLAG_ENABLING,
+ PME_CTX_FLAG_ENABLING, 0);
+ if (ret)
+ return ret;
+ if (!(ctx->flags & PME_CTX_FLAG_EXCLUSIVE)) {
+ ret = qman_init_fq(&ctx->us_data->fqin,
+ QMAN_INITFQ_FLAG_SCHED, NULL);
+ if (ret) {
+ do_flags(ctx, 0, 0, 0, PME_CTX_FLAG_ENABLING);
+ return ret;
+ }
+ }
+ atomic_inc(&ctx->refs);
+ do_flags(ctx, 0, 0, 0, PME_CTX_FLAG_DISABLED | PME_CTX_FLAG_DISABLING |
+ PME_CTX_FLAG_ENABLING);
+ return 0;
+}
+EXPORT_SYMBOL(pme_ctx_enable);
+
+int pme_ctx_reconfigure_tx(struct pme_ctx *ctx, u32 bpid, u8 qosin)
+{
+ struct qm_mcc_initfq initfq;
+ int ret;
+
+ ret = do_flags(ctx, PME_CTX_FLAG_DISABLED,
+ PME_CTX_FLAG_DEAD | PME_CTX_FLAG_RECONFIG,
+ PME_CTX_FLAG_RECONFIG, 0);
+ if (ret)
+ return ret;
+ memset(&initfq,0,sizeof(initfq));
+ pme_initfq(&initfq, ctx->hw_flow, qosin, bpid, qman_fq_fqid(&ctx->fq));
+ ret = qman_init_fq(&ctx->us_data->fqin, 0, &initfq);
+ do_flags(ctx, 0, 0, 0, PME_CTX_FLAG_RECONFIG);
+ return ret;
+}
+EXPORT_SYMBOL(pme_ctx_reconfigure_tx);
+
+int pme_ctx_reconfigure_rx(struct pme_ctx *ctx, u8 qosout,
+ enum qm_channel dest, const struct qm_fqd_stashing *stashing)
+{
+ return reconfigure_rx(ctx, 1, qosout, dest, stashing);
+}
+EXPORT_SYMBOL(pme_ctx_reconfigure_rx);
+
+/* Helpers for 'ctrl' and 'work' APIs. These are used when the 'ctx' in question
+ * is EXCLUSIVE. */
+static inline void release_exclusive(__maybe_unused struct pme_ctx *ctx)
+{
+ unsigned long irqflags;
+
+ BUG_ON(exclusive_ctx != ctx);
+ BUG_ON(!exclusive_refs);
+ spin_lock_irqsave(&exclusive_lock, irqflags);
+ if (!(--exclusive_refs)) {
+ exclusive_ctx = NULL;
+ pme2_exclusive_unset();
+ wake_up(&exclusive_queue);
+ }
+ spin_unlock_irqrestore(&exclusive_lock, irqflags);
+}
+static int __try_exclusive(struct pme_ctx *ctx)
+{
+ int ret = 0;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&exclusive_lock, irqflags);
+ if (exclusive_refs) {
+ /* exclusivity already held, continue if we're the owner */
+ if (exclusive_ctx != ctx)
+ ret = -EBUSY;
+ } else {
+ /* it's not currently held */
+ ret = pme2_exclusive_set(&ctx->us_data->fqin);
+ if (!ret)
+ exclusive_ctx = ctx;
+ }
+ if (!ret)
+ exclusive_refs++;
+ spin_unlock_irqrestore(&exclusive_lock, irqflags);
+ return ret;
+}
+/* Use this macro as the wait expression because we don't want to continue
+ * looping if the reason we're failing is that we don't have CCSR access
+ * (-ENODEV). */
+#define try_exclusive(ret, ctx) \
+ (!(ret = __try_exclusive(ctx)) || (ret == -ENODEV))
+static inline int get_exclusive(struct pme_ctx *ctx, __maybe_unused u32 flags)
+{
+ int ret;
+#ifdef CONFIG_FSL_DPA_CAN_WAIT
+ if (flags & PME_CTX_OP_WAIT) {
+ if (flags & PME_CTX_OP_WAIT_INT) {
+ ret = -EINTR;
+ wait_event_interruptible(exclusive_queue,
+ try_exclusive(ret, ctx));
+ } else
+ wait_event(exclusive_queue,
+ try_exclusive(ret, ctx));
+ } else
+#endif
+ ret = __try_exclusive(ctx);
+ return ret;
+}
+
+/* Used for 'work' APIs, convert PME->QMAN wait flags. The PME and
+ * QMAN "wait" flags have been aligned so that the below conversion should
+ * compile with good straight-line speed. */
+static inline u32 ctrl2eq(u32 flags)
+{
+#ifdef CONFIG_FSL_DPA_CAN_WAIT
+ return flags & (QMAN_ENQUEUE_FLAG_WAIT | QMAN_ENQUEUE_FLAG_WAIT_INT);
+#else
+ return flags;
+#endif
+}
+
+static inline void release_work(struct pme_ctx *ctx)
+{
+ if (atomic_dec_and_test(&ctx->refs))
+ wake_up(&ctx->queue);
+}
+
+#define BLOCK_NORMAL_WORK (PME_CTX_FLAG_DEAD | PME_CTX_FLAG_DISABLING)
+static int try_work(struct pme_ctx *ctx, u32 flags)
+{
+ atomic_inc(&ctx->refs);
+ if (unlikely(!(flags & PME_CTX_OP_INSIDE_DISABLE) &&
+ (ctx->flags & BLOCK_NORMAL_WORK))) {
+ release_work(ctx);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int get_work(struct pme_ctx *ctx, u32 flags)
+{
+ int ret = 0;
+#ifdef CONFIG_FSL_DPA_CAN_WAIT
+ if (flags & PME_CTX_OP_WAIT) {
+ if (flags & PME_CTX_OP_WAIT_INT) {
+ ret = -EINTR;
+ wait_event_interruptible(ctx->queue,
+ !(ret = try_work(ctx, flags)));
+ } else
+ wait_event(ctx->queue, !try_work(ctx, flags));
+ } else
+#endif
+ ret = try_work(ctx, flags);
+ return ret;
+}
+
+static inline int do_work(struct pme_ctx *ctx, u32 flags, struct qm_fd *fd,
+ struct pme_ctx_token *token, struct qman_fq *orp_fq, u16 seqnum)
+{
+ unsigned long irqflags;
+ int ret = get_work(ctx, flags);
+ if (ret)
+ return ret;
+ if (ctx->flags & PME_CTX_FLAG_EXCLUSIVE) {
+ ret = get_exclusive(ctx, flags);
+ if (ret) {
+ release_work(ctx);
+ return ret;
+ }
+ }
+ BUG_ON(sizeof(*fd) != sizeof(token->blob));
+ memcpy(&token->blob, fd, sizeof(*fd));
+
+ spin_lock_irqsave(&ctx->lock, irqflags);
+ list_add_tail(&token->node, &ctx->tokens);
+ spin_unlock_irqrestore(&ctx->lock, irqflags);
+
+ if (!orp_fq)
+ ret = qman_enqueue(&ctx->us_data->fqin, fd, ctrl2eq(flags));
+ else
+ ret = qman_enqueue_orp(&ctx->us_data->fqin, fd, ctrl2eq(flags),
+ orp_fq, seqnum);
+ if (ret) {
+ spin_lock_irqsave(&ctx->lock, irqflags);
+ list_del(&token->node);
+ spin_unlock_irqrestore(&ctx->lock, irqflags);
+ if (ctx->flags & PME_CTX_FLAG_EXCLUSIVE)
+ release_exclusive(ctx);
+ release_work(ctx);
+ }
+ return ret;
+}
+
+static inline int __update_flow(struct pme_ctx *ctx, u32 flags,
+ struct pme_flow *params, struct pme_ctx_ctrl_token *token,
+ int is_disabling)
+{
+ struct qm_fd fd;
+ int ret;
+ int hw_res_used = 0;
+ struct pme_hw_residue *hw_res = pme_hw_residue_new();
+ unsigned long irqflags;
+
+ BUG_ON(ctx->flags & PME_CTX_FLAG_DIRECT);
+ if (!hw_res)
+ return -ENOMEM;
+ token->internal_flow_ptr = pme_hw_flow_new();
+ if (!token->internal_flow_ptr) {
+ pme_hw_residue_free(hw_res);
+ return -ENOMEM;
+ }
+ token->base_token.cmd_type = pme_cmd_flow_write;
+
+ flags &= ~PME_CTX_OP_PRIVATE;
+ /* The callback will want to know this */
+ token->base_token.is_disable_flush = is_disabling ? 1 : 0;
+ flags |= (is_disabling ? PME_CTX_OP_INSIDE_DISABLE : 0);
+ spin_lock_irqsave(&ctx->lock, irqflags);
+ if (flags & PME_CTX_OP_RESETRESLEN) {
+ if (ctx->hw_residue) {
+ params->ren = 1;
+ flags |= PME_CMD_FCW_RES;
+ } else
+ flags &= ~PME_CMD_FCW_RES;
+ }
+ /* allocate residue memory if it is being added */
+ if ((flags & PME_CMD_FCW_RES) && params->ren && !ctx->hw_residue) {
+ ctx->hw_residue = hw_res;
+ hw_res_used = 1;
+ }
+ spin_unlock_irqrestore(&ctx->lock, irqflags);
+ if (!hw_res_used)
+ pme_hw_residue_free(hw_res);
+ /* enqueue the FCW command to PME */
+ memset(&fd, 0, sizeof(fd));
+ if (params)
+ memcpy(token->internal_flow_ptr, params,
+ sizeof(struct pme_flow));
+ pme_fd_cmd_fcw(&fd, flags & PME_CMD_FCW_ALL,
+ (struct pme_flow *)token->internal_flow_ptr,
+ ctx->hw_residue);
+ ret = do_work(ctx, flags, &fd, &token->base_token, NULL, 0);
+ return ret;
+}
+
+int pme_ctx_ctrl_update_flow(struct pme_ctx *ctx, u32 flags,
+ struct pme_flow *params, struct pme_ctx_ctrl_token *token)
+{
+ return __update_flow(ctx, flags, params, token, 0);
+}
+EXPORT_SYMBOL(pme_ctx_ctrl_update_flow);
+
+int pme_ctx_ctrl_read_flow(struct pme_ctx *ctx, u32 flags,
+ struct pme_flow *params, struct pme_ctx_ctrl_token *token)
+{
+ struct qm_fd fd;
+
+ BUG_ON(ctx->flags & (PME_CTX_FLAG_DIRECT | PME_CTX_FLAG_PMTCC));
+ token->base_token.cmd_type = pme_cmd_flow_read;
+ /* enqueue the FCR command to PME */
+ token->usr_flow_ptr = params;
+ token->internal_flow_ptr = pme_hw_flow_new();
+ if (!token->internal_flow_ptr)
+ return -ENOMEM;
+ memset(&fd, 0, sizeof(fd));
+ pme_fd_cmd_fcr(&fd, (struct pme_flow *)token->internal_flow_ptr);
+ return do_work(ctx, flags, &fd, &token->base_token, NULL, 0);
+}
+EXPORT_SYMBOL(pme_ctx_ctrl_read_flow);
+
+int pme_ctx_ctrl_nop(struct pme_ctx *ctx, u32 flags,
+ struct pme_ctx_ctrl_token *token)
+{
+ struct qm_fd fd;
+
+ token->base_token.cmd_type = pme_cmd_nop;
+ /* enqueue the NOP command to PME */
+ memset(&fd, 0, sizeof(fd));
+ qm_fd_addr_set64(&fd, (unsigned long)token);
+ pme_fd_cmd_nop(&fd);
+ return do_work(ctx, flags, &fd, &token->base_token, NULL, 0);
+}
+EXPORT_SYMBOL(pme_ctx_ctrl_nop);
+
+static inline void __prep_scan(__maybe_unused struct pme_ctx *ctx,
+ struct qm_fd *fd, u32 args, struct pme_ctx_token *token)
+{
+ BUG_ON(ctx->flags & PME_CTX_FLAG_PMTCC);
+ token->cmd_type = pme_cmd_scan;
+ pme_fd_cmd_scan(fd, args);
+}
+
+int pme_ctx_scan(struct pme_ctx *ctx, u32 flags, struct qm_fd *fd, u32 args,
+ struct pme_ctx_token *token)
+{
+ __prep_scan(ctx, fd, args, token);
+ return do_work(ctx, flags, fd, token, NULL, 0);
+}
+EXPORT_SYMBOL(pme_ctx_scan);
+
+int pme_ctx_scan_orp(struct pme_ctx *ctx, u32 flags, struct qm_fd *fd, u32 args,
+ struct pme_ctx_token *token, struct qman_fq *orp_fq, u16 seqnum)
+{
+ __prep_scan(ctx, fd, args, token);
+ return do_work(ctx, flags, fd, token, orp_fq, seqnum);
+}
+EXPORT_SYMBOL(pme_ctx_scan_orp);
+
+int pme_ctx_pmtcc(struct pme_ctx *ctx, u32 flags, struct qm_fd *fd,
+ struct pme_ctx_token *token)
+{
+ BUG_ON(!(ctx->flags & PME_CTX_FLAG_PMTCC));
+ token->cmd_type = pme_cmd_pmtcc;
+ pme_fd_cmd_pmtcc(fd);
+ return do_work(ctx, flags, fd, token, NULL, 0);
+}
+EXPORT_SYMBOL(pme_ctx_pmtcc);
+
+int pme_ctx_exclusive_inc(struct pme_ctx *ctx, u32 flags)
+{
+ return get_exclusive(ctx, flags);
+}
+EXPORT_SYMBOL(pme_ctx_exclusive_inc);
+
+void pme_ctx_exclusive_dec(struct pme_ctx *ctx)
+{
+ release_exclusive(ctx);
+}
+EXPORT_SYMBOL(pme_ctx_exclusive_dec);
+
+/* The 99.99% case is that enqueues happen in order or they get order-restored
+ * by the ORP, and so dequeues of responses happen in order too, so our FIFO
+ * linked-list of tokens is append-on-enqueue and pop-on-dequeue, and all's
+ * well.
+ *
+ * *EXCEPT*, if ever an enqueue gets rejected ... what then happens is that we
+ * have dequeues and ERNs to deal with, and the order we see them in is not
+ * necessarily the linked-list order. So we need to handle this in DQRR and MR
+ * callbacks, without sacrificing fast-path performance. Ouch.
+ *
+ * We use pop_matching_token() to take care of the mess (inlined, of course). */
+#define MATCH(fd1,fd2) \
+ ((qm_fd_addr_get64(fd1) == qm_fd_addr_get64(fd2)) && \
+ ((fd1)->opaque == (fd2)->opaque))
+static inline struct pme_ctx_token *pop_matching_token(struct pme_ctx *ctx,
+ const struct qm_fd *fd)
+{
+ struct pme_ctx_token *token;
+ const struct qm_fd *t_fd;
+ unsigned long irqflags;
+
+ /* The fast-path case is that the for() loop actually degenerates into;
+ * token = list_first_entry();
+ * if (likely(MATCH()))
+ * [done]
+ * The penalty of the slow-path case is the for() loop plus the fact
+ * we're optimising for a "likely" match first time, which might hurt
+ * when that assumption is wrong a few times in succession. */
+ spin_lock_irqsave(&ctx->lock, irqflags);
+ list_for_each_entry(token, &ctx->tokens, node) {
+ t_fd = (const struct qm_fd *)&token->blob[0];
+ if (likely(MATCH(t_fd, fd))) {
+ list_del(&token->node);
+ goto found;
+ }
+ }
+ token = NULL;
+ pr_err("PME2 Could not find matching token!\n");
+ BUG();
+found:
+ spin_unlock_irqrestore(&ctx->lock, irqflags);
+ return token;
+}
+
+static inline void cb_helper(__always_unused struct qman_portal *portal,
+ struct pme_ctx *ctx, const struct qm_fd *fd, int error)
+{
+ struct pme_ctx_token *token;
+ struct pme_ctx_ctrl_token *ctrl_token;
+
+ /* Resist the urge to use "unlikely" - 'error' is a constant param to an
+ * inline fn, so the compiler can collapse this completely. */
+ if (error)
+ do_flags(ctx, 0, 0, PME_CTX_FLAG_DEAD, 0);
+ token = pop_matching_token(ctx, fd);
+ if (likely(token->cmd_type == pme_cmd_scan))
+ ctx->cb(ctx, fd, token);
+ else if (token->cmd_type == pme_cmd_pmtcc)
+ ctx->cb(ctx, fd, token);
+ else {
+ /* outcast ctx and call supplied callback */
+ ctrl_token = container_of(token, struct pme_ctx_ctrl_token,
+ base_token);
+ if (token->cmd_type == pme_cmd_flow_write) {
+ /* Release the allocated flow context */
+ pme_hw_flow_free(ctrl_token->internal_flow_ptr);
+ /* Is this pme_ctx_disable() completion? */
+ if (token->is_disable_flush)
+ __disable_done(ctx);
+ } else if (token->cmd_type == pme_cmd_flow_read) {
+ /* Copy read result */
+ memcpy(ctrl_token->usr_flow_ptr,
+ ctrl_token->internal_flow_ptr,
+ sizeof(struct pme_flow));
+ /* Release the allocated flow context */
+ pme_hw_flow_free(ctrl_token->internal_flow_ptr);
+ }
+ ctrl_token->cb(ctx, fd, ctrl_token);
+ }
+ /* Consume the frame */
+ if (ctx->flags & PME_CTX_FLAG_EXCLUSIVE)
+ release_exclusive(ctx);
+ if (atomic_dec_and_test(&ctx->refs))
+ wake_up(&ctx->queue);
+}
+
+/* TODO: this scheme does not allow PME receivers to use held-active at all. Eg.
+ * there's no configuration of held-active for 'fq', and if there was, there's
+ * (a) nothing in the cb_dqrr() to support "park" or "defer" logic, and (b)
+ * nothing in cb_fqs() to support a delayed FQPN (DCAP_PK) notification. */
+static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *portal,
+ struct qman_fq *fq, const struct qm_dqrr_entry *dq)
+{
+ u8 status = (u8)pme_fd_res_status(&dq->fd);
+ u8 flags = pme_fd_res_flags(&dq->fd);
+ struct pme_ctx *ctx = (struct pme_ctx *)fq;
+
+ /* Put context into dead state is an unreliable or serious error is
+ * received
+ */
+ if (unlikely(flags & PME_STATUS_UNRELIABLE))
+ cb_helper(portal, ctx, &dq->fd, 1);
+ else if (unlikely((serious_error_vec[status])))
+ cb_helper(portal, ctx, &dq->fd, 1);
+ else
+ cb_helper(portal, ctx, &dq->fd, 0);
+
+ return qman_cb_dqrr_consume;
+}
+
+static void cb_ern(__always_unused struct qman_portal *portal,
+ struct qman_fq *fq, const struct qm_mr_entry *mr)
+{
+ struct pme_ctx *ctx;
+ struct pme_nostash *data;
+ struct pme_ctx_token *token;
+
+ data = container_of(fq, struct pme_nostash, fqin);
+ ctx = data->parent;
+
+ token = pop_matching_token(ctx, &mr->ern.fd);
+ if (likely(token->cmd_type == pme_cmd_scan)) {
+ BUG_ON(!ctx->ern_cb);
+ ctx->ern_cb(ctx, mr, token);
+ } else if (token->cmd_type == pme_cmd_pmtcc) {
+ BUG_ON(!ctx->ern_cb);
+ ctx->ern_cb(ctx, mr, token);
+ } else {
+ struct pme_ctx_ctrl_token *ctrl_token;
+ /* outcast ctx and call supplied callback */
+ ctrl_token = container_of(token, struct pme_ctx_ctrl_token,
+ base_token);
+ if (token->cmd_type == pme_cmd_flow_write) {
+ /* Release the allocated flow context */
+ pme_hw_flow_free(ctrl_token->internal_flow_ptr);
+ } else if (token->cmd_type == pme_cmd_flow_read) {
+ /* Copy read result */
+ memcpy(ctrl_token->usr_flow_ptr,
+ ctrl_token->internal_flow_ptr,
+ sizeof(struct pme_flow));
+ /* Release the allocated flow context */
+ pme_hw_flow_free(ctrl_token->internal_flow_ptr);
+ }
+ BUG_ON(!ctrl_token->ern_cb);
+ ctrl_token->ern_cb(ctx, mr, ctrl_token);
+ }
+ /* Consume the frame */
+ if (ctx->flags & PME_CTX_FLAG_EXCLUSIVE)
+ release_exclusive(ctx);
+ if (atomic_dec_and_test(&ctx->refs))
+ wake_up(&ctx->queue);
+}
+
+static void cb_dc_ern(struct qman_portal *portal, struct qman_fq *fq,
+ const struct qm_mr_entry *mr)
+{
+ struct pme_ctx *ctx = (struct pme_ctx *)fq;
+ /* This, umm, *shouldn't* happen. It's pretty bad. Things are expected
+ * to fall apart here, but we'll continue long enough to get out of
+ * interrupt context and let the user unwind whatever they can. */
+ pr_err("PME2 h/w enqueue rejection - expect catastrophe!\n");
+ cb_helper(portal, ctx, &mr->dcern.fd, 1);
+}
+
+static void cb_fqs(__always_unused struct qman_portal *portal,
+ __always_unused struct qman_fq *fq,
+ const struct qm_mr_entry *mr)
+{
+ u8 verb = mr->verb & QM_MR_VERB_TYPE_MASK;
+ if (verb == QM_MR_VERB_FQRNI)
+ return;
+ /* nothing else is supposed to occur */
+ BUG();
+}
+
new file mode 100644
@@ -0,0 +1,276 @@
+/* Copyright 2008-2011 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 "pme2_private.h"
+
+MODULE_AUTHOR("Geoff Thorpe");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL PME2 (p4080) device usage");
+
+#define PME_RESIDUE_SIZE 128
+#define PME_RESIDUE_ALIGN 64
+#define PME_FLOW_SIZE sizeof(struct pme_flow)
+#define PME_FLOW_ALIGN 32
+static struct kmem_cache *slab_residue;
+static struct kmem_cache *slab_flow;
+static struct kmem_cache *slab_fq;
+
+/* Hack to support "pme_map()". The point of this is that dma_map_single() now
+ * requires a non-NULL device, so the idea is that address mapping must be
+ * device-sensitive. Now the PAMU IO-MMU already takes care of this, as can be
+ * seen by the device-tree structure generated by the hypervisor (each portal
+ * node has sub-nodes for each h/w end-point it provides access to, and each
+ * sub-node has its own LIODN configuration). So we just need to map cpu
+ * pointers to (guest-)physical address and the PAMU takes care of the rest, so
+ * this doesn't need to be portal-sensitive nor device-sensitive. */
+static struct platform_device *pdev;
+
+static int pme2_low_init(void)
+{
+ int ret = -ENOMEM;
+
+ slab_residue = kmem_cache_create("pme2_residue", PME_RESIDUE_SIZE,
+ PME_RESIDUE_ALIGN, SLAB_HWCACHE_ALIGN, NULL);
+ if (!slab_residue)
+ goto end;
+ slab_flow = kmem_cache_create("pme2_flow", PME_FLOW_SIZE,
+ PME_FLOW_ALIGN, 0, NULL);
+ if (!slab_flow)
+ goto end;
+ slab_fq = kmem_cache_create("pme2_fqslab", sizeof(struct qman_fq),
+ __alignof__(struct qman_fq), SLAB_HWCACHE_ALIGN, NULL);
+ if (!slab_fq)
+ goto end;
+ ret = -ENODEV;
+ pdev = platform_device_alloc("pme", -1);
+ if (!pdev)
+ goto end;
+ if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)))
+ goto end;
+ if (platform_device_add(pdev))
+ goto end;
+ return 0;
+end:
+ if (pdev) {
+ platform_device_put(pdev);
+ pdev = NULL;
+ }
+ if (slab_flow) {
+ kmem_cache_destroy(slab_flow);
+ slab_flow = NULL;
+ }
+ if (slab_residue) {
+ kmem_cache_destroy(slab_residue);
+ slab_residue = NULL;
+ }
+ if (slab_fq) {
+ kmem_cache_destroy(slab_fq);
+ slab_fq = NULL;
+ }
+ return ret;
+}
+
+static void pme2_low_exit(void)
+{
+ platform_device_del(pdev);
+ platform_device_put(pdev);
+ pdev = NULL;
+ kmem_cache_destroy(slab_fq);
+ kmem_cache_destroy(slab_flow);
+ kmem_cache_destroy(slab_residue);
+ slab_fq = slab_flow = slab_residue = NULL;
+}
+
+module_init(pme2_low_init);
+module_exit(pme2_low_exit);
+
+struct qman_fq *slabfq_alloc(void)
+{
+ return kmem_cache_alloc(slab_fq, GFP_KERNEL);
+}
+
+void slabfq_free(struct qman_fq *fq)
+{
+ kmem_cache_free(slab_fq, fq);
+}
+
+/***********************/
+/* low-level functions */
+/***********************/
+
+struct pme_hw_residue *pme_hw_residue_new(void)
+{
+ return kmem_cache_alloc(slab_residue, GFP_KERNEL);
+}
+EXPORT_SYMBOL(pme_hw_residue_new);
+
+void pme_hw_residue_free(struct pme_hw_residue *p)
+{
+ kmem_cache_free(slab_residue, p);
+}
+EXPORT_SYMBOL(pme_hw_residue_free);
+
+struct pme_hw_flow *pme_hw_flow_new(void)
+{
+ struct pme_flow *flow = kmem_cache_zalloc(slab_flow, GFP_KERNEL);
+ return (struct pme_hw_flow *)flow;
+}
+EXPORT_SYMBOL(pme_hw_flow_new);
+
+void pme_hw_flow_free(struct pme_hw_flow *p)
+{
+ kmem_cache_free(slab_flow, p);
+}
+EXPORT_SYMBOL(pme_hw_flow_free);
+
+static const struct pme_flow default_sw_flow = {
+ .sos = 1,
+ .srvm = 0,
+ .esee = 1,
+ .ren = 0,
+ .rlen = 0,
+ .seqnum_hi = 0,
+ .seqnum_lo = 0,
+ .sessionid = 0x7ffffff,
+ .rptr_hi = 0,
+ .rptr_lo = 0,
+ .clim = 0xffff,
+ .mlim = 0xffff
+};
+
+void pme_sw_flow_init(struct pme_flow *flow)
+{
+ memcpy(flow, &default_sw_flow, sizeof(*flow));
+}
+EXPORT_SYMBOL(pme_sw_flow_init);
+
+void pme_initfq(struct qm_mcc_initfq *initfq, struct pme_hw_flow *flow, u8 qos,
+ u8 rbpid, u32 rfqid)
+{
+ struct pme_context_a *pme_a =
+ (struct pme_context_a *)&initfq->fqd.context_a;
+ struct pme_context_b *pme_b =
+ (struct pme_context_b *)&initfq->fqd.context_b;
+
+ initfq->we_mask = QM_INITFQ_WE_DESTWQ | QM_INITFQ_WE_CONTEXTA |
+ QM_INITFQ_WE_CONTEXTB;
+ initfq->fqd.dest.channel = qm_channel_pme;
+ initfq->fqd.dest.wq = qos;
+ if (flow) {
+ dma_addr_t fcp = flow_map((struct pme_flow *)flow);
+ pme_a->mode = pme_mode_flow;
+ pme_context_a_set64(pme_a, fcp);
+ } else {
+ pme_a->mode = pme_mode_direct;
+ pme_context_a_set64(pme_a, 0);
+ }
+ pme_b->rbpid = rbpid;
+ pme_b->rfqid = rfqid;
+}
+EXPORT_SYMBOL(pme_initfq);
+
+void pme_fd_cmd_nop(struct qm_fd *fd)
+{
+ struct pme_cmd_nop *nop = (struct pme_cmd_nop *)&fd->cmd;
+ nop->cmd = pme_cmd_nop;
+}
+EXPORT_SYMBOL(pme_fd_cmd_nop);
+
+void pme_fd_cmd_fcw(struct qm_fd *fd, u8 flags, struct pme_flow *flow,
+ struct pme_hw_residue *residue)
+{
+ dma_addr_t f;
+ struct pme_cmd_flow_write *fcw = (struct pme_cmd_flow_write *)&fd->cmd;
+
+ BUG_ON(!flow);
+ BUG_ON((unsigned long)flow & 31);
+ fcw->cmd = pme_cmd_flow_write;
+ fcw->flags = flags;
+ if (flags & PME_CMD_FCW_RES) {
+ if (residue) {
+ dma_addr_t rptr = residue_map(residue);
+ BUG_ON(!residue);
+ BUG_ON((unsigned long)residue & 63);
+ pme_flow_rptr_set64(flow, rptr);
+ } else
+ pme_flow_rptr_set64(flow, 0);
+ }
+ f = flow_map(flow);
+ qm_fd_addr_set64(fd, f);
+ fd->format = qm_fd_contig;
+ fd->offset = 0;
+ fd->length20 = sizeof(*flow);
+}
+EXPORT_SYMBOL(pme_fd_cmd_fcw);
+
+void pme_fd_cmd_fcr(struct qm_fd *fd, struct pme_flow *flow)
+{
+ dma_addr_t f;
+ struct pme_cmd_flow_read *fcr = (struct pme_cmd_flow_read *)&fd->cmd;
+
+ BUG_ON(!flow);
+ BUG_ON((unsigned long)flow & 31);
+ fcr->cmd = pme_cmd_flow_read;
+ f = flow_map(flow);
+ qm_fd_addr_set64(fd, f);
+ fd->format = qm_fd_contig;
+ fd->offset = 0;
+ fd->length20 = sizeof(*flow);
+}
+EXPORT_SYMBOL(pme_fd_cmd_fcr);
+
+void pme_fd_cmd_pmtcc(struct qm_fd *fd)
+{
+ struct pme_cmd_pmtcc *pmtcc = (struct pme_cmd_pmtcc *)&fd->cmd;
+ pmtcc->cmd = pme_cmd_pmtcc;
+}
+EXPORT_SYMBOL(pme_fd_cmd_pmtcc);
+
+void pme_fd_cmd_scan(struct qm_fd *fd, u32 args)
+{
+ struct pme_cmd_scan *scan = (struct pme_cmd_scan *)&fd->cmd;
+ fd->cmd = args;
+ scan->cmd = pme_cmd_scan;
+}
+EXPORT_SYMBOL(pme_fd_cmd_scan);
+
+dma_addr_t pme_map(void *ptr)
+{
+ return dma_map_single(&pdev->dev, ptr, 1, DMA_BIDIRECTIONAL);
+}
+EXPORT_SYMBOL(pme_map);
+
+int pme_map_error(dma_addr_t dma_addr)
+{
+ return dma_mapping_error(&pdev->dev, dma_addr);
+}
+EXPORT_SYMBOL(pme_map_error);
+
new file mode 100644
@@ -0,0 +1,180 @@
+/* Copyright 2008-2011 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 "pme2_sys.h"
+#include <linux/fsl_pme.h>
+
+#undef PME2_DEBUG
+
+#ifdef PME2_DEBUG
+#define PMEPRINFO(fmt, args...) pr_info("PME2: %s: " fmt, __func__, ## args)
+#else
+#define PMEPRINFO(fmt, args...)
+#endif
+
+#define PMEPRERR(fmt, args...) pr_err("PME2: %s: " fmt, __func__, ## args)
+#define PMEPRCRIT(fmt, args...) pr_crit("PME2: %s: " fmt, __func__, ## args)
+
+#ifdef CONFIG_FSL_PME2_CTRL
+/* Hooks */
+int pme2_create_sysfs_dev_files(struct platform_device *ofdev);
+void pme2_remove_sysfs_dev_files(struct platform_device *ofdev);
+void accumulator_update_interval(u32 interval);
+#endif
+
+static inline void set_fd_addr(struct qm_fd *fd, dma_addr_t addr)
+{
+ qm_fd_addr_set64(fd, addr);
+}
+static inline dma_addr_t get_fd_addr(const struct qm_fd *fd)
+{
+ return (dma_addr_t)qm_fd_addr_get64(fd);
+}
+static inline void set_sg_addr(struct qm_sg_entry *sg, dma_addr_t addr)
+{
+ qm_sg_entry_set64(sg, addr);
+}
+static inline dma_addr_t get_sg_addr(const struct qm_sg_entry *sg)
+{
+ return (dma_addr_t)qm_sg_entry_get64(sg);
+}
+
+/******************/
+/* Datapath types */
+/******************/
+
+enum pme_mode {
+ pme_mode_direct = 0x00,
+ pme_mode_flow = 0x80
+};
+
+struct pme_context_a {
+ enum pme_mode mode:8;
+ u8 __reserved;
+ /* Flow Context pointer (48-bit), ignored if mode==direct */
+ u16 flow_hi;
+ u32 flow_lo;
+} __packed;
+static inline u64 pme_context_a_get64(const struct pme_context_a *p)
+{
+ return ((u64)p->flow_hi << 32) | (u64)p->flow_lo;
+}
+/* Macro, so we compile better if 'v' isn't always 64-bit */
+#define pme_context_a_set64(p, v) \
+ do { \
+ struct pme_context_a *__p931 = (p); \
+ __p931->flow_hi = upper_32_bits(v); \
+ __p931->flow_lo = lower_32_bits(v); \
+ } while (0)
+
+struct pme_context_b {
+ u32 rbpid:8;
+ u32 rfqid:24;
+} __packed;
+
+
+/* This is the 32-bit frame "cmd/status" field, sent to PME */
+union pme_cmd {
+ struct pme_cmd_nop {
+ enum pme_cmd_type cmd:3;
+ } nop;
+ struct pme_cmd_flow_read {
+ enum pme_cmd_type cmd:3;
+ } fcr;
+ struct pme_cmd_flow_write {
+ enum pme_cmd_type cmd:3;
+ u8 __reserved:5;
+ u8 flags; /* See PME_CMD_FCW_*** */
+ } __packed fcw;
+ struct pme_cmd_pmtcc {
+ enum pme_cmd_type cmd:3;
+ } pmtcc;
+ struct pme_cmd_scan {
+ union {
+ struct {
+ enum pme_cmd_type cmd:3;
+ u8 flags:5; /* See PME_CMD_SCAN_*** */
+ } __packed;
+ };
+ u8 set;
+ u16 subset;
+ } __packed scan;
+};
+
+/* The exported macro forms a "scan_args" u32 from 3 inputs, these private
+ * inlines do the inverse, if you need to crack one apart. */
+static inline u8 scan_args_get_flags(u32 args)
+{
+ return args >> 24;
+}
+static inline u8 scan_args_get_set(u32 args)
+{
+ return (args >> 16) & 0xff;
+}
+static inline u16 scan_args_get_subset(u32 args)
+{
+ return args & 0xffff;
+}
+
+/* Hook from pme2_high to pme2_low */
+struct qman_fq *slabfq_alloc(void);
+void slabfq_free(struct qman_fq *fq);
+
+/* Hook from pme2_high to pme2_ctrl */
+int pme2_have_control(void);
+int pme2_exclusive_set(struct qman_fq *fq);
+int pme2_exclusive_unset(void);
+
+#define DECLARE_GLOBAL(name, t, mt, def, desc) \
+ static t name = def; \
+ module_param(name, mt, 0644); \
+ MODULE_PARM_DESC(name, desc ", default: " __stringify(def));
+
+/* Constants used by the SRE ioctl. */
+#define PME_PMFA_SRE_POLL_MS 100
+#define PME_PMFA_SRE_INDEX_MAX (1 << 27)
+#define PME_PMFA_SRE_INC_MAX (1 << 12)
+#define PME_PMFA_SRE_REP_MAX (1 << 28)
+#define PME_PMFA_SRE_INTERVAL_MAX (1 << 12)
+
+/* Encapsulations for mapping */
+#define flow_map(flow) \
+({ \
+ struct pme_flow *__f913 = (flow); \
+ pme_map(__f913); \
+})
+
+#define residue_map(residue) \
+({ \
+ struct pme_hw_residue *__f913 = (residue); \
+ pme_map(__f913); \
+})
+
new file mode 100644
@@ -0,0 +1,173 @@
+/* Copyright 2009-2011 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.
+ */
+
+#ifndef PME2_REGS_H
+#define PME2_REGS_H
+
+#define PME_REG_ISR 0x000
+#define PME_REG_IER 0x004
+#define PME_REG_ISDR 0x008
+#define PME_REG_IIR 0x00C
+#define PME_REG_RLL 0x014
+#define PME_REG_CDCR 0x018
+#define PME_REG_TRUNCI 0x024
+#define PME_REG_RBC 0x028
+#define PME_REG_ESR 0x02C
+#define PME_REG_ECR0 0x030
+#define PME_REG_ECR1 0x034
+#define PME_REG_EFQC 0x050
+#define PME_REG_FACONF 0x060
+#define PME_REG_PMSTAT 0x064
+#define PME_REG_FAMCR 0x068
+#define PME_REG_PMTR 0x06C
+#define PME_REG_PEHD 0x074
+#define PME_REG_BSC0 0x080
+#define PME_REG_BSC1 0x084
+#define PME_REG_BSC2 0x088
+#define PME_REG_BSC3 0x08C
+#define PME_REG_BSC4 0x090
+#define PME_REG_BSC5 0x094
+#define PME_REG_BSC6 0x098
+#define PME_REG_BSC7 0x09C
+#define PME_REG_QMBFD0 0x0E0
+#define PME_REG_QMBFD1 0x0E4
+#define PME_REG_QMBFD2 0x0E8
+#define PME_REG_QMBFD3 0x0EC
+#define PME_REG_QMBCTXTAH 0x0F0
+#define PME_REG_QMBCTXTAL 0x0F4
+#define PME_REG_QMBCTXTB 0x0F8
+#define PME_REG_QMBCTL 0x0FC
+#define PME_REG_ECC1BES 0x100
+#define PME_REG_ECC2BES 0x104
+#define PME_REG_ECCADDR 0x110
+#define PME_REG_ECCCODE 0x118
+#define PME_REG_TBT0ECC1TH 0x180
+#define PME_REG_TBT0ECC1EC 0x184
+#define PME_REG_TBT1ECC1TH 0x188
+#define PME_REG_TBT1ECC1EC 0x18C
+#define PME_REG_VLT0ECC1TH 0x190
+#define PME_REG_VLT0ECC1EC 0x194
+#define PME_REG_VLT1ECC1TH 0x198
+#define PME_REG_VLT1ECC1EC 0x19C
+#define PME_REG_CMECC1TH 0x1A0
+#define PME_REG_CMECC1EC 0x1A4
+#define PME_REG_DXCMECC1TH 0x1B0
+#define PME_REG_DXCMECC1EC 0x1B4
+#define PME_REG_DXEMECC1TH 0x1C0
+#define PME_REG_DXEMECC1EC 0x1C4
+#define PME_REG_STNIB 0x200
+#define PME_REG_STNIS 0x204
+#define PME_REG_STNTH1 0x208
+#define PME_REG_STNTH2 0x20C
+#define PME_REG_STNTHV 0x210
+#define PME_REG_STNTHS 0x214
+#define PME_REG_STNCH 0x218
+#define PME_REG_SWDB 0x21C
+#define PME_REG_KVLTS 0x220
+#define PME_REG_KEC 0x224
+#define PME_REG_STNPM 0x280
+#define PME_REG_STNS1M 0x284
+#define PME_REG_DRCIC 0x288
+#define PME_REG_DRCMC 0x28C
+#define PME_REG_STNPMR 0x290
+#define PME_REG_PDSRBAH 0x2A0
+#define PME_REG_PDSRBAL 0x2A4
+#define PME_REG_DMCR 0x2A8
+#define PME_REG_DEC0 0x2AC
+#define PME_REG_DEC1 0x2B0
+#define PME_REG_DLC 0x2C0
+#define PME_REG_STNDSR 0x300
+#define PME_REG_STNESR 0x304
+#define PME_REG_STNS1R 0x308
+#define PME_REG_STNOB 0x30C
+#define PME_REG_SCBARH 0x310
+#define PME_REG_SCBARL 0x314
+#define PME_REG_SMCR 0x318
+#define PME_REG_SREC 0x320
+#define PME_REG_ESRP 0x328
+#define PME_REG_SRRV0 0x338
+#define PME_REG_SRRV1 0x33C
+#define PME_REG_SRRV2 0x340
+#define PME_REG_SRRV3 0x344
+#define PME_REG_SRRV4 0x348
+#define PME_REG_SRRV5 0x34C
+#define PME_REG_SRRV6 0x350
+#define PME_REG_SRRV7 0x354
+#define PME_REG_SRRFI 0x358
+#define PME_REG_SRRI 0x360
+#define PME_REG_SRRR 0x364
+#define PME_REG_SRRWC 0x368
+#define PME_REG_SFRCC 0x36C
+#define PME_REG_SEC1 0x370
+#define PME_REG_SEC2 0x374
+#define PME_REG_SEC3 0x378
+#define PME_REG_MIA_BYC 0x380
+#define PME_REG_MIA_BLC 0x384
+#define PME_REG_MIA_CE 0x388
+#define PME_REG_MIA_CR 0x390
+#define PME_REG_PPIDMR0 0x800
+#define PME_REG_PPIDMR1 0x804
+#define PME_REG_PPIDMR2 0x808
+#define PME_REG_PPIDMR3 0x80C
+#define PME_REG_PPIDMR4 0x810
+#define PME_REG_PPIDMR5 0x814
+#define PME_REG_PPIDMR6 0x818
+#define PME_REG_PPIDMR7 0x81C
+#define PME_REG_PPIDMR8 0x820
+#define PME_REG_PPIDMR9 0x824
+#define PME_REG_PPIDMR10 0x828
+#define PME_REG_PPIDMR11 0x82C
+#define PME_REG_PPIDMR12 0x830
+#define PME_REG_PPIDMR13 0x834
+#define PME_REG_PPIDMR14 0x838
+#define PME_REG_PPIDMR15 0x83C
+#define PME_REG_PPIDMR16 0x840
+#define PME_REG_PPIDMR17 0x844
+#define PME_REG_PPIDMR18 0x848
+#define PME_REG_PPIDMR19 0x84C
+#define PME_REG_PPIDMR20 0x850
+#define PME_REG_PPIDMR21 0x854
+#define PME_REG_PPIDMR22 0x858
+#define PME_REG_PPIDMR23 0x85C
+#define PME_REG_PPIDMR24 0x860
+#define PME_REG_PPIDMR25 0x864
+#define PME_REG_PPIDMR26 0x868
+#define PME_REG_PPIDMR27 0x86C
+#define PME_REG_PPIDMR28 0x870
+#define PME_REG_PPIDMR29 0x874
+#define PME_REG_PPIDMR30 0x878
+#define PME_REG_PPIDMR31 0x87C
+#define PME_REG_SRCIDR 0xA00
+#define PME_REG_LIODNR 0xA0C
+#define PME_REG_PM_IP_REV1 0xBF8
+#define PME_REG_PM_IP_REV2 0xBFC
+
+#endif /* REGS_H */
new file mode 100644
@@ -0,0 +1,426 @@
+/* Copyright 2009-2011 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 "pme2_test.h"
+
+static u8 pme_db[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* Rev 2.1 */
+ 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x90, 0x41, 0x40, 0x20, 0x00, 0x11,
+/* Rev 2.0 */
+/* 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01,
+ 0x20, 0x41, 0x40, 0x20, 0x00, 0x11, */
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* Rev 2.1 */
+ 0x00, 0x0d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x90, 0x41, 0xff, 0x81, 0x00, 0x00,
+/* Rev 2.0 */
+/* 0x00, 0x0d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01,
+ 0x20, 0x41, 0xff, 0x81, 0x00, 0x00, */
+ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x01,
+ 0x01, 0xff, 0x80, 0x00, 0x41, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
+ 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x41, 0x42, 0x43,
+ 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b,
+ 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b,
+ 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+ 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab,
+ 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb,
+ 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
+ 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3,
+ 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
+ 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb,
+ 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,
+ 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
+ 0xfc, 0xfd, 0xfe, 0xff
+};
+
+static u8 db_read[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* Rev 2.1 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x90, 0x41
+/* Rev 2.0 */
+/* 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01,
+ 0x20, 0x41 */
+};
+
+static u8 db_read_expected_result[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
+ 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* Rev 2.1 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x90, 0x41, 0x40, 0x20, 0x00, 0x11
+/* Rev 2.0 */
+/* 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01,
+ 0x20, 0x41, 0x40, 0x20, 0x00, 0x11*/
+};
+
+struct pmtcc_ctx {
+ struct pme_ctx base_ctx;
+ struct qm_fd result_fd;
+ struct completion done;
+ u8 ern;
+};
+
+static void pmtcc_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_token *ctx_token)
+{
+ struct pmtcc_ctx *my_ctx = (struct pmtcc_ctx *)ctx;
+ memcpy(&my_ctx->result_fd, fd, sizeof(*fd));
+ complete(&my_ctx->done);
+}
+
+static void pmtcc_ern_cb(struct pme_ctx *ctx, const struct qm_mr_entry *mr,
+ struct pme_ctx_token *ctx_token)
+{
+ struct pmtcc_ctx *my_ctx = (struct pmtcc_ctx *)ctx;
+ my_ctx->result_fd = mr->ern.fd;
+ my_ctx->ern = 1;
+ complete(&my_ctx->done);
+}
+
+#define FIRST_PMTCC 56
+int pme2_clear_sample_db(void)
+{
+ struct pmtcc_ctx ctx = {
+ .base_ctx.cb = pmtcc_cb,
+ .base_ctx.ern_cb = pmtcc_ern_cb,
+ .ern = 0
+ };
+ struct qm_fd fd;
+ int ret = 0;
+ enum pme_status status;
+ struct pme_ctx_token token;
+ void *mem;
+ struct cpumask backup_mask = current->cpus_allowed;
+ struct cpumask new_mask = *qman_affine_cpus();
+
+ cpumask_and(&new_mask, &new_mask, bman_affine_cpus());
+ ret = set_cpus_allowed_ptr(current, &new_mask);
+ if (ret) {
+ pr_info("cleanr_sample_db: can't set cpumask\n");
+ goto _clear_0;
+ }
+ init_completion(&ctx.done);
+ ret = pme_ctx_init(&ctx.base_ctx,
+ PME_CTX_FLAG_EXCLUSIVE |
+ PME_CTX_FLAG_PMTCC |
+ PME_CTX_FLAG_LOCAL, 0, 4, 4, 0, NULL);
+ if (ret) {
+ pr_err("clear_sample_db: can't init ctx\n");
+ goto _clear_1;
+ }
+
+ /* enable the context */
+ ret = pme_ctx_enable(&ctx.base_ctx);
+ if (ret) {
+ pr_err("clear_sample_db: can't enable ctx\n");
+ goto _clear_2;
+ }
+
+ /* Write the database */
+ memset(&fd, 0, sizeof(struct qm_fd));
+ mem = kmalloc(FIRST_PMTCC, GFP_KERNEL);
+ if (!mem)
+ goto _clear_3;
+ memcpy(mem, pme_db, FIRST_PMTCC);
+
+ fd.length20 = FIRST_PMTCC;
+ qm_fd_addr_set64(&fd, pme_map(mem));
+
+ ret = pme_ctx_pmtcc(&ctx.base_ctx, PME_CTX_OP_WAIT, &fd, &token);
+ if (ret == -ENODEV) {
+ pr_err("clear_sample_db: not the control plane, bailing\n");
+ goto _clear_4;
+ }
+ if (ret) {
+ pr_err("clear_sample_db: error with pmtcc\n");
+ goto _clear_4;
+ }
+ wait_for_completion(&ctx.done);
+ if (ctx.ern) {
+ pr_err("clear_sample_db: Rx ERN from pmtcc\n");
+ goto _clear_4;
+ }
+ status = pme_fd_res_status(&ctx.result_fd);
+ if (status) {
+ pr_info("clear_sample_db: PMTCC write status failed %d\n",
+ status);
+ goto _clear_4;
+ }
+_clear_4:
+ kfree(mem);
+_clear_3:
+ /* Disable */
+ ret = pme_ctx_disable(&ctx.base_ctx,
+ PME_CTX_OP_WAIT | PME_CTX_OP_WAIT_INT, NULL);
+_clear_2:
+ pme_ctx_finish(&ctx.base_ctx);
+_clear_1:
+ ret = set_cpus_allowed_ptr(current, &backup_mask);
+ if (ret)
+ pr_info("clear_sample_db: can't restore cpumask");
+_clear_0:
+ if (!ret)
+ pr_info("clear_sample_db: Done\n");
+ else
+ pr_info("clear_sample_db: Error 0x%x\n", ret);
+ return ret;
+
+}
+
+int pme2_sample_db(void)
+{
+ struct pmtcc_ctx ctx = {
+ .base_ctx.cb = pmtcc_cb,
+ .base_ctx.ern_cb = pmtcc_ern_cb,
+ .ern = 0
+ };
+ struct qm_fd fd;
+ struct qm_sg_entry *sg_table = NULL;
+ int ret = 0;
+ enum pme_status status;
+ struct pme_ctx_token token;
+ void *mem = NULL, *mem_result = NULL;
+ u32 pme_rev;
+ struct cpumask backup_mask = current->cpus_allowed;
+ struct cpumask new_mask = *qman_affine_cpus();
+
+ cpumask_and(&new_mask, &new_mask, bman_affine_cpus());
+ ret = set_cpus_allowed_ptr(current, &new_mask);
+ if (ret) {
+ pr_info("sample_db: can't set cpumask\n");
+ goto _finish_0;
+ }
+ ret = pme_attr_get(pme_attr_rev1, &pme_rev);
+ if (ret) {
+ pr_err("sample_db: can't read pme revision %d\n", ret);
+ goto _finish_1;
+ }
+ /* If Rev 2.0...update database */
+ if ((pme_rev & 0x0000FFFF) == 0x00000200) {
+ pr_info("sample_db: db for pme ver 2.0\n");
+ pme_db[133] = 0x01;
+ pme_db[134] = 0x20;
+ pme_db[161] = 0x01;
+ pme_db[162] = 0x20;
+ db_read[21] = 0x01;
+ db_read[22] = 0x20;
+ db_read_expected_result[21] = 0x01;
+ db_read_expected_result[22] = 0x20;
+ } else
+ pr_info("sample_db: db for pme ver 2.1 or greater\n");
+ init_completion(&ctx.done);
+ ret = pme_ctx_init(&ctx.base_ctx,
+ PME_CTX_FLAG_EXCLUSIVE |
+ PME_CTX_FLAG_PMTCC |
+ PME_CTX_FLAG_LOCAL, 0, 4, 4, 0, NULL);
+ if (ret) {
+ pr_err("sample_db: can't init ctx\n");
+ goto _finish_1;
+ }
+
+ /* enable the context */
+ ret = pme_ctx_enable(&ctx.base_ctx);
+ if (ret) {
+ pr_err("sample_db: can't enable ctx\n");
+ goto _finish_2;
+ }
+
+ /* Write the database */
+ memset(&fd, 0, sizeof(struct qm_fd));
+ mem = kmalloc(sizeof(pme_db), GFP_KERNEL);
+ if (!mem)
+ goto _finish_3;
+ memcpy(mem, pme_db, sizeof(pme_db));
+
+ fd.length20 = sizeof(pme_db);
+ qm_fd_addr_set64(&fd, pme_map(mem));
+
+ ret = pme_ctx_pmtcc(&ctx.base_ctx, PME_CTX_OP_WAIT, &fd, &token);
+ if (ret == -ENODEV) {
+ pr_err("sample_db: not the control plane, bailing\n");
+ goto _finish_4;
+ }
+ if (ret) {
+ pr_err("sample_db: error with pmtcc\n");
+ goto _finish_4;
+ }
+ wait_for_completion(&ctx.done);
+ if (ctx.ern) {
+ pr_err("sample_db: Rx ERN from pmtcc\n");
+ goto _finish_4;
+ }
+ status = pme_fd_res_status(&ctx.result_fd);
+ if (status) {
+ pr_info("sample_db: PMTCC write status failed %d\n", status);
+ goto _finish_4;
+ }
+ kfree(mem);
+ mem = NULL;
+ /* Read back the database */
+ init_completion(&ctx.done);
+ memset(&fd, 0, sizeof(struct qm_fd));
+ sg_table = kzalloc(2 * sizeof(*sg_table), GFP_KERNEL | GFP_DMA);
+ mem_result = kmalloc(28, GFP_KERNEL);
+ mem = kmalloc(sizeof(db_read), GFP_KERNEL);
+ if (!sg_table || !mem || !mem_result) {
+ pr_err("sample_db: out of memory\n");
+ ret = -ENOMEM;
+ goto _finish_4;
+ }
+ memcpy(mem, db_read, sizeof(db_read));
+ qm_sg_entry_set64(&sg_table[0], pme_map(mem_result));
+ sg_table[0].length = 28;
+ qm_sg_entry_set64(&sg_table[1], pme_map(mem));
+ sg_table[1].length = sizeof(db_read);
+ sg_table[1].final = 1;
+ fd.format = qm_fd_compound;
+ qm_fd_addr_set64(&fd, pme_map(sg_table));
+ ret = pme_ctx_pmtcc(&ctx.base_ctx, PME_CTX_OP_WAIT, &fd, &token);
+ if (ret) {
+ pr_err("sample_db: error with pmtcc\n");
+ goto _finish_4;
+ }
+ wait_for_completion(&ctx.done);
+ if (ctx.ern) {
+ ret = -EINVAL;
+ pr_err("sample_db: Rx ERN from pmtcc\n");
+ goto _finish_4;
+ }
+ status = pme_fd_res_status(&ctx.result_fd);
+ if (status) {
+ ret = -EINVAL;
+ pr_err("sample_db: PMTCC read status failed %d\n", status);
+ goto _finish_4;
+ }
+ if (pme_fd_res_flags(&ctx.result_fd) & PME_STATUS_UNRELIABLE) {
+ pr_err("sample_db: flags result set %x\n",
+ pme_fd_res_flags(&ctx.result_fd));
+ ret = -EINVAL;
+ goto _finish_4;
+ }
+ if (memcmp(db_read_expected_result, mem_result, 28) != 0) {
+ pr_err("sample_db: DB read result not expected\n");
+ pr_err("Expected\n");
+ hexdump(db_read_expected_result,
+ sizeof(db_read_expected_result));
+ pr_info("Received\n");
+ hexdump(mem_result, 28);
+ ret = -EINVAL;
+ }
+_finish_4:
+ kfree(mem_result);
+ kfree(sg_table);
+ kfree(mem);
+_finish_3:
+ /* Disable */
+ ret = pme_ctx_disable(&ctx.base_ctx,
+ PME_CTX_OP_WAIT | PME_CTX_OP_WAIT_INT, NULL);
+_finish_2:
+ pme_ctx_finish(&ctx.base_ctx);
+_finish_1:
+ if (ret)
+ set_cpus_allowed_ptr(current, &backup_mask);
+ else {
+ ret = set_cpus_allowed_ptr(current, &backup_mask);
+ if (ret)
+ pr_info("sample_db: can't restore cpumask");
+ }
+
+_finish_0:
+ if (!ret)
+ pr_info("pme: sample DB initialised\n");
+ else
+ pr_info("pme: Error during sample DB 0x%x\n", ret);
+ return ret;
+}
+
new file mode 100644
@@ -0,0 +1,1111 @@
+/* Copyright 2009-2011 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 "pme2_private.h"
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+#include <linux/compat.h>
+
+#define WAIT_AND_INTERRUPTABLE (PME_CTX_OP_WAIT|PME_CTX_OP_WAIT_INT)
+#define INPUT_FRM 1
+#define OUTPUT_FRM 0
+/* Private structure that is allocated for each open that is done on the
+ * pme_scan device. */
+struct scan_session {
+ /* The ctx that is needed to communicate with the pme high level */
+ struct pme_ctx ctx;
+ /* Locks completed_commands */
+ spinlock_t set_subset_lock;
+ __u8 set;
+ __u16 subset;
+ /* For asynchronous processing */
+ wait_queue_head_t waiting_for_completion;
+ struct list_head completed_commands;
+ /* Locks completed_commands */
+ spinlock_t completed_commands_lock;
+ u32 completed_count;
+};
+
+/* Command Token for scan operations. One of these is created for every
+ * operation on a context. When the context operation is complete cleanup
+ * is done */
+struct cmd_token {
+ /* pme high level token */
+ struct pme_ctx_token hl_token;
+ /* The kernels copy of the user op structure */
+ struct pme_scan_cmd kernel_op;
+ /* Set to non zero if this is a synchronous request */
+ u8 synchronous;
+ /* data */
+ struct qm_fd tx_fd;
+ struct qm_sg_entry tx_comp[2];
+ struct qm_fd rx_fd;
+ void *tx_data;
+ size_t tx_size;
+ void *rx_data;
+ size_t rx_size;
+ /* For blocking requests, we need a wait point and condition */
+ wait_queue_head_t *queue;
+ /* List management for completed async requests */
+ struct list_head completed_list;
+ u8 done;
+ u8 ern;
+};
+
+struct ctrl_op {
+ struct pme_ctx_ctrl_token ctx_ctr;
+ struct completion cb_done;
+ enum pme_status cmd_status;
+ u8 res_flag;
+ u8 ern;
+};
+
+#ifdef CONFIG_COMPAT
+static void compat_to_scan_cmd(struct pme_scan_cmd *dst,
+ struct compat_pme_scan_cmd *src)
+{
+ dst->flags = src->flags;
+ dst->opaque = compat_ptr(src->opaque);
+ dst->input.data = compat_ptr(src->input.data);
+ dst->input.size = src->input.size;
+ dst->output.data = compat_ptr(src->output.data);
+ dst->output.size = src->output.size;
+}
+
+static void scan_result_to_compat(struct compat_pme_scan_result *dst,
+ struct pme_scan_result *src)
+{
+ dst->flags = src->flags;
+ dst->opaque = ptr_to_compat(src->opaque);
+ dst->status = src->status;
+ dst->output.data = ptr_to_compat(src->output.data);
+ dst->output.size = src->output.size;
+}
+
+static void compat_to_scan_result(struct pme_scan_result *dst,
+ struct compat_pme_scan_result *src)
+{
+ dst->flags = src->flags;
+ dst->opaque = compat_ptr(src->opaque);
+ dst->status = src->status;
+ dst->output.data = compat_ptr(src->output.data);
+ dst->output.size = src->output.size;
+}
+#endif
+
+static void ctrl_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_ctrl_token *token)
+{
+ struct ctrl_op *ctrl = (struct ctrl_op *)token;
+ ctrl->cmd_status = pme_fd_res_status(fd);
+ ctrl->res_flag = pme_fd_res_flags(fd) & PME_STATUS_UNRELIABLE;
+ complete(&ctrl->cb_done);
+}
+
+static void ctrl_ern_cb(struct pme_ctx *ctx, const struct qm_mr_entry *mr,
+ struct pme_ctx_ctrl_token *token)
+{
+ struct ctrl_op *ctrl = (struct ctrl_op *)token;
+ ctrl->ern = 1;
+ complete(&ctrl->cb_done);
+}
+
+static inline int scan_data_empty(struct scan_session *session)
+{
+ return list_empty(&session->completed_commands);
+}
+
+/* Cleanup for the execute_cmd method */
+static inline void cleanup_token(struct cmd_token *token_p)
+{
+ kfree(token_p->tx_data);
+ kfree(token_p->rx_data);
+ return;
+}
+
+/* Callback for scan operations */
+static void scan_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_token *ctx_token)
+{
+ struct cmd_token *token = (struct cmd_token *)ctx_token;
+ struct scan_session *session = (struct scan_session *)ctx;
+
+ token->rx_fd = *fd;
+ /* If this is a asynchronous command, queue the token */
+ if (!token->synchronous) {
+ spin_lock(&session->completed_commands_lock);
+ list_add_tail(&token->completed_list,
+ &session->completed_commands);
+ session->completed_count++;
+ spin_unlock(&session->completed_commands_lock);
+ }
+ /* Wake up the thread that's waiting for us */
+ token->done = 1;
+ wake_up(token->queue);
+ return;
+}
+
+static void scan_ern_cb(struct pme_ctx *ctx, const struct qm_mr_entry *mr,
+ struct pme_ctx_token *ctx_token)
+{
+ struct cmd_token *token = (struct cmd_token *)ctx_token;
+ struct scan_session *session = (struct scan_session *)ctx;
+
+ token->ern = 1;
+ token->rx_fd = mr->ern.fd;
+ /* If this is a asynchronous command, queue the token */
+ if (!token->synchronous) {
+ spin_lock(&session->completed_commands_lock);
+ list_add_tail(&token->completed_list,
+ &session->completed_commands);
+ session->completed_count++;
+ spin_unlock(&session->completed_commands_lock);
+ }
+ /* Wake up the thread that's waiting for us */
+ token->done = 1;
+ wake_up(token->queue);
+ return;
+}
+
+static int process_completed_token(struct file *fp, struct cmd_token *token_p,
+ struct pme_scan_result *scan_result)
+{
+ int ret = 0;
+ u32 src_sz, dst_sz;
+
+ memset(scan_result, 0, sizeof(struct pme_scan_result));
+ if (token_p->ern) {
+ ret = -EIO;
+ goto done;
+ }
+ scan_result->output.data = token_p->kernel_op.output.data;
+
+ if (token_p->rx_fd.format == qm_fd_compound) {
+ /* Need to copy output */
+ src_sz = token_p->tx_comp[OUTPUT_FRM].length;
+ dst_sz = token_p->kernel_op.output.size;
+ scan_result->output.size = min(dst_sz, src_sz);
+ /* Doesn't make sense we generated more than available space
+ * should have got truncation.
+ */
+ BUG_ON(dst_sz < src_sz);
+ if (copy_to_user(scan_result->output.data, token_p->rx_data,
+ scan_result->output.size)) {
+ pr_err("Error copying to user data\n");
+ cleanup_token(token_p);
+ return -EFAULT;
+ }
+ } else if (token_p->rx_fd.format == qm_fd_sg_big)
+ scan_result->output.size = 0;
+ else
+ pr_err("pme2_scan: unexpected frame type received\n");
+
+ scan_result->flags |= pme_fd_res_flags(&token_p->rx_fd);
+ scan_result->status |= pme_fd_res_status(&token_p->rx_fd);
+done:
+ scan_result->opaque = token_p->kernel_op.opaque;
+ cleanup_token(token_p);
+ return ret;
+}
+
+static int getscan_cmd(struct file *fp, struct scan_session *session,
+ struct pme_scan_params __user *user_scan_params)
+{
+ int ret = 0;
+ struct pme_flow params;
+ struct pme_scan_params local_scan_params;
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .ctx_ctr.ern_cb = ctrl_ern_cb,
+ .cmd_status = 0,
+ .res_flag = 0,
+ .ern = 0
+ };
+ init_completion(&ctx_ctrl.cb_done);
+
+ memset(&local_scan_params, 0, sizeof(local_scan_params));
+
+ /* must be enabled */
+ if (pme_ctx_is_disabled(&session->ctx)) {
+ pr_err("pme2_scan: ctx is disabled\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = pme_ctx_ctrl_read_flow(&session->ctx, WAIT_AND_INTERRUPTABLE,
+ ¶ms, &ctx_ctrl.ctx_ctr);
+ if (ret) {
+ PMEPRINFO("read flow error %d\n", ret);
+ goto done;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+
+ if (ctx_ctrl.ern || ctx_ctrl.cmd_status || ctx_ctrl.res_flag) {
+ PMEPRINFO("read flow error %d\n", ctx_ctrl.cmd_status);
+ ret = -EFAULT;
+ goto done;
+ }
+ local_scan_params.residue.enable = params.ren;
+ local_scan_params.residue.length = params.rlen;
+ local_scan_params.sre.sessionid = params.sessionid;
+ local_scan_params.sre.verbose = params.srvm;
+ local_scan_params.sre.esee = params.esee;
+ local_scan_params.dxe.clim = params.clim;
+ local_scan_params.dxe.mlim = params.mlim;
+ spin_lock(&session->set_subset_lock);
+ local_scan_params.pattern.set = session->set;
+ local_scan_params.pattern.subset = session->subset;
+ spin_unlock(&session->set_subset_lock);
+
+ if (copy_to_user(user_scan_params, &local_scan_params,
+ sizeof(local_scan_params))) {
+ pr_err("Error copying to user data\n");
+ ret = -EFAULT;
+ }
+done:
+ return ret;
+}
+
+static int setscan_cmd(struct file *fp, struct scan_session *session,
+ struct pme_scan_params __user *user_params)
+{
+ int ret = 0;
+ u32 flag = WAIT_AND_INTERRUPTABLE;
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .ctx_ctr.ern_cb = ctrl_ern_cb,
+ .cmd_status = 0,
+ .res_flag = 0,
+ .ern = 0
+ };
+ struct pme_flow params;
+ struct pme_scan_params local_params;
+
+ pme_sw_flow_init(¶ms);
+ init_completion(&ctx_ctrl.cb_done);
+ if (copy_from_user(&local_params, user_params, sizeof(local_params)))
+ return -EFAULT;
+
+ /* must be enabled */
+ if (pme_ctx_is_disabled(&session->ctx)) {
+ ret = -EINVAL;
+ goto done;
+ }
+ /* Only send a flw_ctx_w if PME_SCAN_PARAMS_{RESIDUE, SRE or DXE}
+ * is being done */
+ if (local_params.flags == PME_SCAN_PARAMS_PATTERN)
+ goto set_subset;
+ if (local_params.flags & PME_SCAN_PARAMS_RESIDUE)
+ flag |= PME_CMD_FCW_RES;
+ if (local_params.flags & PME_SCAN_PARAMS_SRE)
+ flag |= PME_CMD_FCW_SRE;
+ if (local_params.flags & PME_SCAN_PARAMS_DXE)
+ flag |= PME_CMD_FCW_DXE;
+ params.ren = local_params.residue.enable;
+ params.sessionid = local_params.sre.sessionid;
+ params.srvm = local_params.sre.verbose;
+ params.esee = local_params.sre.esee;
+ params.clim = local_params.dxe.clim;
+ params.mlim = local_params.dxe.mlim;
+
+ ret = pme_ctx_ctrl_update_flow(&session->ctx, flag, ¶ms,
+ &ctx_ctrl.ctx_ctr);
+ if (ret) {
+ PMEPRINFO("update flow error %d\n", ret);
+ goto done;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.ern || ctx_ctrl.cmd_status || ctx_ctrl.res_flag) {
+ PMEPRINFO("update flow err %d\n", ctx_ctrl.cmd_status);
+ ret = -EFAULT;
+ goto done;
+ }
+
+set_subset:
+ if (local_params.flags & PME_SCAN_PARAMS_PATTERN) {
+ spin_lock(&session->set_subset_lock);
+ session->set = local_params.pattern.set;
+ session->subset = local_params.pattern.subset;
+ spin_unlock(&session->set_subset_lock);
+ goto done;
+ }
+done:
+ return ret;
+}
+
+static int resetseq_cmd(struct file *fp, struct scan_session *session)
+{
+ int ret = 0;
+ struct pme_flow params;
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .ctx_ctr.ern_cb = ctrl_ern_cb,
+ .cmd_status = 0,
+ .res_flag = 0,
+ .ern = 0
+ };
+ init_completion(&ctx_ctrl.cb_done);
+ pme_sw_flow_init(¶ms);
+
+ /* must be enabled */
+ if (pme_ctx_is_disabled(&session->ctx)) {
+ pr_err("pme2_scan: ctx is disabled\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ pme_flow_seqnum_set64(¶ms, 0);
+ params.sos = 1;
+
+ ret = pme_ctx_ctrl_update_flow(&session->ctx, PME_CMD_FCW_SEQ, ¶ms,
+ &ctx_ctrl.ctx_ctr);
+ if (ret) {
+ pr_err("pme2_scan: update flow error %d\n", ret);
+ return ret;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.ern || ctx_ctrl.cmd_status || ctx_ctrl.res_flag) {
+ PMEPRINFO("update flow err %d\n", ctx_ctrl.cmd_status);
+ ret = -EFAULT;
+ }
+done:
+ return ret;
+}
+
+static int resetresidue_cmd(struct file *fp, struct scan_session *session)
+{
+ int ret = 0;
+ struct pme_flow params;
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .ctx_ctr.ern_cb = ctrl_ern_cb,
+ .cmd_status = 0,
+ .res_flag = 0,
+ .ern = 0
+ };
+
+ init_completion(&ctx_ctrl.cb_done);
+ pme_sw_flow_init(¶ms);
+ /* must be enabled */
+ if (pme_ctx_is_disabled(&session->ctx)) {
+ pr_err("pme2_scan: ctx is disabled\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ params.rlen = 0;
+ ret = pme_ctx_ctrl_update_flow(&session->ctx,
+ WAIT_AND_INTERRUPTABLE | PME_CTX_OP_RESETRESLEN,
+ ¶ms, &ctx_ctrl.ctx_ctr);
+ if (ret)
+ pr_info("pme2_scan: update flow error %d\n", ret);
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.ern || ctx_ctrl.cmd_status || ctx_ctrl.res_flag) {
+ PMEPRINFO("update flow err %d\n", ctx_ctrl.cmd_status);
+ ret = -EFAULT;
+ }
+done:
+ return ret;
+}
+
+static int process_scan_cmd(
+ struct file *fp,
+ struct scan_session *session,
+ struct pme_scan_cmd *user_cmd,
+ struct pme_scan_result *user_ret,
+ u8 synchronous)
+{
+ int ret = 0;
+ struct cmd_token local_token;
+ struct cmd_token *token_p = NULL;
+ DECLARE_WAIT_QUEUE_HEAD(local_waitqueue);
+ u8 scan_flags = 0;
+
+ BUG_ON(synchronous && !user_ret);
+
+ /* If synchronous, use a local token (from the stack)
+ * If asynchronous, allocate a token to use */
+ if (synchronous)
+ token_p = &local_token;
+ else {
+ token_p = kmalloc(sizeof(*token_p), GFP_KERNEL);
+ if (!token_p)
+ return -ENOMEM;
+ }
+ memset(token_p, 0, sizeof(*token_p));
+ /* Copy the command to kernel space */
+ memcpy(&token_p->kernel_op, user_cmd, sizeof(struct pme_scan_cmd));
+ /* Copy the input */
+ token_p->synchronous = synchronous;
+ token_p->tx_size = token_p->kernel_op.input.size;
+ token_p->tx_data = kmalloc(token_p->kernel_op.input.size, GFP_KERNEL);
+ if (!token_p->tx_data) {
+ pr_err("pme2_scan: Err alloc %zd byte", token_p->tx_size);
+ cleanup_token(token_p);
+ return -ENOMEM;
+ }
+ if (copy_from_user(token_p->tx_data,
+ token_p->kernel_op.input.data,
+ token_p->kernel_op.input.size)) {
+ pr_err("Error copying contigous user data\n");
+ cleanup_token(token_p);
+ return -EFAULT;
+ }
+ /* Setup input frame */
+ token_p->tx_comp[INPUT_FRM].final = 1;
+ token_p->tx_comp[INPUT_FRM].length = token_p->tx_size;
+ qm_sg_entry_set64(&token_p->tx_comp[INPUT_FRM],
+ pme_map(token_p->tx_data));
+ /* setup output frame, if output is expected */
+ if (token_p->kernel_op.output.size) {
+ token_p->rx_size = token_p->kernel_op.output.size;
+ PMEPRINFO("pme2_scan: expect output %d\n", token_p->rx_size);
+ token_p->rx_data = kmalloc(token_p->rx_size, GFP_KERNEL);
+ if (!token_p->rx_data) {
+ pr_err("pme2_scan: Err alloc %zd byte",
+ token_p->rx_size);
+ cleanup_token(token_p);
+ return -ENOMEM;
+ }
+ /* Setup output frame */
+ token_p->tx_comp[OUTPUT_FRM].length = token_p->rx_size;
+ qm_sg_entry_set64(&token_p->tx_comp[OUTPUT_FRM],
+ pme_map(token_p->rx_data));
+ token_p->tx_fd.format = qm_fd_compound;
+ /* Build compound frame */
+ qm_fd_addr_set64(&token_p->tx_fd,
+ pme_map(token_p->tx_comp));
+ } else {
+ token_p->tx_fd.format = qm_fd_sg_big;
+ /* Build sg frame */
+ qm_fd_addr_set64(&token_p->tx_fd,
+ pme_map(&token_p->tx_comp[INPUT_FRM]));
+ token_p->tx_fd.length29 = token_p->tx_size;
+ }
+
+ /* use the local wait queue if synchronous, the shared
+ * queue if asynchronous */
+ if (synchronous)
+ token_p->queue = &local_waitqueue;
+ else
+ token_p->queue = &session->waiting_for_completion;
+ token_p->done = 0;
+
+ if (token_p->kernel_op.flags & PME_SCAN_CMD_STARTRESET)
+ scan_flags |= PME_CMD_SCAN_SR;
+ if (token_p->kernel_op.flags & PME_SCAN_CMD_END)
+ scan_flags |= PME_CMD_SCAN_E;
+ ret = pme_ctx_scan(&session->ctx, WAIT_AND_INTERRUPTABLE,
+ &token_p->tx_fd,
+ PME_SCAN_ARGS(scan_flags, session->set, session->subset),
+ &token_p->hl_token);
+ if (unlikely(ret)) {
+ cleanup_token(token_p);
+ return ret;
+ }
+
+ if (!synchronous)
+ /* Don't wait. The command is away */
+ return 0;
+
+ PMEPRINFO("Wait for completion\n");
+ /* Wait for the command to complete */
+ /* TODO: Should this be wait_event_interruptible ?
+ * If so, will need logic to indicate */
+ wait_event(*token_p->queue, token_p->done == 1);
+ return process_completed_token(fp, token_p, user_ret);
+}
+
+/**
+ * fsl_pme2_scan_open - open the driver
+ *
+ * Open the driver and prepare for requests.
+ *
+ * Every time an application opens the driver, we create a scan_session object
+ * for that file handle.
+ */
+static int fsl_pme2_scan_open(struct inode *node, struct file *fp)
+{
+ int ret;
+ struct scan_session *session;
+ struct pme_flow flow;
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .ctx_ctr.ern_cb = ctrl_ern_cb,
+ .cmd_status = 0,
+ .res_flag = 0,
+ .ern = 0
+ };
+
+ pme_sw_flow_init(&flow);
+ init_completion(&ctx_ctrl.cb_done);
+ PMEPRINFO("pme2_scan: open %d\n", smp_processor_id());
+ fp->private_data = kzalloc(sizeof(*session), GFP_KERNEL);
+ if (!fp->private_data)
+ return -ENOMEM;
+ session = (struct scan_session *)fp->private_data;
+ /* Set up the structures used for asynchronous requests */
+ init_waitqueue_head(&session->waiting_for_completion);
+ INIT_LIST_HEAD(&session->completed_commands);
+ spin_lock_init(&session->completed_commands_lock);
+ spin_lock_init(&session->set_subset_lock);
+ PMEPRINFO("kmalloc session %p\n", fp->private_data);
+ session = fp->private_data;
+ session->ctx.cb = scan_cb;
+ session->ctx.ern_cb = scan_ern_cb;
+
+ /* qosin, qosout should be driver attributes */
+ ret = pme_ctx_init(&session->ctx, PME_CTX_FLAG_LOCAL, 0, 4, 4, 0, NULL);
+ if (ret) {
+ pr_err("pme2_scan: pme_ctx_init %d\n", ret);
+ goto exit;
+ }
+ /* enable the context */
+ ret = pme_ctx_enable(&session->ctx);
+ if (ret) {
+ PMEPRINFO("error enabling ctx %d\n", ret);
+ pme_ctx_finish(&session->ctx);
+ goto exit;
+ }
+ /* Update flow to set sane defaults in the flow context */
+ ret = pme_ctx_ctrl_update_flow(&session->ctx,
+ PME_CTX_OP_WAIT | PME_CMD_FCW_ALL, &flow, &ctx_ctrl.ctx_ctr);
+ if (!ret) {
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.ern || ctx_ctrl.cmd_status || ctx_ctrl.res_flag)
+ ret = -EFAULT;
+ }
+ if (ret) {
+ int my_ret;
+ PMEPRINFO("error updating flow ctx %d\n", ret);
+ my_ret = pme_ctx_disable(&session->ctx, PME_CTX_OP_WAIT,
+ &ctx_ctrl.ctx_ctr);
+ if (my_ret > 0)
+ wait_for_completion(&ctx_ctrl.cb_done);
+ else if (my_ret < 0)
+ PMEPRINFO("error disabling ctx %d\n", ret);
+ pme_ctx_finish(&session->ctx);
+ goto exit;
+ }
+ /* Set up the structures used for asynchronous requests */
+ PMEPRINFO("pme2_scan: Finish pme_scan open %d\n", smp_processor_id());
+ return 0;
+exit:
+ kfree(fp->private_data);
+ fp->private_data = NULL;
+ return ret;
+}
+
+static int fsl_pme2_scan_close(struct inode *node, struct file *fp)
+{
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .ctx_ctr.ern_cb = ctrl_ern_cb,
+ .cmd_status = 0,
+ .res_flag = 0,
+ .ern = 0
+ };
+ int ret = 0;
+ struct scan_session *session = fp->private_data;
+
+ init_completion(&ctx_ctrl.cb_done);
+ /* Before disabling check to see if it's already disabled. This can
+ * happen if a pme serious error has occurred for instance.*/
+ if (!pme_ctx_is_disabled(&session->ctx)) {
+ ret = pme_ctx_disable(&session->ctx, PME_CTX_OP_WAIT,
+ &ctx_ctrl.ctx_ctr);
+ if (ret > 0) {
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.ern)
+ PMEPRCRIT("Unexpected ERN\n");
+ } else if (ret < 0) {
+ pr_err("pme2_scan: Error disabling ctx %d\n", ret);
+ return ret;
+ }
+ }
+ pme_ctx_finish(&session->ctx);
+ kfree(session);
+ PMEPRINFO("pme2_scan: Finish pme_session close\n");
+ return 0;
+}
+
+static unsigned int fsl_pme2_scan_poll(struct file *fp,
+ struct poll_table_struct *wait)
+{
+ struct scan_session *session;
+ unsigned int mask = POLLOUT | POLLWRNORM;
+
+ if (!fp->private_data)
+ return -EINVAL;
+
+ session = (struct scan_session *)fp->private_data;
+
+ poll_wait(fp, &session->waiting_for_completion, wait);
+
+ if (!scan_data_empty(session))
+ mask |= (POLLIN | POLLRDNORM);
+ return mask;
+}
+
+
+/* Main switch loop for ioctl operations */
+static long fsl_pme2_scan_ioctl(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct scan_session *session = fp->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+
+ case PMEIO_GETSCAN:
+ return getscan_cmd(fp, session, (struct pme_scan_params *)arg);
+ break;
+
+ case PMEIO_SETSCAN:
+ return setscan_cmd(fp, session, (struct pme_scan_params *)arg);
+ break;
+
+ case PMEIO_RESETSEQ:
+ return resetseq_cmd(fp, session);
+ break;
+
+ case PMEIO_RESETRES:
+ return resetresidue_cmd(fp, session);
+ break;
+
+ case PMEIO_SCAN:
+ {
+ int ret;
+ struct pme_scan scan;
+
+ if (copy_from_user(&scan, (void __user *)arg, sizeof(scan)))
+ return -EFAULT;
+ ret = process_scan_cmd(fp, session, &scan.cmd, &scan.result, 1);
+ if (!ret) {
+ struct pme_scan_result __user *user_result =
+ &((struct pme_scan __user *)arg)->result;
+ ret = copy_to_user(user_result, &scan.result,
+ sizeof(*user_result));
+ }
+ return ret;
+ }
+ break;
+
+ case PMEIO_SCAN_W1:
+ {
+ struct pme_scan_cmd scan_cmd;
+
+ if (copy_from_user(&scan_cmd, (void __user *)arg,
+ sizeof(scan_cmd)))
+ return -EFAULT;
+ return process_scan_cmd(fp, session, &scan_cmd, NULL, 0);
+ }
+ break;
+
+ case PMEIO_SCAN_R1:
+ {
+ struct pme_scan_result result;
+ struct cmd_token *completed_cmd = NULL;
+ struct pme_scan_result __user *ur =
+ (struct pme_scan_result __user *)arg;
+ int ret;
+
+ if (copy_from_user(&result, (void __user *)arg,
+ sizeof(result)))
+ return -EFAULT;
+
+ /* Check to see if any results */
+ spin_lock(&session->completed_commands_lock);
+ if (!list_empty(&session->completed_commands)) {
+ completed_cmd = list_first_entry(
+ &session->completed_commands,
+ struct cmd_token,
+ completed_list);
+ list_del(&completed_cmd->completed_list);
+ session->completed_count--;
+ }
+ spin_unlock(&session->completed_commands_lock);
+ if (completed_cmd) {
+ ret = process_completed_token(fp, completed_cmd,
+ &result);
+ if (!ret)
+ ret = copy_to_user(ur, &result, sizeof(result));
+ return ret;
+ } else
+ return -EIO;
+ }
+ break;
+
+ case PMEIO_SCAN_Wn:
+ {
+ struct pme_scan_cmds scan_cmds;
+ int i, ret = 0;
+
+ /* Copy the command to kernel space */
+ if (copy_from_user(&scan_cmds, (void __user *)arg,
+ sizeof(scan_cmds)))
+ return -EFAULT;
+ PMEPRINFO("Received Wn for %d cmds\n", scan_cmds.num);
+ for (i = 0; i < scan_cmds.num; i++) {
+ struct pme_scan_cmd scan_cmd;
+
+ if (copy_from_user(&scan_cmd, &scan_cmds.cmds[i],
+ sizeof(scan_cmd))) {
+ pr_err("pme2_scan: Err with %d\n", i);
+ scan_cmds.num = i;
+ if (copy_to_user((void __user *)arg, &scan_cmds,
+ sizeof(scan_cmds))) {
+ return -EFAULT;
+ }
+ return -EFAULT;
+ }
+ ret = process_scan_cmd(fp, session, &scan_cmd, NULL, 0);
+ if (ret) {
+ pr_err("pme2_scan: Err with %d cmd %d\n",
+ i, ret);
+ scan_cmds.num = i;
+ if (copy_to_user((void *)arg, &scan_cmds,
+ sizeof(scan_cmds))) {
+ pr_err("Error copying to user data\n");
+ return -EFAULT;
+ }
+ return -EINTR;
+ }
+ }
+ return ret;
+ }
+ break;
+
+ case PMEIO_SCAN_Rn:
+ {
+ struct pme_scan_results results;
+ struct pme_scan_result result;
+ struct pme_scan_result __user *ur;
+ int i = 0, ret = 0;
+ struct cmd_token *completed_cmd = NULL;
+
+ /* Copy the command to kernel space */
+ if (copy_from_user(&results, (void __user *)arg,
+ sizeof(results)))
+ return -EFAULT;
+ ur = ((struct pme_scan_results __user *)arg)->results
+ PMEPRINFO("pme2_scan: Received Rn for %d res\n", results.num);
+ if (!results.num)
+ return 0;
+ do {
+ completed_cmd = NULL;
+ ret = 0;
+ /* Check to see if any results */
+ spin_lock(&session->completed_commands_lock);
+ if (!list_empty(&session->completed_commands)) {
+ /* Move to a different list */
+ PMEPRINFO("pme2_scan: Pop response\n");
+ completed_cmd = list_first_entry(
+ &session->completed_commands,
+ struct cmd_token,
+ completed_list);
+ list_del(&completed_cmd->completed_list);
+ session->completed_count--;
+ }
+ spin_unlock(&session->completed_commands_lock);
+ if (completed_cmd) {
+ if (copy_from_user(&result, (void __user *)ur+i,
+ sizeof(result)))
+ return -EFAULT;
+ ret = process_completed_token(fp, completed_cmd,
+ &result);
+ if (!ret)
+ ret = copy_to_user(ur, &result,
+ sizeof(struct pme_scan_result));
+ if (!ret) {
+ i++;
+ ur++;
+ }
+ }
+ } while (!ret && completed_cmd && (i != results.num));
+
+ if (i != results.num) {
+ PMEPRINFO("pme2_scan: Only filled %d responses\n", i);
+ results.num = i;
+ PMEPRINFO("pme2_scan: results.num = %d\n", results.num);
+ if (copy_to_user((void __user *)arg, &results,
+ sizeof(struct pme_scan_results))) {
+ pr_err("Error copying to user data\n");
+ return -EFAULT;
+ }
+ }
+ return ret;
+ }
+ break;
+
+ case PMEIO_RELEASE_BUFS:
+ return -EINVAL;
+ break;
+
+#ifdef CONFIG_COMPAT
+ case PMEIO_SCAN32:
+ {
+ int ret;
+ struct compat_pme_scan scan32;
+ struct compat_pme_scan __user *user_scan = compat_ptr(arg);
+ struct pme_scan scan;
+
+ if (copy_from_user(&scan32, user_scan, sizeof(scan32)))
+ return -EFAULT;
+ /* Convert to 64-bit structs */
+ compat_to_scan_cmd(&scan.cmd, &scan32.cmd);
+ compat_to_scan_result(&scan.result, &scan32.result);
+
+ ret = process_scan_cmd(fp, session, &scan.cmd, &scan.result, 1);
+ if (!ret) {
+ struct compat_pme_scan_result __user *user_result =
+ &user_scan->result;
+ /* Convert to 32-bit struct */
+ scan_result_to_compat(&scan32.result, &scan.result);
+ ret = copy_to_user(user_result, &scan32.result,
+ sizeof(*user_result));
+ }
+ return ret;
+ }
+ break;
+
+ case PMEIO_SCAN_W132:
+ {
+ struct compat_pme_scan_cmd scan_cmd32;
+ struct pme_scan_cmd scan_cmd;
+
+ if (copy_from_user(&scan_cmd32, compat_ptr(arg),
+ sizeof(scan_cmd32)))
+ return -EFAULT;
+ /* Convert to 64-bit struct */
+ compat_to_scan_cmd(&scan_cmd, &scan_cmd32);
+ return process_scan_cmd(fp, session, &scan_cmd, NULL, 0);
+ }
+ break;
+
+ case PMEIO_SCAN_R132:
+ {
+ struct compat_pme_scan_result result32;
+ struct pme_scan_result result;
+ struct cmd_token *completed_cmd = NULL;
+ struct compat_pme_scan_result __user *ur = compat_ptr(arg);
+ int ret;
+
+ if (copy_from_user(&result32, (void __user *)arg,
+ sizeof(result32)))
+ return -EFAULT;
+ /* copy to 64-bit structure */
+ compat_to_scan_result(&result, &result32);
+
+ /* Check to see if any results */
+ spin_lock(&session->completed_commands_lock);
+ if (!list_empty(&session->completed_commands)) {
+ completed_cmd = list_first_entry(
+ &session->completed_commands,
+ struct cmd_token,
+ completed_list);
+ list_del(&completed_cmd->completed_list);
+ session->completed_count--;
+ }
+ spin_unlock(&session->completed_commands_lock);
+ if (completed_cmd) {
+ ret = process_completed_token(fp, completed_cmd,
+ &result);
+ scan_result_to_compat(&result32, &result);
+ ret = copy_to_user(ur, &result32, sizeof(result32));
+ } else
+ return -EIO;
+ }
+ break;
+
+ case PMEIO_SCAN_Wn32:
+ {
+ struct compat_pme_scan_cmds scan_cmds32;
+ int i, ret = 0;
+
+ /* Copy the command to kernel space */
+ if (copy_from_user(&scan_cmds32, compat_ptr(arg),
+ sizeof(scan_cmds32)))
+ return -EFAULT;
+ PMEPRINFO("Received Wn for %d cmds\n", scan_cmds32.num);
+ for (i = 0; i < scan_cmds32.num; i++) {
+ struct pme_scan_cmd scan_cmd;
+ struct compat_pme_scan_cmd __user *u_scan_cmd32;
+ struct compat_pme_scan_cmd scan_cmd32;
+
+ u_scan_cmd32 = compat_ptr(scan_cmds32.cmds);
+ u_scan_cmd32 += i;
+
+ if (copy_from_user(&scan_cmd32, u_scan_cmd32,
+ sizeof(scan_cmd32))) {
+ pr_err("pme2_scan: Err with %d\n", i);
+ scan_cmds32.num = i;
+ if (copy_to_user(compat_ptr(arg), &scan_cmds32,
+ sizeof(scan_cmds32)))
+ return -EFAULT;
+ return -EFAULT;
+ }
+ compat_to_scan_cmd(&scan_cmd, &scan_cmd32);
+ ret = process_scan_cmd(fp, session, &scan_cmd, NULL, 0);
+ if (ret) {
+ pr_err("pme2_scan: Err with %d cmd %d\n",
+ i, ret);
+ scan_cmds32.num = i;
+ if (copy_to_user(compat_ptr(arg), &scan_cmds32,
+ sizeof(scan_cmds32)))
+ return -EFAULT;
+ return -EINTR;
+ }
+ }
+ return ret;
+ }
+ break;
+
+ case PMEIO_SCAN_Rn32:
+ {
+ struct compat_pme_scan_results results32;
+ int i = 0, ret = 0;
+ struct cmd_token *completed_cmd = NULL;
+ struct compat_pme_scan_result __user *ur;
+
+ /* Copy the command to kernel space */
+ if (copy_from_user(&results32, compat_ptr(arg),
+ sizeof(results32)))
+ return -EFAULT;
+ ur = compat_ptr(results32.results);
+ PMEPRINFO("pme2_scan: Rx Rn for %d res\n", results32.num);
+ if (!results32.num)
+ return 0;
+ do {
+ completed_cmd = NULL;
+ ret = 0;
+ /* Check to see if any results */
+ spin_lock(&session->completed_commands_lock);
+ if (!list_empty(&session->completed_commands)) {
+ /* Move to a different list */
+ PMEPRINFO("pme2_scan: Pop response\n");
+ completed_cmd = list_first_entry(
+ &session->completed_commands,
+ struct cmd_token,
+ completed_list);
+ list_del(&completed_cmd->completed_list);
+ session->completed_count--;
+ }
+ spin_unlock(&session->completed_commands_lock);
+ if (completed_cmd) {
+ struct compat_pme_scan_result l_result32;
+ struct pme_scan_result result;
+
+ if (copy_from_user(&l_result32, ur+i,
+ sizeof(l_result32)))
+ return -EFAULT;
+ compat_to_scan_result(&result, &l_result32);
+ ret = process_completed_token(fp, completed_cmd,
+ &result);
+ scan_result_to_compat(&l_result32, &result);
+ ret = copy_to_user(ur+i, &l_result32,
+ sizeof(l_result32));
+ if (!ret)
+ i++;
+ }
+ } while (!ret && completed_cmd && (i != results32.num));
+
+ if (i != results32.num) {
+ PMEPRINFO("pme2_scan: Only filled %d responses\n", i);
+ results32.num = i;
+ PMEPRINFO("pme2_scan: results32.num = %d\n",
+ results32.num);
+ if (copy_to_user(compat_ptr(arg), &results32,
+ sizeof(struct pme_scan_results))) {
+ pr_err("Error copying to user data\n");
+ return -EFAULT;
+ }
+ }
+ return ret;
+ }
+ break;
+#endif /* CONFIG_COMPAT */
+
+ default:
+ pr_err("UNKNOWN IOCTL cmd 0x%x\n", cmd);
+ return -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct file_operations fsl_pme2_scan_fops = {
+ .owner = THIS_MODULE,
+ .open = fsl_pme2_scan_open,
+ .release = fsl_pme2_scan_close,
+ .poll = fsl_pme2_scan_poll,
+ .unlocked_ioctl = fsl_pme2_scan_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = fsl_pme2_scan_ioctl,
+#endif
+};
+
+static struct miscdevice fsl_pme2_scan_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = PME_DEV_SCAN_NODE,
+ .fops = &fsl_pme2_scan_fops
+};
+
+static int __init fsl_pme2_scan_init(void)
+{
+ int err = 0;
+
+ pr_info("Freescale pme2 scan driver\n");
+ err = misc_register(&fsl_pme2_scan_dev);
+ if (err) {
+ pr_err("fsl-pme2-scan: cannot register device\n");
+ return err;
+ }
+ pr_info("fsl-pme2-scan: device %s registered\n",
+ fsl_pme2_scan_dev.name);
+ return 0;
+}
+
+static void __exit fsl_pme2_scan_exit(void)
+{
+ int err = misc_deregister(&fsl_pme2_scan_dev);
+ if (err)
+ pr_err("fsl-pme2-scan: Failed to deregister device %s, "
+ "code %d\n", fsl_pme2_scan_dev.name, err);
+ pr_info("fsl-pme2-scan: device %s deregistered\n",
+ fsl_pme2_scan_dev.name);
+}
+
+module_init(fsl_pme2_scan_init);
+module_exit(fsl_pme2_scan_exit);
+
+MODULE_AUTHOR("Jeffrey Ladouceur <jeffrey.ladouceur@freescale.com>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Freescale PME2 scan driver");
new file mode 100644
@@ -0,0 +1,64 @@
+/* Copyright 2008-2011 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 <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/bootmem.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <linux/kthread.h>
+#include <linux/memblock.h>
+#include <linux/completion.h>
+#include <linux/log2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <linux/uio_driver.h>
+#include <asm/smp.h>
+#include <sysdev/fsl_soc.h>
+#include <linux/fsl_hypervisor.h>
+#include <linux/fsl_bman.h>
+#include <linux/fsl_pme.h>
+
+int pme2_create_sysfs_dev_files(struct platform_device *ofdev);
+void pme2_remove_sysfs_dev_files(struct platform_device *ofdev);
+void accumulator_update_interval(u32 interval);
+
new file mode 100644
@@ -0,0 +1,565 @@
+/* Copyright 2008-2011 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 "pme2_regs.h"
+#include "pme2_private.h"
+
+#define MAX_ACCUMULATOR_INTERVAL 10000
+extern u32 pme_stat_interval;
+
+/* The pme sysfs contains the following types of attributes
+ * 1) root level: general pme confuration
+ * 2) bsc: bufferpool size configuration
+ * 3) stats: pme statistics
+ */
+static ssize_t pme_store(struct device *dev, struct device_attribute *dev_attr,
+ const char *buf, size_t count, enum pme_attr attr)
+{
+ unsigned long val;
+ size_t ret;
+ if (strict_strtoul(buf, 0, &val)) {
+ dev_dbg(dev, "invalid input %s\n",buf);
+ return -EINVAL;
+ }
+ ret = pme_attr_set(attr, val);
+ if (ret) {
+ dev_err(dev, "attr_set err attr=%u, val=%lu\n", attr, val);
+ return ret;
+ }
+ return count;
+}
+
+static ssize_t pme_show(struct device *dev, struct device_attribute *dev_attr,
+ char *buf, enum pme_attr attr, const char *fmt)
+{
+ u32 data;
+ int ret;
+
+ ret = pme_attr_get(attr, &data);
+ if (!ret)
+ return snprintf(buf, PAGE_SIZE, fmt, data);
+ return ret;
+}
+
+
+static ssize_t pme_stat_show(struct device *dev,
+ struct device_attribute *dev_attr, char *buf, enum pme_attr attr)
+{
+ u64 data = 0;
+ int ret = 0;
+
+ ret = pme_stat_get(attr, &data, 0);
+ if (!ret)
+ return snprintf(buf, PAGE_SIZE, "%llu\n", data);
+ else
+ return ret;
+}
+
+static ssize_t pme_stat_store(struct device *dev,
+ struct device_attribute *dev_attr, const char *buf,
+ size_t count, enum pme_attr attr)
+{
+ unsigned long val;
+ u64 data = 0;
+ size_t ret = 0;
+ if (strict_strtoul(buf, 0, &val)) {
+ pr_err("pme: invalid input %s\n", buf);
+ return -EINVAL;
+ }
+ if (val) {
+ pr_err("pme: invalid input %s\n", buf);
+ return -EINVAL;
+ }
+ ret = pme_stat_get(attr, &data, 1);
+ return count;
+}
+
+
+#define PME_SYSFS_ATTR(pme_attr, perm, showhex) \
+static ssize_t pme_store_##pme_attr(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ return pme_store(dev, attr, buf, count, pme_attr_##pme_attr);\
+} \
+static ssize_t pme_show_##pme_attr(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return pme_show(dev, attr, buf, pme_attr_##pme_attr, showhex);\
+} \
+static DEVICE_ATTR( pme_attr, perm, pme_show_##pme_attr, pme_store_##pme_attr);
+
+
+#define PME_SYSFS_STAT_ATTR(pme_attr, perm) \
+static ssize_t pme_store_##pme_attr(struct device *dev, \
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ return pme_stat_store(dev, attr, buf, count, pme_attr_##pme_attr);\
+} \
+static ssize_t pme_show_##pme_attr(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return pme_stat_show(dev, attr, buf, pme_attr_##pme_attr);\
+} \
+static DEVICE_ATTR(pme_attr, perm, pme_show_##pme_attr, pme_store_##pme_attr);
+
+
+#define PME_SYSFS_BSC_ATTR(bsc_id, perm, showhex) \
+static ssize_t pme_store_bsc_##bsc_id(struct device *dev,\
+ struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ return pme_store(dev, attr, buf, count, pme_attr_bsc(bsc_id));\
+} \
+static ssize_t pme_show_bsc_##bsc_id(struct device *dev,\
+ struct device_attribute *attr, char *buf) \
+{ \
+ return pme_show(dev, attr, buf, pme_attr_bsc(bsc_id), showhex);\
+} \
+static DEVICE_ATTR(bsc_id, perm, pme_show_bsc_##bsc_id, \
+ pme_store_bsc_##bsc_id);
+
+/* Statistics Ctrl: update interval */
+static ssize_t pme_store_update_interval(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long val;
+
+ if (!pme2_have_control()) {
+ PMEPRERR("not on ctrl-plane\n");
+ return -ENODEV;
+ }
+ if (strict_strtoul(buf, 0, &val)) {
+ dev_info(dev, "invalid input %s\n", buf);
+ return -EINVAL;
+ }
+ if (val > MAX_ACCUMULATOR_INTERVAL) {
+ dev_info(dev, "invalid input %s\n", buf);
+ return -ERANGE;
+ }
+ accumulator_update_interval(val);
+ return count;
+}
+static ssize_t pme_show_update_interval(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!pme2_have_control())
+ return -ENODEV;
+ return snprintf(buf, PAGE_SIZE, "%u\n", pme_stat_interval);
+}
+
+#define FMT_0HEX "0x%08x\n"
+#define FMT_HEX "0x%x\n"
+#define FMT_DEC "%u\n"
+#define PRIV_RO S_IRUSR
+#define PRIV_RW (S_IRUSR | S_IWUSR)
+
+/* Register Interfaces */
+/* read-write; */
+PME_SYSFS_ATTR(efqc_int, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(sw_db, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(dmcr, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(smcr, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(famcr, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(kvlts, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(max_chain_length, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(pattern_range_counter_idx, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(pattern_range_counter_mask, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(max_allowed_test_line_per_pattern, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(max_pattern_matches_per_sui, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(max_pattern_evaluations_per_sui, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(report_length_limit, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(end_of_simple_sui_report, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(aim, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(end_of_sui_reaction_ptr, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(sre_pscl, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(sre_max_block_num, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(sre_max_instruction_limit, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(esr, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(pehd, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(ecc1bes, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(ecc2bes, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(miace, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(miacr, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(cdcr, PRIV_RW, FMT_0HEX);
+PME_SYSFS_ATTR(pmtr, PRIV_RW, FMT_DEC);
+
+/* read-only; */
+PME_SYSFS_ATTR(max_pdsr_index, PRIV_RO, FMT_DEC);
+PME_SYSFS_ATTR(sre_context_size, PRIV_RO, FMT_DEC);
+PME_SYSFS_ATTR(sre_rule_num, PRIV_RO, FMT_DEC);
+PME_SYSFS_ATTR(sre_session_ctx_num, PRIV_RO, FMT_DEC);
+PME_SYSFS_ATTR(sre_max_index_size, PRIV_RO, FMT_DEC);
+PME_SYSFS_ATTR(sre_max_offset_ctrl, PRIV_RO, FMT_DEC);
+PME_SYSFS_ATTR(src_id, PRIV_RO, FMT_DEC);
+PME_SYSFS_ATTR(liodnr, PRIV_RO, FMT_DEC);
+PME_SYSFS_ATTR(rev1, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(rev2, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(isr, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(ecr0, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(ecr1, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(pmstat, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(eccaddr, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(ecccode, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(faconf, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(pdsrbah, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(pdsrbal, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(scbarh, PRIV_RO, FMT_0HEX);
+PME_SYSFS_ATTR(scbarl, PRIV_RO, FMT_0HEX);
+
+
+/* Buffer Pool Size Configuration */
+PME_SYSFS_BSC_ATTR(0, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(1, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(2, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(3, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(4, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(5, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(6, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(7, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(8, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(9, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(10, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(11, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(12, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(13, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(14, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(15, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(16, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(17, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(18, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(19, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(20, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(21, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(22, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(23, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(24, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(25, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(26, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(27, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(28, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(29, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(30, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(31, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(32, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(33, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(34, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(35, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(36, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(37, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(38, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(39, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(40, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(41, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(42, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(43, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(44, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(45, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(46, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(47, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(48, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(49, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(50, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(51, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(52, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(53, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(54, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(55, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(56, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(57, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(58, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(59, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(60, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(61, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(62, PRIV_RW, FMT_DEC);
+PME_SYSFS_BSC_ATTR(63, PRIV_RW, FMT_DEC);
+
+/* Stats Counters*/
+PME_SYSFS_STAT_ATTR(trunci, PRIV_RW);
+PME_SYSFS_STAT_ATTR(rbc, PRIV_RW);
+PME_SYSFS_STAT_ATTR(tbt0ecc1ec, PRIV_RW);
+PME_SYSFS_STAT_ATTR(tbt1ecc1ec, PRIV_RW);
+PME_SYSFS_STAT_ATTR(vlt0ecc1ec, PRIV_RW);
+PME_SYSFS_STAT_ATTR(vlt1ecc1ec, PRIV_RW);
+PME_SYSFS_STAT_ATTR(cmecc1ec, PRIV_RW);
+PME_SYSFS_STAT_ATTR(dxcmecc1ec, PRIV_RW);
+PME_SYSFS_STAT_ATTR(dxemecc1ec, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnib, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnis, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnth1, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnth2, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnthv, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnths, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnch, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnpm, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stns1m, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnpmr, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stndsr, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnesr, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stns1r, PRIV_RW);
+PME_SYSFS_STAT_ATTR(stnob, PRIV_RW);
+PME_SYSFS_STAT_ATTR(mia_byc, PRIV_RW);
+PME_SYSFS_STAT_ATTR(mia_blc, PRIV_RW);
+
+/* Stats Control */
+PME_SYSFS_ATTR(tbt0ecc1th, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(tbt1ecc1th, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(vlt0ecc1th, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(vlt1ecc1th, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(cmecc1th, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(dxcmecc1th, PRIV_RW, FMT_DEC);
+PME_SYSFS_ATTR(dxemecc1th, PRIV_RW, FMT_DEC);
+
+static DEVICE_ATTR(update_interval, (S_IRUSR | S_IWUSR),
+ pme_show_update_interval, pme_store_update_interval);
+
+static struct attribute *pme_dev_bsc_attributes[] = {
+ &dev_attr_0.attr,
+ &dev_attr_1.attr,
+ &dev_attr_2.attr,
+ &dev_attr_3.attr,
+ &dev_attr_4.attr,
+ &dev_attr_5.attr,
+ &dev_attr_6.attr,
+ &dev_attr_7.attr,
+ &dev_attr_8.attr,
+ &dev_attr_9.attr,
+ &dev_attr_10.attr,
+ &dev_attr_11.attr,
+ &dev_attr_12.attr,
+ &dev_attr_13.attr,
+ &dev_attr_14.attr,
+ &dev_attr_15.attr,
+ &dev_attr_16.attr,
+ &dev_attr_17.attr,
+ &dev_attr_18.attr,
+ &dev_attr_19.attr,
+ &dev_attr_20.attr,
+ &dev_attr_21.attr,
+ &dev_attr_22.attr,
+ &dev_attr_23.attr,
+ &dev_attr_24.attr,
+ &dev_attr_25.attr,
+ &dev_attr_26.attr,
+ &dev_attr_27.attr,
+ &dev_attr_28.attr,
+ &dev_attr_29.attr,
+ &dev_attr_30.attr,
+ &dev_attr_31.attr,
+ &dev_attr_32.attr,
+ &dev_attr_33.attr,
+ &dev_attr_34.attr,
+ &dev_attr_35.attr,
+ &dev_attr_36.attr,
+ &dev_attr_37.attr,
+ &dev_attr_38.attr,
+ &dev_attr_39.attr,
+ &dev_attr_40.attr,
+ &dev_attr_41.attr,
+ &dev_attr_42.attr,
+ &dev_attr_43.attr,
+ &dev_attr_44.attr,
+ &dev_attr_45.attr,
+ &dev_attr_46.attr,
+ &dev_attr_47.attr,
+ &dev_attr_48.attr,
+ &dev_attr_49.attr,
+ &dev_attr_50.attr,
+ &dev_attr_51.attr,
+ &dev_attr_52.attr,
+ &dev_attr_53.attr,
+ &dev_attr_54.attr,
+ &dev_attr_55.attr,
+ &dev_attr_56.attr,
+ &dev_attr_57.attr,
+ &dev_attr_58.attr,
+ &dev_attr_59.attr,
+ &dev_attr_60.attr,
+ &dev_attr_61.attr,
+ &dev_attr_62.attr,
+ &dev_attr_63.attr,
+ NULL
+};
+
+static struct attribute *pme_dev_attributes[] = {
+ &dev_attr_efqc_int.attr,
+ &dev_attr_sw_db.attr,
+ &dev_attr_dmcr.attr,
+ &dev_attr_smcr.attr,
+ &dev_attr_famcr.attr,
+ &dev_attr_kvlts.attr,
+ &dev_attr_max_chain_length.attr,
+ &dev_attr_pattern_range_counter_idx.attr,
+ &dev_attr_pattern_range_counter_mask.attr,
+ &dev_attr_max_allowed_test_line_per_pattern.attr,
+ &dev_attr_max_pdsr_index.attr,
+ &dev_attr_max_pattern_matches_per_sui.attr,
+ &dev_attr_max_pattern_evaluations_per_sui.attr,
+ &dev_attr_report_length_limit.attr,
+ &dev_attr_end_of_simple_sui_report.attr,
+ &dev_attr_aim.attr,
+ &dev_attr_sre_context_size.attr,
+ &dev_attr_sre_rule_num.attr,
+ &dev_attr_sre_session_ctx_num.attr,
+ &dev_attr_end_of_sui_reaction_ptr.attr,
+ &dev_attr_sre_pscl.attr,
+ &dev_attr_sre_max_block_num.attr,
+ &dev_attr_sre_max_instruction_limit.attr,
+ &dev_attr_sre_max_index_size.attr,
+ &dev_attr_sre_max_offset_ctrl.attr,
+ &dev_attr_src_id.attr,
+ &dev_attr_liodnr.attr,
+ &dev_attr_rev1.attr,
+ &dev_attr_rev2.attr,
+ &dev_attr_isr.attr,
+ &dev_attr_ecr0.attr,
+ &dev_attr_ecr1.attr,
+ &dev_attr_esr.attr,
+ &dev_attr_pmstat.attr,
+ &dev_attr_pehd.attr,
+ &dev_attr_ecc1bes.attr,
+ &dev_attr_ecc2bes.attr,
+ &dev_attr_eccaddr.attr,
+ &dev_attr_ecccode.attr,
+ &dev_attr_miace.attr,
+ &dev_attr_miacr.attr,
+ &dev_attr_cdcr.attr,
+ &dev_attr_pmtr.attr,
+ &dev_attr_faconf.attr,
+ &dev_attr_pdsrbah.attr,
+ &dev_attr_pdsrbal.attr,
+ &dev_attr_scbarh.attr,
+ &dev_attr_scbarl.attr,
+ NULL
+};
+
+static struct attribute *pme_dev_stats_counter_attributes[] = {
+ &dev_attr_trunci.attr,
+ &dev_attr_rbc.attr,
+ &dev_attr_tbt0ecc1ec.attr,
+ &dev_attr_tbt1ecc1ec.attr,
+ &dev_attr_vlt0ecc1ec.attr,
+ &dev_attr_vlt1ecc1ec.attr,
+ &dev_attr_cmecc1ec.attr,
+ &dev_attr_dxcmecc1ec.attr,
+ &dev_attr_dxemecc1ec.attr,
+ &dev_attr_stnib.attr,
+ &dev_attr_stnis.attr,
+ &dev_attr_stnth1.attr,
+ &dev_attr_stnth2.attr,
+ &dev_attr_stnthv.attr,
+ &dev_attr_stnths.attr,
+ &dev_attr_stnch.attr,
+ &dev_attr_stnpm.attr,
+ &dev_attr_stns1m.attr,
+ &dev_attr_stnpmr.attr,
+ &dev_attr_stndsr.attr,
+ &dev_attr_stnesr.attr,
+ &dev_attr_stns1r.attr,
+ &dev_attr_stnob.attr,
+ &dev_attr_mia_byc.attr,
+ &dev_attr_mia_blc.attr,
+ NULL
+};
+
+static struct attribute *pme_dev_stats_ctrl_attributes[] = {
+ &dev_attr_update_interval.attr,
+ &dev_attr_tbt0ecc1th.attr,
+ &dev_attr_tbt1ecc1th.attr,
+ &dev_attr_vlt0ecc1th.attr,
+ &dev_attr_vlt1ecc1th.attr,
+ &dev_attr_cmecc1th.attr,
+ &dev_attr_dxcmecc1th.attr,
+ &dev_attr_dxemecc1th.attr,
+ NULL
+};
+
+/* root level */
+static const struct attribute_group pme_dev_attr_grp = {
+ .name = NULL, /* put in device directory */
+ .attrs = pme_dev_attributes
+};
+
+/* root/bsc */
+static struct attribute_group pme_dev_bsc_attr_grp = {
+ .name = "bsc",
+ .attrs = pme_dev_bsc_attributes
+};
+
+/* root/stats */
+static struct attribute_group pme_dev_stats_counters_attr_grp = {
+ .name = "stats",
+ .attrs = pme_dev_stats_counter_attributes
+};
+
+/* root/stats_ctrl */
+static struct attribute_group pme_dev_stats_ctrl_attr_grp = {
+ .name = "stats_ctrl",
+ .attrs = pme_dev_stats_ctrl_attributes
+};
+
+
+int pme2_create_sysfs_dev_files(struct platform_device *ofdev)
+{
+ int ret;
+
+ ret = sysfs_create_group(&ofdev->dev.kobj, &pme_dev_attr_grp);
+ if (ret)
+ goto done;
+ ret = sysfs_create_group(&ofdev->dev.kobj, &pme_dev_bsc_attr_grp);
+ if (ret)
+ goto del_group_1;
+ ret = sysfs_create_group(&ofdev->dev.kobj, &pme_dev_stats_counters_attr_grp);
+ if (ret)
+ goto del_group_2;
+ ret = sysfs_create_group(&ofdev->dev.kobj, &pme_dev_stats_ctrl_attr_grp);
+ if (ret)
+ goto del_group_3;
+ goto done;
+del_group_3:
+ sysfs_remove_group(&ofdev->dev.kobj, &pme_dev_stats_counters_attr_grp);
+del_group_2:
+ sysfs_remove_group(&ofdev->dev.kobj, &pme_dev_bsc_attr_grp);
+del_group_1:
+ sysfs_remove_group(&ofdev->dev.kobj, &pme_dev_attr_grp);
+done:
+ if (ret)
+ dev_err(&ofdev->dev,
+ "Cannot create dev attributes ret=%d\n", ret);
+ return ret;
+}
+
+void pme2_remove_sysfs_dev_files(struct platform_device *ofdev)
+{
+ sysfs_remove_group(&ofdev->dev.kobj, &pme_dev_stats_ctrl_attr_grp);
+ sysfs_remove_group(&ofdev->dev.kobj, &pme_dev_stats_counters_attr_grp);
+ sysfs_remove_group(&ofdev->dev.kobj, &pme_dev_bsc_attr_grp);
+ sysfs_remove_group(&ofdev->dev.kobj, &pme_dev_attr_grp);
+}
+
+
new file mode 100644
@@ -0,0 +1,74 @@
+/* Copyright 2008-2011 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 "pme2_sys.h"
+
+static inline void __hexdump(unsigned long start, unsigned long end,
+ unsigned long p, size_t sz, const unsigned char *c)
+{
+ while (start < end) {
+ unsigned int pos = 0;
+ char buf[64];
+ int nl = 0;
+ pos += sprintf(buf + pos, "%08lx: ", start);
+ do {
+ if ((start < p) || (start >= (p + sz)))
+ pos += sprintf(buf + pos, "..");
+ else
+ pos += sprintf(buf + pos, "%02x", *(c++));
+ if (!(++start & 15)) {
+ buf[pos++] = '\n';
+ nl = 1;
+ } else {
+ nl = 0;
+ if(!(start & 1))
+ buf[pos++] = ' ';
+ if(!(start & 3))
+ buf[pos++] = ' ';
+ }
+ } while (start & 15);
+ if (!nl)
+ buf[pos++] = '\n';
+ buf[pos] = '\0';
+ pr_info("%s", buf);
+ }
+}
+static inline void hexdump(const void *ptr, size_t sz)
+{
+ unsigned long p = (unsigned long)ptr;
+ unsigned long start = p & ~(unsigned long)15;
+ unsigned long end = (p + sz + 15) & ~(unsigned long)15;
+ const unsigned char *c = ptr;
+ __hexdump(start, end, p, sz, c);
+}
+
+int pme2_sample_db(void);
+int pme2_clear_sample_db(void);
new file mode 100644
@@ -0,0 +1,238 @@
+/* Copyright 2008-2011 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 "pme2_test.h"
+
+MODULE_AUTHOR("Geoff Thorpe");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL PME2 (p4080) high-level self-test");
+
+/* Default Flow Context State */
+static u8 fl_ctx_exp[]={
+ 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
+};
+
+void scan_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_token *token)
+{
+ hexdump(fd, sizeof(*fd));
+}
+
+struct ctrl_op {
+ struct pme_ctx_ctrl_token ctx_ctr;
+ struct completion cb_done;
+ enum pme_status cmd_status;
+ u8 res_flag;
+};
+
+static void ctrl_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_ctrl_token *token)
+{
+ struct ctrl_op *ctrl = (struct ctrl_op *)token;
+ pr_info("pme2_test_high: ctrl_cb() invoked, fd;!\n");
+ ctrl->cmd_status = pme_fd_res_status(fd);
+ ctrl->res_flag = pme_fd_res_flags(fd);
+ hexdump(fd, sizeof(*fd));
+ complete(&ctrl->cb_done);
+}
+
+
+#define POST_CTRL(val) \
+do { \
+ if (ret) \
+ val = -1;\
+ else if (pme_ctx_is_dead(&ctx))\
+ val = -1;\
+ else if (ctx_ctrl.cmd_status)\
+ val = -1;\
+ else if (ctx_ctrl.res_flag)\
+ val = -1;\
+} while (0)
+
+void pme2_test_high(void)
+{
+ int post_ctrl = 0;
+ struct pme_flow flow;
+ struct qm_fqd_stashing stashing;
+ struct pme_ctx ctx = {
+ .cb = scan_cb
+ };
+ int ret;
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .cmd_status = 0,
+ .res_flag = 0
+ };
+ struct cpumask backup_mask = current->cpus_allowed;
+ struct cpumask new_mask = *qman_affine_cpus();
+
+ pr_info("PME2: high-level test starting\n");
+
+ cpumask_and(&new_mask, &new_mask, bman_affine_cpus());
+ ret = set_cpus_allowed_ptr(current, &new_mask);
+ if (ret) {
+ post_ctrl = -1;
+ pr_info("PME2: test high: can't set cpumask\n");
+ goto done;
+ }
+
+ pme_sw_flow_init(&flow);
+ init_completion(&ctx_ctrl.cb_done);
+ ret = pme_ctx_init(&ctx, PME_CTX_FLAG_LOCAL, 0, 4, 4, 0, NULL);
+ POST_CTRL(post_ctrl);
+ if (post_ctrl)
+ goto restore_mask;
+
+ /* enable the context */
+ pme_ctx_enable(&ctx);
+ pr_info("PME2: pme_ctx_enable done\n");
+ ret = pme_ctx_ctrl_update_flow(&ctx, PME_CTX_OP_WAIT | PME_CMD_FCW_ALL,
+ &flow, &ctx_ctrl.ctx_ctr);
+ pr_info("PME2: pme_ctx_ctrl_update_flow done\n");
+ wait_for_completion(&ctx_ctrl.cb_done);
+ POST_CTRL(post_ctrl);
+ if (post_ctrl)
+ goto disable_ctx;
+ /* read back flow settings */
+ ret = pme_ctx_ctrl_read_flow(&ctx, PME_CTX_OP_WAIT, &flow,
+ &ctx_ctrl.ctx_ctr);
+ pr_info("PME2: pme_ctx_ctrl_read_flow done\n");
+ wait_for_completion(&ctx_ctrl.cb_done);
+ POST_CTRL(post_ctrl);
+ if (post_ctrl)
+ goto disable_ctx;
+ if (memcmp(&flow, fl_ctx_exp, sizeof(flow))) {
+ pr_info("Default Flow Context Read FAIL\n");
+ pr_info("Expected:\n");
+ hexdump(fl_ctx_exp, sizeof(fl_ctx_exp));
+ pr_info("Received:\n");
+ hexdump(&flow, sizeof(flow));
+ post_ctrl = -1;
+ goto disable_ctx;
+ } else
+ pr_info("Default Flow Context Read OK\n");
+ /* start a NOP */
+ ret = pme_ctx_ctrl_nop(&ctx, 0, &ctx_ctrl.ctx_ctr);
+ pr_info("PME2: pme_ctx_ctrl_nop done\n");
+ wait_for_completion(&ctx_ctrl.cb_done);
+ POST_CTRL(post_ctrl);
+ if (post_ctrl)
+ goto disable_ctx;
+ /* start an update to add residue to the context */
+ flow.ren = 1;
+ ret = pme_ctx_ctrl_update_flow(&ctx, PME_CTX_OP_WAIT | PME_CMD_FCW_RES,
+ &flow, &ctx_ctrl.ctx_ctr);
+ pr_info("PME2: pme_ctx_ctrl_update_flow done\n");
+ wait_for_completion(&ctx_ctrl.cb_done);
+ POST_CTRL(post_ctrl);
+ if (post_ctrl)
+ goto disable_ctx;
+ /* start a blocking disable */
+ ret = pme_ctx_disable(&ctx, PME_CTX_OP_WAIT, &ctx_ctrl.ctx_ctr);
+ if (ret < 1) {
+ post_ctrl = -1;
+ goto finish_ctx;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+ /* do some reconfiguration */
+ ret = pme_ctx_reconfigure_tx(&ctx, 63, 7);
+ if (ret) {
+ post_ctrl = -1;
+ goto finish_ctx;
+ }
+ stashing.exclusive = 0;
+ stashing.annotation_cl = 0;
+ stashing.data_cl = 2;
+ stashing.context_cl = 2;
+ ret = pme_ctx_reconfigure_rx(&ctx, 7, 0, &stashing);
+ if (ret) {
+ post_ctrl = -1;
+ goto finish_ctx;
+ }
+ /* reenable */
+ ret = pme_ctx_enable(&ctx);
+ if (ret) {
+ post_ctrl = -1;
+ goto finish_ctx;
+ }
+ /* read back flow settings */
+ ret = pme_ctx_ctrl_read_flow(&ctx,
+ PME_CTX_OP_WAIT | PME_CTX_OP_WAIT_INT | PME_CMD_FCW_RES, &flow,
+ &ctx_ctrl.ctx_ctr);
+ pr_info("PME2: pme_ctx_ctrl_read_flow done\n");
+ wait_for_completion(&ctx_ctrl.cb_done);
+ POST_CTRL(post_ctrl);
+ if (post_ctrl)
+ goto disable_ctx;
+ /* blocking NOP */
+ ret = pme_ctx_ctrl_nop(&ctx, PME_CTX_OP_WAIT | PME_CTX_OP_WAIT_INT,
+ &ctx_ctrl.ctx_ctr);
+ pr_info("PME2: pme_ctx_ctrl_nop done\n");
+ wait_for_completion(&ctx_ctrl.cb_done);
+ POST_CTRL(post_ctrl);
+ /* Disable, and done */
+disable_ctx:
+ ret = pme_ctx_disable(&ctx, PME_CTX_OP_WAIT, &ctx_ctrl.ctx_ctr);
+ BUG_ON(ret < 1);
+ wait_for_completion(&ctx_ctrl.cb_done);
+finish_ctx:
+ pme_ctx_finish(&ctx);
+restore_mask:
+ ret = set_cpus_allowed_ptr(current, &backup_mask);
+ if (ret) {
+ pr_err("PME2 test high: can't restore cpumask");
+ post_ctrl = -1;
+ }
+done:
+ if (post_ctrl)
+ pr_info("PME2: high-level test failed\n");
+ else
+ pr_info("PME2: high-level test passed\n");
+}
+
+static int pme2_test_high_init(void)
+{
+ int big_loop = 2;
+ while (big_loop--)
+ pme2_test_high();
+ return 0;
+}
+
+static void pme2_test_high_exit(void)
+{
+}
+
+module_init(pme2_test_high_init);
+module_exit(pme2_test_high_exit);
+
new file mode 100644
@@ -0,0 +1,653 @@
+/* Copyright 2009-2011 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 "pme2_test.h"
+
+enum scan_ctrl_mode {
+ no_scan = 0,
+ do_scan = 1,
+};
+
+enum db_ctrl_mode {
+ create_destroy = 0,
+ create = 1,
+ destroy = 2,
+ nothing = 3
+};
+
+MODULE_AUTHOR("Jeffrey Ladouceur");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("PME scan testing");
+
+static enum db_ctrl_mode db_ctrl;
+module_param(db_ctrl, uint, 0644);
+MODULE_PARM_DESC(db_ctrl, "PME Database control");
+
+static enum scan_ctrl_mode scan_ctrl = 1;
+module_param(scan_ctrl, uint, 0644);
+MODULE_PARM_DESC(scan_ctrl, "Scan control");
+
+static u8 scan_result_direct_mode_inc_mode[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00
+};
+
+static u8 fl_ctx_exp[] = {
+ 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
+};
+
+/* same again with 'sos' bit cleared */
+static u8 fl_ctx_exp_post_scan[] = {
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
+};
+
+struct scan_ctx {
+ struct pme_ctx base_ctx;
+ struct qm_fd result_fd;
+};
+
+struct ctrl_op {
+ struct pme_ctx_ctrl_token ctx_ctr;
+ struct completion cb_done;
+ enum pme_status cmd_status;
+ u8 res_flag;
+};
+
+static void ctrl_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_ctrl_token *token)
+{
+ struct ctrl_op *ctrl = (struct ctrl_op *)token;
+ ctrl->cmd_status = pme_fd_res_status(fd);
+ ctrl->res_flag = pme_fd_res_flags(fd) & PME_STATUS_UNRELIABLE;
+ /* hexdump(fd, sizeof(*fd)); */
+ complete(&ctrl->cb_done);
+}
+
+static DECLARE_COMPLETION(scan_comp);
+
+static void scan_cb(struct pme_ctx *ctx, const struct qm_fd *fd,
+ struct pme_ctx_token *ctx_token)
+{
+ struct scan_ctx *my_ctx = (struct scan_ctx *)ctx;
+ memcpy(&my_ctx->result_fd, fd, sizeof(*fd));
+ complete(&scan_comp);
+}
+
+#ifdef CONFIG_FSL_PME2_TEST_SCAN_WITH_BPID
+
+static struct bman_pool *pool;
+static u32 pme_bpid;
+static void *bman_buffers_virt_base;
+static dma_addr_t bman_buffers_phys_base;
+
+static void release_buffer(dma_addr_t addr)
+{
+ struct bm_buffer bufs_in;
+ bm_buffer_set64(&bufs_in, addr);
+ if (bman_release(pool, &bufs_in, 1, BMAN_RELEASE_FLAG_WAIT))
+ panic("bman_release() failed\n");
+}
+
+static void empty_buffer(void)
+{
+ struct bm_buffer bufs_in;
+ int ret;
+
+ do {
+ ret = bman_acquire(pool, &bufs_in, 1, 0);
+ } while (!ret);
+}
+#endif /*CONFIG_FSL_PME2_TEST_SCAN_WITH_BPID*/
+
+static int scan_test_direct(int trunc, int use_bp)
+{
+ struct scan_ctx a_scan_ctx = {
+ .base_ctx = {
+ .cb = scan_cb
+ }
+ };
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .cmd_status = 0,
+ .res_flag = 0
+ };
+ struct qm_fd fd;
+ struct qm_sg_entry sg_table[2];
+ int ret;
+ enum pme_status status;
+ struct pme_ctx_token token;
+ u8 *scan_result;
+ u32 scan_result_size;
+ u8 scan_data[] = {
+ 0x41, 0x42, 0x43, 0x44, 0x45
+ };
+ u8 result_data[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00
+ };
+
+ init_completion(&ctx_ctrl.cb_done);
+ scan_result = scan_result_direct_mode_inc_mode;
+ scan_result_size = sizeof(scan_result_direct_mode_inc_mode);
+
+ ret = pme_ctx_init(&a_scan_ctx.base_ctx,
+ PME_CTX_FLAG_DIRECT | PME_CTX_FLAG_LOCAL,
+ 0, 4, 4, 0, NULL);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ return ret;
+ }
+ /* enable the context */
+ ret = pme_ctx_enable(&a_scan_ctx.base_ctx);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto ctx_finish;
+ }
+
+ /* Do a pre-built output, scan with match test */
+ /* Build a frame descriptor */
+ memset(&fd, 0, sizeof(struct qm_fd));
+ memset(&sg_table, 0, sizeof(sg_table));
+
+ if (trunc) {
+ fd.length20 = sizeof(scan_data);
+ qm_fd_addr_set64(&fd, pme_map(scan_data));
+ } else {
+ /* build the result */
+ qm_sg_entry_set64(&sg_table[0], pme_map(result_data));
+ sg_table[0].length = sizeof(result_data);
+ qm_sg_entry_set64(&sg_table[1], pme_map(scan_data));
+ sg_table[1].length = sizeof(scan_data);
+ sg_table[1].final = 1;
+ fd._format2 = qm_fd_compound;
+ qm_fd_addr_set64(&fd, pme_map(sg_table));
+ }
+
+ ret = pme_ctx_scan(&a_scan_ctx.base_ctx, 0, &fd,
+ PME_SCAN_ARGS(PME_CMD_SCAN_SR | PME_CMD_SCAN_E, 0, 0xff00),
+ &token);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto ctx_disable;
+ }
+ wait_for_completion(&scan_comp);
+
+ status = pme_fd_res_status(&a_scan_ctx.result_fd);
+ if (status) {
+ pr_err("pme scan test failed 0x%x\n", status);
+ goto ctx_disable;
+ }
+ if (trunc) {
+ int res_flag = pme_fd_res_flags(&a_scan_ctx.result_fd);
+ /* Check the response...expect truncation bit to be set */
+ if (!(res_flag & PME_STATUS_TRUNCATED)) {
+ pr_err("pme scan test failed, expected truncation\n");
+ goto ctx_disable;
+ }
+ } else {
+ if (memcmp(scan_result, result_data, scan_result_size) != 0) {
+ pr_err("pme scan test result not expected\n");
+ hexdump(scan_result, scan_result_size);
+ pr_err("Received...\n");
+ hexdump(result_data, sizeof(result_data));
+ goto ctx_disable;
+ }
+ }
+
+ ret = pme_ctx_disable(&a_scan_ctx.base_ctx, PME_CTX_OP_WAIT, NULL);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto ctx_finish;
+ }
+ if (!use_bp) {
+ pme_ctx_finish(&a_scan_ctx.base_ctx);
+ return 0;
+ }
+ /* use buffer pool */
+ /* Check with bman */
+ /* reconfigure */
+
+#ifdef CONFIG_FSL_PME2_TEST_SCAN_WITH_BPID
+ ret = pme_ctx_reconfigure_tx(&a_scan_ctx.base_ctx, pme_bpid, 5);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto ctx_finish;
+ }
+ ret = pme_ctx_enable(&a_scan_ctx.base_ctx);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto ctx_finish;
+ }
+ /* Do a pre-built output, scan with match test */
+ /* Build a frame descriptor */
+ memset(&fd, 0, sizeof(struct qm_fd));
+ memset(&sg_table, 0, sizeof(sg_table));
+
+ /* build the result */
+ /* result is all zero...use bman */
+ qm_sg_entry_set64(&sg_table[1], pme_map(scan_data));
+ sg_table[1].length = sizeof(scan_data);
+ sg_table[1].final = 1;
+
+ fd._format2 = qm_fd_compound;
+ qm_fd_addr_set64(&fd, pme_map(sg_table));
+
+ ret = pme_ctx_scan(&a_scan_ctx.base_ctx, 0, &fd,
+ PME_SCAN_ARGS(PME_CMD_SCAN_SR | PME_CMD_SCAN_E, 0, 0xff00),
+ &token);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto ctx_disable;
+ }
+ wait_for_completion(&scan_comp);
+
+ status = pme_fd_res_status(&a_scan_ctx.result_fd);
+ if (status) {
+ pr_err("pme scan test failed 0x%x\n", status);
+ goto ctx_disable;
+ }
+ /* sg result should point to bman buffer */
+ if (!qm_sg_entry_get64(&sg_table[0])) {
+ pr_err("pme scan test failed, sg result not bman buffer\n");
+ goto ctx_disable;
+ }
+ if (memcmp(scan_result, bman_buffers_virt_base, scan_result_size)
+ != 0) {
+ pr_err("pme scan test not expected, Expected\n");
+ hexdump(scan_result, scan_result_size);
+ pr_err("Received...\n");
+ hexdump(bman_buffers_virt_base, scan_result_size);
+ release_buffer(qm_sg_entry_get64(&sg_table[0]));
+ goto ctx_disable;
+ }
+ release_buffer(qm_sg_entry_get64(&sg_table[0]));
+ ret = pme_ctx_disable(&a_scan_ctx.base_ctx, PME_CTX_OP_WAIT, NULL);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto ctx_finish;
+ }
+ pme_ctx_finish(&a_scan_ctx.base_ctx);
+ return 0;
+#endif
+
+/* failure path */
+ctx_disable:
+ ret = pme_ctx_disable(&a_scan_ctx.base_ctx, PME_CTX_OP_WAIT, NULL);
+ctx_finish:
+ pme_ctx_finish(&a_scan_ctx.base_ctx);
+ return (!ret) ? -EINVAL : ret;
+}
+
+static int scan_test_flow(void)
+{
+ struct pme_flow flow;
+ struct pme_flow rb_flow;
+ struct scan_ctx a_scan_ctx = {
+ .base_ctx = {
+ .cb = scan_cb
+ }
+ };
+ struct ctrl_op ctx_ctrl = {
+ .ctx_ctr.cb = ctrl_cb,
+ .cmd_status = 0,
+ .res_flag = 0
+ };
+ struct qm_fd fd;
+ struct qm_sg_entry sg_table[2];
+ int ret;
+ enum pme_status status;
+ struct pme_ctx_token token;
+ u8 *scan_result;
+ u32 scan_result_size;
+ u8 scan_data[] = {
+ 0x41, 0x42, 0x43, 0x44, 0x45
+ };
+ u8 result_data[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00
+ };
+
+ pme_sw_flow_init(&flow);
+ init_completion(&ctx_ctrl.cb_done);
+ scan_result = scan_result_direct_mode_inc_mode;
+ scan_result_size = sizeof(scan_result_direct_mode_inc_mode);
+
+ ret = pme_ctx_init(&a_scan_ctx.base_ctx,
+ PME_CTX_FLAG_LOCAL, 0, 4, 4, 0, NULL);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ return ret;
+ }
+ /* enable the context */
+ ret = pme_ctx_enable(&a_scan_ctx.base_ctx);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_finish;
+ }
+ ret = pme_ctx_ctrl_update_flow(&a_scan_ctx.base_ctx,
+ PME_CTX_OP_WAIT | PME_CMD_FCW_ALL, &flow, &ctx_ctrl.ctx_ctr);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_disable;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.cmd_status || ctx_ctrl.res_flag) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_disable;
+ }
+ /* read back flow settings */
+ ret = pme_ctx_ctrl_read_flow(&a_scan_ctx.base_ctx,
+ PME_CTX_OP_WAIT, &rb_flow, &ctx_ctrl.ctx_ctr);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_disable;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.cmd_status || ctx_ctrl.res_flag) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_disable;
+ }
+ if (memcmp(&rb_flow, fl_ctx_exp, sizeof(rb_flow)) != 0) {
+ pr_err("pme scan test Flow Context Read FAIL\n");
+ pr_err("Expected\n");
+ hexdump(fl_ctx_exp, sizeof(fl_ctx_exp));
+ pr_err("Received...\n");
+ hexdump(&rb_flow, sizeof(rb_flow));
+ goto flow_ctx_disable;
+ }
+
+ /* Do a pre-built output, scan with match test */
+ /* Build a frame descriptor */
+ memset(&fd, 0, sizeof(struct qm_fd));
+ memset(&sg_table, 0, sizeof(sg_table));
+
+ /* build the result */
+ qm_sg_entry_set64(&sg_table[0], pme_map(result_data));
+ sg_table[0].length = sizeof(result_data);
+ qm_sg_entry_set64(&sg_table[1], pme_map(scan_data));
+ sg_table[1].length = sizeof(scan_data);
+ sg_table[1].final = 1;
+
+ fd._format2 = qm_fd_compound;
+ qm_fd_addr_set64(&fd, pme_map(sg_table));
+
+ ret = pme_ctx_scan(&a_scan_ctx.base_ctx, 0, &fd,
+ PME_SCAN_ARGS(PME_CMD_SCAN_SR | PME_CMD_SCAN_E, 0, 0xff00),
+ &token);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_disable;
+ }
+ wait_for_completion(&scan_comp);
+
+ status = pme_fd_res_status(&a_scan_ctx.result_fd);
+ if (status) {
+ pr_err("pme scan test failed 0x%x\n", status);
+ goto flow_ctx_disable;
+ }
+
+ if (memcmp(scan_result, result_data, scan_result_size) != 0) {
+ pr_err("pme scan test result not expected\n");
+ hexdump(scan_result, scan_result_size);
+ pr_err("Received...\n");
+ hexdump(result_data, sizeof(result_data));
+ goto flow_ctx_disable;
+ }
+
+ /* read back flow settings */
+ ret = pme_ctx_ctrl_read_flow(&a_scan_ctx.base_ctx,
+ PME_CTX_OP_WAIT, &rb_flow, &ctx_ctrl.ctx_ctr);
+ if (ret) {
+ pr_err("pme scan test failed 0x%x\n", status);
+ goto flow_ctx_disable;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.cmd_status || ctx_ctrl.res_flag) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_disable;
+ }
+ if (memcmp(&rb_flow, fl_ctx_exp_post_scan, sizeof(rb_flow)) != 0) {
+ pr_err("pme scan test Flow Context Read FAIL\n");
+ pr_err("Expected\n");
+ hexdump(fl_ctx_exp_post_scan, sizeof(fl_ctx_exp_post_scan));
+ pr_err("Received\n");
+ hexdump(&rb_flow, sizeof(rb_flow));
+ goto flow_ctx_disable;
+ }
+
+ /* Test truncation test */
+ /* Build a frame descriptor */
+ memset(&fd, 0, sizeof(struct qm_fd));
+
+ fd.length20 = sizeof(scan_data);
+ qm_fd_addr_set64(&fd, pme_map(scan_data));
+
+ ret = pme_ctx_scan(&a_scan_ctx.base_ctx, 0, &fd,
+ PME_SCAN_ARGS(PME_CMD_SCAN_SR | PME_CMD_SCAN_E, 0, 0xff00),
+ &token);
+ if (ret) {
+ pr_err("pme scan test failed 0x%x\n", status);
+ goto flow_ctx_disable;
+ }
+ wait_for_completion(&scan_comp);
+
+ status = pme_fd_res_status(&a_scan_ctx.result_fd);
+ if (status) {
+ pr_err("pme scan test failed 0x%x\n", status);
+ goto flow_ctx_disable;
+ }
+ /* Check the response...expect truncation bit to be set */
+ if (!(pme_fd_res_flags(&a_scan_ctx.result_fd) & PME_STATUS_TRUNCATED)) {
+ pr_err("st: Scan result failed...expected trunc\n");
+ goto flow_ctx_disable;
+ }
+
+ /* read back flow settings */
+ ret = pme_ctx_ctrl_read_flow(&a_scan_ctx.base_ctx,
+ PME_CTX_OP_WAIT, &rb_flow, &ctx_ctrl.ctx_ctr);
+ if (ret) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_disable;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+ if (ctx_ctrl.cmd_status || ctx_ctrl.res_flag) {
+ pr_err("pme scan test failed: 0x%x\n", ret);
+ goto flow_ctx_disable;
+ }
+ if (memcmp(&rb_flow, fl_ctx_exp_post_scan, sizeof(rb_flow)) != 0) {
+ pr_err("pme scan test Flow Context Read FAIL\n");
+ pr_err("Expected\n");
+ hexdump(fl_ctx_exp_post_scan, sizeof(fl_ctx_exp_post_scan));
+ pr_err("Received\n");
+ hexdump(&rb_flow, sizeof(rb_flow));
+ goto flow_ctx_disable;
+ }
+
+ /* Disable */
+ ret = pme_ctx_disable(&a_scan_ctx.base_ctx, PME_CTX_OP_WAIT,
+ &ctx_ctrl.ctx_ctr);
+ if (ret < 1) {
+ pr_err("pme scan test failed 0x%x\n", ret);
+ goto flow_ctx_finish;
+ }
+ wait_for_completion(&ctx_ctrl.cb_done);
+ pme_ctx_finish(&a_scan_ctx.base_ctx);
+ return 0;
+ /* error path */
+/* failure path */
+flow_ctx_disable:
+ ret = pme_ctx_disable(&a_scan_ctx.base_ctx, PME_CTX_OP_WAIT, NULL);
+flow_ctx_finish:
+ pme_ctx_finish(&a_scan_ctx.base_ctx);
+ return (!ret) ? -EINVAL : ret;
+}
+
+void pme2_test_scan(void)
+{
+ int ret;
+
+ ret = scan_test_direct(0, 0);
+ if (ret)
+ goto done;
+ ret = scan_test_direct(1, 0);
+ if (ret)
+ goto done;
+#ifdef CONFIG_FSL_PME2_TEST_SCAN_WITH_BPID
+ ret = scan_test_direct(0, 1);
+ if (ret)
+ goto done;
+#endif
+ ret = scan_test_flow();
+done:
+ if (ret)
+ pr_info("pme scan test FAILED 0x%x\n", ret);
+ else
+ pr_info("pme Scan Test Passed\n");
+}
+
+static int setup_buffer_pool(void)
+{
+#ifdef CONFIG_FSL_PME2_TEST_SCAN_WITH_BPID
+ u32 bpid_size = CONFIG_FSL_PME2_TEST_SCAN_WITH_BPID_SIZE;
+ struct bman_pool_params pparams = {
+ .flags = BMAN_POOL_FLAG_DYNAMIC_BPID,
+ .thresholds = {
+ 0,
+ 0,
+ 0,
+ 0
+ }
+ };
+
+ if (!pme2_have_control()) {
+ pr_err("pme scan test: Not the ctrl-plane\n");
+ return -EINVAL;
+ }
+ pool = bman_new_pool(&pparams);
+ if (!pool) {
+ pr_err("pme scan test: can't get buffer pool\n");
+ return -EINVAL;
+ }
+ pme_bpid = bman_get_params(pool)->bpid;
+ bman_buffers_virt_base = kmalloc(1<<(bpid_size+5), GFP_KERNEL);
+ bman_buffers_phys_base = pme_map(bman_buffers_virt_base);
+ if (pme_map_error(bman_buffers_phys_base)) {
+ pr_info("pme scan test: pme_map_error\n");
+ bman_free_pool(pool);
+ kfree(bman_buffers_virt_base);
+ return -ENODEV;
+ }
+ release_buffer(bman_buffers_phys_base);
+ /* Configure the buffer pool */
+ pme_attr_set(pme_attr_bsc(pme_bpid), bpid_size);
+ /* realease to the specified buffer pool */
+ return 0;
+#endif
+ return 0;
+}
+
+static int teardown_buffer_pool(void)
+{
+#ifdef CONFIG_FSL_PME2_TEST_SCAN_WITH_BPID
+ pme_attr_set(pme_attr_bsc(pme_bpid), 0);
+ empty_buffer();
+ bman_free_pool(pool);
+ kfree(bman_buffers_virt_base);
+#endif
+ return 0;
+}
+
+static int pme2_test_scan_init(void)
+{
+ int big_loop = 2;
+ int ret = 0;
+ struct cpumask backup_mask = current->cpus_allowed;
+ struct cpumask new_mask = *qman_affine_cpus();
+
+ cpumask_and(&new_mask, &new_mask, bman_affine_cpus());
+ ret = set_cpus_allowed_ptr(current, &new_mask);
+ if (ret) {
+ pr_info("pme scan test: can't set cpumask\n");
+ goto done_all;
+ }
+
+ ret = setup_buffer_pool();
+ if (ret)
+ goto done_cpu_mask;
+
+ /* create sample database */
+ if (db_ctrl == create_destroy || db_ctrl == create) {
+ if (!pme2_have_control()) {
+ pr_err("pme scan test: Not the ctrl-plane\n");
+ ret = -EINVAL;
+ goto done_scan;
+ }
+ if (pme2_sample_db()) {
+ pr_err("pme scan test: error creating db\n");
+ goto done_scan;
+ }
+ }
+
+ if (scan_ctrl == do_scan) {
+ while (big_loop--)
+ pme2_test_scan();
+ }
+
+ if (db_ctrl == create_destroy || db_ctrl == destroy) {
+ /* Clear database */
+ if (pme2_clear_sample_db())
+ pr_err("pme scan test: error clearing db\n");
+ }
+
+done_scan:
+ teardown_buffer_pool();
+done_cpu_mask:
+ ret = set_cpus_allowed_ptr(current, &backup_mask);
+ if (ret)
+ pr_err("PME2 test high: can't restore cpumask");
+done_all:
+ return ret;
+}
+
+static void pme2_test_scan_exit(void)
+{
+}
+
+module_init(pme2_test_scan_init);
+module_exit(pme2_test_scan_exit);
new file mode 100644
@@ -0,0 +1,795 @@
+/* Copyright 2009-2011 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.
+ */
+
+#ifndef FSL_PME_H
+#define FSL_PME_H
+
+/* pme_fd_res_status() returns this enum */
+enum pme_status {
+ pme_status_ok = 0x00,
+ pme_status_kes_ccl = 0x40, /* KES Confidence Collision Limit */
+ pme_status_kes_cme = 0x41, /* KES Confidence Mask Error */
+ pme_status_dxe_ire = 0x48, /* DXE Invalid Repeat Error */
+ pme_status_dxe_tlse = 0x49, /* DXE Test Line Syntax Error */
+ pme_status_dxe_ile = 0x4b, /* DXE Instruction Limit Error */
+ pme_status_dxe_pdsrsore = 0x4c, /* DXE PDSR Space Out Range Error */
+ pme_status_dxe_soe = 0x4d, /* DXE Stack Overflow Error */
+ pme_status_dxe_alse = 0x4e, /* DXE Alternate Link Same Error */
+ pme_status_dxe_slse = 0x4f, /* DXE Subsequent Link Same Error */
+ pme_status_dxe_slre = 0x50, /* DXE Subsequent Link Reverse Error */
+ pme_status_dxe_itlb = 0x51, /* DXE Invalid Test Line Branch */
+ pme_status_dxe_cle = 0x52, /* DXE Compare Limit Exceeded */
+ pme_status_dxe_mle = 0x53, /* DXE Match Limit Exceeded */
+ pme_status_sre_irhbni = 0x59, /* SRE Invalid Reaction Head Block */
+ /* Number Instructions */
+ pme_status_sre_rl = 0x5a, /* SRE Reaction Limit */
+ pme_status_sre_pdsrsore = 0x5b, /* SRE PDSR Space Out Range Error */
+ pme_status_sre_score = 0x5c, /* SRE Session Context Out Range Error */
+ pme_status_sre_ctore = 0x5d, /* SRE Context Table Out Range Error */
+ pme_status_sre_il = 0x5e, /* SRE Instruction Limit */
+ pme_status_sre_iij = 0x5f, /* SRE Invalid Instruction Jump */
+ pme_status_sre_ise = 0x60, /* SRE Instruction Syntax Error */
+ pme_status_pmfa_pmtcce = 0x80, /* PMFA PCTCC Error */
+ pme_status_pmfa_fcwe = 0x90, /* PMFA Flow Context Write Command Error */
+ pme_status_pmfa_fcre = 0x91, /* PMFA Flow Context Read Command Error */
+ pme_status_pmfa_ume = 0x93, /* PMFA Unrecognized Mode Error */
+ pme_status_pmfa_uce = 0x94, /* PMFA Unrecognized Command Error */
+ pme_status_pmfa_ufe = 0x95, /* PMFA Unrecognized Frame Error */
+ pme_status_sre_csmre = 0xc0, /* SRE Context System Memory Read Error */
+ pme_status_sre_ismre = 0xc1, /* SRE Instruction System Memory Read */
+ /* Error */
+ pme_status_dxe_smre = 0xc2, /* DXE System Memory Read Error */
+ pme_status_pmfa_pmtccsmre = 0xc4, /* PMFA PMTCC System Memory Read */
+ /* Error */
+ pme_status_pmfa_csmre = 0xc5, /* PMFA Context System Memory Read */
+ /* Error */
+ pme_status_pmfa_dsmre = 0xc6, /* PMFA Data System Memory Read Error */
+ pme_status_kes_cmecce = 0xd2, /* KES Confidence Memory ECC Error */
+ pme_status_kes_2btmecce = 0xd4, /*KES 2-Byte Trigger Memory ECC Error */
+ pme_status_kes_vltmecce = 0xd5, /*KES Variable Length Trigger Memory */
+ /* ECC Error */
+ pme_status_pmfa_cmecce = 0xd7, /* PMFA Confidence Memory ECC Error */
+ pme_status_pmfa_2btmecce = 0xd9, /* PMFA 2-Byte Trigger Memory ECC */
+ /* Error */
+ pme_status_pmfa_vltmecce = 0xda, /* PMFA Variable Length Trigger */
+ /* Memory ECC Error */
+ pme_status_dxe_iemce = 0xdb, /* DXE Internal Examination Memory */
+ /* Collision Error */
+ pme_status_dxe_iemecce = 0xdc, /* DXE Internal Examination Memory */
+ /* ECC Error */
+ pme_status_dxe_icmecce = 0xdd, /* DXE Internal Context Memory ECC */
+ /* Error */
+ pme_status_sre_ctsmwe = 0xe0, /* SRE Context Table System Memory */
+ /* Write Error */
+ pme_status_pmfa_pmtccsmwe = 0xe7, /* PMFA PMTCC System Memory Write */
+ /* Error */
+ pme_status_pmfa_csmwe = 0xe8, /* PMFA Context System Memory Write */
+ /* Error */
+ pme_status_pmfa_dsmwe = 0xe9, /* PMFA Data System Memory Write Error */
+};
+
+/* pme_fd_res_flags() returns these flags */
+#define PME_STATUS_UNRELIABLE 0x80
+#define PME_STATUS_TRUNCATED 0x10
+#define PME_STATUS_MASK 0x90
+
+/**************/
+/* USER SPACE */
+/**************/
+
+#define PME_IOCTL_MAGIC 'p'
+
+/* Wrapper for a pointer and size. */
+struct pme_buffer {
+ void __user *data;
+ size_t size;
+};
+
+/***************/
+/* SCAN DEVICE */
+/***************/
+/* The /dev/pme_scan device creates a file-descriptor that uses scheduled FQs
+ * serviced by PME's datapath portal. This can only be used for scanning. */
+#define PME_DEV_SCAN_NODE "pme_scan"
+#define PME_DEV_SCAN_PATH "/dev/" PME_DEV_SCAN_NODE
+
+/* ioctls for 'scan' device */
+#define PMEIO_SETSCAN _IOW(PME_IOCTL_MAGIC, 0x06, struct pme_scan_params)
+#define PMEIO_GETSCAN _IOR(PME_IOCTL_MAGIC, 0x07, struct pme_scan_params)
+#define PMEIO_RESETSEQ _IO(PME_IOCTL_MAGIC, 0x08)
+#define PMEIO_RESETRES _IO(PME_IOCTL_MAGIC, 0x09)
+#define PMEIO_SCAN_W1 _IOW(PME_IOCTL_MAGIC, 0x0a, struct pme_scan_cmd)
+#define PMEIO_SCAN_Wn _IOWR(PME_IOCTL_MAGIC, 0x0b, struct pme_scan_cmds)
+#define PMEIO_SCAN_R1 _IOR(PME_IOCTL_MAGIC, 0x0c, struct pme_scan_result)
+#define PMEIO_SCAN_Rn _IOWR(PME_IOCTL_MAGIC, 0x0d, struct pme_scan_results)
+#define PMEIO_SCAN _IOWR(PME_IOCTL_MAGIC, 0x0e, struct pme_scan)
+/* The release_bufs ioctl takes as parameter a (void *) */
+#define PMEIO_RELEASE_BUFS _IOW(PME_IOCTL_MAGIC, 0x0f, void *)
+
+/* Parameters for PMEIO_SETSCAN and PMEIO_GETSCAN ioctl()s. This doesn't cover
+ * "sequence" fields ('soc' and 'seqnum'), they can only be influenced by flags
+ * passed to scan operations, or by PMEIO_RESETSEQ ioctl()s. */
+struct pme_scan_params {
+ __u32 flags; /* PME_SCAN_PARAMS_*** bitmask */
+ struct pme_scan_params_residue {
+ __u8 enable; /* boolean, residue enable */
+ __u8 length; /* read-only for GETSCAN, ignored for SETSCAN */
+ } residue;
+ struct pme_scan_params_sre {
+ __u32 sessionid; /* 27-bit */
+ __u8 verbose; /* 0-3 */
+ __u8 esee; /* boolean, End Of Sui Event Enable */
+ } sre;
+ struct pme_scan_params_dxe {
+ __u16 clim; /* compare limit */
+ __u16 mlim; /* match limit */
+ } dxe;
+ struct pme_scan_params_pattern {
+ __u8 set;
+ __u16 subset;
+ } pattern;
+};
+#define PME_SCAN_PARAMS_RESIDUE 0x00000001
+#define PME_SCAN_PARAMS_SRE 0x00000002
+#define PME_SCAN_PARAMS_DXE 0x00000004
+#define PME_SCAN_PARAMS_PATTERN 0x00000008
+
+/* argument to PMEIO_SCAN_W1 ioctl */
+struct pme_scan_cmd {
+ __u32 flags; /* PME_SCAN_CMD_*** bitmask */
+ void *opaque; /* value carried through in the pme_scan_result */
+ struct pme_buffer input;
+ struct pme_buffer output; /* ignored for 'RES_BMAN' output */
+};
+
+#define PME_SCAN_CMD_RES_BMAN 0x00000001 /* use Bman for output */
+#define PME_SCAN_CMD_STARTRESET 0x00000002
+#define PME_SCAN_CMD_END 0x00000004
+
+/* argument to PMEIO_SCAN_Wn ioctl
+ * 'num' indicates how many 'cmds' are present on input and is updated on the
+ * response to indicate how many were sent. */
+struct pme_scan_cmds {
+ unsigned num;
+ struct pme_scan_cmd __user *cmds;
+};
+
+/* argument to PMEIO_SCAN_R1 ioctl. The ioctl doesn't read any of these
+ * fields, they are only written to. If the output comes from BMAN buffer
+ * then 'flags' will have PME_SCAN_RESULT_BMAN set. */
+struct pme_scan_result {
+ __u8 flags; /* PME_SCAN_RESULT_*** bitmask */
+ enum pme_status status;
+ struct pme_buffer output;
+ void *opaque; /* value carried from the pme_scan_cmd */
+};
+#define PME_SCAN_RESULT_UNRELIABLE PME_STATUS_UNRELIABLE
+#define PME_SCAN_RESULT_TRUNCATED PME_STATUS_TRUNCATED
+#define PME_SCAN_RESULT_BMAN 0x01
+
+/* argument to PMEIO_SCAN_Rn ioctl.
+ * 'num' indicates how many 'cmds' are present on input and is updated on the
+ * response to indicate how many were retrieved. */
+struct pme_scan_results {
+ unsigned num;
+ struct pme_scan_result *results;
+};
+
+/* argument to PMEIO_SCANWR ioctl. */
+struct pme_scan {
+ struct pme_scan_cmd cmd;
+ struct pme_scan_result result;
+};
+
+/*************/
+/* DB DEVICE */
+/*************/
+/* The /dev/pme_db device creates a file-descriptor that uses parked FQs
+ * serviced by the PME's EFQC (Exclusive Frame Queue Control) mechanism. This is
+ * usually for PMTCC commands for programming the database, though can also be
+ * used for high-priority scanning. This device would typically require root
+ * perms. The EFQC exclusivity is reference-counted, so by default is asserted
+ * on-demand and released when processing quiesces for the context, but
+ * exclusivity can be maintained across inter-frame gaps using the INC and DEC
+ * ioctls, which provide supplementary increments and decrements of the
+ * reference count. */
+#define PME_DEV_DB_NODE "pme_db"
+#define PME_DEV_DB_PATH "/dev/" PME_DEV_DB_NODE
+
+/* ioctls for 'db' device */
+#define PMEIO_EXL_INC _IO(PME_IOCTL_MAGIC, 0x00)
+#define PMEIO_EXL_DEC _IO(PME_IOCTL_MAGIC, 0x01)
+#define PMEIO_EXL_GET _IOR(PME_IOCTL_MAGIC, 0x02, int)
+#define PMEIO_PMTCC _IOWR(PME_IOCTL_MAGIC, 0x03, struct pme_db)
+#define PMEIO_SRE_RESET _IOR(PME_IOCTL_MAGIC, 0x04, struct pme_db_sre_reset)
+#define PMEIO_NOP _IO(PME_IOCTL_MAGIC, 0x05)
+
+/* Database structures */
+#define PME_DB_RESULT_UNRELIABLE PME_STATUS_UNRELIABLE
+#define PME_DB_RESULT_TRUNCATED PME_STATUS_TRUNCATED
+
+struct pme_db {
+ struct pme_buffer input;
+ struct pme_buffer output;
+ __u8 flags; /* PME_DB_RESULT_*** bitmask */
+ enum pme_status status;
+};
+
+/* This is related to the sre_reset ioctl */
+#define PME_SRE_RULE_VECTOR_SIZE 8
+struct pme_db_sre_reset {
+ __u32 rule_vector[PME_SRE_RULE_VECTOR_SIZE];
+ __u32 rule_index;
+ __u16 rule_increment;
+ __u32 rule_repetitions;
+ __u16 rule_reset_interval;
+ __u8 rule_reset_priority;
+};
+
+/****************/
+/* KERNEL SPACE */
+/****************/
+
+#ifdef __KERNEL__
+
+#include <linux/fsl_qman.h>
+#include <linux/fsl_bman.h>
+
+/* "struct pme_hw_flow" represents a flow-context resource for h/w, whereas
+ * "struct pme_flow" (below) is the s/w type used to provide (and receive)
+ * parameters to(/from) the h/w resource. */
+struct pme_hw_flow;
+
+/* "struct pme_hw_residue" represents a residue resource for h/w. */
+struct pme_hw_residue;
+
+/* This is the pme_flow structure type, used for querying or updating a PME flow
+ * context */
+struct pme_flow {
+ u8 sos:1;
+ u8 __reserved1:1;
+ u8 srvm:2;
+ u8 esee:1;
+ u8 __reserved2:3;
+ u8 ren:1;
+ u8 rlen:7;
+ /* Sequence Number (48-bit) */
+ u16 seqnum_hi;
+ u32 seqnum_lo;
+ u32 __reserved3;
+ u32 sessionid:27;
+ u32 __reserved4:5;
+ u16 __reserved5;
+ /* Residue pointer (48-bit), ignored if ren==0 */
+ u16 rptr_hi;
+ u32 rptr_lo;
+ u16 clim;
+ u16 mlim;
+ u32 __reserved6;
+} __packed;
+static inline u64 pme_flow_seqnum_get64(const struct pme_flow *p)
+{
+ return ((u64)p->seqnum_hi << 32) | (u64)p->seqnum_lo;
+}
+static inline u64 pme_flow_rptr_get64(const struct pme_flow *p)
+{
+ return ((u64)p->rptr_hi << 32) | (u64)p->rptr_lo;
+}
+/* Macro, so we compile better if 'v' isn't always 64-bit */
+#define pme_flow_seqnum_set64(p, v) \
+ do { \
+ struct pme_flow *__p931 = (p); \
+ __p931->seqnum_hi = upper_32_bits(v); \
+ __p931->seqnum_lo = lower_32_bits(v); \
+ } while (0)
+#define pme_flow_rptr_set64(p, v) \
+ do { \
+ struct pme_flow *__p931 = (p); \
+ __p931->rptr_hi = upper_32_bits(v); \
+ __p931->rptr_lo = lower_32_bits(v); \
+ } while (0)
+
+/* pme_ctx_ctrl_update_flow(), pme_fd_cmd_fcw() and pme_scan_params::flags
+ * use these; */
+#define PME_CMD_FCW_RES 0x80 /* "Residue": ren, rlen */
+#define PME_CMD_FCW_SEQ 0x40 /* "Sequence": sos, sequnum */
+#define PME_CMD_FCW_SRE 0x20 /* "Stateful Rule": srvm, esee, sessionid */
+#define PME_CMD_FCW_DXE 0x10 /* "Data Examination": clim, mlim */
+#define PME_CMD_FCW_ALL 0xf0
+
+/* pme_ctx_scan() and pme_fd_cmd_scan() use these; */
+#define PME_CMD_SCAN_SRVM(n) ((n) << 3) /* n in [0..3] */
+#define PME_CMD_SCAN_FLUSH 0x04
+#define PME_CMD_SCAN_SR 0x02 /* aka "Start of Flow or Reset */
+#define PME_CMD_SCAN_E 0x01 /* aka "End of Flow */
+
+/***********************/
+/* low-level functions */
+/***********************/
+
+/* (De)Allocate PME hardware resources */
+struct pme_hw_residue *pme_hw_residue_new(void);
+void pme_hw_residue_free(struct pme_hw_residue *);
+struct pme_hw_flow *pme_hw_flow_new(void);
+void pme_hw_flow_free(struct pme_hw_flow *);
+
+/* Initialise a flow context to known default values */
+void pme_sw_flow_init(struct pme_flow *);
+
+/* Fill in an "Initialise FQ" management command for a PME input FQ. NB, the
+ * caller is responsible for setting the following fields, they will not be set
+ * by the API;
+ * - initfq->fqid, the frame queue to be initialised
+ * - initfq->count, should most likely be zero. A count of 0 initialises 1 FQ,
+ * a count of 1 initialises 2 FQs, etc/
+ * The 'qos' parameter indicates which workqueue in the PME channel the
+ * FQ should schedule to for regular scanning (0..7). If 'flow' is non-NULL the
+ * FQ is configured for Flow Mode, otherwise it is configured for Direct Action
+ * Mode. 'bpid' is the buffer pool ID to use when Bman-based output is
+ * produced, and 'rfqid' is the frame queue ID to enqueue output frames to.
+ * Following this api, when calling qm_mc_commit(), use QM_MCC_VERB_INITFQ_SCHED
+ * for regular PMEscanning or QM_MCC_VERB_INITFQ_PARK for exclusive PME
+ * processing (usually PMTCC).*/
+void pme_initfq(struct qm_mcc_initfq *initfq, struct pme_hw_flow *flow, u8 qos,
+ u8 rbpid, u32 rfqid);
+
+/* Given a dequeued frame from PME, return status/flags */
+static inline enum pme_status pme_fd_res_status(const struct qm_fd *fd)
+{
+ return (enum pme_status)(fd->status >> 24);
+}
+static inline u8 pme_fd_res_flags(const struct qm_fd *fd)
+{
+ return (fd->status >> 16) & PME_STATUS_MASK;
+}
+
+/* Fill in a frame descriptor for a NOP command. */
+void pme_fd_cmd_nop(struct qm_fd *fd);
+
+/* Fill in a frame descriptor for a Flow Context Write command. NB, the caller
+ * is responsible for setting all the relevant fields in 'flow', only the
+ * following fields are set by the API;
+ * - flow->rptr_hi
+ * - flow->rptr_lo
+ * The fields in 'flow' are divided into 4 groups, 'flags' indicates which of
+ * them should be written to the h/w flow context using PME_CMD_FCW_*** defines.
+ * 'residue' should be non-NULL iff flow->ren is non-zero and PME_CMD_FCW_RES is
+ * set. */
+void pme_fd_cmd_fcw(struct qm_fd *fd, u8 flags, struct pme_flow *flow,
+ struct pme_hw_residue *residue);
+
+/* Fill in a frame descriptor for a Flow Context Read command. */
+void pme_fd_cmd_fcr(struct qm_fd *fd, struct pme_flow *flow);
+
+/* Modify a frame descriptor for a PMTCC command (only modifies 'cmd' field) */
+void pme_fd_cmd_pmtcc(struct qm_fd *fd);
+
+/* Modify a frame descriptor for a Scan command (only modifies 'cmd' field).
+ * 'flags' are chosen from PME_CMD_SCAN_*** symbols. NB, the use of the
+ * intermediary representation (and PME_SCAN_ARGS) improves performance - ie.
+ * if the scan params are essentially constant, this compacts them for storage
+ * into the same format used in the interface to h/w. So it reduces parameter
+ * passing, stack-use, and encoding time. */
+#define PME_SCAN_ARGS(flags, set, subset) \
+({ \
+ u8 __flags461 = (flags); \
+ u8 __set461 = (set); \
+ u16 __subset461 = (subset); \
+ u32 __res461 = ((u32)__flags461 << 24) | \
+ ((u32)__set461 << 16) | \
+ (u32)__subset461; \
+ __res461; \
+})
+void pme_fd_cmd_scan(struct qm_fd *fd, u32 args);
+
+/* convert pointer to physical address for use by PME */
+dma_addr_t pme_map(void *ptr);
+int pme_map_error(dma_addr_t dma_addr);
+
+enum pme_cmd_type {
+ pme_cmd_nop = 0x7,
+ pme_cmd_flow_read = 0x5, /* aka FCR */
+ pme_cmd_flow_write = 0x4, /* aka FCW */
+ pme_cmd_pmtcc = 0x1,
+ pme_cmd_scan = 0
+};
+
+/************************/
+/* high-level functions */
+/************************/
+
+/* predeclaration of a private structure" */
+struct pme_ctx;
+struct pme_nostash;
+
+/* Calls to pme_ctx_scan() and pme_ctx_pmtcc() provide these, and they are
+ * provided back in the completion callback. You can embed this within a larger
+ * structure in order to maintain per-command data of your own. The fields are
+ * owned by the driver until the callback is invoked, so for example do not link
+ * this token into a list while the command is in-flight! */
+struct pme_ctx_token {
+ u32 blob[4];
+ struct list_head node;
+ enum pme_cmd_type cmd_type:8;
+ u8 is_disable_flush;
+};
+
+struct pme_ctx_ctrl_token {
+ void (*cb)(struct pme_ctx *, const struct qm_fd *,
+ struct pme_ctx_ctrl_token *);
+ void (*ern_cb)(struct pme_ctx *, const struct qm_mr_entry *,
+ struct pme_ctx_ctrl_token *);
+ /* don't touch the rest */
+ struct pme_hw_flow *internal_flow_ptr;
+ struct pme_flow *usr_flow_ptr;
+ struct pme_ctx_token base_token;
+};
+
+/* Scan results invoke a user-provided callback of this type */
+typedef void (*pme_scan_cb)(struct pme_ctx *, const struct qm_fd *,
+ struct pme_ctx_token *);
+/* Enqueue rejections may happen before order-restoration or after (eg. if due
+ * to congestion or tail-drop). Use * 'rc' code of the 'mr_entry' to
+ * determine. */
+typedef void (*pme_scan_ern_cb)(struct pme_ctx *, const struct qm_mr_entry *,
+ struct pme_ctx_token *);
+
+/* PME "association" - ie. connects two frame-queues, with or without a PME flow
+ * (if not, direct action mode), and manages mux/demux of scans and flow-context
+ * updates. To allow state used by your callback to be stashed, as well as
+ * optimising the PME driver and the Qman driver beneath it, embed this
+ * structure as the first field in your own context structure. */
+struct pme_ctx {
+ struct qman_fq fq;
+ /* IMPORTANT: Set (only) these two fields prior to calling *
+ * pme_ctx_init(). 'ern_cb' can be NULL if you know you will not
+ * receive enqueue rejections. */
+ pme_scan_cb cb;
+ pme_scan_ern_cb ern_cb;
+ /* These fields should not be manipulated directly. Also the structure
+ * may change and/or grow, so avoid making any alignment or size
+ * assumptions. */
+ atomic_t refs;
+ volatile u32 flags;
+ spinlock_t lock;
+ wait_queue_head_t queue;
+ struct list_head tokens;
+ /* TODO: the following "slow-path" values should be bundled into a
+ * secondary structure so that sizeof(struct pme_ctx) is minimised (for
+ * stashing of caller-side fast-path state). */
+ struct pme_hw_flow *hw_flow;
+ struct pme_hw_residue *hw_residue;
+ struct qm_fqd_stashing stashing;
+ struct qm_fd update_fd;
+ struct pme_nostash *us_data;
+};
+
+/* Flags for pme_ctx_init() */
+#define PME_CTX_FLAG_LOCKED 0x00000001 /* use QMAN_FQ_FLAG_LOCKED */
+#define PME_CTX_FLAG_EXCLUSIVE 0x00000002 /* unscheduled, exclusive mode */
+#define PME_CTX_FLAG_PMTCC 0x00000004 /* PMTCC rather than scanning */
+#define PME_CTX_FLAG_DIRECT 0x00000008 /* Direct Action mode (not Flow) */
+#define PME_CTX_FLAG_LOCAL 0x00000020 /* Ignore dest, use cpu portal */
+
+/* Flags for operations */
+#ifdef CONFIG_FSL_DPA_CAN_WAIT
+#define PME_CTX_OP_WAIT QMAN_ENQUEUE_FLAG_WAIT
+#define PME_CTX_OP_WAIT_INT QMAN_ENQUEUE_FLAG_WAIT_INT
+#endif
+#define PME_CTX_OP_RESETRESLEN 0x00000001 /* no en/disable, just set len */
+/* Note that pme_ctx_ctrl_update_flow() also uses PME_CMD_FCW flags, so they
+ * mustn't conflict with PME_CTX_OP_***.
+ * Also, the above are defined to match QMAN_ENQUEUE values for optimisation
+ * purposes (ie. fast-path operations that don't _WAIT will not incur PME->QMAN
+ * flag conversion overheads). */
+
+/**
+ * pme_ctx_init - Initialise a PME context
+ * @ctx: the context structure to initialise
+ * @flags: bit-mask of PME_CTX_FLAG_*** options
+ * @bpid: buffer pool ID used for any Bman-generated output
+ * @qosin: workqueue priority on the PME channel (0-7)
+ * @qosout: workqueue priority on the result channel (0-7)
+ * @dest: channel to receive results from PME
+ * @stashing: desired dequeue stashing behaviour
+ *
+ * This creates and initialises a PME context, composed of two FQs, an optional
+ * flow-context, and scheduling parameters for the datapath. The ctx->cb and
+ * ctx->pool fields must have been initialised prior to calling this api. The
+ * initialised context is left 'disabled', meaning that the FQ towards PME is
+ * Parked and no operations are possible. If PME_CTX_INIT_EXCLUSIVE is specified
+ * in @flags, then the input FQ is not scheduled, otherwise enabling the context
+ * will schedule the FQ to PME. Exclusive access is only available if the driver
+ * is built with control functionality and if the operating system has access to
+ * PME's CCSR map. @qosin applies if EXCLUSIVE is not set, and indicates which
+ * of the PME's 8 prioritised workqueues the FQ should schedule to. @dest
+ * indicates the channel that should receive results from PME, unless
+ * PME_CTX_FLAG_LOCAL is set in which case this parameter is ignored and the
+ * dedicated portal channel for the current cpu will be used instead. @qosout
+ * indicates which of the 8 prioritised workqueus the FQ should schedule to on
+ * the s/w portal. @stashing configures whether FQ context, frame data, and/or
+ * frame annotation should be stashed into cpu cache when dequeuing output, and
+ * if so, how many cachelines. For the FQ context part, set the number of
+ * cachelines to cover; 1. sizeof(struct qman_fq_base), to accelerate only Qman
+ * driver processing, 2. sizeof(struct pme_ctx), to accelerate Qman and PME
+ * driver processing, or 3. sizeof(<user-struct>), where <user-struct> is the
+ * caller's structure of which the pme_ctx is the first member - this will allow
+ * callbacks to operate on state which has a high probability of already being
+ * in-cache.
+ * Returns 0 on success.
+ */
+int pme_ctx_init(struct pme_ctx *ctx, u32 flags, u32 bpid, u8 qosin,
+ u8 qosout, enum qm_channel dest,
+ const struct qm_fqd_stashing *stashing);
+
+/* Cleanup allocated resources */
+void pme_ctx_finish(struct pme_ctx *ctx);
+
+/* enable a context */
+int pme_ctx_enable(struct pme_ctx *ctx);
+
+/* disable a context
+ * If it returns zero, the context is disabled.
+ * If it returns +1, the context is disabling and the token's completion
+ * callback will be invoked when disabling is complete.
+ * Returns -EBUSY on error, in which case the context remains enabled.
+ * If the PME_CTX_OP_WAIT flag is specified, it should only fail if
+ * PME_CTX_OP_WAIT_INT is also specified and a signal is pending. */
+int pme_ctx_disable(struct pme_ctx *ctx, u32 flags,
+ struct pme_ctx_ctrl_token *token);
+
+/* query whether a context is disabled. Returns > 0 if the ctx is disabled. */
+int pme_ctx_is_disabled(struct pme_ctx *ctx);
+
+/* query whether a context is in an error state. */
+int pme_ctx_is_dead(struct pme_ctx *ctx);
+
+/* A pre-condition for the following APIs is the ctx must be disabled
+ * dest maybe ignored if the flags parameter indicated LOCAL during the
+ * corresponding pme_ctx_init.
+ */
+int pme_ctx_reconfigure_tx(struct pme_ctx *ctx, u32 bpid, u8 qosin);
+int pme_ctx_reconfigure_rx(struct pme_ctx *ctx, u8 qosout,
+ enum qm_channel dest, const struct qm_fqd_stashing *stashing);
+
+/* Precondition: pme_ctx must be enabled
+ * if PME_CTX_OP_WAIT is specified, it'll wait (if it has to) to start the ctrl
+ * command but never waits for it to complete. The callback serves that purpose.
+ * NB: 'params' may be modified by this call. For instance if
+ * PME_CTX_OP_RESETRESLEN was specified and residue is enabled, then the
+ * params->ren will be set to 1 (in order not to disabled residue).
+ * NB: _update() will overwrite the 'params->rptr_[hi/low]' fields since the
+ * residue resource is managed by this layer.
+ */
+int pme_ctx_ctrl_update_flow(struct pme_ctx *ctx, u32 flags,
+ struct pme_flow *params, struct pme_ctx_ctrl_token *token);
+int pme_ctx_ctrl_read_flow(struct pme_ctx *ctx, u32 flags,
+ struct pme_flow *params, struct pme_ctx_ctrl_token *token);
+int pme_ctx_ctrl_nop(struct pme_ctx *ctx, u32 flags,
+ struct pme_ctx_ctrl_token *token);
+
+/* if PME_CTX_OP_WAIT is specified, it'll wait (if it has to) to start the scan
+ * but never waits for it to complete. The scan callback serves that purpose.
+ * 'fd' is modified by both these calls, but only the 'cmd' field. The 'args'
+ * parameters is produced by the PME_SCAN_ARGS() inline function. */
+int pme_ctx_scan(struct pme_ctx *ctx, u32 flags, struct qm_fd *fd, u32 args,
+ struct pme_ctx_token *token);
+int pme_ctx_pmtcc(struct pme_ctx *ctx, u32 flags, struct qm_fd *fd,
+ struct pme_ctx_token *token);
+
+/* This is extends pme_ctx_scan() to provide ORP support. 'orp_fq' represents
+ * the FQD that is used as the ORP and 'seqnum' is the sequence number to use
+ * for order restoration, these are usually the FQ the frame was dequeued from
+ * and the sequence number of that dequeued frame (respectively). */
+int pme_ctx_scan_orp(struct pme_ctx *ctx, u32 flags, struct qm_fd *fd, u32 args,
+ struct pme_ctx_token *token, struct qman_fq *orp_fq, u16 seqnum);
+
+/* Precondition: must be PME_CTX_FLAG_EXCLUSIVE */
+int pme_ctx_exclusive_inc(struct pme_ctx *ctx, u32 flags);
+void pme_ctx_exclusive_dec(struct pme_ctx *ctx);
+
+/* Does pme have access to ccsr */
+int pme2_have_control(void);
+
+/**************************/
+/* control-plane only API */
+/**************************/
+#ifdef CONFIG_FSL_PME2_CTRL
+
+/* Attributes for pme_reg_[set|get]() */
+enum pme_attr {
+ pme_attr_efqc_int,
+ pme_attr_sw_db,
+ pme_attr_dmcr,
+ pme_attr_smcr,
+ pme_attr_famcr,
+ pme_attr_kvlts,
+ pme_attr_max_chain_length,
+ pme_attr_pattern_range_counter_idx,
+ pme_attr_pattern_range_counter_mask,
+ pme_attr_max_allowed_test_line_per_pattern,
+ pme_attr_max_pdsr_index,
+ pme_attr_max_pattern_matches_per_sui,
+ pme_attr_max_pattern_evaluations_per_sui,
+ pme_attr_report_length_limit,
+ pme_attr_end_of_simple_sui_report,
+ pme_attr_aim,
+ pme_attr_sre_context_size,
+ pme_attr_sre_rule_num,
+ pme_attr_sre_session_ctx_num,
+ pme_attr_end_of_sui_reaction_ptr,
+ pme_attr_sre_pscl,
+ pme_attr_sre_max_block_num,
+ pme_attr_sre_max_instruction_limit,
+ pme_attr_sre_max_index_size,
+ pme_attr_sre_max_offset_ctrl,
+ pme_attr_src_id,
+ pme_attr_liodnr,
+ pme_attr_rev1,
+ pme_attr_rev2,
+ pme_attr_srrv0,
+ pme_attr_srrv1,
+ pme_attr_srrv2,
+ pme_attr_srrv3,
+ pme_attr_srrv4,
+ pme_attr_srrv5,
+ pme_attr_srrv6,
+ pme_attr_srrv7,
+ pme_attr_srrfi,
+ pme_attr_srri,
+ pme_attr_srrwc,
+ pme_attr_srrr,
+ pme_attr_trunci,
+ pme_attr_rbc,
+ pme_attr_tbt0ecc1ec,
+ pme_attr_tbt1ecc1ec,
+ pme_attr_vlt0ecc1ec,
+ pme_attr_vlt1ecc1ec,
+ pme_attr_cmecc1ec,
+ pme_attr_dxcmecc1ec,
+ pme_attr_dxemecc1ec,
+ pme_attr_stnib,
+ pme_attr_stnis,
+ pme_attr_stnth1,
+ pme_attr_stnth2,
+ pme_attr_stnthv,
+ pme_attr_stnths,
+ pme_attr_stnch,
+ pme_attr_stnpm,
+ pme_attr_stns1m,
+ pme_attr_stnpmr,
+ pme_attr_stndsr,
+ pme_attr_stnesr,
+ pme_attr_stns1r,
+ pme_attr_stnob,
+ pme_attr_mia_byc,
+ pme_attr_mia_blc,
+ pme_attr_isr,
+ pme_attr_tbt0ecc1th,
+ pme_attr_tbt1ecc1th,
+ pme_attr_vlt0ecc1th,
+ pme_attr_vlt1ecc1th,
+ pme_attr_cmecc1th,
+ pme_attr_dxcmecc1th,
+ pme_attr_dxemecc1th,
+ pme_attr_esr,
+ pme_attr_ecr0,
+ pme_attr_ecr1,
+ pme_attr_pmstat,
+ pme_attr_pmtr,
+ pme_attr_pehd,
+ pme_attr_ecc1bes,
+ pme_attr_ecc2bes,
+ pme_attr_eccaddr,
+ pme_attr_ecccode,
+ pme_attr_miace,
+ pme_attr_miacr,
+ pme_attr_cdcr,
+ pme_attr_faconf,
+ pme_attr_ier,
+ pme_attr_isdr,
+ pme_attr_iir,
+ pme_attr_pdsrbah,
+ pme_attr_pdsrbal,
+ pme_attr_scbarh,
+ pme_attr_scbarl,
+ pme_attr_bsc_first, /* create 64-wide space for bsc */
+ pme_attr_bsc_last = pme_attr_bsc_first + 63,
+};
+
+#define pme_attr_bsc(n) (pme_attr_bsc_first + (n))
+/* Get/set driver attributes */
+int pme_attr_set(enum pme_attr attr, u32 val);
+int pme_attr_get(enum pme_attr attr, u32 *val);
+int pme_stat_get(enum pme_attr stat, u64 *value, int reset);
+#endif /* defined(CONFIG_FSL_PME2_CTRL) */
+
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+
+struct compat_pme_buffer {
+ compat_uptr_t data;
+ compat_size_t size;
+};
+
+struct compat_pme_scan_cmd {
+ __u32 flags; /* PME_SCAN_CMD_*** bitmask */
+ compat_uptr_t opaque;
+ struct compat_pme_buffer input;
+ struct compat_pme_buffer output;
+};
+#define PMEIO_SCAN_W132 _IOW(PME_IOCTL_MAGIC, 0x0a, struct compat_pme_scan_cmd)
+
+struct compat_pme_scan_cmds {
+ compat_uint_t num;
+ compat_uptr_t cmds;
+};
+#define PMEIO_SCAN_Wn32 _IOWR(PME_IOCTL_MAGIC, 0x0b, \
+ struct compat_pme_scan_cmds)
+
+
+struct compat_pme_scan_result {
+ __u8 flags; /* PME_SCAN_RESULT_*** bitmask */
+ enum pme_status status;
+ struct compat_pme_buffer output;
+ compat_uptr_t opaque; /* value carried from the pme_scan_cmd */
+};
+#define PMEIO_SCAN_R132 _IOR(PME_IOCTL_MAGIC, 0x0c, \
+ struct compat_pme_scan_result)
+
+
+struct compat_pme_scan_results {
+ compat_uint_t num;
+ compat_uptr_t results;
+};
+#define PMEIO_SCAN_Rn32 _IOWR(PME_IOCTL_MAGIC, 0x0d, \
+ struct compat_pme_scan_results)
+
+
+struct compat_pme_scan {
+ struct compat_pme_scan_cmd cmd;
+ struct compat_pme_scan_result result;
+};
+#define PMEIO_SCAN32 _IOWR(PME_IOCTL_MAGIC, 0x0e, struct compat_pme_scan)
+
+struct compat_pme_db {
+ struct compat_pme_buffer input;
+ struct compat_pme_buffer output;
+ __u8 flags; /* PME_DB_RESULT_*** bitmask */
+ enum pme_status status;
+};
+#define PMEIO_PMTCC32 _IOWR(PME_IOCTL_MAGIC, 0x03, struct compat_pme_db)
+
+#endif /* CONFIG_COMPAT */
+
+#endif /* __KERNEL__ */
+
+#endif /* FSL_PME_H */