* [PATCH 3/9] PM / Domains: Drop genpd as in-param for pm_genpd_remove_device()
From: Ulf Hansson @ 2018-05-18 10:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526639490-12167-1-git-send-email-ulf.hansson@linaro.org>
There is no need to pass a genpd struct to pm_genpd_remove_device(), as we
already have the information about the PM domain (genpd) through the device
structure.
Additionally, we don't allow to remove a PM domain from a device, other
than the one it may have assigned to it, so really it does not make sense
to have a separate in-param for it.
For these reason, drop it and update the current only call to
pm_genpd_remove_device() from amdgpu_acp.
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian K?nig <christian.koenig@amd.com>
Cc: David (ChunMing) Zhou <David1.Zhou@amd.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
drivers/base/power/domain.c | 8 ++++----
drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 2 +-
include/linux/pm_domain.h | 5 ++---
3 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index d58aee3..f08fa15 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1475,13 +1475,13 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
/**
* pm_genpd_remove_device - Remove a device from an I/O PM domain.
- * @genpd: PM domain to remove the device from.
* @dev: Device to be removed.
*/
-int pm_genpd_remove_device(struct generic_pm_domain *genpd,
- struct device *dev)
+int pm_genpd_remove_device(struct device *dev)
{
- if (!genpd || genpd != genpd_lookup_dev(dev))
+ struct generic_pm_domain *genpd = genpd_lookup_dev(dev);
+
+ if (!genpd)
return -EINVAL;
return genpd_remove_device(genpd, dev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
index a29362f..1255804 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
@@ -513,7 +513,7 @@ static int acp_hw_fini(void *handle)
if (adev->acp.acp_genpd) {
for (i = 0; i < ACP_DEVS ; i++) {
dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i);
- ret = pm_genpd_remove_device(&adev->acp.acp_genpd->gpd, dev);
+ ret = pm_genpd_remove_device(dev);
/* If removal fails, dont giveup and try rest */
if (ret)
dev_err(dev, "remove dev from genpd failed\n");
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 79888fb..42e0d64 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -144,7 +144,7 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
}
int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev);
-int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev);
+int pm_genpd_remove_device(struct device *dev);
int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *new_subdomain);
int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
@@ -167,8 +167,7 @@ static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
{
return -ENOSYS;
}
-static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
- struct device *dev)
+static inline int pm_genpd_remove_device(struct device *dev)
{
return -ENOSYS;
}
--
2.7.4
^ permalink raw reply related
* [PATCH 2/9] PM / Domains: Drop __pm_genpd_add_device()
From: Ulf Hansson @ 2018-05-18 10:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526639490-12167-1-git-send-email-ulf.hansson@linaro.org>
There are still a few non-DT existing users of genpd, however neither of
them uses __pm_genpd_add_device(), hence let's drop it.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
drivers/base/power/domain.c | 10 ++++------
include/linux/pm_domain.h | 14 +++-----------
2 files changed, 7 insertions(+), 17 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index da6c886..d58aee3 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1414,23 +1414,21 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
}
/**
- * __pm_genpd_add_device - Add a device to an I/O PM domain.
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
* @genpd: PM domain to add the device to.
* @dev: Device to be added.
- * @td: Set of PM QoS timing parameters to attach to the device.
*/
-int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
- struct gpd_timing_data *td)
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
{
int ret;
mutex_lock(&gpd_list_lock);
- ret = genpd_add_device(genpd, dev, td);
+ ret = genpd_add_device(genpd, dev, NULL);
mutex_unlock(&gpd_list_lock);
return ret;
}
-EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
+EXPORT_SYMBOL_GPL(pm_genpd_add_device);
static int genpd_remove_device(struct generic_pm_domain *genpd,
struct device *dev)
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index c847e9a..79888fb 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -143,8 +143,7 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
return to_gpd_data(dev->power.subsys_data->domain_data);
}
-int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
- struct gpd_timing_data *td);
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev);
int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev);
int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *new_subdomain);
@@ -163,9 +162,8 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
{
return ERR_PTR(-ENOSYS);
}
-static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd,
- struct device *dev,
- struct gpd_timing_data *td)
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+ struct device *dev)
{
return -ENOSYS;
}
@@ -204,12 +202,6 @@ static inline int dev_pm_genpd_set_performance_state(struct device *dev,
#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
#endif
-static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
- struct device *dev)
-{
- return __pm_genpd_add_device(genpd, dev, NULL);
-}
-
#ifdef CONFIG_PM_GENERIC_DOMAINS_SLEEP
void pm_genpd_syscore_poweroff(struct device *dev);
void pm_genpd_syscore_poweron(struct device *dev);
--
2.7.4
^ permalink raw reply related
* [PATCH 1/9] PM / Domains: Drop extern declarations of functions in pm_domain.h
From: Ulf Hansson @ 2018-05-18 10:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526639490-12167-1-git-send-email-ulf.hansson@linaro.org>
Using "extern" to declare a function in a public header file is somewhat
pointless, but also doesn't hurt. However, to make all the function
declarations in pm_domain.h to be consistent, let's drop the use of
"extern".
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
---
include/linux/pm_domain.h | 51 +++++++++++++++++++++--------------------------
1 file changed, 23 insertions(+), 28 deletions(-)
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 4e57640..c847e9a 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -143,21 +143,17 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
return to_gpd_data(dev->power.subsys_data->domain_data);
}
-extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
- struct device *dev,
- struct gpd_timing_data *td);
-
-extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
- struct device *dev);
-extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
- struct generic_pm_domain *new_subdomain);
-extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
- struct generic_pm_domain *target);
-extern int pm_genpd_init(struct generic_pm_domain *genpd,
- struct dev_power_governor *gov, bool is_off);
-extern int pm_genpd_remove(struct generic_pm_domain *genpd);
-extern int dev_pm_genpd_set_performance_state(struct device *dev,
- unsigned int state);
+int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
+ struct gpd_timing_data *td);
+int pm_genpd_remove_device(struct generic_pm_domain *genpd, struct device *dev);
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+ struct generic_pm_domain *new_subdomain);
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+ struct generic_pm_domain *target);
+int pm_genpd_init(struct generic_pm_domain *genpd,
+ struct dev_power_governor *gov, bool is_off);
+int pm_genpd_remove(struct generic_pm_domain *genpd);
+int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state);
extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov;
@@ -215,8 +211,8 @@ static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
}
#ifdef CONFIG_PM_GENERIC_DOMAINS_SLEEP
-extern void pm_genpd_syscore_poweroff(struct device *dev);
-extern void pm_genpd_syscore_poweron(struct device *dev);
+void pm_genpd_syscore_poweroff(struct device *dev);
+void pm_genpd_syscore_poweron(struct device *dev);
#else
static inline void pm_genpd_syscore_poweroff(struct device *dev) {}
static inline void pm_genpd_syscore_poweron(struct device *dev) {}
@@ -240,14 +236,13 @@ int of_genpd_add_provider_simple(struct device_node *np,
int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data);
void of_genpd_del_provider(struct device_node *np);
-extern int of_genpd_add_device(struct of_phandle_args *args,
- struct device *dev);
-extern int of_genpd_add_subdomain(struct of_phandle_args *parent,
- struct of_phandle_args *new_subdomain);
-extern struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
-extern int of_genpd_parse_idle_states(struct device_node *dn,
- struct genpd_power_state **states, int *n);
-extern unsigned int of_genpd_opp_to_performance_state(struct device *dev,
+int of_genpd_add_device(struct of_phandle_args *args, struct device *dev);
+int of_genpd_add_subdomain(struct of_phandle_args *parent,
+ struct of_phandle_args *new_subdomain);
+struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
+int of_genpd_parse_idle_states(struct device_node *dn,
+ struct genpd_power_state **states, int *n);
+unsigned int of_genpd_opp_to_performance_state(struct device *dev,
struct device_node *opp_node);
int genpd_dev_pm_attach(struct device *dev);
@@ -304,9 +299,9 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
#ifdef CONFIG_PM
-extern int dev_pm_domain_attach(struct device *dev, bool power_on);
-extern void dev_pm_domain_detach(struct device *dev, bool power_off);
-extern void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd);
+int dev_pm_domain_attach(struct device *dev, bool power_on);
+void dev_pm_domain_detach(struct device *dev, bool power_off);
+void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd);
#else
static inline int dev_pm_domain_attach(struct device *dev, bool power_on)
{
--
2.7.4
^ permalink raw reply related
* [PATCH 0/9] PM / Domains: Add support for multi PM domains per device
From: Ulf Hansson @ 2018-05-18 10:31 UTC (permalink / raw)
To: linux-arm-kernel
There are devices that are partitioned across multiple PM domains. Currently
these can't be supported well by the available PM infrastructures we have in
the kernel. This series is an attempt to address this.
The interesting parts happens from patch 5 an onwards, including a minor DT
update to the existing power-domain bindings, the 4 earlier are just trivial
clean-ups of some related code in genpd, which I happened to stumble over.
Some additional background:
One existing case where devices are partitioned across multiple PM domains, is
the Nvida Tegra 124/210 X-USB subsystem. A while ago Jon Hunter (Nvidia) sent a
series, trying to address these issues, however this is a new approach, while
it re-uses the same concepts from DT point of view.
The Tegra 124/210 X-USB subsystem contains of a host controller and a device
controller. Each controller have its own independent PM domain, but are being
partitioned across another shared PM domain for the USB super-speed logic.
Currently to make the drivers work, either the related PM domains needs to stay
powered on always or the PM domain topology needs to be in-correctly modelled
through sub-domains. In both cases PM domains may be powered on while they
don't need to be, so in the end this means - wasting power -.
As stated above, this series intends to address these problem from a PM
infrastructure point of view. More details are available in each changelog.
It should be noted that this series has been tested on HW, however only by using
a home-cooked test PM domain driver for genpd and together with a test driver.
This allowed me to play with PM domain (genpd), runtime PM and device links.
Any further deployment for real use cases are greatly appreciated. I am happy to
to help, if needed!
Kind regards
Ulf Hansson
Ulf Hansson (9):
PM / Domains: Drop extern declarations of functions in pm_domain.h
PM / Domains: Drop __pm_genpd_add_device()
PM / Domains: Drop genpd as in-param for pm_genpd_remove_device()
PM / Domains: Drop unused parameter in genpd_allocate_dev_data()
PM / Domains: dt: Allow power-domain property to be a list of phandles
PM / Domains: Don't attach devices in genpd with multi PM domains
PM / Domains: Split genpd_dev_pm_attach()
PM / Domains: Add support for multi PM domains per device to genpd
PM / Domains: Add dev_pm_domain_attach_by_id() to manage multi PM
domains
.../devicetree/bindings/power/power_domain.txt | 18 ++-
drivers/base/power/common.c | 33 ++++-
drivers/base/power/domain.c | 154 ++++++++++++++++-----
drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 2 +-
include/linux/pm_domain.h | 79 +++++------
5 files changed, 208 insertions(+), 78 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH 6/6] arm64: perf: Add support for chaining counters
From: Suzuki K Poulose @ 2018-05-18 10:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638943-2110-1-git-send-email-suzuki.poulose@arm.com>
Add support for chained event counters. PMUv3 allows chaining
a pair of adjacent PMU counters (with the lower counter number
being always "even"). The low counter is programmed to count
the event of interest and the high counter(odd numbered) is
programmed with a special event code (0x1e - Chain). Thus
we need special allocation schemes to make the full use of
available counters. So, we allocate the counters from either
ends. i.e, chained counters are allocated from the lower
end in pairs of two and the normal counters are allocated
from the higher number. Also makes necessary changes to
handle the chained events as a single event with 2 counters.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/kernel/perf_event.c | 226 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 202 insertions(+), 24 deletions(-)
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index ea8e060..5f81cd0 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -446,9 +446,11 @@ static struct attribute_group armv8_pmuv3_events_attr_group = {
};
PMU_FORMAT_ATTR(event, "config:0-15");
+PMU_FORMAT_ATTR(chain, "config1:0");
static struct attribute *armv8_pmuv3_format_attrs[] = {
&format_attr_event.attr,
+ &format_attr_chain.attr,
NULL,
};
@@ -457,6 +459,12 @@ static struct attribute_group armv8_pmuv3_format_attr_group = {
.attrs = armv8_pmuv3_format_attrs,
};
+static bool armv8pmu_event_is_chained(struct perf_event *event)
+{
+ return event->attr.config1 & 0x1;
+}
+
+
/*
* Perf Events' indices
*/
@@ -512,6 +520,36 @@ static inline int armv8pmu_select_counter(int idx)
return idx;
}
+static inline u32 armv8pmu_read_evcntr(int idx)
+{
+ return (armv8pmu_select_counter(idx) == idx) ?
+ read_sysreg(pmxevcntr_el0) : 0;
+}
+
+static inline u64 armv8pmu_read_chain_counter(int idx)
+{
+ u64 prev_hi, hi, lo;
+
+ do {
+ prev_hi = armv8pmu_read_evcntr(idx);
+ isb();
+ lo = armv8pmu_read_evcntr(idx - 1);
+ isb();
+ hi = armv8pmu_read_evcntr(idx);
+ isb();
+ } while (prev_hi != hi);
+
+ return (hi << 32) | lo;
+}
+
+static inline u64 armv8pmu_read_hw_counter(struct perf_event *event)
+{
+ int idx = event->hw.idx;
+
+ return armv8pmu_event_is_chained(event) ?
+ armv8pmu_read_chain_counter(idx) : armv8pmu_read_evcntr(idx);
+}
+
static inline u64 armv8pmu_read_counter(struct perf_event *event)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
@@ -524,12 +562,37 @@ static inline u64 armv8pmu_read_counter(struct perf_event *event)
smp_processor_id(), idx);
else if (idx == ARMV8_IDX_CYCLE_COUNTER)
value = read_sysreg(pmccntr_el0);
- else if (armv8pmu_select_counter(idx) == idx)
- value = read_sysreg(pmxevcntr_el0);
+ else
+ value = armv8pmu_read_hw_counter(event);
return value;
}
+static inline void armv8pmu_write_evcntr(int idx, u32 value)
+{
+ if (armv8pmu_select_counter(idx) == idx)
+ write_sysreg(value, pmxevcntr_el0);
+}
+
+static inline void armv8pmu_write_chain_counter(int idx, u64 value)
+{
+ armv8pmu_write_evcntr(idx, value >> 32);
+ isb();
+ armv8pmu_write_evcntr(idx - 1, value);
+ isb();
+}
+
+static inline void armv8pmu_write_hw_counter(struct perf_event *event,
+ u64 value)
+{
+ int idx = event->hw.idx;
+
+ if (armv8pmu_event_is_chained(event))
+ armv8pmu_write_chain_counter(idx, value);
+ else
+ armv8pmu_write_evcntr(idx, value);
+}
+
static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
@@ -541,8 +604,8 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
smp_processor_id(), idx);
else if (idx == ARMV8_IDX_CYCLE_COUNTER)
write_sysreg(value, pmccntr_el0);
- else if (armv8pmu_select_counter(idx) == idx)
- write_sysreg(value, pmxevcntr_el0);
+ else
+ armv8pmu_write_hw_counter(event, value);
}
static inline void armv8pmu_write_evtype(int idx, u32 val)
@@ -553,6 +616,28 @@ static inline void armv8pmu_write_evtype(int idx, u32 val)
}
}
+static inline void armv8pmu_write_event_type(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ /*
+ * For chained events, write the high counter event type
+ * followed by the low counter.
+ */
+ if (armv8pmu_event_is_chained(event)) {
+ u32 chain_evt = ARMV8_PMUV3_PERFCTR_CHAIN;
+
+ /* Set the filters as that of the main event for chain */
+ chain_evt |= hwc->config_base & ~ARMV8_PMU_EVTYPE_EVENT;
+ armv8pmu_write_evtype(idx, chain_evt);
+ isb();
+ idx--;
+ }
+
+ armv8pmu_write_evtype(idx, hwc->config_base);
+}
+
static inline int armv8pmu_enable_counter(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
@@ -560,6 +645,23 @@ static inline int armv8pmu_enable_counter(int idx)
return idx;
}
+static inline void armv8pmu_enable_event_counter(struct perf_event *event)
+{
+ int idx = event->hw.idx;
+
+ /*
+ * For chained events, we enable the high counter followed by
+ * the low counter.
+ */
+ armv8pmu_enable_counter(idx);
+
+ if (armv8pmu_event_is_chained(event)) {
+ isb();
+ armv8pmu_enable_counter(idx - 1);
+ }
+
+}
+
static inline int armv8pmu_disable_counter(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
@@ -567,6 +669,24 @@ static inline int armv8pmu_disable_counter(int idx)
return idx;
}
+static inline void armv8pmu_disable_event_counter(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ /*
+ * Disable the low counter followed by the high counter
+ * for chained events.
+ */
+ if (armv8pmu_event_is_chained(event)) {
+ armv8pmu_disable_counter(idx - 1);
+ isb();
+ }
+
+ armv8pmu_disable_counter(idx);
+}
+
+
static inline int armv8pmu_enable_intens(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
@@ -574,6 +694,12 @@ static inline int armv8pmu_enable_intens(int idx)
return idx;
}
+static inline int armv8pmu_enable_event_irq(struct perf_event *event)
+{
+ /* For chained events, enable the interrupt for only the high counter */
+ return armv8pmu_enable_intens(event->hw.idx);
+}
+
static inline int armv8pmu_disable_intens(int idx)
{
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
@@ -586,6 +712,11 @@ static inline int armv8pmu_disable_intens(int idx)
return idx;
}
+static inline int armv8pmu_disable_event_irq(struct perf_event *event)
+{
+ return armv8pmu_disable_intens(event->hw.idx);
+}
+
static inline u32 armv8pmu_getreset_flags(void)
{
u32 value;
@@ -603,10 +734,8 @@ static inline u32 armv8pmu_getreset_flags(void)
static void armv8pmu_enable_event(struct perf_event *event)
{
unsigned long flags;
- struct hw_perf_event *hwc = &event->hw;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
- int idx = hwc->idx;
/*
* Enable counter and interrupt, and set the counter to count
@@ -617,22 +746,22 @@ static void armv8pmu_enable_event(struct perf_event *event)
/*
* Disable counter
*/
- armv8pmu_disable_counter(idx);
+ armv8pmu_disable_event_counter(event);
/*
* Set event (if destined for PMNx counters).
*/
- armv8pmu_write_evtype(idx, hwc->config_base);
+ armv8pmu_write_event_type(event);
/*
* Enable interrupt for this counter
*/
- armv8pmu_enable_intens(idx);
+ armv8pmu_enable_event_irq(event);
/*
* Enable counter
*/
- armv8pmu_enable_counter(idx);
+ armv8pmu_enable_event_counter(event);
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
@@ -640,10 +769,8 @@ static void armv8pmu_enable_event(struct perf_event *event)
static void armv8pmu_disable_event(struct perf_event *event)
{
unsigned long flags;
- struct hw_perf_event *hwc = &event->hw;
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events);
- int idx = hwc->idx;
/*
* Disable counter and interrupt
@@ -651,14 +778,14 @@ static void armv8pmu_disable_event(struct perf_event *event)
raw_spin_lock_irqsave(&events->pmu_lock, flags);
/*
- * Disable counter
+ * Disable counter for this event
*/
- armv8pmu_disable_counter(idx);
+ armv8pmu_disable_event_counter(event);
/*
- * Disable interrupt for this counter
+ * Disable interrupt for this event counter
*/
- armv8pmu_disable_intens(idx);
+ armv8pmu_disable_event_irq(event);
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
@@ -747,6 +874,39 @@ static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
+static int armv8pmu_get_single_idx(struct pmu_hw_events *cpuc,
+ struct arm_pmu *cpu_pmu)
+{
+ int idx;
+
+ for (idx = cpu_pmu->num_events - 1; idx >= ARMV8_IDX_COUNTER0; ++idx)
+ if (!test_and_set_bit(idx, cpuc->used_mask))
+ return idx;
+ return -EAGAIN;
+}
+
+static int armv8pmu_get_chain_idx(struct pmu_hw_events *cpuc,
+ struct arm_pmu *cpu_pmu)
+{
+ int idx;
+
+ /*
+ * Chaining requires two consecutive event counters, where
+ * the lower idx must be even. We allocate chain events
+ * from the lower index (i.e, counter0) and the single events
+ * from the higher end to maximise the utilisation.
+ */
+ for (idx = ARMV8_IDX_COUNTER0 + 1; idx < cpu_pmu->num_events; idx += 2)
+ if (!test_and_set_bit(idx, cpuc->used_mask)) {
+ /* Check if the preceding even counter is available */
+ if (!test_and_set_bit(idx - 1, cpuc->used_mask))
+ return idx;
+ /* Release the Odd counter */
+ clear_bit(idx, cpuc->used_mask);
+ }
+ return -EAGAIN;
+}
+
static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
struct perf_event *event)
{
@@ -755,7 +915,10 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
struct hw_perf_event *hwc = &event->hw;
unsigned long evtype = hwc->config_base & ARMV8_PMU_EVTYPE_EVENT;
- /* Always prefer to place a cycle counter into the cycle counter. */
+ /*
+ * Always prefer to place a cycle counter into the cycle counter
+ * irrespective of whether we are counting 32bit/64bit
+ */
if (evtype == ARMV8_PMUV3_PERFCTR_CPU_CYCLES) {
if (!test_and_set_bit(ARMV8_IDX_CYCLE_COUNTER, cpuc->used_mask))
return ARMV8_IDX_CYCLE_COUNTER;
@@ -764,13 +927,21 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
/*
* Otherwise use events counters
*/
- for (idx = ARMV8_IDX_COUNTER0; idx < cpu_pmu->num_events; ++idx) {
- if (!test_and_set_bit(idx, cpuc->used_mask))
- return idx;
- }
+ idx = armv8pmu_event_is_chained(event) ?
+ armv8pmu_get_chain_idx(cpuc, cpu_pmu) :
+ armv8pmu_get_single_idx(cpuc, cpu_pmu);
- /* The counters are all in use. */
- return -EAGAIN;
+ return idx;
+}
+
+static void armv8pmu_clear_event_idx(struct pmu_hw_events *cpuc,
+ struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ clear_bit(hwc->idx, cpuc->used_mask);
+ if (armv8pmu_event_is_chained(event))
+ clear_bit(hwc->idx - 1, cpuc->used_mask);
}
/*
@@ -845,8 +1016,14 @@ static int __armv8_pmuv3_map_event(struct perf_event *event,
&armv8_pmuv3_perf_cache_map,
ARMV8_PMU_EVTYPE_EVENT);
- if (hw_event_id == ARMV8_PMUV3_PERFCTR_CPU_CYCLES)
+ if (hw_event_id == ARMV8_PMUV3_PERFCTR_CPU_CYCLES) {
+ /* Prevent chaining for cycle counter */
+ if (armv8pmu_event_is_chained(event))
+ return -EINVAL;
event->hw.flags |= ARMPMU_EVT_LONG;
+ } else if (armv8pmu_event_is_chained(event)) {
+ event->hw.flags |= ARMPMU_EVT_LONG;
+ }
/* Onl expose micro/arch events supported by this PMU */
if ((hw_event_id > 0) && (hw_event_id < ARMV8_PMUV3_MAX_COMMON_EVENTS)
@@ -954,6 +1131,7 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->read_counter = armv8pmu_read_counter,
cpu_pmu->write_counter = armv8pmu_write_counter,
cpu_pmu->get_event_idx = armv8pmu_get_event_idx,
+ cpu_pmu->clear_event_idx = armv8pmu_clear_event_idx,
cpu_pmu->start = armv8pmu_start,
cpu_pmu->stop = armv8pmu_stop,
cpu_pmu->reset = armv8pmu_reset,
--
2.7.4
^ permalink raw reply related
* [PATCH 5/6] arm_pmu: Tidy up clear_event_idx call backs
From: Suzuki K Poulose @ 2018-05-18 10:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638943-2110-1-git-send-email-suzuki.poulose@arm.com>
The armpmu uses get_event_idx callback to allocate an event
counter for a given event, which marks the selected counter
as "used". Now, when we delete the counter, the arm_pmu goes
ahead and clears the "used" bit and then invokes the "clear_event_idx"
call back, which kind of splits the job between the core code
and the backend. Tidy this up by relying on the clear_event_idx
to do the book keeping, if available. Otherwise, let the core
driver do the default "clear" bit operation. This will be useful
for adding the chained event support, where we leave the event
idx maintenance to the backend.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm/kernel/perf_event_v7.c | 2 ++
drivers/perf/arm_pmu.c | 15 +++++++++++----
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 2f3c06d..1c28b23 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -1639,6 +1639,7 @@ static void krait_pmu_clear_event_idx(struct pmu_hw_events *cpuc,
bool venum_event = EVENT_VENUM(hwc->config_base);
bool krait_event = EVENT_CPU(hwc->config_base);
+ clear_bit(hwc->idx, cpuc->used_mask);
if (venum_event || krait_event) {
bit = krait_event_to_bit(event, region, group);
clear_bit(bit, cpuc->used_mask);
@@ -1968,6 +1969,7 @@ static void scorpion_pmu_clear_event_idx(struct pmu_hw_events *cpuc,
bool venum_event = EVENT_VENUM(hwc->config_base);
bool scorpion_event = EVENT_CPU(hwc->config_base);
+ clear_bit(hwc->idx, cpuc->used_mask);
if (venum_event || scorpion_event) {
bit = scorpion_event_to_bit(event, region, group);
clear_bit(bit, cpuc->used_mask);
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 1adabb5..7207d01 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -240,6 +240,16 @@ static void armpmu_start(struct perf_event *event, int flags)
armpmu->enable(event);
}
+static void armpmu_clear_event_idx(struct arm_pmu *armpmu,
+ struct pmu_hw_events *hw_events,
+ struct perf_event *event)
+{
+ if (armpmu->clear_event_idx)
+ armpmu->clear_event_idx(hw_events, event);
+ else
+ clear_bit(event->hw.idx, hw_events->used_mask);
+}
+
static void
armpmu_del(struct perf_event *event, int flags)
{
@@ -250,10 +260,7 @@ armpmu_del(struct perf_event *event, int flags)
armpmu_stop(event, PERF_EF_UPDATE);
hw_events->events[idx] = NULL;
- clear_bit(idx, hw_events->used_mask);
- if (armpmu->clear_event_idx)
- armpmu->clear_event_idx(hw_events, event);
-
+ armpmu_clear_event_idx(armpmu, hw_events, event);
perf_event_update_userpage(event);
}
--
2.7.4
^ permalink raw reply related
* [PATCH 4/6] arm64: perf: Make the cycle counter 64bit by default
From: Suzuki K Poulose @ 2018-05-18 10:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638943-2110-1-git-send-email-suzuki.poulose@arm.com>
Make the cycle counter by setting the ARPMU_EVT_LONG flag
to indicate that it uses 64bit counter.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/kernel/perf_event.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 7660b7a..ea8e060 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -517,7 +517,7 @@ static inline u64 armv8pmu_read_counter(struct perf_event *event)
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
- u32 value = 0;
+ u64 value = 0;
if (!armv8pmu_counter_valid(cpu_pmu, idx))
pr_err("CPU%u reading wrong counter %d\n",
@@ -539,15 +539,9 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
if (!armv8pmu_counter_valid(cpu_pmu, idx))
pr_err("CPU%u writing wrong counter %d\n",
smp_processor_id(), idx);
- else if (idx == ARMV8_IDX_CYCLE_COUNTER) {
- /*
- * Set the upper 32bits as this is a 64bit counter but we only
- * count using the lower 32bits and we want an interrupt when
- * it overflows.
- */
- value |= 0xffffffff00000000ULL;
+ else if (idx == ARMV8_IDX_CYCLE_COUNTER)
write_sysreg(value, pmccntr_el0);
- } else if (armv8pmu_select_counter(idx) == idx)
+ else if (armv8pmu_select_counter(idx) == idx)
write_sysreg(value, pmxevcntr_el0);
}
@@ -851,6 +845,9 @@ static int __armv8_pmuv3_map_event(struct perf_event *event,
&armv8_pmuv3_perf_cache_map,
ARMV8_PMU_EVTYPE_EVENT);
+ if (hw_event_id == ARMV8_PMUV3_PERFCTR_CPU_CYCLES)
+ event->hw.flags |= ARMPMU_EVT_LONG;
+
/* Onl expose micro/arch events supported by this PMU */
if ((hw_event_id > 0) && (hw_event_id < ARMV8_PMUV3_MAX_COMMON_EVENTS)
&& test_bit(hw_event_id, armpmu->pmceid_bitmap)) {
--
2.7.4
^ permalink raw reply related
* [PATCH 3/6] arm_pmu: Add support for long event counters
From: Suzuki K Poulose @ 2018-05-18 10:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638943-2110-1-git-send-email-suzuki.poulose@arm.com>
Each PMU has a set of fixed width event counters. But in some
special cases, the events could be counted using a counter which
effectively has twice the normal width of a coutner.
e.g, Arm V8 PMUv3 has a 64 bit cycle counter which can count
only the CPU cylces. Also, the PMU can chain the event counters
to effectively count as a 64bit counter.
Add support for tracking the events that uses double the normal
counter size. This only affects the periods set for each counter.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
drivers/perf/arm_pmu.c | 25 ++++++++++++++++++++++---
include/linux/perf/arm_pmu.h | 6 ++++++
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index e23e1a1..1adabb5 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -33,6 +33,21 @@ static inline u64 arm_pmu_max_period(struct arm_pmu *pmu)
return (((u64)1) << (pmu->counter_width)) - 1;
}
+static inline u64 arm_pmu_get_event_max_period(struct arm_pmu *pmu,
+ struct perf_event *event)
+{
+ u64 period = arm_pmu_max_period(pmu);
+
+ /*
+ * To prevent shift-counter-overflow warning, create the
+ * mask, by shift + OR sequence.
+ */
+ if (event->hw.flags & ARMPMU_EVT_LONG)
+ period = (period << pmu->counter_width) | period;
+
+ return period;
+}
+
static int
armpmu_map_cache_event(const unsigned (*cache_map)
[PERF_COUNT_HW_CACHE_MAX]
@@ -122,7 +137,7 @@ int armpmu_event_set_period(struct perf_event *event)
u64 max_period;
int ret = 0;
- max_period = arm_pmu_max_period(armpmu);
+ max_period = arm_pmu_get_event_max_period(armpmu, event);
if (unlikely(left <= -period)) {
left = period;
local64_set(&hwc->period_left, left);
@@ -148,7 +163,7 @@ int armpmu_event_set_period(struct perf_event *event)
local64_set(&hwc->prev_count, (u64)-left);
- armpmu->write_counter(event, (u64)(-left) & 0xffffffff);
+ armpmu->write_counter(event, (u64)(-left) & max_period);
perf_event_update_userpage(event);
@@ -160,7 +175,7 @@ u64 armpmu_event_update(struct perf_event *event)
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
u64 delta, prev_raw_count, new_raw_count;
- u64 max_period = arm_pmu_max_period(armpmu);
+ u64 max_period = arm_pmu_get_event_max_period(armpmu, event);
again:
prev_raw_count = local64_read(&hwc->prev_count);
@@ -368,6 +383,7 @@ __hw_perf_event_init(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw;
int mapping;
+ hwc->flags = 0;
mapping = armpmu->map_event(event);
if (mapping < 0) {
@@ -670,6 +686,9 @@ static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd)
continue;
event = hw_events->events[idx];
+ /* Chained events could use multiple counters */
+ if (!event)
+ continue;
switch (cmd) {
case CPU_PM_ENTER:
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 705e8c3..ed7e3f7 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -25,6 +25,12 @@
*/
#define ARMPMU_MAX_HWEVENTS 32
+/*
+ * ARM PMU hw_event flags
+ */
+/* Event uses a counter with double the normal width */
+#define ARMPMU_EVT_LONG 1
+
#define HW_OP_UNSUPPORTED 0xFFFF
#define C(_x) PERF_COUNT_HW_CACHE_##_x
#define CACHE_OP_UNSUPPORTED 0xFFFF
--
2.7.4
^ permalink raw reply related
* [PATCH 2/6] arm_pmu: Change API to support 64bit counter values
From: Suzuki K Poulose @ 2018-05-18 10:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638943-2110-1-git-send-email-suzuki.poulose@arm.com>
Convert the {read/write}_counter APIs to handle 64bit values
to enable supporting chained event counters.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm/kernel/perf_event_v6.c | 4 ++--
arch/arm/kernel/perf_event_v7.c | 4 ++--
arch/arm/kernel/perf_event_xscale.c | 4 ++--
arch/arm64/kernel/perf_event.c | 9 ++++-----
include/linux/perf/arm_pmu.h | 4 ++--
5 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c
index d52a3fa..720c19e 100644
--- a/arch/arm/kernel/perf_event_v6.c
+++ b/arch/arm/kernel/perf_event_v6.c
@@ -233,7 +233,7 @@ armv6_pmcr_counter_has_overflowed(unsigned long pmcr,
return ret;
}
-static inline u32 armv6pmu_read_counter(struct perf_event *event)
+static inline u64 armv6pmu_read_counter(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
@@ -251,7 +251,7 @@ static inline u32 armv6pmu_read_counter(struct perf_event *event)
return value;
}
-static inline void armv6pmu_write_counter(struct perf_event *event, u32 value)
+static inline void armv6pmu_write_counter(struct perf_event *event, u64 value)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 3d8ec6a..2f3c06d 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -743,7 +743,7 @@ static inline void armv7_pmnc_select_counter(int idx)
isb();
}
-static inline u32 armv7pmu_read_counter(struct perf_event *event)
+static inline u64 armv7pmu_read_counter(struct perf_event *event)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
@@ -763,7 +763,7 @@ static inline u32 armv7pmu_read_counter(struct perf_event *event)
return value;
}
-static inline void armv7pmu_write_counter(struct perf_event *event, u32 value)
+static inline void armv7pmu_write_counter(struct perf_event *event, u64 value)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c
index 6eb0e21..3f2e398 100644
--- a/arch/arm/kernel/perf_event_xscale.c
+++ b/arch/arm/kernel/perf_event_xscale.c
@@ -317,7 +317,7 @@ static void xscale1pmu_stop(struct arm_pmu *cpu_pmu)
raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
-static inline u32 xscale1pmu_read_counter(struct perf_event *event)
+static inline u64 xscale1pmu_read_counter(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
@@ -338,7 +338,7 @@ static inline u32 xscale1pmu_read_counter(struct perf_event *event)
return val;
}
-static inline void xscale1pmu_write_counter(struct perf_event *event, u32 val)
+static inline void xscale1pmu_write_counter(struct perf_event *event, u64 val)
{
struct hw_perf_event *hwc = &event->hw;
int counter = hwc->idx;
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 408f92c..7660b7a 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -512,7 +512,7 @@ static inline int armv8pmu_select_counter(int idx)
return idx;
}
-static inline u32 armv8pmu_read_counter(struct perf_event *event)
+static inline u64 armv8pmu_read_counter(struct perf_event *event)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
@@ -530,7 +530,7 @@ static inline u32 armv8pmu_read_counter(struct perf_event *event)
return value;
}
-static inline void armv8pmu_write_counter(struct perf_event *event, u32 value)
+static inline void armv8pmu_write_counter(struct perf_event *event, u64 value)
{
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
@@ -545,9 +545,8 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u32 value)
* count using the lower 32bits and we want an interrupt when
* it overflows.
*/
- u64 value64 = 0xffffffff00000000ULL | value;
-
- write_sysreg(value64, pmccntr_el0);
+ value |= 0xffffffff00000000ULL;
+ write_sysreg(value, pmccntr_el0);
} else if (armv8pmu_select_counter(idx) == idx)
write_sysreg(value, pmxevcntr_el0);
}
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index c8c31cf..705e8c3 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -87,8 +87,8 @@ struct arm_pmu {
struct perf_event *event);
int (*set_event_filter)(struct hw_perf_event *evt,
struct perf_event_attr *attr);
- u32 (*read_counter)(struct perf_event *event);
- void (*write_counter)(struct perf_event *event, u32 val);
+ u64 (*read_counter)(struct perf_event *event);
+ void (*write_counter)(struct perf_event *event, u64 val);
void (*start)(struct arm_pmu *);
void (*stop)(struct arm_pmu *);
void (*reset)(void *);
--
2.7.4
^ permalink raw reply related
* [PATCH 1/6] arm_pmu: Refactor maximum period handling
From: Suzuki K Poulose @ 2018-05-18 10:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638943-2110-1-git-send-email-suzuki.poulose@arm.com>
Each PMU defines their max_period of the counter as the maximum
value that can be counted. In order to support chaining of the
counters, change this parameter to indicate the counter width
to deduce the max_period. This will be useful to compute the
max_period for chained counters.
No functional changes.
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm/kernel/perf_event_v6.c | 4 ++--
arch/arm/kernel/perf_event_v7.c | 2 +-
arch/arm/kernel/perf_event_xscale.c | 4 ++--
arch/arm64/kernel/perf_event.c | 2 +-
drivers/perf/arm_pmu.c | 16 ++++++++++++----
include/linux/perf/arm_pmu.h | 2 +-
6 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c
index 1d7061a..d52a3fa 100644
--- a/arch/arm/kernel/perf_event_v6.c
+++ b/arch/arm/kernel/perf_event_v6.c
@@ -497,7 +497,7 @@ static void armv6pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->stop = armv6pmu_stop;
cpu_pmu->map_event = armv6_map_event;
cpu_pmu->num_events = 3;
- cpu_pmu->max_period = (1LLU << 32) - 1;
+ cpu_pmu->counter_width = 32;
}
static int armv6_1136_pmu_init(struct arm_pmu *cpu_pmu)
@@ -548,7 +548,7 @@ static int armv6mpcore_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->stop = armv6pmu_stop;
cpu_pmu->map_event = armv6mpcore_map_event;
cpu_pmu->num_events = 3;
- cpu_pmu->max_period = (1LLU << 32) - 1;
+ cpu_pmu->counter_width = 32;
return 0;
}
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 870b66c..3d8ec6a 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -1171,7 +1171,7 @@ static void armv7pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->start = armv7pmu_start;
cpu_pmu->stop = armv7pmu_stop;
cpu_pmu->reset = armv7pmu_reset;
- cpu_pmu->max_period = (1LLU << 32) - 1;
+ cpu_pmu->counter_width = 32;
};
static void armv7_read_num_pmnc_events(void *info)
diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c
index fcf218d..6eb0e21 100644
--- a/arch/arm/kernel/perf_event_xscale.c
+++ b/arch/arm/kernel/perf_event_xscale.c
@@ -375,7 +375,7 @@ static int xscale1pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->stop = xscale1pmu_stop;
cpu_pmu->map_event = xscale_map_event;
cpu_pmu->num_events = 3;
- cpu_pmu->max_period = (1LLU << 32) - 1;
+ cpu_pmu->counter_width = 32;
return 0;
}
@@ -745,7 +745,7 @@ static int xscale2pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->stop = xscale2pmu_stop;
cpu_pmu->map_event = xscale_map_event;
cpu_pmu->num_events = 5;
- cpu_pmu->max_period = (1LLU << 32) - 1;
+ cpu_pmu->counter_width = 32;
return 0;
}
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 85a251b..408f92c 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -961,7 +961,7 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->start = armv8pmu_start,
cpu_pmu->stop = armv8pmu_stop,
cpu_pmu->reset = armv8pmu_reset,
- cpu_pmu->max_period = (1LLU << 32) - 1,
+ cpu_pmu->counter_width = 32;
cpu_pmu->set_event_filter = armv8pmu_set_event_filter;
return 0;
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 1a0d340..e23e1a1 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -28,6 +28,11 @@
static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
static DEFINE_PER_CPU(int, cpu_irq);
+static inline u64 arm_pmu_max_period(struct arm_pmu *pmu)
+{
+ return (((u64)1) << (pmu->counter_width)) - 1;
+}
+
static int
armpmu_map_cache_event(const unsigned (*cache_map)
[PERF_COUNT_HW_CACHE_MAX]
@@ -114,8 +119,10 @@ int armpmu_event_set_period(struct perf_event *event)
struct hw_perf_event *hwc = &event->hw;
s64 left = local64_read(&hwc->period_left);
s64 period = hwc->sample_period;
+ u64 max_period;
int ret = 0;
+ max_period = arm_pmu_max_period(armpmu);
if (unlikely(left <= -period)) {
left = period;
local64_set(&hwc->period_left, left);
@@ -136,8 +143,8 @@ int armpmu_event_set_period(struct perf_event *event)
* effect we are reducing max_period to account for
* interrupt latency (and we are being very conservative).
*/
- if (left > (armpmu->max_period >> 1))
- left = armpmu->max_period >> 1;
+ if (left > (max_period >> 1))
+ left = (max_period >> 1);
local64_set(&hwc->prev_count, (u64)-left);
@@ -153,6 +160,7 @@ u64 armpmu_event_update(struct perf_event *event)
struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
u64 delta, prev_raw_count, new_raw_count;
+ u64 max_period = arm_pmu_max_period(armpmu);
again:
prev_raw_count = local64_read(&hwc->prev_count);
@@ -162,7 +170,7 @@ u64 armpmu_event_update(struct perf_event *event)
new_raw_count) != prev_raw_count)
goto again;
- delta = (new_raw_count - prev_raw_count) & armpmu->max_period;
+ delta = (new_raw_count - prev_raw_count) & max_period;
local64_add(delta, &event->count);
local64_sub(delta, &hwc->period_left);
@@ -402,7 +410,7 @@ __hw_perf_event_init(struct perf_event *event)
* is far less likely to overtake the previous one unless
* you have some serious IRQ latency issues.
*/
- hwc->sample_period = armpmu->max_period >> 1;
+ hwc->sample_period = arm_pmu_max_period(armpmu) >> 1;
hwc->last_period = hwc->sample_period;
local64_set(&hwc->period_left, hwc->sample_period);
}
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 40036a5..c8c31cf 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -94,7 +94,7 @@ struct arm_pmu {
void (*reset)(void *);
int (*map_event)(struct perf_event *event);
int num_events;
- u64 max_period;
+ u8 counter_width;
bool secure_access; /* 32-bit ARM only */
#define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40
DECLARE_BITMAP(pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS);
--
2.7.4
^ permalink raw reply related
* [PATCH 0/6] arm64: perf: Support for chaining event counters
From: Suzuki K Poulose @ 2018-05-18 10:22 UTC (permalink / raw)
To: linux-arm-kernel
This series adds support for counting PMU events using chained counters.
The Arm v8 PMUv3 supports combining two adjacent 32bit counters
(even and odd) to count a given "event" in 64bit mode. This series adds
the support for this mode in the core arm_pmu driver infrastructure and
also adds the support for armv8 64bit kernel PMU. This also removes the
restriction of using CPU Cycles counter (naturally 64bit) in 32bit mode.
Tested on Juno, Fast models. Applies on 4.17-rc4
Suzuki K Poulose (6):
arm_pmu: Refactor maximum period handling
arm_pmu: Change API to support 64bit counter values
arm_pmu: Add support for long event counters
arm64: perf: Make the cycle counter 64bit by default
armpmu: Tidy up clear_event_idx call backs
arm64: perf: Add support for chaining counters
arch/arm/kernel/perf_event_v6.c | 8 +-
arch/arm/kernel/perf_event_v7.c | 8 +-
arch/arm/kernel/perf_event_xscale.c | 8 +-
arch/arm64/kernel/perf_event.c | 258 ++++++++++++++++++++++++++++++------
drivers/perf/arm_pmu.c | 52 ++++++--
include/linux/perf/arm_pmu.h | 12 +-
6 files changed, 281 insertions(+), 65 deletions(-)
--
2.7.4
^ permalink raw reply
* [PATCH v4 3/3] arm64: Introduce command line parameter to disable CNP
From: Vladimir Murzin @ 2018-05-18 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638022-4137-1-git-send-email-vladimir.murzin@arm.com>
There are cases when activating of Common Not Private (CNP) feature
might not be desirable; this patch allows to forcefully disable CNP
even it is supported by hardware.
Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
Documentation/admin-guide/kernel-parameters.txt | 4 ++++
arch/arm64/kernel/cpufeature.c | 11 ++++++++++-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 11fc28e..8f59d47 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2636,6 +2636,10 @@
noclflush [BUGS=X86] Don't use the CLFLUSH instruction
+ nocnp [ARM64]
+ Disable CNP (Common not Private translations)
+ even if it is supported by processor.
+
nodelayacct [KNL] Disable per-task delay accounting
nodsp [SH] Disable hardware DSP at boot time.
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 199e9dd..eee6a31 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -860,6 +860,15 @@ static bool has_cache_dic(const struct arm64_cpu_capabilities *entry,
return read_sanitised_ftr_reg(SYS_CTR_EL0) & BIT(CTR_DIC_SHIFT);
}
+static bool nocnp;
+
+static int __init early_nocnp(char *p)
+{
+ nocnp = true;
+ return 0;
+}
+early_param("nocnp", early_nocnp);
+
static bool __maybe_unused
has_useable_cnp(const struct arm64_cpu_capabilities *entry, int scope)
{
@@ -867,7 +876,7 @@ has_useable_cnp(const struct arm64_cpu_capabilities *entry, int scope)
if (elfcorehdr_size)
return false;
#endif
- return has_cpuid_feature(entry, scope);
+ return has_cpuid_feature(entry, scope) && !nocnp;
}
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
--
2.0.0
^ permalink raw reply related
* [PATCH v4 2/3] arm64: KVM: Enable Common Not Private translations
From: Vladimir Murzin @ 2018-05-18 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638022-4137-1-git-send-email-vladimir.murzin@arm.com>
We rely on cpufeature framework to detect and enable CNP so for KVM we
need to patch hyp to set CNP bit just before TTBR0_EL2 gets written.
For the guest we encode CNP bit while building vttbr, so we don't need
to bother with that in a world switch.
Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
arch/arm/include/asm/kvm_mmu.h | 5 +++++
arch/arm64/include/asm/kvm_mmu.h | 5 +++++
arch/arm64/kvm/hyp-init.S | 3 +++
virt/kvm/arm/arm.c | 4 ++--
4 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 707a1f0..9c83710 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -321,6 +321,11 @@ static inline int kvm_map_vectors(void)
#define kvm_phys_to_vttbr(addr) (addr)
+static inline bool kvm_cpu_has_cnp(void)
+{
+ return false;
+}
+
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_KVM_MMU_H__ */
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 0821109..c9ee6c3 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -459,5 +459,10 @@ static inline int kvm_map_vectors(void)
#define kvm_phys_to_vttbr(addr) phys_to_ttbr(addr)
+static inline bool kvm_cpu_has_cnp(void)
+{
+ return system_supports_cnp();
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
index 6fd91b3..94998d5 100644
--- a/arch/arm64/kvm/hyp-init.S
+++ b/arch/arm64/kvm/hyp-init.S
@@ -64,6 +64,9 @@ __do_hyp_init:
b.lo __kvm_handle_stub_hvc
phys_to_ttbr x4, x0
+alternative_if ARM64_HAS_CNP
+ orr x4, x4, #TTBR_CNP_BIT
+alternative_else_nop_endif
msr ttbr0_el2, x4
mrs x4, tcr_el1
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
index a4c1b76..9a651a2 100644
--- a/virt/kvm/arm/arm.c
+++ b/virt/kvm/arm/arm.c
@@ -472,7 +472,7 @@ static bool need_new_vmid_gen(struct kvm *kvm)
static void update_vttbr(struct kvm *kvm)
{
phys_addr_t pgd_phys;
- u64 vmid;
+ u64 vmid, cnp = kvm_cpu_has_cnp() ? 1 : 0;
bool new_gen;
read_lock(&kvm_vmid_lock);
@@ -522,7 +522,7 @@ static void update_vttbr(struct kvm *kvm)
pgd_phys = virt_to_phys(kvm->arch.pgd);
BUG_ON(pgd_phys & ~VTTBR_BADDR_MASK);
vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK(kvm_vmid_bits);
- kvm->arch.vttbr = kvm_phys_to_vttbr(pgd_phys) | vmid;
+ kvm->arch.vttbr = kvm_phys_to_vttbr(pgd_phys) | vmid | cnp;
write_unlock(&kvm_vmid_lock);
}
--
2.0.0
^ permalink raw reply related
* [PATCH v4 1/3] arm64: mm: Support Common Not Private translations
From: Vladimir Murzin @ 2018-05-18 10:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526638022-4137-1-git-send-email-vladimir.murzin@arm.com>
Common Not Private (CNP) is a feature of ARMv8.2 extension which
allows translation table entries to be shared between different PEs in
the same inner shareable domain, so the hardware can use this fact to
optimise the caching of such entries in the TLB.
CNP occupies one bit in TTBRx_ELy and VTTBR_EL2, which advertises to
the hardware that the translation table entries pointed to by this
TTBR are the same as every PE in the same inner shareable domain for
which the equivalent TTBR also has CNP bit set. In case CNP bit is set
but TTBR does not point at the same translation table entries for a
given ASID and VMID, then the system is mis-configured, so the results
of translations are UNPREDICTABLE.
For EL1 we postpone setting CNP till all cpus are up and rely on
cpufeature framework to 1) patch the code which is sensitive to CNP
and 2) update TTBR1_EL1 with CNP bit set. TTBR1_EL1 can be
reprogrammed as result of hibernation or cpuidle (via __enable_mmu).
cpuidle's path has been changed to restore CnP and for hibernation the
code has been changed to save raw TTBR1_EL1 and blindly restore it on
resume.
For EL0 there are a few cases we need to care of changes in
TTBR0_EL1:
- a switch to idmap
- software emulated PAN
we rule out latter via Kconfig options and for the former we make
sure that CNP is set for non-zero ASIDs only.
Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
---
arch/arm64/Kconfig | 13 +++++++++++++
arch/arm64/include/asm/cpucaps.h | 3 ++-
arch/arm64/include/asm/cpufeature.h | 6 ++++++
arch/arm64/include/asm/mmu_context.h | 12 ++++++++++++
arch/arm64/include/asm/pgtable-hwdef.h | 2 ++
arch/arm64/kernel/cpufeature.c | 30 ++++++++++++++++++++++++++++++
arch/arm64/kernel/hibernate.c | 2 +-
arch/arm64/kernel/suspend.c | 4 ++++
arch/arm64/mm/context.c | 3 +++
arch/arm64/mm/proc.S | 6 ++++++
10 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index eb2cf49..cde5d75 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1125,6 +1125,19 @@ config ARM64_RAS_EXTN
and access the new registers if the system supports the extension.
Platform RAS features may additionally depend on firmware support.
+config ARM64_CNP
+ bool "Enable support for Common Not Private (CNP) translations"
+ depends on ARM64_PAN || !ARM64_SW_TTBR0_PAN
+ help
+ Common Not Private (CNP) allows translation table entries to
+ be shared between different PEs in the same inner shareable
+ domain, so the hardware can use this fact to optimise the
+ caching of such entries in the TLB.
+
+ Selecting this option allows the CNP feature to be detected
+ at runtime, and does not affect PEs that do not implement
+ this feature.
+
endmenu
config ARM64_SVE
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index bc51b72..f474a61 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -48,7 +48,8 @@
#define ARM64_HAS_CACHE_IDC 27
#define ARM64_HAS_CACHE_DIC 28
#define ARM64_HW_DBM 29
+#define ARM64_HAS_CNP 30
-#define ARM64_NCAPS 30
+#define ARM64_NCAPS 31
#endif /* __ASM_CPUCAPS_H */
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 09b0f2a..1f7c056 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -510,6 +510,12 @@ static inline bool system_supports_sve(void)
cpus_have_const_cap(ARM64_SVE);
}
+static inline bool system_supports_cnp(void)
+{
+ return IS_ENABLED(CONFIG_ARM64_CNP) &&
+ cpus_have_const_cap(ARM64_HAS_CNP);
+}
+
/*
* Read the pseudo-ZCR used by cpufeatures to identify the supported SVE
* vector length.
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 39ec0b8..4f9e3ea 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -149,6 +149,18 @@ static inline void cpu_replace_ttbr1(pgd_t *pgdp)
phys_addr_t pgd_phys = virt_to_phys(pgdp);
+ if (system_supports_cnp() && !WARN_ON(pgdp != lm_alias(swapper_pg_dir))) {
+ /*
+ * cpu_replace_ttbr1() is used when there's a boot CPU
+ * up (i.e. cpufeature framework is not up yet) and
+ * latter only when we enable CNP via cpufeature's
+ * enable() callback.
+ * Also we rely on the cpu_hwcap bit being set before
+ * calling the enable() function.
+ */
+ pgd_phys |= TTBR_CNP_BIT;
+ }
+
replace_phys = (void *)__pa_symbol(idmap_cpu_replace_ttbr1);
cpu_install_idmap();
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h
index fd208ea..1d7d8da 100644
--- a/arch/arm64/include/asm/pgtable-hwdef.h
+++ b/arch/arm64/include/asm/pgtable-hwdef.h
@@ -211,6 +211,8 @@
#define PHYS_MASK_SHIFT (CONFIG_ARM64_PA_BITS)
#define PHYS_MASK ((UL(1) << PHYS_MASK_SHIFT) - 1)
+#define TTBR_CNP_BIT (UL(1) << 0)
+
/*
* TCR flags.
*/
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 9d1b06d..199e9dd 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -20,6 +20,7 @@
#include <linux/bsearch.h>
#include <linux/cpumask.h>
+#include <linux/crash_dump.h>
#include <linux/sort.h>
#include <linux/stop_machine.h>
#include <linux/types.h>
@@ -117,6 +118,7 @@ EXPORT_SYMBOL(cpu_hwcap_keys);
static bool __maybe_unused
cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused);
+static void cpu_enable_cnp(struct arm64_cpu_capabilities const *cap);
/*
* NOTE: Any changes to the visibility of features should be kept in
@@ -858,6 +860,16 @@ static bool has_cache_dic(const struct arm64_cpu_capabilities *entry,
return read_sanitised_ftr_reg(SYS_CTR_EL0) & BIT(CTR_DIC_SHIFT);
}
+static bool __maybe_unused
+has_useable_cnp(const struct arm64_cpu_capabilities *entry, int scope)
+{
+#ifdef CONFIG_CRASH_DUMP
+ if (elfcorehdr_size)
+ return false;
+#endif
+ return has_cpuid_feature(entry, scope);
+}
+
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
static int __kpti_forced; /* 0: not forced, >0: forced on, <0: forced off */
@@ -1202,6 +1214,19 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.cpu_enable = cpu_enable_hw_dbm,
},
#endif
+#ifdef CONFIG_ARM64_CNP
+ {
+ .desc = "Common not Private translations",
+ .capability = ARM64_HAS_CNP,
+ .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+ .matches = has_useable_cnp,
+ .sys_reg = SYS_ID_AA64MMFR2_EL1,
+ .sign = FTR_UNSIGNED,
+ .field_pos = ID_AA64MMFR2_CNP_SHIFT,
+ .min_field_value = 1,
+ .cpu_enable = cpu_enable_cnp,
+ },
+#endif
{},
};
@@ -1642,6 +1667,11 @@ cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused)
return (cpus_have_const_cap(ARM64_HAS_PAN) && !cpus_have_const_cap(ARM64_HAS_UAO));
}
+static void __maybe_unused cpu_enable_cnp (struct arm64_cpu_capabilities const *cap)
+{
+ cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
+}
+
/*
* We emulate only the following system register space.
* Op0 = 0x3, CRn = 0x0, Op1 = 0x0, CRm = [0, 4 - 7]
diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
index 1ec5f28..ea27121 100644
--- a/arch/arm64/kernel/hibernate.c
+++ b/arch/arm64/kernel/hibernate.c
@@ -125,7 +125,7 @@ int arch_hibernation_header_save(void *addr, unsigned int max_size)
return -EOVERFLOW;
arch_hdr_invariants(&hdr->invariants);
- hdr->ttbr1_el1 = __pa_symbol(swapper_pg_dir);
+ hdr->ttbr1_el1 = read_sysreg(ttbr1_el1);
hdr->reenter_kernel = _cpu_resume;
/* We can't use __hyp_get_vectors() because kvm may still be loaded */
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c
index a307b9e..9576643 100644
--- a/arch/arm64/kernel/suspend.c
+++ b/arch/arm64/kernel/suspend.c
@@ -48,6 +48,10 @@ void notrace __cpu_suspend_exit(void)
*/
cpu_uninstall_idmap();
+ /* Restore CnP bit in TTBR1_EL1 */
+ if (system_supports_cnp())
+ cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
+
/*
* PSTATE was not saved over suspend/resume, re-enable any detected
* features that might not have been set correctly.
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index 301417a..3c1b2c1 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -196,6 +196,9 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
unsigned long flags;
u64 asid, old_active_asid;
+ if (system_supports_cnp())
+ cpu_set_reserved_ttbr0();
+
asid = atomic64_read(&mm->context.id);
/*
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 5f9a73a..d46d0ca 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -160,6 +160,12 @@ ENTRY(cpu_do_switch_mm)
mrs x2, ttbr1_el1
mmid x1, x1 // get mm->context.id
phys_to_ttbr x3, x0
+
+alternative_if ARM64_HAS_CNP
+ cbz x1, 1f // skip CNP for reserved ASID
+ orr x3, x3, #TTBR_CNP_BIT
+1:
+alternative_else_nop_endif
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
bfi x3, x1, #48, #16 // set the ASID field in TTBR0
#endif
--
2.0.0
^ permalink raw reply related
* [PATCH v4 0/3] Support Common Not Private translations
From: Vladimir Murzin @ 2018-05-18 10:06 UTC (permalink / raw)
To: linux-arm-kernel
Common Not Private (CNP) translations is a feature of ARMv8.2
extension which allows translation table entries to be shared between
different PEs in the same inner shareable domain, so the hardware can
use this fact to optimise the caching of such entries in the TLB.
This patch set is an attempt to bring CNP support into Linux. It was
tested on a v8.2 Fast Model with exploring traces and checking that
TTBRx_ELy and VTTBR_EL2 have CnP bit set where appropriate.
Changelog:
v3 -> v4
- rebased on 4.17-rc4 for real
v2 -> v3
- do not enable CNP if we are crush kernel (per James)
- default to "no"
- rebased on 4.17-rc4
v1 -> v2
- handle cpuilde case (per James)
- use lm_allias with swapper_pg_dir (per James)
- rule out ARM64_SW_TTBR0_PAN case (per Catalin)
- s/BUG_ON/WARN_ON/ (per Catalin)
- comment and commit message updates (per Catalin)
- TTBR_CNP_BIT moved to asm/pgtable-hwdef.h (per Catalin)
- has_useable_cnp() simplified (per Julien)
RFC -> v1
- dropped RFC tag
- rebased on 4.14-rc4
Thanks!
Vladimir Murzin (3):
arm64: mm: Support Common Not Private translations
arm64: KVM: Enable Common Not Private translations
arm64: Introduce command line parameter to disable CNP
Documentation/admin-guide/kernel-parameters.txt | 4 +++
arch/arm/include/asm/kvm_mmu.h | 5 ++++
arch/arm64/Kconfig | 13 +++++++++
arch/arm64/include/asm/cpucaps.h | 3 +-
arch/arm64/include/asm/cpufeature.h | 6 ++++
arch/arm64/include/asm/kvm_mmu.h | 5 ++++
arch/arm64/include/asm/mmu_context.h | 12 ++++++++
arch/arm64/include/asm/pgtable-hwdef.h | 2 ++
arch/arm64/kernel/cpufeature.c | 39 +++++++++++++++++++++++++
arch/arm64/kernel/hibernate.c | 2 +-
arch/arm64/kernel/suspend.c | 4 +++
arch/arm64/kvm/hyp-init.S | 3 ++
arch/arm64/mm/context.c | 3 ++
arch/arm64/mm/proc.S | 6 ++++
virt/kvm/arm/arm.c | 4 +--
15 files changed, 107 insertions(+), 4 deletions(-)
--
2.0.0
^ permalink raw reply
* [PATCH v2 12/26] drm/sun4i: Add support for multiple DW HDMI PHY clock parents
From: Maxime Ripard @ 2018-05-18 10:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518094536.17201-13-jagan@amarulasolutions.com>
On Fri, May 18, 2018 at 03:15:22PM +0530, Jagan Teki wrote:
> From: Jernej Skrabec <jernej.skrabec@siol.net>
>
> Some SoCs with DW HDMI have multiple possible clock parents, like A64
> and R40.
>
> Expand HDMI PHY clock driver to support second clock parent.
>
> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> ---
> Changes for v2:
> - new patch
>
> drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 9 ++-
> drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c | 33 ++++++++---
> drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c | 89 ++++++++++++++++++++++--------
> 3 files changed, 96 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
> index 79154f0f674a..303189d6602c 100644
> --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
> +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h
> @@ -98,7 +98,8 @@
> #define SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN BIT(29)
> #define SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN BIT(28)
> #define SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 BIT(27)
> -#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL BIT(26)
> +#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK BIT(26)
> +#define SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_SHIFT 26
> #define SUN8I_HDMI_PHY_PLL_CFG1_PLLEN BIT(25)
> #define SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(x) ((x) << 22)
> #define SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(x) ((x) << 20)
> @@ -146,7 +147,7 @@
> struct sun8i_hdmi_phy;
>
> struct sun8i_hdmi_phy_variant {
> - bool has_phy_clk;
> + int phy_clk_num;
> void (*phy_init)(struct sun8i_hdmi_phy *phy);
> void (*phy_disable)(struct dw_hdmi *hdmi,
> struct sun8i_hdmi_phy *phy);
> @@ -160,6 +161,7 @@ struct sun8i_hdmi_phy {
> struct clk *clk_mod;
> struct clk *clk_phy;
> struct clk *clk_pll0;
> + struct clk *clk_pll1;
> unsigned int rcal;
> struct regmap *regs;
> struct reset_control *rst_phy;
> @@ -188,6 +190,7 @@ void sun8i_hdmi_phy_remove(struct sun8i_dw_hdmi *hdmi);
> void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy);
> const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void);
>
> -int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev);
> +int sun8i_phy_clk_create(struct sun8i_hdmi_phy *phy, struct device *dev,
> + int clk_num);
>
> #endif /* _SUN8I_DW_HDMI_H_ */
> diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
> index 5a52fc489a9d..0eadf087fc46 100644
> --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
> +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c
> @@ -183,7 +183,13 @@ static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
> regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
> SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0);
>
> - regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, pll_cfg1_init);
> + /*
> + * NOTE: We have to be careful not to overwrite PHY parent
> + * clock selection bit and clock divider.
> + */
> + regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
> + (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK,
> + pll_cfg1_init);
> regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG,
> (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK,
> pll_cfg2_init);
> @@ -232,7 +238,7 @@ static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
> regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
> SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
>
> - if (phy->variant->has_phy_clk)
> + if (phy->variant->phy_clk_num)
> clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000);
>
> return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000);
> @@ -393,7 +399,7 @@ static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
> };
>
> static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
> - .has_phy_clk = true,
> + .phy_clk_num = 1,
> .phy_init = &sun8i_hdmi_phy_init_h3,
> .phy_disable = &sun8i_hdmi_phy_disable_h3,
> .phy_config = &sun8i_hdmi_phy_config_h3,
> @@ -464,7 +470,7 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
> goto err_put_clk_bus;
> }
>
> - if (phy->variant->has_phy_clk) {
> + if (phy->variant->phy_clk_num) {
> phy->clk_pll0 = of_clk_get_by_name(node, "pll-0");
> if (IS_ERR(phy->clk_pll0)) {
> dev_err(dev, "Could not get pll-0 clock\n");
> @@ -472,7 +478,16 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
> goto err_put_clk_mod;
> }
>
> - ret = sun8i_phy_clk_create(phy, dev);
> + if (phy->variant->phy_clk_num) {
> + phy->clk_pll1 = of_clk_get_by_name(node, "pll-1");
> + if (IS_ERR(phy->clk_pll1)) {
> + dev_err(dev, "Could not get pll-1 clock\n");
> + ret = PTR_ERR(phy->clk_pll1);
> + goto err_put_clk_mod;
> + }
> + }
> +
You have a bug here. If phy_clk_num == 1, you'll still try to lookup
pll-1.
And this is a bit sloppy, since if phy_clk_num == 3, you won't try to
lookup pll-2 either.
> + ret = sun8i_phy_clk_create(phy, dev, phy->variant->phy_clk_num);
> if (ret) {
> dev_err(dev, "Couldn't create the PHY clock\n");
> goto err_put_clk_pll0;
> @@ -515,8 +530,8 @@ int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
> err_put_rst_phy:
> reset_control_put(phy->rst_phy);
> err_put_clk_pll0:
> - if (phy->variant->has_phy_clk)
> - clk_put(phy->clk_pll0);
> + clk_put(phy->clk_pll0);
> + clk_put(phy->clk_pll1);
> err_put_clk_mod:
> clk_put(phy->clk_mod);
> err_put_clk_bus:
> @@ -536,8 +551,8 @@ void sun8i_hdmi_phy_remove(struct sun8i_dw_hdmi *hdmi)
>
> reset_control_put(phy->rst_phy);
>
> - if (phy->variant->has_phy_clk)
> - clk_put(phy->clk_pll0);
> + clk_put(phy->clk_pll0);
> + clk_put(phy->clk_pll1);
> clk_put(phy->clk_mod);
> clk_put(phy->clk_bus);
> }
> diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c
> index faea449812f8..85b12fc96dbc 100644
> --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c
> +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy_clk.c
> @@ -22,29 +22,36 @@ static int sun8i_phy_clk_determine_rate(struct clk_hw *hw,
> {
> unsigned long rate = req->rate;
> unsigned long best_rate = 0;
> - struct clk_hw *parent;
> + struct clk_hw *best_parent = NULL;
> + struct clk_hw *parent = NULL;
> int best_div = 1;
> - int i;
> + int i, p;
>
> - parent = clk_hw_get_parent(hw);
> -
> - for (i = 1; i <= 16; i++) {
> - unsigned long ideal = rate * i;
> - unsigned long rounded;
> -
> - rounded = clk_hw_round_rate(parent, ideal);
> -
> - if (rounded == ideal) {
> - best_rate = rounded;
> - best_div = i;
> - break;
> - }
> + for (p = 0; p < clk_hw_get_num_parents(hw); p++) {
> + parent = clk_hw_get_parent_by_index(hw, p);
> + if (!parent)
> + continue;
>
> - if (!best_rate ||
> - abs(rate - rounded / i) <
> - abs(rate - best_rate / best_div)) {
> - best_rate = rounded;
> - best_div = i;
> + for (i = 1; i <= 16; i++) {
> + unsigned long ideal = rate * i;
> + unsigned long rounded;
> +
> + rounded = clk_hw_round_rate(parent, ideal);
> +
> + if (rounded == ideal) {
> + best_rate = rounded;
> + best_div = i;
> + best_parent = parent;
> + break;
> + }
> +
> + if (!best_rate ||
> + abs(rate - rounded / i) <
> + abs(rate - best_rate / best_div)) {
> + best_rate = rounded;
> + best_div = i;
> + best_parent = parent;
> + }
> }
> }
>
> @@ -95,22 +102,58 @@ static int sun8i_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> return 0;
> }
>
> +static u8 sun8i_phy_clk_get_parent(struct clk_hw *hw)
> +{
> + struct sun8i_phy_clk *priv = hw_to_phy_clk(hw);
> + u32 reg;
> +
> + regmap_read(priv->phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, ®);
> + reg = (reg & SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK) >>
> + SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_SHIFT;
> +
> + return reg;
> +}
> +
> +static int sun8i_phy_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> + struct sun8i_phy_clk *priv = hw_to_phy_clk(hw);
> +
> + if (index > 1)
> + return -EINVAL;
> +
> + regmap_update_bits(priv->phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
> + SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK,
> + index << SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_SHIFT);
> +
> + return 0;
> +}
> +
The DT bindings changes and the clk changes should be part of separate
patches.
Maxime
--
Maxime Ripard, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180518/cafacca2/attachment-0001.sig>
^ permalink raw reply
* [PATCH v3 0/3] Support Common Not Private translations
From: Vladimir Murzin @ 2018-05-18 9:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526636765-11289-1-git-send-email-vladimir.murzin@arm.com>
On 18/05/18 10:46, Vladimir Murzin wrote:
> Common Not Private (CNP) translations is a feature of ARMv8.2
> extension which allows translation table entries to be shared between
> different PEs in the same inner shareable domain, so the hardware can
> use this fact to optimise the caching of such entries in the TLB.
>
> This patch set is an attempt to bring CNP support into Linux. It was
> tested on a v8.2 Fast Model with exploring traces and checking that
> TTBRx_ELy and VTTBR_EL2 have CnP bit set where appropriate.
>
> Changelog:
>
> v2 -> v3
> - do not enable CNP if we are crush kernel (per James)
> - default to "no"
> - rebased on 4.17-rc4
Please, ignore it - it was generated from outdated branch. I'll resend
shortly.
Thanks
Vladimir
>
> v1 -> v2
> - handle cpuilde case (per James)
> - use lm_allias with swapper_pg_dir (per James)
> - rule out ARM64_SW_TTBR0_PAN case (per Catalin)
> - s/BUG_ON/WARN_ON/ (per Catalin)
> - comment and commit message updates (per Catalin)
> - TTBR_CNP_BIT moved to asm/pgtable-hwdef.h (per Catalin)
> - has_useable_cnp() simplified (per Julien)
>
> RFC -> v1
> - dropped RFC tag
> - rebased on 4.14-rc4
>
> Thanks!
>
> Vladimir Murzin (3):
> arm64: mm: Support Common Not Private translations
> arm64: KVM: Enable Common Not Private translations
> arm64: Introduce command line parameter to disable CNP
>
> Documentation/admin-guide/kernel-parameters.txt | 4 +++
> arch/arm/include/asm/kvm_mmu.h | 5 ++++
> arch/arm64/Kconfig | 13 ++++++++
> arch/arm64/include/asm/cpucaps.h | 3 +-
> arch/arm64/include/asm/cpufeature.h | 6 ++++
> arch/arm64/include/asm/kvm_mmu.h | 5 ++++
> arch/arm64/include/asm/mmu_context.h | 12 ++++++++
> arch/arm64/include/asm/pgtable-hwdef.h | 2 ++
> arch/arm64/kernel/cpufeature.c | 40 +++++++++++++++++++++++++
> arch/arm64/kernel/hibernate.c | 2 +-
> arch/arm64/kernel/suspend.c | 4 +++
> arch/arm64/kvm/hyp-init.S | 3 ++
> arch/arm64/mm/context.c | 3 ++
> arch/arm64/mm/proc.S | 6 ++++
> virt/kvm/arm/arm.c | 4 +--
> 15 files changed, 108 insertions(+), 4 deletions(-)
>
^ permalink raw reply
* [PATCH v2 00/26] arm64: allwinner: Add A64 DE2 HDMI support
From: Maxime Ripard @ 2018-05-18 9:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180518094536.17201-1-jagan@amarulasolutions.com>
On Fri, May 18, 2018 at 03:15:10PM +0530, Jagan Teki wrote:
> Allwinner A64 has display engine pipeline like other Allwinner SOC's A83T/H3/H5.
>
> A64 behaviour similar to Allwinner A83T where
> Mixer0 => TCON0 => LVDS/RGB/MIPI-DSI
> Mixer1 => TCON1 => HDMI
> as per Display System Block DiagramAllwinner_A64_User_Manual_V1.1.pdf
>
> This is second patch-set followed with previous RFC[1] and first series[2]
> and merely concentrated on HDMI pipeline through TCON1 and rest will add eventually.
>
> This series fixed previous version comments
> - about documenting fallback compatibles
> - adding new compatible for mixer1
> - support for multiple DW HDMI PHY clock parents (thanks, to Jernej)
>
> Note:
> Pine64 boards are unable to get edid by default like other A64 boards,
> but forcing 'video=HDMI-A-1:1920x1080 at 60D' kernel command line can
> create edid with display on penel.
There's no point in trying to push this without the SRAM issue being
solved. It is required, and won't be merged unless this is addressed.
Maxime
--
Maxime Ripard, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180518/87c56e85/attachment-0001.sig>
^ permalink raw reply
* [PATCH v9 07/11] arm64: kexec_file: add crash dump support
From: AKASHI Takahiro @ 2018-05-18 9:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3d70c7d1-4985-a427-ecc6-a7edc84edfff@arm.com>
On Wed, May 16, 2018 at 09:34:41AM +0100, James Morse wrote:
> Hi Akashi,
>
> On 15/05/18 18:11, James Morse wrote:
> > On 25/04/18 07:26, AKASHI Takahiro wrote:
> >> Enabling crash dump (kdump) includes
> >> * prepare contents of ELF header of a core dump file, /proc/vmcore,
> >> using crash_prepare_elf64_headers(), and
> >> * add two device tree properties, "linux,usable-memory-range" and
> >> "linux,elfcorehdr", which represent repsectively a memory range
> >> to be used by crash dump kernel and the header's location
>
> >> diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
> >> index 37c0a9dc2e47..ec674f4d267c 100644
> >> --- a/arch/arm64/kernel/machine_kexec_file.c
> >> +++ b/arch/arm64/kernel/machine_kexec_file.c
> >> @@ -76,6 +81,78 @@ int arch_kexec_walk_mem(struct kexec_buf *kbuf,
>
> >> +static void fill_property(void *buf, u64 val64, int cells)
> >> +{
> >> + u32 val32;
> >> +
> >> + if (cells == 1) {
> >> + val32 = cpu_to_fdt32((u32)val64);
> >> + memcpy(buf, &val32, sizeof(val32));
> >> + } else {
> >
> >> + memset(buf, 0, cells * sizeof(u32) - sizeof(u64));
> >> + buf += cells * sizeof(u32) - sizeof(u64);
> >
> > Is this trying to clear the 'top' cells and shuffle the pointer to point at the
> > 'bottom' 2? I'm pretty sure this isn't endian safe.
>
> It came to me at 2am: this only works on big-endian, which is exactly what you
> want as that is the DT format.
Oops, I was almost tricked as I haven't tested kexec on BE
for a long time :)
Thanks,
-Takahiro AKASHI
>
> > Do we really expect a system to have #address-cells > 2?
>
>
> Thanks,
>
> James
^ permalink raw reply
* [PATCH 17/27] dt-bindings: ap806: add the thermal node in the syscon file
From: Miquel Raynal @ 2018-05-18 9:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180427210722.obyujepjvqc5hwcd@rob-hp-laptop>
Hi Rob,
Thanks for reviewing, one question below.
On Fri, 27 Apr 2018 16:07:22 -0500, Rob Herring <robh@kernel.org> wrote:
> On Sat, Apr 21, 2018 at 05:12:45PM +0200, Miquel Raynal wrote:
> > Explain the thermal bindings now that the thermal IP is described being
> > inside of a system controller. Add a reference to the thermal-zone node.
> >
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > ---
> > .../arm/marvell/ap806-system-controller.txt | 43 ++++++++++++++++++++++
> > 1 file changed, 43 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/arm/marvell/ap806-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/ap806-system-controller.txt
> > index a856eb9a4e05..c95f3ac5c728 100644
> > --- a/Documentation/devicetree/bindings/arm/marvell/ap806-system-controller.txt
> > +++ b/Documentation/devicetree/bindings/arm/marvell/ap806-system-controller.txt
> > @@ -11,6 +11,9 @@ For the top level node:
> > - compatible: must be: "syscon", "simple-mfd";
> > - reg: register area of the AP806 system controller
> >
> > +SYSTEM CONTROLLER 0
> > +===================
> > +
> > Clocks:
> > -------
> >
> > @@ -98,3 +101,43 @@ ap_syscon: system-controller at 6f4000 {
> > gpio-ranges = <&ap_pinctrl 0 0 19>;
> > };
> > };
> > +
> > +SYSTEM CONTROLLER 1
> > +===================
> > +
> > +Thermal:
> > +--------
> > +
> > +For common binding part and usage, refer to
> > +Documentation/devicetree/bindings/thermal/thermal.txt
> > +
> > +The thermal IP can probe the temperature all around the processor. It
> > +may feature several channels, each of them wired to one sensor.
> > +
> > +It is possible to setup an overheat interrupt by giving at least one
> > +critical point to any subnode of the thermal-zone node.
> > +
> > +Required properties:
> > +- compatible: "marvell,armada-ap806-thermal"
> > +
> > +Optional properties:
> > +- interrupt-parent/interrupts: overheat interrupt handle. Should point to
> > + line 18 of the SEI irqchip.
> > + See interrupt-controller/interrupts.txt
> > +- #thermal-sensor-cells: shall be <1> when thermal-zones subnodes refer
> > + to this IP and represents the channel ID. There is one sensor per
> > + channel. O refers to the thermal IP internal channel, while positive
> > + IDs refer to each CPU.
> > +
> > +Example:
> > +ap_syscon1: system-controller at 6f8000 {
> > + compatible = "syscon", "simple-mfd";
> > + reg = <0x6f8000 0x1000>;
> > +
> > + ap_thermal: ap-thermal {
> > + compatible = "marvell,armada-ap806-thermal";
>
> Is there a register range associated with the thermal functions?
Indeed there is a small one (three registers) but we also need to access
some registers out of this area (so called the 'DFX' area, in this
syscon, to route correctly the interrupt) and I fear we'll need to
access others in the future. I choose to declare a syscon because this
area mixes a lot of misc registers.
So do you think I should declare these 3 registers area in a reg
property? Or keep the way I retrieved the offsets in the syscon:
defining offsets in the code?
>
> > + interrupt-parent = <&sei>;
> > + interrupts = <18>;
> > + #thermal-sensor-cells = <1>;
> > + };
> > +};
> > --
> > 2.14.1
> >
Thanks,
Miqu?l
--
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH v9 07/11] arm64: kexec_file: add crash dump support
From: AKASHI Takahiro @ 2018-05-18 9:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <512cde7d-d84b-3623-cb09-a2d78acc11dc@arm.com>
On Wed, May 16, 2018 at 11:06:02AM +0100, James Morse wrote:
> Hi Akashi,
>
> On 15/05/18 18:11, James Morse wrote:
> > On 25/04/18 07:26, AKASHI Takahiro wrote:
> >> Enabling crash dump (kdump) includes
> >> * prepare contents of ELF header of a core dump file, /proc/vmcore,
> >> using crash_prepare_elf64_headers(), and
> >> * add two device tree properties, "linux,usable-memory-range" and
> >> "linux,elfcorehdr", which represent repsectively a memory range
> >> to be used by crash dump kernel and the header's location
>
> >> diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
> >> index 37c0a9dc2e47..ec674f4d267c 100644
> >> --- a/arch/arm64/kernel/machine_kexec_file.c
> >> +++ b/arch/arm64/kernel/machine_kexec_file.c
>
> >> +static struct crash_mem *get_crash_memory_ranges(void)
> >> +{
> >> + unsigned int nr_ranges;
> >> + struct crash_mem *cmem;
> >> +
> >> + nr_ranges = 1; /* for exclusion of crashkernel region */
> >> + walk_system_ram_res(0, -1, &nr_ranges, get_nr_ranges_callback);
> >> +
> >> + cmem = vmalloc(sizeof(struct crash_mem) +
> >> + sizeof(struct crash_mem_range) * nr_ranges);
> >> + if (!cmem)
> >> + return NULL;
> >> +
> >> + cmem->max_nr_ranges = nr_ranges;
> >> + cmem->nr_ranges = 0;
> >> + walk_system_ram_res(0, -1, cmem, add_mem_range_callback);
> >> +
> >> + /* Exclude crashkernel region */
> >> + if (crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end)) {
> >> + vfree(cmem);
> >> + return NULL;
> >> + }
> >> +
> >> + return cmem;
> >> +}
> >
> > Could this function be included in prepare_elf_headers() so that the alloc() and
> > free() occur together.
> >
> >
> >> +static int prepare_elf_headers(void **addr, unsigned long *sz)
> >> +{
> >> + struct crash_mem *cmem;
> >> + int ret = 0;
> >> +
> >> + cmem = get_crash_memory_ranges();
> >> + if (!cmem)
> >> + return -ENOMEM;
> >> +
> >> + ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
> >> +
> >> + vfree(cmem);
> >
> >> + return ret;
> >> +}
> >
> > All this is moving memory-range information from core-code's
> > walk_system_ram_res() into core-code's struct crash_mem, and excluding
> > crashk_res, which again is accessible to the core code.
> >
> > It looks like this is duplicated in arch/x86 and arch/arm64 because arm64
> > doesn't have a second 'crashk_low_res' region, and always wants elf64, instead
> > of when IS_ENABLED(CONFIG_X86_64).
>
> Thinking about it some more: don't we want to walk memblock here, not
> walk_system_ram_res()? What we want is a list of not-nomap regions that the
> kernel may have been using, to form part of vmcore.
> walk_system_ram_res() is becoming a murkier list of maybe-nomap, maybe-reserved.
>
> I think we should walk the same list here as we do in patch 4.
For consistency, yes.
I missed that.
-Takahiro AKASHI
>
>
> Thanks,
>
> James
^ permalink raw reply
* [PATCH 3/3] arm64: dts: renesas: r8a7795: add ccree binding
From: Simon Horman @ 2018-05-18 9:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOtvUMcnrPnkx35Sgru+3EUxYn+FY9FmDSYSNnZEaVUODHA7+w@mail.gmail.com>
On Thu, May 17, 2018 at 04:12:23PM +0300, Gilad Ben-Yossef wrote:
> On Thu, May 17, 2018 at 12:04 PM, Simon Horman <horms@verge.net.au> wrote:
> > On Thu, May 17, 2018 at 11:01:57AM +0300, Gilad Ben-Yossef wrote:
> >> On Wed, May 16, 2018 at 10:43 AM, Simon Horman <horms@verge.net.au> wrote:
> >> > On Tue, May 15, 2018 at 04:50:44PM +0200, Geert Uytterhoeven wrote:
> >> >> Hi Gilad,
> >> >>
> >> >> On Tue, May 15, 2018 at 2:29 PM, Gilad Ben-Yossef <gilad@benyossef.com> wrote:
> >> >> > Add bindings for CryptoCell instance in the SoC.
> >> >> >
> >> >> > Signed-off-by: Gilad Ben-Yossef <gilad@benyossef.com>
> >> >>
> >> >> Thanks for your patch!
> >> >>
> >> >> > --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> >> >> > +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi
> >> >> > @@ -528,6 +528,14 @@
> >> >> > status = "disabled";
> >> >> > };
> >> >> >
> >> >> > + arm_cc630p: crypto at e6601000 {
> >> >> > + compatible = "arm,cryptocell-630p-ree";
> >> >> > + interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
> >> >> > + #interrupt-cells = <2>;
> >> >>
> >> >> I believe the #interrupt-cells property is not needed.
> >> >>
> >> >> > + reg = <0x0 0xe6601000 0 0x1000>;
> >> >> > + clocks = <&cpg CPG_MOD 229>;
> >> >> > + };
> >> >>
> >> >> The rest looks good, but I cannot verify the register block.
> >> >>
> >> >> > +
> >> >> > i2c3: i2c at e66d0000 {
> >> >> > #address-cells = <1>;
> >> >> > #size-cells = <0>;
> >> >
> >> > Thanks, I have applied this after dropping the #interrupt-cells property.
> >>
> >> Thanks you!
> >>
> >> Alas, it will not work without the clk patch (the previous one in the
> >> series) so they need to be
> >> taken or dropped together.
> >
> > I think its fine if it does not yet work.
> > But not if its causes things that previously worked to stop working.
>
> Based on the further discussion with Geert my recommendation is to
> drop my patch for now,
> take Geert CR clock patch and I will follow up next week with a v2
> that fixes the clock
> handing as discussed with Geert.
Thanks, I will drop the patch.
^ permalink raw reply
* [PATCH 00/27] Add multi-channel and overheat IRQ support to Armada thermal driver
From: Miquel Raynal @ 2018-05-18 9:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <871seb33xu.fsf@bootlin.com>
Hi Zhang, Eduardo & Gregory,
On Wed, 16 May 2018 19:28:45 +0200, Gregory CLEMENT
<gregory.clement@bootlin.com> wrote:
> Hi Miquel,
>
> On sam., avril 21 2018, Miquel Raynal <miquel.raynal@bootlin.com> wrote:
>
> > The only capability of the Armada thermal driver is currently just to
> > read one sensor (the default one) per AP and one per CP.
> >
> > Actually, there is one sensor per core in the AP806 plus one sensor in
> > the thermal IP itself. The CP110 just features one thermal sensor in its
> > own thermal IP.
> >
> > Also, there is no need for the thermal core to poll the temperature of
> > each sensor by software as this IP (at least for AP806 and CP110
> > compatibles) features an hardware overheat interrupt.
> >
> > This series first improves the readability of this driver, then adds
> > support for multi-channel thermal IPs, and finally adds support for the
> > hardware overheat interrupt. The bindings and the device-trees are
> > updated accordingly.
> >
> > Please note that the thermal IP raises SEI interrupts, from which the
> > support as just been contributed and not merged yet. Applying the last
> > DT patches referring to the 'sei' and 'icu_sei' nodes will require this
> > feature [1] to have been accepted first.
> >
> > [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2018-April/572852.html
> >
> > Thank you,
> > Miqu?l
> >
> >
> > Miquel Raynal (27):
> > thermal: armada: add a function that sanitizes the thermal zone name
> > thermal: armada: remove useless register accesses
> > thermal: armada: remove misleading comments
> > thermal: armada: rename the initialization routine
> > thermal: armada: dissociate a380 and cp110 ->init() hooks
> > thermal: armada: average over samples to avoid glitches
> > thermal: armada: convert driver to syscon register accesses
> > thermal: armada: use the resource managed registration helper
> > alternative
> > thermal: armada: add multi-channel sensors support
> > thermal: armada: remove sensors validity from the IP initialization
> > thermal: armada: move validity check out of the read function
> > thermal: armada: get rid of the ->is_valid() pointer
> > thermal: armada: add overheat interrupt support
> > dt-bindings: cp110: rename cp110 syscon file
> > dt-bindings: ap806: prepare the syscon file to list other syscons
> > nodes
> > dt-bindings: cp110: prepare the syscon file to list other syscons
> > nodes
> > dt-bindings: ap806: add the thermal node in the syscon file
> > dt-bindings: cp110: update documentation since DT de-duplication
> > dt-bindings: cp110: add the thermal node in the syscon file
> > dt-bindings: thermal: armada: add reference to new bindings
> > arm64: dts: marvell: rename ap806 syscon node
> > arm64: dts: marvell: move AP806/CP110 thermal nodes into a new syscon
> > arm64: dts: marvell: add thermal-zone node in ap806 DTSI file
> > arm64: dts: marvell: add macro to make distinction between node names
> > arm64: dts: marvell: add thermal-zone node in cp110 DTSI file
> > arm64: dts: marvell: add interrupt support to ap806 thermal node
> > arm64: dts: marvell: add interrupt support to cp110 thermal node
> >
> > .../arm/marvell/ap806-system-controller.txt | 55 +-
> > ...controller0.txt => cp110-system-controller.txt} | 66 +-
> > .../devicetree/bindings/thermal/armada-thermal.txt | 5 +
> > arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 85 +-
> > arch/arm64/boot/dts/marvell/armada-common.dtsi | 1 +
> > arch/arm64/boot/dts/marvell/armada-cp110.dtsi | 45 +-
> > drivers/thermal/armada_thermal.c | 875 ++++++++++++++++++---
> > 7 files changed, 976 insertions(+), 156 deletions(-)
> > rename Documentation/devicetree/bindings/arm/marvell/{cp110-system-controller0.txt => cp110-system-controller.txt} (83%)
>
> What is the status of this series?
> I am especially interested in the dt part.
> Do you expect sending a new series modifying them?
I have not received any feedback yet on the thermal part, bindings have
been partially acked by Rob (one request, I will probably add a reg
property in the AP node) so please do not take the DTS changes of
this iteration.
Zhang, Eduardo, could you please share the status of this series?
Thanks,
Miqu?l
^ permalink raw reply
* [PATCH] arm: bcm2835: Add the PMU to the devicetree.
From: Marc Zyngier @ 2018-05-18 9:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1863838560.56771.1526636258081@email.1und1.de>
On 18/05/18 10:37, Stefan Wahren wrote:
> Hi Marc,
>
>> Marc Zyngier <marc.zyngier@arm.com> hat am 18. Mai 2018 um 10:07 geschrieben:
>>
>> I have a pretty simple series[1] which I used to profile 32bit guests on
>> an arm64 KVM host. Nobody really cared about it because running a 32bit
>> kernel on 64bit HW is a bit odd, to say the least, and I'm probably the
>> only one actually running 32bit VMs.
>>
>>> FWIW, Broadcom STB chips, even when 64-bit capable or often used with an
>>> 32-bit ARM kernel, so having the ARMv8 PMUs work under a 32-bit ARM
>>> kernel would be great. The downstream solution we have sued thus far is
>>> to find the closest compatible string to represent those, which is not
>>> great...
>> Ah, so you're *really* doing that? I'm not going to ask why, I'm scared
>> of the answer... ;-)
>>
>> Anyway, I can repost that series if that will prevent people from having
>> that kind of silly hacks.
>
> yes please. But there is a minor nit: some patches introduce new files without SPDX tags.
I'm shocked! ;-)
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH]irqchip/irq-gic-v3:Avoid a waste of LPI resource
From: Zhang, Lei @ 2018-05-18 9:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8898674D84E3B24BA3A2D289B872026A69F0ACA3@G01JPEXMBKW03>
Hi Marc
> -----Original Message-----
> From: linux-arm-kernel
> [mailto:linux-arm-kernel-bounces at lists.infradead.org] On Behalf Of Zhang,
> Lei
> Sent: Saturday, May 12, 2018 10:48 AM
> To: 'Marc Zyngier'; linux-arm-kernel at lists.infradead.org
> Subject: RE: [PATCH]irqchip/irq-gic-v3:Avoid a waste of LPI resource
> By the way I will finish my patch in the next week.
I rewrote the mechanism of lpis's management by using free list.
typedef struct lpi_mng {
struct list_head lpi_list;
int base;
int len;
} lpi_mng_t;
When its_chunk_to_lpi is called, the entries of free list as below.
(Assumption id_bits = 30)
base=8192, len=8192
base=16384, len=16384
base=32768, len=32768
base=65536, len=65536
base=131072, len=131072
base=262144, len=262144
base=524288, len=524288
base=1048576, len=1048576
base=2097152, len=2097152
base=4194304, len=4194304
base=8388608, len=8388608
base=16777216, len=16777216
base=33554432, len=33554432
base=67108864, len=67108864
base=134217728, len=134217728
base=268435456, len=268435456
base=536870912, len=536870912
When its_lpi_alloc_chunks is called, I split the free list until free list' length equal requested number of lpis.
I guarantee base is aligned on the size of len, and len is always a power of two.
Below is my patch for core ITS driver.
Would you give me comments?
By the way,I wanted to add a parameter request_lpis_align.
But I think it is not really necessary.
Because if the number of lpis requested is 32, it will be allocated contiguous 32 lpis
and the first lpi number allocated aligned on 32 even without this parameter.
-----------------
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -672,82 +672,127 @@ static struct irq_chip its_irq_chip = {
.irq_compose_msi_msg = its_irq_compose_msi_msg,
};
-/*
- * How we allocate LPIs:
- *
- * The GIC has id_bits bits for interrupt identifiers. From there, we
- * must subtract 8192 which are reserved for SGIs/PPIs/SPIs. Then, as
- * we allocate LPIs by chunks of 32, we can shift the whole thing by 5
- * bits to the right.
- *
- * This gives us (((1UL << id_bits) - 8192) >> 5) possible allocations.
- */
-#define IRQS_PER_CHUNK_SHIFT 5
-#define IRQS_PER_CHUNK (1 << IRQS_PER_CHUNK_SHIFT)
-
-static unsigned long *lpi_bitmap;
-static u32 lpi_chunks;
-static DEFINE_SPINLOCK(lpi_lock);
-static int its_lpi_to_chunk(int lpi)
-{
- return (lpi - 8192) >> IRQS_PER_CHUNK_SHIFT;
-}
+static struct list_head lpi_free_list;
+static struct list_head lpi_alloc_list;
+typedef struct lpi_mng {
+ struct list_head lpi_list;
+ int base;
+ int len;
+} lpi_mng_t;
-static int its_chunk_to_lpi(int chunk)
-{
- return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192;
-}
+static DEFINE_SPINLOCK(lpi_lock);
static int __init its_lpi_init(u32 id_bits)
{
- lpi_chunks = its_lpi_to_chunk(1UL << id_bits);
- lpi_bitmap = kzalloc(BITS_TO_LONGS(lpi_chunks) * sizeof(long),
- GFP_KERNEL);
- if (!lpi_bitmap) {
- lpi_chunks = 0;
+
+ u32 nr_irq = 1UL << id_bits;
+ lpi_mng_t *lpi_free_mng = NULL;
+ lpi_mng_t *lpi_new = NULL;
+ INIT_LIST_HEAD(&lpi_free_list);
+ INIT_LIST_HEAD(&lpi_alloc_list);
+
+ lpi_free_mng = kzalloc(sizeof(lpi_mng_t), GFP_ATOMIC);
+ if(!lpi_free_mng) {
return -ENOMEM;
}
+ lpi_free_mng->base = 0;
+ lpi_free_mng->len = nr_irq;
+ list_add(&lpi_free_mng->lpi_list, &lpi_free_list);
+
+ do {
+ lpi_free_mng = list_first_entry(&lpi_free_list, lpi_mng_t, lpi_list);
+ if(lpi_free_mng->len == 8192) {
+ /*It is not lpi, so we delete */
+ if(lpi_free_mng->base == 0) {
+ list_del_init(&lpi_free_mng->lpi_list);
+ kfree(lpi_free_mng);
+ continue;
+ }
+ if(lpi_free_mng->base == 8192) {
+ goto out;
+ }
+ }
+ if(lpi_free_mng->len > 8192) {
+ lpi_new = kzalloc(sizeof (lpi_mng_t),
+ GFP_ATOMIC);
+ if(!lpi_new) {
+ return -ENOMEM;
+ }
+ lpi_free_mng->len /= 2;
+ lpi_new->base = lpi_free_mng->base + lpi_free_mng->len;
+ lpi_new->len = lpi_free_mng->len;
+ list_add(&lpi_new->lpi_list,&lpi_free_mng->lpi_list);
+ }
+ }while(1);
- pr_info("ITS: Allocated %d chunks for LPIs\n", (int)lpi_chunks);
+out:
+ pr_info("ITS: Allocated %d LPIs\n", nr_irq - 8192);
return 0;
}
+static lpi_mng_t* its_alloc_lpi(int nr_irqs)
+{
+ lpi_mng_t *lpi_alloc_mng = NULL;
+ lpi_mng_t *lpi_split = NULL;
+ lpi_mng_t *lpi_new = NULL;
+ int base;
+
+ base = 0x7fffffff;
+ do {
+ list_for_each_entry(lpi_alloc_mng, &lpi_free_list, lpi_list) {
+ if(nr_irqs > lpi_alloc_mng->len) {
+ continue;
+ }
+ if(nr_irqs == lpi_alloc_mng->len) {
+ list_del_init(&lpi_alloc_mng->lpi_list);
+ list_add(&lpi_alloc_mng->lpi_list, &lpi_alloc_list);
+ return lpi_alloc_mng;
+ }
+ if((nr_irqs < lpi_alloc_mng->len) && (lpi_alloc_mng->base < base)) {
+ base = lpi_alloc_mng->base;
+ lpi_split = lpi_alloc_mng;
+ }
+ }
+ lpi_new = kzalloc(sizeof (lpi_mng_t),
+ GFP_ATOMIC);
+ if(!lpi_new || !lpi_split) {
+ return NULL;
+ }
+
+ lpi_split->len /= 2;
+ lpi_new->base = lpi_split->base + lpi_split->len;
+ lpi_new->len = lpi_split->len;
+ list_add(&lpi_new->lpi_list,&lpi_split->lpi_list);
+
+ } while(1);
+}
+
static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids)
{
unsigned long *bitmap = NULL;
- int chunk_id;
- int nr_chunks;
- int i;
-
- nr_chunks = DIV_ROUND_UP(nr_irqs, IRQS_PER_CHUNK);
+ lpi_mng_t *lpi_alloc_mng = NULL;
spin_lock(&lpi_lock);
- do {
- chunk_id = bitmap_find_next_zero_area(lpi_bitmap, lpi_chunks,
- 0, nr_chunks, 0);
- if (chunk_id < lpi_chunks)
- break;
-
- nr_chunks--;
- } while (nr_chunks > 0);
+ lpi_alloc_mng = its_alloc_lpi(nr_irqs);
- if (!nr_chunks)
+ if (!lpi_alloc_mng)
goto out;
- bitmap = kzalloc(BITS_TO_LONGS(nr_chunks * IRQS_PER_CHUNK) * sizeof (long),
+ bitmap = kzalloc(BITS_TO_LONGS(nr_irqs) * sizeof (long),
GFP_ATOMIC);
if (!bitmap)
goto out;
- for (i = 0; i < nr_chunks; i++)
- set_bit(chunk_id + i, lpi_bitmap);
- *base = its_chunk_to_lpi(chunk_id);
- *nr_ids = nr_chunks * IRQS_PER_CHUNK;
+ *base = lpi_alloc_mng->base;
+ *nr_ids = lpi_alloc_mng->len;
out:
spin_unlock(&lpi_lock);
@@ -758,21 +803,47 @@ out:
return bitmap;
}
+static void its_joint_free_list(lpi_mng_t *free, lpi_mng_t *alloc)
+{
+ free->len = free->len * 2;
+ if(free->base > alloc->base) {
+ free->base = alloc->base;
+ }
+}
+
static void its_lpi_free(struct event_lpi_map *map)
{
int base = map->lpi_base;
- int nr_ids = map->nr_lpis;
- int lpi;
-
+ lpi_mng_t *lpi_alloc_mng = NULL;
+ lpi_mng_t *lpi_free_mng = NULL;
+ bool first_half;
+ int pair_base;
spin_lock(&lpi_lock);
- for (lpi = base; lpi < (base + nr_ids); lpi += IRQS_PER_CHUNK) {
- int chunk = its_lpi_to_chunk(lpi);
- BUG_ON(chunk > lpi_chunks);
- if (test_bit(chunk, lpi_bitmap)) {
- clear_bit(chunk, lpi_bitmap);
- } else {
- pr_err("Bad LPI chunk %d\n", chunk);
+ list_for_each_entry(lpi_alloc_mng, &lpi_alloc_list, lpi_list) {
+ if(lpi_alloc_mng->base == base) {
+ list_del_init(&lpi_alloc_mng->lpi_list);
+ break;
+ }
+
+ }
+
+ first_half = (lpi_alloc_mng->base % (lpi_alloc_mng->len * 2)) ? false : true;
+ if(first_half) {
+ pair_base = lpi_alloc_mng->base + lpi_alloc_mng->len;
+ }else {
+ pair_base = lpi_alloc_mng->base - lpi_alloc_mng->len;
+ }
+
+ list_for_each_entry(lpi_free_mng, &lpi_free_list, lpi_list) {
+ if(lpi_free_mng->base == pair_base) {
+ its_joint_free_list(lpi_free_mng, lpi_alloc_mng);
+ kfree(lpi_alloc_mng);
+ break;
+ }
+ if(lpi_alloc_mng->base < lpi_free_mng->base) {
+ list_add_tail(&lpi_alloc_mng->lpi_list, &lpi_free_mng->lpi_list);
+ break;
}
}
Best Regards,
Lei Zhang
--
Lei Zhang e-mail: zhang.lei at jp.fujitsu.com FUJITSU LIMITED
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox