提交 cf9b0772 编写于 作者: L Linus Torvalds

Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC driver updates from Arnd Bergmann:
 "This branch contains platform-related driver updates for ARM and
  ARM64, these are the areas that bring the changes:

  New drivers:

   - driver support for Renesas R-Car V3M (R8A77970)

   - power management support for Amlogic GX

   - a new driver for the Tegra BPMP thermal sensor

   - a new bus driver for Technologic Systems NBUS

  Changes for subsystems that prefer to merge through arm-soc:

   - the usual updates for reset controller drivers from Philipp Zabel,
     with five added drivers for SoCs in the arc, meson, socfpa,
     uniphier and mediatek families

   - updates to the ARM SCPI and PSCI frameworks, from Sudeep Holla,
     Heiner Kallweit and Lorenzo Pieralisi

  Changes specific to some ARM-based SoC

   - the Freescale/NXP DPAA QBMan drivers from PowerPC can now work on
     ARM as well

   - several changes for power management on Broadcom SoCs

   - various improvements on Qualcomm, Broadcom, Amlogic, Atmel,
     Mediatek

   - minor Cleanups for Samsung, TI OMAP SoCs"

[ NOTE! This doesn't work without the previous ARM SoC device-tree pull,
  because the R8A77970 driver is missing a header file that came from
  that pull.

  The fact that this got merged afterwards only fixes it at this point,
  and bisection of that driver will fail if/when you walk into the
  history of that driver.           - Linus ]

* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (96 commits)
  soc: amlogic: meson-gx-pwrc-vpu: fix power-off when powered by bootloader
  bus: add driver for the Technologic Systems NBUS
  memory: omap-gpmc: Remove deprecated gpmc_update_nand_reg()
  soc: qcom: remove unused label
  soc: amlogic: gx pm domain: add PM and OF dependencies
  drivers/firmware: psci_checker: Add missing destroy_timer_on_stack()
  dt-bindings: power: add amlogic meson power domain bindings
  soc: amlogic: add Meson GX VPU Domains driver
  soc: qcom: Remote filesystem memory driver
  dt-binding: soc: qcom: Add binding for rmtfs memory
  of: reserved_mem: Accessor for acquiring reserved_mem
  of/platform: Generalize /reserved-memory handling
  soc: mediatek: pwrap: fix fatal compiler error
  soc: mediatek: pwrap: fix compiler errors
  arm64: mediatek: cleanup message for platform selection
  soc: Allow test-building of MediaTek drivers
  soc: mediatek: place Kconfig for all SoC drivers under menu
  soc: mediatek: pwrap: add support for MT7622 SoC
  soc: mediatek: pwrap: add common way for setup CS timing extenstion
  soc: mediatek: pwrap: add MediaTek MT6380 as one slave of pwrap
  ..
