ARM: SoC driver updates for v4.8
Driver updates for ARM SoCs. A slew of changes this release cycle. The reset driver tree, that we merge through arm-soc for historical reasons, is also sizable this time around. Among the changes: - clps711x: Treewide changes to compatible strings, merged here for simplicity. - Qualcomm: SCM firmware driver cleanups, move to platform driver - ux500: Major cleanups, removal of old mach-specific infrastructure. - Atmel external bus memory driver - Move of brcmstb platform to the rest of bcm - PMC driver updates for tegra, various fixes and improvements - Samsung platform driver updates to support 64-bit Exynos platforms - Reset controller cleanups moving to devm_reset_controller_register() APIs - Reset controller driver for Amlogic Meson - Reset controller driver for Hisilicon hi6220 - ARM SCPI power domain support -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXnm1XAAoJEIwa5zzehBx35lcP/ApuQarIXeZCQZtjlUBV9McW o3o7FhKFHePmEPeoYCvVeK5D8NykTkQv3WpnCknoxPJzxGJF7jbPWQJcVnXfKOXD kTcyIK15WL2HHtSE3lYyLfyUPz8AbJyRt0l0cxgcg6jvo+uzlWooNz1y78rLIYzg UwRssj7OiHv4dsyYRHZIsjnB8gMWw8rYMk154gP2xy6MnNXXzzOVVnOiVqxSZBm+ EgIIcROMOqkkHuFlClMYKluIgrmgz1Ypjf+FuAg7dqXZd+TGRrmGXeI7SkGThfLu nyvY3N18NViNu7xOUkI9zg7+ifyYM8Si9ylalSICSJdIAxZfiwFqFaLJvVWKU1rY rBOBjKckQI0/X9WYusFNFHcijhIFV8/FgGAnVRRMPdvlCss7Zp03C9mR4AEhmKMX rLG49x81hU1C+LftC59ml3iB8dhZrrRkbxNHjLFHVGWNrKMrmJKa8JhXGRAoNM+u LRauiuJZatqvLfISNvpfcoW2EashVoU3f+uC8ymT3QCyME3wZm0t7T4tllxhMfBl sOgJqNkTKDmPLofwm/dASiLML7ZF1WePScrFyOACnj9K4mUD+OaCnowtWoQPu0eI aNmT84oosJ2S9F/iUDPtFHXdzQ+1QPPfSiQ9FXMoauciVq/2F+pqq68yYgqoxFOG vmkmG2YM4Wyq43u0BONR =O8+y -----END PGP SIGNATURE----- Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Pull ARM SoC driver updates from Olof Johansson: "Driver updates for ARM SoCs. A slew of changes this release cycle. The reset driver tree, that we merge through arm-soc for historical reasons, is also sizable this time around. Among the changes: - clps711x: Treewide changes to compatible strings, merged here for simplicity. - Qualcomm: SCM firmware driver cleanups, move to platform driver - ux500: Major cleanups, removal of old mach-specific infrastructure. - Atmel external bus memory driver - Move of brcmstb platform to the rest of bcm - PMC driver updates for tegra, various fixes and improvements - Samsung platform driver updates to support 64-bit Exynos platforms - Reset controller cleanups moving to devm_reset_controller_register() APIs - Reset controller driver for Amlogic Meson - Reset controller driver for Hisilicon hi6220 - ARM SCPI power domain support" * tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (100 commits) ARM: ux500: consolidate base platform files ARM: ux500: move soc_id driver to drivers/soc ARM: ux500: call ux500_setup_id later ARM: ux500: consolidate soc_device code in id.c ARM: ux500: remove cpu_is_u* helpers ARM: ux500: use CLK_OF_DECLARE() ARM: ux500: move l2x0 init to .init_irq mfd: db8500 stop passing around platform data ASoC: ab8500-codec: remove platform data based probe ARM: ux500: move ab8500_regulator_plat_data into driver ARM: ux500: remove unused regulator data soc: raspberrypi-power: add CONFIG_OF dependency firmware: scpi: add CONFIG_OF dependency video: clps711x-fb: Changing the compatibility string to match with the smallest supported chip input: clps711x-keypad: Changing the compatibility string to match with the smallest supported chip pwm: clps711x: Changing the compatibility string to match with the smallest supported chip serial: clps711x: Changing the compatibility string to match with the smallest supported chip irqchip: clps711x: Changing the compatibility string to match with the smallest supported chip clocksource: clps711x: Changing the compatibility string to match with the smallest supported chip clk: clps711x: Changing the compatibility string to match with the smallest supported chip ...
This commit is contained in:
commit
43a0a98aa8
@ -87,10 +87,33 @@ Required properties:
|
||||
implementation for the IDs to use. For Juno
|
||||
R0 and Juno R1 refer to [3].
|
||||
|
||||
Power domain bindings for the power domains based on SCPI Message Protocol
|
||||
------------------------------------------------------------
|
||||
|
||||
This binding uses the generic power domain binding[4].
|
||||
|
||||
PM domain providers
|
||||
===================
|
||||
|
||||
Required properties:
|
||||
- #power-domain-cells : Should be 1. Contains the device or the power
|
||||
domain ID value used by SCPI commands.
|
||||
- num-domains: Total number of power domains provided by SCPI. This is
|
||||
needed as the SCPI message protocol lacks a mechanism to
|
||||
query this information at runtime.
|
||||
|
||||
PM domain consumers
|
||||
===================
|
||||
|
||||
Required properties:
|
||||
- power-domains : A phandle and PM domain specifier as defined by bindings of
|
||||
the power controller specified by phandle.
|
||||
|
||||
[0] http://infocenter.arm.com/help/topic/com.arm.doc.dui0922b/index.html
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
[2] Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
[3] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0922b/apas03s22.html
|
||||
[4] Documentation/devicetree/bindings/power/power_domain.txt
|
||||
|
||||
Example:
|
||||
|
||||
@ -144,6 +167,12 @@ scpi_protocol: scpi@2e000000 {
|
||||
compatible = "arm,scpi-sensors";
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
scpi_devpd: scpi-power-domains {
|
||||
compatible = "arm,scpi-power-domains";
|
||||
num-domains = <2>;
|
||||
#power-domain-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
cpu@0 {
|
||||
@ -156,6 +185,7 @@ hdlcd@7ff60000 {
|
||||
...
|
||||
reg = <0 0x7ff60000 0 0x1000>;
|
||||
clocks = <&scpi_clk 4>;
|
||||
power-domains = <&scpi_devpd 1>;
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
@ -186,3 +216,7 @@ The thermal-sensors property in the soc_thermal node uses the
|
||||
temperature sensor provided by SCP firmware to setup a thermal
|
||||
zone. The ID "3" is the sensor identifier for the temperature sensor
|
||||
as used by the firmware.
|
||||
|
||||
The num-domains property in scpi-power-domains domain specifies that
|
||||
SCPI provides 2 power domains. The hdlcd node uses the power domain with
|
||||
domain ID 1.
|
||||
|
@ -0,0 +1,45 @@
|
||||
NVIDIA Tegra ACONNECT Bus
|
||||
|
||||
The Tegra ACONNECT bus is an AXI switch which is used to connnect various
|
||||
components inside the Audio Processing Engine (APE). All CPU accesses to
|
||||
the APE subsystem go through the ACONNECT via an APB to AXI wrapper.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "nvidia,tegra210-aconnect".
|
||||
- clocks: Must contain the entries for the APE clock (TEGRA210_CLK_APE),
|
||||
and APE interface clock (TEGRA210_CLK_APB2APE).
|
||||
- clock-names: Must contain the names "ape" and "apb2ape" for the corresponding
|
||||
'clocks' entries.
|
||||
- power-domains: Must contain a phandle that points to the audio powergate
|
||||
(namely 'aud') for Tegra210.
|
||||
- #address-cells: The number of cells used to represent physical base addresses
|
||||
in the aconnect address space. Should be 1.
|
||||
- #size-cells: The number of cells used to represent the size of an address
|
||||
range in the aconnect address space. Should be 1.
|
||||
- ranges: Mapping of the aconnect address space to the CPU address space.
|
||||
|
||||
All devices accessed via the ACONNNECT are described by child-nodes.
|
||||
|
||||
Example:
|
||||
|
||||
aconnect@702c0000 {
|
||||
compatible = "nvidia,tegra210-aconnect";
|
||||
clocks = <&tegra_car TEGRA210_CLK_APE>,
|
||||
<&tegra_car TEGRA210_CLK_APB2APE>;
|
||||
clock-names = "ape", "apb2ape";
|
||||
power-domains = <&pd_audio>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0x702c0000 0x0 0x702c0000 0x00040000>;
|
||||
|
||||
status = "disabled";
|
||||
|
||||
child1 {
|
||||
...
|
||||
};
|
||||
|
||||
child2 {
|
||||
...
|
||||
};
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
* Clock bindings for the Cirrus Logic CLPS711X CPUs
|
||||
|
||||
Required properties:
|
||||
- compatible : Shall contain "cirrus,clps711x-clk".
|
||||
- compatible : Shall contain "cirrus,ep7209-clk".
|
||||
- reg : Address of the internal register set.
|
||||
- startup-frequency: Factory set CPU startup frequency in HZ.
|
||||
- #clock-cells : Should be <1>.
|
||||
@ -13,7 +13,7 @@ for the full list of CLPS711X clock IDs.
|
||||
Example:
|
||||
clks: clks@80000000 {
|
||||
#clock-cells = <1>;
|
||||
compatible = "cirrus,ep7312-clk", "cirrus,clps711x-clk";
|
||||
compatible = "cirrus,ep7312-clk", "cirrus,ep7209-clk";
|
||||
reg = <0x80000000 0xc000>;
|
||||
startup-frequency = <73728000>;
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
* Currus Logic CLPS711X Framebuffer
|
||||
|
||||
Required properties:
|
||||
- compatible: Shall contain "cirrus,clps711x-fb".
|
||||
- compatible: Shall contain "cirrus,ep7209-fb".
|
||||
- reg : Physical base address and length of the controller's registers +
|
||||
location and size of the framebuffer memory.
|
||||
- clocks : phandle + clock specifier pair of the FB reference clock.
|
||||
@ -18,7 +18,7 @@ Optional properties:
|
||||
|
||||
Example:
|
||||
fb: fb@800002c0 {
|
||||
compatible = "cirrus,ep7312-fb", "cirrus,clps711x-fb";
|
||||
compatible = "cirrus,ep7312-fb", "cirrus,ep7209-fb";
|
||||
reg = <0x800002c0 0xd44>, <0x60000000 0xc000>;
|
||||
clocks = <&clks 2>;
|
||||
lcd-supply = <®5v0>;
|
||||
|
@ -1,7 +1,7 @@
|
||||
* Cirrus Logic CLPS711X matrix keypad device tree bindings
|
||||
|
||||
Required Properties:
|
||||
- compatible: Shall contain "cirrus,clps711x-keypad".
|
||||
- compatible: Shall contain "cirrus,ep7209-keypad".
|
||||
- row-gpios: List of GPIOs used as row lines.
|
||||
- poll-interval: Poll interval time in milliseconds.
|
||||
- linux,keymap: The definition can be found at
|
||||
@ -12,7 +12,7 @@ Optional Properties:
|
||||
|
||||
Example:
|
||||
keypad {
|
||||
compatible = "cirrus,ep7312-keypad", "cirrus,clps711x-keypad";
|
||||
compatible = "cirrus,ep7312-keypad", "cirrus,ep7209-keypad";
|
||||
autorepeat;
|
||||
poll-interval = <120>;
|
||||
row-gpios = <&porta 0 0>,
|
||||
|
@ -2,7 +2,7 @@ Cirrus Logic CLPS711X Interrupt Controller
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be "cirrus,clps711x-intc".
|
||||
- compatible: Should be "cirrus,ep7209-intc".
|
||||
- reg: Specifies base physical address of the registers set.
|
||||
- interrupt-controller: Identifies the node as an interrupt controller.
|
||||
- #interrupt-cells: Specifies the number of cells needed to encode an
|
||||
@ -34,7 +34,7 @@ ID Name Description
|
||||
|
||||
Example:
|
||||
intc: interrupt-controller {
|
||||
compatible = "cirrus,clps711x-intc";
|
||||
compatible = "cirrus,ep7312-intc", "cirrus,ep7209-intc";
|
||||
reg = <0x80000000 0x4000>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
|
20
Documentation/devicetree/bindings/media/nokia,n900-ir
Normal file
20
Documentation/devicetree/bindings/media/nokia,n900-ir
Normal file
@ -0,0 +1,20 @@
|
||||
Device-Tree bindings for LIRC TX driver for Nokia N900(RX51)
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "nokia,n900-ir".
|
||||
- pwms: specifies PWM used for IR signal transmission.
|
||||
|
||||
Example node:
|
||||
|
||||
pwm9: dmtimer-pwm@9 {
|
||||
compatible = "ti,omap-dmtimer-pwm";
|
||||
ti,timers = <&timer9>;
|
||||
ti,clock-source = <0x00>; /* timer_sys_ck */
|
||||
#pwm-cells = <3>;
|
||||
};
|
||||
|
||||
ir: n900-ir {
|
||||
compatible = "nokia,n900-ir";
|
||||
|
||||
pwms = <&pwm9 0 26316 0>; /* 38000 Hz */
|
||||
};
|
@ -0,0 +1,136 @@
|
||||
* Device tree bindings for Atmel EBI
|
||||
|
||||
The External Bus Interface (EBI) controller is a bus where you can connect
|
||||
asynchronous (NAND, NOR, SRAM, ....) and synchronous memories (SDR/DDR SDRAMs).
|
||||
The EBI provides a glue-less interface to asynchronous memories through the SMC
|
||||
(Static Memory Controller).
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: "atmel,at91sam9260-ebi"
|
||||
"atmel,at91sam9261-ebi"
|
||||
"atmel,at91sam9263-ebi0"
|
||||
"atmel,at91sam9263-ebi1"
|
||||
"atmel,at91sam9rl-ebi"
|
||||
"atmel,at91sam9g45-ebi"
|
||||
"atmel,at91sam9x5-ebi"
|
||||
"atmel,sama5d3-ebi"
|
||||
|
||||
- reg: Contains offset/length value for EBI memory mapping.
|
||||
This property might contain several entries if the EBI
|
||||
memory range is not contiguous
|
||||
|
||||
- #address-cells: Must be 2.
|
||||
The first cell encodes the CS.
|
||||
The second cell encode the offset into the CS memory
|
||||
range.
|
||||
|
||||
- #size-cells: Must be set to 1.
|
||||
|
||||
- ranges: Encodes CS to memory region association.
|
||||
|
||||
- clocks: Clock feeding the EBI controller.
|
||||
See clock-bindings.txt
|
||||
|
||||
Children device nodes are representing device connected to the EBI bus.
|
||||
|
||||
Required device node properties:
|
||||
|
||||
- reg: Contains the chip-select id, the offset and the length
|
||||
of the memory region requested by the device.
|
||||
|
||||
EBI bus configuration will be defined directly in the device subnode.
|
||||
|
||||
Optional EBI/SMC properties:
|
||||
|
||||
- atmel,smc-bus-width: width of the asynchronous device's data bus
|
||||
8, 16 or 32.
|
||||
Default to 8 when undefined.
|
||||
|
||||
- atmel,smc-byte-access-type "write" or "select" (see Atmel datasheet).
|
||||
Default to "select" when undefined.
|
||||
|
||||
- atmel,smc-read-mode "nrd" or "ncs".
|
||||
Default to "ncs" when undefined.
|
||||
|
||||
- atmel,smc-write-mode "nwe" or "ncs".
|
||||
Default to "ncs" when undefined.
|
||||
|
||||
- atmel,smc-exnw-mode "disabled", "frozen" or "ready".
|
||||
Default to "disabled" when undefined.
|
||||
|
||||
- atmel,smc-page-mode enable page mode if present. The provided value
|
||||
defines the page size (supported values: 4, 8,
|
||||
16 and 32).
|
||||
|
||||
- atmel,smc-tdf-mode: "normal" or "optimized". When set to
|
||||
"optimized" the data float time is optimized
|
||||
depending on the next device being accessed
|
||||
(next device setup time is subtracted to the
|
||||
current device data float time).
|
||||
Default to "normal" when undefined.
|
||||
|
||||
If at least one atmel,smc- property is defined the following SMC timing
|
||||
properties become mandatory. In the other hand, if none of the atmel,smc-
|
||||
properties are specified, we assume that the EBI bus configuration will be
|
||||
handled by the sub-device driver, and none of those properties should be
|
||||
defined.
|
||||
|
||||
All the timings are expressed in nanoseconds (see Atmel datasheet for a full
|
||||
description).
|
||||
|
||||
- atmel,smc-ncs-rd-setup-ns
|
||||
- atmel,smc-nrd-setup-ns
|
||||
- atmel,smc-ncs-wr-setup-ns
|
||||
- atmel,smc-nwe-setup-ns
|
||||
- atmel,smc-ncs-rd-pulse-ns
|
||||
- atmel,smc-nrd-pulse-ns
|
||||
- atmel,smc-ncs-wr-pulse-ns
|
||||
- atmel,smc-nwe-pulse-ns
|
||||
- atmel,smc-nwe-cycle-ns
|
||||
- atmel,smc-nrd-cycle-ns
|
||||
- atmel,smc-tdf-ns
|
||||
|
||||
Example:
|
||||
|
||||
ebi: ebi@10000000 {
|
||||
compatible = "atmel,sama5d3-ebi";
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
atmel,smc = <&hsmc>;
|
||||
atmel,matrix = <&matrix>;
|
||||
reg = <0x10000000 0x10000000
|
||||
0x40000000 0x30000000>;
|
||||
ranges = <0x0 0x0 0x10000000 0x10000000
|
||||
0x1 0x0 0x40000000 0x10000000
|
||||
0x2 0x0 0x50000000 0x10000000
|
||||
0x3 0x0 0x60000000 0x10000000>;
|
||||
clocks = <&mck>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_ebi_addr>;
|
||||
|
||||
nor: flash@0,0 {
|
||||
compatible = "cfi-flash";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x0 0x0 0x1000000>;
|
||||
bank-width = <2>;
|
||||
|
||||
atmel,smc-read-mode = "nrd";
|
||||
atmel,smc-write-mode = "nwe";
|
||||
atmel,smc-bus-width = <16>;
|
||||
atmel,smc-ncs-rd-setup-ns = <0>;
|
||||
atmel,smc-ncs-wr-setup-ns = <0>;
|
||||
atmel,smc-nwe-setup-ns = <8>;
|
||||
atmel,smc-nrd-setup-ns = <16>;
|
||||
atmel,smc-ncs-rd-pulse-ns = <84>;
|
||||
atmel,smc-ncs-wr-pulse-ns = <84>;
|
||||
atmel,smc-nrd-pulse-ns = <76>;
|
||||
atmel,smc-nwe-pulse-ns = <76>;
|
||||
atmel,smc-nrd-cycle-ns = <107>;
|
||||
atmel,smc-nwe-cycle-ns = <84>;
|
||||
atmel,smc-tdf-ns = <16>;
|
||||
};
|
||||
};
|
||||
|
@ -1,15 +1,14 @@
|
||||
* Cirris Logic CLPS711X PWM controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Shall contain "cirrus,clps711x-pwm".
|
||||
- compatible: Shall contain "cirrus,ep7209-pwm".
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- clocks: phandle + clock specifier pair of the PWM reference clock.
|
||||
- #pwm-cells: Should be 1. The cell specifies the index of the channel.
|
||||
|
||||
Example:
|
||||
pwm: pwm@80000400 {
|
||||
compatible = "cirrus,ep7312-pwm",
|
||||
"cirrus,clps711x-pwm";
|
||||
compatible = "cirrus,ep7312-pwm", "cirrus,ep7209-pwm";
|
||||
reg = <0x80000400 0x4>;
|
||||
clocks = <&clks 8>;
|
||||
#pwm-cells = <1>;
|
||||
|
@ -9,6 +9,10 @@ Required properties:
|
||||
|
||||
Optional properties:
|
||||
- ti,prescaler: Should be a value between 0 and 7, see the timers datasheet
|
||||
- ti,clock-source: Set dmtimer parent clock, values between 0 and 2:
|
||||
- 0x00 - high-frequency system clock (timer_sys_ck)
|
||||
- 0x01 - 32-kHz always-on clock (timer_32k_ck)
|
||||
- 0x02 - external clock (timer_ext_ck, OMAP2 only)
|
||||
|
||||
Example:
|
||||
pwm9: dmtimer-pwm@9 {
|
||||
|
@ -0,0 +1,18 @@
|
||||
Amlogic Meson SoC Reset Controller
|
||||
=======================================
|
||||
|
||||
Please also refer to reset.txt in this directory for common reset
|
||||
controller binding usage.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "amlogic,meson8b-reset" or "amlogic,meson-gxbb-reset"
|
||||
- reg: should contain the register address base
|
||||
- #reset-cells: 1, see below
|
||||
|
||||
example:
|
||||
|
||||
reset: reset-controller {
|
||||
compatible = "amlogic,meson-gxbb-reset";
|
||||
reg = <0x0 0x04404 0x0 0x20>;
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -8,7 +8,9 @@ The reset controller registers are part of the system-ctl block on
|
||||
hi6220 SoC.
|
||||
|
||||
Required properties:
|
||||
- compatible: may be "hisilicon,hi6220-sysctrl"
|
||||
- compatible: should be one of the following:
|
||||
- "hisilicon,hi6220-sysctrl", "syscon" : For peripheral reset controller.
|
||||
- "hisilicon,hi6220-mediactrl", "syscon" : For media reset controller.
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- #reset-cells: 1, see below
|
||||
|
91
Documentation/devicetree/bindings/reset/ti-syscon-reset.txt
Normal file
91
Documentation/devicetree/bindings/reset/ti-syscon-reset.txt
Normal file
@ -0,0 +1,91 @@
|
||||
TI SysCon Reset Controller
|
||||
=======================
|
||||
|
||||
Almost all SoCs have hardware modules that require reset control in addition
|
||||
to clock and power control for their functionality. The reset control is
|
||||
typically provided by means of memory-mapped I/O registers. These registers are
|
||||
sometimes a part of a larger register space region implementing various
|
||||
functionalities. This register range is best represented as a syscon node to
|
||||
allow multiple entities to access their relevant registers in the common
|
||||
register space.
|
||||
|
||||
A SysCon Reset Controller node defines a device that uses a syscon node
|
||||
and provides reset management functionality for various hardware modules
|
||||
present on the SoC.
|
||||
|
||||
SysCon Reset Controller Node
|
||||
============================
|
||||
Each of the reset provider/controller nodes should be a child of a syscon
|
||||
node and have the following properties.
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
- compatible : Should be,
|
||||
"ti,k2e-pscrst"
|
||||
"ti,k2l-pscrst"
|
||||
"ti,k2hk-pscrst"
|
||||
"ti,syscon-reset"
|
||||
- #reset-cells : Should be 1. Please see the reset consumer node below
|
||||
for usage details
|
||||
- ti,reset-bits : Contains the reset control register information
|
||||
Should contain 7 cells for each reset exposed to
|
||||
consumers, defined as:
|
||||
Cell #1 : offset of the reset assert control
|
||||
register from the syscon register base
|
||||
Cell #2 : bit position of the reset in the reset
|
||||
assert control register
|
||||
Cell #3 : offset of the reset deassert control
|
||||
register from the syscon register base
|
||||
Cell #4 : bit position of the reset in the reset
|
||||
deassert control register
|
||||
Cell #5 : offset of the reset status register
|
||||
from the syscon register base
|
||||
Cell #6 : bit position of the reset in the
|
||||
reset status register
|
||||
Cell #7 : Flags used to control reset behavior,
|
||||
availible flags defined in the DT include
|
||||
file <dt-bindings/reset/ti-syscon.h>
|
||||
|
||||
SysCon Reset Consumer Nodes
|
||||
===========================
|
||||
Each of the reset consumer nodes should have the following properties,
|
||||
in addition to their own properties.
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
- resets : A phandle to the reset controller node and an index number
|
||||
to a reset specifier as defined above.
|
||||
|
||||
Please also refer to Documentation/devicetree/bindings/reset/reset.txt for
|
||||
common reset controller usage by consumers.
|
||||
|
||||
Example:
|
||||
--------
|
||||
The following example demonstrates a syscon node, the reset controller node
|
||||
using the syscon node, and a consumer (a DSP device) on the TI Keystone 2
|
||||
Edison SoC.
|
||||
|
||||
/ {
|
||||
soc {
|
||||
psc: power-sleep-controller@02350000 {
|
||||
compatible = "syscon", "simple-mfd";
|
||||
reg = <0x02350000 0x1000>;
|
||||
|
||||
pscrst: psc-reset {
|
||||
compatible = "ti,k2e-pscrst", "ti,syscon-reset";
|
||||
#reset-cells = <1>;
|
||||
|
||||
ti,reset-bits = <
|
||||
0xa3c 8 0xa3c 8 0x83c 8 (ASSERT_SET|DEASSERT_CLEAR|STATUS_SET) /* 0: pcrst-dsp0 */
|
||||
0xa40 5 0xa44 3 0 0 (ASSERT_SET|DEASSERT_CLEAR|STATUS_NONE) /* 1: pcrst-example */
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
dsp0: dsp0 {
|
||||
...
|
||||
resets = <&pscrst 0>;
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
* Cirrus Logic CLPS711X Universal Asynchronous Receiver/Transmitter (UART)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "cirrus,clps711x-uart".
|
||||
- compatible: Should be "cirrus,ep7209-uart".
|
||||
- reg: Address and length of the register set for the device.
|
||||
- interrupts: Should contain UART TX and RX interrupt.
|
||||
- clocks: Should contain UART core clock number.
|
||||
@ -20,7 +20,7 @@ Example:
|
||||
};
|
||||
|
||||
uart1: uart@80000480 {
|
||||
compatible = "cirrus,clps711x-uart";
|
||||
compatible = "cirrus,ep7312-uart","cirrus,ep7209-uart";
|
||||
reg = <0x80000480 0x80>;
|
||||
interrupts = <12 13>;
|
||||
clocks = <&clks 11>;
|
||||
|
@ -68,7 +68,7 @@ important.
|
||||
Value type: <u32>
|
||||
Definition: must be 2 - denoting the bit in the entry and IRQ flags
|
||||
|
||||
- #qcom,state-cells:
|
||||
- #qcom,smem-state-cells:
|
||||
Usage: required for outgoing entries
|
||||
Value type: <u32>
|
||||
Definition: must be 1 - denoting the bit in the entry
|
||||
@ -92,7 +92,7 @@ wcnss-smp2p {
|
||||
wcnss_smp2p_out: master-kernel {
|
||||
qcom,entry-name = "master-kernel";
|
||||
|
||||
#qcom,state-cells = <1>;
|
||||
#qcom,smem-state-cells = <1>;
|
||||
};
|
||||
|
||||
wcnss_smp2p_in: slave-kernel {
|
||||
|
@ -51,7 +51,7 @@ important.
|
||||
Definition: specifies the offset, in words, of the first bit for this
|
||||
entry
|
||||
|
||||
- #qcom,state-cells:
|
||||
- #qcom,smem-state-cells:
|
||||
Usage: required for local entry
|
||||
Value type: <u32>
|
||||
Definition: must be 1 - denotes bit number
|
||||
@ -91,7 +91,7 @@ smsm {
|
||||
apps_smsm: apps@0 {
|
||||
reg = <0>;
|
||||
|
||||
#qcom,state-cells = <1>;
|
||||
#qcom,smem-state-cells = <1>;
|
||||
};
|
||||
|
||||
wcnss_smsm: wcnss@7 {
|
||||
|
@ -1,7 +1,7 @@
|
||||
* Cirrus Logic CLPS711X Timer Counter
|
||||
|
||||
Required properties:
|
||||
- compatible: Shall contain "cirrus,clps711x-timer".
|
||||
- compatible: Shall contain "cirrus,ep7209-timer".
|
||||
- reg : Address and length of the register set.
|
||||
- interrupts: The interrupt number of the timer.
|
||||
- clocks : phandle of timer reference clock.
|
||||
@ -15,14 +15,14 @@ Example:
|
||||
};
|
||||
|
||||
timer1: timer@80000300 {
|
||||
compatible = "cirrus,ep7312-timer", "cirrus,clps711x-timer";
|
||||
compatible = "cirrus,ep7312-timer", "cirrus,ep7209-timer";
|
||||
reg = <0x80000300 0x4>;
|
||||
interrupts = <8>;
|
||||
clocks = <&clks 5>;
|
||||
};
|
||||
|
||||
timer2: timer@80000340 {
|
||||
compatible = "cirrus,ep7312-timer", "cirrus,clps711x-timer";
|
||||
compatible = "cirrus,ep7312-timer", "cirrus,ep7209-timer";
|
||||
reg = <0x80000340 0x4>;
|
||||
interrupts = <9>;
|
||||
clocks = <&clks 6>;
|
||||
|
@ -352,6 +352,10 @@ REGULATOR
|
||||
devm_regulator_put()
|
||||
devm_regulator_register()
|
||||
|
||||
RESET
|
||||
devm_reset_control_get()
|
||||
devm_reset_controller_register()
|
||||
|
||||
SLAVE DMA ENGINE
|
||||
devm_acpi_dma_controller_register()
|
||||
|
||||
|
@ -1536,6 +1536,7 @@ M: David Brown <david.brown@linaro.org>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
L: linux-soc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/soc/qcom/
|
||||
F: arch/arm/boot/dts/qcom-*.dts
|
||||
F: arch/arm/boot/dts/qcom-*.dtsi
|
||||
F: arch/arm/mach-qcom/
|
||||
@ -1841,7 +1842,6 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
T: git git://git.linaro.org/people/ulfh/clk.git
|
||||
S: Maintained
|
||||
F: drivers/clk/ux500/
|
||||
F: include/linux/platform_data/clk-ux500.h
|
||||
|
||||
ARM/VERSATILE EXPRESS PLATFORM
|
||||
M: Liviu Dudau <liviu.dudau@arm.com>
|
||||
|
29
arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi
Normal file
29
arch/arm/boot/dts/exynos-mfc-reserved-memory.dtsi
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Samsung's Exynos SoC MFC (Video Codec) reserved memory common definition.
|
||||
*
|
||||
* Copyright (c) 2016 Samsung Electronics Co., Ltd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/ {
|
||||
reserved-memory {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
mfc_left: region@51000000 {
|
||||
compatible = "shared-dma-pool";
|
||||
no-map;
|
||||
reg = <0x51000000 0x800000>;
|
||||
};
|
||||
|
||||
mfc_right: region@43000000 {
|
||||
compatible = "shared-dma-pool";
|
||||
no-map;
|
||||
reg = <0x43000000 0x800000>;
|
||||
};
|
||||
};
|
||||
};
|
@ -18,6 +18,7 @@
|
||||
#include "exynos4210.dtsi"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Insignal Origen evaluation board based on Exynos4210";
|
||||
@ -288,8 +289,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
/dts-v1/;
|
||||
#include "exynos4210.dtsi"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Samsung smdkv310 evaluation board based on Exynos4210";
|
||||
@ -133,8 +134,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "exynos4412.dtsi"
|
||||
#include "exynos4412-ppmu-common.dtsi"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
@ -499,6 +500,11 @@
|
||||
clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
|
||||
};
|
||||
|
||||
&mfc {
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&mixer {
|
||||
status = "okay";
|
||||
};
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "exynos4412.dtsi"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Insignal Origen evaluation board based on Exynos4412";
|
||||
@ -466,8 +467,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
/dts-v1/;
|
||||
#include "exynos4412.dtsi"
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Samsung SMDK evaluation board based on Exynos4412";
|
||||
@ -112,8 +113,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include "exynos5250.dtsi"
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Insignal Arndale evaluation board based on EXYNOS5250";
|
||||
@ -516,8 +517,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
||||
&mmc_0 {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include "exynos5250.dtsi"
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "SAMSUNG SMDK5250 board based on EXYNOS5250";
|
||||
@ -344,8 +345,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
||||
&mmc_0 {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include "exynos5250.dtsi"
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Google Spring";
|
||||
@ -425,8 +426,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
||||
&mmc_0 {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/clock/samsung,s2mps11.h>
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Insignal Arndale Octa evaluation board based on EXYNOS5420";
|
||||
@ -347,8 +348,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
||||
&mmc_0 {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <dt-bindings/regulator/maxim,max77802.h>
|
||||
#include "exynos5420.dtsi"
|
||||
#include "exynos5420-cpus.dtsi"
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Google Peach Pit Rev 6+";
|
||||
@ -702,8 +703,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
||||
&mmc_0 {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "exynos5420.dtsi"
|
||||
#include "exynos5420-cpus.dtsi"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Samsung SMDK5420 board based on EXYNOS5420";
|
||||
@ -355,8 +356,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
||||
&mmc_0 {
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "exynos5800.dtsi"
|
||||
#include "exynos5422-cpus.dtsi"
|
||||
#include "exynos5422-cpu-thermal.dtsi"
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
memory {
|
||||
@ -406,8 +407,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
||||
&mmc_0 {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <dt-bindings/regulator/maxim,max77802.h>
|
||||
#include "exynos5800.dtsi"
|
||||
#include "exynos5420-cpus.dtsi"
|
||||
#include "exynos-mfc-reserved-memory.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Google Peach Pi Rev 10+";
|
||||
@ -670,8 +671,7 @@
|
||||
};
|
||||
|
||||
&mfc {
|
||||
samsung,mfc-r = <0x43000000 0x800000>;
|
||||
samsung,mfc-l = <0x51000000 0x800000>;
|
||||
memory-region = <&mfc_left>, <&mfc_right>;
|
||||
};
|
||||
|
||||
&mmc_0 {
|
||||
|
@ -18,6 +18,7 @@ menuconfig ARCH_EXYNOS
|
||||
select EXYNOS_THERMAL
|
||||
select EXYNOS_PMU
|
||||
select EXYNOS_SROM
|
||||
select EXYNOS_PM_DOMAINS if PM_GENERIC_DOMAINS
|
||||
select GPIOLIB
|
||||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_S3C2410_I2C if I2C
|
||||
|
@ -13,7 +13,6 @@ obj-$(CONFIG_ARCH_EXYNOS) += exynos.o exynos-smc.o firmware.o
|
||||
|
||||
obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm.o sleep.o
|
||||
obj-$(CONFIG_PM_SLEEP) += suspend.o
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
|
||||
|
||||
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
|
||||
|
||||
@ -23,5 +22,3 @@ AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec)
|
||||
|
||||
obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o
|
||||
CFLAGS_mcpm-exynos.o += -march=armv7-a
|
||||
|
||||
obj-$(CONFIG_S5P_DEV_MFC) += s5p-dev-mfc.o
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <plat/cpu.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "mfc.h"
|
||||
|
||||
static struct map_desc exynos4_iodesc[] __initdata = {
|
||||
{
|
||||
@ -235,23 +234,6 @@ static char const *const exynos_dt_compat[] __initconst = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static void __init exynos_reserve(void)
|
||||
{
|
||||
#ifdef CONFIG_S5P_DEV_MFC
|
||||
int i;
|
||||
char *mfc_mem[] = {
|
||||
"samsung,mfc-v5",
|
||||
"samsung,mfc-v6",
|
||||
"samsung,mfc-v7",
|
||||
"samsung,mfc-v8",
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mfc_mem); i++)
|
||||
if (of_scan_flat_dt(s5p_fdt_alloc_mfc_mem, mfc_mem[i]))
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init exynos_dt_fixup(void)
|
||||
{
|
||||
/*
|
||||
@ -273,6 +255,5 @@ DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)")
|
||||
.init_machine = exynos_dt_machine_init,
|
||||
.init_late = exynos_init_late,
|
||||
.dt_compat = exynos_dt_compat,
|
||||
.reserve = exynos_reserve,
|
||||
.dt_fixup = exynos_dt_fixup,
|
||||
MACHINE_END
|
||||
|
@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Samsung Electronics Co.Ltd
|
||||
*
|
||||
* 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 __MACH_EXYNOS_MFC_H
|
||||
#define __MACH_EXYNOS_MFC_H __FILE__
|
||||
|
||||
int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname,
|
||||
int depth, void *data);
|
||||
|
||||
#endif /* __MACH_EXYNOS_MFC_H */
|
@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
|
||||
*
|
||||
* Base S5P MFC resource and device definitions
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
static struct platform_device s5p_device_mfc_l;
|
||||
static struct platform_device s5p_device_mfc_r;
|
||||
|
||||
struct s5p_mfc_dt_meminfo {
|
||||
unsigned long loff;
|
||||
unsigned long lsize;
|
||||
unsigned long roff;
|
||||
unsigned long rsize;
|
||||
char *compatible;
|
||||
};
|
||||
|
||||
struct s5p_mfc_reserved_mem {
|
||||
phys_addr_t base;
|
||||
unsigned long size;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static struct s5p_mfc_reserved_mem s5p_mfc_mem[2] __initdata;
|
||||
|
||||
|
||||
static void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize,
|
||||
phys_addr_t lbase, unsigned int lsize)
|
||||
{
|
||||
int i;
|
||||
|
||||
s5p_mfc_mem[0].dev = &s5p_device_mfc_r.dev;
|
||||
s5p_mfc_mem[0].base = rbase;
|
||||
s5p_mfc_mem[0].size = rsize;
|
||||
|
||||
s5p_mfc_mem[1].dev = &s5p_device_mfc_l.dev;
|
||||
s5p_mfc_mem[1].base = lbase;
|
||||
s5p_mfc_mem[1].size = lsize;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s5p_mfc_mem); i++) {
|
||||
struct s5p_mfc_reserved_mem *area = &s5p_mfc_mem[i];
|
||||
if (memblock_remove(area->base, area->size)) {
|
||||
printk(KERN_ERR "Failed to reserve memory for MFC device (%ld bytes at 0x%08lx)\n",
|
||||
area->size, (unsigned long) area->base);
|
||||
area->base = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __init s5p_fdt_alloc_mfc_mem(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
const __be32 *prop;
|
||||
int len;
|
||||
struct s5p_mfc_dt_meminfo mfc_mem;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
if (!of_flat_dt_is_compatible(node, data))
|
||||
return 0;
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "samsung,mfc-l", &len);
|
||||
if (!prop || (len != 2 * sizeof(unsigned long)))
|
||||
return 0;
|
||||
|
||||
mfc_mem.loff = be32_to_cpu(prop[0]);
|
||||
mfc_mem.lsize = be32_to_cpu(prop[1]);
|
||||
|
||||
prop = of_get_flat_dt_prop(node, "samsung,mfc-r", &len);
|
||||
if (!prop || (len != 2 * sizeof(unsigned long)))
|
||||
return 0;
|
||||
|
||||
mfc_mem.roff = be32_to_cpu(prop[0]);
|
||||
mfc_mem.rsize = be32_to_cpu(prop[1]);
|
||||
|
||||
s5p_mfc_reserve_mem(mfc_mem.roff, mfc_mem.rsize,
|
||||
mfc_mem.loff, mfc_mem.lsize);
|
||||
|
||||
return 1;
|
||||
}
|
@ -1242,11 +1242,6 @@ static struct pwm_omap_dmtimer_pdata __maybe_unused pwm_dmtimer_pdata = {
|
||||
#if defined(CONFIG_IR_RX51) || defined(CONFIG_IR_RX51_MODULE)
|
||||
static struct lirc_rx51_platform_data rx51_lirc_data = {
|
||||
.set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat,
|
||||
.pwm_timer = 9, /* Use GPT 9 for CIR */
|
||||
#if IS_ENABLED(CONFIG_OMAP_DM_TIMER)
|
||||
.dmtimer = &pwm_dmtimer_pdata,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
static struct platform_device rx51_lirc_device = {
|
||||
|
@ -274,8 +274,6 @@ static struct platform_device omap3_rom_rng_device = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device rx51_lirc_device;
|
||||
|
||||
static void __init nokia_n900_legacy_init(void)
|
||||
{
|
||||
hsmmc2_internal_input_clk();
|
||||
@ -294,10 +292,7 @@ static void __init nokia_n900_legacy_init(void)
|
||||
|
||||
pr_info("RX-51: Registering OMAP3 HWRNG device\n");
|
||||
platform_device_register(&omap3_rom_rng_device);
|
||||
|
||||
}
|
||||
|
||||
platform_device_register(&rx51_lirc_device);
|
||||
}
|
||||
|
||||
static void __init omap3_tao3530_legacy_init(void)
|
||||
@ -492,10 +487,6 @@ static struct pwm_omap_dmtimer_pdata pwm_dmtimer_pdata = {
|
||||
|
||||
static struct lirc_rx51_platform_data __maybe_unused rx51_lirc_data = {
|
||||
.set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat,
|
||||
.pwm_timer = 9, /* Use GPT 9 for CIR */
|
||||
#if IS_ENABLED(CONFIG_OMAP_DM_TIMER)
|
||||
.dmtimer = &pwm_dmtimer_pdata,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct platform_device __maybe_unused rx51_lirc_device = {
|
||||
@ -543,6 +534,7 @@ static struct of_dev_auxdata omap_auxdata_lookup[] __initdata = {
|
||||
&omap3_iommu_pdata),
|
||||
OF_DEV_AUXDATA("ti,omap3-hsmmc", 0x4809c000, "4809c000.mmc", &mmc_pdata[0]),
|
||||
OF_DEV_AUXDATA("ti,omap3-hsmmc", 0x480b4000, "480b4000.mmc", &mmc_pdata[1]),
|
||||
OF_DEV_AUXDATA("nokia,n900-ir", 0, "n900-ir", &rx51_lirc_data),
|
||||
/* Only on am3517 */
|
||||
OF_DEV_AUXDATA("ti,davinci_mdio", 0x5c030000, "davinci_mdio.0", NULL),
|
||||
OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0",
|
||||
|
@ -2,11 +2,9 @@
|
||||
# Makefile for the linux kernel, U8500 machine.
|
||||
#
|
||||
|
||||
obj-y := cpu.o id.o pm.o
|
||||
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
|
||||
obj-y := pm.o
|
||||
obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o
|
||||
obj-$(CONFIG_MACH_MOP500) += board-mop500-regulators.o \
|
||||
board-mop500-audio.o
|
||||
obj-$(CONFIG_MACH_MOP500) += board-mop500-audio.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
*
|
||||
* Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
|
||||
*
|
||||
* MOP500 board specific initialization for regulators
|
||||
*/
|
||||
|
||||
#ifndef __BOARD_MOP500_REGULATORS_H
|
||||
#define __BOARD_MOP500_REGULATORS_H
|
||||
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/ab8500.h>
|
||||
|
||||
extern struct ab8500_regulator_platform_data ab8500_regulator_plat_data;
|
||||
extern struct ab8500_regulator_platform_data ab8505_regulator_plat_data;
|
||||
extern struct regulator_init_data tps61052_regulator;
|
||||
extern struct regulator_init_data gpio_en_3v3_regulator;
|
||||
|
||||
void mop500_regulator_init(void);
|
||||
|
||||
#endif
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2011
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <asm/outercache.h>
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
|
||||
#include "db8500-regs.h"
|
||||
#include "id.h"
|
||||
|
||||
static int __init ux500_l2x0_unlock(void)
|
||||
{
|
||||
int i;
|
||||
struct device_node *np;
|
||||
void __iomem *l2x0_base;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "arm,pl310-cache");
|
||||
l2x0_base = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
if (!l2x0_base)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Unlock Data and Instruction Lock if locked. Ux500 U-Boot versions
|
||||
* apparently locks both caches before jumping to the kernel. The
|
||||
* l2x0 core will not touch the unlock registers if the l2x0 is
|
||||
* already enabled, so we do it right here instead. The PL310 has
|
||||
* 8 sets of registers, one per possible CPU.
|
||||
*/
|
||||
for (i = 0; i < 8; i++) {
|
||||
writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE +
|
||||
i * L2X0_LOCKDOWN_STRIDE);
|
||||
writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE +
|
||||
i * L2X0_LOCKDOWN_STRIDE);
|
||||
}
|
||||
iounmap(l2x0_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ux500_l2c310_write_sec(unsigned long val, unsigned reg)
|
||||
{
|
||||
/*
|
||||
* We can't write to secure registers as we are in non-secure
|
||||
* mode, until we have some SMI service available.
|
||||
*/
|
||||
}
|
||||
|
||||
static int __init ux500_l2x0_init(void)
|
||||
{
|
||||
/* Multiplatform guard */
|
||||
if (!((cpu_is_u8500_family() || cpu_is_ux540_family())))
|
||||
return -ENODEV;
|
||||
|
||||
/* Unlock before init */
|
||||
ux500_l2x0_unlock();
|
||||
outer_cache.write_sec = ux500_l2c310_write_sec;
|
||||
l2x0_of_init(0, ~0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_initcall(ux500_l2x0_init);
|
@ -12,41 +12,107 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/platform_data/arm-ux500-pm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/perf/arm_pmu.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#include <asm/outercache.h>
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/arch.h>
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#include "board-mop500-regulators.h"
|
||||
#include "board-mop500.h"
|
||||
#include "db8500-regs.h"
|
||||
#include "id.h"
|
||||
|
||||
static struct ab8500_platform_data ab8500_platdata = {
|
||||
.regulator = &ab8500_regulator_plat_data,
|
||||
};
|
||||
|
||||
static struct prcmu_pdata db8500_prcmu_pdata = {
|
||||
.ab_platdata = &ab8500_platdata,
|
||||
.version_offset = DB8500_PRCMU_FW_VERSION_OFFSET,
|
||||
.legacy_offset = DB8500_PRCMU_LEGACY_OFFSET,
|
||||
};
|
||||
|
||||
static void __init u8500_map_io(void)
|
||||
static int __init ux500_l2x0_unlock(void)
|
||||
{
|
||||
debug_ll_io_init();
|
||||
ux500_setup_id();
|
||||
int i;
|
||||
struct device_node *np;
|
||||
void __iomem *l2x0_base;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "arm,pl310-cache");
|
||||
l2x0_base = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
if (!l2x0_base)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Unlock Data and Instruction Lock if locked. Ux500 U-Boot versions
|
||||
* apparently locks both caches before jumping to the kernel. The
|
||||
* l2x0 core will not touch the unlock registers if the l2x0 is
|
||||
* already enabled, so we do it right here instead. The PL310 has
|
||||
* 8 sets of registers, one per possible CPU.
|
||||
*/
|
||||
for (i = 0; i < 8; i++) {
|
||||
writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE +
|
||||
i * L2X0_LOCKDOWN_STRIDE);
|
||||
writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE +
|
||||
i * L2X0_LOCKDOWN_STRIDE);
|
||||
}
|
||||
iounmap(l2x0_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ux500_l2c310_write_sec(unsigned long val, unsigned reg)
|
||||
{
|
||||
/*
|
||||
* We can't write to secure registers as we are in non-secure
|
||||
* mode, until we have some SMI service available.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: Should we set up the GPIO domain here?
|
||||
*
|
||||
* The problem is that we cannot put the interrupt resources into the platform
|
||||
* device until the irqdomain has been added. Right now, we set the GIC interrupt
|
||||
* domain from init_irq(), then load the gpio driver from
|
||||
* core_initcall(nmk_gpio_init) and add the platform devices from
|
||||
* arch_initcall(customize_machine).
|
||||
*
|
||||
* This feels fragile because it depends on the gpio device getting probed
|
||||
* _before_ any device uses the gpio interrupts.
|
||||
*/
|
||||
static void __init ux500_init_irq(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct resource r;
|
||||
|
||||
irqchip_init();
|
||||
np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu");
|
||||
of_address_to_resource(np, 0, &r);
|
||||
of_node_put(np);
|
||||
if (!r.start) {
|
||||
pr_err("could not find PRCMU base resource\n");
|
||||
return;
|
||||
}
|
||||
prcmu_early_init(r.start, r.end-r.start);
|
||||
ux500_pm_init(r.start, r.end-r.start);
|
||||
|
||||
/* Unlock before init */
|
||||
ux500_l2x0_unlock();
|
||||
outer_cache.write_sec = ux500_l2c310_write_sec;
|
||||
}
|
||||
|
||||
static void ux500_restart(enum reboot_mode mode, const char *cmd)
|
||||
{
|
||||
local_irq_disable();
|
||||
local_fiq_disable();
|
||||
|
||||
prcmu_system_reset(0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -73,31 +139,6 @@ static struct arm_pmu_platdata db8500_pmu_platdata = {
|
||||
.handle_irq = db8500_pmu_handler,
|
||||
};
|
||||
|
||||
static const char *db8500_read_soc_id(void)
|
||||
{
|
||||
void __iomem *uid;
|
||||
const char *retstr;
|
||||
|
||||
uid = ioremap(U8500_BB_UID_BASE, 0x20);
|
||||
if (!uid)
|
||||
return NULL;
|
||||
/* Throw these device-specific numbers into the entropy pool */
|
||||
add_device_randomness(uid, 0x14);
|
||||
retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x",
|
||||
readl((u32 *)uid+0),
|
||||
readl((u32 *)uid+1), readl((u32 *)uid+2),
|
||||
readl((u32 *)uid+3), readl((u32 *)uid+4));
|
||||
iounmap(uid);
|
||||
return retstr;
|
||||
}
|
||||
|
||||
static struct device * __init db8500_soc_device_init(void)
|
||||
{
|
||||
const char *soc_id = db8500_read_soc_id();
|
||||
|
||||
return ux500_soc_device_init(soc_id);
|
||||
}
|
||||
|
||||
static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = {
|
||||
/* Requires call-back bindings. */
|
||||
OF_DEV_AUXDATA("arm,cortex-a9-pmu", 0, "arm-pmu", &db8500_pmu_platdata),
|
||||
@ -111,8 +152,7 @@ static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = {
|
||||
OF_DEV_AUXDATA("stericsson,ux500-msp-i2s", 0x80125000,
|
||||
"ux500-msp-i2s.3", &msp3_platform_data),
|
||||
/* Requires non-DT:able platform data. */
|
||||
OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu",
|
||||
&db8500_prcmu_pdata),
|
||||
OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu", NULL),
|
||||
OF_DEV_AUXDATA("stericsson,ux500-cryp", 0xa03cb000, "cryp1", NULL),
|
||||
OF_DEV_AUXDATA("stericsson,ux500-hash", 0xa03c2000, "hash1", NULL),
|
||||
OF_DEV_AUXDATA("stericsson,snd-soc-mop500", 0, "snd-soc-mop500.0",
|
||||
@ -121,8 +161,7 @@ static struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = {
|
||||
};
|
||||
|
||||
static struct of_dev_auxdata u8540_auxdata_lookup[] __initdata = {
|
||||
OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu",
|
||||
&db8500_prcmu_pdata),
|
||||
OF_DEV_AUXDATA("stericsson,db8500-prcmu", 0x80157000, "db8500-prcmu", NULL),
|
||||
{},
|
||||
};
|
||||
|
||||
@ -136,15 +175,13 @@ static const struct of_device_id u8500_local_bus_nodes[] = {
|
||||
|
||||
static void __init u8500_init_machine(void)
|
||||
{
|
||||
struct device *parent = db8500_soc_device_init();
|
||||
|
||||
/* automatically probe child nodes of dbx5x0 devices */
|
||||
if (of_machine_is_compatible("st-ericsson,u8540"))
|
||||
of_platform_populate(NULL, u8500_local_bus_nodes,
|
||||
u8540_auxdata_lookup, parent);
|
||||
u8540_auxdata_lookup, NULL);
|
||||
else
|
||||
of_platform_populate(NULL, u8500_local_bus_nodes,
|
||||
u8500_auxdata_lookup, parent);
|
||||
u8500_auxdata_lookup, NULL);
|
||||
}
|
||||
|
||||
static const char * stericsson_dt_platform_compat[] = {
|
||||
@ -156,10 +193,10 @@ static const char * stericsson_dt_platform_compat[] = {
|
||||
};
|
||||
|
||||
DT_MACHINE_START(U8500_DT, "ST-Ericsson Ux5x0 platform (Device Tree Support)")
|
||||
.map_io = u8500_map_io,
|
||||
.l2c_aux_val = 0,
|
||||
.l2c_aux_mask = ~0,
|
||||
.init_irq = ux500_init_irq,
|
||||
.init_machine = u8500_init_machine,
|
||||
.init_late = NULL,
|
||||
.dt_compat = stericsson_dt_platform_compat,
|
||||
.restart = ux500_restart,
|
||||
MACHINE_END
|
||||
|
@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
* Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/platform_data/clk-ux500.h>
|
||||
#include <linux/platform_data/arm-ux500-pm.h>
|
||||
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#include "board-mop500.h"
|
||||
#include "db8500-regs.h"
|
||||
#include "id.h"
|
||||
|
||||
void ux500_restart(enum reboot_mode mode, const char *cmd)
|
||||
{
|
||||
local_irq_disable();
|
||||
local_fiq_disable();
|
||||
|
||||
prcmu_system_reset(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: Should we set up the GPIO domain here?
|
||||
*
|
||||
* The problem is that we cannot put the interrupt resources into the platform
|
||||
* device until the irqdomain has been added. Right now, we set the GIC interrupt
|
||||
* domain from init_irq(), then load the gpio driver from
|
||||
* core_initcall(nmk_gpio_init) and add the platform devices from
|
||||
* arch_initcall(customize_machine).
|
||||
*
|
||||
* This feels fragile because it depends on the gpio device getting probed
|
||||
* _before_ any device uses the gpio interrupts.
|
||||
*/
|
||||
void __init ux500_init_irq(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct resource r;
|
||||
|
||||
irqchip_init();
|
||||
np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu");
|
||||
of_address_to_resource(np, 0, &r);
|
||||
of_node_put(np);
|
||||
if (!r.start) {
|
||||
pr_err("could not find PRCMU base resource\n");
|
||||
return;
|
||||
}
|
||||
prcmu_early_init(r.start, r.end-r.start);
|
||||
ux500_pm_init(r.start, r.end-r.start);
|
||||
|
||||
/*
|
||||
* Init clocks here so that they are available for system timer
|
||||
* initialization.
|
||||
*/
|
||||
if (cpu_is_u8500_family())
|
||||
u8500_clk_init();
|
||||
else if (cpu_is_u9540())
|
||||
u9540_clk_init();
|
||||
else if (cpu_is_u8540())
|
||||
u8540_clk_init();
|
||||
}
|
||||
|
||||
static const char * __init ux500_get_machine(void)
|
||||
{
|
||||
return kasprintf(GFP_KERNEL, "DB%4x", dbx500_partnumber());
|
||||
}
|
||||
|
||||
static const char * __init ux500_get_family(void)
|
||||
{
|
||||
return kasprintf(GFP_KERNEL, "ux500");
|
||||
}
|
||||
|
||||
static const char * __init ux500_get_revision(void)
|
||||
{
|
||||
unsigned int rev = dbx500_revision();
|
||||
|
||||
if (rev == 0x01)
|
||||
return kasprintf(GFP_KERNEL, "%s", "ED");
|
||||
else if (rev >= 0xA0)
|
||||
return kasprintf(GFP_KERNEL, "%d.%d",
|
||||
(rev >> 4) - 0xA + 1, rev & 0xf);
|
||||
|
||||
return kasprintf(GFP_KERNEL, "%s", "Unknown");
|
||||
}
|
||||
|
||||
static ssize_t ux500_get_process(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
if (dbx500_id.process == 0x00)
|
||||
return sprintf(buf, "Standard\n");
|
||||
|
||||
return sprintf(buf, "%02xnm\n", dbx500_id.process);
|
||||
}
|
||||
|
||||
static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr,
|
||||
const char *soc_id)
|
||||
{
|
||||
soc_dev_attr->soc_id = soc_id;
|
||||
soc_dev_attr->machine = ux500_get_machine();
|
||||
soc_dev_attr->family = ux500_get_family();
|
||||
soc_dev_attr->revision = ux500_get_revision();
|
||||
}
|
||||
|
||||
static const struct device_attribute ux500_soc_attr =
|
||||
__ATTR(process, S_IRUGO, ux500_get_process, NULL);
|
||||
|
||||
struct device * __init ux500_soc_device_init(const char *soc_id)
|
||||
{
|
||||
struct device *parent;
|
||||
struct soc_device *soc_dev;
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
|
||||
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
soc_info_populate(soc_dev_attr, soc_id);
|
||||
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
kfree(soc_dev_attr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parent = soc_device_to_device(soc_dev);
|
||||
device_create_file(parent, &ux500_soc_attr);
|
||||
|
||||
return parent;
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#include "db8500-regs.h"
|
||||
#include "id.h"
|
||||
|
||||
struct dbx500_asic_id dbx500_id;
|
||||
|
||||
static unsigned int __init ux500_read_asicid(phys_addr_t addr)
|
||||
{
|
||||
phys_addr_t base = addr & ~0xfff;
|
||||
struct map_desc desc = {
|
||||
.virtual = (unsigned long)UX500_VIRT_ROM,
|
||||
.pfn = __phys_to_pfn(base),
|
||||
.length = SZ_16K,
|
||||
.type = MT_DEVICE,
|
||||
};
|
||||
|
||||
iotable_init(&desc, 1);
|
||||
|
||||
/* As in devicemaps_init() */
|
||||
local_flush_tlb_all();
|
||||
flush_cache_all();
|
||||
|
||||
return readl(UX500_VIRT_ROM + (addr & 0xfff));
|
||||
}
|
||||
|
||||
static void ux500_print_soc_info(unsigned int asicid)
|
||||
{
|
||||
unsigned int rev = dbx500_revision();
|
||||
|
||||
pr_info("DB%4x ", dbx500_partnumber());
|
||||
|
||||
if (rev == 0x01)
|
||||
pr_cont("Early Drop");
|
||||
else if (rev >= 0xA0)
|
||||
pr_cont("v%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf);
|
||||
else
|
||||
pr_cont("Unknown");
|
||||
|
||||
pr_cont(" [%#010x]\n", asicid);
|
||||
}
|
||||
|
||||
static unsigned int partnumber(unsigned int asicid)
|
||||
{
|
||||
return (asicid >> 8) & 0xffff;
|
||||
}
|
||||
|
||||
/*
|
||||
* SOC MIDR ASICID ADDRESS ASICID VALUE
|
||||
* DB8500ed 0x410fc090 0x9001FFF4 0x00850001
|
||||
* DB8500v1 0x411fc091 0x9001FFF4 0x008500A0
|
||||
* DB8500v1.1 0x411fc091 0x9001FFF4 0x008500A1
|
||||
* DB8500v2 0x412fc091 0x9001DBF4 0x008500B0
|
||||
* DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2
|
||||
* DB5500v1 0x412fc091 0x9001FFF4 0x005500A0
|
||||
* DB9540 0x413fc090 0xFFFFDBF4 0x009540xx
|
||||
*/
|
||||
|
||||
void __init ux500_setup_id(void)
|
||||
{
|
||||
unsigned int cpuid = read_cpuid_id();
|
||||
unsigned int asicid = 0;
|
||||
phys_addr_t addr = 0;
|
||||
|
||||
switch (cpuid) {
|
||||
case 0x410fc090: /* DB8500ed */
|
||||
case 0x411fc091: /* DB8500v1 */
|
||||
addr = 0x9001FFF4;
|
||||
break;
|
||||
|
||||
case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 */
|
||||
asicid = ux500_read_asicid(0x9001DBF4);
|
||||
if (partnumber(asicid) == 0x8500 ||
|
||||
partnumber(asicid) == 0x8520)
|
||||
/* DB8500v2 */
|
||||
break;
|
||||
|
||||
/* DB5500v1 */
|
||||
addr = 0x9001FFF4;
|
||||
break;
|
||||
|
||||
case 0x413fc090: /* DB9540 */
|
||||
addr = 0xFFFFDBF4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (addr)
|
||||
asicid = ux500_read_asicid(addr);
|
||||
|
||||
if (!asicid) {
|
||||
pr_err("Unable to identify SoC\n");
|
||||
ux500_unknown_soc();
|
||||
}
|
||||
|
||||
dbx500_id.process = asicid >> 24;
|
||||
dbx500_id.partnumber = partnumber(asicid);
|
||||
dbx500_id.revision = asicid & 0xff;
|
||||
|
||||
ux500_print_soc_info(asicid);
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#ifndef __MACH_UX500_ID
|
||||
#define __MACH_UX500_ID
|
||||
|
||||
/**
|
||||
* struct dbx500_asic_id - fields of the ASIC ID
|
||||
* @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard"
|
||||
* @partnumber: hithereto 0x8500 for DB8500
|
||||
* @revision: version code in the series
|
||||
*/
|
||||
struct dbx500_asic_id {
|
||||
u16 partnumber;
|
||||
u8 revision;
|
||||
u8 process;
|
||||
};
|
||||
|
||||
extern struct dbx500_asic_id dbx500_id;
|
||||
|
||||
static inline unsigned int __attribute_const__ dbx500_partnumber(void)
|
||||
{
|
||||
return dbx500_id.partnumber;
|
||||
}
|
||||
|
||||
static inline unsigned int __attribute_const__ dbx500_revision(void)
|
||||
{
|
||||
return dbx500_id.revision;
|
||||
}
|
||||
|
||||
/*
|
||||
* SOCs
|
||||
*/
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8500(void)
|
||||
{
|
||||
return dbx500_partnumber() == 0x8500;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8520(void)
|
||||
{
|
||||
return dbx500_partnumber() == 0x8520;
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500_family(void)
|
||||
{
|
||||
return cpu_is_u8500() || cpu_is_u8520();
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u9540(void)
|
||||
{
|
||||
return dbx500_partnumber() == 0x9540;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8540(void)
|
||||
{
|
||||
return dbx500_partnumber() == 0x8540;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8580(void)
|
||||
{
|
||||
return dbx500_partnumber() == 0x8580;
|
||||
}
|
||||
|
||||
static inline bool cpu_is_ux540_family(void)
|
||||
{
|
||||
return cpu_is_u9540() || cpu_is_u8540() || cpu_is_u8580();
|
||||
}
|
||||
|
||||
/*
|
||||
* 8500 revisions
|
||||
*/
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8500ed(void)
|
||||
{
|
||||
return cpu_is_u8500() && dbx500_revision() == 0x00;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8500v1(void)
|
||||
{
|
||||
return cpu_is_u8500() && (dbx500_revision() & 0xf0) == 0xA0;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8500v10(void)
|
||||
{
|
||||
return cpu_is_u8500() && dbx500_revision() == 0xA0;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8500v11(void)
|
||||
{
|
||||
return cpu_is_u8500() && dbx500_revision() == 0xA1;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8500v2(void)
|
||||
{
|
||||
return cpu_is_u8500() && ((dbx500_revision() & 0xf0) == 0xB0);
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500v20(void)
|
||||
{
|
||||
return cpu_is_u8500() && (dbx500_revision() == 0xB0);
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500v21(void)
|
||||
{
|
||||
return cpu_is_u8500() && (dbx500_revision() == 0xB1);
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500v22(void)
|
||||
{
|
||||
return cpu_is_u8500() && (dbx500_revision() == 0xB2);
|
||||
}
|
||||
|
||||
static inline bool cpu_is_u8500v20_or_later(void)
|
||||
{
|
||||
return (cpu_is_u8500() && !cpu_is_u8500v10() && !cpu_is_u8500v11());
|
||||
}
|
||||
|
||||
/*
|
||||
* 8540 revisions
|
||||
*/
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8540v10(void)
|
||||
{
|
||||
return cpu_is_u8540() && dbx500_revision() == 0xA0;
|
||||
}
|
||||
|
||||
static inline bool __attribute_const__ cpu_is_u8580v10(void)
|
||||
{
|
||||
return cpu_is_u8580() && dbx500_revision() == 0xA0;
|
||||
}
|
||||
|
||||
static inline bool ux500_is_svp(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#define ux500_unknown_soc() BUG()
|
||||
|
||||
#endif
|
@ -26,7 +26,6 @@
|
||||
#include "setup.h"
|
||||
|
||||
#include "db8500-regs.h"
|
||||
#include "id.h"
|
||||
|
||||
/* Magic triggers in backup RAM */
|
||||
#define UX500_CPU1_JUMPADDR_OFFSET 0x1FF4
|
||||
|
@ -11,18 +11,6 @@
|
||||
#ifndef __ASM_ARCH_SETUP_H
|
||||
#define __ASM_ARCH_SETUP_H
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
|
||||
void ux500_restart(enum reboot_mode mode, const char *cmd);
|
||||
|
||||
void __init ux500_setup_id(void);
|
||||
|
||||
extern void __init ux500_init_irq(void);
|
||||
|
||||
extern struct device *ux500_soc_device_init(const char *soc_id);
|
||||
|
||||
extern void ux500_cpu_die(unsigned int cpu);
|
||||
|
||||
#endif /* __ASM_ARCH_SETUP_H */
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/reset/hisi,hi6220-resets.h>
|
||||
#include <dt-bindings/clock/hi6220-clock.h>
|
||||
#include <dt-bindings/pinctrl/hisi.h>
|
||||
#include <dt-bindings/thermal/thermal.h>
|
||||
@ -252,6 +253,7 @@
|
||||
compatible = "hisilicon,hi6220-mediactrl", "syscon";
|
||||
reg = <0x0 0xf4410000 0x0 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
pm_ctrl: pm_ctrl@f7032000 {
|
||||
|
@ -132,6 +132,19 @@ config SUNXI_RSB
|
||||
with various RSB based devices, such as AXP223, AXP8XX PMICs,
|
||||
and AC100/AC200 ICs.
|
||||
|
||||
# TODO: This uses pm_clk_*() symbols that aren't exported in v4.7 and hence
|
||||
# the driver will fail to build as a module. However there are patches to
|
||||
# address that queued for v4.8, so this can be turned into a tristate symbol
|
||||
# after v4.8-rc1.
|
||||
config TEGRA_ACONNECT
|
||||
bool "Tegra ACONNECT Bus Driver"
|
||||
depends on ARCH_TEGRA_210_SOC
|
||||
depends on OF && PM
|
||||
select PM_CLK
|
||||
help
|
||||
Driver for the Tegra ACONNECT bus which is used to interface with
|
||||
the devices inside the Audio Processing Engine (APE) for Tegra210.
|
||||
|
||||
config UNIPHIER_SYSTEM_BUS
|
||||
tristate "UniPhier System Bus driver"
|
||||
depends on ARCH_UNIPHIER && OF
|
||||
|
@ -17,5 +17,6 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
|
||||
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
|
||||
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
|
||||
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
|
||||
obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o
|
||||
obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
|
||||
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
|
||||
|
112
drivers/bus/tegra-aconnect.c
Normal file
112
drivers/bus/tegra-aconnect.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Tegra ACONNECT Bus Driver
|
||||
*
|
||||
* Copyright (C) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_clock.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
static int tegra_aconnect_add_clock(struct device *dev, char *name)
|
||||
{
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = clk_get(dev, name);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "%s clock not found\n", name);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = pm_clk_add_clk(dev, clk);
|
||||
if (ret)
|
||||
clk_put(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_aconnect_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
return -EINVAL;
|
||||
|
||||
ret = pm_clk_create(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tegra_aconnect_add_clock(&pdev->dev, "ape");
|
||||
if (ret)
|
||||
goto clk_destroy;
|
||||
|
||||
ret = tegra_aconnect_add_clock(&pdev->dev, "apb2ape");
|
||||
if (ret)
|
||||
goto clk_destroy;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
|
||||
|
||||
dev_info(&pdev->dev, "Tegra ACONNECT bus registered\n");
|
||||
|
||||
return 0;
|
||||
|
||||
clk_destroy:
|
||||
pm_clk_destroy(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_aconnect_remove(struct platform_device *pdev)
|
||||
{
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
pm_clk_destroy(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_aconnect_runtime_resume(struct device *dev)
|
||||
{
|
||||
return pm_clk_resume(dev);
|
||||
}
|
||||
|
||||
static int tegra_aconnect_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return pm_clk_suspend(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra_aconnect_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(tegra_aconnect_runtime_suspend,
|
||||
tegra_aconnect_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_aconnect_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra210-aconnect", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_aconnect_of_match);
|
||||
|
||||
static struct platform_driver tegra_aconnect_driver = {
|
||||
.probe = tegra_aconnect_probe,
|
||||
.remove = tegra_aconnect_remove,
|
||||
.driver = {
|
||||
.name = "tegra-aconnect",
|
||||
.of_match_table = tegra_aconnect_of_match,
|
||||
.pm = &tegra_aconnect_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra_aconnect_driver);
|
||||
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra ACONNECT Bus Driver");
|
||||
MODULE_AUTHOR("Jon Hunter <jonathanh@nvidia.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -184,5 +184,5 @@ static void __init clps711x_clk_init_dt(struct device_node *np)
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get,
|
||||
&clps711x_clk->clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE(clps711x, "cirrus,clps711x-clk", clps711x_clk_init_dt);
|
||||
CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt);
|
||||
#endif
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/platform_data/clk-ux500.h>
|
||||
#include "clk.h"
|
||||
|
||||
#define PRCC_NUM_PERIPH_CLUSTERS 6
|
||||
@ -48,11 +47,6 @@ static struct clk *ux500_twocell_get(struct of_phandle_args *clkspec,
|
||||
return PRCC_SHOW(clk_data, base, bit);
|
||||
}
|
||||
|
||||
static const struct of_device_id u8500_clk_of_match[] = {
|
||||
{ .compatible = "stericsson,u8500-clks", },
|
||||
{ },
|
||||
};
|
||||
|
||||
/* CLKRST4 is missing making it hard to index things */
|
||||
enum clkrst_index {
|
||||
CLKRST1_INDEX = 0,
|
||||
@ -63,22 +57,15 @@ enum clkrst_index {
|
||||
CLKRST_MAX,
|
||||
};
|
||||
|
||||
void u8500_clk_init(void)
|
||||
static void u8500_clk_init(struct device_node *np)
|
||||
{
|
||||
struct prcmu_fw_version *fw_version;
|
||||
struct device_node *np = NULL;
|
||||
struct device_node *child = NULL;
|
||||
const char *sgaclk_parent = NULL;
|
||||
struct clk *clk, *rtc_clk, *twd_clk;
|
||||
u32 bases[CLKRST_MAX];
|
||||
int i;
|
||||
|
||||
if (of_have_populated_dt())
|
||||
np = of_find_matching_node(NULL, u8500_clk_of_match);
|
||||
if (!np) {
|
||||
pr_err("Either DT or U8500 Clock node not found\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(bases); i++) {
|
||||
struct resource r;
|
||||
|
||||
@ -573,3 +560,4 @@ void u8500_clk_init(void)
|
||||
of_clk_add_provider(child, of_clk_src_simple_get, twd_clk);
|
||||
}
|
||||
}
|
||||
CLK_OF_DECLARE(u8500_clks, "stericsson,u8500-clks", u8500_clk_init);
|
||||
|
@ -12,14 +12,8 @@
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/platform_data/clk-ux500.h>
|
||||
#include "clk.h"
|
||||
|
||||
static const struct of_device_id u8540_clk_of_match[] = {
|
||||
{ .compatible = "stericsson,u8540-clks", },
|
||||
{ }
|
||||
};
|
||||
|
||||
/* CLKRST4 is missing making it hard to index things */
|
||||
enum clkrst_index {
|
||||
CLKRST1_INDEX = 0,
|
||||
@ -30,19 +24,12 @@ enum clkrst_index {
|
||||
CLKRST_MAX,
|
||||
};
|
||||
|
||||
void u8540_clk_init(void)
|
||||
static void u8540_clk_init(struct device_node *np)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct device_node *np = NULL;
|
||||
u32 bases[CLKRST_MAX];
|
||||
int i;
|
||||
|
||||
if (of_have_populated_dt())
|
||||
np = of_find_matching_node(NULL, u8540_clk_of_match);
|
||||
if (!np) {
|
||||
pr_err("Either DT or U8540 Clock node not found\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(bases); i++) {
|
||||
struct resource r;
|
||||
|
||||
@ -607,3 +594,4 @@ void u8540_clk_init(void)
|
||||
bases[CLKRST6_INDEX], BIT(0), CLK_SET_RATE_GATE);
|
||||
clk_register_clkdev(clk, NULL, "rng");
|
||||
}
|
||||
CLK_OF_DECLARE(u8540_clks, "stericsson,u8540-clks", u8540_clk_init);
|
||||
|
@ -9,10 +9,10 @@
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/platform_data/clk-ux500.h>
|
||||
#include "clk.h"
|
||||
|
||||
void u9540_clk_init(void)
|
||||
static void u9540_clk_init(struct device_node *np)
|
||||
{
|
||||
/* register clocks here */
|
||||
}
|
||||
CLK_OF_DECLARE(u9540_clks, "stericsson,u9540-clks", u9540_clk_init);
|
||||
|
@ -119,5 +119,5 @@ static int __init clps711x_timer_init(struct device_node *np)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
|
||||
CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init);
|
||||
#endif
|
||||
|
@ -220,7 +220,7 @@ static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq)
|
||||
|
||||
tmp1 /= tmp;
|
||||
|
||||
__raw_writel(tmp1, reg);
|
||||
writel_relaxed(tmp1, reg);
|
||||
}
|
||||
|
||||
static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
@ -296,29 +296,29 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
* 1. Temporary Change divider for MFC and G3D
|
||||
* SCLKA2M(200/1=200)->(200/4=50)Mhz
|
||||
*/
|
||||
reg = __raw_readl(S5P_CLK_DIV2);
|
||||
reg = readl_relaxed(S5P_CLK_DIV2);
|
||||
reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
|
||||
reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) |
|
||||
(3 << S5P_CLKDIV2_MFC_SHIFT);
|
||||
__raw_writel(reg, S5P_CLK_DIV2);
|
||||
writel_relaxed(reg, S5P_CLK_DIV2);
|
||||
|
||||
/* For MFC, G3D dividing */
|
||||
do {
|
||||
reg = __raw_readl(S5P_CLKDIV_STAT0);
|
||||
reg = readl_relaxed(S5P_CLKDIV_STAT0);
|
||||
} while (reg & ((1 << 16) | (1 << 17)));
|
||||
|
||||
/*
|
||||
* 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX
|
||||
* (200/4=50)->(667/4=166)Mhz
|
||||
*/
|
||||
reg = __raw_readl(S5P_CLK_SRC2);
|
||||
reg = readl_relaxed(S5P_CLK_SRC2);
|
||||
reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
|
||||
reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) |
|
||||
(1 << S5P_CLKSRC2_MFC_SHIFT);
|
||||
__raw_writel(reg, S5P_CLK_SRC2);
|
||||
writel_relaxed(reg, S5P_CLK_SRC2);
|
||||
|
||||
do {
|
||||
reg = __raw_readl(S5P_CLKMUX_STAT1);
|
||||
reg = readl_relaxed(S5P_CLKMUX_STAT1);
|
||||
} while (reg & ((1 << 7) | (1 << 3)));
|
||||
|
||||
/*
|
||||
@ -330,19 +330,19 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
s5pv210_set_refresh(DMC1, 133000);
|
||||
|
||||
/* 4. SCLKAPLL -> SCLKMPLL */
|
||||
reg = __raw_readl(S5P_CLK_SRC0);
|
||||
reg = readl_relaxed(S5P_CLK_SRC0);
|
||||
reg &= ~(S5P_CLKSRC0_MUX200_MASK);
|
||||
reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
|
||||
__raw_writel(reg, S5P_CLK_SRC0);
|
||||
writel_relaxed(reg, S5P_CLK_SRC0);
|
||||
|
||||
do {
|
||||
reg = __raw_readl(S5P_CLKMUX_STAT0);
|
||||
reg = readl_relaxed(S5P_CLKMUX_STAT0);
|
||||
} while (reg & (0x1 << 18));
|
||||
|
||||
}
|
||||
|
||||
/* Change divider */
|
||||
reg = __raw_readl(S5P_CLK_DIV0);
|
||||
reg = readl_relaxed(S5P_CLK_DIV0);
|
||||
|
||||
reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK |
|
||||
S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK |
|
||||
@ -358,25 +358,25 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
(clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) |
|
||||
(clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
|
||||
|
||||
__raw_writel(reg, S5P_CLK_DIV0);
|
||||
writel_relaxed(reg, S5P_CLK_DIV0);
|
||||
|
||||
do {
|
||||
reg = __raw_readl(S5P_CLKDIV_STAT0);
|
||||
reg = readl_relaxed(S5P_CLKDIV_STAT0);
|
||||
} while (reg & 0xff);
|
||||
|
||||
/* ARM MCS value changed */
|
||||
reg = __raw_readl(S5P_ARM_MCS_CON);
|
||||
reg = readl_relaxed(S5P_ARM_MCS_CON);
|
||||
reg &= ~0x3;
|
||||
if (index >= L3)
|
||||
reg |= 0x3;
|
||||
else
|
||||
reg |= 0x1;
|
||||
|
||||
__raw_writel(reg, S5P_ARM_MCS_CON);
|
||||
writel_relaxed(reg, S5P_ARM_MCS_CON);
|
||||
|
||||
if (pll_changing) {
|
||||
/* 5. Set Lock time = 30us*24Mhz = 0x2cf */
|
||||
__raw_writel(0x2cf, S5P_APLL_LOCK);
|
||||
writel_relaxed(0x2cf, S5P_APLL_LOCK);
|
||||
|
||||
/*
|
||||
* 6. Turn on APLL
|
||||
@ -384,12 +384,12 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
* 6-2. Wait untile the PLL is locked
|
||||
*/
|
||||
if (index == L0)
|
||||
__raw_writel(APLL_VAL_1000, S5P_APLL_CON);
|
||||
writel_relaxed(APLL_VAL_1000, S5P_APLL_CON);
|
||||
else
|
||||
__raw_writel(APLL_VAL_800, S5P_APLL_CON);
|
||||
writel_relaxed(APLL_VAL_800, S5P_APLL_CON);
|
||||
|
||||
do {
|
||||
reg = __raw_readl(S5P_APLL_CON);
|
||||
reg = readl_relaxed(S5P_APLL_CON);
|
||||
} while (!(reg & (0x1 << 29)));
|
||||
|
||||
/*
|
||||
@ -397,39 +397,39 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
* to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX
|
||||
* (667/4=166)->(200/4=50)Mhz
|
||||
*/
|
||||
reg = __raw_readl(S5P_CLK_SRC2);
|
||||
reg = readl_relaxed(S5P_CLK_SRC2);
|
||||
reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
|
||||
reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) |
|
||||
(0 << S5P_CLKSRC2_MFC_SHIFT);
|
||||
__raw_writel(reg, S5P_CLK_SRC2);
|
||||
writel_relaxed(reg, S5P_CLK_SRC2);
|
||||
|
||||
do {
|
||||
reg = __raw_readl(S5P_CLKMUX_STAT1);
|
||||
reg = readl_relaxed(S5P_CLKMUX_STAT1);
|
||||
} while (reg & ((1 << 7) | (1 << 3)));
|
||||
|
||||
/*
|
||||
* 8. Change divider for MFC and G3D
|
||||
* (200/4=50)->(200/1=200)Mhz
|
||||
*/
|
||||
reg = __raw_readl(S5P_CLK_DIV2);
|
||||
reg = readl_relaxed(S5P_CLK_DIV2);
|
||||
reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
|
||||
reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
|
||||
(clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
|
||||
__raw_writel(reg, S5P_CLK_DIV2);
|
||||
writel_relaxed(reg, S5P_CLK_DIV2);
|
||||
|
||||
/* For MFC, G3D dividing */
|
||||
do {
|
||||
reg = __raw_readl(S5P_CLKDIV_STAT0);
|
||||
reg = readl_relaxed(S5P_CLKDIV_STAT0);
|
||||
} while (reg & ((1 << 16) | (1 << 17)));
|
||||
|
||||
/* 9. Change MPLL to APLL in MSYS_MUX */
|
||||
reg = __raw_readl(S5P_CLK_SRC0);
|
||||
reg = readl_relaxed(S5P_CLK_SRC0);
|
||||
reg &= ~(S5P_CLKSRC0_MUX200_MASK);
|
||||
reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT);
|
||||
__raw_writel(reg, S5P_CLK_SRC0);
|
||||
writel_relaxed(reg, S5P_CLK_SRC0);
|
||||
|
||||
do {
|
||||
reg = __raw_readl(S5P_CLKMUX_STAT0);
|
||||
reg = readl_relaxed(S5P_CLKMUX_STAT0);
|
||||
} while (reg & (0x1 << 18));
|
||||
|
||||
/*
|
||||
@ -446,13 +446,13 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
* and memory refresh parameter should be changed
|
||||
*/
|
||||
if (bus_speed_changing) {
|
||||
reg = __raw_readl(S5P_CLK_DIV6);
|
||||
reg = readl_relaxed(S5P_CLK_DIV6);
|
||||
reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
|
||||
reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
|
||||
__raw_writel(reg, S5P_CLK_DIV6);
|
||||
writel_relaxed(reg, S5P_CLK_DIV6);
|
||||
|
||||
do {
|
||||
reg = __raw_readl(S5P_CLKDIV_STAT1);
|
||||
reg = readl_relaxed(S5P_CLKDIV_STAT1);
|
||||
} while (reg & (1 << 15));
|
||||
|
||||
/* Reconfigure DRAM refresh counter value */
|
||||
@ -492,7 +492,7 @@ static int check_mem_type(void __iomem *dmc_reg)
|
||||
{
|
||||
unsigned long val;
|
||||
|
||||
val = __raw_readl(dmc_reg + 0x4);
|
||||
val = readl_relaxed(dmc_reg + 0x4);
|
||||
val = (val & (0xf << 8));
|
||||
|
||||
return val >> 8;
|
||||
@ -537,10 +537,10 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
|
||||
}
|
||||
|
||||
/* Find current refresh counter and frequency each DMC */
|
||||
s5pv210_dram_conf[0].refresh = (__raw_readl(dmc_base[0] + 0x30) * 1000);
|
||||
s5pv210_dram_conf[0].refresh = (readl_relaxed(dmc_base[0] + 0x30) * 1000);
|
||||
s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk);
|
||||
|
||||
s5pv210_dram_conf[1].refresh = (__raw_readl(dmc_base[1] + 0x30) * 1000);
|
||||
s5pv210_dram_conf[1].refresh = (readl_relaxed(dmc_base[1] + 0x30) * 1000);
|
||||
s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk);
|
||||
|
||||
policy->suspend_freq = SLEEP_FREQ;
|
||||
|
@ -10,7 +10,7 @@ config ARM_PSCI_FW
|
||||
|
||||
config ARM_SCPI_PROTOCOL
|
||||
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
|
||||
depends on ARM_MHU
|
||||
depends on MAILBOX
|
||||
help
|
||||
System Control and Power Interface (SCPI) Message Protocol is
|
||||
defined for the purpose of communication between the Application
|
||||
@ -27,6 +27,15 @@ config ARM_SCPI_PROTOCOL
|
||||
This protocol library provides interface for all the client drivers
|
||||
making use of the features offered by the SCP.
|
||||
|
||||
config ARM_SCPI_POWER_DOMAIN
|
||||
tristate "SCPI power domain driver"
|
||||
depends on ARM_SCPI_PROTOCOL || (COMPILE_TEST && OF)
|
||||
default y
|
||||
select PM_GENERIC_DOMAINS if PM
|
||||
help
|
||||
This enables support for the SCPI power domains which can be
|
||||
enabled or disabled via the SCP firmware
|
||||
|
||||
config EDD
|
||||
tristate "BIOS Enhanced Disk Drive calls determine boot disk"
|
||||
depends on X86
|
||||
@ -184,6 +193,7 @@ config FW_CFG_SYSFS_CMDLINE
|
||||
config QCOM_SCM
|
||||
bool
|
||||
depends on ARM || ARM64
|
||||
select RESET_CONTROLLER
|
||||
|
||||
config QCOM_SCM_32
|
||||
def_bool y
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
obj-$(CONFIG_ARM_PSCI_FW) += psci.o
|
||||
obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o
|
||||
obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o
|
||||
obj-$(CONFIG_DMI) += dmi_scan.o
|
||||
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
|
||||
obj-$(CONFIG_EDD) += edd.o
|
||||
|
@ -210,10 +210,6 @@ struct dvfs_info {
|
||||
} opps[MAX_DVFS_OPPS];
|
||||
} __packed;
|
||||
|
||||
struct dvfs_get {
|
||||
u8 index;
|
||||
} __packed;
|
||||
|
||||
struct dvfs_set {
|
||||
u8 domain;
|
||||
u8 index;
|
||||
@ -235,6 +231,11 @@ struct sensor_value {
|
||||
__le32 hi_val;
|
||||
} __packed;
|
||||
|
||||
struct dev_pstate_set {
|
||||
u16 dev_id;
|
||||
u8 pstate;
|
||||
} __packed;
|
||||
|
||||
static struct scpi_drvinfo *scpi_info;
|
||||
|
||||
static int scpi_linux_errmap[SCPI_ERR_MAX] = {
|
||||
@ -431,11 +432,11 @@ static int scpi_clk_set_val(u16 clk_id, unsigned long rate)
|
||||
static int scpi_dvfs_get_idx(u8 domain)
|
||||
{
|
||||
int ret;
|
||||
struct dvfs_get dvfs;
|
||||
u8 dvfs_idx;
|
||||
|
||||
ret = scpi_send_message(SCPI_CMD_GET_DVFS, &domain, sizeof(domain),
|
||||
&dvfs, sizeof(dvfs));
|
||||
return ret ? ret : dvfs.index;
|
||||
&dvfs_idx, sizeof(dvfs_idx));
|
||||
return ret ? ret : dvfs_idx;
|
||||
}
|
||||
|
||||
static int scpi_dvfs_set_idx(u8 domain, u8 index)
|
||||
@ -526,7 +527,7 @@ static int scpi_sensor_get_info(u16 sensor_id, struct scpi_sensor_info *info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int scpi_sensor_get_value(u16 sensor, u64 *val)
|
||||
static int scpi_sensor_get_value(u16 sensor, u64 *val)
|
||||
{
|
||||
__le16 id = cpu_to_le16(sensor);
|
||||
struct sensor_value buf;
|
||||
@ -541,6 +542,29 @@ int scpi_sensor_get_value(u16 sensor, u64 *val)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scpi_device_get_power_state(u16 dev_id)
|
||||
{
|
||||
int ret;
|
||||
u8 pstate;
|
||||
__le16 id = cpu_to_le16(dev_id);
|
||||
|
||||
ret = scpi_send_message(SCPI_CMD_GET_DEVICE_PWR_STATE, &id,
|
||||
sizeof(id), &pstate, sizeof(pstate));
|
||||
return ret ? ret : pstate;
|
||||
}
|
||||
|
||||
static int scpi_device_set_power_state(u16 dev_id, u8 pstate)
|
||||
{
|
||||
int stat;
|
||||
struct dev_pstate_set dev_set = {
|
||||
.dev_id = cpu_to_le16(dev_id),
|
||||
.pstate = pstate,
|
||||
};
|
||||
|
||||
return scpi_send_message(SCPI_CMD_SET_DEVICE_PWR_STATE, &dev_set,
|
||||
sizeof(dev_set), &stat, sizeof(stat));
|
||||
}
|
||||
|
||||
static struct scpi_ops scpi_ops = {
|
||||
.get_version = scpi_get_version,
|
||||
.clk_get_range = scpi_clk_get_range,
|
||||
@ -552,6 +576,8 @@ static struct scpi_ops scpi_ops = {
|
||||
.sensor_get_capability = scpi_sensor_get_capability,
|
||||
.sensor_get_info = scpi_sensor_get_info,
|
||||
.sensor_get_value = scpi_sensor_get_value,
|
||||
.device_get_power_state = scpi_device_get_power_state,
|
||||
.device_set_power_state = scpi_device_set_power_state,
|
||||
};
|
||||
|
||||
struct scpi_ops *get_scpi_ops(void)
|
||||
|
@ -23,8 +23,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "qcom_scm.h"
|
||||
|
||||
@ -96,44 +95,6 @@ struct qcom_scm_response {
|
||||
__le32 is_complete;
|
||||
};
|
||||
|
||||
/**
|
||||
* alloc_qcom_scm_command() - Allocate an SCM command
|
||||
* @cmd_size: size of the command buffer
|
||||
* @resp_size: size of the response buffer
|
||||
*
|
||||
* Allocate an SCM command, including enough room for the command
|
||||
* and response headers as well as the command and response buffers.
|
||||
*
|
||||
* Returns a valid &qcom_scm_command on success or %NULL if the allocation fails.
|
||||
*/
|
||||
static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size)
|
||||
{
|
||||
struct qcom_scm_command *cmd;
|
||||
size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size +
|
||||
resp_size;
|
||||
u32 offset;
|
||||
|
||||
cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
|
||||
if (cmd) {
|
||||
cmd->len = cpu_to_le32(len);
|
||||
offset = offsetof(struct qcom_scm_command, buf);
|
||||
cmd->buf_offset = cpu_to_le32(offset);
|
||||
cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size);
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* free_qcom_scm_command() - Free an SCM command
|
||||
* @cmd: command to free
|
||||
*
|
||||
* Free an SCM command.
|
||||
*/
|
||||
static inline void free_qcom_scm_command(struct qcom_scm_command *cmd)
|
||||
{
|
||||
kfree(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response
|
||||
* @cmd: command
|
||||
@ -168,23 +129,6 @@ static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response
|
||||
return (void *)rsp + le32_to_cpu(rsp->buf_offset);
|
||||
}
|
||||
|
||||
static int qcom_scm_remap_error(int err)
|
||||
{
|
||||
pr_err("qcom_scm_call failed with error code %d\n", err);
|
||||
switch (err) {
|
||||
case QCOM_SCM_ERROR:
|
||||
return -EIO;
|
||||
case QCOM_SCM_EINVAL_ADDR:
|
||||
case QCOM_SCM_EINVAL_ARG:
|
||||
return -EINVAL;
|
||||
case QCOM_SCM_EOPNOTSUPP:
|
||||
return -EOPNOTSUPP;
|
||||
case QCOM_SCM_ENOMEM:
|
||||
return -ENOMEM;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static u32 smc(u32 cmd_addr)
|
||||
{
|
||||
int context_id;
|
||||
@ -209,45 +153,9 @@ static u32 smc(u32 cmd_addr)
|
||||
return r0;
|
||||
}
|
||||
|
||||
static int __qcom_scm_call(const struct qcom_scm_command *cmd)
|
||||
{
|
||||
int ret;
|
||||
u32 cmd_addr = virt_to_phys(cmd);
|
||||
|
||||
/*
|
||||
* Flush the command buffer so that the secure world sees
|
||||
* the correct data.
|
||||
*/
|
||||
secure_flush_area(cmd, cmd->len);
|
||||
|
||||
ret = smc(cmd_addr);
|
||||
if (ret < 0)
|
||||
ret = qcom_scm_remap_error(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_scm_inv_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
u32 cacheline_size, ctr;
|
||||
|
||||
asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr));
|
||||
cacheline_size = 4 << ((ctr >> 16) & 0xf);
|
||||
|
||||
start = round_down(start, cacheline_size);
|
||||
end = round_up(end, cacheline_size);
|
||||
outer_inv_range(start, end);
|
||||
while (start < end) {
|
||||
asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
|
||||
: "memory");
|
||||
start += cacheline_size;
|
||||
}
|
||||
dsb();
|
||||
isb();
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_call() - Send an SCM command
|
||||
* @dev: struct device
|
||||
* @svc_id: service identifier
|
||||
* @cmd_id: command identifier
|
||||
* @cmd_buf: command buffer
|
||||
@ -264,42 +172,59 @@ static void qcom_scm_inv_range(unsigned long start, unsigned long end)
|
||||
* and response buffers is taken care of by qcom_scm_call; however, callers are
|
||||
* responsible for any other cached buffers passed over to the secure world.
|
||||
*/
|
||||
static int qcom_scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf,
|
||||
size_t cmd_len, void *resp_buf, size_t resp_len)
|
||||
static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
|
||||
const void *cmd_buf, size_t cmd_len, void *resp_buf,
|
||||
size_t resp_len)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_command *cmd;
|
||||
struct qcom_scm_response *rsp;
|
||||
unsigned long start, end;
|
||||
size_t alloc_len = sizeof(*cmd) + cmd_len + sizeof(*rsp) + resp_len;
|
||||
dma_addr_t cmd_phys;
|
||||
|
||||
cmd = alloc_qcom_scm_command(cmd_len, resp_len);
|
||||
cmd = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->len = cpu_to_le32(alloc_len);
|
||||
cmd->buf_offset = cpu_to_le32(sizeof(*cmd));
|
||||
cmd->resp_hdr_offset = cpu_to_le32(sizeof(*cmd) + cmd_len);
|
||||
|
||||
cmd->id = cpu_to_le32((svc_id << 10) | cmd_id);
|
||||
if (cmd_buf)
|
||||
memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len);
|
||||
|
||||
rsp = qcom_scm_command_to_response(cmd);
|
||||
|
||||
cmd_phys = dma_map_single(dev, cmd, alloc_len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev, cmd_phys)) {
|
||||
kfree(cmd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_lock(&qcom_scm_lock);
|
||||
ret = __qcom_scm_call(cmd);
|
||||
ret = smc(cmd_phys);
|
||||
if (ret < 0)
|
||||
ret = qcom_scm_remap_error(ret);
|
||||
mutex_unlock(&qcom_scm_lock);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
rsp = qcom_scm_command_to_response(cmd);
|
||||
start = (unsigned long)rsp;
|
||||
|
||||
do {
|
||||
qcom_scm_inv_range(start, start + sizeof(*rsp));
|
||||
dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len,
|
||||
sizeof(*rsp), DMA_FROM_DEVICE);
|
||||
} while (!rsp->is_complete);
|
||||
|
||||
end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len;
|
||||
qcom_scm_inv_range(start, end);
|
||||
|
||||
if (resp_buf)
|
||||
memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len);
|
||||
if (resp_buf) {
|
||||
dma_sync_single_for_cpu(dev, cmd_phys + sizeof(*cmd) + cmd_len +
|
||||
le32_to_cpu(rsp->buf_offset),
|
||||
resp_len, DMA_FROM_DEVICE);
|
||||
memcpy(resp_buf, qcom_scm_get_response_buffer(rsp),
|
||||
resp_len);
|
||||
}
|
||||
out:
|
||||
free_qcom_scm_command(cmd);
|
||||
dma_unmap_single(dev, cmd_phys, alloc_len, DMA_TO_DEVICE);
|
||||
kfree(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -342,6 +267,41 @@ static s32 qcom_scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
|
||||
return r0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_call_atomic2() - Send an atomic SCM command with two arguments
|
||||
* @svc_id: service identifier
|
||||
* @cmd_id: command identifier
|
||||
* @arg1: first argument
|
||||
* @arg2: second argument
|
||||
*
|
||||
* This shall only be used with commands that are guaranteed to be
|
||||
* uninterruptable, atomic and SMP safe.
|
||||
*/
|
||||
static s32 qcom_scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2)
|
||||
{
|
||||
int context_id;
|
||||
|
||||
register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2);
|
||||
register u32 r1 asm("r1") = (u32)&context_id;
|
||||
register u32 r2 asm("r2") = arg1;
|
||||
register u32 r3 asm("r3") = arg2;
|
||||
|
||||
asm volatile(
|
||||
__asmeq("%0", "r0")
|
||||
__asmeq("%1", "r0")
|
||||
__asmeq("%2", "r1")
|
||||
__asmeq("%3", "r2")
|
||||
__asmeq("%4", "r3")
|
||||
#ifdef REQUIRES_SEC
|
||||
".arch_extension sec\n"
|
||||
#endif
|
||||
"smc #0 @ switch to secure world\n"
|
||||
: "=r" (r0)
|
||||
: "r" (r0), "r" (r1), "r" (r2), "r" (r3)
|
||||
);
|
||||
return r0;
|
||||
}
|
||||
|
||||
u32 qcom_scm_get_version(void)
|
||||
{
|
||||
int context_id;
|
||||
@ -378,22 +338,6 @@ u32 qcom_scm_get_version(void)
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_get_version);
|
||||
|
||||
/*
|
||||
* Set the cold/warm boot address for one of the CPU cores.
|
||||
*/
|
||||
static int qcom_scm_set_boot_addr(u32 addr, int flags)
|
||||
{
|
||||
struct {
|
||||
__le32 flags;
|
||||
__le32 addr;
|
||||
} cmd;
|
||||
|
||||
cmd.addr = cpu_to_le32(addr);
|
||||
cmd.flags = cpu_to_le32(flags);
|
||||
return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
|
||||
&cmd, sizeof(cmd), NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
|
||||
* @entry: Entry point function for the cpus
|
||||
@ -423,7 +367,8 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
|
||||
set_cpu_present(cpu, false);
|
||||
}
|
||||
|
||||
return qcom_scm_set_boot_addr(virt_to_phys(entry), flags);
|
||||
return qcom_scm_call_atomic2(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
|
||||
flags, virt_to_phys(entry));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -434,11 +379,16 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
|
||||
* Set the Linux entry point for the SCM to transfer control to when coming
|
||||
* out of a power down. CPU power down may be executed on cpuidle or hotplug.
|
||||
*/
|
||||
int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
|
||||
int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
|
||||
const cpumask_t *cpus)
|
||||
{
|
||||
int ret;
|
||||
int flags = 0;
|
||||
int cpu;
|
||||
struct {
|
||||
__le32 flags;
|
||||
__le32 addr;
|
||||
} cmd;
|
||||
|
||||
/*
|
||||
* Reassign only if we are switching from hotplug entry point
|
||||
@ -454,7 +404,10 @@ int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
|
||||
if (!flags)
|
||||
return 0;
|
||||
|
||||
ret = qcom_scm_set_boot_addr(virt_to_phys(entry), flags);
|
||||
cmd.addr = cpu_to_le32(virt_to_phys(entry));
|
||||
cmd.flags = cpu_to_le32(flags);
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR,
|
||||
&cmd, sizeof(cmd), NULL, 0);
|
||||
if (!ret) {
|
||||
for_each_cpu(cpu, cpus)
|
||||
qcom_scm_wb[cpu].entry = entry;
|
||||
@ -477,25 +430,133 @@ void __qcom_scm_cpu_power_down(u32 flags)
|
||||
flags & QCOM_SCM_FLUSH_FLAG_MASK);
|
||||
}
|
||||
|
||||
int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id)
|
||||
int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
|
||||
{
|
||||
int ret;
|
||||
__le32 svc_cmd = cpu_to_le32((svc_id << 10) | cmd_id);
|
||||
__le32 ret_val = 0;
|
||||
|
||||
ret = qcom_scm_call(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD, &svc_cmd,
|
||||
sizeof(svc_cmd), &ret_val, sizeof(ret_val));
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD,
|
||||
&svc_cmd, sizeof(svc_cmd), &ret_val,
|
||||
sizeof(ret_val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return le32_to_cpu(ret_val);
|
||||
}
|
||||
|
||||
int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
|
||||
int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
|
||||
u32 req_cnt, u32 *resp)
|
||||
{
|
||||
if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT)
|
||||
return -ERANGE;
|
||||
|
||||
return qcom_scm_call(QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP,
|
||||
return qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP,
|
||||
req, req_cnt * sizeof(*req), resp, sizeof(*resp));
|
||||
}
|
||||
|
||||
void __qcom_scm_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
|
||||
{
|
||||
__le32 out;
|
||||
__le32 in;
|
||||
int ret;
|
||||
|
||||
in = cpu_to_le32(peripheral);
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PAS_IS_SUPPORTED_CMD,
|
||||
&in, sizeof(in),
|
||||
&out, sizeof(out));
|
||||
|
||||
return ret ? false : !!out;
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
|
||||
dma_addr_t metadata_phys)
|
||||
{
|
||||
__le32 scm_ret;
|
||||
int ret;
|
||||
struct {
|
||||
__le32 proc;
|
||||
__le32 image_addr;
|
||||
} request;
|
||||
|
||||
request.proc = cpu_to_le32(peripheral);
|
||||
request.image_addr = cpu_to_le32(metadata_phys);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PAS_INIT_IMAGE_CMD,
|
||||
&request, sizeof(request),
|
||||
&scm_ret, sizeof(scm_ret));
|
||||
|
||||
return ret ? : le32_to_cpu(scm_ret);
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
|
||||
phys_addr_t addr, phys_addr_t size)
|
||||
{
|
||||
__le32 scm_ret;
|
||||
int ret;
|
||||
struct {
|
||||
__le32 proc;
|
||||
__le32 addr;
|
||||
__le32 len;
|
||||
} request;
|
||||
|
||||
request.proc = cpu_to_le32(peripheral);
|
||||
request.addr = cpu_to_le32(addr);
|
||||
request.len = cpu_to_le32(size);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PAS_MEM_SETUP_CMD,
|
||||
&request, sizeof(request),
|
||||
&scm_ret, sizeof(scm_ret));
|
||||
|
||||
return ret ? : le32_to_cpu(scm_ret);
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral)
|
||||
{
|
||||
__le32 out;
|
||||
__le32 in;
|
||||
int ret;
|
||||
|
||||
in = cpu_to_le32(peripheral);
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
|
||||
&in, sizeof(in),
|
||||
&out, sizeof(out));
|
||||
|
||||
return ret ? : le32_to_cpu(out);
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral)
|
||||
{
|
||||
__le32 out;
|
||||
__le32 in;
|
||||
int ret;
|
||||
|
||||
in = cpu_to_le32(peripheral);
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PAS_SHUTDOWN_CMD,
|
||||
&in, sizeof(in),
|
||||
&out, sizeof(out));
|
||||
|
||||
return ret ? : le32_to_cpu(out);
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
|
||||
{
|
||||
__le32 out;
|
||||
__le32 in = cpu_to_le32(reset);
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET,
|
||||
&in, sizeof(in),
|
||||
&out, sizeof(out));
|
||||
|
||||
return ret ? : le32_to_cpu(out);
|
||||
}
|
||||
|
@ -12,7 +12,150 @@
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "qcom_scm.h"
|
||||
|
||||
#define QCOM_SCM_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))
|
||||
|
||||
#define MAX_QCOM_SCM_ARGS 10
|
||||
#define MAX_QCOM_SCM_RETS 3
|
||||
|
||||
enum qcom_scm_arg_types {
|
||||
QCOM_SCM_VAL,
|
||||
QCOM_SCM_RO,
|
||||
QCOM_SCM_RW,
|
||||
QCOM_SCM_BUFVAL,
|
||||
};
|
||||
|
||||
#define QCOM_SCM_ARGS_IMPL(num, a, b, c, d, e, f, g, h, i, j, ...) (\
|
||||
(((a) & 0x3) << 4) | \
|
||||
(((b) & 0x3) << 6) | \
|
||||
(((c) & 0x3) << 8) | \
|
||||
(((d) & 0x3) << 10) | \
|
||||
(((e) & 0x3) << 12) | \
|
||||
(((f) & 0x3) << 14) | \
|
||||
(((g) & 0x3) << 16) | \
|
||||
(((h) & 0x3) << 18) | \
|
||||
(((i) & 0x3) << 20) | \
|
||||
(((j) & 0x3) << 22) | \
|
||||
((num) & 0xf))
|
||||
|
||||
#define QCOM_SCM_ARGS(...) QCOM_SCM_ARGS_IMPL(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
|
||||
/**
|
||||
* struct qcom_scm_desc
|
||||
* @arginfo: Metadata describing the arguments in args[]
|
||||
* @args: The array of arguments for the secure syscall
|
||||
* @res: The values returned by the secure syscall
|
||||
*/
|
||||
struct qcom_scm_desc {
|
||||
u32 arginfo;
|
||||
u64 args[MAX_QCOM_SCM_ARGS];
|
||||
};
|
||||
|
||||
static u64 qcom_smccc_convention = -1;
|
||||
static DEFINE_MUTEX(qcom_scm_lock);
|
||||
|
||||
#define QCOM_SCM_EBUSY_WAIT_MS 30
|
||||
#define QCOM_SCM_EBUSY_MAX_RETRY 20
|
||||
|
||||
#define N_EXT_QCOM_SCM_ARGS 7
|
||||
#define FIRST_EXT_ARG_IDX 3
|
||||
#define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1)
|
||||
|
||||
/**
|
||||
* qcom_scm_call() - Invoke a syscall in the secure world
|
||||
* @dev: device
|
||||
* @svc_id: service identifier
|
||||
* @cmd_id: command identifier
|
||||
* @desc: Descriptor structure containing arguments and return values
|
||||
*
|
||||
* Sends a command to the SCM and waits for the command to finish processing.
|
||||
* This should *only* be called in pre-emptible context.
|
||||
*/
|
||||
static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id,
|
||||
const struct qcom_scm_desc *desc,
|
||||
struct arm_smccc_res *res)
|
||||
{
|
||||
int arglen = desc->arginfo & 0xf;
|
||||
int retry_count = 0, i;
|
||||
u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id);
|
||||
u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX];
|
||||
dma_addr_t args_phys = 0;
|
||||
void *args_virt = NULL;
|
||||
size_t alloc_len;
|
||||
|
||||
if (unlikely(arglen > N_REGISTER_ARGS)) {
|
||||
alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64);
|
||||
args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL);
|
||||
|
||||
if (!args_virt)
|
||||
return -ENOMEM;
|
||||
|
||||
if (qcom_smccc_convention == ARM_SMCCC_SMC_32) {
|
||||
__le32 *args = args_virt;
|
||||
|
||||
for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++)
|
||||
args[i] = cpu_to_le32(desc->args[i +
|
||||
FIRST_EXT_ARG_IDX]);
|
||||
} else {
|
||||
__le64 *args = args_virt;
|
||||
|
||||
for (i = 0; i < N_EXT_QCOM_SCM_ARGS; i++)
|
||||
args[i] = cpu_to_le64(desc->args[i +
|
||||
FIRST_EXT_ARG_IDX]);
|
||||
}
|
||||
|
||||
args_phys = dma_map_single(dev, args_virt, alloc_len,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
if (dma_mapping_error(dev, args_phys)) {
|
||||
kfree(args_virt);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
x5 = args_phys;
|
||||
}
|
||||
|
||||
do {
|
||||
mutex_lock(&qcom_scm_lock);
|
||||
|
||||
cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL,
|
||||
qcom_smccc_convention,
|
||||
ARM_SMCCC_OWNER_SIP, fn_id);
|
||||
|
||||
do {
|
||||
arm_smccc_smc(cmd, desc->arginfo, desc->args[0],
|
||||
desc->args[1], desc->args[2], x5, 0, 0,
|
||||
res);
|
||||
} while (res->a0 == QCOM_SCM_INTERRUPTED);
|
||||
|
||||
mutex_unlock(&qcom_scm_lock);
|
||||
|
||||
if (res->a0 == QCOM_SCM_V2_EBUSY) {
|
||||
if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY)
|
||||
break;
|
||||
msleep(QCOM_SCM_EBUSY_WAIT_MS);
|
||||
}
|
||||
} while (res->a0 == QCOM_SCM_V2_EBUSY);
|
||||
|
||||
if (args_virt) {
|
||||
dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE);
|
||||
kfree(args_virt);
|
||||
}
|
||||
|
||||
if (res->a0 < 0)
|
||||
return qcom_scm_remap_error(res->a0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
|
||||
@ -29,13 +172,15 @@ int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus)
|
||||
|
||||
/**
|
||||
* qcom_scm_set_warm_boot_addr() - Set the warm boot address for cpus
|
||||
* @dev: Device pointer
|
||||
* @entry: Entry point function for the cpus
|
||||
* @cpus: The cpumask of cpus that will use the entry point
|
||||
*
|
||||
* Set the Linux entry point for the SCM to transfer control to when coming
|
||||
* out of a power down. CPU power down may be executed on cpuidle or hotplug.
|
||||
*/
|
||||
int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
|
||||
int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
|
||||
const cpumask_t *cpus)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
@ -52,12 +197,164 @@ void __qcom_scm_cpu_power_down(u32 flags)
|
||||
{
|
||||
}
|
||||
|
||||
int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id)
|
||||
int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, u32 cmd_id)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
desc.arginfo = QCOM_SCM_ARGS(1);
|
||||
desc.args[0] = QCOM_SCM_FNID(svc_id, cmd_id) |
|
||||
(ARM_SMCCC_OWNER_SIP << ARM_SMCCC_OWNER_SHIFT);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD,
|
||||
&desc, &res);
|
||||
|
||||
return ret ? : res.a1;
|
||||
}
|
||||
|
||||
int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
|
||||
int __qcom_scm_hdcp_req(struct device *dev, struct qcom_scm_hdcp_req *req,
|
||||
u32 req_cnt, u32 *resp)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
if (req_cnt > QCOM_SCM_HDCP_MAX_REQ_CNT)
|
||||
return -ERANGE;
|
||||
|
||||
desc.args[0] = req[0].addr;
|
||||
desc.args[1] = req[0].val;
|
||||
desc.args[2] = req[1].addr;
|
||||
desc.args[3] = req[1].val;
|
||||
desc.args[4] = req[2].addr;
|
||||
desc.args[5] = req[2].val;
|
||||
desc.args[6] = req[3].addr;
|
||||
desc.args[7] = req[3].val;
|
||||
desc.args[8] = req[4].addr;
|
||||
desc.args[9] = req[4].val;
|
||||
desc.arginfo = QCOM_SCM_ARGS(10);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_HDCP, QCOM_SCM_CMD_HDCP, &desc,
|
||||
&res);
|
||||
*resp = res.a1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __qcom_scm_init(void)
|
||||
{
|
||||
u64 cmd;
|
||||
struct arm_smccc_res res;
|
||||
u32 function = QCOM_SCM_FNID(QCOM_SCM_SVC_INFO, QCOM_IS_CALL_AVAIL_CMD);
|
||||
|
||||
/* First try a SMC64 call */
|
||||
cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64,
|
||||
ARM_SMCCC_OWNER_SIP, function);
|
||||
|
||||
arm_smccc_smc(cmd, QCOM_SCM_ARGS(1), cmd & (~BIT(ARM_SMCCC_TYPE_SHIFT)),
|
||||
0, 0, 0, 0, 0, &res);
|
||||
|
||||
if (!res.a0 && res.a1)
|
||||
qcom_smccc_convention = ARM_SMCCC_SMC_64;
|
||||
else
|
||||
qcom_smccc_convention = ARM_SMCCC_SMC_32;
|
||||
}
|
||||
|
||||
bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
desc.args[0] = peripheral;
|
||||
desc.arginfo = QCOM_SCM_ARGS(1);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PAS_IS_SUPPORTED_CMD,
|
||||
&desc, &res);
|
||||
|
||||
return ret ? false : !!res.a1;
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
|
||||
dma_addr_t metadata_phys)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
desc.args[0] = peripheral;
|
||||
desc.args[1] = metadata_phys;
|
||||
desc.arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_INIT_IMAGE_CMD,
|
||||
&desc, &res);
|
||||
|
||||
return ret ? : res.a1;
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
|
||||
phys_addr_t addr, phys_addr_t size)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
desc.args[0] = peripheral;
|
||||
desc.args[1] = addr;
|
||||
desc.args[2] = size;
|
||||
desc.arginfo = QCOM_SCM_ARGS(3);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MEM_SETUP_CMD,
|
||||
&desc, &res);
|
||||
|
||||
return ret ? : res.a1;
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
desc.args[0] = peripheral;
|
||||
desc.arginfo = QCOM_SCM_ARGS(1);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PAS_AUTH_AND_RESET_CMD,
|
||||
&desc, &res);
|
||||
|
||||
return ret ? : res.a1;
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral)
|
||||
{
|
||||
int ret;
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
|
||||
desc.args[0] = peripheral;
|
||||
desc.arginfo = QCOM_SCM_ARGS(1);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_SHUTDOWN_CMD,
|
||||
&desc, &res);
|
||||
|
||||
return ret ? : res.a1;
|
||||
}
|
||||
|
||||
int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
|
||||
{
|
||||
struct qcom_scm_desc desc = {0};
|
||||
struct arm_smccc_res res;
|
||||
int ret;
|
||||
|
||||
desc.args[0] = reset;
|
||||
desc.args[1] = 0;
|
||||
desc.arginfo = QCOM_SCM_ARGS(2);
|
||||
|
||||
ret = qcom_scm_call(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PAS_MSS_RESET, &desc,
|
||||
&res);
|
||||
|
||||
return ret ? : res.a1;
|
||||
}
|
||||
|
@ -10,19 +10,64 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include "qcom_scm.h"
|
||||
|
||||
struct qcom_scm {
|
||||
struct device *dev;
|
||||
struct clk *core_clk;
|
||||
struct clk *iface_clk;
|
||||
struct clk *bus_clk;
|
||||
struct reset_controller_dev reset;
|
||||
};
|
||||
|
||||
static struct qcom_scm *__scm;
|
||||
|
||||
static int qcom_scm_clk_enable(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(__scm->core_clk);
|
||||
if (ret)
|
||||
goto bail;
|
||||
|
||||
ret = clk_prepare_enable(__scm->iface_clk);
|
||||
if (ret)
|
||||
goto disable_core;
|
||||
|
||||
ret = clk_prepare_enable(__scm->bus_clk);
|
||||
if (ret)
|
||||
goto disable_iface;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_iface:
|
||||
clk_disable_unprepare(__scm->iface_clk);
|
||||
disable_core:
|
||||
clk_disable_unprepare(__scm->core_clk);
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_scm_clk_disable(void)
|
||||
{
|
||||
clk_disable_unprepare(__scm->core_clk);
|
||||
clk_disable_unprepare(__scm->iface_clk);
|
||||
clk_disable_unprepare(__scm->bus_clk);
|
||||
}
|
||||
|
||||
/**
|
||||
* qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus
|
||||
* @entry: Entry point function for the cpus
|
||||
@ -47,7 +92,7 @@ EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr);
|
||||
*/
|
||||
int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus)
|
||||
{
|
||||
return __qcom_scm_set_warm_boot_addr(entry, cpus);
|
||||
return __qcom_scm_set_warm_boot_addr(__scm->dev, entry, cpus);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_set_warm_boot_addr);
|
||||
|
||||
@ -72,12 +117,17 @@ EXPORT_SYMBOL(qcom_scm_cpu_power_down);
|
||||
*/
|
||||
bool qcom_scm_hdcp_available(void)
|
||||
{
|
||||
int ret;
|
||||
int ret = qcom_scm_clk_enable();
|
||||
|
||||
ret = __qcom_scm_is_call_available(QCOM_SCM_SVC_HDCP,
|
||||
QCOM_SCM_CMD_HDCP);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (ret > 0) ? true : false;
|
||||
ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_HDCP,
|
||||
QCOM_SCM_CMD_HDCP);
|
||||
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret > 0 ? true : false;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_hdcp_available);
|
||||
|
||||
@ -91,6 +141,287 @@ EXPORT_SYMBOL(qcom_scm_hdcp_available);
|
||||
*/
|
||||
int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp)
|
||||
{
|
||||
return __qcom_scm_hdcp_req(req, req_cnt, resp);
|
||||
int ret = qcom_scm_clk_enable();
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __qcom_scm_hdcp_req(__scm->dev, req, req_cnt, resp);
|
||||
qcom_scm_clk_disable();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_hdcp_req);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_supported() - Check if the peripheral authentication service is
|
||||
* available for the given peripherial
|
||||
* @peripheral: peripheral id
|
||||
*
|
||||
* Returns true if PAS is supported for this peripheral, otherwise false.
|
||||
*/
|
||||
bool qcom_scm_pas_supported(u32 peripheral)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
|
||||
QCOM_SCM_PAS_IS_SUPPORTED_CMD);
|
||||
if (ret <= 0)
|
||||
return false;
|
||||
|
||||
return __qcom_scm_pas_supported(__scm->dev, peripheral);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_pas_supported);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_init_image() - Initialize peripheral authentication service
|
||||
* state machine for a given peripheral, using the
|
||||
* metadata
|
||||
* @peripheral: peripheral id
|
||||
* @metadata: pointer to memory containing ELF header, program header table
|
||||
* and optional blob of data used for authenticating the metadata
|
||||
* and the rest of the firmware
|
||||
* @size: size of the metadata
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size)
|
||||
{
|
||||
dma_addr_t mdata_phys;
|
||||
void *mdata_buf;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* During the scm call memory protection will be enabled for the meta
|
||||
* data blob, so make sure it's physically contiguous, 4K aligned and
|
||||
* non-cachable to avoid XPU violations.
|
||||
*/
|
||||
mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
|
||||
GFP_KERNEL);
|
||||
if (!mdata_buf) {
|
||||
dev_err(__scm->dev, "Allocation of metadata buffer failed.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(mdata_buf, metadata, size);
|
||||
|
||||
ret = qcom_scm_clk_enable();
|
||||
if (ret)
|
||||
goto free_metadata;
|
||||
|
||||
ret = __qcom_scm_pas_init_image(__scm->dev, peripheral, mdata_phys);
|
||||
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
free_metadata:
|
||||
dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_pas_init_image);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral
|
||||
* for firmware loading
|
||||
* @peripheral: peripheral id
|
||||
* @addr: start address of memory area to prepare
|
||||
* @size: size of the memory area to prepare
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_clk_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __qcom_scm_pas_mem_setup(__scm->dev, peripheral, addr, size);
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_pas_mem_setup);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
|
||||
* and reset the remote processor
|
||||
* @peripheral: peripheral id
|
||||
*
|
||||
* Return 0 on success.
|
||||
*/
|
||||
int qcom_scm_pas_auth_and_reset(u32 peripheral)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_clk_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __qcom_scm_pas_auth_and_reset(__scm->dev, peripheral);
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_pas_auth_and_reset);
|
||||
|
||||
/**
|
||||
* qcom_scm_pas_shutdown() - Shut down the remote processor
|
||||
* @peripheral: peripheral id
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int qcom_scm_pas_shutdown(u32 peripheral)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = qcom_scm_clk_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __qcom_scm_pas_shutdown(__scm->dev, peripheral);
|
||||
qcom_scm_clk_disable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_pas_shutdown);
|
||||
|
||||
static int qcom_scm_pas_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long idx)
|
||||
{
|
||||
if (idx != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return __qcom_scm_pas_mss_reset(__scm->dev, 1);
|
||||
}
|
||||
|
||||
static int qcom_scm_pas_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long idx)
|
||||
{
|
||||
if (idx != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return __qcom_scm_pas_mss_reset(__scm->dev, 0);
|
||||
}
|
||||
|
||||
static const struct reset_control_ops qcom_scm_pas_reset_ops = {
|
||||
.assert = qcom_scm_pas_reset_assert,
|
||||
.deassert = qcom_scm_pas_reset_deassert,
|
||||
};
|
||||
|
||||
/**
|
||||
* qcom_scm_is_available() - Checks if SCM is available
|
||||
*/
|
||||
bool qcom_scm_is_available(void)
|
||||
{
|
||||
return !!__scm;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_scm_is_available);
|
||||
|
||||
static int qcom_scm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_scm *scm;
|
||||
int ret;
|
||||
|
||||
scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL);
|
||||
if (!scm)
|
||||
return -ENOMEM;
|
||||
|
||||
scm->core_clk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(scm->core_clk)) {
|
||||
if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
|
||||
return PTR_ERR(scm->core_clk);
|
||||
|
||||
scm->core_clk = NULL;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "qcom,scm")) {
|
||||
scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
|
||||
if (IS_ERR(scm->iface_clk)) {
|
||||
if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "failed to acquire iface clk\n");
|
||||
return PTR_ERR(scm->iface_clk);
|
||||
}
|
||||
|
||||
scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
|
||||
if (IS_ERR(scm->bus_clk)) {
|
||||
if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "failed to acquire bus clk\n");
|
||||
return PTR_ERR(scm->bus_clk);
|
||||
}
|
||||
}
|
||||
|
||||
scm->reset.ops = &qcom_scm_pas_reset_ops;
|
||||
scm->reset.nr_resets = 1;
|
||||
scm->reset.of_node = pdev->dev.of_node;
|
||||
reset_controller_register(&scm->reset);
|
||||
|
||||
/* vote for max clk rate for highest performance */
|
||||
ret = clk_set_rate(scm->core_clk, INT_MAX);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
__scm = scm;
|
||||
__scm->dev = &pdev->dev;
|
||||
|
||||
__qcom_scm_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id qcom_scm_dt_match[] = {
|
||||
{ .compatible = "qcom,scm-apq8064",},
|
||||
{ .compatible = "qcom,scm-msm8660",},
|
||||
{ .compatible = "qcom,scm-msm8960",},
|
||||
{ .compatible = "qcom,scm",},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
|
||||
|
||||
static struct platform_driver qcom_scm_driver = {
|
||||
.driver = {
|
||||
.name = "qcom_scm",
|
||||
.of_match_table = qcom_scm_dt_match,
|
||||
},
|
||||
.probe = qcom_scm_probe,
|
||||
};
|
||||
|
||||
static int __init qcom_scm_init(void)
|
||||
{
|
||||
struct device_node *np, *fw_np;
|
||||
int ret;
|
||||
|
||||
fw_np = of_find_node_by_name(NULL, "firmware");
|
||||
|
||||
if (!fw_np)
|
||||
return -ENODEV;
|
||||
|
||||
np = of_find_matching_node(fw_np, qcom_scm_dt_match);
|
||||
|
||||
if (!np) {
|
||||
of_node_put(fw_np);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
ret = of_platform_populate(fw_np, qcom_scm_dt_match, NULL, NULL);
|
||||
|
||||
of_node_put(fw_np);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return platform_driver_register(&qcom_scm_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(qcom_scm_init);
|
||||
|
||||
static void __exit qcom_scm_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&qcom_scm_driver);
|
||||
}
|
||||
module_exit(qcom_scm_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm SCM driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -19,7 +19,8 @@
|
||||
#define QCOM_SCM_FLAG_HLOS 0x01
|
||||
#define QCOM_SCM_FLAG_COLDBOOT_MC 0x02
|
||||
#define QCOM_SCM_FLAG_WARMBOOT_MC 0x04
|
||||
extern int __qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus);
|
||||
extern int __qcom_scm_set_warm_boot_addr(struct device *dev, void *entry,
|
||||
const cpumask_t *cpus);
|
||||
extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus);
|
||||
|
||||
#define QCOM_SCM_CMD_TERMINATE_PC 0x2
|
||||
@ -29,14 +30,34 @@ extern void __qcom_scm_cpu_power_down(u32 flags);
|
||||
|
||||
#define QCOM_SCM_SVC_INFO 0x6
|
||||
#define QCOM_IS_CALL_AVAIL_CMD 0x1
|
||||
extern int __qcom_scm_is_call_available(u32 svc_id, u32 cmd_id);
|
||||
extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id,
|
||||
u32 cmd_id);
|
||||
|
||||
#define QCOM_SCM_SVC_HDCP 0x11
|
||||
#define QCOM_SCM_CMD_HDCP 0x01
|
||||
extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
|
||||
u32 *resp);
|
||||
extern int __qcom_scm_hdcp_req(struct device *dev,
|
||||
struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp);
|
||||
|
||||
extern void __qcom_scm_init(void);
|
||||
|
||||
#define QCOM_SCM_SVC_PIL 0x2
|
||||
#define QCOM_SCM_PAS_INIT_IMAGE_CMD 0x1
|
||||
#define QCOM_SCM_PAS_MEM_SETUP_CMD 0x2
|
||||
#define QCOM_SCM_PAS_AUTH_AND_RESET_CMD 0x5
|
||||
#define QCOM_SCM_PAS_SHUTDOWN_CMD 0x6
|
||||
#define QCOM_SCM_PAS_IS_SUPPORTED_CMD 0x7
|
||||
#define QCOM_SCM_PAS_MSS_RESET 0xa
|
||||
extern bool __qcom_scm_pas_supported(struct device *dev, u32 peripheral);
|
||||
extern int __qcom_scm_pas_init_image(struct device *dev, u32 peripheral,
|
||||
dma_addr_t metadata_phys);
|
||||
extern int __qcom_scm_pas_mem_setup(struct device *dev, u32 peripheral,
|
||||
phys_addr_t addr, phys_addr_t size);
|
||||
extern int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 peripheral);
|
||||
extern int __qcom_scm_pas_shutdown(struct device *dev, u32 peripheral);
|
||||
extern int __qcom_scm_pas_mss_reset(struct device *dev, bool reset);
|
||||
|
||||
/* common error codes */
|
||||
#define QCOM_SCM_V2_EBUSY -12
|
||||
#define QCOM_SCM_ENOMEM -5
|
||||
#define QCOM_SCM_EOPNOTSUPP -4
|
||||
#define QCOM_SCM_EINVAL_ADDR -3
|
||||
@ -44,4 +65,22 @@ extern int __qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt,
|
||||
#define QCOM_SCM_ERROR -1
|
||||
#define QCOM_SCM_INTERRUPTED 1
|
||||
|
||||
static inline int qcom_scm_remap_error(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case QCOM_SCM_ERROR:
|
||||
return -EIO;
|
||||
case QCOM_SCM_EINVAL_ADDR:
|
||||
case QCOM_SCM_EINVAL_ARG:
|
||||
return -EINVAL;
|
||||
case QCOM_SCM_EOPNOTSUPP:
|
||||
return -EOPNOTSUPP;
|
||||
case QCOM_SCM_ENOMEM:
|
||||
return -ENOMEM;
|
||||
case QCOM_SCM_V2_EBUSY:
|
||||
return -EBUSY;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
163
drivers/firmware/scpi_pm_domain.c
Normal file
163
drivers/firmware/scpi_pm_domain.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* SCPI Generic power domain support.
|
||||
*
|
||||
* Copyright (C) 2016 ARM Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/scpi_protocol.h>
|
||||
|
||||
struct scpi_pm_domain {
|
||||
struct generic_pm_domain genpd;
|
||||
struct scpi_ops *ops;
|
||||
u32 domain;
|
||||
char name[30];
|
||||
};
|
||||
|
||||
/*
|
||||
* These device power state values are not well-defined in the specification.
|
||||
* In case, different implementations use different values, we can make these
|
||||
* specific to compatibles rather than getting these values from device tree.
|
||||
*/
|
||||
enum scpi_power_domain_state {
|
||||
SCPI_PD_STATE_ON = 0,
|
||||
SCPI_PD_STATE_OFF = 3,
|
||||
};
|
||||
|
||||
#define to_scpi_pd(gpd) container_of(gpd, struct scpi_pm_domain, genpd)
|
||||
|
||||
static int scpi_pd_power(struct scpi_pm_domain *pd, bool power_on)
|
||||
{
|
||||
int ret;
|
||||
enum scpi_power_domain_state state;
|
||||
|
||||
if (power_on)
|
||||
state = SCPI_PD_STATE_ON;
|
||||
else
|
||||
state = SCPI_PD_STATE_OFF;
|
||||
|
||||
ret = pd->ops->device_set_power_state(pd->domain, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return !(state == pd->ops->device_get_power_state(pd->domain));
|
||||
}
|
||||
|
||||
static int scpi_pd_power_on(struct generic_pm_domain *domain)
|
||||
{
|
||||
struct scpi_pm_domain *pd = to_scpi_pd(domain);
|
||||
|
||||
return scpi_pd_power(pd, true);
|
||||
}
|
||||
|
||||
static int scpi_pd_power_off(struct generic_pm_domain *domain)
|
||||
{
|
||||
struct scpi_pm_domain *pd = to_scpi_pd(domain);
|
||||
|
||||
return scpi_pd_power(pd, false);
|
||||
}
|
||||
|
||||
static int scpi_pm_domain_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct scpi_pm_domain *scpi_pd;
|
||||
struct genpd_onecell_data *scpi_pd_data;
|
||||
struct generic_pm_domain **domains;
|
||||
struct scpi_ops *scpi_ops;
|
||||
int ret, num_domains, i;
|
||||
|
||||
scpi_ops = get_scpi_ops();
|
||||
if (!scpi_ops)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!np) {
|
||||
dev_err(dev, "device tree node not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!scpi_ops->device_set_power_state ||
|
||||
!scpi_ops->device_get_power_state) {
|
||||
dev_err(dev, "power domains not supported in the firmware\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "num-domains", &num_domains);
|
||||
if (ret) {
|
||||
dev_err(dev, "number of domains not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
scpi_pd = devm_kcalloc(dev, num_domains, sizeof(*scpi_pd), GFP_KERNEL);
|
||||
if (!scpi_pd)
|
||||
return -ENOMEM;
|
||||
|
||||
scpi_pd_data = devm_kzalloc(dev, sizeof(*scpi_pd_data), GFP_KERNEL);
|
||||
if (!scpi_pd_data)
|
||||
return -ENOMEM;
|
||||
|
||||
domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
|
||||
if (!domains)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_domains; i++, scpi_pd++) {
|
||||
domains[i] = &scpi_pd->genpd;
|
||||
|
||||
scpi_pd->domain = i;
|
||||
scpi_pd->ops = scpi_ops;
|
||||
sprintf(scpi_pd->name, "%s.%d", np->name, i);
|
||||
scpi_pd->genpd.name = scpi_pd->name;
|
||||
scpi_pd->genpd.power_off = scpi_pd_power_off;
|
||||
scpi_pd->genpd.power_on = scpi_pd_power_on;
|
||||
|
||||
/*
|
||||
* Treat all power domains as off at boot.
|
||||
*
|
||||
* The SCP firmware itself may have switched on some domains,
|
||||
* but for reference counting purpose, keep it this way.
|
||||
*/
|
||||
pm_genpd_init(&scpi_pd->genpd, NULL, true);
|
||||
}
|
||||
|
||||
scpi_pd_data->domains = domains;
|
||||
scpi_pd_data->num_domains = num_domains;
|
||||
|
||||
of_genpd_add_provider_onecell(np, scpi_pd_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id scpi_power_domain_ids[] = {
|
||||
{ .compatible = "arm,scpi-power-domains", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, scpi_power_domain_ids);
|
||||
|
||||
static struct platform_driver scpi_power_domain_driver = {
|
||||
.driver = {
|
||||
.name = "scpi_power_domain",
|
||||
.of_match_table = scpi_power_domain_ids,
|
||||
},
|
||||
.probe = scpi_pm_domain_probe,
|
||||
};
|
||||
module_platform_driver(scpi_power_domain_driver);
|
||||
|
||||
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
|
||||
MODULE_DESCRIPTION("ARM SCPI power domain driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -101,7 +101,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->syscon =
|
||||
syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1");
|
||||
syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon1");
|
||||
if (IS_ERR(priv->syscon))
|
||||
return PTR_ERR(priv->syscon);
|
||||
|
||||
@ -181,7 +181,7 @@ static int clps711x_keypad_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id clps711x_keypad_of_match[] = {
|
||||
{ .compatible = "cirrus,clps711x-keypad", },
|
||||
{ .compatible = "cirrus,ep7209-keypad", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match);
|
||||
|
@ -234,5 +234,5 @@ static int __init clps711x_intc_init_dt(struct device_node *np,
|
||||
|
||||
return _clps711x_intc_init(np, res.start, resource_size(&res));
|
||||
}
|
||||
IRQCHIP_DECLARE(clps711x, "cirrus,clps711x-intc", clps711x_intc_init_dt);
|
||||
IRQCHIP_DECLARE(clps711x, "cirrus,ep7209-intc", clps711x_intc_init_dt);
|
||||
#endif
|
||||
|
@ -336,7 +336,7 @@ config IR_TTUSBIR
|
||||
|
||||
config IR_RX51
|
||||
tristate "Nokia N900 IR transmitter diode"
|
||||
depends on OMAP_DM_TIMER && ARCH_OMAP2PLUS && LIRC && !ARCH_MULTIPLATFORM
|
||||
depends on OMAP_DM_TIMER && PWM_OMAP_DMTIMER && ARCH_OMAP2PLUS && LIRC
|
||||
---help---
|
||||
Say Y or M here if you want to enable support for the IR
|
||||
transmitter diode built in the Nokia N900 (RX51) device.
|
||||
|
@ -12,22 +12,17 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <plat/dmtimer.h>
|
||||
#include <plat/clock.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/hrtimer.h>
|
||||
|
||||
#include <media/lirc.h>
|
||||
#include <media/lirc_dev.h>
|
||||
@ -41,100 +36,51 @@
|
||||
|
||||
#define WBUF_LEN 256
|
||||
|
||||
#define TIMER_MAX_VALUE 0xffffffff
|
||||
|
||||
struct lirc_rx51 {
|
||||
struct omap_dm_timer *pwm_timer;
|
||||
struct omap_dm_timer *pulse_timer;
|
||||
struct pwm_device *pwm;
|
||||
struct hrtimer timer;
|
||||
struct device *dev;
|
||||
struct lirc_rx51_platform_data *pdata;
|
||||
wait_queue_head_t wqueue;
|
||||
|
||||
unsigned long fclk_khz;
|
||||
unsigned int freq; /* carrier frequency */
|
||||
unsigned int duty_cycle; /* carrier duty cycle */
|
||||
unsigned int irq_num;
|
||||
unsigned int match;
|
||||
int wbuf[WBUF_LEN];
|
||||
int wbuf_index;
|
||||
unsigned long device_is_open;
|
||||
int pwm_timer_num;
|
||||
};
|
||||
|
||||
static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51)
|
||||
static inline void lirc_rx51_on(struct lirc_rx51 *lirc_rx51)
|
||||
{
|
||||
omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
|
||||
OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
|
||||
pwm_enable(lirc_rx51->pwm);
|
||||
}
|
||||
|
||||
static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51)
|
||||
static inline void lirc_rx51_off(struct lirc_rx51 *lirc_rx51)
|
||||
{
|
||||
omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
|
||||
OMAP_TIMER_TRIGGER_NONE);
|
||||
pwm_disable(lirc_rx51->pwm);
|
||||
}
|
||||
|
||||
static int init_timing_params(struct lirc_rx51 *lirc_rx51)
|
||||
{
|
||||
u32 load, match;
|
||||
struct pwm_device *pwm = lirc_rx51->pwm;
|
||||
int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, lirc_rx51->freq);
|
||||
|
||||
load = -(lirc_rx51->fclk_khz * 1000 / lirc_rx51->freq);
|
||||
match = -(lirc_rx51->duty_cycle * -load / 100);
|
||||
omap_dm_timer_set_load(lirc_rx51->pwm_timer, 1, load);
|
||||
omap_dm_timer_set_match(lirc_rx51->pwm_timer, 1, match);
|
||||
omap_dm_timer_write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2);
|
||||
omap_dm_timer_start(lirc_rx51->pwm_timer);
|
||||
omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
|
||||
omap_dm_timer_start(lirc_rx51->pulse_timer);
|
||||
duty = DIV_ROUND_CLOSEST(lirc_rx51->duty_cycle * period, 100);
|
||||
|
||||
lirc_rx51->match = 0;
|
||||
pwm_config(pwm, duty, period);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define tics_after(a, b) ((long)(b) - (long)(a) < 0)
|
||||
|
||||
static int pulse_timer_set_timeout(struct lirc_rx51 *lirc_rx51, int usec)
|
||||
static enum hrtimer_restart lirc_rx51_timer_cb(struct hrtimer *timer)
|
||||
{
|
||||
int counter;
|
||||
struct lirc_rx51 *lirc_rx51 =
|
||||
container_of(timer, struct lirc_rx51, timer);
|
||||
ktime_t now;
|
||||
|
||||
BUG_ON(usec < 0);
|
||||
|
||||
if (lirc_rx51->match == 0)
|
||||
counter = omap_dm_timer_read_counter(lirc_rx51->pulse_timer);
|
||||
else
|
||||
counter = lirc_rx51->match;
|
||||
|
||||
counter += (u32)(lirc_rx51->fclk_khz * usec / (1000));
|
||||
omap_dm_timer_set_match(lirc_rx51->pulse_timer, 1, counter);
|
||||
omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer,
|
||||
OMAP_TIMER_INT_MATCH);
|
||||
if (tics_after(omap_dm_timer_read_counter(lirc_rx51->pulse_timer),
|
||||
counter)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr)
|
||||
{
|
||||
unsigned int retval;
|
||||
struct lirc_rx51 *lirc_rx51 = ptr;
|
||||
|
||||
retval = omap_dm_timer_read_status(lirc_rx51->pulse_timer);
|
||||
if (!retval)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (retval & ~OMAP_TIMER_INT_MATCH)
|
||||
dev_err_ratelimited(lirc_rx51->dev,
|
||||
": Unexpected interrupt source: %x\n", retval);
|
||||
|
||||
omap_dm_timer_write_status(lirc_rx51->pulse_timer,
|
||||
OMAP_TIMER_INT_MATCH |
|
||||
OMAP_TIMER_INT_OVERFLOW |
|
||||
OMAP_TIMER_INT_CAPTURE);
|
||||
if (lirc_rx51->wbuf_index < 0) {
|
||||
dev_err_ratelimited(lirc_rx51->dev,
|
||||
": BUG wbuf_index has value of %i\n",
|
||||
"BUG wbuf_index has value of %i\n",
|
||||
lirc_rx51->wbuf_index);
|
||||
goto end;
|
||||
}
|
||||
@ -144,6 +90,8 @@ static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr)
|
||||
* pulses until we catch up.
|
||||
*/
|
||||
do {
|
||||
u64 ns;
|
||||
|
||||
if (lirc_rx51->wbuf_index >= WBUF_LEN)
|
||||
goto end;
|
||||
if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1)
|
||||
@ -154,84 +102,24 @@ static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr)
|
||||
else
|
||||
lirc_rx51_on(lirc_rx51);
|
||||
|
||||
retval = pulse_timer_set_timeout(lirc_rx51,
|
||||
lirc_rx51->wbuf[lirc_rx51->wbuf_index]);
|
||||
ns = 1000 * lirc_rx51->wbuf[lirc_rx51->wbuf_index];
|
||||
hrtimer_add_expires_ns(timer, ns);
|
||||
|
||||
lirc_rx51->wbuf_index++;
|
||||
|
||||
} while (retval);
|
||||
now = timer->base->get_time();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
} while (hrtimer_get_expires_tv64(timer) < now.tv64);
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
end:
|
||||
/* Stop TX here */
|
||||
lirc_rx51_off(lirc_rx51);
|
||||
lirc_rx51->wbuf_index = -1;
|
||||
omap_dm_timer_stop(lirc_rx51->pwm_timer);
|
||||
omap_dm_timer_stop(lirc_rx51->pulse_timer);
|
||||
omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
|
||||
|
||||
wake_up_interruptible(&lirc_rx51->wqueue);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51)
|
||||
{
|
||||
struct clk *clk_fclk;
|
||||
int retval, pwm_timer = lirc_rx51->pwm_timer_num;
|
||||
|
||||
lirc_rx51->pwm_timer = omap_dm_timer_request_specific(pwm_timer);
|
||||
if (lirc_rx51->pwm_timer == NULL) {
|
||||
dev_err(lirc_rx51->dev, ": Error requesting GPT%d timer\n",
|
||||
pwm_timer);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
lirc_rx51->pulse_timer = omap_dm_timer_request();
|
||||
if (lirc_rx51->pulse_timer == NULL) {
|
||||
dev_err(lirc_rx51->dev, ": Error requesting pulse timer\n");
|
||||
retval = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
omap_dm_timer_set_source(lirc_rx51->pwm_timer, OMAP_TIMER_SRC_SYS_CLK);
|
||||
omap_dm_timer_set_source(lirc_rx51->pulse_timer,
|
||||
OMAP_TIMER_SRC_SYS_CLK);
|
||||
|
||||
omap_dm_timer_enable(lirc_rx51->pwm_timer);
|
||||
omap_dm_timer_enable(lirc_rx51->pulse_timer);
|
||||
|
||||
lirc_rx51->irq_num = omap_dm_timer_get_irq(lirc_rx51->pulse_timer);
|
||||
retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler,
|
||||
IRQF_SHARED, "lirc_pulse_timer", lirc_rx51);
|
||||
if (retval) {
|
||||
dev_err(lirc_rx51->dev, ": Failed to request interrupt line\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
clk_fclk = omap_dm_timer_get_fclk(lirc_rx51->pwm_timer);
|
||||
lirc_rx51->fclk_khz = clk_fclk->rate / 1000;
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
omap_dm_timer_free(lirc_rx51->pulse_timer);
|
||||
err1:
|
||||
omap_dm_timer_free(lirc_rx51->pwm_timer);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51)
|
||||
{
|
||||
omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
|
||||
free_irq(lirc_rx51->irq_num, lirc_rx51);
|
||||
lirc_rx51_off(lirc_rx51);
|
||||
omap_dm_timer_disable(lirc_rx51->pwm_timer);
|
||||
omap_dm_timer_disable(lirc_rx51->pulse_timer);
|
||||
omap_dm_timer_free(lirc_rx51->pwm_timer);
|
||||
omap_dm_timer_free(lirc_rx51->pulse_timer);
|
||||
lirc_rx51->wbuf_index = -1;
|
||||
|
||||
return 0;
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static ssize_t lirc_rx51_write(struct file *file, const char *buf,
|
||||
@ -270,8 +158,9 @@ static ssize_t lirc_rx51_write(struct file *file, const char *buf,
|
||||
|
||||
lirc_rx51_on(lirc_rx51);
|
||||
lirc_rx51->wbuf_index = 1;
|
||||
pulse_timer_set_timeout(lirc_rx51, lirc_rx51->wbuf[0]);
|
||||
|
||||
hrtimer_start(&lirc_rx51->timer,
|
||||
ns_to_ktime(1000 * lirc_rx51->wbuf[0]),
|
||||
HRTIMER_MODE_REL);
|
||||
/*
|
||||
* Don't return back to the userspace until the transfer has
|
||||
* finished
|
||||
@ -371,14 +260,24 @@ static int lirc_rx51_open(struct inode *inode, struct file *file)
|
||||
if (test_and_set_bit(1, &lirc_rx51->device_is_open))
|
||||
return -EBUSY;
|
||||
|
||||
return lirc_rx51_init_port(lirc_rx51);
|
||||
lirc_rx51->pwm = pwm_get(lirc_rx51->dev, NULL);
|
||||
if (IS_ERR(lirc_rx51->pwm)) {
|
||||
int res = PTR_ERR(lirc_rx51->pwm);
|
||||
|
||||
dev_err(lirc_rx51->dev, "pwm_get failed: %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lirc_rx51_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct lirc_rx51 *lirc_rx51 = file->private_data;
|
||||
|
||||
lirc_rx51_free_port(lirc_rx51);
|
||||
hrtimer_cancel(&lirc_rx51->timer);
|
||||
lirc_rx51_off(lirc_rx51);
|
||||
pwm_put(lirc_rx51->pwm);
|
||||
|
||||
clear_bit(1, &lirc_rx51->device_is_open);
|
||||
|
||||
@ -386,7 +285,6 @@ static int lirc_rx51_release(struct inode *inode, struct file *file)
|
||||
}
|
||||
|
||||
static struct lirc_rx51 lirc_rx51 = {
|
||||
.freq = 38000,
|
||||
.duty_cycle = 50,
|
||||
.wbuf_index = -1,
|
||||
};
|
||||
@ -444,9 +342,32 @@ static int lirc_rx51_resume(struct platform_device *dev)
|
||||
|
||||
static int lirc_rx51_probe(struct platform_device *dev)
|
||||
{
|
||||
struct pwm_device *pwm;
|
||||
|
||||
lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES;
|
||||
lirc_rx51.pdata = dev->dev.platform_data;
|
||||
lirc_rx51.pwm_timer_num = lirc_rx51.pdata->pwm_timer;
|
||||
|
||||
if (!lirc_rx51.pdata) {
|
||||
dev_err(&dev->dev, "Platform Data is missing\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
pwm = pwm_get(&dev->dev, NULL);
|
||||
if (IS_ERR(pwm)) {
|
||||
int err = PTR_ERR(pwm);
|
||||
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(&dev->dev, "pwm_get failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Use default, in case userspace does not set the carrier */
|
||||
lirc_rx51.freq = DIV_ROUND_CLOSEST(pwm_get_period(pwm), NSEC_PER_SEC);
|
||||
pwm_put(pwm);
|
||||
|
||||
hrtimer_init(&lirc_rx51.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
lirc_rx51.timer.function = lirc_rx51_timer_cb;
|
||||
|
||||
lirc_rx51.dev = &dev->dev;
|
||||
lirc_rx51_driver.dev = &dev->dev;
|
||||
lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver);
|
||||
@ -457,8 +378,6 @@ static int lirc_rx51_probe(struct platform_device *dev)
|
||||
lirc_rx51_driver.minor);
|
||||
return lirc_rx51_driver.minor;
|
||||
}
|
||||
dev_info(lirc_rx51.dev, "registration ok, minor: %d, pwm: %d\n",
|
||||
lirc_rx51_driver.minor, lirc_rx51.pwm_timer_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -468,6 +387,14 @@ static int lirc_rx51_remove(struct platform_device *dev)
|
||||
return lirc_unregister_driver(lirc_rx51_driver.minor);
|
||||
}
|
||||
|
||||
static const struct of_device_id lirc_rx51_match[] = {
|
||||
{
|
||||
.compatible = "nokia,n900-ir",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lirc_rx51_match);
|
||||
|
||||
struct platform_driver lirc_rx51_platform_driver = {
|
||||
.probe = lirc_rx51_probe,
|
||||
.remove = lirc_rx51_remove,
|
||||
@ -475,7 +402,7 @@ struct platform_driver lirc_rx51_platform_driver = {
|
||||
.resume = lirc_rx51_resume,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(lirc_rx51_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(lirc_rx51_platform_driver);
|
||||
|
@ -25,6 +25,17 @@ config ATMEL_SDRAMC
|
||||
Starting with the at91sam9g45, this controller supports SDR, DDR and
|
||||
LP-DDR memories.
|
||||
|
||||
config ATMEL_EBI
|
||||
bool "Atmel EBI driver"
|
||||
default y
|
||||
depends on ARCH_AT91 && OF
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Driver for Atmel EBI controller.
|
||||
Used to configure the EBI (external bus interface) when the device-
|
||||
tree is used. This bus supports NANDs, external ethernet controller,
|
||||
SRAMs, ATA devices, etc.
|
||||
|
||||
config TI_AEMIF
|
||||
tristate "Texas Instruments AEMIF driver"
|
||||
depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
|
||||
|
@ -7,6 +7,7 @@ obj-$(CONFIG_OF) += of_memory.o
|
||||
endif
|
||||
obj-$(CONFIG_ARM_PL172_MPMC) += pl172.o
|
||||
obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o
|
||||
obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o
|
||||
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
|
||||
obj-$(CONFIG_TI_EMIF) += emif.o
|
||||
obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
|
||||
|
766
drivers/memory/atmel-ebi.c
Normal file
766
drivers/memory/atmel-ebi.c
Normal file
@ -0,0 +1,766 @@
|
||||
/*
|
||||
* EBI driver for Atmel chips
|
||||
* inspired by the fsl weim bus driver
|
||||
*
|
||||
* Copyright (C) 2013 Jean-Jacques Hiblot <jjhiblot@traphandler.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.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/atmel-matrix.h>
|
||||
#include <linux/mfd/syscon/atmel-smc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct at91sam9_smc_timings {
|
||||
u32 ncs_rd_setup_ns;
|
||||
u32 nrd_setup_ns;
|
||||
u32 ncs_wr_setup_ns;
|
||||
u32 nwe_setup_ns;
|
||||
u32 ncs_rd_pulse_ns;
|
||||
u32 nrd_pulse_ns;
|
||||
u32 ncs_wr_pulse_ns;
|
||||
u32 nwe_pulse_ns;
|
||||
u32 nrd_cycle_ns;
|
||||
u32 nwe_cycle_ns;
|
||||
u32 tdf_ns;
|
||||
};
|
||||
|
||||
struct at91sam9_smc_generic_fields {
|
||||
struct regmap_field *setup;
|
||||
struct regmap_field *pulse;
|
||||
struct regmap_field *cycle;
|
||||
struct regmap_field *mode;
|
||||
};
|
||||
|
||||
struct at91sam9_ebi_dev_config {
|
||||
struct at91sam9_smc_timings timings;
|
||||
u32 mode;
|
||||
};
|
||||
|
||||
struct at91_ebi_dev_config {
|
||||
int cs;
|
||||
union {
|
||||
struct at91sam9_ebi_dev_config sam9;
|
||||
};
|
||||
};
|
||||
|
||||
struct at91_ebi;
|
||||
|
||||
struct at91_ebi_dev {
|
||||
struct list_head node;
|
||||
struct at91_ebi *ebi;
|
||||
u32 mode;
|
||||
int numcs;
|
||||
struct at91_ebi_dev_config configs[];
|
||||
};
|
||||
|
||||
struct at91_ebi_caps {
|
||||
unsigned int available_cs;
|
||||
const struct reg_field *ebi_csa;
|
||||
void (*get_config)(struct at91_ebi_dev *ebid,
|
||||
struct at91_ebi_dev_config *conf);
|
||||
int (*xlate_config)(struct at91_ebi_dev *ebid,
|
||||
struct device_node *configs_np,
|
||||
struct at91_ebi_dev_config *conf);
|
||||
int (*apply_config)(struct at91_ebi_dev *ebid,
|
||||
struct at91_ebi_dev_config *conf);
|
||||
int (*init)(struct at91_ebi *ebi);
|
||||
};
|
||||
|
||||
struct at91_ebi {
|
||||
struct clk *clk;
|
||||
struct regmap *smc;
|
||||
struct regmap *matrix;
|
||||
|
||||
struct regmap_field *ebi_csa;
|
||||
|
||||
struct device *dev;
|
||||
const struct at91_ebi_caps *caps;
|
||||
struct list_head devs;
|
||||
union {
|
||||
struct at91sam9_smc_generic_fields sam9;
|
||||
};
|
||||
};
|
||||
|
||||
static void at91sam9_ebi_get_config(struct at91_ebi_dev *ebid,
|
||||
struct at91_ebi_dev_config *conf)
|
||||
{
|
||||
struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
|
||||
unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
|
||||
struct at91sam9_ebi_dev_config *config = &conf->sam9;
|
||||
struct at91sam9_smc_timings *timings = &config->timings;
|
||||
unsigned int val;
|
||||
|
||||
regmap_fields_read(fields->mode, conf->cs, &val);
|
||||
config->mode = val & ~AT91_SMC_TDF;
|
||||
|
||||
val = (val & AT91_SMC_TDF) >> 16;
|
||||
timings->tdf_ns = clk_rate * val;
|
||||
|
||||
regmap_fields_read(fields->setup, conf->cs, &val);
|
||||
timings->ncs_rd_setup_ns = (val >> 24) & 0x1f;
|
||||
timings->ncs_rd_setup_ns += ((val >> 29) & 0x1) * 128;
|
||||
timings->ncs_rd_setup_ns *= clk_rate;
|
||||
timings->nrd_setup_ns = (val >> 16) & 0x1f;
|
||||
timings->nrd_setup_ns += ((val >> 21) & 0x1) * 128;
|
||||
timings->nrd_setup_ns *= clk_rate;
|
||||
timings->ncs_wr_setup_ns = (val >> 8) & 0x1f;
|
||||
timings->ncs_wr_setup_ns += ((val >> 13) & 0x1) * 128;
|
||||
timings->ncs_wr_setup_ns *= clk_rate;
|
||||
timings->nwe_setup_ns = val & 0x1f;
|
||||
timings->nwe_setup_ns += ((val >> 5) & 0x1) * 128;
|
||||
timings->nwe_setup_ns *= clk_rate;
|
||||
|
||||
regmap_fields_read(fields->pulse, conf->cs, &val);
|
||||
timings->ncs_rd_pulse_ns = (val >> 24) & 0x3f;
|
||||
timings->ncs_rd_pulse_ns += ((val >> 30) & 0x1) * 256;
|
||||
timings->ncs_rd_pulse_ns *= clk_rate;
|
||||
timings->nrd_pulse_ns = (val >> 16) & 0x3f;
|
||||
timings->nrd_pulse_ns += ((val >> 22) & 0x1) * 256;
|
||||
timings->nrd_pulse_ns *= clk_rate;
|
||||
timings->ncs_wr_pulse_ns = (val >> 8) & 0x3f;
|
||||
timings->ncs_wr_pulse_ns += ((val >> 14) & 0x1) * 256;
|
||||
timings->ncs_wr_pulse_ns *= clk_rate;
|
||||
timings->nwe_pulse_ns = val & 0x3f;
|
||||
timings->nwe_pulse_ns += ((val >> 6) & 0x1) * 256;
|
||||
timings->nwe_pulse_ns *= clk_rate;
|
||||
|
||||
regmap_fields_read(fields->cycle, conf->cs, &val);
|
||||
timings->nrd_cycle_ns = (val >> 16) & 0x7f;
|
||||
timings->nrd_cycle_ns += ((val >> 23) & 0x3) * 256;
|
||||
timings->nrd_cycle_ns *= clk_rate;
|
||||
timings->nwe_cycle_ns = val & 0x7f;
|
||||
timings->nwe_cycle_ns += ((val >> 7) & 0x3) * 256;
|
||||
timings->nwe_cycle_ns *= clk_rate;
|
||||
}
|
||||
|
||||
static int at91_xlate_timing(struct device_node *np, const char *prop,
|
||||
u32 *val, bool *required)
|
||||
{
|
||||
if (!of_property_read_u32(np, prop, val)) {
|
||||
*required = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*required)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91sam9_smc_xslate_timings(struct at91_ebi_dev *ebid,
|
||||
struct device_node *np,
|
||||
struct at91sam9_smc_timings *timings,
|
||||
bool *required)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-setup-ns",
|
||||
&timings->ncs_rd_setup_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nrd-setup-ns",
|
||||
&timings->nrd_setup_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-setup-ns",
|
||||
&timings->ncs_wr_setup_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nwe-setup-ns",
|
||||
&timings->nwe_setup_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-ncs-rd-pulse-ns",
|
||||
&timings->ncs_rd_pulse_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nrd-pulse-ns",
|
||||
&timings->nrd_pulse_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-ncs-wr-pulse-ns",
|
||||
&timings->ncs_wr_pulse_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nwe-pulse-ns",
|
||||
&timings->nwe_pulse_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nwe-cycle-ns",
|
||||
&timings->nwe_cycle_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-nrd-cycle-ns",
|
||||
&timings->nrd_cycle_ns, required);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = at91_xlate_timing(np, "atmel,smc-tdf-ns",
|
||||
&timings->tdf_ns, required);
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
dev_err(ebid->ebi->dev,
|
||||
"missing or invalid timings definition in %s",
|
||||
np->full_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int at91sam9_ebi_xslate_config(struct at91_ebi_dev *ebid,
|
||||
struct device_node *np,
|
||||
struct at91_ebi_dev_config *conf)
|
||||
{
|
||||
struct at91sam9_ebi_dev_config *config = &conf->sam9;
|
||||
bool required = false;
|
||||
const char *tmp_str;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "atmel,smc-bus-width", &tmp);
|
||||
if (!ret) {
|
||||
switch (tmp) {
|
||||
case 8:
|
||||
config->mode |= AT91_SMC_DBW_8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
config->mode |= AT91_SMC_DBW_16;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
config->mode |= AT91_SMC_DBW_32;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
required = true;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "atmel,smc-tdf-optimized")) {
|
||||
config->mode |= AT91_SMC_TDFMODE_OPTIMIZED;
|
||||
required = true;
|
||||
}
|
||||
|
||||
tmp_str = NULL;
|
||||
of_property_read_string(np, "atmel,smc-byte-access-type", &tmp_str);
|
||||
if (tmp_str && !strcmp(tmp_str, "write")) {
|
||||
config->mode |= AT91_SMC_BAT_WRITE;
|
||||
required = true;
|
||||
}
|
||||
|
||||
tmp_str = NULL;
|
||||
of_property_read_string(np, "atmel,smc-read-mode", &tmp_str);
|
||||
if (tmp_str && !strcmp(tmp_str, "nrd")) {
|
||||
config->mode |= AT91_SMC_READMODE_NRD;
|
||||
required = true;
|
||||
}
|
||||
|
||||
tmp_str = NULL;
|
||||
of_property_read_string(np, "atmel,smc-write-mode", &tmp_str);
|
||||
if (tmp_str && !strcmp(tmp_str, "nwe")) {
|
||||
config->mode |= AT91_SMC_WRITEMODE_NWE;
|
||||
required = true;
|
||||
}
|
||||
|
||||
tmp_str = NULL;
|
||||
of_property_read_string(np, "atmel,smc-exnw-mode", &tmp_str);
|
||||
if (tmp_str) {
|
||||
if (!strcmp(tmp_str, "frozen"))
|
||||
config->mode |= AT91_SMC_EXNWMODE_FROZEN;
|
||||
else if (!strcmp(tmp_str, "ready"))
|
||||
config->mode |= AT91_SMC_EXNWMODE_READY;
|
||||
else if (strcmp(tmp_str, "disabled"))
|
||||
return -EINVAL;
|
||||
|
||||
required = true;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "atmel,smc-page-mode", &tmp);
|
||||
if (!ret) {
|
||||
switch (tmp) {
|
||||
case 4:
|
||||
config->mode |= AT91_SMC_PS_4;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
config->mode |= AT91_SMC_PS_8;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
config->mode |= AT91_SMC_PS_16;
|
||||
break;
|
||||
|
||||
case 32:
|
||||
config->mode |= AT91_SMC_PS_32;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config->mode |= AT91_SMC_PMEN;
|
||||
required = true;
|
||||
}
|
||||
|
||||
ret = at91sam9_smc_xslate_timings(ebid, np, &config->timings,
|
||||
&required);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return required;
|
||||
}
|
||||
|
||||
static int at91sam9_ebi_apply_config(struct at91_ebi_dev *ebid,
|
||||
struct at91_ebi_dev_config *conf)
|
||||
{
|
||||
unsigned int clk_rate = clk_get_rate(ebid->ebi->clk);
|
||||
struct at91sam9_ebi_dev_config *config = &conf->sam9;
|
||||
struct at91sam9_smc_timings *timings = &config->timings;
|
||||
struct at91sam9_smc_generic_fields *fields = &ebid->ebi->sam9;
|
||||
u32 coded_val;
|
||||
u32 val;
|
||||
|
||||
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
|
||||
timings->ncs_rd_setup_ns);
|
||||
val = AT91SAM9_SMC_NCS_NRDSETUP(coded_val);
|
||||
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
|
||||
timings->nrd_setup_ns);
|
||||
val |= AT91SAM9_SMC_NRDSETUP(coded_val);
|
||||
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
|
||||
timings->ncs_wr_setup_ns);
|
||||
val |= AT91SAM9_SMC_NCS_WRSETUP(coded_val);
|
||||
coded_val = at91sam9_smc_setup_ns_to_cycles(clk_rate,
|
||||
timings->nwe_setup_ns);
|
||||
val |= AT91SAM9_SMC_NWESETUP(coded_val);
|
||||
regmap_fields_write(fields->setup, conf->cs, val);
|
||||
|
||||
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
|
||||
timings->ncs_rd_pulse_ns);
|
||||
val = AT91SAM9_SMC_NCS_NRDPULSE(coded_val);
|
||||
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
|
||||
timings->nrd_pulse_ns);
|
||||
val |= AT91SAM9_SMC_NRDPULSE(coded_val);
|
||||
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
|
||||
timings->ncs_wr_pulse_ns);
|
||||
val |= AT91SAM9_SMC_NCS_WRPULSE(coded_val);
|
||||
coded_val = at91sam9_smc_pulse_ns_to_cycles(clk_rate,
|
||||
timings->nwe_pulse_ns);
|
||||
val |= AT91SAM9_SMC_NWEPULSE(coded_val);
|
||||
regmap_fields_write(fields->pulse, conf->cs, val);
|
||||
|
||||
coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate,
|
||||
timings->nrd_cycle_ns);
|
||||
val = AT91SAM9_SMC_NRDCYCLE(coded_val);
|
||||
coded_val = at91sam9_smc_cycle_ns_to_cycles(clk_rate,
|
||||
timings->nwe_cycle_ns);
|
||||
val |= AT91SAM9_SMC_NWECYCLE(coded_val);
|
||||
regmap_fields_write(fields->cycle, conf->cs, val);
|
||||
|
||||
val = DIV_ROUND_UP(timings->tdf_ns, clk_rate);
|
||||
if (val > AT91_SMC_TDF_MAX)
|
||||
val = AT91_SMC_TDF_MAX;
|
||||
regmap_fields_write(fields->mode, conf->cs,
|
||||
config->mode | AT91_SMC_TDF_(val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91sam9_ebi_init(struct at91_ebi *ebi)
|
||||
{
|
||||
struct at91sam9_smc_generic_fields *fields = &ebi->sam9;
|
||||
struct reg_field field = REG_FIELD(0, 0, 31);
|
||||
|
||||
field.id_size = fls(ebi->caps->available_cs);
|
||||
field.id_offset = AT91SAM9_SMC_GENERIC_BLK_SZ;
|
||||
|
||||
field.reg = AT91SAM9_SMC_SETUP(AT91SAM9_SMC_GENERIC);
|
||||
fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||
if (IS_ERR(fields->setup))
|
||||
return PTR_ERR(fields->setup);
|
||||
|
||||
field.reg = AT91SAM9_SMC_PULSE(AT91SAM9_SMC_GENERIC);
|
||||
fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||
if (IS_ERR(fields->pulse))
|
||||
return PTR_ERR(fields->pulse);
|
||||
|
||||
field.reg = AT91SAM9_SMC_CYCLE(AT91SAM9_SMC_GENERIC);
|
||||
fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||
if (IS_ERR(fields->cycle))
|
||||
return PTR_ERR(fields->cycle);
|
||||
|
||||
field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC);
|
||||
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||
if (IS_ERR(fields->mode))
|
||||
return PTR_ERR(fields->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sama5d3_ebi_init(struct at91_ebi *ebi)
|
||||
{
|
||||
struct at91sam9_smc_generic_fields *fields = &ebi->sam9;
|
||||
struct reg_field field = REG_FIELD(0, 0, 31);
|
||||
|
||||
field.id_size = fls(ebi->caps->available_cs);
|
||||
field.id_offset = SAMA5_SMC_GENERIC_BLK_SZ;
|
||||
|
||||
field.reg = AT91SAM9_SMC_SETUP(SAMA5_SMC_GENERIC);
|
||||
fields->setup = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||
if (IS_ERR(fields->setup))
|
||||
return PTR_ERR(fields->setup);
|
||||
|
||||
field.reg = AT91SAM9_SMC_PULSE(SAMA5_SMC_GENERIC);
|
||||
fields->pulse = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||
if (IS_ERR(fields->pulse))
|
||||
return PTR_ERR(fields->pulse);
|
||||
|
||||
field.reg = AT91SAM9_SMC_CYCLE(SAMA5_SMC_GENERIC);
|
||||
fields->cycle = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||
if (IS_ERR(fields->cycle))
|
||||
return PTR_ERR(fields->cycle);
|
||||
|
||||
field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC);
|
||||
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||
if (IS_ERR(fields->mode))
|
||||
return PTR_ERR(fields->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
|
||||
int reg_cells)
|
||||
{
|
||||
const struct at91_ebi_caps *caps = ebi->caps;
|
||||
struct at91_ebi_dev_config conf = { };
|
||||
struct device *dev = ebi->dev;
|
||||
struct at91_ebi_dev *ebid;
|
||||
int ret, numcs = 0, i;
|
||||
bool apply = false;
|
||||
|
||||
numcs = of_property_count_elems_of_size(np, "reg",
|
||||
reg_cells * sizeof(u32));
|
||||
if (numcs <= 0) {
|
||||
dev_err(dev, "invalid reg property in %s\n", np->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ebid = devm_kzalloc(ebi->dev,
|
||||
sizeof(*ebid) + (numcs * sizeof(*ebid->configs)),
|
||||
GFP_KERNEL);
|
||||
if (!ebid)
|
||||
return -ENOMEM;
|
||||
|
||||
ebid->ebi = ebi;
|
||||
|
||||
ret = caps->xlate_config(ebid, np, &conf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret)
|
||||
apply = true;
|
||||
|
||||
for (i = 0; i < numcs; i++) {
|
||||
u32 cs;
|
||||
|
||||
ret = of_property_read_u32_index(np, "reg", i * reg_cells,
|
||||
&cs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (cs > AT91_MATRIX_EBI_NUM_CS ||
|
||||
!(ebi->caps->available_cs & BIT(cs))) {
|
||||
dev_err(dev, "invalid reg property in %s\n",
|
||||
np->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ebid->configs[i].cs = cs;
|
||||
|
||||
if (apply) {
|
||||
conf.cs = cs;
|
||||
ret = caps->apply_config(ebid, &conf);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
caps->get_config(ebid, &ebid->configs[i]);
|
||||
|
||||
/*
|
||||
* Attach the EBI device to the generic SMC logic if at least
|
||||
* one "atmel,smc-" property is present.
|
||||
*/
|
||||
if (ebi->ebi_csa && ret)
|
||||
regmap_field_update_bits(ebi->ebi_csa,
|
||||
BIT(cs), 0);
|
||||
}
|
||||
|
||||
list_add_tail(&ebid->node, &ebi->devs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct reg_field at91sam9260_ebi_csa =
|
||||
REG_FIELD(AT91SAM9260_MATRIX_EBICSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9260_ebi_caps = {
|
||||
.available_cs = 0xff,
|
||||
.ebi_csa = &at91sam9260_ebi_csa,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9261_ebi_csa =
|
||||
REG_FIELD(AT91SAM9261_MATRIX_EBICSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9261_ebi_caps = {
|
||||
.available_cs = 0xff,
|
||||
.ebi_csa = &at91sam9261_ebi_csa,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9263_ebi0_csa =
|
||||
REG_FIELD(AT91SAM9263_MATRIX_EBI0CSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9263_ebi0_caps = {
|
||||
.available_cs = 0x3f,
|
||||
.ebi_csa = &at91sam9263_ebi0_csa,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9263_ebi1_csa =
|
||||
REG_FIELD(AT91SAM9263_MATRIX_EBI1CSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9263_ebi1_caps = {
|
||||
.available_cs = 0x7,
|
||||
.ebi_csa = &at91sam9263_ebi1_csa,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9rl_ebi_csa =
|
||||
REG_FIELD(AT91SAM9RL_MATRIX_EBICSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9rl_ebi_caps = {
|
||||
.available_cs = 0x3f,
|
||||
.ebi_csa = &at91sam9rl_ebi_csa,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct reg_field at91sam9g45_ebi_csa =
|
||||
REG_FIELD(AT91SAM9G45_MATRIX_EBICSA, 0,
|
||||
AT91_MATRIX_EBI_NUM_CS - 1);
|
||||
|
||||
static const struct at91_ebi_caps at91sam9g45_ebi_caps = {
|
||||
.available_cs = 0x3f,
|
||||
.ebi_csa = &at91sam9g45_ebi_csa,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct at91_ebi_caps at91sam9x5_ebi_caps = {
|
||||
.available_cs = 0x3f,
|
||||
.ebi_csa = &at91sam9263_ebi0_csa,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = at91sam9_ebi_init,
|
||||
};
|
||||
|
||||
static const struct at91_ebi_caps sama5d3_ebi_caps = {
|
||||
.available_cs = 0xf,
|
||||
.get_config = at91sam9_ebi_get_config,
|
||||
.xlate_config = at91sam9_ebi_xslate_config,
|
||||
.apply_config = at91sam9_ebi_apply_config,
|
||||
.init = sama5d3_ebi_init,
|
||||
};
|
||||
|
||||
static const struct of_device_id at91_ebi_id_table[] = {
|
||||
{
|
||||
.compatible = "atmel,at91sam9260-ebi",
|
||||
.data = &at91sam9260_ebi_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9261-ebi",
|
||||
.data = &at91sam9261_ebi_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9263-ebi0",
|
||||
.data = &at91sam9263_ebi0_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9263-ebi1",
|
||||
.data = &at91sam9263_ebi1_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9rl-ebi",
|
||||
.data = &at91sam9rl_ebi_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9g45-ebi",
|
||||
.data = &at91sam9g45_ebi_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-ebi",
|
||||
.data = &at91sam9x5_ebi_caps,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,sama5d3-ebi",
|
||||
.data = &sama5d3_ebi_caps,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int at91_ebi_dev_disable(struct at91_ebi *ebi, struct device_node *np)
|
||||
{
|
||||
struct device *dev = ebi->dev;
|
||||
struct property *newprop;
|
||||
|
||||
newprop = devm_kzalloc(dev, sizeof(*newprop), GFP_KERNEL);
|
||||
if (!newprop)
|
||||
return -ENOMEM;
|
||||
|
||||
newprop->name = devm_kstrdup(dev, "status", GFP_KERNEL);
|
||||
if (!newprop->name)
|
||||
return -ENOMEM;
|
||||
|
||||
newprop->value = devm_kstrdup(dev, "disabled", GFP_KERNEL);
|
||||
if (!newprop->name)
|
||||
return -ENOMEM;
|
||||
|
||||
newprop->length = sizeof("disabled");
|
||||
|
||||
return of_update_property(np, newprop);
|
||||
}
|
||||
|
||||
static int at91_ebi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child, *np = dev->of_node;
|
||||
const struct of_device_id *match;
|
||||
struct at91_ebi *ebi;
|
||||
int ret, reg_cells;
|
||||
struct clk *clk;
|
||||
u32 val;
|
||||
|
||||
match = of_match_device(at91_ebi_id_table, dev);
|
||||
if (!match || !match->data)
|
||||
return -EINVAL;
|
||||
|
||||
ebi = devm_kzalloc(dev, sizeof(*ebi), GFP_KERNEL);
|
||||
if (!ebi)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&ebi->devs);
|
||||
ebi->caps = match->data;
|
||||
ebi->dev = dev;
|
||||
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ebi->clk = clk;
|
||||
|
||||
ebi->smc = syscon_regmap_lookup_by_phandle(np, "atmel,smc");
|
||||
if (IS_ERR(ebi->smc))
|
||||
return PTR_ERR(ebi->smc);
|
||||
|
||||
/*
|
||||
* The sama5d3 does not provide an EBICSA register and thus does need
|
||||
* to access the matrix registers.
|
||||
*/
|
||||
if (ebi->caps->ebi_csa) {
|
||||
ebi->matrix =
|
||||
syscon_regmap_lookup_by_phandle(np, "atmel,matrix");
|
||||
if (IS_ERR(ebi->matrix))
|
||||
return PTR_ERR(ebi->matrix);
|
||||
|
||||
ebi->ebi_csa = regmap_field_alloc(ebi->matrix,
|
||||
*ebi->caps->ebi_csa);
|
||||
if (IS_ERR(ebi->ebi_csa))
|
||||
return PTR_ERR(ebi->ebi_csa);
|
||||
}
|
||||
|
||||
ret = ebi->caps->init(ebi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = of_property_read_u32(np, "#address-cells", &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "missing #address-cells property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_cells = val;
|
||||
|
||||
ret = of_property_read_u32(np, "#size-cells", &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "missing #address-cells property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_cells += val;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
if (!of_find_property(child, "reg", NULL))
|
||||
continue;
|
||||
|
||||
ret = at91_ebi_dev_setup(ebi, child, reg_cells);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to configure EBI bus for %s, disabling the device",
|
||||
child->full_name);
|
||||
|
||||
ret = at91_ebi_dev_disable(ebi, child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return of_platform_populate(np, NULL, NULL, dev);
|
||||
}
|
||||
|
||||
static struct platform_driver at91_ebi_driver = {
|
||||
.driver = {
|
||||
.name = "atmel-ebi",
|
||||
.of_match_table = at91_ebi_id_table,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver_probe(at91_ebi_driver, at91_ebi_probe);
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Atmel (Multi-port DDR-)SDRAM Controller driver
|
||||
*
|
||||
* Author: Alexandre Belloni <alexandre.belloni@free-electrons.com>
|
||||
*
|
||||
* Copyright (C) 2014 Atmel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -20,7 +22,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
@ -48,7 +50,6 @@ static const struct of_device_id atmel_ramc_of_match[] = {
|
||||
{ .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atmel_ramc_of_match);
|
||||
|
||||
static int atmel_ramc_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -90,8 +91,4 @@ static int __init atmel_ramc_init(void)
|
||||
{
|
||||
return platform_driver_register(&atmel_ramc_driver);
|
||||
}
|
||||
module_init(atmel_ramc_init);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller");
|
||||
device_initcall(atmel_ramc_init);
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
@ -1807,7 +1806,6 @@ static const struct of_device_id gpmc_dt_ids[] = {
|
||||
{ .compatible = "ti,am3352-gpmc" }, /* am335x devices */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpmc_dt_ids);
|
||||
|
||||
/**
|
||||
* gpmc_read_settings_dt - read gpmc settings from device-tree
|
||||
@ -2154,68 +2152,6 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
return 1; /* we're input only */
|
||||
}
|
||||
|
||||
static int gpmc_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
return 0; /* we're input only */
|
||||
}
|
||||
|
||||
static int gpmc_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
return -EINVAL; /* we're input only */
|
||||
}
|
||||
|
||||
static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
}
|
||||
|
||||
static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
offset += 8;
|
||||
|
||||
reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset);
|
||||
|
||||
return !!reg;
|
||||
}
|
||||
|
||||
static int gpmc_gpio_init(struct gpmc_device *gpmc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
gpmc->gpio_chip.parent = gpmc->dev;
|
||||
gpmc->gpio_chip.owner = THIS_MODULE;
|
||||
gpmc->gpio_chip.label = DEVICE_NAME;
|
||||
gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
|
||||
gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
|
||||
gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
|
||||
gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
|
||||
gpmc->gpio_chip.set = gpmc_gpio_set;
|
||||
gpmc->gpio_chip.get = gpmc_gpio_get;
|
||||
gpmc->gpio_chip.base = -1;
|
||||
|
||||
ret = gpiochip_add(&gpmc->gpio_chip);
|
||||
if (ret < 0) {
|
||||
dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpmc_gpio_exit(struct gpmc_device *gpmc)
|
||||
{
|
||||
gpiochip_remove(&gpmc->gpio_chip);
|
||||
}
|
||||
|
||||
static int gpmc_probe_dt(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
@ -2280,7 +2216,69 @@ static int gpmc_probe_dt_children(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
return 1; /* we're input only */
|
||||
}
|
||||
|
||||
static int gpmc_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
return 0; /* we're input only */
|
||||
}
|
||||
|
||||
static int gpmc_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
return -EINVAL; /* we're input only */
|
||||
}
|
||||
|
||||
static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
}
|
||||
|
||||
static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
offset += 8;
|
||||
|
||||
reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset);
|
||||
|
||||
return !!reg;
|
||||
}
|
||||
|
||||
static int gpmc_gpio_init(struct gpmc_device *gpmc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
gpmc->gpio_chip.parent = gpmc->dev;
|
||||
gpmc->gpio_chip.owner = THIS_MODULE;
|
||||
gpmc->gpio_chip.label = DEVICE_NAME;
|
||||
gpmc->gpio_chip.ngpio = gpmc_nr_waitpins;
|
||||
gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction;
|
||||
gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input;
|
||||
gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output;
|
||||
gpmc->gpio_chip.set = gpmc_gpio_set;
|
||||
gpmc->gpio_chip.get = gpmc_gpio_get;
|
||||
gpmc->gpio_chip.base = -1;
|
||||
|
||||
ret = gpiochip_add(&gpmc->gpio_chip);
|
||||
if (ret < 0) {
|
||||
dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpmc_gpio_exit(struct gpmc_device *gpmc)
|
||||
{
|
||||
gpiochip_remove(&gpmc->gpio_chip);
|
||||
}
|
||||
|
||||
static int gpmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -2436,15 +2434,7 @@ static __init int gpmc_init(void)
|
||||
{
|
||||
return platform_driver_register(&gpmc_driver);
|
||||
}
|
||||
|
||||
static __exit void gpmc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&gpmc_driver);
|
||||
|
||||
}
|
||||
|
||||
postcore_initcall(gpmc_init);
|
||||
module_exit(gpmc_exit);
|
||||
|
||||
static struct omap3_gpmc_regs gpmc_context;
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
@ -91,17 +91,17 @@ static int exynos_srom_configure_bank(struct exynos_srom *srom,
|
||||
if (width == 2)
|
||||
cs |= 1 << EXYNOS_SROM_BW__DATAWIDTH__SHIFT;
|
||||
|
||||
bw = __raw_readl(srom->reg_base + EXYNOS_SROM_BW);
|
||||
bw = readl_relaxed(srom->reg_base + EXYNOS_SROM_BW);
|
||||
bw = (bw & ~(EXYNOS_SROM_BW__CS_MASK << bank)) | (cs << bank);
|
||||
__raw_writel(bw, srom->reg_base + EXYNOS_SROM_BW);
|
||||
writel_relaxed(bw, srom->reg_base + EXYNOS_SROM_BW);
|
||||
|
||||
__raw_writel(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) |
|
||||
(timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) |
|
||||
(timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) |
|
||||
(timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) |
|
||||
(timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) |
|
||||
(timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT),
|
||||
srom->reg_base + EXYNOS_SROM_BC0 + bank);
|
||||
writel_relaxed(pmc | (timing[0] << EXYNOS_SROM_BCX__TACP__SHIFT) |
|
||||
(timing[1] << EXYNOS_SROM_BCX__TCAH__SHIFT) |
|
||||
(timing[2] << EXYNOS_SROM_BCX__TCOH__SHIFT) |
|
||||
(timing[3] << EXYNOS_SROM_BCX__TACC__SHIFT) |
|
||||
(timing[4] << EXYNOS_SROM_BCX__TCOS__SHIFT) |
|
||||
(timing[5] << EXYNOS_SROM_BCX__TACS__SHIFT),
|
||||
srom->reg_base + EXYNOS_SROM_BC0 + bank);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -134,7 +134,7 @@ static int exynos_srom_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, srom);
|
||||
|
||||
srom->reg_offset = exynos_srom_alloc_reg_dump(exynos_srom_offsets,
|
||||
sizeof(exynos_srom_offsets));
|
||||
ARRAY_SIZE(exynos_srom_offsets));
|
||||
if (!srom->reg_offset) {
|
||||
iounmap(srom->reg_base);
|
||||
return -ENOMEM;
|
||||
@ -159,16 +159,6 @@ static int exynos_srom_probe(struct platform_device *pdev)
|
||||
return of_platform_populate(np, NULL, NULL, dev);
|
||||
}
|
||||
|
||||
static int exynos_srom_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_srom *srom = platform_get_drvdata(pdev);
|
||||
|
||||
kfree(srom->reg_offset);
|
||||
iounmap(srom->reg_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void exynos_srom_save(void __iomem *base,
|
||||
struct exynos_srom_reg_dump *rd,
|
||||
@ -211,21 +201,16 @@ static const struct of_device_id of_exynos_srom_ids[] = {
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_exynos_srom_ids);
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(exynos_srom_pm_ops, exynos_srom_suspend, exynos_srom_resume);
|
||||
|
||||
static struct platform_driver exynos_srom_driver = {
|
||||
.probe = exynos_srom_probe,
|
||||
.remove = exynos_srom_remove,
|
||||
.driver = {
|
||||
.name = "exynos-srom",
|
||||
.of_match_table = of_exynos_srom_ids,
|
||||
.pm = &exynos_srom_pm_ops,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
module_platform_driver(exynos_srom_driver);
|
||||
|
||||
MODULE_AUTHOR("Pankaj Dubey <pankaj.dubey@samsung.com>");
|
||||
MODULE_DESCRIPTION("Exynos SROM Controller Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
builtin_platform_driver(exynos_srom_driver);
|
||||
|
@ -186,8 +186,10 @@ static int load_timings(struct tegra_mc *mc, struct device_node *node)
|
||||
timing = &mc->timings[i++];
|
||||
|
||||
err = load_one_timing(mc, timing, child);
|
||||
if (err)
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -206,15 +208,13 @@ static int tegra_mc_setup_timings(struct tegra_mc *mc)
|
||||
for_each_child_of_node(mc->dev->of_node, node) {
|
||||
err = of_property_read_u32(node, "nvidia,ram-code",
|
||||
&node_ram_code);
|
||||
if (err || (node_ram_code != ram_code)) {
|
||||
of_node_put(node);
|
||||
if (err || (node_ram_code != ram_code))
|
||||
continue;
|
||||
}
|
||||
|
||||
err = load_timings(mc, node);
|
||||
of_node_put(node);
|
||||
if (err)
|
||||
return err;
|
||||
of_node_put(node);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -970,8 +970,10 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
timing = &emc->timings[i++];
|
||||
|
||||
err = load_one_timing_from_dt(emc, timing, child);
|
||||
if (err)
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
|
||||
@ -995,10 +997,8 @@ tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
|
||||
u32 value;
|
||||
|
||||
err = of_property_read_u32(np, "nvidia,ram-code", &value);
|
||||
if (err || (value != ram_code)) {
|
||||
of_node_put(np);
|
||||
if (err || (value != ram_code))
|
||||
continue;
|
||||
}
|
||||
|
||||
return np;
|
||||
}
|
||||
|
@ -1087,7 +1087,6 @@ static int ab8500_probe(struct platform_device *pdev)
|
||||
"Vbus Detect (USB)",
|
||||
"USB ID Detect",
|
||||
"UART Factory Mode Detect"};
|
||||
struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
enum ab8500_version version = AB8500_VERSION_UNDEFINED;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
@ -1219,9 +1218,6 @@ static int ab8500_probe(struct platform_device *pdev)
|
||||
pr_cont("None\n");
|
||||
}
|
||||
|
||||
if (plat && plat->init)
|
||||
plat->init(ab8500);
|
||||
|
||||
if (is_ab9540(ab8500)) {
|
||||
ret = get_register_interruptible(ab8500, AB8500_CHARGER,
|
||||
AB8500_CH_USBCH_STAT1_REG, &value);
|
||||
|
@ -127,45 +127,11 @@ EXPORT_SYMBOL(ab8500_sysctrl_write);
|
||||
|
||||
static int ab8500_sysctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct ab8500_platform_data *plat;
|
||||
struct ab8500_sysctrl_platform_data *pdata;
|
||||
|
||||
plat = dev_get_platdata(pdev->dev.parent);
|
||||
|
||||
if (!plat)
|
||||
return -EINVAL;
|
||||
|
||||
sysctrl_dev = &pdev->dev;
|
||||
|
||||
if (!pm_power_off)
|
||||
pm_power_off = ab8500_power_off;
|
||||
|
||||
pdata = plat->sysctrl;
|
||||
if (pdata) {
|
||||
int last, ret, i, j;
|
||||
|
||||
if (is_ab8505(ab8500))
|
||||
last = AB8500_SYSCLKREQ4RFCLKBUF;
|
||||
else
|
||||
last = AB8500_SYSCLKREQ8RFCLKBUF;
|
||||
|
||||
for (i = AB8500_SYSCLKREQ1RFCLKBUF; i <= last; i++) {
|
||||
j = i - AB8500_SYSCLKREQ1RFCLKBUF;
|
||||
ret = ab8500_sysctrl_write(i, 0xff,
|
||||
pdata->initial_req_buf_config[j]);
|
||||
dev_dbg(&pdev->dev,
|
||||
"Setting SysClkReq%dRfClkBuf 0x%X\n",
|
||||
j + 1,
|
||||
pdata->initial_req_buf_config[j]);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"Can't set sysClkReq%dRfClkBuf: %d\n",
|
||||
j + 1, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3094,8 +3094,7 @@ static void db8500_prcmu_update_cpufreq(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int db8500_prcmu_register_ab8500(struct device *parent,
|
||||
struct ab8500_platform_data *pdata)
|
||||
static int db8500_prcmu_register_ab8500(struct device *parent)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct resource ab8500_resource;
|
||||
@ -3103,8 +3102,6 @@ static int db8500_prcmu_register_ab8500(struct device *parent,
|
||||
.name = "ab8500-core",
|
||||
.of_compatible = "stericsson,ab8500",
|
||||
.id = AB8500_VERSION_AB8500,
|
||||
.platform_data = pdata,
|
||||
.pdata_size = sizeof(struct ab8500_platform_data),
|
||||
.resources = &ab8500_resource,
|
||||
.num_resources = 1,
|
||||
};
|
||||
@ -3133,7 +3130,6 @@ static int db8500_prcmu_register_ab8500(struct device *parent,
|
||||
static int db8500_prcmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct prcmu_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||
int irq = 0, err = 0;
|
||||
struct resource *res;
|
||||
|
||||
@ -3149,7 +3145,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
init_prcm_registers();
|
||||
dbx500_fw_version_init(pdev, pdata->version_offset);
|
||||
dbx500_fw_version_init(pdev, DB8500_PRCMU_FW_VERSION_OFFSET);
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no prcmu tcdm region provided\n");
|
||||
@ -3204,7 +3200,7 @@ static int db8500_prcmu_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata);
|
||||
err = db8500_prcmu_register_ab8500(&pdev->dev);
|
||||
if (err) {
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
pr_err("prcmu: Failed to add ab8500 subdevice\n");
|
||||
|
@ -46,6 +46,16 @@ config POWER_RESET_AXXIA
|
||||
|
||||
Say Y if you have an Axxia family SoC.
|
||||
|
||||
config POWER_RESET_BRCMKONA
|
||||
bool "Broadcom Kona reset driver"
|
||||
depends on ARM || COMPILE_TEST
|
||||
default ARCH_BCM_MOBILE
|
||||
help
|
||||
This driver provides restart support for Broadcom Kona chips.
|
||||
|
||||
Say Y here if you have a Broadcom Kona-based board and you wish
|
||||
to have restart support.
|
||||
|
||||
config POWER_RESET_BRCMSTB
|
||||
bool "Broadcom STB reset driver"
|
||||
depends on ARM || MIPS || COMPILE_TEST
|
||||
|
@ -3,6 +3,7 @@ obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o
|
||||
obj-$(CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC) += at91-sama5d2_shdwc.o
|
||||
obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
|
||||
obj-$(CONFIG_POWER_RESET_BRCMKONA) += brcm-kona-reset.o
|
||||
obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
|
||||
|
73
drivers/power/reset/brcm-kona-reset.c
Normal file
73
drivers/power/reset/brcm-kona-reset.c
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#define RSTMGR_REG_WR_ACCESS_OFFSET 0
|
||||
#define RSTMGR_REG_CHIP_SOFT_RST_OFFSET 4
|
||||
|
||||
#define RSTMGR_WR_PASSWORD 0xa5a5
|
||||
#define RSTMGR_WR_PASSWORD_SHIFT 8
|
||||
#define RSTMGR_WR_ACCESS_ENABLE 1
|
||||
|
||||
static void __iomem *kona_reset_base;
|
||||
|
||||
static int kona_reset_handler(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
{
|
||||
/*
|
||||
* A soft reset is triggered by writing a 0 to bit 0 of the soft reset
|
||||
* register. To write to that register we must first write the password
|
||||
* and the enable bit in the write access enable register.
|
||||
*/
|
||||
writel((RSTMGR_WR_PASSWORD << RSTMGR_WR_PASSWORD_SHIFT) |
|
||||
RSTMGR_WR_ACCESS_ENABLE,
|
||||
kona_reset_base + RSTMGR_REG_WR_ACCESS_OFFSET);
|
||||
writel(0, kona_reset_base + RSTMGR_REG_CHIP_SOFT_RST_OFFSET);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block kona_reset_nb = {
|
||||
.notifier_call = kona_reset_handler,
|
||||
.priority = 128,
|
||||
};
|
||||
|
||||
static int kona_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
kona_reset_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(kona_reset_base))
|
||||
return PTR_ERR(kona_reset_base);
|
||||
|
||||
return register_restart_handler(&kona_reset_nb);
|
||||
}
|
||||
|
||||
static const struct of_device_id of_match[] = {
|
||||
{ .compatible = "brcm,bcm21664-resetmgr" },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver bcm_kona_reset_driver = {
|
||||
.probe = kona_reset_probe,
|
||||
.driver = {
|
||||
.name = "brcm-kona-reset",
|
||||
.of_match_table = of_match,
|
||||
},
|
||||
};
|
||||
|
||||
builtin_platform_driver(bcm_kona_reset_driver);
|
@ -155,7 +155,7 @@ static int clps711x_pwm_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = {
|
||||
{ .compatible = "cirrus,clps711x-pwm", },
|
||||
{ .compatible = "cirrus,ep7209-pwm", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, clps711x_pwm_dt_ids);
|
||||
|
@ -245,7 +245,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
|
||||
struct pwm_omap_dmtimer_chip *omap;
|
||||
struct pwm_omap_dmtimer_pdata *pdata;
|
||||
pwm_omap_dmtimer *dm_timer;
|
||||
u32 prescaler;
|
||||
u32 v;
|
||||
int status;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
@ -306,10 +306,12 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
|
||||
if (pm_runtime_active(&omap->dm_timer_pdev->dev))
|
||||
omap->pdata->stop(omap->dm_timer);
|
||||
|
||||
/* setup dmtimer prescaler */
|
||||
if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler",
|
||||
&prescaler))
|
||||
omap->pdata->set_prescaler(omap->dm_timer, prescaler);
|
||||
if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v))
|
||||
omap->pdata->set_prescaler(omap->dm_timer, v);
|
||||
|
||||
/* setup dmtimer clock source */
|
||||
if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v))
|
||||
omap->pdata->set_source(omap->dm_timer, v);
|
||||
|
||||
omap->chip.dev = &pdev->dev;
|
||||
omap->chip.ops = &pwm_omap_dmtimer_ops;
|
||||
|
@ -25,6 +25,456 @@
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/regulator/ab8500.h>
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vaux1_consumers[] = {
|
||||
/* Main display, u8500 R3 uib */
|
||||
REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
|
||||
/* Main display, u8500 uib and ST uib */
|
||||
REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"),
|
||||
/* Secondary display, ST uib */
|
||||
REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"),
|
||||
/* SFH7741 proximity sensor */
|
||||
REGULATOR_SUPPLY("vcc", "gpio-keys.0"),
|
||||
/* BH1780GLS ambient light sensor */
|
||||
REGULATOR_SUPPLY("vcc", "2-0029"),
|
||||
/* lsm303dlh accelerometer */
|
||||
REGULATOR_SUPPLY("vdd", "2-0018"),
|
||||
/* lsm303dlhc accelerometer */
|
||||
REGULATOR_SUPPLY("vdd", "2-0019"),
|
||||
/* lsm303dlh magnetometer */
|
||||
REGULATOR_SUPPLY("vdd", "2-001e"),
|
||||
/* Rohm BU21013 Touchscreen devices */
|
||||
REGULATOR_SUPPLY("avdd", "3-005c"),
|
||||
REGULATOR_SUPPLY("avdd", "3-005d"),
|
||||
/* Synaptics RMI4 Touchscreen device */
|
||||
REGULATOR_SUPPLY("vdd", "3-004b"),
|
||||
/* L3G4200D Gyroscope device */
|
||||
REGULATOR_SUPPLY("vdd", "2-0068"),
|
||||
/* Ambient light sensor device */
|
||||
REGULATOR_SUPPLY("vdd", "3-0029"),
|
||||
/* Pressure sensor device */
|
||||
REGULATOR_SUPPLY("vdd", "2-005c"),
|
||||
/* Cypress TrueTouch Touchscreen device */
|
||||
REGULATOR_SUPPLY("vcpin", "spi8.0"),
|
||||
/* Camera device */
|
||||
REGULATOR_SUPPLY("vaux12v5", "mmio_camera"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
|
||||
/* On-board eMMC power */
|
||||
REGULATOR_SUPPLY("vmmc", "sdi4"),
|
||||
/* AB8500 audio codec */
|
||||
REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"),
|
||||
/* AB8500 accessory detect 1 */
|
||||
REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"),
|
||||
/* AB8500 Tv-out device */
|
||||
REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"),
|
||||
/* AV8100 HDMI device */
|
||||
REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vaux3_consumers[] = {
|
||||
REGULATOR_SUPPLY("v-SD-STM", "stm"),
|
||||
/* External MMC slot power */
|
||||
REGULATOR_SUPPLY("vmmc", "sdi0"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vtvout_consumers[] = {
|
||||
/* TV-out DENC supply */
|
||||
REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"),
|
||||
/* Internal general-purpose ADC */
|
||||
REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"),
|
||||
/* ADC for charger */
|
||||
REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"),
|
||||
/* AB8500 Tv-out device */
|
||||
REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vaud_consumers[] = {
|
||||
/* AB8500 audio-codec main supply */
|
||||
REGULATOR_SUPPLY("vaud", "ab8500-codec.0"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vamic1_consumers[] = {
|
||||
/* AB8500 audio-codec Mic1 supply */
|
||||
REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vamic2_consumers[] = {
|
||||
/* AB8500 audio-codec Mic2 supply */
|
||||
REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vdmic_consumers[] = {
|
||||
/* AB8500 audio-codec DMic supply */
|
||||
REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vintcore_consumers[] = {
|
||||
/* SoC core supply, no device */
|
||||
REGULATOR_SUPPLY("v-intcore", NULL),
|
||||
/* USB Transceiver */
|
||||
REGULATOR_SUPPLY("vddulpivio18", "ab8500-usb.0"),
|
||||
/* Handled by abx500 clk driver */
|
||||
REGULATOR_SUPPLY("v-intcore", "abx500-clk.0"),
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply ab8500_vana_consumers[] = {
|
||||
/* DB8500 DSI */
|
||||
REGULATOR_SUPPLY("vdddsi1v2", "mcde"),
|
||||
REGULATOR_SUPPLY("vdddsi1v2", "b2r2_core"),
|
||||
REGULATOR_SUPPLY("vdddsi1v2", "b2r2_1_core"),
|
||||
REGULATOR_SUPPLY("vdddsi1v2", "dsilink.0"),
|
||||
REGULATOR_SUPPLY("vdddsi1v2", "dsilink.1"),
|
||||
REGULATOR_SUPPLY("vdddsi1v2", "dsilink.2"),
|
||||
/* DB8500 CSI */
|
||||
REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"),
|
||||
};
|
||||
|
||||
/* ab8500 regulator register initialization */
|
||||
static struct ab8500_regulator_reg_init ab8500_reg_init[] = {
|
||||
/*
|
||||
* VanaRequestCtrl = HP/LP depending on VxRequest
|
||||
* VextSupply1RequestCtrl = HP/LP depending on VxRequest
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00),
|
||||
/*
|
||||
* VextSupply2RequestCtrl = HP/LP depending on VxRequest
|
||||
* VextSupply3RequestCtrl = HP/LP depending on VxRequest
|
||||
* Vaux1RequestCtrl = HP/LP depending on VxRequest
|
||||
* Vaux2RequestCtrl = HP/LP depending on VxRequest
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00),
|
||||
/*
|
||||
* Vaux3RequestCtrl = HP/LP depending on VxRequest
|
||||
* SwHPReq = Control through SWValid disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00),
|
||||
/*
|
||||
* VanaSysClkReq1HPValid = disabled
|
||||
* Vaux1SysClkReq1HPValid = disabled
|
||||
* Vaux2SysClkReq1HPValid = disabled
|
||||
* Vaux3SysClkReq1HPValid = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00),
|
||||
/*
|
||||
* VextSupply1SysClkReq1HPValid = disabled
|
||||
* VextSupply2SysClkReq1HPValid = disabled
|
||||
* VextSupply3SysClkReq1HPValid = SysClkReq1 controlled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40),
|
||||
/*
|
||||
* VanaHwHPReq1Valid = disabled
|
||||
* Vaux1HwHPreq1Valid = disabled
|
||||
* Vaux2HwHPReq1Valid = disabled
|
||||
* Vaux3HwHPReqValid = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00),
|
||||
/*
|
||||
* VextSupply1HwHPReq1Valid = disabled
|
||||
* VextSupply2HwHPReq1Valid = disabled
|
||||
* VextSupply3HwHPReq1Valid = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00),
|
||||
/*
|
||||
* VanaHwHPReq2Valid = disabled
|
||||
* Vaux1HwHPReq2Valid = disabled
|
||||
* Vaux2HwHPReq2Valid = disabled
|
||||
* Vaux3HwHPReq2Valid = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00),
|
||||
/*
|
||||
* VextSupply1HwHPReq2Valid = disabled
|
||||
* VextSupply2HwHPReq2Valid = disabled
|
||||
* VextSupply3HwHPReq2Valid = HWReq2 controlled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04),
|
||||
/*
|
||||
* VanaSwHPReqValid = disabled
|
||||
* Vaux1SwHPReqValid = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00),
|
||||
/*
|
||||
* Vaux2SwHPReqValid = disabled
|
||||
* Vaux3SwHPReqValid = disabled
|
||||
* VextSupply1SwHPReqValid = disabled
|
||||
* VextSupply2SwHPReqValid = disabled
|
||||
* VextSupply3SwHPReqValid = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00),
|
||||
/*
|
||||
* SysClkReq2Valid1 = SysClkReq2 controlled
|
||||
* SysClkReq3Valid1 = disabled
|
||||
* SysClkReq4Valid1 = SysClkReq4 controlled
|
||||
* SysClkReq5Valid1 = disabled
|
||||
* SysClkReq6Valid1 = SysClkReq6 controlled
|
||||
* SysClkReq7Valid1 = disabled
|
||||
* SysClkReq8Valid1 = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a),
|
||||
/*
|
||||
* SysClkReq2Valid2 = disabled
|
||||
* SysClkReq3Valid2 = disabled
|
||||
* SysClkReq4Valid2 = disabled
|
||||
* SysClkReq5Valid2 = disabled
|
||||
* SysClkReq6Valid2 = SysClkReq6 controlled
|
||||
* SysClkReq7Valid2 = disabled
|
||||
* SysClkReq8Valid2 = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20),
|
||||
/*
|
||||
* VTVoutEna = disabled
|
||||
* Vintcore12Ena = disabled
|
||||
* Vintcore12Sel = 1.25 V
|
||||
* Vintcore12LP = inactive (HP)
|
||||
* VTVoutLP = inactive (HP)
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10),
|
||||
/*
|
||||
* VaudioEna = disabled
|
||||
* VdmicEna = disabled
|
||||
* Vamic1Ena = disabled
|
||||
* Vamic2Ena = disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00),
|
||||
/*
|
||||
* Vamic1_dzout = high-Z when Vamic1 is disabled
|
||||
* Vamic2_dzout = high-Z when Vamic2 is disabled
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00),
|
||||
/*
|
||||
* VPll = Hw controlled (NOTE! PRCMU bits)
|
||||
* VanaRegu = force off
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02),
|
||||
/*
|
||||
* VrefDDREna = disabled
|
||||
* VrefDDRSleepMode = inactive (no pulldown)
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00),
|
||||
/*
|
||||
* VextSupply1Regu = force LP
|
||||
* VextSupply2Regu = force OFF
|
||||
* VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP)
|
||||
* ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0
|
||||
* ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13),
|
||||
/*
|
||||
* Vaux1Regu = force HP
|
||||
* Vaux2Regu = force off
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01),
|
||||
/*
|
||||
* Vaux3Regu = force off
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00),
|
||||
/*
|
||||
* Vaux1Sel = 2.8 V
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C),
|
||||
/*
|
||||
* Vaux2Sel = 2.9 V
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d),
|
||||
/*
|
||||
* Vaux3Sel = 2.91 V
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07),
|
||||
/*
|
||||
* VextSupply12LP = disabled (no LP)
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00),
|
||||
/*
|
||||
* Vaux1Disch = short discharge time
|
||||
* Vaux2Disch = short discharge time
|
||||
* Vaux3Disch = short discharge time
|
||||
* Vintcore12Disch = short discharge time
|
||||
* VTVoutDisch = short discharge time
|
||||
* VaudioDisch = short discharge time
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00),
|
||||
/*
|
||||
* VanaDisch = short discharge time
|
||||
* VdmicPullDownEna = pulldown disabled when Vdmic is disabled
|
||||
* VdmicDisch = short discharge time
|
||||
*/
|
||||
INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00),
|
||||
};
|
||||
|
||||
/* AB8500 regulators */
|
||||
static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
|
||||
/* supplies to the display/camera */
|
||||
[AB8500_LDO_AUX1] = {
|
||||
.supply_regulator = "ab8500-ext-supply3",
|
||||
.constraints = {
|
||||
.name = "V-DISPLAY",
|
||||
.min_uV = 2800000,
|
||||
.max_uV = 3300000,
|
||||
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
|
||||
REGULATOR_CHANGE_STATUS,
|
||||
.boot_on = 1, /* display is on at boot */
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux1_consumers),
|
||||
.consumer_supplies = ab8500_vaux1_consumers,
|
||||
},
|
||||
/* supplies to the on-board eMMC */
|
||||
[AB8500_LDO_AUX2] = {
|
||||
.supply_regulator = "ab8500-ext-supply3",
|
||||
.constraints = {
|
||||
.name = "V-eMMC1",
|
||||
.min_uV = 1100000,
|
||||
.max_uV = 3300000,
|
||||
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
|
||||
REGULATOR_CHANGE_STATUS |
|
||||
REGULATOR_CHANGE_MODE,
|
||||
.valid_modes_mask = REGULATOR_MODE_NORMAL |
|
||||
REGULATOR_MODE_IDLE,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers),
|
||||
.consumer_supplies = ab8500_vaux2_consumers,
|
||||
},
|
||||
/* supply for VAUX3, supplies to SDcard slots */
|
||||
[AB8500_LDO_AUX3] = {
|
||||
.supply_regulator = "ab8500-ext-supply3",
|
||||
.constraints = {
|
||||
.name = "V-MMC-SD",
|
||||
.min_uV = 1100000,
|
||||
.max_uV = 3300000,
|
||||
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
|
||||
REGULATOR_CHANGE_STATUS |
|
||||
REGULATOR_CHANGE_MODE,
|
||||
.valid_modes_mask = REGULATOR_MODE_NORMAL |
|
||||
REGULATOR_MODE_IDLE,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers),
|
||||
.consumer_supplies = ab8500_vaux3_consumers,
|
||||
},
|
||||
/* supply for tvout, gpadc, TVOUT LDO */
|
||||
[AB8500_LDO_TVOUT] = {
|
||||
.constraints = {
|
||||
.name = "V-TVOUT",
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vtvout_consumers),
|
||||
.consumer_supplies = ab8500_vtvout_consumers,
|
||||
},
|
||||
/* supply for ab8500-vaudio, VAUDIO LDO */
|
||||
[AB8500_LDO_AUDIO] = {
|
||||
.constraints = {
|
||||
.name = "V-AUD",
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaud_consumers),
|
||||
.consumer_supplies = ab8500_vaud_consumers,
|
||||
},
|
||||
/* supply for v-anamic1 VAMic1-LDO */
|
||||
[AB8500_LDO_ANAMIC1] = {
|
||||
.constraints = {
|
||||
.name = "V-AMIC1",
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vamic1_consumers),
|
||||
.consumer_supplies = ab8500_vamic1_consumers,
|
||||
},
|
||||
/* supply for v-amic2, VAMIC2 LDO, reuse constants for AMIC1 */
|
||||
[AB8500_LDO_ANAMIC2] = {
|
||||
.constraints = {
|
||||
.name = "V-AMIC2",
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vamic2_consumers),
|
||||
.consumer_supplies = ab8500_vamic2_consumers,
|
||||
},
|
||||
/* supply for v-dmic, VDMIC LDO */
|
||||
[AB8500_LDO_DMIC] = {
|
||||
.constraints = {
|
||||
.name = "V-DMIC",
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vdmic_consumers),
|
||||
.consumer_supplies = ab8500_vdmic_consumers,
|
||||
},
|
||||
/* supply for v-intcore12, VINTCORE12 LDO */
|
||||
[AB8500_LDO_INTCORE] = {
|
||||
.constraints = {
|
||||
.name = "V-INTCORE",
|
||||
.min_uV = 1250000,
|
||||
.max_uV = 1350000,
|
||||
.input_uV = 1800000,
|
||||
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
|
||||
REGULATOR_CHANGE_STATUS |
|
||||
REGULATOR_CHANGE_MODE |
|
||||
REGULATOR_CHANGE_DRMS,
|
||||
.valid_modes_mask = REGULATOR_MODE_NORMAL |
|
||||
REGULATOR_MODE_IDLE,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers),
|
||||
.consumer_supplies = ab8500_vintcore_consumers,
|
||||
},
|
||||
/* supply for U8500 CSI-DSI, VANA LDO */
|
||||
[AB8500_LDO_ANA] = {
|
||||
.constraints = {
|
||||
.name = "V-CSI-DSI",
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
.num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers),
|
||||
.consumer_supplies = ab8500_vana_consumers,
|
||||
},
|
||||
};
|
||||
|
||||
/* supply for VextSupply3 */
|
||||
static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = {
|
||||
/* SIM supply for 3 V SIM cards */
|
||||
REGULATOR_SUPPLY("vinvsim", "sim-detect.0"),
|
||||
};
|
||||
|
||||
/*
|
||||
* AB8500 external regulators
|
||||
*/
|
||||
static struct regulator_init_data ab8500_ext_regulators[] = {
|
||||
/* fixed Vbat supplies VSMPS1_EXT_1V8 */
|
||||
[AB8500_EXT_SUPPLY1] = {
|
||||
.constraints = {
|
||||
.name = "ab8500-ext-supply1",
|
||||
.min_uV = 1800000,
|
||||
.max_uV = 1800000,
|
||||
.initial_mode = REGULATOR_MODE_IDLE,
|
||||
.boot_on = 1,
|
||||
.always_on = 1,
|
||||
},
|
||||
},
|
||||
/* fixed Vbat supplies VSMPS2_EXT_1V36 and VSMPS5_EXT_1V15 */
|
||||
[AB8500_EXT_SUPPLY2] = {
|
||||
.constraints = {
|
||||
.name = "ab8500-ext-supply2",
|
||||
.min_uV = 1360000,
|
||||
.max_uV = 1360000,
|
||||
},
|
||||
},
|
||||
/* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */
|
||||
[AB8500_EXT_SUPPLY3] = {
|
||||
.constraints = {
|
||||
.name = "ab8500-ext-supply3",
|
||||
.min_uV = 3400000,
|
||||
.max_uV = 3400000,
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
.boot_on = 1,
|
||||
},
|
||||
.num_consumer_supplies =
|
||||
ARRAY_SIZE(ab8500_ext_supply3_consumers),
|
||||
.consumer_supplies = ab8500_ext_supply3_consumers,
|
||||
},
|
||||
};
|
||||
|
||||
static struct ab8500_regulator_platform_data ab8500_regulator_plat_data = {
|
||||
.reg_init = ab8500_reg_init,
|
||||
.num_reg_init = ARRAY_SIZE(ab8500_reg_init),
|
||||
.regulator = ab8500_regulators,
|
||||
.num_regulator = ARRAY_SIZE(ab8500_regulators),
|
||||
.ext_regulator = ab8500_ext_regulators,
|
||||
.num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab8500_ext_regulator_info - ab8500 regulator information
|
||||
* @dev: device pointer
|
||||
@ -344,8 +794,7 @@ static struct of_regulator_match ab8500_ext_regulator_match[] = {
|
||||
static int ab8500_ext_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct ab8500_platform_data *ppdata;
|
||||
struct ab8500_regulator_platform_data *pdata;
|
||||
struct ab8500_regulator_platform_data *pdata = &ab8500_regulator_plat_data;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct regulator_config config = { };
|
||||
int i, err;
|
||||
@ -366,18 +815,6 @@ static int ab8500_ext_regulator_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ppdata = dev_get_platdata(ab8500->dev);
|
||||
if (!ppdata) {
|
||||
dev_err(&pdev->dev, "null parent pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata = ppdata->regulator;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "null pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make sure the platform data has the correct size */
|
||||
if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) {
|
||||
dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
|
||||
|
@ -12,8 +12,22 @@ menuconfig RESET_CONTROLLER
|
||||
|
||||
If unsure, say no.
|
||||
|
||||
if RESET_CONTROLLER
|
||||
|
||||
config RESET_OXNAS
|
||||
bool
|
||||
|
||||
config TI_SYSCON_RESET
|
||||
tristate "TI SYSCON Reset Driver"
|
||||
depends on HAS_IOMEM
|
||||
select MFD_SYSCON
|
||||
help
|
||||
This enables the reset driver support for TI devices with
|
||||
memory-mapped reset registers as part of a syscon device node. If
|
||||
you wish to use the reset framework for such memory-mapped devices,
|
||||
say Y here. Otherwise, say N.
|
||||
|
||||
source "drivers/reset/sti/Kconfig"
|
||||
source "drivers/reset/hisilicon/Kconfig"
|
||||
|
||||
endif
|
||||
|
@ -3,9 +3,11 @@ obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o
|
||||
obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o
|
||||
obj-$(CONFIG_ARCH_BERLIN) += reset-berlin.o
|
||||
obj-$(CONFIG_MACH_PISTACHIO) += reset-pistachio.o
|
||||
obj-$(CONFIG_ARCH_MESON) += reset-meson.o
|
||||
obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
|
||||
obj-$(CONFIG_ARCH_STI) += sti/
|
||||
obj-$(CONFIG_ARCH_HISI) += hisilicon/
|
||||
obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o
|
||||
obj-$(CONFIG_ATH79) += reset-ath79.o
|
||||
obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
|
||||
obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o
|
||||
|
@ -93,6 +93,43 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(reset_controller_unregister);
|
||||
|
||||
static void devm_reset_controller_release(struct device *dev, void *res)
|
||||
{
|
||||
reset_controller_unregister(*(struct reset_controller_dev **)res);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_reset_controller_register - resource managed reset_controller_register()
|
||||
* @dev: device that is registering this reset controller
|
||||
* @rcdev: a pointer to the initialized reset controller device
|
||||
*
|
||||
* Managed reset_controller_register(). For reset controllers registered by
|
||||
* this function, reset_controller_unregister() is automatically called on
|
||||
* driver detach. See reset_controller_register() for more information.
|
||||
*/
|
||||
int devm_reset_controller_register(struct device *dev,
|
||||
struct reset_controller_dev *rcdev)
|
||||
{
|
||||
struct reset_controller_dev **rcdevp;
|
||||
int ret;
|
||||
|
||||
rcdevp = devres_alloc(devm_reset_controller_release, sizeof(*rcdevp),
|
||||
GFP_KERNEL);
|
||||
if (!rcdevp)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = reset_controller_register(rcdev);
|
||||
if (!ret) {
|
||||
*rcdevp = rcdev;
|
||||
devres_add(dev, rcdevp);
|
||||
} else {
|
||||
devres_free(rcdevp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_reset_controller_register);
|
||||
|
||||
/**
|
||||
* reset_control_reset - reset the controlled device
|
||||
* @rstc: reset controller
|
||||
|
@ -1,7 +1,8 @@
|
||||
/*
|
||||
* Hisilicon Hi6220 reset controller driver
|
||||
*
|
||||
* Copyright (c) 2015 Hisilicon Limited.
|
||||
* Copyright (c) 2016 Linaro Limited.
|
||||
* Copyright (c) 2015-2016 Hisilicon Limited.
|
||||
*
|
||||
* Author: Feng Chen <puck.chen@hisilicon.com>
|
||||
*
|
||||
@ -15,81 +16,130 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define ASSERT_OFFSET 0x300
|
||||
#define DEASSERT_OFFSET 0x304
|
||||
#define MAX_INDEX 0x509
|
||||
#define PERIPH_ASSERT_OFFSET 0x300
|
||||
#define PERIPH_DEASSERT_OFFSET 0x304
|
||||
#define PERIPH_MAX_INDEX 0x509
|
||||
|
||||
#define SC_MEDIA_RSTEN 0x052C
|
||||
#define SC_MEDIA_RSTDIS 0x0530
|
||||
#define MEDIA_MAX_INDEX 8
|
||||
|
||||
#define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev)
|
||||
|
||||
struct hi6220_reset_data {
|
||||
void __iomem *assert_base;
|
||||
void __iomem *deassert_base;
|
||||
struct reset_controller_dev rc_dev;
|
||||
enum hi6220_reset_ctrl_type {
|
||||
PERIPHERAL,
|
||||
MEDIA,
|
||||
};
|
||||
|
||||
static int hi6220_reset_assert(struct reset_controller_dev *rc_dev,
|
||||
struct hi6220_reset_data {
|
||||
struct reset_controller_dev rc_dev;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev,
|
||||
unsigned long idx)
|
||||
{
|
||||
struct hi6220_reset_data *data = to_reset_data(rc_dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
u32 bank = idx >> 8;
|
||||
u32 offset = idx & 0xff;
|
||||
u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10;
|
||||
|
||||
return regmap_write(regmap, reg, BIT(offset));
|
||||
}
|
||||
|
||||
static int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev,
|
||||
unsigned long idx)
|
||||
{
|
||||
struct hi6220_reset_data *data = to_reset_data(rc_dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
u32 bank = idx >> 8;
|
||||
u32 offset = idx & 0xff;
|
||||
u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10;
|
||||
|
||||
return regmap_write(regmap, reg, BIT(offset));
|
||||
}
|
||||
|
||||
static const struct reset_control_ops hi6220_peripheral_reset_ops = {
|
||||
.assert = hi6220_peripheral_assert,
|
||||
.deassert = hi6220_peripheral_deassert,
|
||||
};
|
||||
|
||||
static int hi6220_media_assert(struct reset_controller_dev *rc_dev,
|
||||
unsigned long idx)
|
||||
{
|
||||
struct hi6220_reset_data *data = to_reset_data(rc_dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
|
||||
int bank = idx >> 8;
|
||||
int offset = idx & 0xff;
|
||||
|
||||
writel(BIT(offset), data->assert_base + (bank * 0x10));
|
||||
|
||||
return 0;
|
||||
return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx));
|
||||
}
|
||||
|
||||
static int hi6220_reset_deassert(struct reset_controller_dev *rc_dev,
|
||||
static int hi6220_media_deassert(struct reset_controller_dev *rc_dev,
|
||||
unsigned long idx)
|
||||
{
|
||||
struct hi6220_reset_data *data = to_reset_data(rc_dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
|
||||
int bank = idx >> 8;
|
||||
int offset = idx & 0xff;
|
||||
|
||||
writel(BIT(offset), data->deassert_base + (bank * 0x10));
|
||||
|
||||
return 0;
|
||||
return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx));
|
||||
}
|
||||
|
||||
static const struct reset_control_ops hi6220_reset_ops = {
|
||||
.assert = hi6220_reset_assert,
|
||||
.deassert = hi6220_reset_deassert,
|
||||
static const struct reset_control_ops hi6220_media_reset_ops = {
|
||||
.assert = hi6220_media_assert,
|
||||
.deassert = hi6220_media_deassert,
|
||||
};
|
||||
|
||||
static int hi6220_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
enum hi6220_reset_ctrl_type type;
|
||||
struct hi6220_reset_data *data;
|
||||
struct resource *res;
|
||||
void __iomem *src_base;
|
||||
struct regmap *regmap;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
src_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(src_base))
|
||||
return PTR_ERR(src_base);
|
||||
type = (enum hi6220_reset_ctrl_type)of_device_get_match_data(dev);
|
||||
|
||||
data->assert_base = src_base + ASSERT_OFFSET;
|
||||
data->deassert_base = src_base + DEASSERT_OFFSET;
|
||||
data->rc_dev.nr_resets = MAX_INDEX;
|
||||
data->rc_dev.ops = &hi6220_reset_ops;
|
||||
data->rc_dev.of_node = pdev->dev.of_node;
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "failed to get reset controller regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
data->regmap = regmap;
|
||||
data->rc_dev.of_node = np;
|
||||
if (type == MEDIA) {
|
||||
data->rc_dev.ops = &hi6220_media_reset_ops;
|
||||
data->rc_dev.nr_resets = MEDIA_MAX_INDEX;
|
||||
} else {
|
||||
data->rc_dev.ops = &hi6220_peripheral_reset_ops;
|
||||
data->rc_dev.nr_resets = PERIPH_MAX_INDEX;
|
||||
}
|
||||
|
||||
return reset_controller_register(&data->rc_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id hi6220_reset_match[] = {
|
||||
{ .compatible = "hisilicon,hi6220-sysctrl" },
|
||||
{ },
|
||||
{
|
||||
.compatible = "hisilicon,hi6220-sysctrl",
|
||||
.data = (void *)PERIPHERAL,
|
||||
},
|
||||
{
|
||||
.compatible = "hisilicon,hi6220-mediactrl",
|
||||
.data = (void *)MEDIA,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hi6220_reset_match);
|
||||
|
||||
static struct platform_driver hi6220_reset_driver = {
|
||||
.probe = hi6220_reset_probe,
|
||||
|
@ -112,7 +112,7 @@ static int ath79_reset_probe(struct platform_device *pdev)
|
||||
ath79_reset->rcdev.of_reset_n_cells = 1;
|
||||
ath79_reset->rcdev.nr_resets = 32;
|
||||
|
||||
err = reset_controller_register(&ath79_reset->rcdev);
|
||||
err = devm_reset_controller_register(&pdev->dev, &ath79_reset->rcdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -131,7 +131,6 @@ static int ath79_reset_remove(struct platform_device *pdev)
|
||||
struct ath79_reset *ath79_reset = platform_get_drvdata(pdev);
|
||||
|
||||
unregister_restart_handler(&ath79_reset->restart_nb);
|
||||
reset_controller_unregister(&ath79_reset->rcdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
136
drivers/reset/reset-meson.c
Normal file
136
drivers/reset/reset-meson.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright (c) 2016 BayLibre, SAS.
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called COPYING.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) 2016 BayLibre, SAS.
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define REG_COUNT 8
|
||||
#define BITS_PER_REG 32
|
||||
|
||||
struct meson_reset {
|
||||
void __iomem *reg_base;
|
||||
struct reset_controller_dev rcdev;
|
||||
};
|
||||
|
||||
static int meson_reset_reset(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
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 + (bank << 2);
|
||||
|
||||
if (bank >= REG_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
writel(BIT(offset), reg_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct reset_control_ops meson_reset_ops = {
|
||||
.reset = meson_reset_reset,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_reset_dt_ids[] = {
|
||||
{ .compatible = "amlogic,meson8b-reset", },
|
||||
{ .compatible = "amlogic,meson-gxbb-reset", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_reset_dt_ids);
|
||||
|
||||
static int meson_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_reset *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->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(data->reg_base))
|
||||
return PTR_ERR(data->reg_base);
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
data->rcdev.owner = THIS_MODULE;
|
||||
data->rcdev.nr_resets = REG_COUNT * BITS_PER_REG;
|
||||
data->rcdev.ops = &meson_reset_ops;
|
||||
data->rcdev.of_node = pdev->dev.of_node;
|
||||
|
||||
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
|
||||
}
|
||||
|
||||
static struct platform_driver meson_reset_driver = {
|
||||
.probe = meson_reset_probe,
|
||||
.driver = {
|
||||
.name = "meson_reset",
|
||||
.of_match_table = meson_reset_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(meson_reset_driver);
|
||||
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_DESCRIPTION("Amlogic Meson Reset Controller driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
@ -112,21 +112,11 @@ static int oxnas_reset_probe(struct platform_device *pdev)
|
||||
data->rcdev.ops = &oxnas_reset_ops;
|
||||
data->rcdev.of_node = pdev->dev.of_node;
|
||||
|
||||
return reset_controller_register(&data->rcdev);
|
||||
}
|
||||
|
||||
static int oxnas_reset_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct oxnas_reset *data = platform_get_drvdata(pdev);
|
||||
|
||||
reset_controller_unregister(&data->rcdev);
|
||||
|
||||
return 0;
|
||||
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
|
||||
}
|
||||
|
||||
static struct platform_driver oxnas_reset_driver = {
|
||||
.probe = oxnas_reset_probe,
|
||||
.remove = oxnas_reset_remove,
|
||||
.driver = {
|
||||
.name = "oxnas-reset",
|
||||
.of_match_table = oxnas_reset_dt_ids,
|
||||
|
@ -121,16 +121,7 @@ static int pistachio_reset_probe(struct platform_device *pdev)
|
||||
rd->rcdev.ops = &pistachio_reset_ops;
|
||||
rd->rcdev.of_node = np;
|
||||
|
||||
return reset_controller_register(&rd->rcdev);
|
||||
}
|
||||
|
||||
static int pistachio_reset_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pistachio_reset_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
reset_controller_unregister(&data->rcdev);
|
||||
|
||||
return 0;
|
||||
return devm_reset_controller_register(dev, &rd->rcdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id pistachio_reset_dt_ids[] = {
|
||||
@ -141,7 +132,6 @@ MODULE_DEVICE_TABLE(of, pistachio_reset_dt_ids);
|
||||
|
||||
static struct platform_driver pistachio_reset_driver = {
|
||||
.probe = pistachio_reset_probe,
|
||||
.remove = pistachio_reset_remove,
|
||||
.driver = {
|
||||
.name = "pistachio-reset",
|
||||
.of_match_table = pistachio_reset_dt_ids,
|
||||
|
@ -134,16 +134,7 @@ static int socfpga_reset_probe(struct platform_device *pdev)
|
||||
data->rcdev.ops = &socfpga_reset_ops;
|
||||
data->rcdev.of_node = pdev->dev.of_node;
|
||||
|
||||
return reset_controller_register(&data->rcdev);
|
||||
}
|
||||
|
||||
static int socfpga_reset_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct socfpga_reset_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
reset_controller_unregister(&data->rcdev);
|
||||
|
||||
return 0;
|
||||
return devm_reset_controller_register(dev, &data->rcdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id socfpga_reset_dt_ids[] = {
|
||||
@ -153,7 +144,6 @@ static const struct of_device_id socfpga_reset_dt_ids[] = {
|
||||
|
||||
static struct platform_driver socfpga_reset_driver = {
|
||||
.probe = socfpga_reset_probe,
|
||||
.remove = socfpga_reset_remove,
|
||||
.driver = {
|
||||
.name = "socfpga-reset",
|
||||
.of_match_table = socfpga_reset_dt_ids,
|
||||
|
@ -165,21 +165,11 @@ static int sunxi_reset_probe(struct platform_device *pdev)
|
||||
data->rcdev.ops = &sunxi_reset_ops;
|
||||
data->rcdev.of_node = pdev->dev.of_node;
|
||||
|
||||
return reset_controller_register(&data->rcdev);
|
||||
}
|
||||
|
||||
static int sunxi_reset_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sunxi_reset_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
reset_controller_unregister(&data->rcdev);
|
||||
|
||||
return 0;
|
||||
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
|
||||
}
|
||||
|
||||
static struct platform_driver sunxi_reset_driver = {
|
||||
.probe = sunxi_reset_probe,
|
||||
.remove = sunxi_reset_remove,
|
||||
.driver = {
|
||||
.name = "sunxi-reset",
|
||||
.of_match_table = sunxi_reset_dt_ids,
|
||||
|
237
drivers/reset/reset-ti-syscon.c
Normal file
237
drivers/reset/reset-ti-syscon.c
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* TI SYSCON regmap reset driver
|
||||
*
|
||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
* Suman Anna <afd@ti.com>
|
||||
*
|
||||
* 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 "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/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include <dt-bindings/reset/ti-syscon.h>
|
||||
|
||||
/**
|
||||
* struct ti_syscon_reset_control - reset control structure
|
||||
* @assert_offset: reset assert control register offset from syscon base
|
||||
* @assert_bit: reset assert bit in the reset assert control register
|
||||
* @deassert_offset: reset deassert control register offset from syscon base
|
||||
* @deassert_bit: reset deassert bit in the reset deassert control register
|
||||
* @status_offset: reset status register offset from syscon base
|
||||
* @status_bit: reset status bit in the reset status register
|
||||
* @flags: reset flag indicating how the (de)assert and status are handled
|
||||
*/
|
||||
struct ti_syscon_reset_control {
|
||||
unsigned int assert_offset;
|
||||
unsigned int assert_bit;
|
||||
unsigned int deassert_offset;
|
||||
unsigned int deassert_bit;
|
||||
unsigned int status_offset;
|
||||
unsigned int status_bit;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_syscon_reset_data - reset controller information structure
|
||||
* @rcdev: reset controller entity
|
||||
* @regmap: regmap handle containing the memory-mapped reset registers
|
||||
* @controls: array of reset controls
|
||||
* @nr_controls: number of controls in control array
|
||||
*/
|
||||
struct ti_syscon_reset_data {
|
||||
struct reset_controller_dev rcdev;
|
||||
struct regmap *regmap;
|
||||
struct ti_syscon_reset_control *controls;
|
||||
unsigned int nr_controls;
|
||||
};
|
||||
|
||||
#define to_ti_syscon_reset_data(rcdev) \
|
||||
container_of(rcdev, struct ti_syscon_reset_data, rcdev)
|
||||
|
||||
/**
|
||||
* ti_syscon_reset_assert() - assert device reset
|
||||
* @rcdev: reset controller entity
|
||||
* @id: ID of the reset to be asserted
|
||||
*
|
||||
* This function implements the reset driver op to assert a device's reset.
|
||||
* This asserts the reset in a manner prescribed by the reset flags.
|
||||
*
|
||||
* Return: 0 for successful request, else a corresponding error value
|
||||
*/
|
||||
static int ti_syscon_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
|
||||
struct ti_syscon_reset_control *control;
|
||||
unsigned int mask, value;
|
||||
|
||||
if (id >= data->nr_controls)
|
||||
return -EINVAL;
|
||||
|
||||
control = &data->controls[id];
|
||||
|
||||
if (control->flags & ASSERT_NONE)
|
||||
return -ENOTSUPP; /* assert not supported for this reset */
|
||||
|
||||
mask = BIT(control->assert_bit);
|
||||
value = (control->flags & ASSERT_SET) ? mask : 0x0;
|
||||
|
||||
return regmap_update_bits(data->regmap, control->assert_offset, mask, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_syscon_reset_deassert() - deassert device reset
|
||||
* @rcdev: reset controller entity
|
||||
* @id: ID of reset to be deasserted
|
||||
*
|
||||
* This function implements the reset driver op to deassert a device's reset.
|
||||
* This deasserts the reset in a manner prescribed by the reset flags.
|
||||
*
|
||||
* Return: 0 for successful request, else a corresponding error value
|
||||
*/
|
||||
static int ti_syscon_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
|
||||
struct ti_syscon_reset_control *control;
|
||||
unsigned int mask, value;
|
||||
|
||||
if (id >= data->nr_controls)
|
||||
return -EINVAL;
|
||||
|
||||
control = &data->controls[id];
|
||||
|
||||
if (control->flags & DEASSERT_NONE)
|
||||
return -ENOTSUPP; /* deassert not supported for this reset */
|
||||
|
||||
mask = BIT(control->deassert_bit);
|
||||
value = (control->flags & DEASSERT_SET) ? mask : 0x0;
|
||||
|
||||
return regmap_update_bits(data->regmap, control->deassert_offset, mask, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_syscon_reset_status() - check device reset status
|
||||
* @rcdev: reset controller entity
|
||||
* @id: ID of the reset for which the status is being requested
|
||||
*
|
||||
* This function implements the reset driver op to return the status of a
|
||||
* device's reset.
|
||||
*
|
||||
* Return: 0 if reset is deasserted, true if reset is asserted, else a
|
||||
* corresponding error value
|
||||
*/
|
||||
static int ti_syscon_reset_status(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
|
||||
struct ti_syscon_reset_control *control;
|
||||
unsigned int reset_state;
|
||||
int ret;
|
||||
|
||||
if (id >= data->nr_controls)
|
||||
return -EINVAL;
|
||||
|
||||
control = &data->controls[id];
|
||||
|
||||
if (control->flags & STATUS_NONE)
|
||||
return -ENOTSUPP; /* status not supported for this reset */
|
||||
|
||||
ret = regmap_read(data->regmap, control->status_offset, &reset_state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (reset_state & BIT(control->status_bit)) &&
|
||||
(control->flags & STATUS_SET);
|
||||
}
|
||||
|
||||
static struct reset_control_ops ti_syscon_reset_ops = {
|
||||
.assert = ti_syscon_reset_assert,
|
||||
.deassert = ti_syscon_reset_deassert,
|
||||
.status = ti_syscon_reset_status,
|
||||
};
|
||||
|
||||
static int ti_syscon_reset_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct ti_syscon_reset_data *data;
|
||||
struct regmap *regmap;
|
||||
const __be32 *list;
|
||||
struct ti_syscon_reset_control *controls;
|
||||
int size, nr_controls, i;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
regmap = syscon_node_to_regmap(np->parent);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
list = of_get_property(np, "ti,reset-bits", &size);
|
||||
if (!list || (size / sizeof(*list)) % 7 != 0) {
|
||||
dev_err(dev, "invalid DT reset description\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nr_controls = (size / sizeof(*list)) / 7;
|
||||
controls = devm_kzalloc(dev, nr_controls * sizeof(*controls), GFP_KERNEL);
|
||||
if (!controls)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nr_controls; i++) {
|
||||
controls[i].assert_offset = be32_to_cpup(list++);
|
||||
controls[i].assert_bit = be32_to_cpup(list++);
|
||||
controls[i].deassert_offset = be32_to_cpup(list++);
|
||||
controls[i].deassert_bit = be32_to_cpup(list++);
|
||||
controls[i].status_offset = be32_to_cpup(list++);
|
||||
controls[i].status_bit = be32_to_cpup(list++);
|
||||
controls[i].flags = be32_to_cpup(list++);
|
||||
}
|
||||
|
||||
data->rcdev.ops = &ti_syscon_reset_ops;
|
||||
data->rcdev.owner = THIS_MODULE;
|
||||
data->rcdev.of_node = np;
|
||||
data->rcdev.nr_resets = nr_controls;
|
||||
data->regmap = regmap;
|
||||
data->controls = controls;
|
||||
data->nr_controls = nr_controls;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
return devm_reset_controller_register(dev, &data->rcdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_syscon_reset_of_match[] = {
|
||||
{ .compatible = "ti,syscon-reset", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_syscon_reset_of_match);
|
||||
|
||||
static struct platform_driver ti_syscon_reset_driver = {
|
||||
.probe = ti_syscon_reset_probe,
|
||||
.driver = {
|
||||
.name = "ti-syscon-reset",
|
||||
.of_match_table = ti_syscon_reset_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ti_syscon_reset_driver);
|
||||
|
||||
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
|
||||
MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
|
||||
MODULE_DESCRIPTION("TI SYSCON Regmap Reset Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user