123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- /* 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 IOMMUv0 PMU driver that actually touches
- * IOMMU PMU registers.
- */
- #include <linux/io.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <linux/device.h>
- #include <mach/iommu_hw-v0.h>
- #include <mach/iommu_perfmon.h>
- #include <mach/iommu.h>
- #define PM_RESET_MASK (0xF)
- #define PM_RESET_SHIFT (0x8)
- #define PM_RESET (PM_RESET_MASK << PM_RESET_SHIFT)
- #define PM_ENABLE_MASK (0x1)
- #define PM_ENABLE_SHIFT (0x0)
- #define PM_ENABLE (PM_ENABLE_MASK << PM_ENABLE_SHIFT)
- #define PM_OVFL_FLAG_MASK (0xF)
- #define PM_OVFL_FLAG_SHIFT (0x0)
- #define PM_OVFL_FLAG (PM_OVFL_FLAG_MASK << PM_OVFL_FLAG_SHIFT)
- #define PM_EVENT_TYPE_MASK (0x1F)
- #define PM_EVENT_TYPE_SHIFT (0x2)
- #define PM_EVENT_TYPE (PM_EVENT_TYPE_MASK << PM_EVENT_TYPE_SHIFT)
- #define PM_INT_EN_MASK (0x1)
- #define PM_INT_EN_SHIFT (0x0)
- #define PM_INT_EN (PM_INT_EN_MASK << PM_INT_EN_SHIFT)
- #define PM_INT_POL_MASK (0x1)
- #define PM_INT_POL_SHIFT (0x2)
- #define PM_INT_ACTIVE_HIGH (0x1)
- #define PMEVCNTR_(n) (EMC_N + n*4)
- #define PMEVTYPER_(n) (EMCC_N + n*4)
- /**
- * Translate between SMMUv0 event classes and standard ARM SMMU event classes
- */
- static int iommu_pm_event_class_translation_table[] = {
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- 0x8,
- 0x9,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- 0x80,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- 0x12,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- MSM_IOMMU_PMU_NO_EVENT_CLASS,
- 0x10,
- };
- static int iommu_pm_translate_event_class(int event_class)
- {
- const unsigned int TBL_LEN =
- ARRAY_SIZE(iommu_pm_event_class_translation_table);
- unsigned int i;
- if (event_class < 0)
- return event_class;
- for (i = 0; i < TBL_LEN; ++i) {
- if (iommu_pm_event_class_translation_table[i] == event_class)
- return i;
- }
- return MSM_IOMMU_PMU_NO_EVENT_CLASS;
- }
- static unsigned int iommu_pm_is_hw_access_OK(const struct iommu_pmon *pmon)
- {
- /*
- * IOMMUv0 is in always ON domain so we don't care whether we are
- * attached or not. We only care whether the PMU is enabled or
- * not meaning clocks are turned on.
- */
- return pmon->enabled;
- }
- static void iommu_pm_grp_enable(struct iommu_info *iommu, unsigned int grp_no)
- {
- /* No group concept in v0. */
- }
- static void iommu_pm_grp_disable(struct iommu_info *iommu, unsigned int grp_no)
- {
- /* No group concept in v0. */
- }
- static void iommu_pm_set_int_active_high(const struct iommu_info *iommu)
- {
- unsigned int emmc;
- emmc = readl_relaxed(iommu->base + EMMC);
- emmc |= (PM_INT_ACTIVE_HIGH & PM_INT_POL_MASK) << PM_INT_POL_SHIFT;
- writel_relaxed(emmc, iommu->base + EMMC);
- }
- static void iommu_pm_enable(struct iommu_info *iommu)
- {
- unsigned int emmc;
- emmc = readl_relaxed(iommu->base + EMMC);
- emmc |= PM_ENABLE;
- writel_relaxed(emmc, iommu->base + EMMC);
- }
- static void iommu_pm_disable(struct iommu_info *iommu)
- {
- unsigned int emmc;
- emmc = readl_relaxed(iommu->base + EMMC);
- emmc &= ~PM_ENABLE;
- writel_relaxed(emmc, iommu->base + EMMC);
- }
- static void iommu_pm_reset_counters(const struct iommu_info *iommu)
- {
- unsigned int emmc;
- emmc = readl_relaxed(iommu->base + EMMC);
- emmc |= PM_RESET;
- writel_relaxed(emmc, iommu->base + EMMC);
- }
- 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_value;
- unsigned int j;
- struct iommu_pmon_cnt_group *cnt_grp = &pmon->cnt_grp[0];
- reg_value = readl_relaxed(iommu->base + EMCS);
- reg_value &= PM_OVFL_FLAG;
- for (j = 0; j < cnt_grp->num_counters; ++j) {
- counter = &cnt_grp->counters[j];
- if (counter->enabled) {
- if (reg_value & (1 << counter->absolute_counter_no))
- counter->overflow_count++;
- }
- }
- /* Clear overflow */
- writel_relaxed(reg_value, iommu->base + EMCS);
- }
- 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(1);
- iommu_pm_check_for_overflow(pmon);
- iommu->ops->iommu_lock_release(1);
- 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 bit_no = counter->absolute_counter_no;
- unsigned int reg_value;
- /* Clear overflow of counter */
- reg_value = readl_relaxed(iommu->base + EMCS);
- reg_value &= (1 << bit_no);
- writel_relaxed(reg_value, iommu->base + EMCS);
- /* Enable counter */
- counter->enabled = 1;
- }
- static void iommu_pm_counter_disable(struct iommu_info *iommu,
- struct iommu_pmon_counter *counter)
- {
- unsigned int bit_no = counter->absolute_counter_no;
- unsigned int reg_value;
- /* Disable counter */
- counter->enabled = 0;
- /* Clear overflow of counter */
- reg_value = readl_relaxed(iommu->base + EMCS);
- reg_value &= (1 << bit_no);
- writel_relaxed(reg_value, iommu->base + EMCS);
- }
- /*
- * 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;
- unsigned int reg_value;
- /* Enable overflow interrupt for counter */
- reg_value = readl_relaxed(iommu->base + PMEVTYPER_(reg_no));
- reg_value |= PM_INT_EN;
- writel_relaxed(reg_value, iommu->base + PMEVTYPER_(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;
- unsigned int reg_value;
- /* Disable overflow interrupt for counter */
- reg_value = readl_relaxed(iommu->base + PMEVTYPER_(reg_no));
- reg_value &= ~PM_INT_EN;
- writel_relaxed(reg_value, iommu->base + PMEVTYPER_(reg_no));
- }
- static void iommu_pm_set_event_class(struct iommu_pmon *pmon,
- unsigned int count_no,
- unsigned int event_class)
- {
- unsigned int reg_no = count_no;
- unsigned int reg_value;
- int event = iommu_pm_translate_event_class(event_class);
- if (event == MSM_IOMMU_PMU_NO_EVENT_CLASS)
- event = 0;
- reg_value = readl_relaxed(pmon->iommu.base + PMEVTYPER_(reg_no));
- reg_value &= ~(PM_EVENT_TYPE_MASK << PM_EVENT_TYPE_SHIFT);
- reg_value |= (event & PM_EVENT_TYPE_MASK) << PM_EVENT_TYPE_SHIFT;
- writel_relaxed(reg_value, pmon->iommu.base + PMEVTYPER_(reg_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)
- {
- const struct iommu_info *iommu = &pmon->iommu;
- struct msm_iommu_drvdata *iommu_drvdata =
- dev_get_drvdata(iommu->iommu_dev);
- /* This is called during bootup device initialization so no need
- * for locking here.
- */
- iommu->ops->iommu_power_on(iommu_drvdata);
- iommu->ops->iommu_clk_on(iommu_drvdata);
- iommu_pm_set_int_active_high(iommu);
- iommu->ops->iommu_clk_off(iommu_drvdata);
- iommu->ops->iommu_power_off(iommu_drvdata);
- }
- 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_v0(void)
- {
- return &iommu_pm_hw_ops;
- }
- EXPORT_SYMBOL(iommu_pm_get_hw_ops_v0);
|