123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- /* Copyright (c) 2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only 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.
- */
- /**
- * This file contains the part of the IOMMUv1 PMU driver that actually touches
- * IOMMU PMU registers.
- */
- #include <linux/io.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <mach/iommu_hw-v1.h>
- #include <mach/iommu_perfmon.h>
- #include <mach/iommu.h>
- #define PMCR_P_MASK (0x1)
- #define PMCR_P_SHIFT (1)
- #define PMCR_P (PMCR_P_MASK << PMCR_P_SHIFT)
- #define PMCFGR_NCG_MASK (0xFF)
- #define PMCFGR_NCG_SHIFT (24)
- #define PMCFGR_NCG (PMCFGR_NCG_MASK << PMCFGR_NCG_SHIFT)
- #define PMCFGR_N_MASK (0xFF)
- #define PMCFGR_N_SHIFT (0)
- #define PMCFGR_N (PMCFGR_N_MASK << PMCFGR_N_SHIFT)
- #define CR_E 0x1
- #define CGCR_CEN 0x800
- #define CGCR_CEN_SHFT (1 << 11)
- #define PMCGCR_CGNC_MASK (0x0F)
- #define PMCGCR_CGNC_SHIFT (24)
- #define PMCGCR_CGNC (PMCGCR_CGNC_MASK << PMCGCR_CGNC_SHIFT)
- #define PMCGCR_(group) (PMCGCR_N + group*4)
- #define PMOVSCLR_(n) (PMOVSCLR_N + n*4)
- #define PMCNTENSET_(n) (PMCNTENSET_N + n*4)
- #define PMCNTENCLR_(n) (PMCNTENCLR_N + n*4)
- #define PMINTENSET_(n) (PMINTENSET_N + n*4)
- #define PMINTENCLR_(n) (PMINTENCLR_N + n*4)
- #define PMEVCNTR_(n) (PMEVCNTR_N + n*4)
- #define PMEVTYPER_(n) (PMEVTYPER_N + n*4)
- static unsigned int iommu_pm_is_hw_access_OK(const struct iommu_pmon *pmon)
- {
- /*
- * IOMMUv1 is not in the always on domain so we need to make sure
- * the regulators are turned on in addition to clocks before we allow
- * access to the hardware thus we check if we have attached to the
- * IOMMU in addition to checking if we have enabled PMU.
- */
- return pmon->enabled && (pmon->iommu_attach_count > 0);
- }
- static void iommu_pm_grp_enable(struct iommu_info *iommu, unsigned int grp_no)
- {
- unsigned int pmcgcr;
- pmcgcr = readl_relaxed(iommu->base + PMCGCR_(grp_no));
- pmcgcr |= CGCR_CEN;
- writel_relaxed(pmcgcr, iommu->base + PMCGCR_(grp_no));
- }
- static void iommu_pm_grp_disable(struct iommu_info *iommu, unsigned int grp_no)
- {
- unsigned int pmcgcr;
- pmcgcr = readl_relaxed(iommu->base + PMCGCR_(grp_no));
- pmcgcr &= ~CGCR_CEN;
- writel_relaxed(pmcgcr, iommu->base + PMCGCR_(grp_no));
- }
- static void iommu_pm_enable(struct iommu_info *iommu)
- {
- unsigned int pmcr;
- pmcr = readl_relaxed(iommu->base + PMCR);
- pmcr |= CR_E;
- writel_relaxed(pmcr, iommu->base + PMCR);
- }
- static void iommu_pm_disable(struct iommu_info *iommu)
- {
- unsigned int pmcr;
- pmcr = readl_relaxed(iommu->base + PMCR);
- pmcr &= ~CR_E;
- writel_relaxed(pmcr, iommu->base + PMCR);
- }
- static void iommu_pm_reset_counters(const struct iommu_info *iommu)
- {
- unsigned int pmcr;
- pmcr = readl_relaxed(iommu->base + PMCR);
- pmcr |= PMCR_P;
- writel_relaxed(pmcr, iommu->base + PMCR);
- }
- static void iommu_pm_check_for_overflow(struct iommu_pmon *pmon)
- {
- struct iommu_pmon_counter *counter;
- struct iommu_info *iommu = &pmon->iommu;
- unsigned int reg_no = 0;
- unsigned int bit_no;
- unsigned int reg_value;
- unsigned int i;
- unsigned int j;
- unsigned int curr_reg = 0;
- reg_value = readl_relaxed(iommu->base + PMOVSCLR_(curr_reg));
- for (i = 0; i < pmon->num_groups; ++i) {
- struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[i];
- for (j = 0; j < cnt_grp->num_counters; ++j) {
- counter = &cnt_grp->counters[j];
- reg_no = counter->absolute_counter_no / 32;
- bit_no = counter->absolute_counter_no % 32;
- if (reg_no != curr_reg) {
- /* Clear overflow bits */
- writel_relaxed(reg_value, iommu->base +
- PMOVSCLR_(reg_no));
- curr_reg = reg_no;
- reg_value = readl_relaxed(iommu->base +
- PMOVSCLR_(curr_reg));
- }
- if (counter->enabled) {
- if (reg_value & (1 << bit_no))
- counter->overflow_count++;
- }
- }
- }
- /* Clear overflow */
- writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
- }
- static irqreturn_t iommu_pm_evt_ovfl_int_handler(int irq, void *dev_id)
- {
- struct iommu_pmon *pmon = dev_id;
- struct iommu_info *iommu = &pmon->iommu;
- mutex_lock(&pmon->lock);
- if (!iommu_pm_is_hw_access_OK(pmon)) {
- mutex_unlock(&pmon->lock);
- goto out;
- }
- iommu->ops->iommu_lock_acquire(0);
- iommu_pm_check_for_overflow(pmon);
- iommu->ops->iommu_lock_release(0);
- mutex_unlock(&pmon->lock);
- out:
- return IRQ_HANDLED;
- }
- static void iommu_pm_counter_enable(struct iommu_info *iommu,
- struct iommu_pmon_counter *counter)
- {
- unsigned int reg_no = counter->absolute_counter_no / 32;
- unsigned int bit_no = counter->absolute_counter_no % 32;
- unsigned int reg_value;
- /* Clear overflow of counter */
- reg_value = 1 << bit_no;
- writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
- /* Enable counter */
- writel_relaxed(reg_value, iommu->base + PMCNTENSET_(reg_no));
- counter->enabled = 1;
- }
- static void iommu_pm_counter_disable(struct iommu_info *iommu,
- struct iommu_pmon_counter *counter)
- {
- unsigned int reg_no = counter->absolute_counter_no / 32;
- unsigned int bit_no = counter->absolute_counter_no % 32;
- unsigned int reg_value;
- counter->enabled = 0;
- /* Disable counter */
- reg_value = 1 << bit_no;
- writel_relaxed(reg_value, iommu->base + PMCNTENCLR_(reg_no));
- /* Clear overflow of counter */
- writel_relaxed(reg_value, iommu->base + PMOVSCLR_(reg_no));
- }
- /*
- * Must be called after iommu_start_access() is called
- */
- static void iommu_pm_ovfl_int_enable(struct iommu_info *iommu,
- const struct iommu_pmon_counter *counter)
- {
- unsigned int reg_no = counter->absolute_counter_no / 32;
- unsigned int bit_no = counter->absolute_counter_no % 32;
- unsigned int reg_value;
- /* Enable overflow interrupt for counter */
- reg_value = (1 << bit_no);
- writel_relaxed(reg_value, iommu->base + PMINTENSET_(reg_no));
- }
- /*
- * Must be called after iommu_start_access() is called
- */
- static void iommu_pm_ovfl_int_disable(struct iommu_info *iommu,
- const struct iommu_pmon_counter *counter)
- {
- unsigned int reg_no = counter->absolute_counter_no / 32;
- unsigned int bit_no = counter->absolute_counter_no % 32;
- unsigned int reg_value;
- /* Disable overflow interrupt for counter */
- reg_value = 1 << bit_no;
- writel_relaxed(reg_value, iommu->base + PMINTENCLR_(reg_no));
- }
- static void iommu_pm_set_event_class(struct iommu_pmon *pmon,
- unsigned int count_no,
- unsigned int event_class)
- {
- writel_relaxed(event_class, pmon->iommu.base + PMEVTYPER_(count_no));
- }
- static unsigned int iommu_pm_read_counter(struct iommu_pmon_counter *counter)
- {
- struct iommu_pmon *pmon = counter->cnt_group->pmon;
- struct iommu_info *info = &pmon->iommu;
- unsigned int cnt_no = counter->absolute_counter_no;
- return readl_relaxed(info->base + PMEVCNTR_(cnt_no));
- }
- static void iommu_pm_initialize_hw(const struct iommu_pmon *pmon)
- {
- /* No initialization needed */
- }
- static struct iommu_pm_hw_ops iommu_pm_hw_ops = {
- .initialize_hw = iommu_pm_initialize_hw,
- .is_hw_access_OK = iommu_pm_is_hw_access_OK,
- .grp_enable = iommu_pm_grp_enable,
- .grp_disable = iommu_pm_grp_disable,
- .enable_pm = iommu_pm_enable,
- .disable_pm = iommu_pm_disable,
- .reset_counters = iommu_pm_reset_counters,
- .check_for_overflow = iommu_pm_check_for_overflow,
- .evt_ovfl_int_handler = iommu_pm_evt_ovfl_int_handler,
- .counter_enable = iommu_pm_counter_enable,
- .counter_disable = iommu_pm_counter_disable,
- .ovfl_int_enable = iommu_pm_ovfl_int_enable,
- .ovfl_int_disable = iommu_pm_ovfl_int_disable,
- .set_event_class = iommu_pm_set_event_class,
- .read_counter = iommu_pm_read_counter,
- };
- struct iommu_pm_hw_ops *iommu_pm_get_hw_ops_v1(void)
- {
- return &iommu_pm_hw_ops;
- }
- EXPORT_SYMBOL(iommu_pm_get_hw_ops_v1);
|