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