* [PATCH v26 0/7] arm64: add kdump support
From: Ruslan Bilovol @ 2016-10-17 15:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160907042908.6232-1-takahiro.akashi@linaro.org>
Hi,
On 09/07/2016 07:29 AM, AKASHI Takahiro wrote:
> v26-specific note: After a comment from Rob[0], an idea of adding
> "linux,usable-memory-range" was dropped. Instead, an existing
> "reserved-memory" node will be used to limit usable memory ranges
> on crash dump kernel.
> This works not only on UEFI/ACPI systems but also on DT-only systems,
> but if he really insists on using DT-specific "usable-memory" property,
> I will post additional patches for kexec-tools. Those would be
> redundant, though.
> Even in that case, the kernel will not have to be changed.
>
> This patch series adds kdump support on arm64.
> There are some prerequisite patches [1],[2].
>
> To load a crash-dump kernel to the systems, a series of patches to
> kexec-tools, which have not yet been merged upstream, are needed.
> Please always use my latest kdump patches, v3 [3].
>
> To examine vmcore (/proc/vmcore) on a crash-dump kernel, you can use
> - crash utility (coming v7.1.6 or later) [4]
> (Necessary patches have already been queued in the master.)
>
>
> [0] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/452582.html
> [1] "arm64: mark reserved memblock regions explicitly in iomem"
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450433.html
> [2] "efi: arm64: treat regions with WT/WC set but WB cleared as memory"
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/451491.html
> [3] T.B.D.
> [4] https://github.com/crash-utility/crash.git
Are you going to rebase your patch series onto v4.9-rc1 tag soon? I see
that patches [1] and [2] are already in v4.9-rc1, but when tried to apply
this series, I've got conflict on first patch of the series ("arm64: kdump:
reserve memory for crash dump kernel"). I want to try arm64 kdump
patches again on my board, so I'm interested in this. The question is
whether I need to rebase it myself or you will do the same (and address
comments) soon.
Also I see Geoff published v6 of arm64 kexec-tools patches, so same
question is applicable to "(kexec-tools) arm64: add kdump support"
patch series.
Thanks,
Ruslan
^ permalink raw reply
* [PATCH 0/5] [media] Fix module autoload for media platform drivers
From: Javier Martinez Canillas @ 2016-10-17 15:44 UTC (permalink / raw)
To: linux-arm-kernel
Hello Mauro,
I noticed that module autoload won't be working in a bunch of media
platform drivers because the module alias information is not filled
in the modules. This patch series contains the fixes for them.
Best regards,
Javier
Javier Martinez Canillas (5):
[media] v4l: vsp1: Fix module autoload for OF registration
[media] v4l: rcar-fcp: Fix module autoload for OF registration
[media] rc: meson-ir: Fix module autoload
[media] s5p-cec: Fix module autoload
[media] st-cec: Fix module autoload
drivers/media/platform/rcar-fcp.c | 1 +
drivers/media/platform/vsp1/vsp1_drv.c | 1 +
drivers/media/rc/meson-ir.c | 1 +
drivers/staging/media/s5p-cec/s5p_cec.c | 1 +
drivers/staging/media/st-cec/stih-cec.c | 1 +
5 files changed, 5 insertions(+)
--
2.7.4
^ permalink raw reply
* [PATCH 3/5] [media] rc: meson-ir: Fix module autoload
From: Javier Martinez Canillas @ 2016-10-17 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476719053-17600-1-git-send-email-javier@osg.samsung.com>
If the driver is built as a module, autoload won't work because the module
alias information is not filled. So user-space can't match the registered
device with the corresponding module.
Export the module alias information using the MODULE_DEVICE_TABLE() macro.
Before this patch:
$ modinfo drivers/media/rc/meson-ir.ko | grep alias
$
After this patch:
$ modinfo drivers/media/rc/meson-ir.ko | grep alias
alias: of:N*T*Camlogic,meson-gxbb-irC*
alias: of:N*T*Camlogic,meson-gxbb-ir
alias: of:N*T*Camlogic,meson8b-irC*
alias: of:N*T*Camlogic,meson8b-ir
alias: of:N*T*Camlogic,meson6-irC*
alias: of:N*T*Camlogic,meson6-ir
Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>
---
drivers/media/rc/meson-ir.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
index 003fff07ade2..7eb3f4f1ddcd 100644
--- a/drivers/media/rc/meson-ir.c
+++ b/drivers/media/rc/meson-ir.c
@@ -218,6 +218,7 @@ static const struct of_device_id meson_ir_match[] = {
{ .compatible = "amlogic,meson-gxbb-ir" },
{ },
};
+MODULE_DEVICE_TABLE(of, meson_ir_match);
static struct platform_driver meson_ir_driver = {
.probe = meson_ir_probe,
--
2.7.4
^ permalink raw reply related
* [PATCH v7 REPOST 0/9] CPUs capacity information for heterogeneous systems
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
Hi all,
this is a repost of version 7 of "CPUs capacity information for heterogeneous
systems" patchset [1] (please refer to previous postings to get some context).
I only added Juno r1 dts, as discussed off-line with Sudeep meanwhile (no code
changes at all, so that's why I'm saying this is a repost).
I'm reposting as I didn't receive any comment (despite pinging people) on the
original v7 posting (apart from Vincent acking patches 2 and 4, thanks!). I
then waited until merge window for 4.9 was closed.
I'm thus now assuming that everybody is OK with the patches and that they can
be queued for 4.10 (we certainly need this plumbing at this point). Please
speak if my assumption is wrong (and provide feedback! :).
Otherwise I'm going to:
- use Russell's patching system for patches 2 and 8
- ask Sudeep to pull patches 3,5,6 and 7
- ask Catalin/Will to pull patches 1,4 and 9
Do you think we might get into trouble splitting the merge process this way?
Please let me know how to proceed otherwise.
As per orginal v7 posting, patches high level description:
o 01/09 introduces documentation for the new optional DT binding
o [02-07]/09 add cpu-capacity attribute to TC2, Juno, Juno r1 and Juno r2 DTs
and provide parsing of such information at boot time
o [08-09]/09 introduce sysfs attribute
In case you would like to test this out, I updated the branch here:
git://linux-arm.org/linux-jl.git upstream/default_caps_v7
This branch contains additional patches, useful to better understand how CPU
capacity information is actually used by the scheduler.
These patches also form the basis for Morten/Dietmar's "Clean-ups and
asymmetric cpu capacity support" series [2] (and the EAS stack in general).
Best,
- Juri
[1] v1 - https://lkml.org/lkml/2015/11/23/391
v2 - https://lkml.org/lkml/2016/1/8/417
v3 - https://lkml.org/lkml/2016/2/3/405
v4 - https://lkml.org/lkml/2016/3/18/350
v5 - https://lkml.org/lkml/2016/6/15/291
v6 - https://lkml.org/lkml/2016/7/19/419
v7 - https://lkml.org/lkml/2016/9/5/409
[2] https://lkml.org/lkml/2016/10/14/312
Juri Lelli (9):
Documentation: arm: define DT cpu capacity-dmips-mhz bindings
arm: parse cpu capacity-dmips-mhz from DT
arm, dts: add TC2 cpu capacity-dmips-mhz information
arm64: parse cpu capacity-dmips-mhz from DT
arm64, dts: add Juno cpu capacity-dmips-mhz information
arm64, dts: add Juno r1 cpu capacity-dmips-mhz information
arm64, dts: add Juno r2 cpu capacity-dmips-mhz information
arm: add sysfs cpu_capacity attribute
arm64: add sysfs cpu_capacity attribute
.../devicetree/bindings/arm/cpu-capacity.txt | 236 +++++++++++++++++++++
Documentation/devicetree/bindings/arm/cpus.txt | 10 +
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 5 +
arch/arm/kernel/topology.c | 229 +++++++++++++++++++-
arch/arm64/boot/dts/arm/juno-r1.dts | 6 +
arch/arm64/boot/dts/arm/juno-r2.dts | 6 +
arch/arm64/boot/dts/arm/juno.dts | 6 +
arch/arm64/kernel/topology.c | 232 +++++++++++++++++++-
8 files changed, 728 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/cpu-capacity.txt
--
2.10.0
^ permalink raw reply
* [PATCH v7 REPOST 1/9] Documentation: arm: define DT cpu capacity-dmips-mhz bindings
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
ARM systems may be configured to have cpus with different power/performance
characteristics within the same chip. In this case, additional information
has to be made available to the kernel (the scheduler in particular) for it
to be aware of such differences and take decisions accordingly.
Therefore, this patch aims at standardizing cpu capacities device tree
bindings for ARM platforms. Bindings define cpu capacity-dmips-mhz
parameter, to allow operating systems to retrieve such information from
the device tree and initialize related kernel structures, paving the way
for common code in the kernel to deal with heterogeneity.
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Olof Johansson <olof@lixom.net>
Cc: Gregory CLEMENT <gregory.clement@free-electrons.com>
Cc: Paul Walmsley <paul@pwsan.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: devicetree at vger.kernel.org
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Vincent Guittot <vincent.guittot@linaro.org>
---
Changes from v1:
- removed section regarding capacity-scale
- added information regarding normalization
Changes from v4:
- binding changed to capacity-dmips-mhz
- sections and changelod updated accordingly
Changes from v5:
- addressed Mark and Vincent comments
---
.../devicetree/bindings/arm/cpu-capacity.txt | 236 +++++++++++++++++++++
Documentation/devicetree/bindings/arm/cpus.txt | 10 +
2 files changed, 246 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/cpu-capacity.txt
diff --git a/Documentation/devicetree/bindings/arm/cpu-capacity.txt b/Documentation/devicetree/bindings/arm/cpu-capacity.txt
new file mode 100644
index 000000000000..7809fbe0cdb7
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/cpu-capacity.txt
@@ -0,0 +1,236 @@
+==========================================
+ARM CPUs capacity bindings
+==========================================
+
+==========================================
+1 - Introduction
+==========================================
+
+ARM systems may be configured to have cpus with different power/performance
+characteristics within the same chip. In this case, additional information has
+to be made available to the kernel for it to be aware of such differences and
+take decisions accordingly.
+
+==========================================
+2 - CPU capacity definition
+==========================================
+
+CPU capacity is a number that provides the scheduler information about CPUs
+heterogeneity. Such heterogeneity can come from micro-architectural differences
+(e.g., ARM big.LITTLE systems) or maximum frequency at which CPUs can run
+(e.g., SMP systems with multiple frequency domains). Heterogeneity in this
+context is about differing performance characteristics; this binding tries to
+capture a first-order approximation of the relative performance of CPUs.
+
+CPU capacities are obtained by running a suitable benchmark. This binding makes
+no guarantees on the validity or suitability of any particular benchmark, the
+final capacity should, however, be:
+
+* A "single-threaded" or CPU affine benchmark
+* Divided by the running frequency of the CPU executing the benchmark
+* Not subject to dynamic frequency scaling of the CPU
+
+For the time being we however advise usage of the Dhrystone benchmark. What
+above thus becomes:
+
+CPU capacities are obtained by running the Dhrystone benchmark on each CPU at
+max frequency (with caches enabled). The obtained DMIPS score is then divided
+by the frequency (in MHz) at which the benchmark has been run, so that
+DMIPS/MHz are obtained. Such values are then normalized w.r.t. the highest
+score obtained in the system.
+
+==========================================
+3 - capacity-dmips-mhz
+==========================================
+
+capacity-dmips-mhz is an optional cpu node [1] property: u32 value
+representing CPU capacity expressed in normalized DMIPS/MHz. At boot time, the
+maximum frequency available to the cpu is then used to calculate the capacity
+value internally used by the kernel.
+
+capacity-dmips-mhz property is all-or-nothing: if it is specified for a cpu
+node, it has to be specified for every other cpu nodes, or the system will
+fall back to the default capacity value for every CPU. If cpufreq is not
+available, final capacities are calculated by directly using capacity-dmips-
+mhz values (normalized w.r.t. the highest value found while parsing the DT).
+
+===========================================
+4 - Examples
+===========================================
+
+Example 1 (ARM 64-bit, 6-cpu system, two clusters):
+capacities-dmips-mhz are scaled w.r.t. 1024 (cpu at 0 and cpu at 1)
+supposing cluster0 at max-freq=1100 and custer1 at max-freq=850,
+final capacities are 1024 for cluster0 and 446 for cluster1
+
+cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu-map {
+ cluster0 {
+ core0 {
+ cpu = <&A57_0>;
+ };
+ core1 {
+ cpu = <&A57_1>;
+ };
+ };
+
+ cluster1 {
+ core0 {
+ cpu = <&A53_0>;
+ };
+ core1 {
+ cpu = <&A53_1>;
+ };
+ core2 {
+ cpu = <&A53_2>;
+ };
+ core3 {
+ cpu = <&A53_3>;
+ };
+ };
+ };
+
+ idle-states {
+ entry-method = "arm,psci";
+
+ CPU_SLEEP_0: cpu-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ local-timer-stop;
+ entry-latency-us = <100>;
+ exit-latency-us = <250>;
+ min-residency-us = <150>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x1010000>;
+ local-timer-stop;
+ entry-latency-us = <800>;
+ exit-latency-us = <700>;
+ min-residency-us = <2500>;
+ };
+ };
+
+ A57_0: cpu at 0 {
+ compatible = "arm,cortex-a57","arm,armv8";
+ reg = <0x0 0x0>;
+ device_type = "cpu";
+ enable-method = "psci";
+ next-level-cache = <&A57_L2>;
+ clocks = <&scpi_dvfs 0>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <1024>;
+ };
+
+ A57_1: cpu at 1 {
+ compatible = "arm,cortex-a57","arm,armv8";
+ reg = <0x0 0x1>;
+ device_type = "cpu";
+ enable-method = "psci";
+ next-level-cache = <&A57_L2>;
+ clocks = <&scpi_dvfs 0>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <1024>;
+ };
+
+ A53_0: cpu at 100 {
+ compatible = "arm,cortex-a53","arm,armv8";
+ reg = <0x0 0x100>;
+ device_type = "cpu";
+ enable-method = "psci";
+ next-level-cache = <&A53_L2>;
+ clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
+ };
+
+ A53_1: cpu at 101 {
+ compatible = "arm,cortex-a53","arm,armv8";
+ reg = <0x0 0x101>;
+ device_type = "cpu";
+ enable-method = "psci";
+ next-level-cache = <&A53_L2>;
+ clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
+ };
+
+ A53_2: cpu at 102 {
+ compatible = "arm,cortex-a53","arm,armv8";
+ reg = <0x0 0x102>;
+ device_type = "cpu";
+ enable-method = "psci";
+ next-level-cache = <&A53_L2>;
+ clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
+ };
+
+ A53_3: cpu at 103 {
+ compatible = "arm,cortex-a53","arm,armv8";
+ reg = <0x0 0x103>;
+ device_type = "cpu";
+ enable-method = "psci";
+ next-level-cache = <&A53_L2>;
+ clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
+ };
+
+ A57_L2: l2-cache0 {
+ compatible = "cache";
+ };
+
+ A53_L2: l2-cache1 {
+ compatible = "cache";
+ };
+};
+
+Example 2 (ARM 32-bit, 4-cpu system, two clusters,
+ cpus 0,1 at 1GHz, cpus 2,3 at 500MHz):
+capacities-dmips-mhz are scaled w.r.t. 2 (cpu at 0 and cpu at 1), this means that first
+cpu at 0 and cpu at 1 are twice fast than cpu at 2 and cpu at 3 (at the same frequency)
+
+cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu0: cpu at 0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0>;
+ capacity-dmips-mhz = <2>;
+ };
+
+ cpu1: cpu at 1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <1>;
+ capacity-dmips-mhz = <2>;
+ };
+
+ cpu2: cpu at 2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x100>;
+ capacity-dmips-mhz = <1>;
+ };
+
+ cpu3: cpu at 3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a15";
+ reg = <0x101>;
+ capacity-dmips-mhz = <1>;
+ };
+};
+
+===========================================
+5 - References
+===========================================
+
+[1] ARM Linux Kernel documentation - CPUs bindings
+ Documentation/devicetree/bindings/arm/cpus.txt
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index e6782d50cbcd..c1dcf4cade2e 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -241,6 +241,14 @@ nodes to be present and contain the properties described below.
# List of phandles to idle state nodes supported
by this cpu [3].
+ - capacity-dmips-mhz
+ Usage: Optional
+ Value type: <u32>
+ Definition:
+ # u32 value representing CPU capacity [3] in
+ DMIPS/MHz, relative to highest capacity-dmips-mhz
+ in the system.
+
- rockchip,pmu
Usage: optional for systems that have an "enable-method"
property value of "rockchip,rk3066-smp"
@@ -464,3 +472,5 @@ cpus {
[2] arm/msm/qcom,kpss-acc.txt
[3] ARM Linux kernel documentation - idle states bindings
Documentation/devicetree/bindings/arm/idle-states.txt
+[3] ARM Linux kernel documentation - cpu capacity bindings
+ Documentation/devicetree/bindings/arm/cpu-capacity.txt
--
2.10.0
^ permalink raw reply related
* [PATCH v7 REPOST 2/9] arm: parse cpu capacity-dmips-mhz from DT
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
With the introduction of cpu capacity-dmips-mhz bindings, CPU capacities
can now be calculated from values extracted from DT and information
coming from cpufreq. Add parsing of DT information at boot time, and
complement it with cpufreq information. We keep code that can produce
same information, based on different DT properties and hard-coded
values, as fall-back for backward compatibility.
Caveat: the information provided by this patch will start to be used in
the future. We need to #define arch_scale_cpu_capacity to something
provided in arch, so that scheduler's default implementation (which gets
used if arch_scale_cpu_capacity is not defined) is overwritten.
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
Acked-by: Vincent Guittot <vincent.guittot@linaro.org>
---
Changes from v1:
- normalize w.r.t. highest capacity found in DT
- bailout conditions (all-or-nothing)
Changes from v4:
- parsing modified to reflect change in binding (capacity-dmips-mhz)
Changes from v5:
- allocate raw_capacity array with kcalloc()
- pr_err() only for partial capacity information
Changes from v6:
- use cpuinfo.max_freq instead of policy->max
- add delayed work to unregister cpufreq notifier
---
arch/arm/kernel/topology.c | 156 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 155 insertions(+), 1 deletion(-)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index ec279d161b32..18bb0474f7ec 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/cpufreq.h>
#include <asm/cputype.h>
#include <asm/topology.h>
@@ -78,6 +79,144 @@ static unsigned long *__cpu_capacity;
#define cpu_capacity(cpu) __cpu_capacity[cpu]
static unsigned long middle_capacity = 1;
+static bool cap_from_dt = true;
+static u32 *raw_capacity;
+static bool cap_parsing_failed;
+static u32 capacity_scale;
+
+static int __init parse_cpu_capacity(struct device_node *cpu_node, int cpu)
+{
+ int ret = 1;
+ u32 cpu_capacity;
+
+ if (cap_parsing_failed)
+ return !ret;
+
+ ret = of_property_read_u32(cpu_node,
+ "capacity-dmips-mhz",
+ &cpu_capacity);
+ if (!ret) {
+ if (!raw_capacity) {
+ raw_capacity = kcalloc(num_possible_cpus(),
+ sizeof(*raw_capacity),
+ GFP_KERNEL);
+ if (!raw_capacity) {
+ pr_err("cpu_capacity: failed to allocate memory for raw capacities\n");
+ cap_parsing_failed = true;
+ return !ret;
+ }
+ }
+ capacity_scale = max(cpu_capacity, capacity_scale);
+ raw_capacity[cpu] = cpu_capacity;
+ pr_debug("cpu_capacity: %s cpu_capacity=%u (raw)\n",
+ cpu_node->full_name, raw_capacity[cpu]);
+ } else {
+ if (raw_capacity) {
+ pr_err("cpu_capacity: missing %s raw capacity\n",
+ cpu_node->full_name);
+ pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n");
+ }
+ cap_parsing_failed = true;
+ kfree(raw_capacity);
+ }
+
+ return !ret;
+}
+
+static void normalize_cpu_capacity(void)
+{
+ u64 capacity;
+ int cpu;
+
+ if (!raw_capacity || cap_parsing_failed)
+ return;
+
+ pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
+ for_each_possible_cpu(cpu) {
+ capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
+ / capacity_scale;
+ set_capacity_scale(cpu, capacity);
+ pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
+ cpu, arch_scale_cpu_capacity(NULL, cpu));
+ }
+}
+
+#ifdef CONFIG_CPU_FREQ
+static cpumask_var_t cpus_to_visit;
+static bool cap_parsing_done;
+static void parsing_done_workfn(struct work_struct *work);
+static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
+
+static int
+init_cpu_capacity_callback(struct notifier_block *nb,
+ unsigned long val,
+ void *data)
+{
+ struct cpufreq_policy *policy = data;
+ int cpu;
+
+ if (cap_parsing_failed || cap_parsing_done)
+ return 0;
+
+ switch (val) {
+ case CPUFREQ_NOTIFY:
+ pr_debug("cpu_capacity: init cpu capacity for CPUs [%*pbl] (to_visit=%*pbl)\n",
+ cpumask_pr_args(policy->related_cpus),
+ cpumask_pr_args(cpus_to_visit));
+ cpumask_andnot(cpus_to_visit,
+ cpus_to_visit,
+ policy->related_cpus);
+ for_each_cpu(cpu, policy->related_cpus) {
+ raw_capacity[cpu] = arch_scale_cpu_capacity(NULL, cpu) *
+ policy->cpuinfo.max_freq / 1000UL;
+ capacity_scale = max(raw_capacity[cpu], capacity_scale);
+ }
+ if (cpumask_empty(cpus_to_visit)) {
+ normalize_cpu_capacity();
+ kfree(raw_capacity);
+ pr_debug("cpu_capacity: parsing done\n");
+ cap_parsing_done = true;
+ schedule_work(&parsing_done_work);
+ }
+ }
+ return 0;
+}
+
+static struct notifier_block init_cpu_capacity_notifier = {
+ .notifier_call = init_cpu_capacity_callback,
+};
+
+static int __init register_cpufreq_notifier(void)
+{
+ if (cap_parsing_failed)
+ return -EINVAL;
+
+ if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) {
+ pr_err("cpu_capacity: failed to allocate memory for cpus_to_visit\n");
+ return -ENOMEM;
+ }
+ cpumask_copy(cpus_to_visit, cpu_possible_mask);
+
+ return cpufreq_register_notifier(&init_cpu_capacity_notifier,
+ CPUFREQ_POLICY_NOTIFIER);
+}
+core_initcall(register_cpufreq_notifier);
+
+static void parsing_done_workfn(struct work_struct *work)
+{
+ cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
+ CPUFREQ_POLICY_NOTIFIER);
+}
+
+#else
+static int __init free_raw_capacity(void)
+{
+ kfree(raw_capacity);
+
+ return 0;
+}
+core_initcall(free_raw_capacity);
+#endif
/*
* Iterate all CPUs' descriptor in DT and compute the efficiency
@@ -99,6 +238,12 @@ static void __init parse_dt_topology(void)
__cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity),
GFP_NOWAIT);
+ cn = of_find_node_by_path("/cpus");
+ if (!cn) {
+ pr_err("No CPU information found in DT\n");
+ return;
+ }
+
for_each_possible_cpu(cpu) {
const u32 *rate;
int len;
@@ -110,6 +255,13 @@ static void __init parse_dt_topology(void)
continue;
}
+ if (parse_cpu_capacity(cn, cpu)) {
+ of_node_put(cn);
+ continue;
+ }
+
+ cap_from_dt = false;
+
for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++)
if (of_device_is_compatible(cn, cpu_eff->compatible))
break;
@@ -151,6 +303,8 @@ static void __init parse_dt_topology(void)
middle_capacity = ((max_capacity / 3)
>> (SCHED_CAPACITY_SHIFT-1)) + 1;
+ if (cap_from_dt && !cap_parsing_failed)
+ normalize_cpu_capacity();
}
/*
@@ -160,7 +314,7 @@ static void __init parse_dt_topology(void)
*/
static void update_cpu_capacity(unsigned int cpu)
{
- if (!cpu_capacity(cpu))
+ if (!cpu_capacity(cpu) || cap_from_dt)
return;
set_capacity_scale(cpu, cpu_capacity(cpu) / middle_capacity);
--
2.10.0
^ permalink raw reply related
* [PATCH v7 REPOST 3/9] arm, dts: add TC2 cpu capacity-dmips-mhz information
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
Add TC2 cpu capacity information.
Cc: Liviu Dudau <liviu.dudau@arm.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: devicetree at vger.kernel.org
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
Acked-by: Sudeep Holla <sudeep.holla@arm.com>
---
Changes from v1:
- capacity-scale removed
Changes from v4:
- binding changed to capacity-dmips-mhz
Changes from v6:
- s/binding// in changelog
---
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
index 0205c97efdef..45d08cc37b01 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
@@ -39,6 +39,7 @@
reg = <0>;
cci-control-port = <&cci_control1>;
cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
+ capacity-dmips-mhz = <1024>;
};
cpu1: cpu at 1 {
@@ -47,6 +48,7 @@
reg = <1>;
cci-control-port = <&cci_control1>;
cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
+ capacity-dmips-mhz = <1024>;
};
cpu2: cpu at 2 {
@@ -55,6 +57,7 @@
reg = <0x100>;
cci-control-port = <&cci_control2>;
cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
+ capacity-dmips-mhz = <516>;
};
cpu3: cpu at 3 {
@@ -63,6 +66,7 @@
reg = <0x101>;
cci-control-port = <&cci_control2>;
cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
+ capacity-dmips-mhz = <516>;
};
cpu4: cpu at 4 {
@@ -71,6 +75,7 @@
reg = <0x102>;
cci-control-port = <&cci_control2>;
cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
+ capacity-dmips-mhz = <516>;
};
idle-states {
--
2.10.0
^ permalink raw reply related
* [PATCH v7 REPOST 4/9] arm64: parse cpu capacity-dmips-mhz from DT
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
With the introduction of cpu capacity-dmips-mhz bindings, CPU capacities
can now be calculated from values extracted from DT and information
coming from cpufreq. Add parsing of DT information at boot time, and
complement it with cpufreq information. Also, store such information
using per CPU variables, as we do for arm.
Caveat: the information provided by this patch will start to be used in
the future. We need to #define arch_scale_cpu_capacity to something
provided in arch, so that scheduler's default implementation (which gets
used if arch_scale_cpu_capacity is not defined) is overwritten.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
Acked-by: Vincent Guittot <vincent.guittot@linaro.org>
---
Changes from v1:
- normalize w.r.t. highest capacity found in DT
- bailout conditions (all-or-nothing)
Changes from v4:
- parsing modified to reflect change in binding (capacity-dmips-mhz)
Changes from v5:
- allocate raw_capacity array with kcalloc()
- pr_err() only for partial capacity information
Changes from v6:
- use cpuinfo.max_freq instead of policy->max
- add delayed work to unregister cpufreq notifier
---
arch/arm64/kernel/topology.c | 159 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 158 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 694f6deedbab..b75b0ba2e113 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -19,10 +19,162 @@
#include <linux/nodemask.h>
#include <linux/of.h>
#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/cpufreq.h>
#include <asm/cputype.h>
#include <asm/topology.h>
+static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
+
+unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
+{
+ return per_cpu(cpu_scale, cpu);
+}
+
+static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
+{
+ per_cpu(cpu_scale, cpu) = capacity;
+}
+
+static u32 capacity_scale;
+static u32 *raw_capacity;
+static bool cap_parsing_failed;
+
+static void __init parse_cpu_capacity(struct device_node *cpu_node, int cpu)
+{
+ int ret;
+ u32 cpu_capacity;
+
+ if (cap_parsing_failed)
+ return;
+
+ ret = of_property_read_u32(cpu_node,
+ "capacity-dmips-mhz",
+ &cpu_capacity);
+ if (!ret) {
+ if (!raw_capacity) {
+ raw_capacity = kcalloc(num_possible_cpus(),
+ sizeof(*raw_capacity),
+ GFP_KERNEL);
+ if (!raw_capacity) {
+ pr_err("cpu_capacity: failed to allocate memory for raw capacities\n");
+ cap_parsing_failed = true;
+ return;
+ }
+ }
+ capacity_scale = max(cpu_capacity, capacity_scale);
+ raw_capacity[cpu] = cpu_capacity;
+ pr_debug("cpu_capacity: %s cpu_capacity=%u (raw)\n",
+ cpu_node->full_name, raw_capacity[cpu]);
+ } else {
+ if (raw_capacity) {
+ pr_err("cpu_capacity: missing %s raw capacity\n",
+ cpu_node->full_name);
+ pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n");
+ }
+ cap_parsing_failed = true;
+ kfree(raw_capacity);
+ }
+}
+
+static void normalize_cpu_capacity(void)
+{
+ u64 capacity;
+ int cpu;
+
+ if (!raw_capacity || cap_parsing_failed)
+ return;
+
+ pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
+ for_each_possible_cpu(cpu) {
+ pr_debug("cpu_capacity: cpu=%d raw_capacity=%u\n",
+ cpu, raw_capacity[cpu]);
+ capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
+ / capacity_scale;
+ set_capacity_scale(cpu, capacity);
+ pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
+ cpu, arch_scale_cpu_capacity(NULL, cpu));
+ }
+}
+
+#ifdef CONFIG_CPU_FREQ
+static cpumask_var_t cpus_to_visit;
+static bool cap_parsing_done;
+static void parsing_done_workfn(struct work_struct *work);
+static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
+
+static int
+init_cpu_capacity_callback(struct notifier_block *nb,
+ unsigned long val,
+ void *data)
+{
+ struct cpufreq_policy *policy = data;
+ int cpu;
+
+ if (cap_parsing_failed || cap_parsing_done)
+ return 0;
+
+ switch (val) {
+ case CPUFREQ_NOTIFY:
+ pr_debug("cpu_capacity: init cpu capacity for CPUs [%*pbl] (to_visit=%*pbl)\n",
+ cpumask_pr_args(policy->related_cpus),
+ cpumask_pr_args(cpus_to_visit));
+ cpumask_andnot(cpus_to_visit,
+ cpus_to_visit,
+ policy->related_cpus);
+ for_each_cpu(cpu, policy->related_cpus) {
+ raw_capacity[cpu] = arch_scale_cpu_capacity(NULL, cpu) *
+ policy->cpuinfo.max_freq / 1000UL;
+ capacity_scale = max(raw_capacity[cpu], capacity_scale);
+ }
+ if (cpumask_empty(cpus_to_visit)) {
+ normalize_cpu_capacity();
+ kfree(raw_capacity);
+ pr_debug("cpu_capacity: parsing done\n");
+ cap_parsing_done = true;
+ schedule_work(&parsing_done_work);
+ }
+ }
+ return 0;
+}
+
+static struct notifier_block init_cpu_capacity_notifier = {
+ .notifier_call = init_cpu_capacity_callback,
+};
+
+static int __init register_cpufreq_notifier(void)
+{
+ if (cap_parsing_failed)
+ return -EINVAL;
+
+ if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) {
+ pr_err("cpu_capacity: failed to allocate memory for cpus_to_visit\n");
+ return -ENOMEM;
+ }
+ cpumask_copy(cpus_to_visit, cpu_possible_mask);
+
+ return cpufreq_register_notifier(&init_cpu_capacity_notifier,
+ CPUFREQ_POLICY_NOTIFIER);
+}
+core_initcall(register_cpufreq_notifier);
+
+static void parsing_done_workfn(struct work_struct *work)
+{
+ cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
+ CPUFREQ_POLICY_NOTIFIER);
+}
+
+#else
+static int __init free_raw_capacity(void)
+{
+ kfree(raw_capacity);
+
+ return 0;
+}
+core_initcall(free_raw_capacity);
+#endif
+
static int __init get_cpu_for_node(struct device_node *node)
{
struct device_node *cpu_node;
@@ -34,6 +186,7 @@ static int __init get_cpu_for_node(struct device_node *node)
for_each_possible_cpu(cpu) {
if (of_get_cpu_node(cpu, NULL) == cpu_node) {
+ parse_cpu_capacity(cpu_node, cpu);
of_node_put(cpu_node);
return cpu;
}
@@ -178,13 +331,17 @@ static int __init parse_dt_topology(void)
* cluster with restricted subnodes.
*/
map = of_get_child_by_name(cn, "cpu-map");
- if (!map)
+ if (!map) {
+ cap_parsing_failed = true;
goto out;
+ }
ret = parse_cluster(map, 0);
if (ret != 0)
goto out_map;
+ normalize_cpu_capacity();
+
/*
* Check that all cores are in the topology; the SMP code will
* only mark cores described in the DT as possible.
--
2.10.0
^ permalink raw reply related
* [PATCH v7 REPOST 5/9] arm64, dts: add Juno cpu capacity-dmips-mhz information
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
Add Juno cpu capacity-dmips-mhz information.
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Liviu Dudau <Liviu.Dudau@arm.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jon Medhurst <tixy@linaro.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: devicetree at vger.kernel.org
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
Acked-by: Sudeep Holla <sudeep.holla@arm.com>
---
Changes from v1:
- capacity-scale removed
Changes from v4:
- binding changed to capacity-dmips-mhz
Changes from v6:
- s/bindings// in changelog
---
arch/arm64/boot/dts/arm/juno.dts | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index a7270eff6939..6b4135e9cfe5 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -90,6 +90,7 @@
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <1024>;
};
A57_1: cpu at 1 {
@@ -100,6 +101,7 @@
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <1024>;
};
A53_0: cpu at 100 {
@@ -110,6 +112,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
};
A53_1: cpu at 101 {
@@ -120,6 +123,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
};
A53_2: cpu at 102 {
@@ -130,6 +134,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
};
A53_3: cpu at 103 {
@@ -140,6 +145,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
};
A57_L2: l2-cache0 {
--
2.10.0
^ permalink raw reply related
* [PATCH v7 REPOST 6/9] arm64, dts: add Juno r1 cpu capacity-dmips-mhz information
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
Add Juno r1 cpu capacity-dmips-mhz information.
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Liviu Dudau <Liviu.Dudau@arm.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jon Medhurst <tixy@linaro.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: devicetree at vger.kernel.org
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
---
Changes from v6:
- new patch as per off-line discussion with Sudeep
---
arch/arm64/boot/dts/arm/juno-r1.dts | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno-r1.dts b/arch/arm64/boot/dts/arm/juno-r1.dts
index 123a58b29cbd..3be8a3ef671c 100644
--- a/arch/arm64/boot/dts/arm/juno-r1.dts
+++ b/arch/arm64/boot/dts/arm/juno-r1.dts
@@ -90,6 +90,7 @@
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <1024>;
};
A57_1: cpu at 1 {
@@ -100,6 +101,7 @@
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <1024>;
};
A53_0: cpu at 100 {
@@ -110,6 +112,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
};
A53_1: cpu at 101 {
@@ -120,6 +123,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
};
A53_2: cpu at 102 {
@@ -130,6 +134,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
};
A53_3: cpu at 103 {
@@ -140,6 +145,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <578>;
};
A57_L2: l2-cache0 {
--
2.10.0
^ permalink raw reply related
* [PATCH v7 REPOST 7/9] arm64, dts: add Juno r2 cpu capacity-dmips-mhz information
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
Add Juno r2 cpu capacity-dmips-mhz information.
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Liviu Dudau <Liviu.Dudau@arm.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Jon Medhurst <tixy@linaro.org>
Cc: Olof Johansson <olof@lixom.net>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: devicetree at vger.kernel.org
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
Acked-by: Sudeep Holla <sudeep.holla@arm.com>
---
Changes from v4:
- new patch since Juno r2 dt has been merged
Changes from v6:
- s/bindings// in changelog
---
arch/arm64/boot/dts/arm/juno-r2.dts | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno-r2.dts b/arch/arm64/boot/dts/arm/juno-r2.dts
index 007be826efce..614fc9227943 100644
--- a/arch/arm64/boot/dts/arm/juno-r2.dts
+++ b/arch/arm64/boot/dts/arm/juno-r2.dts
@@ -90,6 +90,7 @@
next-level-cache = <&A72_L2>;
clocks = <&scpi_dvfs 0>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <1024>;
};
A72_1: cpu at 1 {
@@ -100,6 +101,7 @@
next-level-cache = <&A72_L2>;
clocks = <&scpi_dvfs 0>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <1024>;
};
A53_0: cpu at 100 {
@@ -110,6 +112,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <485>;
};
A53_1: cpu at 101 {
@@ -120,6 +123,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <485>;
};
A53_2: cpu at 102 {
@@ -130,6 +134,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <485>;
};
A53_3: cpu at 103 {
@@ -140,6 +145,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ capacity-dmips-mhz = <485>;
};
A72_L2: l2-cache0 {
--
2.10.0
^ permalink raw reply related
* [PATCH v7 REPOST 8/9] arm: add sysfs cpu_capacity attribute
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
Add a sysfs cpu_capacity attribute with which it is possible to read and
write (thus over-writing default values) CPUs capacity. This might be
useful in situations where values needs changing after boot.
The new attribute shows up as:
/sys/devices/system/cpu/cpu*/cpu_capacity
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
---
Changes from v5:
- add mutex to protect cpu_scale (as pointed out by Morten off-line)
---
arch/arm/kernel/topology.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 18bb0474f7ec..46167bffbcf1 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -42,6 +42,7 @@
* updated during this sequence.
*/
static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
+static DEFINE_MUTEX(cpu_scale_mutex);
unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
{
@@ -53,6 +54,76 @@ static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
per_cpu(cpu_scale, cpu) = capacity;
}
+#ifdef CONFIG_PROC_SYSCTL
+#include <asm/cpu.h>
+#include <linux/string.h>
+static ssize_t show_cpu_capacity(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+ ssize_t rc;
+ int cpunum = cpu->dev.id;
+ unsigned long capacity = arch_scale_cpu_capacity(NULL, cpunum);
+
+ rc = sprintf(buf, "%lu\n", capacity);
+
+ return rc;
+}
+
+static ssize_t store_cpu_capacity(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+ int this_cpu = cpu->dev.id, i;
+ unsigned long new_capacity;
+ ssize_t ret;
+
+ if (count) {
+ char *p = (char *) buf;
+
+ ret = kstrtoul(p, 0, &new_capacity);
+ if (ret)
+ return ret;
+ if (new_capacity > SCHED_CAPACITY_SCALE)
+ return -EINVAL;
+
+ mutex_lock(&cpu_scale_mutex);
+ for_each_cpu(i, &cpu_topology[this_cpu].core_sibling)
+ set_capacity_scale(i, new_capacity);
+ mutex_unlock(&cpu_scale_mutex);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(cpu_capacity,
+ 0644,
+ show_cpu_capacity,
+ store_cpu_capacity);
+
+static int register_cpu_capacity_sysctl(void)
+{
+ int i;
+ struct device *cpu;
+
+ for_each_possible_cpu(i) {
+ cpu = get_cpu_device(i);
+ if (!cpu) {
+ pr_err("%s: too early to get CPU%d device!\n",
+ __func__, i);
+ continue;
+ }
+ device_create_file(cpu, &dev_attr_cpu_capacity);
+ }
+
+ return 0;
+}
+late_initcall(register_cpu_capacity_sysctl);
+#endif
+
#ifdef CONFIG_OF
struct cpu_efficiency {
const char *compatible;
@@ -132,6 +203,7 @@ static void normalize_cpu_capacity(void)
return;
pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
+ mutex_lock(&cpu_scale_mutex);
for_each_possible_cpu(cpu) {
capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
/ capacity_scale;
@@ -139,6 +211,7 @@ static void normalize_cpu_capacity(void)
pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
cpu, arch_scale_cpu_capacity(NULL, cpu));
}
+ mutex_unlock(&cpu_scale_mutex);
}
#ifdef CONFIG_CPU_FREQ
--
2.10.0
^ permalink raw reply related
* [PATCH v7 REPOST 9/9] arm64: add sysfs cpu_capacity attribute
From: Juri Lelli @ 2016-10-17 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
Add a sysfs cpu_capacity attribute with which it is possible to read and
write (thus over-writing default values) CPUs capacity. This might be
useful in situations where values needs changing after boot.
The new attribute shows up as:
/sys/devices/system/cpu/cpu*/cpu_capacity
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Juri Lelli <juri.lelli@arm.com>
---
Changes from v5:
- add mutex to protect cpu_scale (as pointed out by Morten off-line)
---
arch/arm64/kernel/topology.c | 73 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index b75b0ba2e113..cff34cc858b7 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -26,6 +26,7 @@
#include <asm/topology.h>
static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
+static DEFINE_MUTEX(cpu_scale_mutex);
unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu)
{
@@ -37,6 +38,76 @@ static void set_capacity_scale(unsigned int cpu, unsigned long capacity)
per_cpu(cpu_scale, cpu) = capacity;
}
+#ifdef CONFIG_PROC_SYSCTL
+#include <asm/cpu.h>
+#include <linux/string.h>
+static ssize_t show_cpu_capacity(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+ ssize_t rc;
+ int cpunum = cpu->dev.id;
+ unsigned long capacity = arch_scale_cpu_capacity(NULL, cpunum);
+
+ rc = sprintf(buf, "%lu\n", capacity);
+
+ return rc;
+}
+
+static ssize_t store_cpu_capacity(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, dev);
+ int this_cpu = cpu->dev.id, i;
+ unsigned long new_capacity;
+ ssize_t ret;
+
+ if (count) {
+ char *p = (char *) buf;
+
+ ret = kstrtoul(p, 0, &new_capacity);
+ if (ret)
+ return ret;
+ if (new_capacity > SCHED_CAPACITY_SCALE)
+ return -EINVAL;
+
+ mutex_lock(&cpu_scale_mutex);
+ for_each_cpu(i, &cpu_topology[this_cpu].core_sibling)
+ set_capacity_scale(i, new_capacity);
+ mutex_unlock(&cpu_scale_mutex);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(cpu_capacity,
+ 0644,
+ show_cpu_capacity,
+ store_cpu_capacity);
+
+static int register_cpu_capacity_sysctl(void)
+{
+ int i;
+ struct device *cpu;
+
+ for_each_possible_cpu(i) {
+ cpu = get_cpu_device(i);
+ if (!cpu) {
+ pr_err("%s: too early to get CPU%d device!\n",
+ __func__, i);
+ continue;
+ }
+ device_create_file(cpu, &dev_attr_cpu_capacity);
+ }
+
+ return 0;
+}
+late_initcall(register_cpu_capacity_sysctl);
+#endif
+
static u32 capacity_scale;
static u32 *raw_capacity;
static bool cap_parsing_failed;
@@ -87,6 +158,7 @@ static void normalize_cpu_capacity(void)
return;
pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
+ mutex_lock(&cpu_scale_mutex);
for_each_possible_cpu(cpu) {
pr_debug("cpu_capacity: cpu=%d raw_capacity=%u\n",
cpu, raw_capacity[cpu]);
@@ -96,6 +168,7 @@ static void normalize_cpu_capacity(void)
pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
cpu, arch_scale_cpu_capacity(NULL, cpu));
}
+ mutex_unlock(&cpu_scale_mutex);
}
#ifdef CONFIG_CPU_FREQ
--
2.10.0
^ permalink raw reply related
* [PATCH] ARM/orion/gpio: Replace three seq_printf() calls by seq_puts() in orion_gpio_dbg_show()
From: Gregory CLEMENT @ 2016-10-17 15:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <92ba3c30-386a-bb86-623d-0917cd9e61a2@users.sourceforge.net>
Hi Markus,
On dim., oct. 16 2016, SF Markus Elfring <elfring@users.sourceforge.net> wrote:
> From: Markus Elfring <elfring@users.sourceforge.net>
> Date: Sun, 16 Oct 2016 12:30:48 +0200
>
> Strings which did not contain data format specifications should be put
> into a sequence. Thus use the corresponding function "seq_puts".
>
> This issue was detected by using the Coccinelle software.
>
Applied on mvebu/drivers
Thanks,
Gregory
> Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
> ---
> arch/arm/plat-orion/gpio.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c
> index f740693..26a531e 100644
> --- a/arch/arm/plat-orion/gpio.c
> +++ b/arch/arm/plat-orion/gpio.c
> @@ -478,13 +478,13 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
> (data_in ^ in_pol) & msk ? "hi" : "lo",
> in_pol & msk ? "lo" : "hi");
> if (!((edg_msk | lvl_msk) & msk)) {
> - seq_printf(s, " disabled\n");
> + seq_puts(s, " disabled\n");
> continue;
> }
> if (edg_msk & msk)
> - seq_printf(s, " edge ");
> + seq_puts(s, " edge ");
> if (lvl_msk & msk)
> - seq_printf(s, " level");
> + seq_puts(s, " level");
> seq_printf(s, " (%s)\n", cause & msk ? "pending" : "clear ");
> }
> }
> --
> 2.10.1
>
--
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
^ permalink raw reply
* [PATCH v2] ARM: dts: exynos: Add entries for sound support on Odroid-XU board
From: Krzysztof Kozlowski @ 2016-10-17 15:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160922184036.GA10000@kozik-lap>
On Thu, Sep 22, 2016 at 08:40:36PM +0200, Krzysztof Kozlowski wrote:
> On Mon, Sep 19, 2016 at 10:47:46AM +0200, Sylwester Nawrocki wrote:
> > On 09/17/2016 10:00 PM, Krzysztof Kozlowski wrote:
> > > On Fri, Sep 16, 2016 at 01:58:32PM +0200, Krzysztof Kozlowski wrote:
> > >> > On 09/16/2016 01:25 PM, Sylwester Nawrocki wrote:
> > >>> > > On 09/16/2016 01:22 PM, Sylwester Nawrocki wrote:
> > >>>> > >> This patch adds device nodes for the AUDSS clock controller,
> > >>>> > >> peripheral DMA 0/1 controllers and the Audio Subsystem I2S controller.
> > >>>> > >> These entries are required for sound support on Odroid-XU board.
> > >>>> > >>
> > >>>> > >> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> > >>>> > >> ---
> > >>>> > >> This patch depends on a patch adding clock ID macro definitions.
> > >>>> > >> I'm going to provide a topic branch containing required changes.
> > >>>> > >>
> > >>>> > >> Changes since v1:
> > >>>> > >> - GIC_SPI, IRQ_TYPE_NONE used in the PDMA and max98080 interrupt
> > >>>> > >> specifiers,
> > >>>> > >> - assigned-clock-* properties moved to respective controller
> > >>>> > >> nodes.
> > >>> > >
> > >>> > > And here is a pull request containing clk dependency patches:
> > >>> > >
> > >>> > > The following changes since commit 29b4817d4018df78086157ea3a55c1d9424a7cfc:
> > >>> > >
> > >>> > > Linux 4.8-rc1 (2016-08-07 18:18:00 -0700)
> > >>> > >
> > >>> > > are available in the git repository at:
> > >>> > >
> > >>> > > git://linuxtv.org/snawrocki/samsung.git tags/clk-v4.9-exynos54x0-dt
> > >>> > >
> > >>> > > for you to fetch changes up to 58d6506f327e3d192998ba03632f546da221b8d8:
> > >>> > >
> > >>> > > clk: samsung: exynos5410: Add clock IDs for PDMA and EPLL clocks (2016-09-09
> > >>> > > 10:13:02 +0200)
> > >>> > >
> > >> >
> > >> > Pulled and applied, thanks!
> > >
> > > This does not boot...
> > > http://www.krzk.eu/builders/boot-odroid-xu-exynos/builds/216
> > > http://www.krzk.eu/builders/boot-odroid-xu-multi_v7/builds/195
> > >
> > > I am going to send pull request this weekend (or Monday) so probably this
> > > won't be included. If by any chance you prepare a fix soon, then it
> > > will save me from rebasing the branch.
> >
> > Oops, indeed, I didn't test with just that part of clk changes applied.
> > The only resolution here I can see is to pull my whole clk branch,
> > it has already been pulled to the upstream clk tree.
>
> Hmmmm, why the clock implementation is needed? Usually it should work
> without it. The driver will just handle ENODEV or DEFER... Maybe there
> is similar issue to the serial: e51e4d8a185d ("serial: samsung: Fix ERR
> pointer dereference on deferred probe")?
>
> Anyway, the patch will wait till next window opens. I hope it will work
> then. :)
>
I re-applied the patch. However I do not have Odroid XU board anymore
(Samsung R&D Institute Poland was kind enough to provide XU and U3
boards to me this year... but I had to give it back :( ) so I cannot
verify that it works... If it does not, then Sylwester you owe me
another bottle of good whisky!
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v20 00/10] Device Tree support for FPGA Programming
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
This patchset supports FPGA programming under the control
of Device Tree overlays.
Changes from v19:
* One line fix in FPGA Freeze Bridge DT binding doc (s/_/-/).
* Squashed patches that add FPGA image info struct so they
won't break bisect.
* Minor changes as suggested for the SoCFPGA Arria10 support.
* Added Rob's reviewed-by for the FPGA Regions binding doc.
* Added Moritz' ack for the sysfs document patch.
The following were acked so they are no longer in this patch set:
* bindings for Altera SOCFPGA bridges
https://patchwork.kernel.org/patch/9226093/
* bindings for Arria 10 FPGA Mgr
https://patchwork.kernel.org/patch/9226111/
* "[PATCH v3] of/overlay: add of overlay notifications"
https://lkml.org/lkml/2016/4/19/704
The patchset is dependent on:
* Pantelis Antonious's dtc changes for dynamic device tree.
https://github.com/pantoniou/dtc.git
* Pantelis' configfs interface patches and fixes
https://github.com/pantoniou/linux-beagle-track-mainline
Alan
Alan Tull (10):
fpga: add bindings document for fpga region
doc: fpga-mgr: add fpga image info to api
add bindings document for altera freeze bridge
add sysfs document for fpga bridge class
fpga-mgr: add fpga image information struct
fpga: add fpga bridge framework
fpga: fpga-region: device tree control for FPGA
ARM: socfpga: fpga bridge driver support
fpga: add altera freeze bridge support
fpga-manager: Add Socfpga Arria10 support
Documentation/ABI/testing/sysfs-class-fpga-bridge | 11 +
.../bindings/fpga/altera-freeze-bridge.txt | 23 +
.../devicetree/bindings/fpga/fpga-region.txt | 494 +++++++++++++++++
Documentation/fpga/fpga-mgr.txt | 32 +-
drivers/fpga/Kconfig | 36 ++
drivers/fpga/Makefile | 9 +
drivers/fpga/altera-fpga2sdram.c | 180 ++++++
drivers/fpga/altera-freeze-bridge.c | 273 ++++++++++
drivers/fpga/altera-hps2fpga.c | 221 ++++++++
drivers/fpga/fpga-bridge.c | 398 ++++++++++++++
drivers/fpga/fpga-mgr.c | 17 +-
drivers/fpga/fpga-region.c | 603 +++++++++++++++++++++
drivers/fpga/socfpga-a10.c | 556 +++++++++++++++++++
drivers/fpga/socfpga.c | 7 +-
drivers/fpga/zynq-fpga.c | 10 +-
include/linux/fpga/fpga-bridge.h | 60 ++
include/linux/fpga/fpga-mgr.h | 25 +-
17 files changed, 2925 insertions(+), 30 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-bridge
create mode 100644 Documentation/devicetree/bindings/fpga/altera-freeze-bridge.txt
create mode 100644 Documentation/devicetree/bindings/fpga/fpga-region.txt
create mode 100644 drivers/fpga/altera-fpga2sdram.c
create mode 100644 drivers/fpga/altera-freeze-bridge.c
create mode 100644 drivers/fpga/altera-hps2fpga.c
create mode 100644 drivers/fpga/fpga-bridge.c
create mode 100644 drivers/fpga/fpga-region.c
create mode 100644 drivers/fpga/socfpga-a10.c
create mode 100644 include/linux/fpga/fpga-bridge.h
--
2.10.1
^ permalink raw reply
* [PATCH v20 01/10] fpga: add bindings document for fpga region
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
New bindings document for FPGA Region to support programming
FPGA's under Device Tree control
Signed-off-by: Alan Tull <atull@opensource.altera.com>
Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
v9: initial version added to this patchset
v10: s/fpga/FPGA/g
replace DT overlay example with slightly more complicated example
move to staging/simple-fpga-bus
v11: No change in this patch for v11 of the patch set
v12: Moved out of staging.
Changed to use FPGA bridges framework instead of resets
for bridges.
v13: bridge at 0xff20000 -> bridge at ff200000, etc
Leave out directly talking about overlays
Remove regs and clocks directly under simple-fpga-bus in example
Use common "firmware-name" binding instead of "fpga-firmware"
v14: Use firmware-name in bindings description
Call it FPGA Area
Remove bindings that specify FPGA Manager and FPGA Bridges
v15: Cleanup as per Rob's comments
Combine usage doc with bindings document
Document as being Altera specific
Additions and changes to add FPGA Bus
v16: Reworked to document FPGA Regions
rename altera-fpga-bus-fpga-area.txt -> fpga-region.txt
Remove references that made it sound exclusive to Altera
Remove altr, prefix from fpga-bus and fpga-area compatible strings
Added Moritz' usage example with Xilinx
Cleaned up unit addresses
v17: Lots of rewrites to try to make things clearer
Clarify that overlay can be rejected if FPGA isn't programmed
Add external-fpga-config binding already used in u-boot
Change partial-reconfig binding to partial-fpga-config to align
with existing u-boot binding format *-fpga-config
Add a document from Xilinx' website
v18: Fix node names underscores to be hyphens
Fix copy/pasted duplicate nodes in diagram
v19: Fix more underscores
Make FPGA regions to be children of bridges
General cleanup and clarification
v20: Add Rob's reviewed-by
---
.../devicetree/bindings/fpga/fpga-region.txt | 494 +++++++++++++++++++++
1 file changed, 494 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fpga/fpga-region.txt
diff --git a/Documentation/devicetree/bindings/fpga/fpga-region.txt b/Documentation/devicetree/bindings/fpga/fpga-region.txt
new file mode 100644
index 0000000..3b32ba1
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/fpga-region.txt
@@ -0,0 +1,494 @@
+FPGA Region Device Tree Binding
+
+Alan Tull 2016
+
+ CONTENTS
+ - Introduction
+ - Terminology
+ - Sequence
+ - FPGA Region
+ - Supported Use Models
+ - Device Tree Examples
+ - Constraints
+
+
+Introduction
+============
+
+FPGA Regions represent FPGA's and partial reconfiguration regions of FPGA's in
+the Device Tree. FPGA Regions provide a way to program FPGAs under device tree
+control.
+
+This device tree binding document hits some of the high points of FPGA usage and
+attempts to include terminology used by both major FPGA manufacturers. This
+document isn't a replacement for any manufacturers specifications for FPGA
+usage.
+
+
+Terminology
+===========
+
+Full Reconfiguration
+ * The entire FPGA is programmed.
+
+Partial Reconfiguration (PR)
+ * A section of an FPGA is reprogrammed while the rest of the FPGA is not
+ affected.
+ * Not all FPGA's support PR.
+
+Partial Reconfiguration Region (PRR)
+ * Also called a "reconfigurable partition"
+ * A PRR is a specific section of a FPGA reserved for reconfiguration.
+ * A base (or static) FPGA image may create a set of PRR's that later may
+ be independently reprogrammed many times.
+ * The size and specific location of each PRR is fixed.
+ * The connections at the edge of each PRR are fixed. The image that is loaded
+ into a PRR must fit and must use a subset of the region's connections.
+ * The busses within the FPGA are split such that each region gets its own
+ branch that may be gated independently.
+
+Persona
+ * Also called a "partial bit stream"
+ * An FPGA image that is designed to be loaded into a PRR. There may be
+ any number of personas designed to fit into a PRR, but only one at at time
+ may be loaded.
+ * A persona may create more regions.
+
+FPGA Bridge
+ * FPGA Bridges gate bus signals between a host and FPGA.
+ * FPGA Bridges should be disabled while the FPGA is being programmed to
+ prevent spurious signals on the cpu bus and to the soft logic.
+ * FPGA bridges may be actual hardware or soft logic on an FPGA.
+ * During Full Reconfiguration, hardware bridges between the host and FPGA
+ will be disabled.
+ * During Partial Reconfiguration of a specific region, that region's bridge
+ will be used to gate the busses. Traffic to other regions is not affected.
+ * In some implementations, the FPGA Manager transparantly handles gating the
+ buses, eliminating the need to show the hardware FPGA bridges in the
+ device tree.
+ * An FPGA image may create a set of reprogrammable regions, each having its
+ own bridge and its own split of the busses in the FPGA.
+
+FPGA Manager
+ * An FPGA Manager is a hardware block that programs an FPGA under the control
+ of a host processor.
+
+Base Image
+ * Also called the "static image"
+ * An FPGA image that is designed to do full reconfiguration of the FPGA.
+ * A base image may set up a set of partial reconfiguration regions that may
+ later be reprogrammed.
+
+ ---------------- ----------------------------------
+ | Host CPU | | FPGA |
+ | | | |
+ | ----| | ----------- -------- |
+ | | H | | |==>| Bridge0 |<==>| PRR0 | |
+ | | W | | | ----------- -------- |
+ | | | | | |
+ | | B |<=====>|<==| ----------- -------- |
+ | | R | | |==>| Bridge1 |<==>| PRR1 | |
+ | | I | | | ----------- -------- |
+ | | D | | | |
+ | | G | | | ----------- -------- |
+ | | E | | |==>| Bridge2 |<==>| PRR2 | |
+ | ----| | ----------- -------- |
+ | | | |
+ ---------------- ----------------------------------
+
+Figure 1: An FPGA set up with a base image that created three regions. Each
+region (PRR0-2) gets its own split of the busses that is independently gated by
+a soft logic bridge (Bridge0-2) in the FPGA. The contents of each PRR can be
+reprogrammed independently while the rest of the system continues to function.
+
+
+Sequence
+========
+
+When a DT overlay that targets a FPGA Region is applied, the FPGA Region will
+do the following:
+
+ 1. Disable appropriate FPGA bridges.
+ 2. Program the FPGA using the FPGA manager.
+ 3. Enable the FPGA bridges.
+ 4. The Device Tree overlay is accepted into the live tree.
+ 5. Child devices are populated.
+
+When the overlay is removed, the child nodes will be removed and the FPGA Region
+will disable the bridges.
+
+
+FPGA Region
+===========
+
+FPGA Regions represent FPGA's and FPGA PR regions in the device tree. An FPGA
+Region brings together the elements needed to program on a running system and
+add the child devices:
+
+ * FPGA Manager
+ * FPGA Bridges
+ * image-specific information needed to to the programming.
+ * child nodes
+
+The intended use is that a Device Tree overlay (DTO) can be used to reprogram an
+FPGA while an operating system is running.
+
+An FPGA Region that exists in the live Device Tree reflects the current state.
+If the live tree shows a "firmware-name" property or child nodes under a FPGA
+Region, the FPGA already has been programmed. A DTO that targets a FPGA Region
+and adds the "firmware-name" property is taken as a request to reprogram the
+FPGA. After reprogramming is successful, the overlay is accepted into the live
+tree.
+
+The base FPGA Region in the device tree represents the FPGA and supports full
+reconfiguration. It must include a phandle to an FPGA Manager. The base
+FPGA region will be the child of one of the hardware bridges (the bridge that
+allows register access) between the cpu and the FPGA. If there are more than
+one bridge to control during FPGA programming, the region will also contain a
+list of phandles to the additional hardware FPGA Bridges.
+
+For partial reconfiguration (PR), each PR region will have an FPGA Region.
+These FPGA regions are children of FPGA bridges which are then children of the
+base FPGA region. The "Full Reconfiguration to add PRR's" example below shows
+this.
+
+If an FPGA Region does not specify a FPGA Manager, it will inherit the FPGA
+Manager specified by its ancestor FPGA Region. This supports both the case
+where the same FPGA Manager is used for all of a FPGA as well the case where
+a different FPGA Manager is used for each region.
+
+FPGA Regions do not inherit their ancestor FPGA regions' bridges. This prevents
+shutting down bridges that are upstream from the other active regions while one
+region is getting reconfigured (see Figure 1 above). During PR, the FPGA's
+hardware bridges remain enabled. The PR regions' bridges will be FPGA bridges
+within the static image of the FPGA.
+
+Required properties:
+- compatible : should contain "fpga-region"
+- fpga-mgr : should contain a phandle to an FPGA Manager. Child FPGA Regions
+ inherit this property from their ancestor regions. A fpga-mgr property
+ in a region will override any inherited FPGA manager.
+- #address-cells, #size-cells, ranges : must be present to handle address space
+ mapping for child nodes.
+
+Optional properties:
+- firmware-name : should contain the name of an FPGA image file located on the
+ firmware search path. If this property shows up in a live device tree
+ it indicates that the FPGA has already been programmed with this image.
+ If this property is in an overlay targeting a FPGA region, it is a
+ request to program the FPGA with that image.
+- fpga-bridges : should contain a list of phandles to FPGA Bridges that must be
+ controlled during FPGA programming along with the parent FPGA bridge.
+ This property is optional if the FPGA Manager handles the bridges.
+ If the fpga-region is the child of a fpga-bridge, the list should not
+ contain the parent bridge.
+- partial-fpga-config : boolean, set if partial reconfiguration is to be done,
+ otherwise full reconfiguration is done.
+- external-fpga-config : boolean, set if the FPGA has already been configured
+ prior to OS boot up.
+- region-unfreeze-timeout-us : The maximum time in microseconds to wait for
+ bridges to successfully become enabled after the region has been
+ programmed.
+- region-freeze-timeout-us : The maximum time in microseconds to wait for
+ bridges to successfully become disabled before the region has been
+ programmed.
+- child nodes : devices in the FPGA after programming.
+
+In the example below, when an overlay is applied targeting fpga-region0,
+fpga_mgr is used to program the FPGA. Two bridges are controlled during
+programming: the parent fpga_bridge0 and fpga_bridge1. Because the region is
+the child of fpga_bridge0, only fpga_bridge1 needs to be specified in the
+fpga-bridges property. During programming, these bridges are disabled, the
+firmware specified in the overlay is loaded to the FPGA using the FPGA manager
+specified in the region. If FPGA programming succeeds, the bridges are
+reenabled and the overlay makes it into the live device tree. The child devices
+are then populated. If FPGA programming fails, the bridges are left disabled
+and the overlay is rejected. The overlay's ranges property maps the lwhps
+bridge's region (0xff200000) and the hps bridge's region (0xc0000000) for use by
+the two child devices.
+
+Example:
+Base tree contains:
+
+ fpga_mgr: fpga-mgr at ff706000 {
+ compatible = "altr,socfpga-fpga-mgr";
+ reg = <0xff706000 0x1000
+ 0xffb90000 0x20>;
+ interrupts = <0 175 4>;
+ };
+
+ fpga_bridge0: fpga-bridge at ff400000 {
+ compatible = "altr,socfpga-lwhps2fpga-bridge";
+ reg = <0xff400000 0x100000>;
+ resets = <&rst LWHPS2FPGA_RESET>;
+ clocks = <&l4_main_clk>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ fpga_region0: fpga-region0 {
+ compatible = "fpga-region";
+ fpga-mgr = <&fpga_mgr>;
+ };
+ };
+
+ fpga_bridge1: fpga-bridge at ff500000 {
+ compatible = "altr,socfpga-hps2fpga-bridge";
+ reg = <0xff500000 0x10000>;
+ resets = <&rst HPS2FPGA_RESET>;
+ clocks = <&l4_main_clk>;
+ };
+
+Overlay contains:
+
+/dts-v1/ /plugin/;
+/ {
+ fragment at 0 {
+ target = <&fpga_region0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ firmware-name = "soc_system.rbf";
+ fpga-bridges = <&fpga_bridge1>;
+ ranges = <0x20000 0xff200000 0x100000>,
+ <0x0 0xc0000000 0x20000000>;
+
+ gpio at 10040 {
+ compatible = "altr,pio-1.0";
+ reg = <0x10040 0x20>;
+ altr,gpio-bank-width = <4>;
+ #gpio-cells = <2>;
+ clocks = <2>;
+ gpio-controller;
+ };
+
+ onchip-memory {
+ device_type = "memory";
+ compatible = "altr,onchipmem-15.1";
+ reg = <0x0 0x10000>;
+ };
+ };
+ };
+};
+
+
+Supported Use Models
+====================
+
+In all cases the live DT must have the FPGA Manager, FPGA Bridges (if any), and
+a FPGA Region. The target of the Device Tree Overlay is the FPGA Region. Some
+uses are specific to a FPGA device.
+
+ * No FPGA Bridges
+ In this case, the FPGA Manager which programs the FPGA also handles the
+ bridges behind the scenes. No FPGA Bridge devices are needed for full
+ reconfiguration.
+
+ * Full reconfiguration with hardware bridges
+ In this case, there are hardware bridges between the processor and FPGA that
+ need to be controlled during full reconfiguration. Before the overlay is
+ applied, the live DT must include the FPGA Manager, FPGA Bridges, and a
+ FPGA Region. The FPGA Region is the child of the bridge that allows
+ register access to the FPGA. Additional bridges may be listed in a
+ fpga-bridges property in the FPGA region or in the device tree overlay.
+
+ * Partial reconfiguration with bridges in the FPGA
+ In this case, the FPGA will have one or more PRR's that may be programmed
+ separately while the rest of the FPGA can remain active. To manage this,
+ bridges need to exist in the FPGA that can gate the buses going to each FPGA
+ region while the buses are enabled for other sections. Before any partial
+ reconfiguration can be done, a base FPGA image must be loaded which includes
+ PRR's with FPGA bridges. The device tree should have a FPGA region for each
+ PRR.
+
+Device Tree Examples
+====================
+
+The intention of this section is to give some simple examples, focusing on
+the placement of the elements detailed above, especially:
+ * FPGA Manager
+ * FPGA Bridges
+ * FPGA Region
+ * ranges
+ * target-path or target
+
+For the purposes of this section, I'm dividing the Device Tree into two parts,
+each with its own requirements. The two parts are:
+ * The live DT prior to the overlay being added
+ * The DT overlay
+
+The live Device Tree must contain an FPGA Region, an FPGA Manager, and any FPGA
+Bridges. The FPGA Region's "fpga-mgr" property specifies the manager by phandle
+to handle programming the FPGA. If the FPGA Region is the child of another FPGA
+Region, the parent's FPGA Manager is used. If FPGA Bridges need to be involved,
+they are specified in the FPGA Region by the "fpga-bridges" property. During
+FPGA programming, the FPGA Region will disable the bridges that are in its
+"fpga-bridges" list and will re-enable them after FPGA programming has
+succeeded.
+
+The Device Tree Overlay will contain:
+ * "target-path" or "target"
+ The insertion point where the the contents of the overlay will go into the
+ live tree. target-path is a full path, while target is a phandle.
+ * "ranges"
+ The address space mapping from processor to FPGA bus(ses).
+ * "firmware-name"
+ Specifies the name of the FPGA image file on the firmware search
+ path. The search path is described in the firmware class documentation.
+ * "partial-fpga-config"
+ This binding is a boolean and should be present if partial reconfiguration
+ is to be done.
+ * child nodes corresponding to hardware that will be loaded in this region of
+ the FPGA.
+
+Device Tree Example: Full Reconfiguration without Bridges
+=========================================================
+
+Live Device Tree contains:
+ fpga_mgr0: fpga-mgr at f8007000 {
+ compatible = "xlnx,zynq-devcfg-1.0";
+ reg = <0xf8007000 0x100>;
+ interrupt-parent = <&intc>;
+ interrupts = <0 8 4>;
+ clocks = <&clkc 12>;
+ clock-names = "ref_clk";
+ syscon = <&slcr>;
+ };
+
+ fpga_region0: fpga-region0 {
+ compatible = "fpga-region";
+ fpga-mgr = <&fpga_mgr0>;
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ ranges;
+ };
+
+DT Overlay contains:
+/dts-v1/ /plugin/;
+/ {
+fragment at 0 {
+ target = <&fpga_region0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ firmware-name = "zynq-gpio.bin";
+
+ gpio1: gpio at 40000000 {
+ compatible = "xlnx,xps-gpio-1.00.a";
+ reg = <0x40000000 0x10000>;
+ gpio-controller;
+ #gpio-cells = <0x2>;
+ xlnx,gpio-width= <0x6>;
+ };
+ };
+};
+
+Device Tree Example: Full Reconfiguration to add PRR's
+======================================================
+
+The base FPGA Region is specified similar to the first example above.
+
+This example programs the FPGA to have two regions that can later be partially
+configured. Each region has its own bridge in the FPGA fabric.
+
+DT Overlay contains:
+/dts-v1/ /plugin/;
+/ {
+ fragment at 0 {
+ target = <&fpga_region0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ firmware-name = "base.rbf";
+
+ fpga-bridge at 4400 {
+ compatible = "altr,freeze-bridge";
+ reg = <0x4400 0x10>;
+
+ fpga_region1: fpga-region1 {
+ compatible = "fpga-region";
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ ranges;
+ };
+ };
+
+ fpga-bridge at 4420 {
+ compatible = "altr,freeze-bridge";
+ reg = <0x4420 0x10>;
+
+ fpga_region2: fpga-region2 {
+ compatible = "fpga-region";
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ ranges;
+ };
+ };
+ };
+ };
+};
+
+Device Tree Example: Partial Reconfiguration
+============================================
+
+This example reprograms one of the PRR's set up in the previous example.
+
+The sequence that occurs when this overlay is similar to the above, the only
+differences are that the FPGA is partially reconfigured due to the
+"partial-fpga-config" boolean and the only bridge that is controlled during
+programming is the FPGA based bridge of fpga_region1.
+
+/dts-v1/ /plugin/;
+/ {
+ fragment at 0 {
+ target = <&fpga_region1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ firmware-name = "soc_image2.rbf";
+ partial-fpga-config;
+
+ gpio at 10040 {
+ compatible = "altr,pio-1.0";
+ reg = <0x10040 0x20>;
+ clocks = <0x2>;
+ altr,gpio-bank-width = <0x4>;
+ resetvalue = <0x0>;
+ #gpio-cells = <0x2>;
+ gpio-controller;
+ };
+ };
+ };
+};
+
+Constraints
+===========
+
+It is beyond the scope of this document to fully describe all the FPGA design
+constraints required to make partial reconfiguration work[1] [2] [3], but a few
+deserve quick mention.
+
+A persona must have boundary connections that line up with those of the partion
+or region it is designed to go into.
+
+During programming, transactions through those connections must be stopped and
+the connections must be held at a fixed logic level. This can be achieved by
+FPGA Bridges that exist on the FPGA fabric prior to the partial reconfiguration.
+
+--
+[1] www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/ug/ug_partrecon.pdf
+[2] tspace.library.utoronto.ca/bitstream/1807/67932/1/Byma_Stuart_A_201411_MAS_thesis.pdf
+[3] http://www.xilinx.com/support/documentation/sw_manuals/xilinx14_1/ug702.pdf
--
2.10.1
^ permalink raw reply related
* [PATCH v20 02/10] doc: fpga-mgr: add fpga image info to api
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
This patch adds a minor change in the FPGA Mangager API
to hold information that is specific to an FPGA image
file. This change is expected to bring little, if any,
pain.
An FPGA image file will have particulars that affect how the
image is programmed to the FPGA. One example is that
current 'flags' currently has one bit which shows whether the
FPGA image was built for full reconfiguration or partial
reconfiguration. Another example is timeout values for
enabling or disabling the bridges in the FPGA. As the
complexity of the FPGA design increases, the bridges in the
FPGA may take longer times to enable or disable.
This patch documents the change in the FPGA Manager API
functions, replacing the 'u32 flag' parameter with a pointer
to struct fpga_image_info.
Signed-off-by: Alan Tull <atull@opensource.altera.com>
---
v19: Added in v19 of this patchset
v20: No change for this patch in v20 of patchset
---
Documentation/fpga/fpga-mgr.txt | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/Documentation/fpga/fpga-mgr.txt b/Documentation/fpga/fpga-mgr.txt
index ce3e84f..9227e3f 100644
--- a/Documentation/fpga/fpga-mgr.txt
+++ b/Documentation/fpga/fpga-mgr.txt
@@ -18,21 +18,25 @@ API Functions:
To program the FPGA from a file or from a buffer:
-------------------------------------------------
- int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags,
+ int fpga_mgr_buf_load(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
const char *buf, size_t count);
Load the FPGA from an image which exists as a buffer in memory.
- int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags,
+ int fpga_mgr_firmware_load(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
const char *image_name);
Load the FPGA from an image which exists as a file. The image file must be on
-the firmware search path (see the firmware class documentation).
-
-For both these functions, flags == 0 for normal full reconfiguration or
-FPGA_MGR_PARTIAL_RECONFIG for partial reconfiguration. If successful, the FPGA
-ends up in operating mode. Return 0 on success or a negative error code.
+the firmware search path (see the firmware class documentation). If successful,
+the FPGA ends up in operating mode. Return 0 on success or a negative error
+code.
+A FPGA design contained in a FPGA image file will likely have particulars that
+affect how the image is programmed to the FPGA. These are contained in struct
+fpga_image_info. Currently the only such particular is a single flag bit
+indicating whether the image is for full or partial reconfiguration.
To get/put a reference to a FPGA manager:
-----------------------------------------
@@ -70,8 +74,11 @@ struct device_node *mgr_node = ...
char *buf = ...
int count = ...
+/* struct with information about the FPGA image to program. */
+struct fpga_image_info info;
+
/* flags indicates whether to do full or partial reconfiguration */
-int flags = 0;
+info.flags = 0;
int ret;
@@ -79,7 +86,7 @@ int ret;
struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node);
/* Load the buffer to the FPGA */
-ret = fpga_mgr_buf_load(mgr, flags, buf, count);
+ret = fpga_mgr_buf_load(mgr, &info, buf, count);
/* Release the FPGA manager */
fpga_mgr_put(mgr);
@@ -96,8 +103,11 @@ struct device_node *mgr_node = ...
/* FPGA image is in this file which is in the firmware search path */
const char *path = "fpga-image-9.rbf"
+/* struct with information about the FPGA image to program. */
+struct fpga_image_info info;
+
/* flags indicates whether to do full or partial reconfiguration */
-int flags = 0;
+info.flags = 0;
int ret;
@@ -105,7 +115,7 @@ int ret;
struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node);
/* Get the firmware image (path) and load it to the FPGA */
-ret = fpga_mgr_firmware_load(mgr, flags, path);
+ret = fpga_mgr_firmware_load(mgr, &info, path);
/* Release the FPGA manager */
fpga_mgr_put(mgr);
--
2.10.1
^ permalink raw reply related
* [PATCH v20 03/10] add bindings document for altera freeze bridge
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
Add bindings document for the Altera Freeze Bridge. A Freeze
Bridge is used to gate traffic to/from a region of a FPGA
such that that region can be reprogrammed. The Freeze Bridge
exist in FPGA fabric that is not currently being reconfigured.
Signed-off-by: Alan Tull <atull@opensource.altera.com>
Signed-off-by: Matthew Gerlach <mgerlach@opensource.altera.com>
---
v19: Added in v19 of patchset, uses fpga image info struct
v20: fix one underscore to hyphen
---
.../bindings/fpga/altera-freeze-bridge.txt | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fpga/altera-freeze-bridge.txt
diff --git a/Documentation/devicetree/bindings/fpga/altera-freeze-bridge.txt b/Documentation/devicetree/bindings/fpga/altera-freeze-bridge.txt
new file mode 100644
index 0000000..f8e288c7
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/altera-freeze-bridge.txt
@@ -0,0 +1,23 @@
+Altera Freeze Bridge Controller Driver
+
+The Altera Freeze Bridge Controller manages one or more freeze bridges.
+The controller can freeze/disable the bridges which prevents signal
+changes from passing through the bridge. The controller can also
+unfreeze/enable the bridges which allows traffic to pass through the
+bridge normally.
+
+Required properties:
+- compatible : Should contain "altr,freeze-bridge-controller"
+- regs : base address and size for freeze bridge module
+
+Optional properties:
+- bridge-enable : 0 if driver should disable bridge at startup
+ 1 if driver should enable bridge at startup
+ Default is to leave bridge in current state.
+
+Example:
+ freeze-controller at 100000450 {
+ compatible = "altr,freeze-bridge-controller";
+ regs = <0x1000 0x10>;
+ bridge-enable = <0>;
+ };
--
2.10.1
^ permalink raw reply related
* [PATCH v20 04/10] add sysfs document for fpga bridge class
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
Add documentation for new FPGA bridge class's sysfs interface.
Signed-off-by: Alan Tull <atull@opensource.altera.com>
Acked-by: Moritz Fischer <moritz.fischer@ettus.com>
--
v15: Document added in v15 of patch set
v16: No change to this patch in v16 of patch set
v17: No change to this patch in v17 of patch set
v18: No change to this patch in v18 of patch set
v19: No change to this patch in this version of patch set
v20: Added Moritz' ack
---
Documentation/ABI/testing/sysfs-class-fpga-bridge | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-bridge
diff --git a/Documentation/ABI/testing/sysfs-class-fpga-bridge b/Documentation/ABI/testing/sysfs-class-fpga-bridge
new file mode 100644
index 0000000..312ae2c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-fpga-bridge
@@ -0,0 +1,11 @@
+What: /sys/class/fpga_bridge/<bridge>/name
+Date: January 2016
+KernelVersion: 4.5
+Contact: Alan Tull <atull@opensource.altera.com>
+Description: Name of low level FPGA bridge driver.
+
+What: /sys/class/fpga_bridge/<bridge>/state
+Date: January 2016
+KernelVersion: 4.5
+Contact: Alan Tull <atull@opensource.altera.com>
+Description: Show bridge state as "enabled" or "disabled"
--
2.10.1
^ permalink raw reply related
* [PATCH v20 05/10] fpga-mgr: add fpga image information struct
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
This patch adds a minor change in the FPGA Mangager API
to hold information that is specific to an FPGA image
file. This change is expected to bring little, if any,
pain. The socfpga and zynq drivers are fixed up in
this patch.
An FPGA image file will have particulars that affect how the
image is programmed to the FPGA. One example is that
current 'flags' currently has one bit which shows whether the
FPGA image was built for full reconfiguration or partial
reconfiguration. Another example is timeout values for
enabling or disabling the bridges in the FPGA. As the
complexity of the FPGA design increases, the bridges in the
FPGA may take longer times to enable or disable.
This patch adds a new 'struct fpga_image_info', moves the
current 'u32 flags' to it. Two other image-specific u32's
are added for the bridge enable/disable timeouts. The FPGA
Manager API functions are changed, replacing the 'u32 flag'
parameter with a pointer to struct fpga_image_info.
Subsequent patches fix the existing low level FPGA manager
drivers.
Signed-off-by: Alan Tull <atull@opensource.altera.com>
---
v19: Added in v19 of this patchset
v20: Squashed patches that change API for socfpga and zynq
---
drivers/fpga/fpga-mgr.c | 17 +++++++++--------
drivers/fpga/socfpga.c | 7 ++++---
drivers/fpga/zynq-fpga.c | 10 ++++++----
include/linux/fpga/fpga-mgr.h | 23 +++++++++++++++++++----
4 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index 953dc91..c58b4c4 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -32,7 +32,7 @@ static struct class *fpga_mgr_class;
/**
* fpga_mgr_buf_load - load fpga from image in buffer
* @mgr: fpga manager
- * @flags: flags setting fpga confuration modes
+ * @info: fpga image specific information
* @buf: buffer contain fpga image
* @count: byte count of buf
*
@@ -43,8 +43,8 @@ static struct class *fpga_mgr_class;
*
* Return: 0 on success, negative error code otherwise.
*/
-int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf,
- size_t count)
+int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info,
+ const char *buf, size_t count)
{
struct device *dev = &mgr->dev;
int ret;
@@ -55,7 +55,7 @@ int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf,
* ready to receive an FPGA image.
*/
mgr->state = FPGA_MGR_STATE_WRITE_INIT;
- ret = mgr->mops->write_init(mgr, flags, buf, count);
+ ret = mgr->mops->write_init(mgr, info, buf, count);
if (ret) {
dev_err(dev, "Error preparing FPGA for writing\n");
mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
@@ -78,7 +78,7 @@ int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags, const char *buf,
* steps to finish and set the FPGA into operating mode.
*/
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
- ret = mgr->mops->write_complete(mgr, flags);
+ ret = mgr->mops->write_complete(mgr, info);
if (ret) {
dev_err(dev, "Error after writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
@@ -93,7 +93,7 @@ EXPORT_SYMBOL_GPL(fpga_mgr_buf_load);
/**
* fpga_mgr_firmware_load - request firmware and load to fpga
* @mgr: fpga manager
- * @flags: flags setting fpga confuration modes
+ * @info: fpga image specific information
* @image_name: name of image file on the firmware search path
*
* Request an FPGA image using the firmware class, then write out to the FPGA.
@@ -103,7 +103,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_buf_load);
*
* Return: 0 on success, negative error code otherwise.
*/
-int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags,
+int fpga_mgr_firmware_load(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
const char *image_name)
{
struct device *dev = &mgr->dev;
@@ -121,7 +122,7 @@ int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags,
return ret;
}
- ret = fpga_mgr_buf_load(mgr, flags, fw->data, fw->size);
+ ret = fpga_mgr_buf_load(mgr, info, fw->data, fw->size);
release_firmware(fw);
diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c
index 27d2ff2..b6672e6 100644
--- a/drivers/fpga/socfpga.c
+++ b/drivers/fpga/socfpga.c
@@ -407,13 +407,14 @@ static int socfpga_fpga_reset(struct fpga_manager *mgr)
/*
* Prepare the FPGA to receive the configuration data.
*/
-static int socfpga_fpga_ops_configure_init(struct fpga_manager *mgr, u32 flags,
+static int socfpga_fpga_ops_configure_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
const char *buf, size_t count)
{
struct socfpga_fpga_priv *priv = mgr->priv;
int ret;
- if (flags & FPGA_MGR_PARTIAL_RECONFIG) {
+ if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
return -EINVAL;
}
@@ -478,7 +479,7 @@ static int socfpga_fpga_ops_configure_write(struct fpga_manager *mgr,
}
static int socfpga_fpga_ops_configure_complete(struct fpga_manager *mgr,
- u32 flags)
+ struct fpga_image_info *info)
{
struct socfpga_fpga_priv *priv = mgr->priv;
u32 status;
diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c
index c2fb412..249682e 100644
--- a/drivers/fpga/zynq-fpga.c
+++ b/drivers/fpga/zynq-fpga.c
@@ -175,7 +175,8 @@ static irqreturn_t zynq_fpga_isr(int irq, void *data)
return IRQ_HANDLED;
}
-static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
+static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
const char *buf, size_t count)
{
struct zynq_fpga_priv *priv;
@@ -189,7 +190,7 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
return err;
/* don't globally reset PL if we're doing partial reconfig */
- if (!(flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+ if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
/* assert AXI interface resets */
regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET,
FPGA_RST_ALL_MASK);
@@ -343,7 +344,8 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr,
return err;
}
-static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags)
+static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
{
struct zynq_fpga_priv *priv = mgr->priv;
int err;
@@ -364,7 +366,7 @@ static int zynq_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags)
return err;
/* for the partial reconfig case we didn't touch the level shifters */
- if (!(flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+ if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
/* enable level shifters from PL to PS */
regmap_write(priv->slcr, SLCR_LVL_SHFTR_EN_OFFSET,
LVL_SHFTR_ENABLE_PL_TO_PS);
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 0940bf4..040b86d 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -69,6 +69,18 @@ enum fpga_mgr_states {
#define FPGA_MGR_PARTIAL_RECONFIG BIT(0)
/**
+ * struct fpga_image_info - information specific to a FPGA image
+ * @flags: boolean flags as defined above
+ * @enable_timeout_us: maximum time to enable traffic through bridge (uSec)
+ * @disable_timeout_us: maximum time to disable traffic through bridge (uSec)
+ */
+struct fpga_image_info {
+ u32 flags;
+ u32 enable_timeout_us;
+ u32 disable_timeout_us;
+};
+
+/**
* struct fpga_manager_ops - ops for low level fpga manager drivers
* @state: returns an enum value of the FPGA's state
* @write_init: prepare the FPGA to receive confuration data
@@ -82,10 +94,12 @@ enum fpga_mgr_states {
*/
struct fpga_manager_ops {
enum fpga_mgr_states (*state)(struct fpga_manager *mgr);
- int (*write_init)(struct fpga_manager *mgr, u32 flags,
+ int (*write_init)(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
const char *buf, size_t count);
int (*write)(struct fpga_manager *mgr, const char *buf, size_t count);
- int (*write_complete)(struct fpga_manager *mgr, u32 flags);
+ int (*write_complete)(struct fpga_manager *mgr,
+ struct fpga_image_info *info);
void (*fpga_remove)(struct fpga_manager *mgr);
};
@@ -109,10 +123,11 @@ struct fpga_manager {
#define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
-int fpga_mgr_buf_load(struct fpga_manager *mgr, u32 flags,
+int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info,
const char *buf, size_t count);
-int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags,
+int fpga_mgr_firmware_load(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
const char *image_name);
struct fpga_manager *of_fpga_mgr_get(struct device_node *node);
--
2.10.1
^ permalink raw reply related
* [PATCH v20 06/10] fpga: add fpga bridge framework
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
This framework adds API functions for enabling/
disabling FPGA bridges under kernel control.
This allows the Linux kernel to disable FPGA bridges
during FPGA reprogramming and to enable FPGA bridges
when FPGA reprogramming is done. This framework is
be manufacturer-agnostic, allowing it to be used in
interfaces that use the FPGA Manager Framework to
reprogram FPGA's.
The functions are:
* of_fpga_bridge_get
* fpga_bridge_put
Get/put an exclusive reference to a FPGA bridge.
* fpga_bridge_enable
* fpga_bridge_disable
Enable/Disable traffic through a bridge.
* fpga_bridge_register
* fpga_bridge_unregister
Register/unregister a device-specific low level FPGA
Bridge driver.
Get an exclusive reference to a bridge and add it to a list:
* fpga_bridge_get_to_list
To enable/disable/put a set of bridges that are on a list:
* fpga_bridges_enable
* fpga_bridges_disable
* fpga_bridges_put
Signed-off-by: Alan Tull <atull@opensource.altera.com>
---
v2: Minor cleanup
v12: Bump version to line up with simple fpga bus
Remove sysfs
Improve get/put functions, get the low level driver too.
Clean up class implementation
Add kernel doc documentation
Rename (un)register_fpga_bridge -> fpga_bridge_(un)register
v13: Add inlined empty functions for if not CONFIG_FPGA_BRIDGE
Clean up debugging
Remove unneeded #include in .h
Remove unnecessary prints
Remove 'label' DT binding.
Document the mutex
v14: Allow bridges with no ops
*const* struct fpga_bridge_ops
Add functions to git/put/enable/disable list of bridges
Add list node to struct fpga_bridge
Do of_node_get/put in of_fpga_bridge_get()
Add r/o attributes: name and state
v15: No change in this patch for v15 of this patch set
v16: Remove of_get_fpga_bus function
v17: No change to this patch in v17 of patch set
v18: No change to this patch in v18 of patch set
v19: Use fpga image info struct
Support fpga image specific timeouts
Support child fpga-regions by doing of_platform_populate
make a tristate in Kconfig
v20: No change for this patch in v20 of patchset
---
drivers/fpga/Kconfig | 7 +
drivers/fpga/Makefile | 3 +
drivers/fpga/fpga-bridge.c | 398 +++++++++++++++++++++++++++++++++++++++
include/linux/fpga/fpga-bridge.h | 60 ++++++
4 files changed, 468 insertions(+)
create mode 100644 drivers/fpga/fpga-bridge.c
create mode 100644 include/linux/fpga/fpga-bridge.h
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index cd84934..9b20f45 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -26,6 +26,13 @@ config FPGA_MGR_ZYNQ_FPGA
help
FPGA manager driver support for Xilinx Zynq FPGAs.
+config FPGA_BRIDGE
+ tristate "FPGA Bridge Framework"
+ depends on OF
+ help
+ Say Y here if you want to support bridges connected between host
+ processors and FPGAs or between FPGAs.
+
endif # FPGA
endmenu
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 8d83fc6..4baef00 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -8,3 +8,6 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o
# FPGA Manager Drivers
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
+
+# FPGA Bridge Drivers
+obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o
diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c
new file mode 100644
index 0000000..5a733c8
--- /dev/null
+++ b/drivers/fpga/fpga-bridge.c
@@ -0,0 +1,398 @@
+/*
+ * FPGA Bridge Framework Driver
+ *
+ * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/fpga/fpga-bridge.h>
+#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+static DEFINE_IDA(fpga_bridge_ida);
+static struct class *fpga_bridge_class;
+
+/* Lock for adding/removing bridges to linked lists*/
+spinlock_t bridge_list_lock;
+
+static int fpga_bridge_of_node_match(struct device *dev, const void *data)
+{
+ return dev->of_node == data;
+}
+
+/**
+ * fpga_bridge_enable - Enable transactions on the bridge
+ *
+ * @bridge: FPGA bridge
+ *
+ * Return: 0 for success, error code otherwise.
+ */
+int fpga_bridge_enable(struct fpga_bridge *bridge)
+{
+ dev_dbg(&bridge->dev, "enable\n");
+
+ if (bridge->br_ops && bridge->br_ops->enable_set)
+ return bridge->br_ops->enable_set(bridge, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_enable);
+
+/**
+ * fpga_bridge_disable - Disable transactions on the bridge
+ *
+ * @bridge: FPGA bridge
+ *
+ * Return: 0 for success, error code otherwise.
+ */
+int fpga_bridge_disable(struct fpga_bridge *bridge)
+{
+ dev_dbg(&bridge->dev, "disable\n");
+
+ if (bridge->br_ops && bridge->br_ops->enable_set)
+ return bridge->br_ops->enable_set(bridge, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_disable);
+
+/**
+ * of_fpga_bridge_get - get an exclusive reference to a fpga bridge
+ *
+ * @np: node pointer of a FPGA bridge
+ * @info: fpga image specific information
+ *
+ * Return fpga_bridge struct if successful.
+ * Return -EBUSY if someone already has a reference to the bridge.
+ * Return -ENODEV if @np is not a FPGA Bridge.
+ */
+struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
+ struct fpga_image_info *info)
+
+{
+ struct device *dev;
+ struct fpga_bridge *bridge;
+ int ret = -ENODEV;
+
+ of_node_get(np);
+
+ dev = class_find_device(fpga_bridge_class, NULL, np,
+ fpga_bridge_of_node_match);
+ if (!dev)
+ goto err_dev;
+
+ bridge = to_fpga_bridge(dev);
+ if (!bridge)
+ goto err_dev;
+
+ bridge->info = info;
+
+ if (!mutex_trylock(&bridge->mutex)) {
+ ret = -EBUSY;
+ goto err_dev;
+ }
+
+ if (!try_module_get(dev->parent->driver->owner))
+ goto err_ll_mod;
+
+ dev_dbg(&bridge->dev, "get\n");
+
+ return bridge;
+
+err_ll_mod:
+ mutex_unlock(&bridge->mutex);
+err_dev:
+ put_device(dev);
+ of_node_put(np);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(of_fpga_bridge_get);
+
+/**
+ * fpga_bridge_put - release a reference to a bridge
+ *
+ * @bridge: FPGA bridge
+ */
+void fpga_bridge_put(struct fpga_bridge *bridge)
+{
+ dev_dbg(&bridge->dev, "put\n");
+
+ bridge->info = NULL;
+ module_put(bridge->dev.parent->driver->owner);
+ mutex_unlock(&bridge->mutex);
+ put_device(&bridge->dev);
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_put);
+
+/**
+ * fpga_bridges_enable - enable bridges in a list
+ * @bridge_list: list of FPGA bridges
+ *
+ * Enable each bridge in the list. If list is empty, do nothing.
+ *
+ * Return 0 for success or empty bridge list; return error code otherwise.
+ */
+int fpga_bridges_enable(struct list_head *bridge_list)
+{
+ struct fpga_bridge *bridge;
+ struct list_head *node;
+ int ret;
+
+ list_for_each(node, bridge_list) {
+ bridge = list_entry(node, struct fpga_bridge, node);
+ ret = fpga_bridge_enable(bridge);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridges_enable);
+
+/**
+ * fpga_bridges_disable - disable bridges in a list
+ *
+ * @bridge_list: list of FPGA bridges
+ *
+ * Disable each bridge in the list. If list is empty, do nothing.
+ *
+ * Return 0 for success or empty bridge list; return error code otherwise.
+ */
+int fpga_bridges_disable(struct list_head *bridge_list)
+{
+ struct fpga_bridge *bridge;
+ struct list_head *node;
+ int ret;
+
+ list_for_each(node, bridge_list) {
+ bridge = list_entry(node, struct fpga_bridge, node);
+ ret = fpga_bridge_disable(bridge);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridges_disable);
+
+/**
+ * fpga_bridges_put - put bridges
+ *
+ * @bridge_list: list of FPGA bridges
+ *
+ * For each bridge in the list, put the bridge and remove it from the list.
+ * If list is empty, do nothing.
+ */
+void fpga_bridges_put(struct list_head *bridge_list)
+{
+ struct fpga_bridge *bridge;
+ struct list_head *node, *next;
+ unsigned long flags;
+
+ list_for_each_safe(node, next, bridge_list) {
+ bridge = list_entry(node, struct fpga_bridge, node);
+
+ fpga_bridge_put(bridge);
+
+ spin_lock_irqsave(&bridge_list_lock, flags);
+ list_del(&bridge->node);
+ spin_unlock_irqrestore(&bridge_list_lock, flags);
+ }
+}
+EXPORT_SYMBOL_GPL(fpga_bridges_put);
+
+/**
+ * fpga_bridges_get_to_list - get a bridge, add it to a list
+ *
+ * @np: node pointer of a FPGA bridge
+ * @info: fpga image specific information
+ * @bridge_list: list of FPGA bridges
+ *
+ * Get an exclusive reference to the bridge and and it to the list.
+ *
+ * Return 0 for success, error code from of_fpga_bridge_get() othewise.
+ */
+int fpga_bridge_get_to_list(struct device_node *np,
+ struct fpga_image_info *info,
+ struct list_head *bridge_list)
+{
+ struct fpga_bridge *bridge;
+ unsigned long flags;
+
+ bridge = of_fpga_bridge_get(np, info);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+
+ spin_lock_irqsave(&bridge_list_lock, flags);
+ list_add(&bridge->node, bridge_list);
+ spin_unlock_irqrestore(&bridge_list_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_get_to_list);
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_bridge *bridge = to_fpga_bridge(dev);
+
+ return sprintf(buf, "%s\n", bridge->name);
+}
+
+static ssize_t state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_bridge *bridge = to_fpga_bridge(dev);
+ int enable = 1;
+
+ if (bridge->br_ops && bridge->br_ops->enable_show)
+ enable = bridge->br_ops->enable_show(bridge);
+
+ return sprintf(buf, "%s\n", enable ? "enabled" : "disabled");
+}
+
+static DEVICE_ATTR_RO(name);
+static DEVICE_ATTR_RO(state);
+
+static struct attribute *fpga_bridge_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_state.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(fpga_bridge);
+
+/**
+ * fpga_bridge_register - register a fpga bridge driver
+ * @dev: FPGA bridge device from pdev
+ * @name: FPGA bridge name
+ * @br_ops: pointer to structure of fpga bridge ops
+ * @priv: FPGA bridge private data
+ *
+ * Return: 0 for success, error code otherwise.
+ */
+int fpga_bridge_register(struct device *dev, const char *name,
+ const struct fpga_bridge_ops *br_ops, void *priv)
+{
+ struct fpga_bridge *bridge;
+ int id, ret = 0;
+
+ if (!name || !strlen(name)) {
+ dev_err(dev, "Attempt to register with no name!\n");
+ return -EINVAL;
+ }
+
+ bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return -ENOMEM;
+
+ id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ ret = id;
+ goto error_kfree;
+ }
+
+ mutex_init(&bridge->mutex);
+ INIT_LIST_HEAD(&bridge->node);
+
+ bridge->name = name;
+ bridge->br_ops = br_ops;
+ bridge->priv = priv;
+
+ device_initialize(&bridge->dev);
+ bridge->dev.class = fpga_bridge_class;
+ bridge->dev.parent = dev;
+ bridge->dev.of_node = dev->of_node;
+ bridge->dev.id = id;
+ dev_set_drvdata(dev, bridge);
+
+ ret = dev_set_name(&bridge->dev, "br%d", id);
+ if (ret)
+ goto error_device;
+
+ ret = device_add(&bridge->dev);
+ if (ret)
+ goto error_device;
+
+ of_platform_populate(dev->of_node, NULL, NULL, dev);
+
+ dev_info(bridge->dev.parent, "fpga bridge [%s] registered\n",
+ bridge->name);
+
+ return 0;
+
+error_device:
+ ida_simple_remove(&fpga_bridge_ida, id);
+error_kfree:
+ kfree(bridge);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_register);
+
+/**
+ * fpga_bridge_unregister - unregister a fpga bridge driver
+ * @dev: FPGA bridge device from pdev
+ */
+void fpga_bridge_unregister(struct device *dev)
+{
+ struct fpga_bridge *bridge = dev_get_drvdata(dev);
+
+ /*
+ * If the low level driver provides a method for putting bridge into
+ * a desired state upon unregister, do it.
+ */
+ if (bridge->br_ops && bridge->br_ops->fpga_bridge_remove)
+ bridge->br_ops->fpga_bridge_remove(bridge);
+
+ device_unregister(&bridge->dev);
+}
+EXPORT_SYMBOL_GPL(fpga_bridge_unregister);
+
+static void fpga_bridge_dev_release(struct device *dev)
+{
+ struct fpga_bridge *bridge = to_fpga_bridge(dev);
+
+ ida_simple_remove(&fpga_bridge_ida, bridge->dev.id);
+ kfree(bridge);
+}
+
+static int __init fpga_bridge_dev_init(void)
+{
+ spin_lock_init(&bridge_list_lock);
+
+ fpga_bridge_class = class_create(THIS_MODULE, "fpga_bridge");
+ if (IS_ERR(fpga_bridge_class))
+ return PTR_ERR(fpga_bridge_class);
+
+ fpga_bridge_class->dev_groups = fpga_bridge_groups;
+ fpga_bridge_class->dev_release = fpga_bridge_dev_release;
+
+ return 0;
+}
+
+static void __exit fpga_bridge_dev_exit(void)
+{
+ class_destroy(fpga_bridge_class);
+ ida_destroy(&fpga_bridge_ida);
+}
+
+MODULE_DESCRIPTION("FPGA Bridge Driver");
+MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
+MODULE_LICENSE("GPL v2");
+
+subsys_initcall(fpga_bridge_dev_init);
+module_exit(fpga_bridge_dev_exit);
diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h
new file mode 100644
index 0000000..dba6e3c
--- /dev/null
+++ b/include/linux/fpga/fpga-bridge.h
@@ -0,0 +1,60 @@
+#include <linux/device.h>
+#include <linux/fpga/fpga-mgr.h>
+
+#ifndef _LINUX_FPGA_BRIDGE_H
+#define _LINUX_FPGA_BRIDGE_H
+
+struct fpga_bridge;
+
+/**
+ * struct fpga_bridge_ops - ops for low level FPGA bridge drivers
+ * @enable_show: returns the FPGA bridge's status
+ * @enable_set: set a FPGA bridge as enabled or disabled
+ * @fpga_bridge_remove: set FPGA into a specific state during driver remove
+ */
+struct fpga_bridge_ops {
+ int (*enable_show)(struct fpga_bridge *bridge);
+ int (*enable_set)(struct fpga_bridge *bridge, bool enable);
+ void (*fpga_bridge_remove)(struct fpga_bridge *bridge);
+};
+
+/**
+ * struct fpga_bridge - FPGA bridge structure
+ * @name: name of low level FPGA bridge
+ * @dev: FPGA bridge device
+ * @mutex: enforces exclusive reference to bridge
+ * @br_ops: pointer to struct of FPGA bridge ops
+ * @info: fpga image specific information
+ * @node: FPGA bridge list node
+ * @priv: low level driver private date
+ */
+struct fpga_bridge {
+ const char *name;
+ struct device dev;
+ struct mutex mutex; /* for exclusive reference to bridge */
+ const struct fpga_bridge_ops *br_ops;
+ struct fpga_image_info *info;
+ struct list_head node;
+ void *priv;
+};
+
+#define to_fpga_bridge(d) container_of(d, struct fpga_bridge, dev)
+
+struct fpga_bridge *of_fpga_bridge_get(struct device_node *node,
+ struct fpga_image_info *info);
+void fpga_bridge_put(struct fpga_bridge *bridge);
+int fpga_bridge_enable(struct fpga_bridge *bridge);
+int fpga_bridge_disable(struct fpga_bridge *bridge);
+
+int fpga_bridges_enable(struct list_head *bridge_list);
+int fpga_bridges_disable(struct list_head *bridge_list);
+void fpga_bridges_put(struct list_head *bridge_list);
+int fpga_bridge_get_to_list(struct device_node *np,
+ struct fpga_image_info *info,
+ struct list_head *bridge_list);
+
+int fpga_bridge_register(struct device *dev, const char *name,
+ const struct fpga_bridge_ops *br_ops, void *priv);
+void fpga_bridge_unregister(struct device *dev);
+
+#endif /* _LINUX_FPGA_BRIDGE_H */
--
2.10.1
^ permalink raw reply related
* [PATCH v20 07/10] fpga: fpga-region: device tree control for FPGA
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
FPGA Regions support programming FPGA under control of the Device
Tree.
Signed-off-by: Alan Tull <atull@opensource.altera.com>
---
v9: initial version (this patch added during rest of patchset's v9)
v10: request deferral if fpga mgr or bridges not available yet
cleanup as fpga manager core goes into the real kernel
Don't assume bridges are disabled before programming FPGA
Don't hang onto reference for fpga manager
Move to staging/simple-fpga-bus
v11: No change in this patch for v11 of the patch set
v12: Moved out of staging.
Use fpga bridges framework.
v13: If no bridges are specified, assume we don't need any.
Clean up debug messages
Some dev_info -> dev_dbg
Remove unneeded #include
Fix size of array of pointers
Don't need to specify .owner
Use common binding: firmware-name
v14: OK it's not a simple bus. Call it "FPGA Area"
Remove bindings that specify FPGA manager and FPGA bridges
Use parent FPGA bridge and bridges that are its peers
Use ancestor FPGA Manager
v15: Add altr,fpga-bus implementation
Change compatible string "fpga-area" -> "altr,fpga-area"
v16: Much changes as FPGA Areas and Busses become FPGA Regions
Add reconfig notifier, don't rely on simple-bus
v17: Use new overlay notifier instead of reconfig notifier
Add external_fpga_config property used in u-boot
Change partial-reconfig binding to partial-fpga-config to be
similar to *-fpga-config bindings used in u-boot
v18: No change to this patch in v18 of patch set
v19: add fpga image information struct
support region as child of fpga bridge
remove message about not finding region
Make a tristate in the Kconfig
Add dependency on bridges in Kconfig
v20: No change for this patch in v20 of patchset
---
drivers/fpga/Kconfig | 7 +
drivers/fpga/Makefile | 3 +
drivers/fpga/fpga-region.c | 603 ++++++++++++++++++++++++++++++++++++++++++
include/linux/fpga/fpga-mgr.h | 2 +
4 files changed, 615 insertions(+)
create mode 100644 drivers/fpga/fpga-region.c
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 9b20f45..e0e1257 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -13,6 +13,13 @@ config FPGA
if FPGA
+config FPGA_REGION
+ tristate "FPGA Region"
+ depends on OF && FPGA_BRIDGE
+ help
+ FPGA Regions allow loading FPGA images under control of
+ the Device Tree.
+
config FPGA_MGR_SOCFPGA
tristate "Altera SOCFPGA FPGA Manager"
depends on ARCH_SOCFPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 4baef00..8d746c3 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -11,3 +11,6 @@ obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
# FPGA Bridge Drivers
obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o
+
+# High Level Interfaces
+obj-$(CONFIG_FPGA_REGION) += fpga-region.o
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
new file mode 100644
index 0000000..3222fdb
--- /dev/null
+++ b/drivers/fpga/fpga-region.c
@@ -0,0 +1,603 @@
+/*
+ * FPGA Region - Device Tree support for FPGA programming under Linux
+ *
+ * Copyright (C) 2013-2016 Altera Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fpga/fpga-bridge.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/**
+ * struct fpga_region - FPGA Region structure
+ * @dev: FPGA Region device
+ * @mutex: enforces exclusive reference to region
+ * @bridge_list: list of FPGA bridges specified in region
+ * @info: fpga image specific information
+ */
+struct fpga_region {
+ struct device dev;
+ struct mutex mutex; /* for exclusive reference to region */
+ struct list_head bridge_list;
+ struct fpga_image_info *info;
+};
+
+#define to_fpga_region(d) container_of(d, struct fpga_region, dev)
+
+static DEFINE_IDA(fpga_region_ida);
+static struct class *fpga_region_class;
+
+static const struct of_device_id fpga_region_of_match[] = {
+ { .compatible = "fpga-region", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fpga_region_of_match);
+
+static int fpga_region_of_node_match(struct device *dev, const void *data)
+{
+ return dev->of_node == data;
+}
+
+/**
+ * fpga_region_find - find FPGA region
+ * @np: device node of FPGA Region
+ * Caller will need to put_device(®ion->dev) when done.
+ * Returns FPGA Region struct or NULL
+ */
+static struct fpga_region *fpga_region_find(struct device_node *np)
+{
+ struct device *dev;
+
+ dev = class_find_device(fpga_region_class, NULL, np,
+ fpga_region_of_node_match);
+ if (!dev)
+ return NULL;
+
+ return to_fpga_region(dev);
+}
+
+/**
+ * fpga_region_get - get an exclusive reference to a fpga region
+ * @region: FPGA Region struct
+ *
+ * Caller should call fpga_region_put() when done with region.
+ *
+ * Return fpga_region struct if successful.
+ * Return -EBUSY if someone already has a reference to the region.
+ * Return -ENODEV if @np is not a FPGA Region.
+ */
+static struct fpga_region *fpga_region_get(struct fpga_region *region)
+{
+ struct device *dev = ®ion->dev;
+
+ if (!mutex_trylock(®ion->mutex)) {
+ dev_dbg(dev, "%s: FPGA Region already in use\n", __func__);
+ return ERR_PTR(-EBUSY);
+ }
+
+ get_device(dev);
+ of_node_get(dev->of_node);
+ if (!try_module_get(dev->parent->driver->owner)) {
+ of_node_put(dev->of_node);
+ put_device(dev);
+ mutex_unlock(®ion->mutex);
+ return ERR_PTR(-ENODEV);
+ }
+
+ dev_dbg(®ion->dev, "get\n");
+
+ return region;
+}
+
+/**
+ * fpga_region_put - release a reference to a region
+ *
+ * @region: FPGA region
+ */
+static void fpga_region_put(struct fpga_region *region)
+{
+ struct device *dev = ®ion->dev;
+
+ dev_dbg(®ion->dev, "put\n");
+
+ module_put(dev->parent->driver->owner);
+ of_node_put(dev->of_node);
+ put_device(dev);
+ mutex_unlock(®ion->mutex);
+}
+
+/**
+ * fpga_region_get_manager - get exclusive reference for FPGA manager
+ * @region: FPGA region
+ *
+ * Get FPGA Manager from "fpga-mgr" property or from ancestor region.
+ *
+ * Caller should call fpga_mgr_put() when done with manager.
+ *
+ * Return: fpga manager struct or IS_ERR() condition containing error code.
+ */
+static struct fpga_manager *fpga_region_get_manager(struct fpga_region *region)
+{
+ struct device *dev = ®ion->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *mgr_node;
+ struct fpga_manager *mgr;
+
+ of_node_get(np);
+ while (np) {
+ if (of_device_is_compatible(np, "fpga-region")) {
+ mgr_node = of_parse_phandle(np, "fpga-mgr", 0);
+ if (mgr_node) {
+ mgr = of_fpga_mgr_get(mgr_node);
+ of_node_put(np);
+ return mgr;
+ }
+ }
+ np = of_get_next_parent(np);
+ }
+ of_node_put(np);
+
+ return ERR_PTR(-EINVAL);
+}
+
+/**
+ * fpga_region_get_bridges - create a list of bridges
+ * @region: FPGA region
+ * @overlay: device node of the overlay
+ *
+ * Create a list of bridges including the parent bridge and the bridges
+ * specified by "fpga-bridges" property. Note that the
+ * fpga_bridges_enable/disable/put functions are all fine with an empty list
+ * if that happens.
+ *
+ * Caller should call fpga_bridges_put(®ion->bridge_list) when
+ * done with the bridges.
+ *
+ * Return 0 for success (even if there are no bridges specified)
+ * or -EBUSY if any of the bridges are in use.
+ */
+static int fpga_region_get_bridges(struct fpga_region *region,
+ struct device_node *overlay)
+{
+ struct device *dev = ®ion->dev;
+ struct device_node *region_np = dev->of_node;
+ struct device_node *br, *np, *parent_br = NULL;
+ int i, ret;
+
+ /* If parent is a bridge, add to list */
+ ret = fpga_bridge_get_to_list(region_np->parent, region->info,
+ ®ion->bridge_list);
+ if (ret == -EBUSY)
+ return ret;
+
+ if (!ret)
+ parent_br = region_np->parent;
+
+ /* If overlay has a list of bridges, use it. */
+ if (of_parse_phandle(overlay, "fpga-bridges", 0))
+ np = overlay;
+ else
+ np = region_np;
+
+ for (i = 0; ; i++) {
+ br = of_parse_phandle(np, "fpga-bridges", i);
+ if (!br)
+ break;
+
+ /* If parent bridge is in list, skip it. */
+ if (br == parent_br)
+ continue;
+
+ /* If node is a bridge, get it and add to list */
+ ret = fpga_bridge_get_to_list(br, region->info,
+ ®ion->bridge_list);
+
+ /* If any of the bridges are in use, give up */
+ if (ret == -EBUSY) {
+ fpga_bridges_put(®ion->bridge_list);
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * fpga_region_program_fpga - program FPGA
+ * @region: FPGA region
+ * @firmware_name: name of FPGA image firmware file
+ * @overlay: device node of the overlay
+ * Program an FPGA using information in the device tree.
+ * Function assumes that there is a firmware-name property.
+ * Return 0 for success or negative error code.
+ */
+static int fpga_region_program_fpga(struct fpga_region *region,
+ const char *firmware_name,
+ struct device_node *overlay)
+{
+ struct fpga_manager *mgr;
+ int ret;
+
+ region = fpga_region_get(region);
+ if (IS_ERR(region)) {
+ pr_err("failed to get fpga region\n");
+ return PTR_ERR(region);
+ }
+
+ mgr = fpga_region_get_manager(region);
+ if (IS_ERR(mgr)) {
+ pr_err("failed to get fpga region manager\n");
+ return PTR_ERR(mgr);
+ }
+
+ ret = fpga_region_get_bridges(region, overlay);
+ if (ret) {
+ pr_err("failed to get fpga region bridges\n");
+ goto err_put_mgr;
+ }
+
+ ret = fpga_bridges_disable(®ion->bridge_list);
+ if (ret) {
+ pr_err("failed to disable region bridges\n");
+ goto err_put_br;
+ }
+
+ ret = fpga_mgr_firmware_load(mgr, region->info, firmware_name);
+ if (ret) {
+ pr_err("failed to load fpga image\n");
+ goto err_put_br;
+ }
+
+ ret = fpga_bridges_enable(®ion->bridge_list);
+ if (ret) {
+ pr_err("failed to enable region bridges\n");
+ goto err_put_br;
+ }
+
+ fpga_mgr_put(mgr);
+ fpga_region_put(region);
+
+ return 0;
+
+err_put_br:
+ fpga_bridges_put(®ion->bridge_list);
+err_put_mgr:
+ fpga_mgr_put(mgr);
+ fpga_region_put(region);
+
+ return ret;
+}
+
+/**
+ * child_regions_with_firmware
+ * @overlay: device node of the overlay
+ *
+ * If the overlay adds child FPGA regions, they are not allowed to have
+ * firmware-name property.
+ *
+ * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name.
+ */
+static int child_regions_with_firmware(struct device_node *overlay)
+{
+ struct device_node *child_region;
+ const char *child_firmware_name;
+ int ret = 0;
+
+ of_node_get(overlay);
+
+ child_region = of_find_matching_node(overlay, fpga_region_of_match);
+ while (child_region) {
+ if (!of_property_read_string(child_region, "firmware-name",
+ &child_firmware_name)) {
+ ret = -EINVAL;
+ break;
+ }
+ child_region = of_find_matching_node(child_region,
+ fpga_region_of_match);
+ }
+
+ of_node_put(child_region);
+
+ if (ret)
+ pr_err("firmware-name not allowed in child FPGA region: %s",
+ child_region->full_name);
+
+ return ret;
+}
+
+/**
+ * fpga_region_notify_pre_apply - pre-apply overlay notification
+ *
+ * @region: FPGA region that the overlay was applied to
+ * @nd: overlay notification data
+ *
+ * Called after when an overlay targeted to a FPGA Region is about to be
+ * applied. Function will check the properties that will be added to the FPGA
+ * region. If the checks pass, it will program the FPGA.
+ *
+ * The checks are:
+ * The overlay must add either firmware-name or external-fpga-config property
+ * to the FPGA Region.
+ *
+ * firmware-name : program the FPGA
+ * external-fpga-config : FPGA is already programmed
+ *
+ * The overlay can add other FPGA regions, but child FPGA regions cannot have a
+ * firmware-name property since those regions don't exist yet.
+ *
+ * If the overlay that breaks the rules, notifier returns an error and the
+ * overlay is rejected before it goes into the main tree.
+ *
+ * Returns 0 for success or negative error code for failure.
+ */
+static int fpga_region_notify_pre_apply(struct fpga_region *region,
+ struct of_overlay_notify_data *nd)
+{
+ const char *firmware_name = NULL;
+ struct fpga_image_info *info;
+ int ret;
+
+ info = devm_kzalloc(®ion->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ region->info = info;
+
+ /* Reject overlay if child FPGA Regions have firmware-name property */
+ ret = child_regions_with_firmware(nd->overlay);
+ if (ret)
+ return ret;
+
+ /* Read FPGA region properties from the overlay */
+ if (of_property_read_bool(nd->overlay, "partial-fpga-config"))
+ info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
+
+ if (of_property_read_bool(nd->overlay, "external-fpga-config"))
+ info->flags |= FPGA_MGR_EXTERNAL_CONFIG;
+
+ of_property_read_string(nd->overlay, "firmware-name", &firmware_name);
+
+ of_property_read_u32(nd->overlay, "region-unfreeze-timeout-us",
+ &info->enable_timeout_us);
+
+ of_property_read_u32(nd->overlay, "region-freeze-timeout-us",
+ &info->disable_timeout_us);
+
+ /* If FPGA was externally programmed, don't specify firmware */
+ if ((info->flags & FPGA_MGR_EXTERNAL_CONFIG) && firmware_name) {
+ pr_err("error: specified firmware and external-fpga-config");
+ return -EINVAL;
+ }
+
+ /* FPGA is already configured externally. We're done. */
+ if (info->flags & FPGA_MGR_EXTERNAL_CONFIG)
+ return 0;
+
+ /* If we got this far, we should be programming the FPGA */
+ if (!firmware_name) {
+ pr_err("should specify firmware-name or external-fpga-config\n");
+ return -EINVAL;
+ }
+
+ return fpga_region_program_fpga(region, firmware_name, nd->overlay);
+}
+
+/**
+ * fpga_region_notify_post_remove - post-remove overlay notification
+ *
+ * @region: FPGA region that was targeted by the overlay that was removed
+ * @nd: overlay notification data
+ *
+ * Called after an overlay has been removed if the overlay's target was a
+ * FPGA region.
+ */
+static void fpga_region_notify_post_remove(struct fpga_region *region,
+ struct of_overlay_notify_data *nd)
+{
+ fpga_bridges_disable(®ion->bridge_list);
+ fpga_bridges_put(®ion->bridge_list);
+ devm_kfree(®ion->dev, region->info);
+ region->info = NULL;
+}
+
+/**
+ * of_fpga_region_notify - reconfig notifier for dynamic DT changes
+ * @nb: notifier block
+ * @action: notifier action
+ * @arg: reconfig data
+ *
+ * This notifier handles programming a FPGA when a "firmware-name" property is
+ * added to a fpga-region.
+ *
+ * Returns NOTIFY_OK or error if FPGA programming fails.
+ */
+static int of_fpga_region_notify(struct notifier_block *nb,
+ unsigned long action, void *arg)
+{
+ struct of_overlay_notify_data *nd = arg;
+ struct fpga_region *region;
+ int ret;
+
+ switch (action) {
+ case OF_OVERLAY_PRE_APPLY:
+ pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__);
+ break;
+ case OF_OVERLAY_POST_APPLY:
+ pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__);
+ return NOTIFY_OK; /* not for us */
+ case OF_OVERLAY_PRE_REMOVE:
+ pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__);
+ return NOTIFY_OK; /* not for us */
+ case OF_OVERLAY_POST_REMOVE:
+ pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__);
+ break;
+ default: /* should not happen */
+ return NOTIFY_OK;
+ }
+
+ region = fpga_region_find(nd->target);
+ if (!region)
+ return NOTIFY_OK;
+
+ ret = 0;
+ switch (action) {
+ case OF_OVERLAY_PRE_APPLY:
+ ret = fpga_region_notify_pre_apply(region, nd);
+ break;
+
+ case OF_OVERLAY_POST_REMOVE:
+ fpga_region_notify_post_remove(region, nd);
+ break;
+ }
+
+ put_device(®ion->dev);
+
+ if (ret)
+ return notifier_from_errno(ret);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block fpga_region_of_nb = {
+ .notifier_call = of_fpga_region_notify,
+};
+
+static int fpga_region_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct fpga_region *region;
+ int id, ret = 0;
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+
+ id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ ret = id;
+ goto err_kfree;
+ }
+
+ mutex_init(®ion->mutex);
+ INIT_LIST_HEAD(®ion->bridge_list);
+
+ device_initialize(®ion->dev);
+ region->dev.class = fpga_region_class;
+ region->dev.parent = dev;
+ region->dev.of_node = np;
+ region->dev.id = id;
+ dev_set_drvdata(dev, region);
+
+ ret = dev_set_name(®ion->dev, "region%d", id);
+ if (ret)
+ goto err_remove;
+
+ ret = device_add(®ion->dev);
+ if (ret)
+ goto err_remove;
+
+ of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev);
+
+ dev_info(dev, "FPGA Region probed\n");
+
+ return 0;
+
+err_remove:
+ ida_simple_remove(&fpga_region_ida, id);
+err_kfree:
+ kfree(region);
+
+ return ret;
+}
+
+static int fpga_region_remove(struct platform_device *pdev)
+{
+ struct fpga_region *region = platform_get_drvdata(pdev);
+
+ device_unregister(®ion->dev);
+
+ return 0;
+}
+
+static struct platform_driver fpga_region_driver = {
+ .probe = fpga_region_probe,
+ .remove = fpga_region_remove,
+ .driver = {
+ .name = "fpga-region",
+ .of_match_table = of_match_ptr(fpga_region_of_match),
+ },
+};
+
+static void fpga_region_dev_release(struct device *dev)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+
+ ida_simple_remove(&fpga_region_ida, region->dev.id);
+ kfree(region);
+}
+
+/**
+ * fpga_region_init - init function for fpga_region class
+ * Creates the fpga_region class and registers a reconfig notifier.
+ */
+static int __init fpga_region_init(void)
+{
+ int ret;
+
+ fpga_region_class = class_create(THIS_MODULE, "fpga_region");
+ if (IS_ERR(fpga_region_class))
+ return PTR_ERR(fpga_region_class);
+
+ fpga_region_class->dev_release = fpga_region_dev_release;
+
+ ret = of_overlay_notifier_register(&fpga_region_of_nb);
+ if (ret)
+ goto err_class;
+
+ ret = platform_driver_register(&fpga_region_driver);
+ if (ret)
+ goto err_plat;
+
+ return 0;
+
+err_plat:
+ of_overlay_notifier_unregister(&fpga_region_of_nb);
+err_class:
+ class_destroy(fpga_region_class);
+ ida_destroy(&fpga_region_ida);
+ return ret;
+}
+
+static void __exit fpga_region_exit(void)
+{
+ platform_driver_unregister(&fpga_region_driver);
+ of_overlay_notifier_unregister(&fpga_region_of_nb);
+ class_destroy(fpga_region_class);
+ ida_destroy(&fpga_region_ida);
+}
+
+subsys_initcall(fpga_region_init);
+module_exit(fpga_region_exit);
+
+MODULE_DESCRIPTION("FPGA Region");
+MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 040b86d..12f6207 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -65,8 +65,10 @@ enum fpga_mgr_states {
/*
* FPGA Manager flags
* FPGA_MGR_PARTIAL_RECONFIG: do partial reconfiguration if supported
+ * FPGA_MGR_EXTERNAL_CONFIG: FPGA has been configured prior to Linux booting
*/
#define FPGA_MGR_PARTIAL_RECONFIG BIT(0)
+#define FPGA_MGR_EXTERNAL_CONFIG BIT(1)
/**
* struct fpga_image_info - information specific to a FPGA image
--
2.10.1
^ permalink raw reply related
* [PATCH v20 08/10] ARM: socfpga: fpga bridge driver support
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
Supports Altera SOCFPGA bridges:
* fpga2sdram
* fpga2hps
* hps2fpga
* lwhps2fpga
Allows enabling/disabling the bridges through the FPGA
Bridge Framework API functions.
The fpga2sdram driver only supports enabling and disabling
of the ports that been configured early on. This is due to
a hardware limitation where the read, write, and command
ports on the fpga2sdram bridge can only be reconfigured
while there are no transactions to the sdram, i.e. when
running out of OCRAM before the kernel boots.
Device tree property 'init-val' configures the driver to
enable or disable the bridge during probe. If the property
does not exist, the driver will leave the bridge in its
current state.
Signed-off-by: Alan Tull <atull@opensource.altera.com>
Signed-off-by: Matthew Gerlach <mgerlach@altera.com>
Signed-off-by: Dinh Nguyen <dinguyen@opensource.altera.com>
---
v2: Use resets instead of directly writing reset registers
v12: Bump version to align with simple-fpga-bus version
Get rid of the sysfs interface
fpga2sdram: get configuration stored in handoff register
v13: Remove unneeded WARN_ON
Change property from init-val to bridge-enable
Checkpatch cleanup
Fix email address
v14: use module_platform_driver
remove unused struct field and some #defines
don't really need exclamation points on error msgs
*const* struct fpga_bridge_ops
v15: No change in this patch for v15 of this patch set
v16: No change in this patch for v16 of this patch set
v17: No change to this patch for v17 of this patch set
v18: Eliminate need to specify reset names since only one reset
v19: Set bridge state before registering driver with framework
Include fix for drivers sharing the l3_remap_value reg
use devm_clk_get
make tristate in Kconfig
documentation of handoff registers
v20: No change for this patch in v20 of patchset
---
drivers/fpga/Kconfig | 7 ++
drivers/fpga/Makefile | 1 +
drivers/fpga/altera-fpga2sdram.c | 180 +++++++++++++++++++++++++++++++
drivers/fpga/altera-hps2fpga.c | 221 +++++++++++++++++++++++++++++++++++++++
4 files changed, 409 insertions(+)
create mode 100644 drivers/fpga/altera-fpga2sdram.c
create mode 100644 drivers/fpga/altera-hps2fpga.c
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index e0e1257..5605ad6 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -40,6 +40,13 @@ config FPGA_BRIDGE
Say Y here if you want to support bridges connected between host
processors and FPGAs or between FPGAs.
+config SOCFPGA_FPGA_BRIDGE
+ tristate "Altera SoCFPGA FPGA Bridges"
+ depends on ARCH_SOCFPGA && FPGA_BRIDGE
+ help
+ Say Y to enable drivers for FPGA bridges for Altera SOCFPGA
+ devices.
+
endif # FPGA
endmenu
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 8d746c3..e658436 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
# FPGA Bridge Drivers
obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o
+obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE) += altera-hps2fpga.o altera-fpga2sdram.o
# High Level Interfaces
obj-$(CONFIG_FPGA_REGION) += fpga-region.o
diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c
new file mode 100644
index 0000000..7ab358e
--- /dev/null
+++ b/drivers/fpga/altera-fpga2sdram.c
@@ -0,0 +1,180 @@
+/*
+ * FPGA to SDRAM Bridge Driver for Altera SoCFPGA Devices
+ *
+ * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This driver manages a bridge between an FPGA and the SDRAM used by the ARM
+ * host processor system (HPS).
+ *
+ * The bridge contains 4 read ports, 4 write ports, and 6 command ports.
+ * Reconfiguring these ports requires that no SDRAM transactions occur during
+ * reconfiguration. The code reconfiguring the ports cannot run out of SDRAM
+ * nor can the FPGA access the SDRAM during reconfiguration. This driver does
+ * not support reconfiguring the ports. The ports are configured by code
+ * running out of on chip ram before Linux is started and the configuration
+ * is passed in a handoff register in the system manager.
+ *
+ * This driver supports enabling and disabling of the configured ports, which
+ * allows for safe reprogramming of the FPGA, assuming that the new FPGA image
+ * uses the same port configuration. Bridges must be disabled before
+ * reprogramming the FPGA and re-enabled after the FPGA has been programmed.
+ */
+
+#include <linux/fpga/fpga-bridge.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+#define ALT_SDR_CTL_FPGAPORTRST_OFST 0x80
+#define ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK 0x00003fff
+#define ALT_SDR_CTL_FPGAPORTRST_RD_SHIFT 0
+#define ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT 4
+#define ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT 8
+
+/*
+ * From the Cyclone V HPS Memory Map document:
+ * These registers are used to store handoff information between the
+ * preloader and the OS. These 8 registers can be used to store any
+ * information. The contents of these registers have no impact on
+ * the state of the HPS hardware.
+ */
+#define SYSMGR_ISWGRP_HANDOFF3 (0x8C)
+
+#define F2S_BRIDGE_NAME "fpga2sdram"
+
+struct alt_fpga2sdram_data {
+ struct device *dev;
+ struct regmap *sdrctl;
+ int mask;
+};
+
+static int alt_fpga2sdram_enable_show(struct fpga_bridge *bridge)
+{
+ struct alt_fpga2sdram_data *priv = bridge->priv;
+ int value;
+
+ regmap_read(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST, &value);
+
+ return (value & priv->mask) == priv->mask;
+}
+
+static inline int _alt_fpga2sdram_enable_set(struct alt_fpga2sdram_data *priv,
+ bool enable)
+{
+ return regmap_update_bits(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST,
+ priv->mask, enable ? priv->mask : 0);
+}
+
+static int alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+ return _alt_fpga2sdram_enable_set(bridge->priv, enable);
+}
+
+struct prop_map {
+ char *prop_name;
+ u32 *prop_value;
+ u32 prop_max;
+};
+
+static const struct fpga_bridge_ops altera_fpga2sdram_br_ops = {
+ .enable_set = alt_fpga2sdram_enable_set,
+ .enable_show = alt_fpga2sdram_enable_show,
+};
+
+static const struct of_device_id altera_fpga_of_match[] = {
+ { .compatible = "altr,socfpga-fpga2sdram-bridge" },
+ {},
+};
+
+static int alt_fpga_bridge_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct alt_fpga2sdram_data *priv;
+ u32 enable;
+ struct regmap *sysmgr;
+ int ret = 0;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+
+ priv->sdrctl = syscon_regmap_lookup_by_compatible("altr,sdr-ctl");
+ if (IS_ERR(priv->sdrctl)) {
+ dev_err(dev, "regmap for altr,sdr-ctl lookup failed.\n");
+ return PTR_ERR(priv->sdrctl);
+ }
+
+ sysmgr = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
+ if (IS_ERR(priv->sdrctl)) {
+ dev_err(dev, "regmap for altr,sys-mgr lookup failed.\n");
+ return PTR_ERR(sysmgr);
+ }
+
+ /* Get f2s bridge configuration saved in handoff register */
+ regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask);
+
+ ret = fpga_bridge_register(dev, F2S_BRIDGE_NAME,
+ &altera_fpga2sdram_br_ops, priv);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "driver initialized with handoff %08x\n", priv->mask);
+
+ if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
+ if (enable > 1) {
+ dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
+ } else {
+ dev_info(dev, "%s bridge\n",
+ (enable ? "enabling" : "disabling"));
+ ret = _alt_fpga2sdram_enable_set(priv, enable);
+ if (ret) {
+ fpga_bridge_unregister(&pdev->dev);
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int alt_fpga_bridge_remove(struct platform_device *pdev)
+{
+ fpga_bridge_unregister(&pdev->dev);
+
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
+
+static struct platform_driver altera_fpga_driver = {
+ .probe = alt_fpga_bridge_probe,
+ .remove = alt_fpga_bridge_remove,
+ .driver = {
+ .name = "altera_fpga2sdram_bridge",
+ .of_match_table = of_match_ptr(altera_fpga_of_match),
+ },
+};
+
+module_platform_driver(altera_fpga_driver);
+
+MODULE_DESCRIPTION("Altera SoCFPGA FPGA to SDRAM Bridge");
+MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c
new file mode 100644
index 0000000..63ff0e3
--- /dev/null
+++ b/drivers/fpga/altera-hps2fpga.c
@@ -0,0 +1,221 @@
+/*
+ * FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices
+ *
+ * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved.
+ *
+ * Includes this patch from the mailing list:
+ * fpga: altera-hps2fpga: fix HPS2FPGA bridge visibility to L3 masters
+ * Signed-off-by: Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This driver manages bridges on a Altera SOCFPGA between the ARM host
+ * processor system (HPS) and the embedded FPGA.
+ *
+ * This driver supports enabling and disabling of the configured ports, which
+ * allows for safe reprogramming of the FPGA, assuming that the new FPGA image
+ * uses the same port configuration. Bridges must be disabled before
+ * reprogramming the FPGA and re-enabled after the FPGA has been programmed.
+ */
+
+#include <linux/clk.h>
+#include <linux/fpga/fpga-bridge.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+
+#define ALT_L3_REMAP_OFST 0x0
+#define ALT_L3_REMAP_MPUZERO_MSK 0x00000001
+#define ALT_L3_REMAP_H2F_MSK 0x00000008
+#define ALT_L3_REMAP_LWH2F_MSK 0x00000010
+
+#define HPS2FPGA_BRIDGE_NAME "hps2fpga"
+#define LWHPS2FPGA_BRIDGE_NAME "lwhps2fpga"
+#define FPGA2HPS_BRIDGE_NAME "fpga2hps"
+
+struct altera_hps2fpga_data {
+ const char *name;
+ struct reset_control *bridge_reset;
+ struct regmap *l3reg;
+ unsigned int remap_mask;
+ struct clk *clk;
+};
+
+static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge)
+{
+ struct altera_hps2fpga_data *priv = bridge->priv;
+
+ return reset_control_status(priv->bridge_reset);
+}
+
+/* The L3 REMAP register is write only, so keep a cached value. */
+static unsigned int l3_remap_shadow;
+static spinlock_t l3_remap_lock;
+
+static int _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv,
+ bool enable)
+{
+ unsigned long flags;
+ int ret;
+
+ /* bring bridge out of reset */
+ if (enable)
+ ret = reset_control_deassert(priv->bridge_reset);
+ else
+ ret = reset_control_assert(priv->bridge_reset);
+ if (ret)
+ return ret;
+
+ /* Allow bridge to be visible to L3 masters or not */
+ if (priv->remap_mask) {
+ spin_lock_irqsave(&l3_remap_lock, flags);
+ l3_remap_shadow |= ALT_L3_REMAP_MPUZERO_MSK;
+
+ if (enable)
+ l3_remap_shadow |= priv->remap_mask;
+ else
+ l3_remap_shadow &= ~priv->remap_mask;
+
+ ret = regmap_write(priv->l3reg, ALT_L3_REMAP_OFST,
+ l3_remap_shadow);
+ spin_unlock_irqrestore(&l3_remap_lock, flags);
+ }
+
+ return ret;
+}
+
+static int alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+ return _alt_hps2fpga_enable_set(bridge->priv, enable);
+}
+
+static const struct fpga_bridge_ops altera_hps2fpga_br_ops = {
+ .enable_set = alt_hps2fpga_enable_set,
+ .enable_show = alt_hps2fpga_enable_show,
+};
+
+static struct altera_hps2fpga_data hps2fpga_data = {
+ .name = HPS2FPGA_BRIDGE_NAME,
+ .remap_mask = ALT_L3_REMAP_H2F_MSK,
+};
+
+static struct altera_hps2fpga_data lwhps2fpga_data = {
+ .name = LWHPS2FPGA_BRIDGE_NAME,
+ .remap_mask = ALT_L3_REMAP_LWH2F_MSK,
+};
+
+static struct altera_hps2fpga_data fpga2hps_data = {
+ .name = FPGA2HPS_BRIDGE_NAME,
+};
+
+static const struct of_device_id altera_fpga_of_match[] = {
+ { .compatible = "altr,socfpga-hps2fpga-bridge",
+ .data = &hps2fpga_data },
+ { .compatible = "altr,socfpga-lwhps2fpga-bridge",
+ .data = &lwhps2fpga_data },
+ { .compatible = "altr,socfpga-fpga2hps-bridge",
+ .data = &fpga2hps_data },
+ {},
+};
+
+static int alt_fpga_bridge_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct altera_hps2fpga_data *priv;
+ const struct of_device_id *of_id;
+ u32 enable;
+ int ret;
+
+ of_id = of_match_device(altera_fpga_of_match, dev);
+ priv = (struct altera_hps2fpga_data *)of_id->data;
+
+ priv->bridge_reset = of_reset_control_get_by_index(dev->of_node, 0);
+ if (IS_ERR(priv->bridge_reset)) {
+ dev_err(dev, "Could not get %s reset control\n", priv->name);
+ return PTR_ERR(priv->bridge_reset);
+ }
+
+ priv->l3reg = syscon_regmap_lookup_by_compatible("altr,l3regs");
+ if (IS_ERR(priv->l3reg)) {
+ dev_err(dev, "regmap for altr,l3regs lookup failed\n");
+ return PTR_ERR(priv->l3reg);
+ }
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "no clock specified\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(dev, "could not enable clock\n");
+ return -EBUSY;
+ }
+
+ spin_lock_init(&l3_remap_lock);
+
+ if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) {
+ if (enable > 1) {
+ dev_warn(dev, "invalid bridge-enable %u > 1\n", enable);
+ } else {
+ dev_info(dev, "%s bridge\n",
+ (enable ? "enabling" : "disabling"));
+
+ ret = _alt_hps2fpga_enable_set(priv, enable);
+ if (ret) {
+ fpga_bridge_unregister(&pdev->dev);
+ return ret;
+ }
+ }
+ }
+
+ return fpga_bridge_register(dev, priv->name, &altera_hps2fpga_br_ops,
+ priv);
+}
+
+static int alt_fpga_bridge_remove(struct platform_device *pdev)
+{
+ struct fpga_bridge *bridge = platform_get_drvdata(pdev);
+ struct altera_hps2fpga_data *priv = bridge->priv;
+
+ fpga_bridge_unregister(&pdev->dev);
+
+ clk_disable_unprepare(priv->clk);
+ clk_put(priv->clk);
+
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(of, altera_fpga_of_match);
+
+static struct platform_driver alt_fpga_bridge_driver = {
+ .probe = alt_fpga_bridge_probe,
+ .remove = alt_fpga_bridge_remove,
+ .driver = {
+ .name = "altera_hps2fpga_bridge",
+ .of_match_table = of_match_ptr(altera_fpga_of_match),
+ },
+};
+
+module_platform_driver(alt_fpga_bridge_driver);
+
+MODULE_DESCRIPTION("Altera SoCFPGA HPS to FPGA Bridge");
+MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
+MODULE_LICENSE("GPL v2");
--
2.10.1
^ permalink raw reply related
* [PATCH v20 09/10] fpga: add altera freeze bridge support
From: Alan Tull @ 2016-10-17 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161017160941.4205-1-atull@opensource.altera.com>
Add a low level driver for Altera Freeze Bridges to the FPGA Bridge
framework. A freeze bridge is a bridge that exists in the FPGA
fabric to isolate one region of the FPGA from the busses while that
one region is being reprogrammed.
Signed-off-by: Alan Tull <atull@opensource.altera.com>
Signed-off-by: Matthew Gerlach <mgerlach@opensource.altera.com>
---
v19: added in v19 of patchset as it needs for fpga info struct
v20: No change for this patch in v20 of patchset
---
drivers/fpga/Kconfig | 9 ++
drivers/fpga/Makefile | 1 +
drivers/fpga/altera-freeze-bridge.c | 273 ++++++++++++++++++++++++++++++++++++
3 files changed, 283 insertions(+)
create mode 100644 drivers/fpga/altera-freeze-bridge.c
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 5605ad6..8fe6a84 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -47,6 +47,15 @@ config SOCFPGA_FPGA_BRIDGE
Say Y to enable drivers for FPGA bridges for Altera SOCFPGA
devices.
+config ALTERA_FREEZE_BRIDGE
+ tristate "Altera FPGA Freeze Bridge"
+ depends on ARCH_SOCFPGA && FPGA_BRIDGE
+ help
+ Say Y to enable drivers for Altera FPGA Freeze bridges. A
+ freeze bridge is a bridge that exists in the FPGA fabric to
+ isolate one region of the FPGA from the busses while that
+ region is being reprogrammed.
+
endif # FPGA
endmenu
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index e658436..a6f874d 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
# FPGA Bridge Drivers
obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o
obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE) += altera-hps2fpga.o altera-fpga2sdram.o
+obj-$(CONFIG_ALTERA_FREEZE_BRIDGE) += altera-freeze-bridge.o
# High Level Interfaces
obj-$(CONFIG_FPGA_REGION) += fpga-region.o
diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c
new file mode 100644
index 0000000..8dcd9fb
--- /dev/null
+++ b/drivers/fpga/altera-freeze-bridge.c
@@ -0,0 +1,273 @@
+/*
+ * FPGA Freeze Bridge Controller
+ *
+ * Copyright (C) 2016 Altera Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/fpga/fpga-bridge.h>
+
+#define FREEZE_CSR_STATUS_OFFSET 0
+#define FREEZE_CSR_CTRL_OFFSET 4
+#define FREEZE_CSR_ILLEGAL_REQ_OFFSET 8
+#define FREEZE_CSR_REG_VERSION 12
+
+#define FREEZE_CSR_SUPPORTED_VERSION 2
+
+#define FREEZE_CSR_STATUS_FREEZE_REQ_DONE BIT(0)
+#define FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE BIT(1)
+
+#define FREEZE_CSR_CTRL_FREEZE_REQ BIT(0)
+#define FREEZE_CSR_CTRL_RESET_REQ BIT(1)
+#define FREEZE_CSR_CTRL_UNFREEZE_REQ BIT(2)
+
+#define FREEZE_BRIDGE_NAME "freeze"
+
+struct altera_freeze_br_data {
+ struct device *dev;
+ void __iomem *base_addr;
+ bool enable;
+};
+
+/*
+ * Poll status until status bit is set or we have a timeout.
+ */
+static int altera_freeze_br_req_ack(struct altera_freeze_br_data *priv,
+ u32 timeout, u32 req_ack)
+{
+ struct device *dev = priv->dev;
+ void __iomem *csr_illegal_req_addr = priv->base_addr +
+ FREEZE_CSR_ILLEGAL_REQ_OFFSET;
+ u32 status, illegal, ctrl;
+ int ret = -ETIMEDOUT;
+
+ do {
+ illegal = readl(csr_illegal_req_addr);
+ if (illegal) {
+ dev_err(dev, "illegal request detected 0x%x", illegal);
+
+ writel(1, csr_illegal_req_addr);
+
+ illegal = readl(csr_illegal_req_addr);
+ if (illegal)
+ dev_err(dev, "illegal request not cleared 0x%x",
+ illegal);
+
+ ret = -EINVAL;
+ break;
+ }
+
+ status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
+ dev_dbg(dev, "%s %x %x\n", __func__, status, req_ack);
+ status &= req_ack;
+ if (status) {
+ ctrl = readl(priv->base_addr + FREEZE_CSR_CTRL_OFFSET);
+ dev_dbg(dev, "%s request %x acknowledged %x %x\n",
+ __func__, req_ack, status, ctrl);
+ ret = 0;
+ break;
+ }
+
+ udelay(1);
+ } while (timeout--);
+
+ if (ret == -ETIMEDOUT)
+ dev_err(dev, "%s timeout waiting for 0x%x\n",
+ __func__, req_ack);
+
+ return ret;
+}
+
+static int altera_freeze_br_do_freeze(struct altera_freeze_br_data *priv,
+ u32 timeout)
+{
+ struct device *dev = priv->dev;
+ void __iomem *csr_ctrl_addr = priv->base_addr +
+ FREEZE_CSR_CTRL_OFFSET;
+ u32 status;
+ int ret;
+
+ status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
+
+ dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
+
+ if (status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE) {
+ dev_dbg(dev, "%s bridge already disabled %d\n",
+ __func__, status);
+ return 0;
+ } else if (!(status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)) {
+ dev_err(dev, "%s bridge not enabled %d\n", __func__, status);
+ return -EINVAL;
+ }
+
+ writel(FREEZE_CSR_CTRL_FREEZE_REQ, csr_ctrl_addr);
+
+ ret = altera_freeze_br_req_ack(priv, timeout,
+ FREEZE_CSR_STATUS_FREEZE_REQ_DONE);
+
+ if (ret)
+ writel(0, csr_ctrl_addr);
+ else
+ writel(FREEZE_CSR_CTRL_RESET_REQ, csr_ctrl_addr);
+
+ return ret;
+}
+
+static int altera_freeze_br_do_unfreeze(struct altera_freeze_br_data *priv,
+ u32 timeout)
+{
+ struct device *dev = priv->dev;
+ void __iomem *csr_ctrl_addr = priv->base_addr +
+ FREEZE_CSR_CTRL_OFFSET;
+ u32 status;
+ int ret;
+
+ writel(0, csr_ctrl_addr);
+
+ status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
+
+ dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
+
+ if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE) {
+ dev_dbg(dev, "%s bridge already enabled %d\n",
+ __func__, status);
+ return 0;
+ } else if (!(status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE)) {
+ dev_err(dev, "%s bridge not frozen %d\n", __func__, status);
+ return -EINVAL;
+ }
+
+ writel(FREEZE_CSR_CTRL_UNFREEZE_REQ, csr_ctrl_addr);
+
+ ret = altera_freeze_br_req_ack(priv, timeout,
+ FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE);
+
+ status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
+
+ dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
+
+ writel(0, csr_ctrl_addr);
+
+ return ret;
+}
+
+/*
+ * enable = 1 : allow traffic through the bridge
+ * enable = 0 : disable traffic through the bridge
+ */
+static int altera_freeze_br_enable_set(struct fpga_bridge *bridge,
+ bool enable)
+{
+ struct altera_freeze_br_data *priv = bridge->priv;
+ struct fpga_image_info *info = bridge->info;
+ u32 timeout = 0;
+ int ret;
+
+ if (enable) {
+ if (info)
+ timeout = info->enable_timeout_us;
+
+ ret = altera_freeze_br_do_unfreeze(bridge->priv, timeout);
+ } else {
+ if (info)
+ timeout = info->disable_timeout_us;
+
+ ret = altera_freeze_br_do_freeze(bridge->priv, timeout);
+ }
+
+ if (!ret)
+ priv->enable = enable;
+
+ return ret;
+}
+
+static int altera_freeze_br_enable_show(struct fpga_bridge *bridge)
+{
+ struct altera_freeze_br_data *priv = bridge->priv;
+
+ return priv->enable;
+}
+
+static struct fpga_bridge_ops altera_freeze_br_br_ops = {
+ .enable_set = altera_freeze_br_enable_set,
+ .enable_show = altera_freeze_br_enable_show,
+};
+
+static const struct of_device_id altera_freeze_br_of_match[] = {
+ { .compatible = "altr,freeze-bridge-controller", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altera_freeze_br_of_match);
+
+static int altera_freeze_br_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct altera_freeze_br_data *priv;
+ struct resource *res;
+ u32 status, revision;
+
+ if (!np)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->base_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->base_addr))
+ return PTR_ERR(priv->base_addr);
+
+ status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
+ if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)
+ priv->enable = 1;
+
+ revision = readl(priv->base_addr + FREEZE_CSR_REG_VERSION);
+ if (revision != FREEZE_CSR_SUPPORTED_VERSION)
+ dev_warn(dev,
+ "%s Freeze Controller unexpected revision %d != %d\n",
+ __func__, revision, FREEZE_CSR_SUPPORTED_VERSION);
+
+ return fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
+ &altera_freeze_br_br_ops, priv);
+}
+
+static int altera_freeze_br_remove(struct platform_device *pdev)
+{
+ fpga_bridge_unregister(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver altera_freeze_br_driver = {
+ .probe = altera_freeze_br_probe,
+ .remove = altera_freeze_br_remove,
+ .driver = {
+ .name = "altera_freeze_br",
+ .of_match_table = of_match_ptr(altera_freeze_br_of_match),
+ },
+};
+
+module_platform_driver(altera_freeze_br_driver);
+
+MODULE_DESCRIPTION("Altera Freeze Bridge");
+MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
+MODULE_LICENSE("GPL v2");
--
2.10.1
^ permalink raw reply related
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