From: Jonas Svennebring <jonas.svennebr...@lsi.com>

The basic support includes:
* Generic AXXIA SMON (Statistic Monitor) functionality.

 * Support for Memory Controllers:
    - DDRC, DDR Controllers.
    - ELM, Encryption Memory Controllers .

 *Preparation for support of:
    - PCX, integrated Ethernet switch.
    - VP, virtual pipeline packet processing engines.

 Documentation, VP/PCX support as well as patch alignment with coming
 ARM L3$ and interconnect is in the workings.

Signed-off-by: Jonas Svennebring <jonas.svennebr...@lsi.com>
---
 arch/arm/mach-axxia/perf_event_memc.c     |  130 ++++++++++++++
 arch/arm/mach-axxia/perf_event_memc.h     |   62 +++++++
 arch/arm/mach-axxia/perf_event_pcx.c      |   46 +++++
 arch/arm/mach-axxia/perf_event_platform.c |  270 +++++++++++++++++++++++++++++
 arch/arm/mach-axxia/perf_event_platform.h |   10 ++
 arch/arm/mach-axxia/perf_event_vp.c       |   51 ++++++
 arch/arm/mach-axxia/smon.c                |  200 +++++++++++++++++++++
 arch/arm/mach-axxia/smon.h                |   71 ++++++++
 8 files changed, 840 insertions(+)
 create mode 100644 arch/arm/mach-axxia/perf_event_memc.c
 create mode 100644 arch/arm/mach-axxia/perf_event_memc.h
 create mode 100644 arch/arm/mach-axxia/perf_event_pcx.c
 create mode 100644 arch/arm/mach-axxia/perf_event_platform.c
 create mode 100644 arch/arm/mach-axxia/perf_event_platform.h
 create mode 100644 arch/arm/mach-axxia/perf_event_vp.c
 create mode 100644 arch/arm/mach-axxia/smon.c
 create mode 100644 arch/arm/mach-axxia/smon.h