......@@ -164,6 +164,8 @@ Control registers for this memory controller's DDR PHY.
Required properties:
- compatible : should contain one of these
"brcm,brcmstb-ddr-phy-v71.1"
"brcm,brcmstb-ddr-phy-v72.0"
"brcm,brcmstb-ddr-phy-v225.1"
"brcm,brcmstb-ddr-phy-v240.1"
"brcm,brcmstb-ddr-phy-v240.2"
......@@ -184,7 +186,9 @@ Sequencer DRAM parameters and control registers. Used for Self-Refresh
Power-Down (SRPD), among other things.
Required properties:
- compatible : should contain "brcm,brcmstb-memc-ddr"
- compatible : should contain one of these
"brcm,brcmstb-memc-ddr-rev-b.2.2"
"brcm,brcmstb-memc-ddr"
- reg : the MEMC DDR register range
Example:
......
......@@ -4,7 +4,6 @@ Properties:
- compatible : should contain two values. First value must be one from following list:
- "samsung,exynos3250-pmu" - for Exynos3250 SoC,
- "samsung,exynos4210-pmu" - for Exynos4210 SoC,
- "samsung,exynos4212-pmu" - for Exynos4212 SoC,
- "samsung,exynos4412-pmu" - for Exynos4412 SoC,
- "samsung,exynos5250-pmu" - for Exynos5250 SoC,
- "samsung,exynos5260-pmu" - for Exynos5260 SoC.
......
......@@ -18,6 +18,8 @@ Required properties:
* Core, iface, and bus clocks required for "qcom,scm"
- clock-names: Must contain "core" for the core clock, "iface" for the interface
clock and "bus" for the bus clock per the requirements of the compatible.
- qcom,dload-mode: phandle to the TCSR hardware block and offset of the
download mode control register (optional)
Example for MSM8916:
......
DDR PHY Front End (DPFE) for Broadcom STB
=========================================
DPFE and the DPFE firmware provide an interface for the host CPU to
communicate with the DCPU, which resides inside the DDR PHY.
There are three memory regions for interacting with the DCPU. These are
specified in a single reg property.
Required properties:
- compatible: must be "brcm,bcm7271-dpfe-cpu", "brcm,bcm7268-dpfe-cpu"
or "brcm,dpfe-cpu"
- reg: must reference three register ranges
- start address and length of the DCPU register space
- start address and length of the DCPU data memory space
- start address and length of the DCPU instruction memory space
- reg-names: must contain "dpfe-cpu", "dpfe-dmem", and "dpfe-imem";
they must be in the same order as the register declarations
Example:
dpfe_cpu0: dpfe-cpu@f1132000 {
compatible = "brcm,bcm7271-dpfe-cpu", "brcm,dpfe-cpu";
reg = <0xf1132000 0x180
0xf1134000 0x1000
0xf1138000 0x4000>;
reg-names = "dpfe-cpu", "dpfe-dmem", "dpfe-imem";
};
......@@ -11,3 +11,156 @@ Required properties:
The experimental -viper variants are for running Linux on the 3384's
BMIPS4355 cable modem CPU instead of the BMIPS5000 application processor.
Power management
----------------
For power management (particularly, S2/S3/S5 system suspend), the following SoC
components are needed:
= Always-On control block (AON CTRL)
This hardware provides control registers for the "always-on" (even in low-power
modes) hardware, such as the Power Management State Machine (PMSM).
Required properties:
- compatible : should be one of
"brcm,bcm7425-aon-ctrl"
"brcm,bcm7429-aon-ctrl"
"brcm,bcm7435-aon-ctrl" and
"brcm,brcmstb-aon-ctrl"
- reg : the register start and length for the AON CTRL block
Example:
syscon@410000 {
compatible = "brcm,bcm7425-aon-ctrl", "brcm,brcmstb-aon-ctrl";
reg = <0x410000 0x400>;
};
= Memory controllers
A Broadcom STB SoC typically has a number of independent memory controllers,
each of which may have several associated hardware blocks, which are versioned
independently (control registers, DDR PHYs, etc.). One might consider
describing these controllers as a parent "memory controllers" block, which
contains N sub-nodes (one for each controller in the system), each of which is
associated with a number of hardware register resources (e.g., its PHY.
== MEMC (MEMory Controller)
Represents a single memory controller instance.
Required properties:
- compatible : should contain "brcm,brcmstb-memc" and "simple-bus"
- ranges : should contain the child address in the parent address
space, must be 0 here, and the register start and length of
the entire memory controller (including all sub nodes: DDR PHY,
arbiter, etc.)
- #address-cells : must be 1
- #size-cells : must be 1
Example:
memory-controller@0 {
compatible = "brcm,brcmstb-memc", "simple-bus";
ranges = <0x0 0x0 0xa000>;
#address-cells = <1>;
#size-cells = <1>;
memc-arb@1000 {
...
};
memc-ddr@2000 {
...
};
ddr-phy@6000 {
...
};
};
Should contain subnodes for any of the following relevant hardware resources:
== DDR PHY control
Control registers for this memory controller's DDR PHY.
Required properties:
- compatible : should contain one of these
"brcm,brcmstb-ddr-phy-v64.5"
"brcm,brcmstb-ddr-phy"
- reg : the DDR PHY register range and length
Example:
ddr-phy@6000 {
compatible = "brcm,brcmstb-ddr-phy-v64.5";
reg = <0x6000 0xc8>;
};
== DDR memory controller sequencer
Control registers for this memory controller's DDR memory sequencer
Required properties:
- compatible : should contain one of these
"brcm,bcm7425-memc-ddr"
"brcm,bcm7429-memc-ddr"
"brcm,bcm7435-memc-ddr" and
"brcm,brcmstb-memc-ddr"
- reg : the DDR sequencer register range and length
Example:
memc-ddr@2000 {
compatible = "brcm,bcm7425-memc-ddr", "brcm,brcmstb-memc-ddr";
reg = <0x2000 0x300>;
};
== MEMC Arbiter
The memory controller arbiter is responsible for memory clients allocation
(bandwidth, priorities etc.) and needs to have its contents restored during
deep sleep states (S3).
Required properties:
- compatible : should contain one of these
"brcm,brcmstb-memc-arb-v10.0.0.0"
"brcm,brcmstb-memc-arb"
- reg : the DDR Arbiter register range and length
Example:
memc-arb@1000 {
compatible = "brcm,brcmstb-memc-arb-v10.0.0.0";
reg = <0x1000 0x248>;
};
== Timers
The Broadcom STB chips contain a timer block with several general purpose
timers that can be used.
Required properties:
- compatible : should contain one of:
"brcm,bcm7425-timers"
"brcm,bcm7429-timers"
"brcm,bcm7435-timers and
"brcm,brcmstb-timers"
- reg : the timers register range
- interrupts : the interrupt line for this timer block
Example:
timers: timer@4067c0 {
compatible = "brcm,bcm7425-timers", "brcm,brcmstb-timers";
reg = <0x4067c0 0x40>;
interrupts = <&periph_intc 19>;
};
Amlogic Meson Power Controller
==============================
The Amlogic Meson SoCs embeds an internal Power domain controller.
VPU Power Domain
----------------
The Video Processing Unit power domain is controlled by this power controller,
but the domain requires some external resources to meet the correct power
sequences.
The bindings must respect the power domain bindings as described in the file
power_domain.txt
Device Tree Bindings:
---------------------
Required properties:
- compatible: should be "amlogic,meson-gx-pwrc-vpu" for the Meson GX SoCs
- #power-domain-cells: should be 0
- amlogic,hhi-sysctrl: phandle to the HHI sysctrl node
- resets: phandles to the reset lines needed for this power demain sequence
as described in ../reset/reset.txt
- clocks: from common clock binding: handle to VPU and VAPB clocks
- clock-names: from common clock binding: must contain "vpu", "vapb"
corresponding to entry in the clocks property.
Parent node should have the following properties :
- compatible: "amlogic,meson-gx-ao-sysctrl", "syscon", "simple-mfd"
- reg: base address and size of the AO system control register space.
Example:
-------
ao_sysctrl: sys-ctrl@0 {
compatible = "amlogic,meson-gx-ao-sysctrl", "syscon", "simple-mfd";
reg = <0x0 0x0 0x0 0x100>;
pwrc_vpu: power-controller-vpu {
compatible = "amlogic,meson-gx-pwrc-vpu";
#power-domain-cells = <0>;
amlogic,hhi-sysctrl = <&sysctrl>;
resets = <&reset RESET_VIU>,
<&reset RESET_VENC>,
<&reset RESET_VCBUS>,
<&reset RESET_BT656>,
<&reset RESET_DVIN_RESET>,
<&reset RESET_RDMA>,
<&reset RESET_VENCI>,
<&reset RESET_VENCP>,
<&reset RESET_VDAC>,
<&reset RESET_VDI6>,
<&reset RESET_VENCL>,
<&reset RESET_VID_LOCK>;
clocks = <&clkc CLKID_VPU>,
<&clkc CLKID_VAPB>;
clock-names = "vpu", "vapb";
};
};
......@@ -17,6 +17,7 @@ Required properties:
- "renesas,r8a7794-sysc" (R-Car E2)
- "renesas,r8a7795-sysc" (R-Car H3)
- "renesas,r8a7796-sysc" (R-Car M3-W)
- "renesas,r8a77970-sysc" (R-Car V3M)
- "renesas,r8a77995-sysc" (R-Car D3)
- reg: Address start and address range for the device.
- #power-domain-cells: Must be 1.
......
Qualcomm Remote File System Memory binding
This binding describes the Qualcomm remote filesystem memory, which serves the
purpose of describing the shared memory region used for remote processors to
access block device data using the Remote Filesystem protocol.
- compatible:
Usage: required
Value type: <stringlist>
Definition: must be:
"qcom,rmtfs-mem"
- reg:
Usage: required for static allocation
Value type: <prop-encoded-array>
Definition: must specify base address and size of the memory region,
as described in reserved-memory.txt
- size:
Usage: required for dynamic allocation
Value type: <prop-encoded-array>
Definition: must specify a size of the memory region, as described in
reserved-memory.txt
- qcom,client-id:
Usage: required
Value type: <u32>
Definition: identifier of the client to use this region for buffers.
- qcom,vmid:
Usage: optional
Value type: <u32>
Definition: vmid of the remote processor, to set up memory protection.
= EXAMPLE
The following example shows the remote filesystem memory setup for APQ8016,
with the rmtfs region for the Hexagon DSP (id #1) located at 0x86700000.
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
rmtfs@86700000 {
compatible = "qcom,rmtfs-mem";
reg = <0x0 0x86700000 0x0 0xe0000>;
no-map;
qcom,client-id = <1>;
};
};
......@@ -26,6 +26,7 @@ Required properties:
- "renesas,r8a7794-rst" (R-Car E2)
- "renesas,r8a7795-rst" (R-Car H3)
- "renesas,r8a7796-rst" (R-Car M3-W)
- "renesas,r8a77970-rst" (R-Car V3M)
- "renesas,r8a77995-rst" (R-Car D3)
- reg: Address start and address range for the device.
......
Binding for the AXS10x reset controller
This binding describes the ARC AXS10x boards custom IP-block which allows
to control reset signals of selected peripherals. For example DW GMAC, etc...
This block is controlled via memory-mapped register (AKA CREG) which
represents up-to 32 reset lines.
As of today only the following lines are used:
- DW GMAC - line 5
This binding uses the common reset binding[1].
[1] Documentation/devicetree/bindings/reset/reset.txt
Required properties:
- compatible: should be "snps,axs10x-reset".
- reg: should always contain pair address - length: for creg reset
bits register.
- #reset-cells: from common reset binding; Should always be set to 1.
Example:
reset: reset-controller@11220 {
compatible = "snps,axs10x-reset";
#reset-cells = <1>;
reg = <0x11220 0x4>;
};
Specifying reset lines connected to IP modules:
ethernet@.... {
....
resets = <&reset 5>;
....
};
......@@ -13,6 +13,7 @@ Required properties:
"socionext,uniphier-pxs2-reset" - for PXs2/LD6b SoC
"socionext,uniphier-ld11-reset" - for LD11 SoC
"socionext,uniphier-ld20-reset" - for LD20 SoC
"socionext,uniphier-pxs3-reset" - for PXs3 SoC
- #reset-cells: should be 1.
Example:
......@@ -44,6 +45,7 @@ Required properties:
"socionext,uniphier-ld11-mio-reset" - for LD11 SoC (MIO)
"socionext,uniphier-ld11-sd-reset" - for LD11 SoC (SD)
"socionext,uniphier-ld20-sd-reset" - for LD20 SoC
"socionext,uniphier-pxs3-sd-reset" - for PXs3 SoC
- #reset-cells: should be 1.
Example:
......@@ -74,6 +76,7 @@ Required properties:
"socionext,uniphier-pxs2-peri-reset" - for PXs2/LD6b SoC
"socionext,uniphier-ld11-peri-reset" - for LD11 SoC
"socionext,uniphier-ld20-peri-reset" - for LD20 SoC
"socionext,uniphier-pxs3-peri-reset" - for PXs3 SoC
- #reset-cells: should be 1.
Example:
......
......@@ -65,8 +65,8 @@ to the respective BMan instance
BMan Private Memory Node
BMan requires a contiguous range of physical memory used for the backing store
for BMan Free Buffer Proxy Records (FBPR). This memory is reserved/allocated as a
node under the /reserved-memory node
for BMan Free Buffer Proxy Records (FBPR). This memory is reserved/allocated as
a node under the /reserved-memory node.
The BMan FBPR memory node must be named "bman-fbpr"
......@@ -75,7 +75,9 @@ PROPERTIES
- compatible
Usage: required
Value type: <stringlist>
Definition: Must inclide "fsl,bman-fbpr"
Definition: PPC platforms: Must include "fsl,bman-fbpr"
ARM platforms: Must include "shared-dma-pool"
as well as the "no-map" property
The following constraints are relevant to the FBPR private memory:
- The size must be 2^(size + 1), with size = 11..33. That is 4 KiB to
......@@ -100,10 +102,10 @@ The example below shows a BMan FBPR dynamic allocation memory node
ranges;
bman_fbpr: bman-fbpr {
compatible = "fsl,bman-fbpr";
alloc-ranges = <0 0 0x10 0>;
compatible = "shared-mem-pool";
size = <0 0x1000000>;
alignment = <0 0x1000000>;
no-map;
};
};
......
......@@ -60,6 +60,12 @@ are located at offsets 0xbf8 and 0xbfc
Value type: <prop-encoded-array>
Definition: Reference input clock. Its frequency is half of the
platform clock
- memory-regions
Usage: Required for ARM
Value type: <phandle array>
Definition: List of phandles referencing the QMan private memory
nodes (described below). The qman-fqd node must be
first followed by qman-pfdr node. Only used on ARM
Devices connected to a QMan instance via Direct Connect Portals (DCP) must link
to the respective QMan instance
......@@ -74,7 +80,9 @@ QMan Private Memory Nodes
QMan requires two contiguous range of physical memory used for the backing store
for QMan Frame Queue Descriptor (FQD) and Packed Frame Descriptor Record (PFDR).
This memory is reserved/allocated as a nodes under the /reserved-memory node
This memory is reserved/allocated as a node under the /reserved-memory node.
For additional details about reserved memory regions see reserved-memory.txt
The QMan FQD memory node must be named "qman-fqd"
......@@ -83,7 +91,9 @@ PROPERTIES
- compatible
Usage: required
Value type: <stringlist>
Definition: Must inclide "fsl,qman-fqd"
Definition: PPC platforms: Must include "fsl,qman-fqd"
ARM platforms: Must include "shared-dma-pool"
as well as the "no-map" property
The QMan PFDR memory node must be named "qman-pfdr"
......@@ -92,7 +102,9 @@ PROPERTIES
- compatible
Usage: required
Value type: <stringlist>
Definition: Must inclide "fsl,qman-pfdr"
Definition: PPC platforms: Must include "fsl,qman-pfdr"
ARM platforms: Must include "shared-dma-pool"
as well as the "no-map" property
The following constraints are relevant to the FQD and PFDR private memory:
- The size must be 2^(size + 1), with size = 11..29. That is 4 KiB to
......@@ -117,16 +129,16 @@ The example below shows a QMan FQD and a PFDR dynamic allocation memory nodes
ranges;
qman_fqd: qman-fqd {
compatible = "fsl,qman-fqd";
alloc-ranges = <0 0 0x10 0>;
compatible = "shared-dma-pool";
size = <0 0x400000>;
alignment = <0 0x400000>;
no-map;
};
qman_pfdr: qman-pfdr {
compatible = "fsl,qman-pfdr";
alloc-ranges = <0 0 0x10 0>;
compatible = "shared-dma-pool";
size = <0 0x2000000>;
alignment = <0 0x2000000>;
no-map;
};
};
......
......@@ -19,6 +19,7 @@ IP Pairing
Required properties in pwrap device node.
- compatible:
"mediatek,mt2701-pwrap" for MT2701/7623 SoCs
"mediatek,mt7622-pwrap" for MT7622 SoCs
"mediatek,mt8135-pwrap" for MT8135 SoCs
"mediatek,mt8173-pwrap" for MT8173 SoCs
- interrupts: IRQ for pwrap in SOC
......@@ -36,9 +37,12 @@ Required properties in pwrap device node.
- clocks: Must contain an entry for each entry in clock-names.
Optional properities:
- pmic: Mediatek PMIC MFD is the child device of pwrap
- pmic: Using either MediaTek PMIC MFD as the child device of pwrap
See the following for child node definitions:
Documentation/devicetree/bindings/mfd/mt6397.txt
or the regulator-only device as the child device of pwrap, such as MT6380.
See the following definitions for such kinds of devices.
Documentation/devicetree/bindings/regulator/mt6380-regulator.txt
Example:
pwrap: pwrap@1000f000 {
......
......@@ -1219,6 +1219,8 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
W: http://www.linux4sam.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/nferre/linux-at91.git
S: Supported
N: at91
N: atmel
F: arch/arm/mach-at91/
F: include/soc/at91/
F: arch/arm/boot/dts/at91*.dts
......@@ -1227,6 +1229,9 @@ F: arch/arm/boot/dts/sama*.dts
F: arch/arm/boot/dts/sama*.dtsi
F: arch/arm/include/debug/at91.S
F: drivers/memory/atmel*
F: drivers/watchdog/sama5d4_wdt.c
X: drivers/input/touchscreen/atmel_mxt_ts.c
X: drivers/net/wireless/atmel/
ARM/CALXEDA HIGHBANK ARCHITECTURE
M: Rob Herring <robh@kernel.org>
......@@ -2141,7 +2146,6 @@ F: drivers/gpio/gpio-zx.c
F: drivers/i2c/busses/i2c-zx2967.c
F: drivers/mmc/host/dw_mmc-zx.*
F: drivers/pinctrl/zte/
F: drivers/reset/reset-zx2967.c
F: drivers/soc/zte/
F: drivers/thermal/zx2967_thermal.c
F: drivers/watchdog/zx2967_wdt.c
......@@ -2990,6 +2994,14 @@ L: bcm-kernel-feedback-list@broadcom.com
S: Maintained
F: drivers/mtd/nand/brcmnand/
BROADCOM STB DPFE DRIVER
M: Markus Mayer <mmayer@broadcom.com>
M: bcm-kernel-feedback-list@broadcom.com
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/brcm,dpfe-cpu.txt
F: drivers/memory/brcmstb_dpfe.c
BROADCOM SYSTEMPORT ETHERNET DRIVER
M: Florian Fainelli <f.fainelli@gmail.com>
L: netdev@vger.kernel.org
......@@ -13004,6 +13016,12 @@ F: arch/arc/plat-axs10x
F: arch/arc/boot/dts/ax*
F: Documentation/devicetree/bindings/arc/axs10*
SYNOPSYS AXS10x RESET CONTROLLER DRIVER
M: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
S: Supported
F: drivers/reset/reset-axs10x.c
F: Documentation/devicetree/bindings/reset/snps,axs10x-reset.txt
SYNOPSYS DESIGNWARE APB GPIO DRIVER
M: Hoan Tran <hotran@apm.com>
L: linux-gpio@vger.kernel.org
......
......@@ -54,12 +54,14 @@ static const struct of_device_id mtk_tz_smp_boot_infos[] __initconst = {
{ .compatible = "mediatek,mt8135", .data = &mtk_mt8135_tz_boot },
{ .compatible = "mediatek,mt8127", .data = &mtk_mt8135_tz_boot },
{ .compatible = "mediatek,mt2701", .data = &mtk_mt8135_tz_boot },
{},
};
static const struct of_device_id mtk_smp_boot_infos[] __initconst = {
{ .compatible = "mediatek,mt6589", .data = &mtk_mt6589_boot },
{ .compatible = "mediatek,mt7623", .data = &mtk_mt7623_boot },
{ .compatible = "mediatek,mt7623a", .data = &mtk_mt7623_boot },
{},
};
static void __iomem *mtk_smp_base;
......
......@@ -91,12 +91,13 @@ config ARCH_HISI
This enables support for Hisilicon ARMv8 SoC family
config ARCH_MEDIATEK
bool "Mediatek MT65xx & MT81xx ARMv8 SoC"
bool "MediaTek SoC Family"
select ARM_GIC
select PINCTRL
select MTK_TIMER
help
Support for Mediatek MT65xx & MT81xx ARMv8 SoCs
This enables support for MediaTek MT27xx, MT65xx, MT76xx
& MT81xx ARMv8 SoCs
config ARCH_MESON
bool "Amlogic Platforms"
......
......@@ -165,6 +165,14 @@ config TI_SYSC
Generic driver for Texas Instruments interconnect target module
found on many TI SoCs.
config TS_NBUS
tristate "Technologic Systems NBUS Driver"
depends on SOC_IMX28
depends on OF_GPIO && PWM
help
Driver for the Technologic Systems NBUS which is used to interface
with the peripherals in the FPGA of the TS-4600 SoM.
config UNIPHIER_SYSTEM_BUS
tristate "UniPhier System Bus driver"
depends on ARCH_UNIPHIER && OF
......
......@@ -22,6 +22,7 @@ obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o
obj-$(CONFIG_TEGRA_GMI) += tegra-gmi.o
obj-$(CONFIG_TI_SYSC) += ti-sysc.o
obj-$(CONFIG_TS_NBUS) += ts-nbus.o
obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
......
/*
* NBUS driver for TS-4600 based boards
*
* Copyright (c) 2016 - Savoir-faire Linux
* Author: Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
* This driver implements a GPIOs bit-banged bus, called the NBUS by Technologic
* Systems. It is used to communicate with the peripherals in the FPGA on the
* TS-4600 SoM.
*/
#include <linux/bitops.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/ts-nbus.h>
#define TS_NBUS_DIRECTION_IN 0
#define TS_NBUS_DIRECTION_OUT 1
#define TS_NBUS_WRITE_ADR 0
#define TS_NBUS_WRITE_VAL 1
struct ts_nbus {
struct pwm_device *pwm;
struct gpio_descs *data;
struct gpio_desc *csn;
struct gpio_desc *txrx;
struct gpio_desc *strobe;
struct gpio_desc *ale;
struct gpio_desc *rdy;
struct mutex lock;
};
/*
* request all gpios required by the bus.
*/
static int ts_nbus_init_pdata(struct platform_device *pdev, struct ts_nbus
*ts_nbus)
{
ts_nbus->data = devm_gpiod_get_array(&pdev->dev, "ts,data",
GPIOD_OUT_HIGH);
if (IS_ERR(ts_nbus->data)) {
dev_err(&pdev->dev, "failed to retrieve ts,data-gpio from dts\n");
return PTR_ERR(ts_nbus->data);
}
ts_nbus->csn = devm_gpiod_get(&pdev->dev, "ts,csn", GPIOD_OUT_HIGH);
if (IS_ERR(ts_nbus->csn)) {
dev_err(&pdev->dev, "failed to retrieve ts,csn-gpio from dts\n");
return PTR_ERR(ts_nbus->csn);
}
ts_nbus->txrx = devm_gpiod_get(&pdev->dev, "ts,txrx", GPIOD_OUT_HIGH);
if (IS_ERR(ts_nbus->txrx)) {
dev_err(&pdev->dev, "failed to retrieve ts,txrx-gpio from dts\n");
return PTR_ERR(ts_nbus->txrx);
}
ts_nbus->strobe = devm_gpiod_get(&pdev->dev, "ts,strobe", GPIOD_OUT_HIGH);
if (IS_ERR(ts_nbus->strobe)) {
dev_err(&pdev->dev, "failed to retrieve ts,strobe-gpio from dts\n");
return PTR_ERR(ts_nbus->strobe);
}
ts_nbus->ale = devm_gpiod_get(&pdev->dev, "ts,ale", GPIOD_OUT_HIGH);
if (IS_ERR(ts_nbus->ale)) {
dev_err(&pdev->dev, "failed to retrieve ts,ale-gpio from dts\n");
return PTR_ERR(ts_nbus->ale);
}
ts_nbus->rdy = devm_gpiod_get(&pdev->dev, "ts,rdy", GPIOD_IN);
if (IS_ERR(ts_nbus->rdy)) {
dev_err(&pdev->dev, "failed to retrieve ts,rdy-gpio from dts\n");
return PTR_ERR(ts_nbus->rdy);
}
return 0;
}
/*
* the data gpios are used for reading and writing values, their directions
* should be adjusted accordingly.
*/
static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
{
int i;
for (i = 0; i < 8; i++) {
if (direction == TS_NBUS_DIRECTION_IN)
gpiod_direction_input(ts_nbus->data->desc[i]);
else
/* when used as output the default state of the data
* lines are set to high */
gpiod_direction_output(ts_nbus->data->desc[i], 1);
}
}
/*
* reset the bus in its initial state.
* The data, csn, strobe and ale lines must be zero'ed to let the FPGA knows a
* new transaction can be process.
*/
static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
int i;
int values[8];
for (i = 0; i < 8; i++)
values[i] = 0;
gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
}
/*
* let the FPGA knows it can process.
*/
static void ts_nbus_start_transaction(struct ts_nbus *ts_nbus)
{
gpiod_set_value_cansleep(ts_nbus->strobe, 1);
}
/*
* read a byte value from the data gpios.
* return 0 on success or negative errno on failure.
*/
static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
{
struct gpio_descs *gpios = ts_nbus->data;
int ret, i;
*val = 0;
for (i = 0; i < 8; i++) {
ret = gpiod_get_value_cansleep(gpios->desc[i]);
if (ret < 0)
return ret;
if (ret)
*val |= BIT(i);
}
return 0;
}
/*
* set the data gpios accordingly to the byte value.
*/
static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
{
struct gpio_descs *gpios = ts_nbus->data;
int i;
int values[8];
for (i = 0; i < 8; i++)
if (byte & BIT(i))
values[i] = 1;
else
values[i] = 0;
gpiod_set_array_value_cansleep(8, gpios->desc, values);
}
/*
* reading the bus consists of resetting the bus, then notifying the FPGA to
* send the data in the data gpios and return the read value.
* return 0 on success or negative errno on failure.
*/
static int ts_nbus_read_bus(struct ts_nbus *ts_nbus, u8 *val)
{
ts_nbus_reset_bus(ts_nbus);
ts_nbus_start_transaction(ts_nbus);
return ts_nbus_read_byte(ts_nbus, val);
}
/*
* writing to the bus consists of resetting the bus, then define the type of
* command (address/value), write the data and notify the FPGA to retrieve the
* value in the data gpios.
*/
static void ts_nbus_write_bus(struct ts_nbus *ts_nbus, int cmd, u8 val)
{
ts_nbus_reset_bus(ts_nbus);
if (cmd == TS_NBUS_WRITE_ADR)
gpiod_set_value_cansleep(ts_nbus->ale, 1);
ts_nbus_write_byte(ts_nbus, val);
ts_nbus_start_transaction(ts_nbus);
}
/*
* read the value in the FPGA register at the given address.
* return 0 on success or negative errno on failure.
*/
int ts_nbus_read(struct ts_nbus *ts_nbus, u8 adr, u16 *val)
{
int ret, i;
u8 byte;
/* bus access must be atomic */
mutex_lock(&ts_nbus->lock);
/* set the bus in read mode */
gpiod_set_value_cansleep(ts_nbus->txrx, 0);
/* write address */
ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr);
/* set the data gpios direction as input before reading */
ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_IN);
/* reading value MSB first */
do {
*val = 0;
byte = 0;
for (i = 1; i >= 0; i--) {
/* read a byte from the bus, leave on error */
ret = ts_nbus_read_bus(ts_nbus, &byte);
if (ret < 0)
goto err;
/* append the byte read to the final value */
*val |= byte << (i * 8);
}
gpiod_set_value_cansleep(ts_nbus->csn, 1);
ret = gpiod_get_value_cansleep(ts_nbus->rdy);
} while (ret);
err:
/* restore the data gpios direction as output after reading */
ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_OUT);
mutex_unlock(&ts_nbus->lock);
return ret;
}
EXPORT_SYMBOL_GPL(ts_nbus_read);
/*
* write the desired value in the FPGA register at the given address.
*/
int ts_nbus_write(struct ts_nbus *ts_nbus, u8 adr, u16 val)
{
int i;
/* bus access must be atomic */
mutex_lock(&ts_nbus->lock);
/* set the bus in write mode */
gpiod_set_value_cansleep(ts_nbus->txrx, 1);
/* write address */
ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr);
/* writing value MSB first */
for (i = 1; i >= 0; i--)
ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_VAL, (u8)(val >> (i * 8)));
/* wait for completion */
gpiod_set_value_cansleep(ts_nbus->csn, 1);
while (gpiod_get_value_cansleep(ts_nbus->rdy) != 0) {
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->csn, 1);
}
mutex_unlock(&ts_nbus->lock);
return 0;
}
EXPORT_SYMBOL_GPL(ts_nbus_write);
static int ts_nbus_probe(struct platform_device *pdev)
{
struct pwm_device *pwm;
struct pwm_args pargs;
struct device *dev = &pdev->dev;
struct ts_nbus *ts_nbus;
int ret;
ts_nbus = devm_kzalloc(dev, sizeof(*ts_nbus), GFP_KERNEL);
if (!ts_nbus)
return -ENOMEM;
mutex_init(&ts_nbus->lock);
ret = ts_nbus_init_pdata(pdev, ts_nbus);
if (ret < 0)
return ret;
pwm = devm_pwm_get(dev, NULL);
if (IS_ERR(pwm)) {
ret = PTR_ERR(pwm);
if (ret != -EPROBE_DEFER)
dev_err(dev, "unable to request PWM\n");
return ret;
}
pwm_get_args(pwm, &pargs);
if (!pargs.period) {
dev_err(&pdev->dev, "invalid PWM period\n");
return -EINVAL;
}
/*
* FIXME: pwm_apply_args() should be removed when switching to
* the atomic PWM API.
*/
pwm_apply_args(pwm);
ret = pwm_config(pwm, pargs.period, pargs.period);
if (ret < 0)
return ret;
/*
* we can now start the FPGA and populate the peripherals.
*/
pwm_enable(pwm);
ts_nbus->pwm = pwm;
/*
* let the child nodes retrieve this instance of the ts-nbus.
*/
dev_set_drvdata(dev, ts_nbus);
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
if (ret < 0)
return ret;
dev_info(dev, "initialized\n");
return 0;
}
static int ts_nbus_remove(struct platform_device *pdev)
{
struct ts_nbus *ts_nbus = dev_get_drvdata(&pdev->dev);
/* shutdown the FPGA */
mutex_lock(&ts_nbus->lock);
pwm_disable(ts_nbus->pwm);
mutex_unlock(&ts_nbus->lock);
return 0;
}
static const struct of_device_id ts_nbus_of_match[] = {
{ .compatible = "technologic,ts-nbus", },
{ },
};
MODULE_DEVICE_TABLE(of, ts_nbus_of_match);
static struct platform_driver ts_nbus_driver = {
.probe = ts_nbus_probe,
.remove = ts_nbus_remove,
.driver = {
.name = "ts_nbus",
.of_match_table = ts_nbus_of_match,
},
};
module_platform_driver(ts_nbus_driver);
MODULE_ALIAS("platform:ts_nbus");
MODULE_AUTHOR("Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>");
MODULE_DESCRIPTION("Technologic Systems NBUS");
MODULE_LICENSE("GPL v2");
......@@ -30,6 +30,15 @@ config CLK_BCM_CYGNUS
help
Enable common clock framework support for the Broadcom Cygnus SoC
config CLK_BCM_HR2
bool "Broadcom Hurricane 2 clock support"
depends on ARCH_BCM_HR2 || COMPILE_TEST
select COMMON_CLK_IPROC
default ARCH_BCM_HR2
help
Enable common clock framework support for the Broadcom Hurricane 2
SoC
config CLK_BCM_NSP
bool "Broadcom Northstar/Northstar Plus clock support"
depends on ARCH_BCM_5301X || ARCH_BCM_NSP || COMPILE_TEST
......
......@@ -9,6 +9,7 @@ obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o
obj-$(CONFIG_ARCH_BCM_53573) += clk-bcm53573-ilp.o
obj-$(CONFIG_CLK_BCM_CYGNUS) += clk-cygnus.o
obj-$(CONFIG_CLK_BCM_HR2) += clk-hr2.o
obj-$(CONFIG_CLK_BCM_NSP) += clk-nsp.o
obj-$(CONFIG_CLK_BCM_NS2) += clk-ns2.o
obj-$(CONFIG_CLK_BCM_SR) += clk-sr.o
/*
* Copyright (C) 2017 Broadcom
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "clk-iproc.h"
static void __init hr2_armpll_init(struct device_node *node)
{
iproc_armpll_setup(node);
}
CLK_OF_DECLARE(hr2_armpll, "brcm,hr2-armpll", hr2_armpll_init);
......@@ -215,6 +215,17 @@ config QCOM_SCM_64
def_bool y
depends on QCOM_SCM && ARM64
config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
bool "Qualcomm download mode enabled by default"
depends on QCOM_SCM
help
A device with "download mode" enabled will upon an unexpected
warm-restart enter a special debug mode that allows the user to
"download" memory content over USB for offline postmortem analysis.
The feature can be enabled/disabled on the kernel command line.
Say Y here to enable "download mode" by default.
config TI_SCI_PROTOCOL
tristate "TI System Control Interface (TISCI) Message Protocol"
depends on TI_MESSAGE_MANAGER
......
......@@ -28,6 +28,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitmap.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
......@@ -72,21 +73,13 @@
#define MAX_DVFS_DOMAINS 8
#define MAX_DVFS_OPPS 16
#define DVFS_LATENCY(hdr) (le32_to_cpu(hdr) >> 16)
#define DVFS_OPP_COUNT(hdr) ((le32_to_cpu(hdr) >> 8) & 0xff)
#define PROTOCOL_REV_MINOR_BITS 16
#define PROTOCOL_REV_MINOR_MASK ((1U << PROTOCOL_REV_MINOR_BITS) - 1)
#define PROTOCOL_REV_MAJOR(x) ((x) >> PROTOCOL_REV_MINOR_BITS)
#define PROTOCOL_REV_MINOR(x) ((x) & PROTOCOL_REV_MINOR_MASK)
#define FW_REV_MAJOR_BITS 24
#define FW_REV_MINOR_BITS 16
#define FW_REV_PATCH_MASK ((1U << FW_REV_MINOR_BITS) - 1)
#define FW_REV_MINOR_MASK ((1U << FW_REV_MAJOR_BITS) - 1)
#define FW_REV_MAJOR(x) ((x) >> FW_REV_MAJOR_BITS)
#define FW_REV_MINOR(x) (((x) & FW_REV_MINOR_MASK) >> FW_REV_MINOR_BITS)
#define FW_REV_PATCH(x) ((x) & FW_REV_PATCH_MASK)
#define PROTO_REV_MAJOR_MASK GENMASK(31, 16)
#define PROTO_REV_MINOR_MASK GENMASK(15, 0)
#define FW_REV_MAJOR_MASK GENMASK(31, 24)
#define FW_REV_MINOR_MASK GENMASK(23, 16)
#define FW_REV_PATCH_MASK GENMASK(15, 0)
#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
......@@ -311,10 +304,6 @@ struct clk_get_info {
u8 name[20];
} __packed;
struct clk_get_value {
__le32 rate;
} __packed;
struct clk_set_value {
__le16 id;
__le16 reserved;
......@@ -328,7 +317,9 @@ struct legacy_clk_set_value {
} __packed;
struct dvfs_info {
__le32 header;
u8 domain;
u8 opp_count;
__le16 latency;
struct {
__le32 freq;
__le32 m_volt;
......@@ -351,11 +342,6 @@ struct _scpi_sensor_info {
char name[20];
};
struct sensor_value {
__le32 lo_val;
__le32 hi_val;
} __packed;
struct dev_pstate_set {
__le16 dev_id;
u8 pstate;
......@@ -419,19 +405,20 @@ static void scpi_process_cmd(struct scpi_chan *ch, u32 cmd)
unsigned int len;
if (scpi_info->is_legacy) {
struct legacy_scpi_shared_mem *mem = ch->rx_payload;
struct legacy_scpi_shared_mem __iomem *mem =
ch->rx_payload;
/* RX Length is not replied by the legacy Firmware */
len = match->rx_len;
match->status = le32_to_cpu(mem->status);
match->status = ioread32(&mem->status);
memcpy_fromio(match->rx_buf, mem->payload, len);
} else {
struct scpi_shared_mem *mem = ch->rx_payload;
struct scpi_shared_mem __iomem *mem = ch->rx_payload;
len = min(match->rx_len, CMD_SIZE(cmd));
match->status = le32_to_cpu(mem->status);
match->status = ioread32(&mem->status);
memcpy_fromio(match->rx_buf, mem->payload, len);
}
......@@ -445,11 +432,11 @@ static void scpi_process_cmd(struct scpi_chan *ch, u32 cmd)
static void scpi_handle_remote_msg(struct mbox_client *c, void *msg)
{
struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
struct scpi_shared_mem *mem = ch->rx_payload;
struct scpi_shared_mem __iomem *mem = ch->rx_payload;
u32 cmd = 0;
if (!scpi_info->is_legacy)
cmd = le32_to_cpu(mem->command);
cmd = ioread32(&mem->command);
scpi_process_cmd(ch, cmd);
}
......@@ -459,7 +446,7 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
unsigned long flags;
struct scpi_xfer *t = msg;
struct scpi_chan *ch = container_of(c, struct scpi_chan, cl);
struct scpi_shared_mem *mem = (struct scpi_shared_mem *)ch->tx_payload;
struct scpi_shared_mem __iomem *mem = ch->tx_payload;
if (t->tx_buf) {
if (scpi_info->is_legacy)
......@@ -478,7 +465,7 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
}
if (!scpi_info->is_legacy)
mem->command = cpu_to_le32(t->cmd);
iowrite32(t->cmd, &mem->command);
}
static struct scpi_xfer *get_scpi_xfer(struct scpi_chan *ch)
......@@ -583,13 +570,13 @@ scpi_clk_get_range(u16 clk_id, unsigned long *min, unsigned long *max)
static unsigned long scpi_clk_get_val(u16 clk_id)
{
int ret;
struct clk_get_value clk;
__le32 rate;
__le16 le_clk_id = cpu_to_le16(clk_id);
ret = scpi_send_message(CMD_GET_CLOCK_VALUE, &le_clk_id,
sizeof(le_clk_id), &clk, sizeof(clk));
sizeof(le_clk_id), &rate, sizeof(rate));
return ret ? ret : le32_to_cpu(clk.rate);
return ret ? ret : le32_to_cpu(rate);
}
static int scpi_clk_set_val(u16 clk_id, unsigned long rate)
......@@ -644,35 +631,35 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
}
static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
{
if (domain >= MAX_DVFS_DOMAINS)
return ERR_PTR(-EINVAL);
return scpi_info->dvfs[domain] ?: ERR_PTR(-EINVAL);
}
static int scpi_dvfs_populate_info(struct device *dev, u8 domain)
{
struct scpi_dvfs_info *info;
struct scpi_opp *opp;
struct dvfs_info buf;
int ret, i;
if (domain >= MAX_DVFS_DOMAINS)
return ERR_PTR(-EINVAL);
if (scpi_info->dvfs[domain]) /* data already populated */
return scpi_info->dvfs[domain];
ret = scpi_send_message(CMD_GET_DVFS_INFO, &domain, sizeof(domain),
&buf, sizeof(buf));
if (ret)
return ERR_PTR(ret);
return ret;
info = kmalloc(sizeof(*info), GFP_KERNEL);
info = devm_kmalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info)
return ERR_PTR(-ENOMEM);
return -ENOMEM;
info->count = DVFS_OPP_COUNT(buf.header);
info->latency = DVFS_LATENCY(buf.header) * 1000; /* uS to nS */
info->count = buf.opp_count;
info->latency = le16_to_cpu(buf.latency) * 1000; /* uS to nS */
info->opps = kcalloc(info->count, sizeof(*opp), GFP_KERNEL);
if (!info->opps) {
kfree(info);
return ERR_PTR(-ENOMEM);
}
info->opps = devm_kcalloc(dev, info->count, sizeof(*opp), GFP_KERNEL);
if (!info->opps)
return -ENOMEM;
for (i = 0, opp = info->opps; i < info->count; i++, opp++) {
opp->freq = le32_to_cpu(buf.opps[i].freq);
......@@ -682,7 +669,15 @@ static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
sort(info->opps, info->count, sizeof(*opp), opp_cmp_func, NULL);
scpi_info->dvfs[domain] = info;
return info;
return 0;
}
static void scpi_dvfs_populate(struct device *dev)
{
int domain;
for (domain = 0; domain < MAX_DVFS_DOMAINS; domain++)
scpi_dvfs_populate_info(dev, domain);
}
static int scpi_dev_domain_id(struct device *dev)
......@@ -713,9 +708,6 @@ static int scpi_dvfs_get_transition_latency(struct device *dev)
if (IS_ERR(info))
return PTR_ERR(info);
if (!info->latency)
return 0;
return info->latency;
}
......@@ -776,20 +768,19 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info)
static int scpi_sensor_get_value(u16 sensor, u64 *val)
{
__le16 id = cpu_to_le16(sensor);
struct sensor_value buf;
__le64 value;
int ret;
ret = scpi_send_message(CMD_SENSOR_VALUE, &id, sizeof(id),
&buf, sizeof(buf));
&value, sizeof(value));
if (ret)
return ret;
if (scpi_info->is_legacy)
/* only 32-bits supported, hi_val can be junk */
*val = le32_to_cpu(buf.lo_val);
/* only 32-bits supported, upper 32 bits can be junk */
*val = le32_to_cpup((__le32 *)&value);
else
*val = (u64)le32_to_cpu(buf.hi_val) << 32 |
le32_to_cpu(buf.lo_val);
*val = le64_to_cpu(value);
return 0;
}
......@@ -862,23 +853,19 @@ static int scpi_init_versions(struct scpi_drvinfo *info)
static ssize_t protocol_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scpi_drvinfo *scpi_info = dev_get_drvdata(dev);
return sprintf(buf, "%d.%d\n",
PROTOCOL_REV_MAJOR(scpi_info->protocol_version),
PROTOCOL_REV_MINOR(scpi_info->protocol_version));
return sprintf(buf, "%lu.%lu\n",
FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version));
}
static DEVICE_ATTR_RO(protocol_version);
static ssize_t firmware_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct scpi_drvinfo *scpi_info = dev_get_drvdata(dev);
return sprintf(buf, "%d.%d.%d\n",
FW_REV_MAJOR(scpi_info->firmware_version),
FW_REV_MINOR(scpi_info->firmware_version),
FW_REV_PATCH(scpi_info->firmware_version));
return sprintf(buf, "%lu.%lu.%lu\n",
FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
}
static DEVICE_ATTR_RO(firmware_version);
......@@ -889,39 +876,13 @@ static struct attribute *versions_attrs[] = {
};
ATTRIBUTE_GROUPS(versions);
static void
scpi_free_channels(struct device *dev, struct scpi_chan *pchan, int count)
static void scpi_free_channels(void *data)
{
struct scpi_drvinfo *info = data;
int i;
for (i = 0; i < count && pchan->chan; i++, pchan++) {
mbox_free_channel(pchan->chan);
devm_kfree(dev, pchan->xfers);
devm_iounmap(dev, pchan->rx_payload);
}
}
static int scpi_remove(struct platform_device *pdev)
{
int i;
struct device *dev = &pdev->dev;
struct scpi_drvinfo *info = platform_get_drvdata(pdev);
scpi_info = NULL; /* stop exporting SCPI ops through get_scpi_ops */
of_platform_depopulate(dev);
sysfs_remove_groups(&dev->kobj, versions_groups);
scpi_free_channels(dev, info->channels, info->num_chans);
platform_set_drvdata(pdev, NULL);
for (i = 0; i < MAX_DVFS_DOMAINS && info->dvfs[i]; i++) {
kfree(info->dvfs[i]->opps);
kfree(info->dvfs[i]);
}
devm_kfree(dev, info->channels);
devm_kfree(dev, info);
return 0;
for (i = 0; i < info->num_chans; i++)
mbox_free_channel(info->channels[i].chan);
}
#define MAX_SCPI_XFERS 10
......@@ -952,7 +913,6 @@ static int scpi_probe(struct platform_device *pdev)
{
int count, idx, ret;
struct resource res;
struct scpi_chan *scpi_chan;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
......@@ -969,13 +929,19 @@ static int scpi_probe(struct platform_device *pdev)
return -ENODEV;
}
scpi_chan = devm_kcalloc(dev, count, sizeof(*scpi_chan), GFP_KERNEL);
if (!scpi_chan)
scpi_info->channels = devm_kcalloc(dev, count, sizeof(struct scpi_chan),
GFP_KERNEL);
if (!scpi_info->channels)
return -ENOMEM;
for (idx = 0; idx < count; idx++) {
ret = devm_add_action(dev, scpi_free_channels, scpi_info);
if (ret)
return ret;
for (; scpi_info->num_chans < count; scpi_info->num_chans++) {
resource_size_t size;
struct scpi_chan *pchan = scpi_chan + idx;
int idx = scpi_info->num_chans;
struct scpi_chan *pchan = scpi_info->channels + idx;
struct mbox_client *cl = &pchan->cl;
struct device_node *shmem = of_parse_phandle(np, "shmem", idx);
......@@ -983,15 +949,14 @@ static int scpi_probe(struct platform_device *pdev)
of_node_put(shmem);
if (ret) {
dev_err(dev, "failed to get SCPI payload mem resource\n");
goto err;
return ret;
}
size = resource_size(&res);
pchan->rx_payload = devm_ioremap(dev, res.start, size);
if (!pchan->rx_payload) {
dev_err(dev, "failed to ioremap SCPI payload\n");
ret = -EADDRNOTAVAIL;
goto err;
return -EADDRNOTAVAIL;
}
pchan->tx_payload = pchan->rx_payload + (size >> 1);
......@@ -1017,17 +982,11 @@ static int scpi_probe(struct platform_device *pdev)
dev_err(dev, "failed to get channel%d err %d\n",
idx, ret);
}
err:
scpi_free_channels(dev, scpi_chan, idx);
scpi_info = NULL;
return ret;
}
scpi_info->channels = scpi_chan;
scpi_info->num_chans = count;
scpi_info->commands = scpi_std_commands;
platform_set_drvdata(pdev, scpi_info);
scpi_info->scpi_ops = &scpi_ops;
if (scpi_info->is_legacy) {
/* Replace with legacy variants */
......@@ -1043,23 +1002,23 @@ static int scpi_probe(struct platform_device *pdev)
ret = scpi_init_versions(scpi_info);
if (ret) {
dev_err(dev, "incorrect or no SCP firmware found\n");
scpi_remove(pdev);
return ret;
}
_dev_info(dev, "SCP Protocol %d.%d Firmware %d.%d.%d version\n",
PROTOCOL_REV_MAJOR(scpi_info->protocol_version),
PROTOCOL_REV_MINOR(scpi_info->protocol_version),
FW_REV_MAJOR(scpi_info->firmware_version),
FW_REV_MINOR(scpi_info->firmware_version),
FW_REV_PATCH(scpi_info->firmware_version));
scpi_info->scpi_ops = &scpi_ops;
scpi_dvfs_populate(dev);
_dev_info(dev, "SCP Protocol %lu.%lu Firmware %lu.%lu.%lu version\n",
FIELD_GET(PROTO_REV_MAJOR_MASK, scpi_info->protocol_version),
FIELD_GET(PROTO_REV_MINOR_MASK, scpi_info->protocol_version),
FIELD_GET(FW_REV_MAJOR_MASK, scpi_info->firmware_version),
FIELD_GET(FW_REV_MINOR_MASK, scpi_info->firmware_version),
FIELD_GET(FW_REV_PATCH_MASK, scpi_info->firmware_version));
ret = sysfs_create_groups(&dev->kobj, versions_groups);
ret = devm_device_add_groups(dev, versions_groups);
if (ret)
dev_err(dev, "unable to create sysfs version group\n");
return of_platform_populate(dev->of_node, NULL, NULL, dev);
return devm_of_platform_populate(dev);
}
static const struct of_device_id scpi_of_match[] = {
......@@ -1076,7 +1035,6 @@ static struct platform_driver scpi_driver = {
.of_match_table = scpi_of_match,
},
.probe = scpi_probe,
.remove = scpi_remove,
};
module_platform_driver(scpi_driver);
......
......@@ -340,6 +340,7 @@ static int suspend_test_thread(void *arg)
* later.
*/
del_timer(&wakeup_timer);
destroy_timer_on_stack(&wakeup_timer);
if (atomic_dec_return_relaxed(&nb_active_threads) == 0)
complete(&suspend_threads_done);
......
......@@ -561,6 +561,12 @@ int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
return ret ? : le32_to_cpu(out);
}
int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
{
return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE,
enable ? QCOM_SCM_SET_DLOAD_MODE : 0, 0);
}
int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id)
{
struct {
......@@ -596,3 +602,21 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size,
{
return -ENODEV;
}
int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
unsigned int *val)
{
int ret;
ret = qcom_scm_call_atomic1(QCOM_SCM_SVC_IO, QCOM_SCM_IO_READ, addr);
if (ret >= 0)
*val = ret;
return ret < 0 ? ret : 0;
}
int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val)
{
return qcom_scm_call_atomic2(QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE,
addr, val);
}
......@@ -439,3 +439,47 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size,
return ret;
}
int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
{
struct qcom_scm_desc desc = {0};
struct arm_smccc_res res;
desc.args[0] = QCOM_SCM_SET_DLOAD_MODE;
desc.args[1] = enable ? QCOM_SCM_SET_DLOAD_MODE : 0;
desc.arginfo = QCOM_SCM_ARGS(2);
return qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_SET_DLOAD_MODE,
&desc, &res);
}
int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr,
unsigned int *val)
{
struct qcom_scm_desc desc = {0};
struct arm_smccc_res res;
int ret;
desc.args[0] = addr;
desc.arginfo = QCOM_SCM_ARGS(1);
ret = qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_READ,
&desc, &res);
if (ret >= 0)
*val = res.a1;
return ret < 0 ? ret : 0;
}
int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val)
{
struct qcom_scm_desc desc = {0};
struct arm_smccc_res res;
desc.args[0] = addr;
desc.args[1] = val;
desc.arginfo = QCOM_SCM_ARGS(2);
return qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE,
&desc, &res);
}
......@@ -19,15 +19,20 @@
#include <linux/cpumask.h>
#include <linux/export.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/qcom_scm.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <linux/reset-controller.h>
#include "qcom_scm.h"
static bool download_mode = IS_ENABLED(CONFIG_QCOM_SCM_DOWNLOAD_MODE_DEFAULT);
module_param(download_mode, bool, 0);
#define SCM_HAS_CORE_CLK BIT(0)
#define SCM_HAS_IFACE_CLK BIT(1)
#define SCM_HAS_BUS_CLK BIT(2)
......@@ -38,6 +43,8 @@ struct qcom_scm {
struct clk *iface_clk;
struct clk *bus_clk;
struct reset_controller_dev reset;
u64 dload_mode_addr;
};
static struct qcom_scm *__scm;
......@@ -333,6 +340,66 @@ int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare)
}
EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init);
int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val)
{
return __qcom_scm_io_readl(__scm->dev, addr, val);
}
EXPORT_SYMBOL(qcom_scm_io_readl);
int qcom_scm_io_writel(phys_addr_t addr, unsigned int val)
{
return __qcom_scm_io_writel(__scm->dev, addr, val);
}
EXPORT_SYMBOL(qcom_scm_io_writel);
static void qcom_scm_set_download_mode(bool enable)
{
bool avail;
int ret = 0;
avail = __qcom_scm_is_call_available(__scm->dev,
QCOM_SCM_SVC_BOOT,
QCOM_SCM_SET_DLOAD_MODE);
if (avail) {
ret = __qcom_scm_set_dload_mode(__scm->dev, enable);
} else if (__scm->dload_mode_addr) {
ret = __qcom_scm_io_writel(__scm->dev, __scm->dload_mode_addr,
enable ? QCOM_SCM_SET_DLOAD_MODE : 0);
} else {
dev_err(__scm->dev,
"No available mechanism for setting download mode\n");
}
if (ret)
dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
}
static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
{
struct device_node *tcsr;
struct device_node *np = dev->of_node;
struct resource res;
u32 offset;
int ret;
tcsr = of_parse_phandle(np, "qcom,dload-mode", 0);
if (!tcsr)
return 0;
ret = of_address_to_resource(tcsr, 0, &res);
of_node_put(tcsr);
if (ret)
return ret;
ret = of_property_read_u32_index(np, "qcom,dload-mode", 1, &offset);
if (ret < 0)
return ret;
*addr = res.start + offset;
return 0;
}
/**
* qcom_scm_is_available() - Checks if SCM is available
*/
......@@ -358,6 +425,10 @@ static int qcom_scm_probe(struct platform_device *pdev)
if (!scm)
return -ENOMEM;
ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
if (ret < 0)
return ret;
clks = (unsigned long)of_device_get_match_data(&pdev->dev);
if (clks & SCM_HAS_CORE_CLK) {
scm->core_clk = devm_clk_get(&pdev->dev, "core");
......@@ -406,9 +477,24 @@ static int qcom_scm_probe(struct platform_device *pdev)
__qcom_scm_init();
/*
* If requested enable "download mode", from this point on warmboot
* will cause the the boot stages to enter download mode, unless
* disabled below by a clean shutdown/reboot.
*/
if (download_mode)
qcom_scm_set_download_mode(true);
return 0;
}
static void qcom_scm_shutdown(struct platform_device *pdev)
{
/* Clean shutdown, disable download mode to allow normal restart */
if (download_mode)
qcom_scm_set_download_mode(false);
}
static const struct of_device_id qcom_scm_dt_match[] = {
{ .compatible = "qcom,scm-apq8064",
/* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
......@@ -436,6 +522,7 @@ static struct platform_driver qcom_scm_driver = {
.of_match_table = qcom_scm_dt_match,
},
.probe = qcom_scm_probe,
.shutdown = qcom_scm_shutdown,
};
static int __init qcom_scm_init(void)
......
......@@ -14,9 +14,11 @@
#define QCOM_SCM_SVC_BOOT 0x1
#define QCOM_SCM_BOOT_ADDR 0x1
#define QCOM_SCM_SET_DLOAD_MODE 0x10
#define QCOM_SCM_BOOT_ADDR_MC 0x11
#define QCOM_SCM_SET_REMOTE_STATE 0xa
extern int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id);
extern int __qcom_scm_set_dload_mode(struct device *dev, bool enable);
#define QCOM_SCM_FLAG_HLOS 0x01
#define QCOM_SCM_FLAG_COLDBOOT_MC 0x02
......@@ -30,6 +32,12 @@ extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus);
#define QCOM_SCM_CMD_CORE_HOTPLUGGED 0x10
extern void __qcom_scm_cpu_power_down(u32 flags);
#define QCOM_SCM_SVC_IO 0x5
#define QCOM_SCM_IO_READ 0x1
#define QCOM_SCM_IO_WRITE 0x2
extern int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, unsigned int *val);
extern int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val);
#define QCOM_SCM_SVC_INFO 0x6
#define QCOM_IS_CALL_AVAIL_CMD 0x1
extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
......
obj-$(CONFIG_TEGRA_BPMP) += bpmp.o
tegra-bpmp-y = bpmp.o
tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o
obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o
obj-$(CONFIG_TEGRA_IVC) += ivc.o
/*
* Copyright (c) 2017, NVIDIA 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.
*
*/
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/uaccess.h>
#include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h>
struct seqbuf {
char *buf;
size_t pos;
size_t size;
};
static void seqbuf_init(struct seqbuf *seqbuf, void *buf, size_t size)
{
seqbuf->buf = buf;
seqbuf->size = size;
seqbuf->pos = 0;
}
static size_t seqbuf_avail(struct seqbuf *seqbuf)
{
return seqbuf->pos < seqbuf->size ? seqbuf->size - seqbuf->pos : 0;
}
static size_t seqbuf_status(struct seqbuf *seqbuf)
{
return seqbuf->pos <= seqbuf->size ? 0 : -EOVERFLOW;
}
static int seqbuf_eof(struct seqbuf *seqbuf)
{
return seqbuf->pos >= seqbuf->size;
}
static int seqbuf_read(struct seqbuf *seqbuf, void *buf, size_t nbyte)
{
nbyte = min(nbyte, seqbuf_avail(seqbuf));
memcpy(buf, seqbuf->buf + seqbuf->pos, nbyte);
seqbuf->pos += nbyte;
return seqbuf_status(seqbuf);
}
static int seqbuf_read_u32(struct seqbuf *seqbuf, uint32_t *v)
{
int err;
err = seqbuf_read(seqbuf, v, 4);
*v = le32_to_cpu(*v);
return err;
}
static int seqbuf_read_str(struct seqbuf *seqbuf, const char **str)
{
*str = seqbuf->buf + seqbuf->pos;
seqbuf->pos += strnlen(*str, seqbuf_avail(seqbuf));
seqbuf->pos++;
return seqbuf_status(seqbuf);
}
static void seqbuf_seek(struct seqbuf *seqbuf, ssize_t offset)
{
seqbuf->pos += offset;
}
/* map filename in Linux debugfs to corresponding entry in BPMP */
static const char *get_filename(struct tegra_bpmp *bpmp,
const struct file *file, char *buf, int size)
{
char root_path_buf[512];
const char *root_path;
const char *filename;
size_t root_len;
root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,
sizeof(root_path_buf));
if (IS_ERR(root_path))
return NULL;
root_len = strlen(root_path);
filename = dentry_path(file->f_path.dentry, buf, size);
if (IS_ERR(filename))
return NULL;
if (strlen(filename) < root_len ||
strncmp(filename, root_path, root_len))
return NULL;
filename += root_len;
return filename;
}
static int mrq_debugfs_read(struct tegra_bpmp *bpmp,
dma_addr_t name, size_t sz_name,
dma_addr_t data, size_t sz_data,
size_t *nbytes)
{
struct mrq_debugfs_request req = {
.cmd = cpu_to_le32(CMD_DEBUGFS_READ),
.fop = {
.fnameaddr = cpu_to_le32((uint32_t)name),
.fnamelen = cpu_to_le32((uint32_t)sz_name),
.dataaddr = cpu_to_le32((uint32_t)data),
.datalen = cpu_to_le32((uint32_t)sz_data),
},
};
struct mrq_debugfs_response resp;
struct tegra_bpmp_message msg = {
.mrq = MRQ_DEBUGFS,
.tx = {
.data = &req,
.size = sizeof(req),
},
.rx = {
.data = &resp,
.size = sizeof(resp),
},
};
int err;
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
return err;
*nbytes = (size_t)resp.fop.nbytes;
return 0;
}
static int mrq_debugfs_write(struct tegra_bpmp *bpmp,
dma_addr_t name, size_t sz_name,
dma_addr_t data, size_t sz_data)
{
const struct mrq_debugfs_request req = {
.cmd = cpu_to_le32(CMD_DEBUGFS_WRITE),
.fop = {
.fnameaddr = cpu_to_le32((uint32_t)name),
.fnamelen = cpu_to_le32((uint32_t)sz_name),
.dataaddr = cpu_to_le32((uint32_t)data),
.datalen = cpu_to_le32((uint32_t)sz_data),
},
};
struct tegra_bpmp_message msg = {
.mrq = MRQ_DEBUGFS,
.tx = {
.data = &req,
.size = sizeof(req),
},
};
return tegra_bpmp_transfer(bpmp, &msg);
}
static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr,
size_t size, size_t *nbytes)
{
const struct mrq_debugfs_request req = {
.cmd = cpu_to_le32(CMD_DEBUGFS_DUMPDIR),
.dumpdir = {
.dataaddr = cpu_to_le32((uint32_t)addr),
.datalen = cpu_to_le32((uint32_t)size),
},
};
struct mrq_debugfs_response resp;
struct tegra_bpmp_message msg = {
.mrq = MRQ_DEBUGFS,
.tx = {
.data = &req,
.size = sizeof(req),
},
.rx = {
.data = &resp,
.size = sizeof(resp),
},
};
int err;
err = tegra_bpmp_transfer(bpmp, &msg);
if (err < 0)
return err;
*nbytes = (size_t)resp.dumpdir.nbytes;
return 0;
}
static int debugfs_show(struct seq_file *m, void *p)
{
struct file *file = m->private;
struct inode *inode = file_inode(file);
struct tegra_bpmp *bpmp = inode->i_private;
const size_t datasize = m->size;
const size_t namesize = SZ_256;
void *datavirt, *namevirt;
dma_addr_t dataphys, namephys;
char buf[256];
const char *filename;
size_t len, nbytes;
int ret;
filename = get_filename(bpmp, file, buf, sizeof(buf));
if (!filename)
return -ENOENT;
namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,
GFP_KERNEL | GFP_DMA32);
if (!namevirt)
return -ENOMEM;
datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,
GFP_KERNEL | GFP_DMA32);
if (!datavirt) {
ret = -ENOMEM;
goto free_namebuf;
}
len = strlen(filename);
strncpy(namevirt, filename, namesize);
ret = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize,
&nbytes);
if (!ret)
seq_write(m, datavirt, nbytes);
dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);
free_namebuf:
dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
return ret;
}
static int debugfs_open(struct inode *inode, struct file *file)
{
return single_open_size(file, debugfs_show, file, SZ_128K);
}
static ssize_t debugfs_store(struct file *file, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct inode *inode = file_inode(file);
struct tegra_bpmp *bpmp = inode->i_private;
const size_t datasize = count;
const size_t namesize = SZ_256;
void *datavirt, *namevirt;
dma_addr_t dataphys, namephys;
char fnamebuf[256];
const char *filename;
size_t len;
int ret;
filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf));
if (!filename)
return -ENOENT;
namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys,
GFP_KERNEL | GFP_DMA32);
if (!namevirt)
return -ENOMEM;
datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys,
GFP_KERNEL | GFP_DMA32);
if (!datavirt) {
ret = -ENOMEM;
goto free_namebuf;
}
len = strlen(filename);
strncpy(namevirt, filename, namesize);
if (copy_from_user(datavirt, buf, count)) {
ret = -EFAULT;
goto free_databuf;
}
ret = mrq_debugfs_write(bpmp, namephys, len, dataphys,
count);
free_databuf:
dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys);
free_namebuf:
dma_free_coherent(bpmp->dev, namesize, namevirt, namephys);
return ret ?: count;
}
static const struct file_operations debugfs_fops = {
.open = debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.write = debugfs_store,
.release = single_release,
};
static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf,
struct dentry *parent, uint32_t depth)
{
int err;
uint32_t d, t;
const char *name;
struct dentry *dentry;
while (!seqbuf_eof(seqbuf)) {
err = seqbuf_read_u32(seqbuf, &d);
if (err < 0)
return err;
if (d < depth) {
seqbuf_seek(seqbuf, -4);
/* go up a level */
return 0;
} else if (d != depth) {
/* malformed data received from BPMP */
return -EIO;
}
err = seqbuf_read_u32(seqbuf, &t);
if (err < 0)
return err;
err = seqbuf_read_str(seqbuf, &name);
if (err < 0)
return err;
if (t & DEBUGFS_S_ISDIR) {
dentry = debugfs_create_dir(name, parent);
if (!dentry)
return -ENOMEM;
err = bpmp_populate_dir(bpmp, seqbuf, dentry, depth+1);
if (err < 0)
return err;
} else {
umode_t mode;
mode = t & DEBUGFS_S_IRUSR ? S_IRUSR : 0;
mode |= t & DEBUGFS_S_IWUSR ? S_IWUSR : 0;
dentry = debugfs_create_file(name, mode,
parent, bpmp,
&debugfs_fops);
if (!dentry)
return -ENOMEM;
}
}
return 0;
}
static int create_debugfs_mirror(struct tegra_bpmp *bpmp, void *buf,
size_t bufsize, struct dentry *root)
{
struct seqbuf seqbuf;
int err;
bpmp->debugfs_mirror = debugfs_create_dir("debug", root);
if (!bpmp->debugfs_mirror)
return -ENOMEM;
seqbuf_init(&seqbuf, buf, bufsize);
err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0);
if (err < 0) {
debugfs_remove_recursive(bpmp->debugfs_mirror);
bpmp->debugfs_mirror = NULL;
}
return err;
}
static int mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq)
{
struct mrq_query_abi_request req = { .mrq = cpu_to_le32(mrq) };
struct mrq_query_abi_response resp;
struct tegra_bpmp_message msg = {
.mrq = MRQ_QUERY_ABI,
.tx = {
.data = &req,
.size = sizeof(req),
},
.rx = {
.data = &resp,
.size = sizeof(resp),
},
};
int ret;
ret = tegra_bpmp_transfer(bpmp, &msg);
if (ret < 0) {
/* something went wrong; assume not supported */
dev_warn(bpmp->dev, "tegra_bpmp_transfer failed (%d)\n", ret);
return 0;
}
return resp.status ? 0 : 1;
}
int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp)
{
dma_addr_t phys;
void *virt;
const size_t sz = SZ_256K;
size_t nbytes;
int ret;
struct dentry *root;
if (!mrq_is_supported(bpmp, MRQ_DEBUGFS))
return 0;
root = debugfs_create_dir("bpmp", NULL);
if (!root)
return -ENOMEM;
virt = dma_alloc_coherent(bpmp->dev, sz, &phys,
GFP_KERNEL | GFP_DMA32);
if (!virt) {
ret = -ENOMEM;
goto out;
}
ret = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes);
if (ret < 0)
goto free;
ret = create_debugfs_mirror(bpmp, virt, nbytes, root);
free:
dma_free_coherent(bpmp->dev, sz, virt, phys);
out:
if (ret < 0)
debugfs_remove(root);
return ret;
}
......@@ -194,16 +194,24 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel)
}
static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
void *data, size_t size)
void *data, size_t size, int *ret)
{
int err;
if (data && size > 0)
memcpy(data, channel->ib->data, size);
return tegra_ivc_read_advance(channel->ivc);
err = tegra_ivc_read_advance(channel->ivc);
if (err < 0)
return err;
*ret = channel->ib->code;
return 0;
}
static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
void *data, size_t size)
void *data, size_t size, int *ret)
{
struct tegra_bpmp *bpmp = channel->bpmp;
unsigned long flags;
......@@ -217,7 +225,7 @@ static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
}
spin_lock_irqsave(&bpmp->lock, flags);
err = __tegra_bpmp_channel_read(channel, data, size);
err = __tegra_bpmp_channel_read(channel, data, size, ret);
clear_bit(index, bpmp->threaded.allocated);
spin_unlock_irqrestore(&bpmp->lock, flags);
......@@ -337,7 +345,8 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
if (err < 0)
return err;
return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size,
&msg->rx.ret);
}
EXPORT_SYMBOL_GPL(tegra_bpmp_transfer_atomic);
......@@ -371,7 +380,8 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
if (err == 0)
return -ETIMEDOUT;
return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size,
&msg->rx.ret);
}
EXPORT_SYMBOL_GPL(tegra_bpmp_transfer);
......@@ -387,8 +397,8 @@ static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
return NULL;
}
static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel,
int code, const void *data, size_t size)
void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code,
const void *data, size_t size)
{
unsigned long flags = channel->ib->flags;
struct tegra_bpmp *bpmp = channel->bpmp;
......@@ -426,6 +436,7 @@ static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel,
mbox_client_txdone(bpmp->mbox.channel, 0);
}
}
EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_return);
static void tegra_bpmp_handle_mrq(struct tegra_bpmp *bpmp,
unsigned int mrq,
......@@ -824,6 +835,10 @@ static int tegra_bpmp_probe(struct platform_device *pdev)
if (err < 0)
goto free_mrq;
err = tegra_bpmp_init_debugfs(bpmp);
if (err < 0)
dev_err(&pdev->dev, "debugfs initialization failed: %d\n", err);
return 0;
free_mrq:
......
......@@ -439,7 +439,7 @@ static inline int ti_sci_do_xfer(struct ti_sci_info *info,
/* And we wait for the response. */
timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
if (!wait_for_completion_timeout(&xfer->done, timeout)) {
dev_err(dev, "Mbox timedout in resp(caller: %pF)\n",
dev_err(dev, "Mbox timedout in resp(caller: %pS)\n",
(void *)_RET_IP_);
ret = -ETIMEDOUT;
}
......
......@@ -9,6 +9,7 @@ endif
obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o
obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o
obj-$(CONFIG_ARCH_BRCMSTB) += brcmstb_dpfe.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
......
/*
* DDR PHY Front End (DPFE) driver for Broadcom set top box SoCs
*
* Copyright (c) 2017 Broadcom
*
* Released under the GPLv2 only.
* SPDX-License-Identifier: GPL-2.0
*/
/*
* This driver provides access to the DPFE interface of Broadcom STB SoCs.
* The firmware running on the DCPU inside the DDR PHY can provide current
* information about the system's RAM, for instance the DRAM refresh rate.
* This can be used as an indirect indicator for the DRAM's temperature.
* Slower refresh rate means cooler RAM, higher refresh rate means hotter
* RAM.
*
* Throughout the driver, we use readl_relaxed() and writel_relaxed(), which
* already contain the appropriate le32_to_cpu()/cpu_to_le32() calls.
*
* Note regarding the loading of the firmware image: we use be32_to_cpu()
* and le_32_to_cpu(), so we can support the following four cases:
* - LE kernel + LE firmware image (the most common case)
* - LE kernel + BE firmware image
* - BE kernel + LE firmware image
* - BE kernel + BE firmware image
*
* The DPCU always runs in big endian mode. The firwmare image, however, can
* be in either format. Also, communication between host CPU and DCPU is
* always in little endian.
*/
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#define DRVNAME "brcmstb-dpfe"
#define FIRMWARE_NAME "dpfe.bin"
/* DCPU register offsets */
#define REG_DCPU_RESET 0x0
#define REG_TO_DCPU_MBOX 0x10
#define REG_TO_HOST_MBOX 0x14
/* Message RAM */
#define DCPU_MSG_RAM(x) (0x100 + (x) * sizeof(u32))
/* DRAM Info Offsets & Masks */
#define DRAM_INFO_INTERVAL 0x0
#define DRAM_INFO_MR4 0x4
#define DRAM_INFO_ERROR 0x8
#define DRAM_INFO_MR4_MASK 0xff
/* DRAM MR4 Offsets & Masks */
#define DRAM_MR4_REFRESH 0x0 /* Refresh rate */
#define DRAM_MR4_SR_ABORT 0x3 /* Self Refresh Abort */
#define DRAM_MR4_PPRE 0x4 /* Post-package repair entry/exit */
#define DRAM_MR4_TH_OFFS 0x5 /* Thermal Offset; vendor specific */
#define DRAM_MR4_TUF 0x7 /* Temperature Update Flag */
#define DRAM_MR4_REFRESH_MASK 0x7
#define DRAM_MR4_SR_ABORT_MASK 0x1
#define DRAM_MR4_PPRE_MASK 0x1
#define DRAM_MR4_TH_OFFS_MASK 0x3
#define DRAM_MR4_TUF_MASK 0x1
/* DRAM Vendor Offsets & Masks */
#define DRAM_VENDOR_MR5 0x0
#define DRAM_VENDOR_MR6 0x4
#define DRAM_VENDOR_MR7 0x8
#define DRAM_VENDOR_MR8 0xc
#define DRAM_VENDOR_ERROR 0x10
#define DRAM_VENDOR_MASK 0xff
/* Reset register bits & masks */
#define DCPU_RESET_SHIFT 0x0
#define DCPU_RESET_MASK 0x1
#define DCPU_CLK_DISABLE_SHIFT 0x2
/* DCPU return codes */
#define DCPU_RET_ERROR_BIT BIT(31)
#define DCPU_RET_SUCCESS 0x1
#define DCPU_RET_ERR_HEADER (DCPU_RET_ERROR_BIT | BIT(0))
#define DCPU_RET_ERR_INVAL (DCPU_RET_ERROR_BIT | BIT(1))
#define DCPU_RET_ERR_CHKSUM (DCPU_RET_ERROR_BIT | BIT(2))
#define DCPU_RET_ERR_COMMAND (DCPU_RET_ERROR_BIT | BIT(3))
/* This error code is not firmware defined and only used in the driver. */
#define DCPU_RET_ERR_TIMEDOUT (DCPU_RET_ERROR_BIT | BIT(4))
/* Firmware magic */
#define DPFE_BE_MAGIC 0xfe1010fe
#define DPFE_LE_MAGIC 0xfe0101fe
/* Error codes */
#define ERR_INVALID_MAGIC -1
#define ERR_INVALID_SIZE -2
#define ERR_INVALID_CHKSUM -3
/* Message types */
#define DPFE_MSG_TYPE_COMMAND 1
#define DPFE_MSG_TYPE_RESPONSE 2
#define DELAY_LOOP_MAX 200000
enum dpfe_msg_fields {
MSG_HEADER,
MSG_COMMAND,
MSG_ARG_COUNT,
MSG_ARG0,
MSG_CHKSUM,
MSG_FIELD_MAX /* Last entry */
};
enum dpfe_commands {
DPFE_CMD_GET_INFO,
DPFE_CMD_GET_REFRESH,
DPFE_CMD_GET_VENDOR,
DPFE_CMD_MAX /* Last entry */
};
struct dpfe_msg {
u32 header;
u32 command;
u32 arg_count;
u32 arg0;
u32 chksum; /* This is the sum of all other entries. */
};
/*
* Format of the binary firmware file:
*
* entry
* 0 header
* value: 0xfe0101fe <== little endian
* 0xfe1010fe <== big endian
* 1 sequence:
* [31:16] total segments on this build
* [15:0] this segment sequence.
* 2 FW version
* 3 IMEM byte size
* 4 DMEM byte size
* IMEM
* DMEM
* last checksum ==> sum of everything
*/
struct dpfe_firmware_header {
u32 magic;
u32 sequence;
u32 version;
u32 imem_size;
u32 dmem_size;
};
/* Things we only need during initialization. */
struct init_data {
unsigned int dmem_len;
unsigned int imem_len;
unsigned int chksum;
bool is_big_endian;
};
/* Things we need for as long as we are active. */
struct private_data {
void __iomem *regs;
void __iomem *dmem;
void __iomem *imem;
struct device *dev;
unsigned int index;
struct mutex lock;
};
static const char *error_text[] = {
"Success", "Header code incorrect", "Unknown command or argument",
"Incorrect checksum", "Malformed command", "Timed out",
};
/* List of supported firmware commands */
static const u32 dpfe_commands[DPFE_CMD_MAX][MSG_FIELD_MAX] = {
[DPFE_CMD_GET_INFO] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 1,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 1,
[MSG_CHKSUM] = 4,
},
[DPFE_CMD_GET_REFRESH] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 2,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 1,
[MSG_CHKSUM] = 5,
},
[DPFE_CMD_GET_VENDOR] = {
[MSG_HEADER] = DPFE_MSG_TYPE_COMMAND,
[MSG_COMMAND] = 2,
[MSG_ARG_COUNT] = 1,
[MSG_ARG0] = 2,
[MSG_CHKSUM] = 6,
},
};
static bool is_dcpu_enabled(void __iomem *regs)
{
u32 val;
val = readl_relaxed(regs + REG_DCPU_RESET);
return !(val & DCPU_RESET_MASK);
}
static void __disable_dcpu(void __iomem *regs)
{
u32 val;
if (!is_dcpu_enabled(regs))
return;
/* Put DCPU in reset if it's running. */
val = readl_relaxed(regs + REG_DCPU_RESET);
val |= (1 << DCPU_RESET_SHIFT);
writel_relaxed(val, regs + REG_DCPU_RESET);
}
static void __enable_dcpu(void __iomem *regs)
{
u32 val;
/* Clear mailbox registers. */
writel_relaxed(0, regs + REG_TO_DCPU_MBOX);
writel_relaxed(0, regs + REG_TO_HOST_MBOX);
/* Disable DCPU clock gating */
val = readl_relaxed(regs + REG_DCPU_RESET);
val &= ~(1 << DCPU_CLK_DISABLE_SHIFT);
writel_relaxed(val, regs + REG_DCPU_RESET);
/* Take DCPU out of reset */
val = readl_relaxed(regs + REG_DCPU_RESET);
val &= ~(1 << DCPU_RESET_SHIFT);
writel_relaxed(val, regs + REG_DCPU_RESET);
}
static unsigned int get_msg_chksum(const u32 msg[])
{
unsigned int sum = 0;
unsigned int i;
/* Don't include the last field in the checksum. */
for (i = 0; i < MSG_FIELD_MAX - 1; i++)
sum += msg[i];
return sum;
}
static int __send_command(struct private_data *priv, unsigned int cmd,
u32 result[])
{
const u32 *msg = dpfe_commands[cmd];
void __iomem *regs = priv->regs;
unsigned int i, chksum;
int ret = 0;
u32 resp;
if (cmd >= DPFE_CMD_MAX)
return -1;
mutex_lock(&priv->lock);
/* Write command and arguments to message area */
for (i = 0; i < MSG_FIELD_MAX; i++)
writel_relaxed(msg[i], regs + DCPU_MSG_RAM(i));
/* Tell DCPU there is a command waiting */
writel_relaxed(1, regs + REG_TO_DCPU_MBOX);
/* Wait for DCPU to process the command */
for (i = 0; i < DELAY_LOOP_MAX; i++) {
/* Read response code */
resp = readl_relaxed(regs + REG_TO_HOST_MBOX);
if (resp > 0)
break;
udelay(5);
}
if (i == DELAY_LOOP_MAX) {
resp = (DCPU_RET_ERR_TIMEDOUT & ~DCPU_RET_ERROR_BIT);
ret = -ffs(resp);
} else {
/* Read response data */
for (i = 0; i < MSG_FIELD_MAX; i++)
result[i] = readl_relaxed(regs + DCPU_MSG_RAM(i));
}
/* Tell DCPU we are done */
writel_relaxed(0, regs + REG_TO_HOST_MBOX);
mutex_unlock(&priv->lock);
if (ret)
return ret;
/* Verify response */
chksum = get_msg_chksum(result);
if (chksum != result[MSG_CHKSUM])
resp = DCPU_RET_ERR_CHKSUM;
if (resp != DCPU_RET_SUCCESS) {
resp &= ~DCPU_RET_ERROR_BIT;
ret = -ffs(resp);
}
return ret;
}
/* Ensure that the firmware file loaded meets all the requirements. */
static int __verify_firmware(struct init_data *init,
const struct firmware *fw)
{
const struct dpfe_firmware_header *header = (void *)fw->data;
unsigned int dmem_size, imem_size, total_size;
bool is_big_endian = false;
const u32 *chksum_ptr;
if (header->magic == DPFE_BE_MAGIC)
is_big_endian = true;
else if (header->magic != DPFE_LE_MAGIC)
return ERR_INVALID_MAGIC;
if (is_big_endian) {
dmem_size = be32_to_cpu(header->dmem_size);
imem_size = be32_to_cpu(header->imem_size);
} else {
dmem_size = le32_to_cpu(header->dmem_size);
imem_size = le32_to_cpu(header->imem_size);
}
/* Data and instruction sections are 32 bit words. */
if ((dmem_size % sizeof(u32)) != 0 || (imem_size % sizeof(u32)) != 0)
return ERR_INVALID_SIZE;
/*
* The header + the data section + the instruction section + the
* checksum must be equal to the total firmware size.
*/
total_size = dmem_size + imem_size + sizeof(*header) +
sizeof(*chksum_ptr);
if (total_size != fw->size)
return ERR_INVALID_SIZE;
/* The checksum comes at the very end. */
chksum_ptr = (void *)fw->data + sizeof(*header) + dmem_size + imem_size;
init->is_big_endian = is_big_endian;
init->dmem_len = dmem_size;
init->imem_len = imem_size;
init->chksum = (is_big_endian)
? be32_to_cpu(*chksum_ptr) : le32_to_cpu(*chksum_ptr);
return 0;
}
/* Verify checksum by reading back the firmware from co-processor RAM. */
static int __verify_fw_checksum(struct init_data *init,
struct private_data *priv,
const struct dpfe_firmware_header *header,
u32 checksum)
{
u32 magic, sequence, version, sum;
u32 __iomem *dmem = priv->dmem;
u32 __iomem *imem = priv->imem;
unsigned int i;
if (init->is_big_endian) {
magic = be32_to_cpu(header->magic);
sequence = be32_to_cpu(header->sequence);
version = be32_to_cpu(header->version);
} else {
magic = le32_to_cpu(header->magic);
sequence = le32_to_cpu(header->sequence);
version = le32_to_cpu(header->version);
}
sum = magic + sequence + version + init->dmem_len + init->imem_len;
for (i = 0; i < init->dmem_len / sizeof(u32); i++)
sum += readl_relaxed(dmem + i);
for (i = 0; i < init->imem_len / sizeof(u32); i++)
sum += readl_relaxed(imem + i);
return (sum == checksum) ? 0 : -1;
}
static int __write_firmware(u32 __iomem *mem, const u32 *fw,
unsigned int size, bool is_big_endian)
{
unsigned int i;
/* Convert size to 32-bit words. */
size /= sizeof(u32);
/* It is recommended to clear the firmware area first. */
for (i = 0; i < size; i++)
writel_relaxed(0, mem + i);
/* Now copy it. */
if (is_big_endian) {
for (i = 0; i < size; i++)
writel_relaxed(be32_to_cpu(fw[i]), mem + i);
} else {
for (i = 0; i < size; i++)
writel_relaxed(le32_to_cpu(fw[i]), mem + i);
}
return 0;
}
static int brcmstb_dpfe_download_firmware(struct platform_device *pdev,
struct init_data *init)
{
const struct dpfe_firmware_header *header;
unsigned int dmem_size, imem_size;
struct device *dev = &pdev->dev;
bool is_big_endian = false;
struct private_data *priv;
const struct firmware *fw;
const u32 *dmem, *imem;
const void *fw_blob;
int ret;
priv = platform_get_drvdata(pdev);
/*
* Skip downloading the firmware if the DCPU is already running and
* responding to commands.
*/
if (is_dcpu_enabled(priv->regs)) {
u32 response[MSG_FIELD_MAX];
ret = __send_command(priv, DPFE_CMD_GET_INFO, response);
if (!ret)
return 0;
}
ret = request_firmware(&fw, FIRMWARE_NAME, dev);
/* request_firmware() prints its own error messages. */
if (ret)
return ret;
ret = __verify_firmware(init, fw);
if (ret)
return -EFAULT;
__disable_dcpu(priv->regs);
is_big_endian = init->is_big_endian;
dmem_size = init->dmem_len;
imem_size = init->imem_len;
/* At the beginning of the firmware blob is a header. */
header = (struct dpfe_firmware_header *)fw->data;
/* Void pointer to the beginning of the actual firmware. */
fw_blob = fw->data + sizeof(*header);
/* IMEM comes right after the header. */
imem = fw_blob;
/* DMEM follows after IMEM. */
dmem = fw_blob + imem_size;
ret = __write_firmware(priv->dmem, dmem, dmem_size, is_big_endian);
if (ret)
return ret;
ret = __write_firmware(priv->imem, imem, imem_size, is_big_endian);
if (ret)
return ret;
ret = __verify_fw_checksum(init, priv, header, init->chksum);
if (ret)
return ret;
__enable_dcpu(priv->regs);
return 0;
}
static ssize_t generic_show(unsigned int command, u32 response[],
struct device *dev, char *buf)
{
struct private_data *priv;
int ret;
priv = dev_get_drvdata(dev);
if (!priv)
return sprintf(buf, "ERROR: driver private data not set\n");
ret = __send_command(priv, command, response);
if (ret < 0)
return sprintf(buf, "ERROR: %s\n", error_text[-ret]);
return 0;
}
static ssize_t show_info(struct device *dev, struct device_attribute *devattr,
char *buf)
{
u32 response[MSG_FIELD_MAX];
unsigned int info;
int ret;
ret = generic_show(DPFE_CMD_GET_INFO, response, dev, buf);
if (ret)
return ret;
info = response[MSG_ARG0];
return sprintf(buf, "%u.%u.%u.%u\n",
(info >> 24) & 0xff,
(info >> 16) & 0xff,
(info >> 8) & 0xff,
info & 0xff);
}
static ssize_t show_refresh(struct device *dev,
struct device_attribute *devattr, char *buf)
{
u32 response[MSG_FIELD_MAX];
void __iomem *info;
struct private_data *priv;
unsigned int offset;
u8 refresh, sr_abort, ppre, thermal_offs, tuf;
u32 mr4;
int ret;
ret = generic_show(DPFE_CMD_GET_REFRESH, response, dev, buf);
if (ret)
return ret;
priv = dev_get_drvdata(dev);
offset = response[MSG_ARG0];
info = priv->dmem + offset;
mr4 = readl_relaxed(info + DRAM_INFO_MR4) & DRAM_INFO_MR4_MASK;
refresh = (mr4 >> DRAM_MR4_REFRESH) & DRAM_MR4_REFRESH_MASK;
sr_abort = (mr4 >> DRAM_MR4_SR_ABORT) & DRAM_MR4_SR_ABORT_MASK;
ppre = (mr4 >> DRAM_MR4_PPRE) & DRAM_MR4_PPRE_MASK;
thermal_offs = (mr4 >> DRAM_MR4_TH_OFFS) & DRAM_MR4_TH_OFFS_MASK;
tuf = (mr4 >> DRAM_MR4_TUF) & DRAM_MR4_TUF_MASK;
return sprintf(buf, "%#x %#x %#x %#x %#x %#x %#x\n",
readl_relaxed(info + DRAM_INFO_INTERVAL),
refresh, sr_abort, ppre, thermal_offs, tuf,
readl_relaxed(info + DRAM_INFO_ERROR));
}
static ssize_t store_refresh(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u32 response[MSG_FIELD_MAX];
struct private_data *priv;
void __iomem *info;
unsigned int offset;
unsigned long val;
int ret;
if (kstrtoul(buf, 0, &val) < 0)
return -EINVAL;
priv = dev_get_drvdata(dev);
ret = __send_command(priv, DPFE_CMD_GET_REFRESH, response);
if (ret)
return ret;
offset = response[MSG_ARG0];
info = priv->dmem + offset;
writel_relaxed(val, info + DRAM_INFO_INTERVAL);
return count;
}
static ssize_t show_vendor(struct device *dev, struct device_attribute *devattr,
char *buf)
{
u32 response[MSG_FIELD_MAX];
struct private_data *priv;
void __iomem *info;
unsigned int offset;
int ret;
ret = generic_show(DPFE_CMD_GET_VENDOR, response, dev, buf);
if (ret)
return ret;
offset = response[MSG_ARG0];
priv = dev_get_drvdata(dev);
info = priv->dmem + offset;
return sprintf(buf, "%#x %#x %#x %#x %#x\n",
readl_relaxed(info + DRAM_VENDOR_MR5) & DRAM_VENDOR_MASK,
readl_relaxed(info + DRAM_VENDOR_MR6) & DRAM_VENDOR_MASK,
readl_relaxed(info + DRAM_VENDOR_MR7) & DRAM_VENDOR_MASK,
readl_relaxed(info + DRAM_VENDOR_MR8) & DRAM_VENDOR_MASK,
readl_relaxed(info + DRAM_VENDOR_ERROR));
}
static int brcmstb_dpfe_resume(struct platform_device *pdev)
{
struct init_data init;
return brcmstb_dpfe_download_firmware(pdev, &init);
}
static DEVICE_ATTR(dpfe_info, 0444, show_info, NULL);
static DEVICE_ATTR(dpfe_refresh, 0644, show_refresh, store_refresh);
static DEVICE_ATTR(dpfe_vendor, 0444, show_vendor, NULL);
static struct attribute *dpfe_attrs[] = {
&dev_attr_dpfe_info.attr,
&dev_attr_dpfe_refresh.attr,
&dev_attr_dpfe_vendor.attr,
NULL
};
ATTRIBUTE_GROUPS(dpfe);
static int brcmstb_dpfe_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct private_data *priv;
struct device *dpfe_dev;
struct init_data init;
struct resource *res;
u32 index;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
mutex_init(&priv->lock);
platform_set_drvdata(pdev, priv);
/* Cell index is optional; default to 0 if not present. */
ret = of_property_read_u32(dev->of_node, "cell-index", &index);
if (ret)
index = 0;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-cpu");
priv->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->regs)) {
dev_err(dev, "couldn't map DCPU registers\n");
return -ENODEV;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-dmem");
priv->dmem = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->dmem)) {
dev_err(dev, "Couldn't map DCPU data memory\n");
return -ENOENT;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpfe-imem");
priv->imem = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->imem)) {
dev_err(dev, "Couldn't map DCPU instruction memory\n");
return -ENOENT;
}
ret = brcmstb_dpfe_download_firmware(pdev, &init);
if (ret)
goto err;
dpfe_dev = devm_kzalloc(dev, sizeof(*dpfe_dev), GFP_KERNEL);
if (!dpfe_dev) {
ret = -ENOMEM;
goto err;
}
priv->dev = dpfe_dev;
priv->index = index;
dpfe_dev->parent = dev;
dpfe_dev->groups = dpfe_groups;
dpfe_dev->of_node = dev->of_node;
dev_set_drvdata(dpfe_dev, priv);
dev_set_name(dpfe_dev, "dpfe%u", index);
ret = device_register(dpfe_dev);
if (ret)
goto err;
dev_info(dev, "registered.\n");
return 0;
err:
dev_err(dev, "failed to initialize -- error %d\n", ret);
return ret;
}
static const struct of_device_id brcmstb_dpfe_of_match[] = {
{ .compatible = "brcm,dpfe-cpu", },
{}
};
MODULE_DEVICE_TABLE(of, brcmstb_dpfe_of_match);
static struct platform_driver brcmstb_dpfe_driver = {
.driver = {
.name = DRVNAME,
.of_match_table = brcmstb_dpfe_of_match,
},
.probe = brcmstb_dpfe_probe,
.resume = brcmstb_dpfe_resume,
};
module_platform_driver(brcmstb_dpfe_driver);
MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
MODULE_DESCRIPTION("BRCMSTB DDR PHY Front End Driver");
MODULE_LICENSE("GPL");
......@@ -1075,11 +1075,33 @@ int gpmc_configure(int cmd, int wval)
}
EXPORT_SYMBOL(gpmc_configure);
void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
static bool gpmc_nand_writebuffer_empty(void)
{
if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS)
return true;
return false;
}
static struct gpmc_nand_ops nand_ops = {
.nand_writebuffer_empty = gpmc_nand_writebuffer_empty,
};
/**
* gpmc_omap_get_nand_ops - Get the GPMC NAND interface
* @regs: the GPMC NAND register map exclusive for NAND use.
* @cs: GPMC chip select number on which the NAND sits. The
* register map returned will be specific to this chip select.
*
* Returns NULL on error e.g. invalid cs.
*/
struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs)
{
int i;
reg->gpmc_status = NULL; /* deprecated */
if (cs >= gpmc_cs_num)
return NULL;
reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET +
GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs;
reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET +
......@@ -1111,34 +1133,6 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs)
reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 +
i * GPMC_BCH_SIZE;
}
}
static bool gpmc_nand_writebuffer_empty(void)
{
if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS)
return true;
return false;
}
static struct gpmc_nand_ops nand_ops = {
.nand_writebuffer_empty = gpmc_nand_writebuffer_empty,
};
/**
* gpmc_omap_get_nand_ops - Get the GPMC NAND interface
* @regs: the GPMC NAND register map exclusive for NAND use.
* @cs: GPMC chip select number on which the NAND sits. The
* register map returned will be specific to this chip select.
*
* Returns NULL on error e.g. invalid cs.
*/
struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs)
{
if (cs >= gpmc_cs_num)
return NULL;
gpmc_update_nand_reg(reg, cs);
return &nand_ops;
}
......
......@@ -397,3 +397,29 @@ void of_reserved_mem_device_release(struct device *dev)
rmem->ops->device_release(rmem, dev);
}
EXPORT_SYMBOL_GPL(of_reserved_mem_device_release);
/**
* of_reserved_mem_lookup() - acquire reserved_mem from a device node
* @np: node pointer of the desired reserved-memory region
*
* This function allows drivers to acquire a reference to the reserved_mem
* struct based on a device node handle.
*
* Returns a reserved_mem reference, or NULL on error.
*/
struct reserved_mem *of_reserved_mem_lookup(struct device_node *np)
{
const char *name;
int i;
if (!np->full_name)
return NULL;
name = kbasename(np->full_name);
for (i = 0; i < reserved_mem_count; i++)
if (!strcmp(reserved_mem[i].name, name))
return &reserved_mem[i];
return NULL;
}
EXPORT_SYMBOL_GPL(of_reserved_mem_lookup);
......@@ -497,6 +497,12 @@ int of_platform_default_populate(struct device_node *root,
EXPORT_SYMBOL_GPL(of_platform_default_populate);
#ifndef CONFIG_PPC
static const struct of_device_id reserved_mem_matches[] = {
{ .compatible = "qcom,rmtfs-mem" },
{ .compatible = "ramoops" },
{}
};
static int __init of_platform_default_populate_init(void)
{
struct device_node *node;
......@@ -505,15 +511,12 @@ static int __init of_platform_default_populate_init(void)
return -ENODEV;
/*
* Handle ramoops explicitly, since it is inside /reserved-memory,
* which lacks a "compatible" property.
* Handle certain compatibles explicitly, since we don't want to create
* platform_devices for every node in /reserved-memory with a
* "compatible",
*/
node = of_find_node_by_path("/reserved-memory");
if (node) {
node = of_find_compatible_node(node, NULL, "ramoops");
if (node)
of_platform_device_create(node, NULL, NULL);
}
for_each_matching_node(node, reserved_mem_matches)
of_platform_device_create(node, NULL, NULL);
/* Populate everything else. */
of_platform_default_populate(NULL, NULL, NULL);
......
......@@ -28,6 +28,12 @@ config RESET_ATH79
This enables the ATH79 reset controller driver that supports the
AR71xx SoC reset controller.
config RESET_AXS10X
bool "AXS10x Reset Driver" if COMPILE_TEST
default ARC_PLAT_AXS10X
help
This enables the reset controller driver for AXS10x.
config RESET_BERLIN
bool "Berlin Reset Driver" if COMPILE_TEST
default ARCH_BERLIN
......@@ -75,21 +81,21 @@ config RESET_PISTACHIO
help
This enables the reset driver for ImgTec Pistachio SoCs.
config RESET_SOCFPGA
bool "SoCFPGA Reset Driver" if COMPILE_TEST
default ARCH_SOCFPGA
config RESET_SIMPLE
bool "Simple Reset Controller Driver" if COMPILE_TEST
default ARCH_SOCFPGA || ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX
help
This enables the reset controller driver for Altera SoCFPGAs.
This enables a simple reset controller driver for reset lines that
that can be asserted and deasserted by toggling bits in a contiguous,
exclusive register space.
config RESET_STM32
bool "STM32 Reset Driver" if COMPILE_TEST
default ARCH_STM32
help
This enables the RCC reset controller driver for STM32 MCUs.
Currently this driver supports Altera SoCFPGAs, the RCC reset
controller in STM32 MCUs, Allwinner SoCs, and ZTE's zx2967 family.
config RESET_SUNXI
bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI
default ARCH_SUNXI
select RESET_SIMPLE
help
This enables the reset driver for Allwinner SoCs.
......@@ -121,12 +127,6 @@ config RESET_UNIPHIER
Say Y if you want to control reset signals provided by System Control
block, Media I/O block, Peripheral Block.
config RESET_ZX2967
bool "ZTE ZX2967 Reset Driver"
depends on ARCH_ZX || COMPILE_TEST
help
This enables the reset controller driver for ZTE's zx2967 family.
config RESET_ZYNQ
bool "ZYNQ Reset Driver" if COMPILE_TEST
default ARCH_ZYNQ
......
......@@ -5,6 +5,7 @@ obj-$(CONFIG_ARCH_STI) += sti/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o
obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o
obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
......@@ -13,12 +14,10 @@ obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
obj-$(CONFIG_RESET_STM32) += reset-stm32.o
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
obj-$(CONFIG_RESET_ZX2967) += reset-zx2967.o
obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
/*
* Copyright (C) 2017 Synopsys.
*
* Synopsys AXS10x reset driver.
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#define to_axs10x_rst(p) container_of((p), struct axs10x_rst, rcdev)
#define AXS10X_MAX_RESETS 32
struct axs10x_rst {
void __iomem *regs_rst;
spinlock_t lock;
struct reset_controller_dev rcdev;
};
static int axs10x_reset_reset(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct axs10x_rst *rst = to_axs10x_rst(rcdev);
unsigned long flags;
spin_lock_irqsave(&rst->lock, flags);
writel(BIT(id), rst->regs_rst);
spin_unlock_irqrestore(&rst->lock, flags);
return 0;
}
static const struct reset_control_ops axs10x_reset_ops = {
.reset = axs10x_reset_reset,
};
static int axs10x_reset_probe(struct platform_device *pdev)
{
struct axs10x_rst *rst;
struct resource *mem;
rst = devm_kzalloc(&pdev->dev, sizeof(*rst), GFP_KERNEL);
if (!rst)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rst->regs_rst = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(rst->regs_rst))
return PTR_ERR(rst->regs_rst);
spin_lock_init(&rst->lock);
rst->rcdev.owner = THIS_MODULE;
rst->rcdev.ops = &axs10x_reset_ops;
rst->rcdev.of_node = pdev->dev.of_node;
rst->rcdev.nr_resets = AXS10X_MAX_RESETS;
return devm_reset_controller_register(&pdev->dev, &rst->rcdev);
}
static const struct of_device_id axs10x_reset_dt_match[] = {
{ .compatible = "snps,axs10x-reset" },
{ },
};
static struct platform_driver axs10x_reset_driver = {
.probe = axs10x_reset_probe,
.driver = {
.name = "axs10x-reset",
.of_match_table = axs10x_reset_dt_match,
},
};
builtin_platform_driver(axs10x_reset_driver);
MODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>");
MODULE_DESCRIPTION("Synopsys AXS10x reset driver");
MODULE_LICENSE("GPL v2");
......@@ -62,13 +62,16 @@
#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/of_device.h>
#define REG_COUNT 8
#define BITS_PER_REG 32
#define LEVEL_OFFSET 0x7c
struct meson_reset {
void __iomem *reg_base;
struct reset_controller_dev rcdev;
spinlock_t lock;
};
static int meson_reset_reset(struct reset_controller_dev *rcdev,
......@@ -80,26 +83,68 @@ static int meson_reset_reset(struct reset_controller_dev *rcdev,
unsigned int offset = id % BITS_PER_REG;
void __iomem *reg_addr = data->reg_base + (bank << 2);
if (bank >= REG_COUNT)
return -EINVAL;
writel(BIT(offset), reg_addr);
return 0;
}
static const struct reset_control_ops meson_reset_ops = {
static int meson_reset_level(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct meson_reset *data =
container_of(rcdev, struct meson_reset, rcdev);
unsigned int bank = id / BITS_PER_REG;
unsigned int offset = id % BITS_PER_REG;
void __iomem *reg_addr = data->reg_base + LEVEL_OFFSET + (bank << 2);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(reg_addr);
if (assert)
writel(reg & ~BIT(offset), reg_addr);
else
writel(reg | BIT(offset), reg_addr);
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int meson_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return meson_reset_level(rcdev, id, true);
}
static int meson_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return meson_reset_level(rcdev, id, false);
}
static const struct reset_control_ops meson_reset_meson8_ops = {
.reset = meson_reset_reset,
};
static const struct reset_control_ops meson_reset_gx_ops = {
.reset = meson_reset_reset,
.assert = meson_reset_assert,
.deassert = meson_reset_deassert,
};
static const struct of_device_id meson_reset_dt_ids[] = {
{ .compatible = "amlogic,meson8b-reset", },
{ .compatible = "amlogic,meson-gxbb-reset", },
{ .compatible = "amlogic,meson8b-reset",
.data = &meson_reset_meson8_ops, },
{ .compatible = "amlogic,meson-gxbb-reset",
.data = &meson_reset_gx_ops, },
{ /* sentinel */ },
};
static int meson_reset_probe(struct platform_device *pdev)
{
const struct reset_control_ops *ops;
struct meson_reset *data;
struct resource *res;
......@@ -107,6 +152,10 @@ static int meson_reset_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
ops = of_device_get_match_data(&pdev->dev);
if (!ops)
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->reg_base))
......@@ -114,9 +163,11 @@ static int meson_reset_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
spin_lock_init(&data->lock);
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG;
data->rcdev.ops = &meson_reset_ops;
data->rcdev.ops = ops;
data->rcdev.of_node = pdev->dev.of_node;
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
......
/*
* Simple Reset Controller Driver
*
* Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
*
* Based on Allwinner SoCs Reset Controller driver
*
* Copyright 2013 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
#include "reset-simple.h"
static inline struct reset_simple_data *
to_reset_simple_data(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct reset_simple_data, rcdev);
}
static int reset_simple_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct reset_simple_data *data = to_reset_simple_data(rcdev);
int reg_width = sizeof(u32);
int bank = id / (reg_width * BITS_PER_BYTE);
int offset = id % (reg_width * BITS_PER_BYTE);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(data->membase + (bank * reg_width));
if (assert ^ data->active_low)
reg |= BIT(offset);
else
reg &= ~BIT(offset);
writel(reg, data->membase + (bank * reg_width));
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int reset_simple_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return reset_simple_update(rcdev, id, true);
}
static int reset_simple_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return reset_simple_update(rcdev, id, false);
}
static int reset_simple_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct reset_simple_data *data = to_reset_simple_data(rcdev);
int reg_width = sizeof(u32);
int bank = id / (reg_width * BITS_PER_BYTE);
int offset = id % (reg_width * BITS_PER_BYTE);
u32 reg;
reg = readl(data->membase + (bank * reg_width));
return !(reg & BIT(offset)) ^ !data->status_active_low;
}
const struct reset_control_ops reset_simple_ops = {
.assert = reset_simple_assert,
.deassert = reset_simple_deassert,
.status = reset_simple_status,
};
/**
* struct reset_simple_devdata - simple reset controller properties
* @reg_offset: offset between base address and first reset register.
* @nr_resets: number of resets. If not set, default to resource size in bits.
* @active_low: if true, bits are cleared to assert the reset. Otherwise, bits
* are set to assert the reset.
* @status_active_low: if true, bits read back as cleared while the reset is
* asserted. Otherwise, bits read back as set while the
* reset is asserted.
*/
struct reset_simple_devdata {
u32 reg_offset;
u32 nr_resets;
bool active_low;
bool status_active_low;
};
#define SOCFPGA_NR_BANKS 8
static const struct reset_simple_devdata reset_simple_socfpga = {
.reg_offset = 0x10,
.nr_resets = SOCFPGA_NR_BANKS * 32,
.status_active_low = true,
};
static const struct reset_simple_devdata reset_simple_active_low = {
.active_low = true,
.status_active_low = true,
};
static const struct of_device_id reset_simple_dt_ids[] = {
{ .compatible = "altr,rst-mgr", .data = &reset_simple_socfpga },
{ .compatible = "st,stm32-rcc", },
{ .compatible = "allwinner,sun6i-a31-clock-reset",
.data = &reset_simple_active_low },
{ .compatible = "zte,zx296718-reset",
.data = &reset_simple_active_low },
{ /* sentinel */ },
};
static int reset_simple_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct reset_simple_devdata *devdata;
struct reset_simple_data *data;
void __iomem *membase;
struct resource *res;
u32 reg_offset = 0;
devdata = of_device_get_match_data(dev);
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
membase = devm_ioremap_resource(dev, res);
if (IS_ERR(membase))
return PTR_ERR(membase);
spin_lock_init(&data->lock);
data->membase = membase;
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
data->rcdev.ops = &reset_simple_ops;
data->rcdev.of_node = dev->of_node;
if (devdata) {
reg_offset = devdata->reg_offset;
if (devdata->nr_resets)
data->rcdev.nr_resets = devdata->nr_resets;
data->active_low = devdata->active_low;
data->status_active_low = devdata->status_active_low;
}
if (of_device_is_compatible(dev->of_node, "altr,rst-mgr") &&
of_property_read_u32(dev->of_node, "altr,modrst-offset",
&reg_offset)) {
dev_warn(dev,
"missing altr,modrst-offset property, assuming 0x%x!\n",
reg_offset);
}
data->membase += reg_offset;
return devm_reset_controller_register(dev, &data->rcdev);
}
static struct platform_driver reset_simple_driver = {
.probe = reset_simple_probe,
.driver = {
.name = "simple-reset",
.of_match_table = reset_simple_dt_ids,
},
};
builtin_platform_driver(reset_simple_driver);
/*
* Simple Reset Controller ops
*
* Based on Allwinner SoCs Reset Controller driver
*
* Copyright 2013 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __RESET_SIMPLE_H__
#define __RESET_SIMPLE_H__
#include <linux/io.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
/**
* struct reset_simple_data - driver data for simple reset controllers
* @lock: spinlock to protect registers during read-modify-write cycles
* @membase: memory mapped I/O register range
* @rcdev: reset controller device base structure
* @active_low: if true, bits are cleared to assert the reset. Otherwise, bits
* are set to assert the reset. Note that this says nothing about
* the voltage level of the actual reset line.
* @status_active_low: if true, bits read back as cleared while the reset is
* asserted. Otherwise, bits read back as set while the
* reset is asserted.
*/
struct reset_simple_data {
spinlock_t lock;
void __iomem *membase;
struct reset_controller_dev rcdev;
bool active_low;
bool status_active_low;
};
extern const struct reset_control_ops reset_simple_ops;
#endif /* __RESET_SIMPLE_H__ */
/*
* Socfpga Reset Controller Driver
*
* Copyright 2014 Steffen Trumtrar <s.trumtrar@pengutronix.de>
*
* based on
* Allwinner SoCs Reset Controller driver
*
* Copyright 2013 Maxime Ripard
*
* Maxime Ripard <maxime.ripard@free-electrons.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#define BANK_INCREMENT 4
#define NR_BANKS 8
struct socfpga_reset_data {
spinlock_t lock;
void __iomem *membase;
struct reset_controller_dev rcdev;
};
static int socfpga_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct socfpga_reset_data *data = container_of(rcdev,
struct socfpga_reset_data,
rcdev);
int reg_width = sizeof(u32);
int bank = id / (reg_width * BITS_PER_BYTE);
int offset = id % (reg_width * BITS_PER_BYTE);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(data->membase + (bank * BANK_INCREMENT));
writel(reg | BIT(offset), data->membase + (bank * BANK_INCREMENT));
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int socfpga_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct socfpga_reset_data *data = container_of(rcdev,
struct socfpga_reset_data,
rcdev);
int reg_width = sizeof(u32);
int bank = id / (reg_width * BITS_PER_BYTE);
int offset = id % (reg_width * BITS_PER_BYTE);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(data->membase + (bank * BANK_INCREMENT));
writel(reg & ~BIT(offset), data->membase + (bank * BANK_INCREMENT));
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int socfpga_reset_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct socfpga_reset_data *data = container_of(rcdev,
struct socfpga_reset_data, rcdev);
int reg_width = sizeof(u32);
int bank = id / (reg_width * BITS_PER_BYTE);
int offset = id % (reg_width * BITS_PER_BYTE);
u32 reg;
reg = readl(data->membase + (bank * BANK_INCREMENT));
return !(reg & BIT(offset));
}
static const struct reset_control_ops socfpga_reset_ops = {
.assert = socfpga_reset_assert,
.deassert = socfpga_reset_deassert,
.status = socfpga_reset_status,
};
static int socfpga_reset_probe(struct platform_device *pdev)
{
struct socfpga_reset_data *data;
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
u32 modrst_offset;
/*
* The binding was mainlined without the required property.
* Do not continue, when we encounter an old DT.
*/
if (!of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) {
dev_err(&pdev->dev, "%pOF missing #reset-cells property\n",
pdev->dev.of_node);
return -EINVAL;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->membase))
return PTR_ERR(data->membase);
if (of_property_read_u32(np, "altr,modrst-offset", &modrst_offset)) {
dev_warn(dev, "missing altr,modrst-offset property, assuming 0x10!\n");
modrst_offset = 0x10;
}
data->membase += modrst_offset;
spin_lock_init(&data->lock);
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = NR_BANKS * (sizeof(u32) * BITS_PER_BYTE);
data->rcdev.ops = &socfpga_reset_ops;
data->rcdev.of_node = pdev->dev.of_node;
return devm_reset_controller_register(dev, &data->rcdev);
}
static const struct of_device_id socfpga_reset_dt_ids[] = {
{ .compatible = "altr,rst-mgr", },
{ /* sentinel */ },
};
static struct platform_driver socfpga_reset_driver = {
.probe = socfpga_reset_probe,
.driver = {
.name = "socfpga-reset",
.of_match_table = socfpga_reset_dt_ids,
},
};
builtin_platform_driver(socfpga_reset_driver);
/*
* Copyright (C) Maxime Coquelin 2015
* Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
* License terms: GNU General Public License (GPL), version 2
*
* Heavily based on sunxi driver from Maxime Ripard.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
struct stm32_reset_data {
spinlock_t lock;
void __iomem *membase;
struct reset_controller_dev rcdev;
};
static int stm32_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct stm32_reset_data *data = container_of(rcdev,
struct stm32_reset_data,
rcdev);
int bank = id / BITS_PER_LONG;
int offset = id % BITS_PER_LONG;
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(data->membase + (bank * 4));
writel(reg | BIT(offset), data->membase + (bank * 4));
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct stm32_reset_data *data = container_of(rcdev,
struct stm32_reset_data,
rcdev);
int bank = id / BITS_PER_LONG;
int offset = id % BITS_PER_LONG;
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(data->membase + (bank * 4));
writel(reg & ~BIT(offset), data->membase + (bank * 4));
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static const struct reset_control_ops stm32_reset_ops = {
.assert = stm32_reset_assert,
.deassert = stm32_reset_deassert,
};
static const struct of_device_id stm32_reset_dt_ids[] = {
{ .compatible = "st,stm32-rcc", },
{ /* sentinel */ },
};
static int stm32_reset_probe(struct platform_device *pdev)
{
struct stm32_reset_data *data;
struct resource *res;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->membase))
return PTR_ERR(data->membase);
spin_lock_init(&data->lock);
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = resource_size(res) * 8;
data->rcdev.ops = &stm32_reset_ops;
data->rcdev.of_node = pdev->dev.of_node;
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
}
static struct platform_driver stm32_reset_driver = {
.probe = stm32_reset_probe,
.driver = {
.name = "stm32-rcc-reset",
.of_match_table = stm32_reset_dt_ids,
},
};
builtin_platform_driver(stm32_reset_driver);
......@@ -22,64 +22,11 @@
#include <linux/spinlock.h>
#include <linux/types.h>
struct sunxi_reset_data {
spinlock_t lock;
void __iomem *membase;
struct reset_controller_dev rcdev;
};
static int sunxi_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct sunxi_reset_data *data = container_of(rcdev,
struct sunxi_reset_data,
rcdev);
int reg_width = sizeof(u32);
int bank = id / (reg_width * BITS_PER_BYTE);
int offset = id % (reg_width * BITS_PER_BYTE);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(data->membase + (bank * reg_width));
writel(reg & ~BIT(offset), data->membase + (bank * reg_width));
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int sunxi_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct sunxi_reset_data *data = container_of(rcdev,
struct sunxi_reset_data,
rcdev);
int reg_width = sizeof(u32);
int bank = id / (reg_width * BITS_PER_BYTE);
int offset = id % (reg_width * BITS_PER_BYTE);
unsigned long flags;
u32 reg;
spin_lock_irqsave(&data->lock, flags);
reg = readl(data->membase + (bank * reg_width));
writel(reg | BIT(offset), data->membase + (bank * reg_width));
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static const struct reset_control_ops sunxi_reset_ops = {
.assert = sunxi_reset_assert,
.deassert = sunxi_reset_deassert,
};
#include "reset-simple.h"
static int sunxi_reset_init(struct device_node *np)
{
struct sunxi_reset_data *data;
struct reset_simple_data *data;
struct resource res;
resource_size_t size;
int ret;
......@@ -108,8 +55,9 @@ static int sunxi_reset_init(struct device_node *np)
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = size * 8;
data->rcdev.ops = &sunxi_reset_ops;
data->rcdev.ops = &reset_simple_ops;
data->rcdev.of_node = np;
data->active_low = true;
return reset_controller_register(&data->rcdev);
......@@ -122,6 +70,8 @@ static int sunxi_reset_init(struct device_node *np)
* These are the reset controller we need to initialize early on in
* our system, before we can even think of using a regular device
* driver for it.
* The controllers that we can register through the regular device
* model are handled by the simple reset driver directly.
*/
static const struct of_device_id sunxi_early_reset_dt_ids[] __initconst = {
{ .compatible = "allwinner,sun6i-a31-ahb1-reset", },
......@@ -135,45 +85,3 @@ void __init sun6i_reset_init(void)
for_each_matching_node(np, sunxi_early_reset_dt_ids)
sunxi_reset_init(np);
}
/*
* And these are the controllers we can register through the regular
* device model.
*/
static const struct of_device_id sunxi_reset_dt_ids[] = {
{ .compatible = "allwinner,sun6i-a31-clock-reset", },
{ /* sentinel */ },
};
static int sunxi_reset_probe(struct platform_device *pdev)
{
struct sunxi_reset_data *data;
struct resource *res;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->membase))
return PTR_ERR(data->membase);
spin_lock_init(&data->lock);
data->rcdev.owner = THIS_MODULE;
data->rcdev.nr_resets = resource_size(res) * 8;
data->rcdev.ops = &sunxi_reset_ops;
data->rcdev.of_node = pdev->dev.of_node;
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
}
static struct platform_driver sunxi_reset_driver = {
.probe = sunxi_reset_probe,
.driver = {
.name = "sunxi-reset",
.of_match_table = sunxi_reset_dt_ids,
},
};
builtin_platform_driver(sunxi_reset_driver);
......@@ -58,6 +58,7 @@ static const struct uniphier_reset_data uniphier_ld4_sys_reset_data[] = {
static const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = {
UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */
UNIPHIER_RESETX(6, 0x2000, 12), /* Ether */
UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (HSC, MIO, RLE) */
UNIPHIER_RESETX(12, 0x2000, 6), /* GIO (Ether, SATA, USB3) */
UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */
......@@ -76,6 +77,7 @@ static const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = {
static const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = {
UNIPHIER_RESETX(2, 0x2000, 2), /* NAND */
UNIPHIER_RESETX(6, 0x2000, 12), /* Ether */
UNIPHIER_RESETX(8, 0x2000, 10), /* STDMAC (HSC, RLE) */
UNIPHIER_RESETX(14, 0x2000, 17), /* USB30 */
UNIPHIER_RESETX(15, 0x2004, 17), /* USB31 */
......@@ -92,6 +94,7 @@ static const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = {
static const struct uniphier_reset_data uniphier_ld11_sys_reset_data[] = {
UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */
UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */
UNIPHIER_RESETX(6, 0x200c, 6), /* Ether */
UNIPHIER_RESETX(8, 0x200c, 8), /* STDMAC (HSC, MIO) */
UNIPHIER_RESETX(40, 0x2008, 0), /* AIO */
UNIPHIER_RESETX(41, 0x2008, 1), /* EVEA */
......@@ -102,6 +105,7 @@ static const struct uniphier_reset_data uniphier_ld11_sys_reset_data[] = {
static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */
UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */
UNIPHIER_RESETX(6, 0x200c, 6), /* Ether */
UNIPHIER_RESETX(8, 0x200c, 8), /* STDMAC (HSC) */
UNIPHIER_RESETX(12, 0x200c, 5), /* GIO (PCIe, USB3) */
UNIPHIER_RESETX(16, 0x200c, 12), /* USB30-PHY0 */
......@@ -114,6 +118,20 @@ static const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
UNIPHIER_RESET_END,
};
static const struct uniphier_reset_data uniphier_pxs3_sys_reset_data[] = {
UNIPHIER_RESETX(2, 0x200c, 0), /* NAND */
UNIPHIER_RESETX(4, 0x200c, 2), /* eMMC */
UNIPHIER_RESETX(8, 0x200c, 12), /* STDMAC */
UNIPHIER_RESETX(12, 0x200c, 4), /* USB30 link (GIO0) */
UNIPHIER_RESETX(13, 0x200c, 5), /* USB31 link (GIO1) */
UNIPHIER_RESETX(16, 0x200c, 16), /* USB30-PHY0 */
UNIPHIER_RESETX(17, 0x200c, 18), /* USB30-PHY1 */
UNIPHIER_RESETX(18, 0x200c, 20), /* USB30-PHY2 */
UNIPHIER_RESETX(20, 0x200c, 17), /* USB31-PHY0 */
UNIPHIER_RESETX(21, 0x200c, 19), /* USB31-PHY1 */
UNIPHIER_RESET_END,
};
/* Media I/O reset data */
#define UNIPHIER_MIO_RESET_SD(id, ch) \
UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 0)
......@@ -359,6 +377,10 @@ static const struct of_device_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-ld20-reset",
.data = uniphier_ld20_sys_reset_data,
},
{
.compatible = "socionext,uniphier-pxs3-reset",
.data = uniphier_pxs3_sys_reset_data,
},
/* Media I/O reset, SD reset */
{
.compatible = "socionext,uniphier-ld4-mio-reset",
......@@ -392,6 +414,10 @@ static const struct of_device_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-ld20-sd-reset",
.data = uniphier_pro5_sd_reset_data,
},
{
.compatible = "socionext,uniphier-pxs3-sd-reset",
.data = uniphier_pro5_sd_reset_data,
},
/* Peripheral reset */
{
.compatible = "socionext,uniphier-ld4-peri-reset",
......@@ -421,6 +447,10 @@ static const struct of_device_id uniphier_reset_match[] = {
.compatible = "socionext,uniphier-ld20-peri-reset",
.data = uniphier_pro4_peri_reset_data,
},
{
.compatible = "socionext,uniphier-pxs3-peri-reset",
.data = uniphier_pro4_peri_reset_data,
},
/* Analog signal amplifiers reset */
{
.compatible = "socionext,uniphier-ld11-adamv-reset",
......
/*
* ZTE's zx2967 family reset controller driver
*
* Copyright (C) 2017 ZTE Ltd.
*
* Author: Baoyou Xie <baoyou.xie@linaro.org>
*
* License terms: GNU General Public License (GPL) version 2
*/
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
struct zx2967_reset {
void __iomem *reg_base;
spinlock_t lock;
struct reset_controller_dev rcdev;
};
static int zx2967_reset_act(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct zx2967_reset *reset = NULL;
int bank = id / 32;
int offset = id % 32;
u32 reg;
unsigned long flags;
reset = container_of(rcdev, struct zx2967_reset, rcdev);
spin_lock_irqsave(&reset->lock, flags);
reg = readl_relaxed(reset->reg_base + (bank * 4));
if (assert)
reg &= ~BIT(offset);
else
reg |= BIT(offset);
writel_relaxed(reg, reset->reg_base + (bank * 4));
spin_unlock_irqrestore(&reset->lock, flags);
return 0;
}
static int zx2967_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return zx2967_reset_act(rcdev, id, true);
}
static int zx2967_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return zx2967_reset_act(rcdev, id, false);
}
static const struct reset_control_ops zx2967_reset_ops = {
.assert = zx2967_reset_assert,
.deassert = zx2967_reset_deassert,
};
static int zx2967_reset_probe(struct platform_device *pdev)
{
struct zx2967_reset *reset;
struct resource *res;
reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL);
if (!reset)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reset->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(reset->reg_base))
return PTR_ERR(reset->reg_base);
spin_lock_init(&reset->lock);
reset->rcdev.owner = THIS_MODULE;
reset->rcdev.nr_resets = resource_size(res) * 8;
reset->rcdev.ops = &zx2967_reset_ops;
reset->rcdev.of_node = pdev->dev.of_node;
return devm_reset_controller_register(&pdev->dev, &reset->rcdev);
}
static const struct of_device_id zx2967_reset_dt_ids[] = {
{ .compatible = "zte,zx296718-reset", },
{},
};
static struct platform_driver zx2967_reset_driver = {
.probe = zx2967_reset_probe,
.driver = {
.name = "zx2967-reset",
.of_match_table = zx2967_reset_dt_ids,
},
};
builtin_platform_driver(zx2967_reset_driver);
......@@ -11,7 +11,7 @@ obj-$(CONFIG_MACH_DOVE) += dove/
obj-y += fsl/
obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_SOC_XWAY) += lantiq/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-y += mediatek/
obj-$(CONFIG_ARCH_MESON) += amlogic/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-y += renesas/
......
......@@ -9,4 +9,25 @@ config MESON_GX_SOCINFO
Say yes to support decoding of Amlogic Meson GX SoC family
information about the type, package and version.
config MESON_GX_PM_DOMAINS
bool "Amlogic Meson GX Power Domains driver"
depends on ARCH_MESON || COMPILE_TEST
depends on PM && OF
default ARCH_MESON
select PM_GENERIC_DOMAINS
select PM_GENERIC_DOMAINS_OF
help
Say yes to expose Amlogic Meson GX Power Domains as
Generic Power Domains.
config MESON_MX_SOCINFO
bool "Amlogic Meson MX SoC Information driver"
depends on ARCH_MESON || COMPILE_TEST
default ARCH_MESON
select SOC_BUS
help
Say yes to support decoding of Amlogic Meson6, Meson8,
Meson8b and Meson8m2 SoC family information about the type
and version.
endmenu
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
/*
* Copyright (c) 2017 BayLibre, SAS
* Author: Neil Armstrong <narmstrong@baylibre.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/reset.h>
#include <linux/clk.h>
/* AO Offsets */
#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
#define GEN_PWR_VPU_HDMI BIT(8)
#define GEN_PWR_VPU_HDMI_ISO BIT(9)
/* HHI Offsets */
#define HHI_MEM_PD_REG0 (0x40 << 2)
#define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
#define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
struct meson_gx_pwrc_vpu {
struct generic_pm_domain genpd;
struct regmap *regmap_ao;
struct regmap *regmap_hhi;
struct reset_control *rstc;
struct clk *vpu_clk;
struct clk *vapb_clk;
};
static inline
struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d)
{
return container_of(d, struct meson_gx_pwrc_vpu, genpd);
}
static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd)
{
struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
int i;
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO);
udelay(20);
/* Power Down Memories */
for (i = 0; i < 32; i += 2) {
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
0x2 << i, 0x3 << i);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
0x2 << i, 0x3 << i);
udelay(5);
}
for (i = 8; i < 16; i++) {
regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
BIT(i), BIT(i));
udelay(5);
}
udelay(20);
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI);
msleep(20);
clk_disable_unprepare(pd->vpu_clk);
clk_disable_unprepare(pd->vapb_clk);
return 0;
}
static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd)
{
int ret;
ret = clk_prepare_enable(pd->vpu_clk);
if (ret)
return ret;
ret = clk_prepare_enable(pd->vapb_clk);
if (ret)
clk_disable_unprepare(pd->vpu_clk);
return ret;
}
static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd)
{
struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd);
int ret;
int i;
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI, 0);
udelay(20);
/* Power Up Memories */
for (i = 0; i < 32; i += 2) {
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0,
0x2 << i, 0);
udelay(5);
}
for (i = 0; i < 32; i += 2) {
regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1,
0x2 << i, 0);
udelay(5);
}
for (i = 8; i < 16; i++) {
regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0,
BIT(i), 0);
udelay(5);
}
udelay(20);
ret = reset_control_assert(pd->rstc);
if (ret)
return ret;
regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
GEN_PWR_VPU_HDMI_ISO, 0);
ret = reset_control_deassert(pd->rstc);
if (ret)
return ret;
ret = meson_gx_pwrc_vpu_setup_clk(pd);
if (ret)
return ret;
return 0;
}
static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd)
{
u32 reg;
regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, &reg);
return (reg & GEN_PWR_VPU_HDMI);
}
static struct meson_gx_pwrc_vpu vpu_hdmi_pd = {
.genpd = {
.name = "vpu_hdmi",
.power_off = meson_gx_pwrc_vpu_power_off,
.power_on = meson_gx_pwrc_vpu_power_on,
},
};
static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev)
{
struct regmap *regmap_ao, *regmap_hhi;
struct reset_control *rstc;
struct clk *vpu_clk;
struct clk *vapb_clk;
bool powered_off;
int ret;
regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
if (IS_ERR(regmap_ao)) {
dev_err(&pdev->dev, "failed to get regmap\n");
return PTR_ERR(regmap_ao);
}
regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"amlogic,hhi-sysctrl");
if (IS_ERR(regmap_hhi)) {
dev_err(&pdev->dev, "failed to get HHI regmap\n");
return PTR_ERR(regmap_hhi);
}
rstc = devm_reset_control_array_get(&pdev->dev, false, false);
if (IS_ERR(rstc)) {
dev_err(&pdev->dev, "failed to get reset lines\n");
return PTR_ERR(rstc);
}
vpu_clk = devm_clk_get(&pdev->dev, "vpu");
if (IS_ERR(vpu_clk)) {
dev_err(&pdev->dev, "vpu clock request failed\n");
return PTR_ERR(vpu_clk);
}
vapb_clk = devm_clk_get(&pdev->dev, "vapb");
if (IS_ERR(vapb_clk)) {
dev_err(&pdev->dev, "vapb clock request failed\n");
return PTR_ERR(vapb_clk);
}
vpu_hdmi_pd.regmap_ao = regmap_ao;
vpu_hdmi_pd.regmap_hhi = regmap_hhi;
vpu_hdmi_pd.rstc = rstc;
vpu_hdmi_pd.vpu_clk = vpu_clk;
vpu_hdmi_pd.vapb_clk = vapb_clk;
powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd);
/* If already powered, sync the clock states */
if (!powered_off) {
ret = meson_gx_pwrc_vpu_setup_clk(&vpu_hdmi_pd);
if (ret)
return ret;
}
pm_genpd_init(&vpu_hdmi_pd.genpd, &pm_domain_always_on_gov,
powered_off);
return of_genpd_add_provider_simple(pdev->dev.of_node,
&vpu_hdmi_pd.genpd);
}
static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev)
{
meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd);
}
static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = {
{ .compatible = "amlogic,meson-gx-pwrc-vpu" },
{ /* sentinel */ }
};
static struct platform_driver meson_gx_pwrc_vpu_driver = {
.probe = meson_gx_pwrc_vpu_probe,
.shutdown = meson_gx_pwrc_vpu_shutdown,
.driver = {
.name = "meson_gx_pwrc_vpu",
.of_match_table = meson_gx_pwrc_vpu_match_table,
},
};
builtin_platform_driver(meson_gx_pwrc_vpu_driver);
/*
* Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/bitfield.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#define MESON_SOCINFO_MAJOR_VER_MESON6 0x16
#define MESON_SOCINFO_MAJOR_VER_MESON8 0x19
#define MESON_SOCINFO_MAJOR_VER_MESON8B 0x1b
#define MESON_MX_ASSIST_HW_REV 0x14c
#define MESON_MX_ANALOG_TOP_METAL_REVISION 0x0
#define MESON_MX_BOOTROM_MISC_VER 0x4
static const char *meson_mx_socinfo_revision(unsigned int major_ver,
unsigned int misc_ver,
unsigned int metal_rev)
{
unsigned int minor_ver;
switch (major_ver) {
case MESON_SOCINFO_MAJOR_VER_MESON6:
minor_ver = 0xa;
break;
case MESON_SOCINFO_MAJOR_VER_MESON8:
if (metal_rev == 0x11111112)
major_ver = 0x1d;
if (metal_rev == 0x11111111 || metal_rev == 0x11111112)
minor_ver = 0xa;
else if (metal_rev == 0x11111113)
minor_ver = 0xb;
else if (metal_rev == 0x11111133)
minor_ver = 0xc;
else
minor_ver = 0xd;
break;
case MESON_SOCINFO_MAJOR_VER_MESON8B:
if (metal_rev == 0x11111111)
minor_ver = 0xa;
else
minor_ver = 0xb;
break;
default:
minor_ver = 0x0;
break;
}
return kasprintf(GFP_KERNEL, "Rev%X (%x - 0:%X)", minor_ver, major_ver,
misc_ver);
}
static const char *meson_mx_socinfo_soc_id(unsigned int major_ver,
unsigned int metal_rev)
{
const char *soc_id;
switch (major_ver) {
case MESON_SOCINFO_MAJOR_VER_MESON6:
soc_id = "Meson6 (AML8726-MX)";
break;
case MESON_SOCINFO_MAJOR_VER_MESON8:
if (metal_rev == 0x11111112)
soc_id = "Meson8m2 (S812)";
else
soc_id = "Meson8 (S802)";
break;
case MESON_SOCINFO_MAJOR_VER_MESON8B:
soc_id = "Meson8b (S805)";
break;
default:
soc_id = "Unknown";
break;
}
return kstrdup_const(soc_id, GFP_KERNEL);
}
static const struct of_device_id meson_mx_socinfo_analog_top_ids[] = {
{ .compatible = "amlogic,meson8-analog-top", },
{ .compatible = "amlogic,meson8b-analog-top", },
{ /* sentinel */ }
};
int __init meson_mx_socinfo_init(void)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
struct device_node *np;
struct regmap *assist_regmap, *bootrom_regmap, *analog_top_regmap;
unsigned int major_ver, misc_ver, metal_rev = 0;
int ret;
assist_regmap =
syscon_regmap_lookup_by_compatible("amlogic,meson-mx-assist");
if (IS_ERR(assist_regmap))
return PTR_ERR(assist_regmap);
bootrom_regmap =
syscon_regmap_lookup_by_compatible("amlogic,meson-mx-bootrom");
if (IS_ERR(bootrom_regmap))
return PTR_ERR(bootrom_regmap);
np = of_find_matching_node(NULL, meson_mx_socinfo_analog_top_ids);
if (np) {
analog_top_regmap = syscon_node_to_regmap(np);
if (IS_ERR(analog_top_regmap))
return PTR_ERR(analog_top_regmap);
ret = regmap_read(analog_top_regmap,
MESON_MX_ANALOG_TOP_METAL_REVISION,
&metal_rev);
if (ret)
return ret;
}
ret = regmap_read(assist_regmap, MESON_MX_ASSIST_HW_REV, &major_ver);
if (ret < 0)
return ret;
ret = regmap_read(bootrom_regmap, MESON_MX_BOOTROM_MISC_VER,
&misc_ver);
if (ret < 0)
return ret;
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return -ENODEV;
soc_dev_attr->family = "Amlogic Meson";
np = of_find_node_by_path("/");
of_property_read_string(np, "model", &soc_dev_attr->machine);
of_node_put(np);
soc_dev_attr->revision = meson_mx_socinfo_revision(major_ver, misc_ver,
metal_rev);
soc_dev_attr->soc_id = meson_mx_socinfo_soc_id(major_ver, metal_rev);
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
kfree_const(soc_dev_attr->revision);
kfree_const(soc_dev_attr->soc_id);
kfree(soc_dev_attr);
return PTR_ERR(soc_dev);
}
dev_info(soc_device_to_device(soc_dev), "Amlogic %s %s detected\n",
soc_dev_attr->soc_id, soc_dev_attr->revision);
return 0;
}
device_initcall(meson_mx_socinfo_init);
......@@ -72,6 +72,8 @@ static const struct at91_soc __initconst socs[] = {
"sama5d21", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D22CU_EXID_MATCH,
"sama5d22", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D225C_D1M_EXID_MATCH,
"sama5d225c 16MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D23CU_EXID_MATCH,
"sama5d23", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D24CX_EXID_MATCH,
......@@ -84,10 +86,16 @@ static const struct at91_soc __initconst socs[] = {
"sama5d27", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27CN_EXID_MATCH,
"sama5d27", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D1G_EXID_MATCH,
"sama5d27c 128MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D27C_D5M_EXID_MATCH,
"sama5d27c 64MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CU_EXID_MATCH,
"sama5d28", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28CN_EXID_MATCH,
"sama5d28", "sama5d2"),
AT91_SOC(SAMA5D2_CIDR_MATCH, SAMA5D28C_D1G_EXID_MATCH,
"sama5d28c 128MiB SiP", "sama5d2"),
AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D31_EXID_MATCH,
"sama5d31", "sama5d3"),
AT91_SOC(SAMA5D3_CIDR_MATCH, SAMA5D33_EXID_MATCH,
......
......@@ -64,14 +64,18 @@ at91_soc_init(const struct at91_soc *socs);
#define SAMA5D2_CIDR_MATCH 0x0a5c08c0
#define SAMA5D21CU_EXID_MATCH 0x0000005a
#define SAMA5D225C_D1M_EXID_MATCH 0x00000053
#define SAMA5D22CU_EXID_MATCH 0x00000059
#define SAMA5D22CN_EXID_MATCH 0x00000069
#define SAMA5D23CU_EXID_MATCH 0x00000058
#define SAMA5D24CX_EXID_MATCH 0x00000004
#define SAMA5D24CU_EXID_MATCH 0x00000014
#define SAMA5D26CU_EXID_MATCH 0x00000012
#define SAMA5D27C_D1G_EXID_MATCH 0x00000033
#define SAMA5D27C_D5M_EXID_MATCH 0x00000032
#define SAMA5D27CU_EXID_MATCH 0x00000011
#define SAMA5D27CN_EXID_MATCH 0x00000021
#define SAMA5D28C_D1G_EXID_MATCH 0x00000013
#define SAMA5D28CU_EXID_MATCH 0x00000010
#define SAMA5D28CN_EXID_MATCH 0x00000020
......
......@@ -20,4 +20,6 @@ config SOC_BRCMSTB
If unsure, say N.
source "drivers/soc/bcm/brcmstb/Kconfig"
endmenu
if SOC_BRCMSTB
config BRCMSTB_PM
bool "Support suspend/resume for STB platforms"
default y
depends on PM
depends on ARCH_BRCMSTB || BMIPS_GENERIC
select ARM_CPU_SUSPEND if ARM
endif # SOC_BRCMSTB
obj-y += common.o biuctrl.o
obj-$(CONFIG_BRCMSTB_PM) += pm/
obj-$(CONFIG_ARM) += s2-arm.o pm-arm.o
AFLAGS_s2-arm.o := -march=armv7-a
obj-$(CONFIG_BMIPS_GENERIC) += s2-mips.o s3-mips.o pm-mips.o
/*
* Always ON (AON) register interface between bootloader and Linux
*
* Copyright © 2014-2017 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __BRCMSTB_AON_DEFS_H__
#define __BRCMSTB_AON_DEFS_H__
#include <linux/compiler.h>
/* Magic number in upper 16-bits */
#define BRCMSTB_S3_MAGIC_MASK 0xffff0000
#define BRCMSTB_S3_MAGIC_SHORT 0x5AFE0000
enum {
/* Restore random key for AES memory verification (off = fixed key) */
S3_FLAG_LOAD_RANDKEY = (1 << 0),
/* Scratch buffer page table is present */
S3_FLAG_SCRATCH_BUFFER_TABLE = (1 << 1),
/* Skip all memory verification */
S3_FLAG_NO_MEM_VERIFY = (1 << 2),
/*
* Modification of this bit reserved for bootloader only.
* 1=PSCI started Linux, 0=Direct jump to Linux.
*/
S3_FLAG_PSCI_BOOT = (1 << 3),
/*
* Modification of this bit reserved for bootloader only.
* 1=64 bit boot, 0=32 bit boot.
*/
S3_FLAG_BOOTED64 = (1 << 4),
};
#define BRCMSTB_HASH_LEN (128 / 8) /* 128-bit hash */
#define AON_REG_MAGIC_FLAGS 0x00
#define AON_REG_CONTROL_LOW 0x04
#define AON_REG_CONTROL_HIGH 0x08
#define AON_REG_S3_HASH 0x0c /* hash of S3 params */
#define AON_REG_CONTROL_HASH_LEN 0x1c
#define AON_REG_PANIC 0x20
#define BRCMSTB_S3_MAGIC 0x5AFEB007
#define BRCMSTB_PANIC_MAGIC 0x512E115E
#define BOOTLOADER_SCRATCH_SIZE 64
#define BRCMSTB_DTU_STATE_MAP_ENTRIES (8*1024)
#define BRCMSTB_DTU_CONFIG_ENTRIES (512)
#define BRCMSTB_DTU_COUNT (2)
#define IMAGE_DESCRIPTORS_BUFSIZE (2 * 1024)
#define S3_BOOTLOADER_RESERVED (S3_FLAG_PSCI_BOOT | S3_FLAG_BOOTED64)
struct brcmstb_bootloader_dtu_table {
uint32_t dtu_state_map[BRCMSTB_DTU_STATE_MAP_ENTRIES];
uint32_t dtu_config[BRCMSTB_DTU_CONFIG_ENTRIES];
};
/*
* Bootloader utilizes a custom parameter block left in DRAM for handling S3
* warm resume
*/
struct brcmstb_s3_params {
/* scratch memory for bootloader */
uint8_t scratch[BOOTLOADER_SCRATCH_SIZE];
uint32_t magic; /* BRCMSTB_S3_MAGIC */
uint64_t reentry; /* PA */
/* descriptors */
uint32_t hash[BRCMSTB_HASH_LEN / 4];
/*
* If 0, then ignore this parameter (there is only one set of
* descriptors)
*
* If non-0, then a second set of descriptors is stored at:
*
* descriptors + desc_offset_2
*
* The MAC result of both descriptors is XOR'd and stored in @hash
*/
uint32_t desc_offset_2;
/*
* (Physical) address of a brcmstb_bootloader_scratch_table, for
* providing a large DRAM buffer to the bootloader
*/
uint64_t buffer_table;
uint32_t spare[70];
uint8_t descriptors[IMAGE_DESCRIPTORS_BUFSIZE];
/*
* Must be last member of struct. See brcmstb_pm_s3_finish() for reason.
*/
struct brcmstb_bootloader_dtu_table dtu[BRCMSTB_DTU_COUNT];
} __packed;
#endif /* __BRCMSTB_AON_DEFS_H__ */
此差异已折叠。
此差异已折叠。
/*
* Definitions for Broadcom STB power management / Always ON (AON) block
*
* Copyright © 2016-2017 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __BRCMSTB_PM_H__
#define __BRCMSTB_PM_H__
#define AON_CTRL_RESET_CTRL 0x00
#define AON_CTRL_PM_CTRL 0x04
#define AON_CTRL_PM_STATUS 0x08
#define AON_CTRL_PM_CPU_WAIT_COUNT 0x10
#define AON_CTRL_PM_INITIATE 0x88
#define AON_CTRL_HOST_MISC_CMDS 0x8c
#define AON_CTRL_SYSTEM_DATA_RAM_OFS 0x200
/* MIPS PM constants */
/* MEMC0 offsets */
#define DDR40_PHY_CONTROL_REGS_0_PLL_STATUS 0x10
#define DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL 0xa4
/* TIMER offsets */
#define TIMER_TIMER1_CTRL 0x0c
#define TIMER_TIMER1_STAT 0x1c
/* TIMER defines */
#define RESET_TIMER 0x0
#define START_TIMER 0xbfffffff
#define TIMER_MASK 0x3fffffff
/* PM_CTRL bitfield (Method #0) */
#define PM_FAST_PWRDOWN (1 << 6)
#define PM_WARM_BOOT (1 << 5)
#define PM_DEEP_STANDBY (1 << 4)
#define PM_CPU_PWR (1 << 3)
#define PM_USE_CPU_RDY (1 << 2)
#define PM_PLL_PWRDOWN (1 << 1)
#define PM_PWR_DOWN (1 << 0)
/* PM_CTRL bitfield (Method #1) */
#define PM_DPHY_STANDBY_CLEAR (1 << 20)
#define PM_MIN_S3_WIDTH_TIMER_BYPASS (1 << 7)
#define PM_S2_COMMAND (PM_PLL_PWRDOWN | PM_USE_CPU_RDY | PM_PWR_DOWN)
/* Method 0 bitmasks */
#define PM_COLD_CONFIG (PM_PLL_PWRDOWN | PM_DEEP_STANDBY)
#define PM_WARM_CONFIG (PM_COLD_CONFIG | PM_USE_CPU_RDY | PM_WARM_BOOT)
/* Method 1 bitmask */
#define M1_PM_WARM_CONFIG (PM_DPHY_STANDBY_CLEAR | \
PM_MIN_S3_WIDTH_TIMER_BYPASS | \
PM_WARM_BOOT | PM_DEEP_STANDBY | \
PM_PLL_PWRDOWN | PM_PWR_DOWN)
#define M1_PM_COLD_CONFIG (PM_DPHY_STANDBY_CLEAR | \
PM_MIN_S3_WIDTH_TIMER_BYPASS | \
PM_DEEP_STANDBY | \
PM_PLL_PWRDOWN | PM_PWR_DOWN)
#ifndef __ASSEMBLY__
#ifndef CONFIG_MIPS
extern const unsigned long brcmstb_pm_do_s2_sz;
extern asmlinkage int brcmstb_pm_do_s2(void __iomem *aon_ctrl_base,
void __iomem *ddr_phy_pll_status);
#else
/* s2 asm */
extern asmlinkage int brcm_pm_do_s2(u32 *s2_params);
/* s3 asm */
extern asmlinkage int brcm_pm_do_s3(void __iomem *aon_ctrl_base,
int dcache_linesz);
extern int s3_reentry;
#endif /* CONFIG_MIPS */
#endif
#endif /* __BRCMSTB_PM_H__ */
/*
* Copyright © 2014-2017 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
#include "pm.h"
.text
.align 3
#define AON_CTRL_REG r10
#define DDR_PHY_STATUS_REG r11
/*
* r0: AON_CTRL base address
* r1: DDRY PHY PLL status register address
*/
ENTRY(brcmstb_pm_do_s2)
stmfd sp!, {r4-r11, lr}
mov AON_CTRL_REG, r0
mov DDR_PHY_STATUS_REG, r1
/* Flush memory transactions */
dsb
/* Cache DDR_PHY_STATUS_REG translation */
ldr r0, [DDR_PHY_STATUS_REG]
/* power down request */
ldr r0, =PM_S2_COMMAND
ldr r1, =0
str r1, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
ldr r1, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
str r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
ldr r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
/* Wait for interrupt */
wfi
nop
/* Bring MEMC back up */
1: ldr r0, [DDR_PHY_STATUS_REG]
ands r0, #1
beq 1b
/* Power-up handshake */
ldr r0, =1
str r0, [AON_CTRL_REG, #AON_CTRL_HOST_MISC_CMDS]
ldr r0, [AON_CTRL_REG, #AON_CTRL_HOST_MISC_CMDS]
ldr r0, =0
str r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
ldr r0, [AON_CTRL_REG, #AON_CTRL_PM_CTRL]
/* Return to caller */
ldr r0, =0
ldmfd sp!, {r4-r11, pc}
ENDPROC(brcmstb_pm_do_s2)
/* Place literal pool here */
.ltorg
ENTRY(brcmstb_pm_do_s2_sz)
.word . - brcmstb_pm_do_s2
此差异已折叠。
此差异已折叠。
......@@ -213,6 +213,7 @@ static const struct of_device_id fsl_guts_of_match[] = {
{ .compatible = "fsl,ls1021a-dcfg", },
{ .compatible = "fsl,ls1043a-dcfg", },
{ .compatible = "fsl,ls2080a-dcfg", },
{ .compatible = "fsl,ls1088a-dcfg", },
{}
};
MODULE_DEVICE_TABLE(of, fsl_guts_of_match);
......
menuconfig FSL_DPAA
bool "Freescale DPAA 1.x support"
depends on FSL_SOC_BOOKE
depends on (FSL_SOC_BOOKE || ARCH_LAYERSCAPE)
select GENERIC_ALLOCATOR
help
The Freescale Data Path Acceleration Architecture (DPAA) is a set of
......
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_FSL_DPAA) += bman_ccsr.o qman_ccsr.o \
bman_portal.o qman_portal.o \
bman.o qman.o
bman.o qman.o dpaa_sys.o
obj-$(CONFIG_FSL_BMAN_TEST) += bman-test.o
bman-test-y = bman_test.o
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册