diff --git a/arch/arm/mach-axxia/perf_event_memc.c 
b/arch/arm/mach-axxia/perf_event_memc.c
new file mode 100644
index 0000000..a20fc8a
--- /dev/null
+++ b/arch/arm/mach-axxia/perf_event_memc.c
@@ -0,0 +1,130 @@
+/*
+ * arch/arm/mach-axxia/perf_event_memc.c
+ * included from arch/arm/mach-axxia/perf_event_platform.c
+ *
+ * Support for the LSI Axxia boards based on ARM cores.
+ *
+ * Copyright (C) 2014 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "perf_event_memc.h"
+
+static void memc_startup_init(void)
+{
+       smon_init_ncp(&ddrc0_smon, DDRC0, DDRC_PERF, DDRC_SMON);
+       smon_init_ncp(&ddrc1_smon, DDRC1, DDRC_PERF, DDRC_SMON);
+       smon_init_mem(&elm0_smon, ELM0, ELM_SMON);
+       smon_init_mem(&elm1_smon, ELM1, ELM_SMON);
+}
+
+static uint32_t memc_pmu_event_init(uint32_t event, struct perf_event *pevent)
+{
+       return 0;
+}
+
+static void memc_pmu_event_destroy(uint32_t event, struct perf_event *pevent)
+{
+       smon_stop_if_unassigned(&ddrc0_smon);
+       smon_stop_if_unassigned(&ddrc1_smon);
+       smon_stop_if_unassigned(&elm0_smon);
+       smon_stop_if_unassigned(&elm1_smon);
+}
+
+static uint32_t memc_pmu_event_add(uint32_t ev, struct perf_event *pevent)
+{
+       uint32_t ret;
+
+       if (ev >= DDRC0_OFFSET && ev <= DDRC0_SMON_MAX) {
+
+               ret = smon_allocate(&ddrc0_smon, ev - DDRC0_OFFSET);
+               if (ret != 0)
+                       return ret;
+
+               ret = smon_start(&ddrc0_smon, ev - DDRC0_OFFSET);
+               if (ret != 0)
+                       return ret;
+       } else if (ev >= DDRC1_OFFSET && ev <= DDRC1_SMON_MAX) {
+
+               ret = smon_allocate(&ddrc1_smon, ev - DDRC1_OFFSET);
+               if (ret != 0)
+                       return ret;
+
+               ret = smon_start(&ddrc1_smon, ev - DDRC1_OFFSET);
+               if (ret != 0)
+                       return ret;
+       } else if (ev >= ELM0_OFFSET && ev <= ELM0_SMON_MAX) {
+
+               ret = smon_allocate(&elm0_smon, ev - ELM0_OFFSET);
+               if (ret != 0)
+                       return ret;
+
+               ret = smon_start(&elm0_smon, ev - ELM0_OFFSET);
+               if (ret != 0)
+                       return ret;
+       } else if (ev >= ELM1_OFFSET && ev <= ELM1_SMON_MAX) {
+
+               ret = smon_allocate(&elm1_smon, ev - ELM1_OFFSET);
+               if (ret != 0)
+                       return ret;
+
+               ret = smon_start(&elm1_smon, ev - ELM1_OFFSET);
+               if (ret != 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Remove event and return counter update.
+ */
+static uint32_t memc_pmu_event_del(uint32_t ev, struct perf_event *pevent,
+               int flags)
+{
+       uint32_t count = 0;
+
+       if (ev >= DDRC0_OFFSET && ev <= DDRC0_SMON_MAX) {
+
+               count = smon_read(&ddrc0_smon, ev - DDRC0_OFFSET);
+               if (count == -ENOEVENT)
+                       count = 0;
+
+               smon_deallocate(&ddrc0_smon, ev - DDRC0_OFFSET);
+       } else if (ev >= DDRC1_OFFSET && ev <= DDRC1_SMON_MAX) {
+
+               count = smon_read(&ddrc1_smon, ev - DDRC1_OFFSET);
+               if (count == -ENOEVENT)
+                       count = 0;
+
+               smon_deallocate(&ddrc1_smon, ev - DDRC1_OFFSET);
+       } else if (ev >= ELM0_OFFSET && ev <= ELM0_SMON_MAX) {
+               count = smon_read(&elm0_smon, ev - ELM0_OFFSET);
+               if (count == -ENOEVENT)
+                       count = 0;
+
+               smon_deallocate(&elm0_smon, ev - ELM0_OFFSET);
+       } else if (ev >= ELM1_OFFSET && ev <= ELM1_SMON_MAX) {
+
+               count = smon_read(&elm1_smon, ev - ELM1_OFFSET);
+               if (count == -ENOEVENT)
+                       count = 0;
+
+               smon_deallocate(&elm1_smon, ev - ELM1_OFFSET);
+       }
+
+       return count;
+}
diff --git a/arch/arm/mach-axxia/perf_event_memc.h 
b/arch/arm/mach-axxia/perf_event_memc.h
new file mode 100644
index 0000000..e4a4f7d
--- /dev/null
+++ b/arch/arm/mach-axxia/perf_event_memc.h
@@ -0,0 +1,62 @@
+/*
+ * arch/arm/mach-axxia/perf_event_memc.h
+ * included from arch/arm/mach-axxia/perf_event_memc.c
+ *
+ * Support for the LSI Axxia boards based on ARM cores.
+ *
+ * Copyright (C) 2014 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __ASM__ARCH_AXXIA_PERF_EVENT_MEMC_H
+#define __ASM__ARCH_AXXIA_PERF_EVENT_MEMC_H
+
+#define DDRC0_OFFSET 0x00
+#define DDRC0_SMON_MAX (DDRC0_OFFSET + 22)
+#define DDRC1_OFFSET 0x100
+#define DDRC1_SMON_MAX (DDRC1_OFFSET + 22)
+
+#define ELM0_OFFSET 0x200
+#define ELM0_SMON_MAX (ELM0_OFFSET + 15)
+#define ELM1_OFFSET 0x300
+#define ELM1_SMON_MAX (ELM1_OFFSET + 15)
+
+/* Node */
+#define DDRC0 0x0f
+#define DDRC1 0x22
+/* Target */
+#define DDRC_PERF 0x02
+
+/* Address */
+#ifdef AXM55XX_R1
+#define DDRC_SMON 0x40
+#endif
+#ifdef AXM55XX_R2
+#define DDRC_SMON 0xA0
+#endif
+
+/* Base Address */
+#define ELM0 0x2010060000
+#define ELM1 0x2010070000
+/* SMON Offset */
+#define ELM_SMON (0x300/4)
+
+struct smon_s ddrc0_smon;
+struct smon_s ddrc1_smon;
+struct smon_s elm0_smon;
+struct smon_s elm1_smon;
+
+#endif
diff --git a/arch/arm/mach-axxia/perf_event_pcx.c 
b/arch/arm/mach-axxia/perf_event_pcx.c
new file mode 100644
index 0000000..a419c67
--- /dev/null
+++ b/arch/arm/mach-axxia/perf_event_pcx.c
@@ -0,0 +1,46 @@
+/*
+ * arch/arm/mach-axxia/perf_event_pcx.c
+ * included from arch/arm/mach-axxia/perf_event_platform.c
+ *
+ * Support for the LSI Axxia boards based on ARM cores.
+ *
+ * Copyright (C) 2014 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * Generic PCX
+ */
+
+static void pcx_startup_init(void)
+{
+}
+
+static uint32_t pcx_pmu_event_init(uint32_t ev, struct perf_event *event)
+{
+       return 0;
+}
+
+static uint32_t pcx_pmu_event_add(uint32_t ev, struct perf_event *event)
+{
+       return 0;
+}
+
+static uint32_t pcx_pmu_event_del(uint32_t ev, struct perf_event *event,
+               int flags)
+{
+       return 0;
+}
diff --git a/arch/arm/mach-axxia/perf_event_platform.c 
b/arch/arm/mach-axxia/perf_event_platform.c
new file mode 100644
index 0000000..83a221d
--- /dev/null
+++ b/arch/arm/mach-axxia/perf_event_platform.c
@@ -0,0 +1,270 @@
+/*
+ * arch/arm/mach-axxia/perf_event_platform.c
+ *
+ * Support for the LSI Axxia boards based on ARM cores.
+ *
+ * Copyright (C) 2014 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <linux/bitmap.h>
+#include <linux/cpu_pm.h>
+#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/cputype.h>
+#include <asm/irq_regs.h>
+#include <asm/pmu.h>
+
+#include <linux/kthread.h>
+#include <linux/sched.h>
+
+#include <linux/cpu.h>
+#include <linux/reboot.h>
+#include <linux/syscore_ops.h>
+
+#include <linux/proc_fs.h>
+
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <../../../drivers/misc/lsi-ncr.h>
+
+#include "perf_event_platform.h"
+
+#include "smon.h"
+
+/*
+ * Include code for individual block support
+ */
+
+#include "perf_event_pcx.c"
+#include "perf_event_vp.c"
+#include "perf_event_memc.c"
+
+/*
+ * General platform perf code, muxed out to individual blocks
+ */
+
+int platform_pmu_event_idx(struct perf_event *event)
+{
+       return 0;
+}
+
+int platform_pmu_event_init(struct perf_event *event)
+{
+       uint64_t ev = event->attr.config;
+
+       if (event->attr.type != event->pmu->type)
+               return -ENOENT;
+
+       if ((ev < AXM_55XX_PLATFORM_BASE) || (ev > AXM_55XX_PLATFORM_MAX))
+               return -ENOENT;
+
+       event->hw.config = ev - AXM_55XX_PLATFORM_BASE;
+
+       event->hw.idx = -1;
+       event->hw.config_base = 1;
+
+/*
+ if (event->group_leader != event) {
+  printk("This is not the group leader!\n");
+  printk("event->group_leader 0x%x\n", (unsigned int)event->group_leader);
+ }
+*/
+
+       if (event->attr.exclude_user)
+               return -ENOTSUPP;
+       if (event->attr.exclude_kernel)
+               return -ENOTSUPP;
+       if (event->attr.exclude_idle)
+               return -ENOTSUPP;
+
+       event->hw.last_period = event->hw.sample_period;
+       local64_set(&event->hw.period_left, event->hw.last_period);
+/*
+ event->destroy = hw_perf_event_destroy;
+*/
+       local64_set(&event->count, 0);
+
+       if (ev >= AXM_55XX_VP_BASE && ev <= AXM_55XX_VP_MAX)
+               vp_pmu_event_init(ev - AXM_55XX_VP_BASE, event);
+       else if (ev >= AXM_55XX_PCX_BASE && ev <= AXM_55XX_PCX_MAX)
+               pcx_pmu_event_init(ev - AXM_55XX_PCX_BASE, event);
+       else if (ev >= AXM_55XX_MEMC_BASE && ev <= AXM_55XX_MEMC_MAX)
+               memc_pmu_event_init(ev - AXM_55XX_MEMC_BASE, event);
+       else
+               pr_info("Platform perf, undefined event, %llu\n", ev);
+
+       return 0;
+}
+
+static int platform_pmu_event_add(struct perf_event *event, int flags)
+{
+       uint64_t ev = event->attr.config;
+
+       if (ev >= AXM_55XX_VP_BASE && ev <= AXM_55XX_VP_MAX)
+               vp_pmu_event_add(ev - AXM_55XX_VP_BASE, event);
+       else if (ev >= AXM_55XX_PCX_BASE && ev <= AXM_55XX_PCX_MAX)
+               pcx_pmu_event_add(ev - AXM_55XX_PCX_BASE, event);
+       else if (ev >= AXM_55XX_MEMC_BASE && ev <= AXM_55XX_MEMC_MAX)
+               memc_pmu_event_add(ev - AXM_55XX_MEMC_BASE, event);
+
+       return 0;
+}
+
+static void platform_pmu_event_del(struct perf_event *event, int flags)
+{
+       uint64_t ev = event->attr.config;
+       uint32_t n;
+
+       if (ev >= AXM_55XX_VP_BASE && ev <= AXM_55XX_VP_MAX) {
+               n = vp_pmu_event_del(ev - AXM_55XX_VP_BASE, event, flags);
+               local64_add(n, &event->count);
+       } else if (ev >= AXM_55XX_PCX_BASE && ev <= AXM_55XX_PCX_MAX) {
+               n = pcx_pmu_event_del(ev - AXM_55XX_PCX_BASE, event, flags);
+               local64_add(n, &event->count);
+       } else if (ev >= AXM_55XX_MEMC_BASE && ev <= AXM_55XX_MEMC_MAX) {
+               n = memc_pmu_event_del(ev - AXM_55XX_MEMC_BASE, event, flags);
+               local64_add(n, &event->count);
+       } else {
+               local64_set(&event->count, 0);
+       }
+}
+
+static void platform_pmu_event_start(struct perf_event *event, int flags)
+{
+}
+
+static void platform_pmu_event_stop(struct perf_event *event, int flags)
+{
+}
+
+static void platform_pmu_event_read(struct perf_event *event)
+{
+       uint64_t ev = event->attr.config;
+       uint32_t n;
+
+       if (ev >= AXM_55XX_VP_BASE && ev <= AXM_55XX_VP_MAX) {
+               n = vp_pmu_event_del(ev - AXM_55XX_VP_BASE, event, 0);
+               local64_add(n, &event->count);
+       } else if (ev >= AXM_55XX_PCX_BASE && ev <= AXM_55XX_PCX_MAX) {
+               n = pcx_pmu_event_del(ev - AXM_55XX_PCX_BASE, event, 0);
+               local64_add(n, &event->count);
+       } else if (ev >= AXM_55XX_MEMC_BASE && ev <= AXM_55XX_MEMC_MAX) {
+               n = memc_pmu_event_del(ev - AXM_55XX_MEMC_BASE, event, 0);
+               local64_add(n, &event->count);
+       }
+}
+
+/*
+ * Device
+ */
+
+static void axmperf_device_release(struct device *dev)
+{
+       pr_warn("AXM55xxPlatformPerf release device\n");
+}
+
+static struct platform_device axmperf_device = {
+       .name = "AXM55xxPlatformPerf",
+       .id = 0,
+       .dev = {
+               .release = axmperf_device_release,
+               },
+};
+
+/*
+ * Driver
+ */
+
+#define PLATFORM_PMU_NAME_LEN 32
+
+struct lsi_platform_pmu {
+       struct pmu pmu;
+       char name[PLATFORM_PMU_NAME_LEN];
+};
+
+static int axmperf_probe(struct platform_device *dev)
+{
+       int ret;
+       struct lsi_platform_pmu *axm_pmu;
+
+       axm_pmu = kzalloc(sizeof(struct lsi_platform_pmu), GFP_KERNEL);
+       if (!axm_pmu) {
+               pr_warn("Failed platform perf memory alloc!\n");
+               return -ENOMEM;
+       }
+
+       axm_pmu->pmu = (struct pmu) {
+               .attr_groups = 0,
+               .event_init = platform_pmu_event_init,
+               .add = platform_pmu_event_add,
+               .del = platform_pmu_event_del,
+               .start = platform_pmu_event_start,
+               .stop = platform_pmu_event_stop,
+               .read = platform_pmu_event_read,
+               .event_idx = platform_pmu_event_idx,
+       };
+
+       sprintf(axm_pmu->name, "LSI AXM55xx Platform");
+
+       ret = perf_pmu_register(&axm_pmu->pmu, axm_pmu->name, PERF_TYPE_RAW);
+
+       if (ret == 0)
+               pr_info("axxia platform perf enabled\n");
+       else
+               pr_info("axxia platform perf failed\n");
+
+       vp_startup_init();
+       pcx_startup_init();
+       memc_startup_init();
+
+       return ret;
+}
+
+static struct platform_driver axmperf_driver = {
+       .driver = {
+                  .name = "AXM55xxPlatformPerf",
+                  .owner = THIS_MODULE,
+                  },
+       .probe = axmperf_probe,
+};
+
+static int __init axmperf_init(void)
+{
+       platform_device_register(&axmperf_device);
+       platform_driver_register(&axmperf_driver);
+
+       return 0;
+}
+
+static void __exit axmperf_exit(void)
+{
+       pr_warn("AXM55xx platform perf exit!\n");
+       platform_driver_unregister(&axmperf_driver);
+       platform_device_unregister(&axmperf_device);
+}
+
+module_init(axmperf_init);
+module_exit(axmperf_exit);
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-axxia/perf_event_platform.h 
b/arch/arm/mach-axxia/perf_event_platform.h
new file mode 100644
index 0000000..5446855
--- /dev/null
+++ b/arch/arm/mach-axxia/perf_event_platform.h
@@ -0,0 +1,10 @@
+ #define AXM55XX_R1
+
+#define AXM_55XX_PLATFORM_BASE 0x10000
+#define AXM_55XX_VP_BASE (AXM_55XX_PLATFORM_BASE + 0x00)
+#define AXM_55XX_VP_MAX (AXM_55XX_VP_BASE + 0x1fff)
+#define AXM_55XX_PCX_BASE (AXM_55XX_PLATFORM_BASE + 0x4000)
+#define AXM_55XX_PCX_MAX (AXM_55XX_PCX_BASE + 0x0fff)
+#define AXM_55XX_MEMC_BASE (AXM_55XX_PLATFORM_BASE + 0x8000)
+#define AXM_55XX_MEMC_MAX (AXM_55XX_MEMC_BASE + 0x0fff)
+#define AXM_55XX_PLATFORM_MAX (AXM_55XX_MEMC_MAX)
diff --git a/arch/arm/mach-axxia/perf_event_vp.c 
b/arch/arm/mach-axxia/perf_event_vp.c
new file mode 100644
index 0000000..3993f24
--- /dev/null
+++ b/arch/arm/mach-axxia/perf_event_vp.c
@@ -0,0 +1,51 @@
+/*
+ * arch/arm/mach-axxia/perf_event_vp.c
+ * included from arch/arm/mach-axxia/perf_event_platform.c
+ *
+ * Support for the LSI Axxia boards based on ARM cores.
+ *
+ * Copyright (C) 2014 LSI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+/*
+ * Generic VP
+ */
+
+static void vp_startup_init(void)
+{
+}
+
+static uint32_t vp_pmu_event_init(uint32_t event, struct perf_event *pevent)
+{
+       return 0;
+}
+
+static uint32_t vp_pmu_event_add(uint32_t event, struct perf_event *pevent)
+{
+       return 0;
+}
+
+static uint32_t vp_pmu_event_del(uint32_t event, struct perf_event *pevent,
+               int flags)
+{
+       return 0;
+}
+
+static void vp_pmu_event_destroy(uint32_t event, struct perf_event *pevent)
+{
+}
diff --git a/arch/arm/mach-axxia/smon.c b/arch/arm/mach-axxia/smon.c
new file mode 100644
index 0000000..b2f0a56
--- /dev/null
+++ b/arch/arm/mach-axxia/smon.c
@@ -0,0 +1,200 @@
+/*
+ * linux/arch/arm/mach-axxia/smon.c
+ *
+ * Platform perf helper module for generic VP statistical monitor
+ *
+ * Copyright (C) 2013 LSI Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/io.h>
+
+#include <../../../drivers/misc/lsi-ncr.h>
+
+#include "smon.h"
+
+static void memcpy32_fromio(uint32_t *dest, uint32_t *src, uint32_t len)
+{
+       uint32_t i;
+
+       for (i = 0; i < len; i++)
+               dest[i] = ioread32(src + i);
+}
+
+static void memcpy32_toio(uint32_t *dest, uint32_t *src, uint32_t len)
+{
+       uint32_t i;
+
+       for (i = 0; i < len; i++)
+               iowrite32(src[i], dest + i);
+}
+
+void smon_init_ncp(struct smon_s *smon, uint32_t node, uint32_t target,
+       uint32_t offset)
+{
+       smon->assigned[0] = UNASSIGNED;
+       smon->assigned[1] = UNASSIGNED;
+       smon->type = NCP_SMON;
+       smon->node = node;
+       smon->target = target;
+       smon->offset = offset;
+}
+
+void smon_init_mem(struct smon_s *smon, uint64_t addr, uint32_t offset)
+{
+       smon->assigned[0] = UNASSIGNED;
+       smon->assigned[1] = UNASSIGNED;
+       smon->type = MEM_SMON;
+       smon->addr = ioremap(addr, SZ_4K);
+       smon->offset = offset;
+
+       if (smon->addr == NULL)
+               pr_err("axxia perf, smon can't remap memory %lld\n", addr);
+}
+
+void smon_stop_if_unassigned(struct smon_s *smon)
+{
+       uint32_t control = 0;
+
+       if (smon->assigned[0] == UNASSIGNED &&
+           smon->assigned[1] == UNASSIGNED) {
+               ncr_write(NCP_REGION_ID(smon->node, smon->target), smon->offset,
+                         1 * REG_SZ, &control);
+       }
+}
+
+uint32_t smon_allocate(struct smon_s *smon, uint8_t event)
+{
+       if (smon->assigned[0] == UNASSIGNED) {
+               smon->events[0] = event;
+               smon->assigned[0] = ASSIGNED;
+       } else if (smon->assigned[1] == UNASSIGNED) {
+               smon->events[1] = event;
+               smon->assigned[1] = ASSIGNED;
+       } else {
+               pr_warn("smon_allocate, no counter availible\n");
+               return -ENOCOUNTER;
+       }
+
+       return 0;
+}
+
+uint32_t smon_deallocate(struct smon_s *smon, uint8_t event)
+{
+       if ((smon->assigned[0] == ASSIGNED) && (smon->events[0] == event))
+               smon->assigned[0] = UNASSIGNED;
+       else if ((smon->assigned[1] == ASSIGNED) && (smon->events[1] == event))
+               smon->assigned[1] = UNASSIGNED;
+       else
+               return -ENOCOUNTER;
+
+       return 0;
+}
+
+uint32_t smon_event_active(struct smon_s *smon, uint8_t event)
+{
+       if ((smon->assigned[0] == ASSIGNED) && (smon->events[0] == event))
+               return 0;
+       else if ((smon->assigned[1] == ASSIGNED) && (smon->events[1] == event))
+               return 0;
+
+       return -ENOCOUNTER;
+}
+
+uint32_t smon_read(struct smon_s *smon, uint8_t event)
+{
+       if (smon->type == NCP_SMON)
+               ncr_read(NCP_REGION_ID(smon->node, smon->target), smon->offset,
+                        8 * REG_SZ, &smon->regs);
+       else if (smon->type == MEM_SMON)
+               memcpy32_fromio((uint32_t *)&smon->regs,
+                       (uint32_t *)smon->addr + smon->offset, 8);
+
+       if ((smon->assigned[0] == ASSIGNED) && (smon->events[0] == event))
+               return smon->regs.count0;
+       else if ((smon->assigned[1] == ASSIGNED) && (smon->events[1] == event))
+               return smon->regs.count1;
+
+       return -ENOEVENT;
+}
+
+uint32_t smon_start(struct smon_s *smon, uint8_t event)
+{
+       /* get currect configuration */
+       if (smon->type == NCP_SMON)
+               ncr_read(NCP_REGION_ID(smon->node, smon->target), smon->offset,
+                        8 * REG_SZ, &smon->regs);
+       else if (smon->type == MEM_SMON)
+               memcpy32_fromio((uint32_t *)&smon->regs,
+                       (uint32_t *)smon->addr + smon->offset, 8);
+
+       smon->regs.control = 1; /* run counters */
+
+       if ((smon->assigned[0] == ASSIGNED) && (smon->events[0] == event)) {
+               smon->regs.event0 = event;
+               smon->regs.count0 = 0;
+
+               if (smon->type == NCP_SMON) {
+                       /* write configuration, but do not change count reg */
+                       ncr_write(NCP_REGION_ID(smon->node, smon->target),
+                               smon->offset, 2 * REG_SZ, &smon->regs);
+
+                       /* clear this events counter register */
+                       ncr_write(NCP_REGION_ID(smon->node, smon->target),
+                               smon->offset + 4 * REG_SZ, 1 * REG_SZ,
+                               &smon->regs.count0);
+               } else if (smon->type == MEM_SMON) {
+                       /* write configuration, but do not change count reg */
+                       memcpy32_toio((uint32_t *)smon->addr + smon->offset,
+                               (uint32_t *)&smon->regs, 2);
+
+                       /* clear this events counter register */
+                       memcpy32_toio((uint32_t *)smon->addr + smon->offset + 4,
+                               (uint32_t *)&smon->regs.count0, 1);
+
+               }
+
+       } else if ((smon->assigned[1] == ASSIGNED)
+               && (smon->events[1] == event)) {
+               smon->regs.event1 = event;
+               smon->regs.count1 = 0;
+
+               if (smon->type == NCP_SMON) {
+                       /* write configuration, but do not change count reg */
+                       ncr_write(NCP_REGION_ID(smon->node, smon->target),
+                               smon->offset, 2 * REG_SZ, &smon->regs);
+
+                       /* clear this events counter register */
+                       ncr_write(NCP_REGION_ID(smon->node, smon->target),
+                                 smon->offset + 5 * REG_SZ, 1 * REG_SZ,
+                                 &smon->regs.count1);
+               } else if (smon->type == MEM_SMON) {
+                       /* write configuration, but do not change count reg */
+                       memcpy32_toio((uint32_t *)smon->addr + smon->offset,
+                               (uint32_t *)&smon->regs, 2);
+
+                       /* clear this events counter register */
+                       memcpy32_toio((uint32_t *)smon->addr + smon->offset + 5,
+                               (uint32_t *)&smon->regs.count1, 1);
+               }
+
+       } else {
+               pr_warn("smon_start, no counter availible\n");
+               return -ENOCOUNTER;
+       }
+
+       return 0;
+}
diff --git a/arch/arm/mach-axxia/smon.h b/arch/arm/mach-axxia/smon.h
new file mode 100644
index 0000000..bcdf39f
--- /dev/null
+++ b/arch/arm/mach-axxia/smon.h
@@ -0,0 +1,71 @@
+/*
+ * Helper module for board specific I2C bus registration
+ *
+ * Copyright (C) 2014 LSI Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef __ASM__ARCH_AXXIA_SMON_H
+#define __ASM__ARCH_AXXIA_SMON_H
+
+#include <linux/kernel.h>
+
+struct smon_reg_s {
+       uint32_t control;
+       uint8_t event0;
+       uint8_t event1;
+       uint16_t reserved;
+       uint32_t compare0;
+       uint32_t compare1;
+       uint32_t count0;
+       uint32_t count1;
+       uint32_t time;
+       uint32_t maxtime;
+};
+
+struct smon_s {
+       struct smon_reg_s regs;
+       uint32_t type; /* NCP_SMON or MEM_SMON */
+       uint32_t *addr; /* MEM_SMON */
+       uint32_t node; /* NCP_SMON */
+       uint32_t target; /* " */
+       uint32_t offset;
+       uint8_t assigned[2];
+       uint8_t events[2];
+};
+
+#define REG_SZ 4
+
+#define MEM_SMON 0
+#define NCP_SMON 1
+
+#define UNASSIGNED 0
+#define ASSIGNED 1
+
+#define ENOCOUNTER 1
+#define ENOEVENT 2
+
+void smon_init_ncp(struct smon_s *smon, uint32_t node, uint32_t target,
+       uint32_t offset);
+void smon_init_mem(struct smon_s *smon, uint64_t addr, uint32_t offset);
+void smon_stop_if_unassigned(struct smon_s *smon);
+uint32_t smon_allocate(struct smon_s *smon, uint8_t event);
+uint32_t smon_deallocate(struct smon_s *smon, uint8_t event);
+uint32_t smon_event_active(struct smon_s *smon, uint8_t event);
+uint32_t smon_read(struct smon_s *smon, uint8_t event);
+uint32_t smon_start(struct smon_s *smon, uint8_t event);
+
+#endif /* __ASM__ARCH_AXXIA_SMON_H */
-- 
1.7.9.5

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to