mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-03-21 06:24:12 +00:00
ARM: SoC driver updates for v4.9
Driver updates for ARM SoCs, including a couple of newly added drivers: - The Qualcomm external bus interface 2 (EBI2), used in some of their mobile phone chips for connecting flash memory, LCD displays or other peripherals - Secure monitor firmware for Amlogic SoCs, and an NVMEM driver for the EFUSE based on that firmware interface. - Perf support for the AppliedMicro X-Gene performance monitor unit - Reset driver for STMicroelectronics STM32 - Reset driver for SocioNext UniPhier SoCs Aside from these, there are minor updates to SoC-specific bus, clocksource, firmware, pinctrl, reset, rtc and pmic drivers. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIVAwUAV/gaimCrR//JCVInAQJaOQ/6A++YfLVmdF4wxgcu/0ti28lA7SkQIGJV UAsfCmqMEutbeDvnloVGmTV2K2NS7mzxdxsJGbVB7Oe/zdOFN+T9sf9hAlId01QA oVkoagpofoxlyKoKJ/l+heuEEZMa0Ekk3XXRTGv/Ovymo7252o4tEdGu9c+gyaMJ KqgixcrQRzxuWDgPpHUPUez2vY1iRMvvdcb0EmfiHcIgPOEJc6MIxulsqEIrkoMz WYeGFIeqRJxnrur3QD8WnD+aZD6bV01wkFTkWXGWg4H87QfEESgVBu5A7TL+5sL8 1SlX/b7S5/ZJbrOiOS2IUyvbK7NiA/Q+NunHW2rMVnUWuEvJ9HAQB1kVSQH5LIYO 6OBokjcijm6m/j6O6fdDfvNd6PLsIEUqfWVws7O+uofMMqKPxqak4VBTRdFM+aeF ZtK7mEbzteCX0bnC+XblZrseAlkIehYnP80CLDbtDTerTWP4gsjxGVt3U6MO0NzB K0ACWZOclzrcFscNKrmP6uPCpfZriiPV/XMCEHcylA/X2iYsVmpqKzdLuNs5aeUr uPzQbNWu9ygg/bDRXMYY2E3Kzjsc0eIOKEOPyhLaZdSo4e1FQxud6L2V2Vj0RLB/ iMA7/CyQZqn6Yzgs0VMZm/bnh+hIdHioGFl5K5j6Fcw9VZRkNmnEQJzX4VU5efGO g1+5av0vFXg= =GvTq -----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 Arnd Bergmann: "Driver updates for ARM SoCs, including a couple of newly added drivers: - The Qualcomm external bus interface 2 (EBI2), used in some of their mobile phone chips for connecting flash memory, LCD displays or other peripherals - Secure monitor firmware for Amlogic SoCs, and an NVMEM driver for the EFUSE based on that firmware interface. - Perf support for the AppliedMicro X-Gene performance monitor unit - Reset driver for STMicroelectronics STM32 - Reset driver for SocioNext UniPhier SoCs Aside from these, there are minor updates to SoC-specific bus, clocksource, firmware, pinctrl, reset, rtc and pmic drivers" * tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (50 commits) bus: qcom-ebi2: depend on HAS_IOMEM pinctrl: mvebu: orion5x: Generalise mv88f5181l support for 88f5181 clk: mvebu: Add clk support for the orion5x SoC mv88f5181 dt-bindings: EXYNOS: Add Exynos5433 PMU compatible clocksource: exynos_mct: Add the support for ARM64 perf: xgene: Add APM X-Gene SoC Performance Monitoring Unit driver Documentation: Add documentation for APM X-Gene SoC PMU DTS binding MAINTAINERS: Add entry for APM X-Gene SoC PMU driver bus: qcom: add EBI2 driver bus: qcom: add EBI2 device tree bindings rtc: rtc-pm8xxx: Add support for pm8018 rtc nvmem: amlogic: Add Amlogic Meson EFUSE driver firmware: Amlogic: Add secure monitor driver soc: qcom: smd: Reset rx tail rather than tx memory: atmel-sdramc: fix a possible NULL dereference reset: hi6220: allow to compile test driver on other architectures reset: zynq: add driver Kconfig option reset: sunxi: add driver Kconfig option reset: stm32: add driver Kconfig option reset: socfpga: add driver Kconfig option ...
This commit is contained in:
commit
6afd563d4b
52 changed files with 3790 additions and 291 deletions
|
@ -10,6 +10,7 @@ Properties:
|
||||||
- "samsung,exynos5260-pmu" - for Exynos5260 SoC.
|
- "samsung,exynos5260-pmu" - for Exynos5260 SoC.
|
||||||
- "samsung,exynos5410-pmu" - for Exynos5410 SoC,
|
- "samsung,exynos5410-pmu" - for Exynos5410 SoC,
|
||||||
- "samsung,exynos5420-pmu" - for Exynos5420 SoC.
|
- "samsung,exynos5420-pmu" - for Exynos5420 SoC.
|
||||||
|
- "samsung,exynos5433-pmu" - for Exynos5433 SoC.
|
||||||
- "samsung,exynos7-pmu" - for Exynos7 SoC.
|
- "samsung,exynos7-pmu" - for Exynos7 SoC.
|
||||||
second value must be always "syscon".
|
second value must be always "syscon".
|
||||||
|
|
||||||
|
|
138
Documentation/devicetree/bindings/bus/qcom,ebi2.txt
Normal file
138
Documentation/devicetree/bindings/bus/qcom,ebi2.txt
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
Qualcomm External Bus Interface 2 (EBI2)
|
||||||
|
|
||||||
|
The EBI2 contains two peripheral blocks: XMEM and LCDC. The XMEM handles any
|
||||||
|
external memory (such as NAND or other memory-mapped peripherals) whereas
|
||||||
|
LCDC handles LCD displays.
|
||||||
|
|
||||||
|
As it says it connects devices to an external bus interface, meaning address
|
||||||
|
lines (up to 9 address lines so can only address 1KiB external memory space),
|
||||||
|
data lines (16 bits), OE (output enable), ADV (address valid, used on some
|
||||||
|
NOR flash memories), WE (write enable). This on top of 6 different chip selects
|
||||||
|
(CS0 thru CS5) so that in theory 6 different devices can be connected.
|
||||||
|
|
||||||
|
Apparently this bus is clocked at 64MHz. It has dedicated pins on the package
|
||||||
|
and the bus can only come out on these pins, however if some of the pins are
|
||||||
|
unused they can be left unconnected or remuxed to be used as GPIO or in some
|
||||||
|
cases other orthogonal functions as well.
|
||||||
|
|
||||||
|
Also CS1 and CS2 has -A and -B signals. Why they have that is unclear to me.
|
||||||
|
|
||||||
|
The chip selects have the following memory range assignments. This region of
|
||||||
|
memory is referred to as "Chip Peripheral SS FPB0" and is 168MB big.
|
||||||
|
|
||||||
|
Chip Select Physical address base
|
||||||
|
CS0 GPIO134 0x1a800000-0x1b000000 (8MB)
|
||||||
|
CS1 GPIO39 (A) / GPIO123 (B) 0x1b000000-0x1b800000 (8MB)
|
||||||
|
CS2 GPIO40 (A) / GPIO124 (B) 0x1b800000-0x1c000000 (8MB)
|
||||||
|
CS3 GPIO133 0x1d000000-0x25000000 (128 MB)
|
||||||
|
CS4 GPIO132 0x1c800000-0x1d000000 (8MB)
|
||||||
|
CS5 GPIO131 0x1c000000-0x1c800000 (8MB)
|
||||||
|
|
||||||
|
The APQ8060 Qualcomm Application Processor User Guide, 80-N7150-14 Rev. A,
|
||||||
|
August 6, 2012 contains some incomplete documentation of the EBI2.
|
||||||
|
|
||||||
|
FIXME: the manual mentions "write precharge cycles" and "precharge cycles".
|
||||||
|
We have not been able to figure out which bit fields these correspond to
|
||||||
|
in the hardware, or what valid values exist. The current hypothesis is that
|
||||||
|
this is something just used on the FAST chip selects and that the SLOW
|
||||||
|
chip selects are understood fully. There is also a "byte device enable"
|
||||||
|
flag somewhere for 8bit memories.
|
||||||
|
|
||||||
|
FIXME: The chipselects have SLOW and FAST configuration registers. It's a bit
|
||||||
|
unclear what this means, if they are mutually exclusive or can be used
|
||||||
|
together, or if some chip selects are hardwired to be FAST and others are SLOW
|
||||||
|
by design.
|
||||||
|
|
||||||
|
The XMEM registers are totally undocumented but could be partially decoded
|
||||||
|
because the Cypress AN49576 Antioch Westbridge apparently has suspiciously
|
||||||
|
similar register layout, see: http://www.cypress.com/file/105771/download
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be one of:
|
||||||
|
"qcom,msm8660-ebi2"
|
||||||
|
"qcom,apq8060-ebi2"
|
||||||
|
- #address-cells: shoule be <2>: the first cell is the chipselect,
|
||||||
|
the second cell is the offset inside the memory range
|
||||||
|
- #size-cells: should be <1>
|
||||||
|
- ranges: should be set to:
|
||||||
|
ranges = <0 0x0 0x1a800000 0x00800000>,
|
||||||
|
<1 0x0 0x1b000000 0x00800000>,
|
||||||
|
<2 0x0 0x1b800000 0x00800000>,
|
||||||
|
<3 0x0 0x1d000000 0x08000000>,
|
||||||
|
<4 0x0 0x1c800000 0x00800000>,
|
||||||
|
<5 0x0 0x1c000000 0x00800000>;
|
||||||
|
- reg: two ranges of registers: EBI2 config and XMEM config areas
|
||||||
|
- reg-names: should be "ebi2", "xmem"
|
||||||
|
- clocks: two clocks, EBI_2X and EBI
|
||||||
|
- clock-names: shoule be "ebi2x", "ebi2"
|
||||||
|
|
||||||
|
Optional subnodes:
|
||||||
|
- Nodes inside the EBI2 will be considered device nodes.
|
||||||
|
|
||||||
|
The following optional properties are properties that can be tagged onto
|
||||||
|
any device subnode. We are assuming that there can be only ONE device per
|
||||||
|
chipselect subnode, else the properties will become ambigous.
|
||||||
|
|
||||||
|
Optional properties arrays for SLOW chip selects:
|
||||||
|
- qcom,xmem-recovery-cycles: recovery cycles is the time the memory continues to
|
||||||
|
drive the data bus after OE is de-asserted, in order to avoid contention on
|
||||||
|
the data bus. They are inserted when reading one CS and switching to another
|
||||||
|
CS or read followed by write on the same CS. Valid values 0 thru 15. Minimum
|
||||||
|
value is actually 1, so a value of 0 will still yield 1 recovery cycle.
|
||||||
|
- qcom,xmem-write-hold-cycles: write hold cycles, these are extra cycles
|
||||||
|
inserted after every write minimum 1. The data out is driven from the time
|
||||||
|
WE is asserted until CS is asserted. With a hold of 1 (value = 0), the CS
|
||||||
|
stays active for 1 extra cycle etc. Valid values 0 thru 15.
|
||||||
|
- qcom,xmem-write-delta-cycles: initial latency for write cycles inserted for
|
||||||
|
the first write to a page or burst memory. Valid values 0 thru 255.
|
||||||
|
- qcom,xmem-read-delta-cycles: initial latency for read cycles inserted for the
|
||||||
|
first read to a page or burst memory. Valid values 0 thru 255.
|
||||||
|
- qcom,xmem-write-wait-cycles: number of wait cycles for every write access, 0=1
|
||||||
|
cycle. Valid values 0 thru 15.
|
||||||
|
- qcom,xmem-read-wait-cycles: number of wait cycles for every read access, 0=1
|
||||||
|
cycle. Valid values 0 thru 15.
|
||||||
|
|
||||||
|
Optional properties arrays for FAST chip selects:
|
||||||
|
- qcom,xmem-address-hold-enable: this is a boolean property stating that we
|
||||||
|
shall hold the address for an extra cycle to meet hold time requirements
|
||||||
|
with ADV assertion.
|
||||||
|
- qcom,xmem-adv-to-oe-recovery-cycles: the number of cycles elapsed before an OE
|
||||||
|
assertion, with respect to the cycle where ADV (address valid) is asserted.
|
||||||
|
2 means 2 cycles between ADV and OE. Valid values 0, 1, 2 or 3.
|
||||||
|
- qcom,xmem-read-hold-cycles: the length in cycles of the first segment of a
|
||||||
|
read transfer. For a single read trandfer this will be the time from CS
|
||||||
|
assertion to OE assertion. Valid values 0 thru 15.
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
ebi2@1a100000 {
|
||||||
|
compatible = "qcom,apq8060-ebi2";
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
ranges = <0 0x0 0x1a800000 0x00800000>,
|
||||||
|
<1 0x0 0x1b000000 0x00800000>,
|
||||||
|
<2 0x0 0x1b800000 0x00800000>,
|
||||||
|
<3 0x0 0x1d000000 0x08000000>,
|
||||||
|
<4 0x0 0x1c800000 0x00800000>,
|
||||||
|
<5 0x0 0x1c000000 0x00800000>;
|
||||||
|
reg = <0x1a100000 0x1000>, <0x1a110000 0x1000>;
|
||||||
|
reg-names = "ebi2", "xmem";
|
||||||
|
clocks = <&gcc EBI2_2X_CLK>, <&gcc EBI2_CLK>;
|
||||||
|
clock-names = "ebi2x", "ebi2";
|
||||||
|
/* Make sure to set up the pin control for the EBI2 */
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&foo_ebi2_pins>;
|
||||||
|
|
||||||
|
foo-ebi2@2,0 {
|
||||||
|
compatible = "foo";
|
||||||
|
reg = <2 0x0 0x100>;
|
||||||
|
(...)
|
||||||
|
qcom,xmem-recovery-cycles = <0>;
|
||||||
|
qcom,xmem-write-hold-cycles = <3>;
|
||||||
|
qcom,xmem-write-delta-cycles = <31>;
|
||||||
|
qcom,xmem-read-delta-cycles = <28>;
|
||||||
|
qcom,xmem-write-wait-cycles = <9>;
|
||||||
|
qcom,xmem-read-wait-cycles = <9>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -52,6 +52,7 @@ Required properties:
|
||||||
"marvell,dove-core-clock" - for Dove SoC core clocks
|
"marvell,dove-core-clock" - for Dove SoC core clocks
|
||||||
"marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180)
|
"marvell,kirkwood-core-clock" - for Kirkwood SoC (except mv88f6180)
|
||||||
"marvell,mv88f6180-core-clock" - for Kirkwood MV88f6180 SoC
|
"marvell,mv88f6180-core-clock" - for Kirkwood MV88f6180 SoC
|
||||||
|
"marvell,mv88f5181-core-clock" - for Orion MV88F5181 SoC
|
||||||
"marvell,mv88f5182-core-clock" - for Orion MV88F5182 SoC
|
"marvell,mv88f5182-core-clock" - for Orion MV88F5182 SoC
|
||||||
"marvell,mv88f5281-core-clock" - for Orion MV88F5281 SoC
|
"marvell,mv88f5281-core-clock" - for Orion MV88F5281 SoC
|
||||||
"marvell,mv88f6183-core-clock" - for Orion MV88F6183 SoC
|
"marvell,mv88f6183-core-clock" - for Orion MV88F6183 SoC
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
STMicroelectronics STM32 Reset and Clock Controller
|
STMicroelectronics STM32 Reset and Clock Controller
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
The RCC IP is both a reset and a clock controller. This documentation only
|
The RCC IP is both a reset and a clock controller.
|
||||||
describes the clock part.
|
|
||||||
|
|
||||||
Please also refer to clock-bindings.txt in this directory for common clock
|
Please refer to clock-bindings.txt for common clock controller binding usage.
|
||||||
controller binding usage.
|
Please also refer to reset.txt for common reset controller binding usage.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should be "st,stm32f42xx-rcc"
|
- compatible: Should be "st,stm32f42xx-rcc"
|
||||||
- reg: should be register base and length as documented in the
|
- reg: should be register base and length as documented in the
|
||||||
datasheet
|
datasheet
|
||||||
|
- #reset-cells: 1, see below
|
||||||
- #clock-cells: 2, device nodes should specify the clock in their "clocks"
|
- #clock-cells: 2, device nodes should specify the clock in their "clocks"
|
||||||
property, containing a phandle to the clock device node, an index selecting
|
property, containing a phandle to the clock device node, an index selecting
|
||||||
between gated clocks and other clocks and an index specifying the clock to
|
between gated clocks and other clocks and an index specifying the clock to
|
||||||
|
@ -19,6 +19,7 @@ Required properties:
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
rcc: rcc@40023800 {
|
rcc: rcc@40023800 {
|
||||||
|
#reset-cells = <1>;
|
||||||
#clock-cells = <2>
|
#clock-cells = <2>
|
||||||
compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
|
compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
|
||||||
reg = <0x40023800 0x400>;
|
reg = <0x40023800 0x400>;
|
||||||
|
@ -35,16 +36,23 @@ from the first RCC clock enable register (RCC_AHB1ENR, address offset 0x30).
|
||||||
It is calculated as: index = register_offset / 4 * 32 + bit_offset.
|
It is calculated as: index = register_offset / 4 * 32 + bit_offset.
|
||||||
Where bit_offset is the bit offset within the register (LSB is 0, MSB is 31).
|
Where bit_offset is the bit offset within the register (LSB is 0, MSB is 31).
|
||||||
|
|
||||||
|
To simplify the usage and to share bit definition with the reset and clock
|
||||||
|
drivers of the RCC IP, macros are available to generate the index in
|
||||||
|
human-readble format.
|
||||||
|
|
||||||
|
For STM32F4 series, the macro are available here:
|
||||||
|
- include/dt-bindings/mfd/stm32f4-rcc.h
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
/* Gated clock, AHB1 bit 0 (GPIOA) */
|
/* Gated clock, AHB1 bit 0 (GPIOA) */
|
||||||
... {
|
... {
|
||||||
clocks = <&rcc 0 0>
|
clocks = <&rcc 0 STM32F4_AHB1_CLOCK(GPIOA)>
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Gated clock, AHB2 bit 4 (CRYP) */
|
/* Gated clock, AHB2 bit 4 (CRYP) */
|
||||||
... {
|
... {
|
||||||
clocks = <&rcc 0 36>
|
clocks = <&rcc 0 STM32F4_AHB2_CLOCK(CRYP)>
|
||||||
};
|
};
|
||||||
|
|
||||||
Specifying other clocks
|
Specifying other clocks
|
||||||
|
@ -61,5 +69,25 @@ Example:
|
||||||
|
|
||||||
/* Misc clock, FCLK */
|
/* Misc clock, FCLK */
|
||||||
... {
|
... {
|
||||||
clocks = <&rcc 1 1>
|
clocks = <&rcc 1 STM32F4_APB1_CLOCK(TIM2)>
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Specifying softreset control of devices
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Device nodes should specify the reset channel required in their "resets"
|
||||||
|
property, containing a phandle to the reset device node and an index specifying
|
||||||
|
which channel to use.
|
||||||
|
The index is the bit number within the RCC registers bank, starting from RCC
|
||||||
|
base address.
|
||||||
|
It is calculated as: index = register_offset / 4 * 32 + bit_offset.
|
||||||
|
Where bit_offset is the bit offset within the register.
|
||||||
|
For example, for CRC reset:
|
||||||
|
crc = AHB1RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x10 / 4 * 32 + 12 = 140
|
||||||
|
|
||||||
|
example:
|
||||||
|
|
||||||
|
timer2 {
|
||||||
|
resets = <&rcc STM32F4_APB1_RESET(TIM2)>;
|
||||||
};
|
};
|
||||||
|
|
112
Documentation/devicetree/bindings/perf/apm-xgene-pmu.txt
Normal file
112
Documentation/devicetree/bindings/perf/apm-xgene-pmu.txt
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
* APM X-Gene SoC PMU bindings
|
||||||
|
|
||||||
|
This is APM X-Gene SoC PMU (Performance Monitoring Unit) module.
|
||||||
|
The following PMU devices are supported:
|
||||||
|
|
||||||
|
L3C - L3 cache controller
|
||||||
|
IOB - IO bridge
|
||||||
|
MCB - Memory controller bridge
|
||||||
|
MC - Memory controller
|
||||||
|
|
||||||
|
The following section describes the SoC PMU DT node binding.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : Shall be "apm,xgene-pmu" for revision 1 or
|
||||||
|
"apm,xgene-pmu-v2" for revision 2.
|
||||||
|
- regmap-csw : Regmap of the CPU switch fabric (CSW) resource.
|
||||||
|
- regmap-mcba : Regmap of the MCB-A (memory bridge) resource.
|
||||||
|
- regmap-mcbb : Regmap of the MCB-B (memory bridge) resource.
|
||||||
|
- reg : First resource shall be the CPU bus PMU resource.
|
||||||
|
- interrupts : Interrupt-specifier for PMU IRQ.
|
||||||
|
|
||||||
|
Required properties for L3C subnode:
|
||||||
|
- compatible : Shall be "apm,xgene-pmu-l3c".
|
||||||
|
- reg : First resource shall be the L3C PMU resource.
|
||||||
|
|
||||||
|
Required properties for IOB subnode:
|
||||||
|
- compatible : Shall be "apm,xgene-pmu-iob".
|
||||||
|
- reg : First resource shall be the IOB PMU resource.
|
||||||
|
|
||||||
|
Required properties for MCB subnode:
|
||||||
|
- compatible : Shall be "apm,xgene-pmu-mcb".
|
||||||
|
- reg : First resource shall be the MCB PMU resource.
|
||||||
|
- enable-bit-index : The bit indicates if the according MCB is enabled.
|
||||||
|
|
||||||
|
Required properties for MC subnode:
|
||||||
|
- compatible : Shall be "apm,xgene-pmu-mc".
|
||||||
|
- reg : First resource shall be the MC PMU resource.
|
||||||
|
- enable-bit-index : The bit indicates if the according MC is enabled.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
csw: csw@7e200000 {
|
||||||
|
compatible = "apm,xgene-csw", "syscon";
|
||||||
|
reg = <0x0 0x7e200000 0x0 0x1000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
mcba: mcba@7e700000 {
|
||||||
|
compatible = "apm,xgene-mcb", "syscon";
|
||||||
|
reg = <0x0 0x7e700000 0x0 0x1000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
mcbb: mcbb@7e720000 {
|
||||||
|
compatible = "apm,xgene-mcb", "syscon";
|
||||||
|
reg = <0x0 0x7e720000 0x0 0x1000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmu: pmu@78810000 {
|
||||||
|
compatible = "apm,xgene-pmu-v2";
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
ranges;
|
||||||
|
regmap-csw = <&csw>;
|
||||||
|
regmap-mcba = <&mcba>;
|
||||||
|
regmap-mcbb = <&mcbb>;
|
||||||
|
reg = <0x0 0x78810000 0x0 0x1000>;
|
||||||
|
interrupts = <0x0 0x22 0x4>;
|
||||||
|
|
||||||
|
pmul3c@7e610000 {
|
||||||
|
compatible = "apm,xgene-pmu-l3c";
|
||||||
|
reg = <0x0 0x7e610000 0x0 0x1000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmuiob@7e940000 {
|
||||||
|
compatible = "apm,xgene-pmu-iob";
|
||||||
|
reg = <0x0 0x7e940000 0x0 0x1000>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmucmcb@7e710000 {
|
||||||
|
compatible = "apm,xgene-pmu-mcb";
|
||||||
|
reg = <0x0 0x7e710000 0x0 0x1000>;
|
||||||
|
enable-bit-index = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmucmcb@7e730000 {
|
||||||
|
compatible = "apm,xgene-pmu-mcb";
|
||||||
|
reg = <0x0 0x7e730000 0x0 0x1000>;
|
||||||
|
enable-bit-index = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmucmc@7e810000 {
|
||||||
|
compatible = "apm,xgene-pmu-mc";
|
||||||
|
reg = <0x0 0x7e810000 0x0 0x1000>;
|
||||||
|
enable-bit-index = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmucmc@7e850000 {
|
||||||
|
compatible = "apm,xgene-pmu-mc";
|
||||||
|
reg = <0x0 0x7e850000 0x0 0x1000>;
|
||||||
|
enable-bit-index = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmucmc@7e890000 {
|
||||||
|
compatible = "apm,xgene-pmu-mc";
|
||||||
|
reg = <0x0 0x7e890000 0x0 0x1000>;
|
||||||
|
enable-bit-index = <2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
pmucmc@7e8d0000 {
|
||||||
|
compatible = "apm,xgene-pmu-mc";
|
||||||
|
reg = <0x0 0x7e8d0000 0x0 0x1000>;
|
||||||
|
enable-bit-index = <3>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -4,7 +4,9 @@ Please refer to marvell,mvebu-pinctrl.txt in this directory for common binding
|
||||||
part and usage.
|
part and usage.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "marvell,88f5181l-pinctrl", "marvell,88f5182-pinctrl",
|
- compatible: "marvell,88f5181-pinctrl",
|
||||||
|
"marvell,88f5181l-pinctrl",
|
||||||
|
"marvell,88f5182-pinctrl",
|
||||||
"marvell,88f5281-pinctrl"
|
"marvell,88f5281-pinctrl"
|
||||||
|
|
||||||
- reg: two register areas, the first one describing the first two
|
- reg: two register areas, the first one describing the first two
|
||||||
|
|
6
Documentation/devicetree/bindings/reset/st,stm32-rcc.txt
Normal file
6
Documentation/devicetree/bindings/reset/st,stm32-rcc.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
STMicroelectronics STM32 Peripheral Reset Controller
|
||||||
|
====================================================
|
||||||
|
|
||||||
|
The RCC IP is both a reset and a clock controller.
|
||||||
|
|
||||||
|
Please see Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
|
93
Documentation/devicetree/bindings/reset/uniphier-reset.txt
Normal file
93
Documentation/devicetree/bindings/reset/uniphier-reset.txt
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
UniPhier reset controller
|
||||||
|
|
||||||
|
|
||||||
|
System reset
|
||||||
|
------------
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be one of the following:
|
||||||
|
"socionext,uniphier-sld3-reset" - for PH1-sLD3 SoC.
|
||||||
|
"socionext,uniphier-ld4-reset" - for PH1-LD4 SoC.
|
||||||
|
"socionext,uniphier-pro4-reset" - for PH1-Pro4 SoC.
|
||||||
|
"socionext,uniphier-sld8-reset" - for PH1-sLD8 SoC.
|
||||||
|
"socionext,uniphier-pro5-reset" - for PH1-Pro5 SoC.
|
||||||
|
"socionext,uniphier-pxs2-reset" - for ProXstream2/PH1-LD6b SoC.
|
||||||
|
"socionext,uniphier-ld11-reset" - for PH1-LD11 SoC.
|
||||||
|
"socionext,uniphier-ld20-reset" - for PH1-LD20 SoC.
|
||||||
|
- #reset-cells: should be 1.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
sysctrl@61840000 {
|
||||||
|
compatible = "socionext,uniphier-ld20-sysctrl",
|
||||||
|
"simple-mfd", "syscon";
|
||||||
|
reg = <0x61840000 0x4000>;
|
||||||
|
|
||||||
|
reset {
|
||||||
|
compatible = "socionext,uniphier-ld20-reset";
|
||||||
|
#reset-cells = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
other nodes ...
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Media I/O (MIO) reset
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be one of the following:
|
||||||
|
"socionext,uniphier-sld3-mio-reset" - for PH1-sLD3 SoC.
|
||||||
|
"socionext,uniphier-ld4-mio-reset" - for PH1-LD4 SoC.
|
||||||
|
"socionext,uniphier-pro4-mio-reset" - for PH1-Pro4 SoC.
|
||||||
|
"socionext,uniphier-sld8-mio-reset" - for PH1-sLD8 SoC.
|
||||||
|
"socionext,uniphier-pro5-mio-reset" - for PH1-Pro5 SoC.
|
||||||
|
"socionext,uniphier-pxs2-mio-reset" - for ProXstream2/PH1-LD6b SoC.
|
||||||
|
"socionext,uniphier-ld11-mio-reset" - for PH1-LD11 SoC.
|
||||||
|
"socionext,uniphier-ld20-mio-reset" - for PH1-LD20 SoC.
|
||||||
|
- #reset-cells: should be 1.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
mioctrl@59810000 {
|
||||||
|
compatible = "socionext,uniphier-ld20-mioctrl",
|
||||||
|
"simple-mfd", "syscon";
|
||||||
|
reg = <0x59810000 0x800>;
|
||||||
|
|
||||||
|
reset {
|
||||||
|
compatible = "socionext,uniphier-ld20-mio-reset";
|
||||||
|
#reset-cells = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
other nodes ...
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Peripheral reset
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should be one of the following:
|
||||||
|
"socionext,uniphier-ld4-peri-reset" - for PH1-LD4 SoC.
|
||||||
|
"socionext,uniphier-pro4-peri-reset" - for PH1-Pro4 SoC.
|
||||||
|
"socionext,uniphier-sld8-peri-reset" - for PH1-sLD8 SoC.
|
||||||
|
"socionext,uniphier-pro5-peri-reset" - for PH1-Pro5 SoC.
|
||||||
|
"socionext,uniphier-pxs2-peri-reset" - for ProXstream2/PH1-LD6b SoC.
|
||||||
|
"socionext,uniphier-ld11-peri-reset" - for PH1-LD11 SoC.
|
||||||
|
"socionext,uniphier-ld20-peri-reset" - for PH1-LD20 SoC.
|
||||||
|
- #reset-cells: should be 1.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
perictrl@59820000 {
|
||||||
|
compatible = "socionext,uniphier-ld20-perictrl",
|
||||||
|
"simple-mfd", "syscon";
|
||||||
|
reg = <0x59820000 0x200>;
|
||||||
|
|
||||||
|
reset {
|
||||||
|
compatible = "socionext,uniphier-ld20-peri-reset";
|
||||||
|
#reset-cells = <1>;
|
||||||
|
};
|
||||||
|
|
||||||
|
other nodes ...
|
||||||
|
};
|
48
Documentation/perf/xgene-pmu.txt
Normal file
48
Documentation/perf/xgene-pmu.txt
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
APM X-Gene SoC Performance Monitoring Unit (PMU)
|
||||||
|
================================================
|
||||||
|
|
||||||
|
X-Gene SoC PMU consists of various independent system device PMUs such as
|
||||||
|
L3 cache(s), I/O bridge(s), memory controller bridge(s) and memory
|
||||||
|
controller(s). These PMU devices are loosely architected to follow the
|
||||||
|
same model as the PMU for ARM cores. The PMUs share the same top level
|
||||||
|
interrupt and status CSR region.
|
||||||
|
|
||||||
|
PMU (perf) driver
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The xgene-pmu driver registers several perf PMU drivers. Each of the perf
|
||||||
|
driver provides description of its available events and configuration options
|
||||||
|
in sysfs, see /sys/devices/<l3cX/iobX/mcbX/mcX>/.
|
||||||
|
|
||||||
|
The "format" directory describes format of the config (event ID),
|
||||||
|
config1 (agent ID) fields of the perf_event_attr structure. The "events"
|
||||||
|
directory provides configuration templates for all supported event types that
|
||||||
|
can be used with perf tool. For example, "l3c0/bank-fifo-full/" is an
|
||||||
|
equivalent of "l3c0/config=0x0b/".
|
||||||
|
|
||||||
|
Most of the SoC PMU has a specific list of agent ID used for monitoring
|
||||||
|
performance of a specific datapath. For example, agents of a L3 cache can be
|
||||||
|
a specific CPU or an I/O bridge. Each PMU has a set of 2 registers capable of
|
||||||
|
masking the agents from which the request come from. If the bit with
|
||||||
|
the bit number corresponding to the agent is set, the event is counted only if
|
||||||
|
it is caused by a request from that agent. Each agent ID bit is inversely mapped
|
||||||
|
to a corresponding bit in "config1" field. By default, the event will be
|
||||||
|
counted for all agent requests (config1 = 0x0). For all the supported agents of
|
||||||
|
each PMU, please refer to APM X-Gene User Manual.
|
||||||
|
|
||||||
|
Each perf driver also provides a "cpumask" sysfs attribute, which contains a
|
||||||
|
single CPU ID of the processor which will be used to handle all the PMU events.
|
||||||
|
|
||||||
|
Example for perf tool use:
|
||||||
|
|
||||||
|
/ # perf list | grep -e l3c -e iob -e mcb -e mc
|
||||||
|
l3c0/ackq-full/ [Kernel PMU event]
|
||||||
|
<...>
|
||||||
|
mcb1/mcb-csw-stall/ [Kernel PMU event]
|
||||||
|
|
||||||
|
/ # perf stat -a -e l3c0/read-miss/,mcb1/csw-write-request/ sleep 1
|
||||||
|
|
||||||
|
/ # perf stat -a -e l3c0/read-miss,config1=0xfffffffffffffffe/ sleep 1
|
||||||
|
|
||||||
|
The driver does not support sampling, therefore "perf record" will
|
||||||
|
not work. Per-task (without "-a") perf sessions are not supported.
|
|
@ -866,6 +866,13 @@ F: drivers/net/phy/mdio-xgene.c
|
||||||
F: Documentation/devicetree/bindings/net/apm-xgene-enet.txt
|
F: Documentation/devicetree/bindings/net/apm-xgene-enet.txt
|
||||||
F: Documentation/devicetree/bindings/net/apm-xgene-mdio.txt
|
F: Documentation/devicetree/bindings/net/apm-xgene-mdio.txt
|
||||||
|
|
||||||
|
APPLIED MICRO (APM) X-GENE SOC PMU
|
||||||
|
M: Tai Nguyen <ttnguyen@apm.com>
|
||||||
|
S: Supported
|
||||||
|
F: drivers/perf/xgene_pmu.c
|
||||||
|
F: Documentation/perf/xgene-pmu.txt
|
||||||
|
F: Documentation/devicetree/bindings/perf/apm-xgene-pmu.txt
|
||||||
|
|
||||||
APTINA CAMERA SENSOR PLL
|
APTINA CAMERA SENSOR PLL
|
||||||
M: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
|
M: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
|
||||||
L: linux-media@vger.kernel.org
|
L: linux-media@vger.kernel.org
|
||||||
|
@ -1861,6 +1868,7 @@ F: drivers/bus/uniphier-system-bus.c
|
||||||
F: drivers/clk/uniphier/
|
F: drivers/clk/uniphier/
|
||||||
F: drivers/i2c/busses/i2c-uniphier*
|
F: drivers/i2c/busses/i2c-uniphier*
|
||||||
F: drivers/pinctrl/uniphier/
|
F: drivers/pinctrl/uniphier/
|
||||||
|
F: drivers/reset/reset-uniphier.c
|
||||||
F: drivers/tty/serial/8250/8250_uniphier.c
|
F: drivers/tty/serial/8250/8250_uniphier.c
|
||||||
N: uniphier
|
N: uniphier
|
||||||
|
|
||||||
|
|
|
@ -334,6 +334,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
rcc: rcc@40023810 {
|
rcc: rcc@40023810 {
|
||||||
|
#reset-cells = <1>;
|
||||||
#clock-cells = <2>;
|
#clock-cells = <2>;
|
||||||
compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
|
compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
|
||||||
reg = <0x40023800 0x400>;
|
reg = <0x40023800 0x400>;
|
||||||
|
|
|
@ -108,6 +108,14 @@ config OMAP_OCP2SCP
|
||||||
OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via
|
OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via
|
||||||
OCP2SCP.
|
OCP2SCP.
|
||||||
|
|
||||||
|
config QCOM_EBI2
|
||||||
|
bool "Qualcomm External Bus Interface 2 (EBI2)"
|
||||||
|
depends on HAS_IOMEM
|
||||||
|
help
|
||||||
|
Say y here to enable support for the Qualcomm External Bus
|
||||||
|
Interface 2, which can be used to connect things like NAND Flash,
|
||||||
|
SRAM, ethernet adapters, FPGAs and LCD displays.
|
||||||
|
|
||||||
config SIMPLE_PM_BUS
|
config SIMPLE_PM_BUS
|
||||||
bool "Simple Power-Managed Bus Driver"
|
bool "Simple Power-Managed Bus Driver"
|
||||||
depends on OF && PM
|
depends on OF && PM
|
||||||
|
@ -132,12 +140,8 @@ config SUNXI_RSB
|
||||||
with various RSB based devices, such as AXP223, AXP8XX PMICs,
|
with various RSB based devices, such as AXP223, AXP8XX PMICs,
|
||||||
and AC100/AC200 ICs.
|
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
|
config TEGRA_ACONNECT
|
||||||
bool "Tegra ACONNECT Bus Driver"
|
tristate "Tegra ACONNECT Bus Driver"
|
||||||
depends on ARCH_TEGRA_210_SOC
|
depends on ARCH_TEGRA_210_SOC
|
||||||
depends on OF && PM
|
depends on OF && PM
|
||||||
select PM_CLK
|
select PM_CLK
|
||||||
|
|
|
@ -15,6 +15,7 @@ obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
|
||||||
obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
|
obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
|
||||||
|
|
||||||
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
|
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
|
||||||
|
obj-$(CONFIG_QCOM_EBI2) += qcom-ebi2.o
|
||||||
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
|
obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
|
||||||
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
|
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
|
||||||
obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o
|
obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o
|
||||||
|
|
408
drivers/bus/qcom-ebi2.c
Normal file
408
drivers/bus/qcom-ebi2.c
Normal file
|
@ -0,0 +1,408 @@
|
||||||
|
/*
|
||||||
|
* Qualcomm External Bus Interface 2 (EBI2) driver
|
||||||
|
* an older version of the Qualcomm Parallel Interface Controller (QPIC)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Linaro Ltd.
|
||||||
|
*
|
||||||
|
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* See the device tree bindings for this block for more details on the
|
||||||
|
* hardware.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CS0, CS1, CS4 and CS5 are two bits wide, CS2 and CS3 are one bit.
|
||||||
|
*/
|
||||||
|
#define EBI2_CS0_ENABLE_MASK BIT(0)|BIT(1)
|
||||||
|
#define EBI2_CS1_ENABLE_MASK BIT(2)|BIT(3)
|
||||||
|
#define EBI2_CS2_ENABLE_MASK BIT(4)
|
||||||
|
#define EBI2_CS3_ENABLE_MASK BIT(5)
|
||||||
|
#define EBI2_CS4_ENABLE_MASK BIT(6)|BIT(7)
|
||||||
|
#define EBI2_CS5_ENABLE_MASK BIT(8)|BIT(9)
|
||||||
|
#define EBI2_CSN_MASK GENMASK(9, 0)
|
||||||
|
|
||||||
|
#define EBI2_XMEM_CFG 0x0000 /* Power management etc */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SLOW CSn CFG
|
||||||
|
*
|
||||||
|
* Bits 31-28: RECOVERY recovery cycles (0 = 1, 1 = 2 etc) this is the time the
|
||||||
|
* memory continues to drive the data bus after OE is de-asserted.
|
||||||
|
* Inserted when reading one CS and switching to another CS or read
|
||||||
|
* followed by write on the same CS. Valid values 0 thru 15.
|
||||||
|
* Bits 27-24: WR_HOLD write hold cycles, these are extra cycles inserted after
|
||||||
|
* every write minimum 1. The data out is driven from the time WE is
|
||||||
|
* asserted until CS is asserted. With a hold of 1, the CS stays
|
||||||
|
* active for 1 extra cycle etc. Valid values 0 thru 15.
|
||||||
|
* Bits 23-16: WR_DELTA initial latency for write cycles inserted for the first
|
||||||
|
* write to a page or burst memory
|
||||||
|
* Bits 15-8: RD_DELTA initial latency for read cycles inserted for the first
|
||||||
|
* read to a page or burst memory
|
||||||
|
* Bits 7-4: WR_WAIT number of wait cycles for every write access, 0=1 cycle
|
||||||
|
* so 1 thru 16 cycles.
|
||||||
|
* Bits 3-0: RD_WAIT number of wait cycles for every read access, 0=1 cycle
|
||||||
|
* so 1 thru 16 cycles.
|
||||||
|
*/
|
||||||
|
#define EBI2_XMEM_CS0_SLOW_CFG 0x0008
|
||||||
|
#define EBI2_XMEM_CS1_SLOW_CFG 0x000C
|
||||||
|
#define EBI2_XMEM_CS2_SLOW_CFG 0x0010
|
||||||
|
#define EBI2_XMEM_CS3_SLOW_CFG 0x0014
|
||||||
|
#define EBI2_XMEM_CS4_SLOW_CFG 0x0018
|
||||||
|
#define EBI2_XMEM_CS5_SLOW_CFG 0x001C
|
||||||
|
|
||||||
|
#define EBI2_XMEM_RECOVERY_SHIFT 28
|
||||||
|
#define EBI2_XMEM_WR_HOLD_SHIFT 24
|
||||||
|
#define EBI2_XMEM_WR_DELTA_SHIFT 16
|
||||||
|
#define EBI2_XMEM_RD_DELTA_SHIFT 8
|
||||||
|
#define EBI2_XMEM_WR_WAIT_SHIFT 4
|
||||||
|
#define EBI2_XMEM_RD_WAIT_SHIFT 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FAST CSn CFG
|
||||||
|
* Bits 31-28: ?
|
||||||
|
* Bits 27-24: RD_HOLD: the length in cycles of the first segment of a read
|
||||||
|
* transfer. For a single read trandfer this will be the time
|
||||||
|
* from CS assertion to OE assertion.
|
||||||
|
* Bits 18-24: ?
|
||||||
|
* Bits 17-16: ADV_OE_RECOVERY, the number of cycles elapsed before an OE
|
||||||
|
* assertion, with respect to the cycle where ADV is asserted.
|
||||||
|
* 2 means 2 cycles between ADV and OE. Values 0, 1, 2 or 3.
|
||||||
|
* Bits 5: ADDR_HOLD_ENA, The address is held for an extra cycle to meet
|
||||||
|
* hold time requirements with ADV assertion.
|
||||||
|
*
|
||||||
|
* The manual mentions "write precharge cycles" and "precharge cycles".
|
||||||
|
* We have not been able to figure out which bit fields these correspond to
|
||||||
|
* in the hardware, or what valid values exist. The current hypothesis is that
|
||||||
|
* this is something just used on the FAST chip selects. There is also a "byte
|
||||||
|
* device enable" flag somewhere for 8bit memories.
|
||||||
|
*/
|
||||||
|
#define EBI2_XMEM_CS0_FAST_CFG 0x0028
|
||||||
|
#define EBI2_XMEM_CS1_FAST_CFG 0x002C
|
||||||
|
#define EBI2_XMEM_CS2_FAST_CFG 0x0030
|
||||||
|
#define EBI2_XMEM_CS3_FAST_CFG 0x0034
|
||||||
|
#define EBI2_XMEM_CS4_FAST_CFG 0x0038
|
||||||
|
#define EBI2_XMEM_CS5_FAST_CFG 0x003C
|
||||||
|
|
||||||
|
#define EBI2_XMEM_RD_HOLD_SHIFT 24
|
||||||
|
#define EBI2_XMEM_ADV_OE_RECOVERY_SHIFT 16
|
||||||
|
#define EBI2_XMEM_ADDR_HOLD_ENA_SHIFT 5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct cs_data - struct with info on a chipselect setting
|
||||||
|
* @enable_mask: mask to enable the chipselect in the EBI2 config
|
||||||
|
* @slow_cfg0: offset to XMEMC slow CS config
|
||||||
|
* @fast_cfg1: offset to XMEMC fast CS config
|
||||||
|
*/
|
||||||
|
struct cs_data {
|
||||||
|
u32 enable_mask;
|
||||||
|
u16 slow_cfg;
|
||||||
|
u16 fast_cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct cs_data cs_info[] = {
|
||||||
|
{
|
||||||
|
/* CS0 */
|
||||||
|
.enable_mask = EBI2_CS0_ENABLE_MASK,
|
||||||
|
.slow_cfg = EBI2_XMEM_CS0_SLOW_CFG,
|
||||||
|
.fast_cfg = EBI2_XMEM_CS0_FAST_CFG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* CS1 */
|
||||||
|
.enable_mask = EBI2_CS1_ENABLE_MASK,
|
||||||
|
.slow_cfg = EBI2_XMEM_CS1_SLOW_CFG,
|
||||||
|
.fast_cfg = EBI2_XMEM_CS1_FAST_CFG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* CS2 */
|
||||||
|
.enable_mask = EBI2_CS2_ENABLE_MASK,
|
||||||
|
.slow_cfg = EBI2_XMEM_CS2_SLOW_CFG,
|
||||||
|
.fast_cfg = EBI2_XMEM_CS2_FAST_CFG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* CS3 */
|
||||||
|
.enable_mask = EBI2_CS3_ENABLE_MASK,
|
||||||
|
.slow_cfg = EBI2_XMEM_CS3_SLOW_CFG,
|
||||||
|
.fast_cfg = EBI2_XMEM_CS3_FAST_CFG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* CS4 */
|
||||||
|
.enable_mask = EBI2_CS4_ENABLE_MASK,
|
||||||
|
.slow_cfg = EBI2_XMEM_CS4_SLOW_CFG,
|
||||||
|
.fast_cfg = EBI2_XMEM_CS4_FAST_CFG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* CS5 */
|
||||||
|
.enable_mask = EBI2_CS5_ENABLE_MASK,
|
||||||
|
.slow_cfg = EBI2_XMEM_CS5_SLOW_CFG,
|
||||||
|
.fast_cfg = EBI2_XMEM_CS5_FAST_CFG,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ebi2_xmem_prop - describes an XMEM config property
|
||||||
|
* @prop: the device tree binding name
|
||||||
|
* @max: maximum value for the property
|
||||||
|
* @slowreg: true if this property is in the SLOW CS config register
|
||||||
|
* else it is assumed to be in the FAST config register
|
||||||
|
* @shift: the bit field start in the SLOW or FAST register for this
|
||||||
|
* property
|
||||||
|
*/
|
||||||
|
struct ebi2_xmem_prop {
|
||||||
|
const char *prop;
|
||||||
|
u32 max;
|
||||||
|
bool slowreg;
|
||||||
|
u16 shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct ebi2_xmem_prop xmem_props[] = {
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-recovery-cycles",
|
||||||
|
.max = 15,
|
||||||
|
.slowreg = true,
|
||||||
|
.shift = EBI2_XMEM_RECOVERY_SHIFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-write-hold-cycles",
|
||||||
|
.max = 15,
|
||||||
|
.slowreg = true,
|
||||||
|
.shift = EBI2_XMEM_WR_HOLD_SHIFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-write-delta-cycles",
|
||||||
|
.max = 255,
|
||||||
|
.slowreg = true,
|
||||||
|
.shift = EBI2_XMEM_WR_DELTA_SHIFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-read-delta-cycles",
|
||||||
|
.max = 255,
|
||||||
|
.slowreg = true,
|
||||||
|
.shift = EBI2_XMEM_RD_DELTA_SHIFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-write-wait-cycles",
|
||||||
|
.max = 15,
|
||||||
|
.slowreg = true,
|
||||||
|
.shift = EBI2_XMEM_WR_WAIT_SHIFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-read-wait-cycles",
|
||||||
|
.max = 15,
|
||||||
|
.slowreg = true,
|
||||||
|
.shift = EBI2_XMEM_RD_WAIT_SHIFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-address-hold-enable",
|
||||||
|
.max = 1, /* boolean prop */
|
||||||
|
.slowreg = false,
|
||||||
|
.shift = EBI2_XMEM_ADDR_HOLD_ENA_SHIFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-adv-to-oe-recovery-cycles",
|
||||||
|
.max = 3,
|
||||||
|
.slowreg = false,
|
||||||
|
.shift = EBI2_XMEM_ADV_OE_RECOVERY_SHIFT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.prop = "qcom,xmem-read-hold-cycles",
|
||||||
|
.max = 15,
|
||||||
|
.slowreg = false,
|
||||||
|
.shift = EBI2_XMEM_RD_HOLD_SHIFT,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void qcom_ebi2_setup_chipselect(struct device_node *np,
|
||||||
|
struct device *dev,
|
||||||
|
void __iomem *ebi2_base,
|
||||||
|
void __iomem *ebi2_xmem,
|
||||||
|
u32 csindex)
|
||||||
|
{
|
||||||
|
const struct cs_data *csd;
|
||||||
|
u32 slowcfg, fastcfg;
|
||||||
|
u32 val;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
csd = &cs_info[csindex];
|
||||||
|
val = readl(ebi2_base);
|
||||||
|
val |= csd->enable_mask;
|
||||||
|
writel(val, ebi2_base);
|
||||||
|
dev_dbg(dev, "enabled CS%u\n", csindex);
|
||||||
|
|
||||||
|
/* Next set up the XMEMC */
|
||||||
|
slowcfg = 0;
|
||||||
|
fastcfg = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(xmem_props); i++) {
|
||||||
|
const struct ebi2_xmem_prop *xp = &xmem_props[i];
|
||||||
|
|
||||||
|
/* All are regular u32 values */
|
||||||
|
ret = of_property_read_u32(np, xp->prop, &val);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(dev, "could not read %s for CS%d\n",
|
||||||
|
xp->prop, csindex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First check boolean props */
|
||||||
|
if (xp->max == 1 && val) {
|
||||||
|
if (xp->slowreg)
|
||||||
|
slowcfg |= BIT(xp->shift);
|
||||||
|
else
|
||||||
|
fastcfg |= BIT(xp->shift);
|
||||||
|
dev_dbg(dev, "set %s flag\n", xp->prop);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're dealing with an u32 */
|
||||||
|
if (val > xp->max) {
|
||||||
|
dev_err(dev,
|
||||||
|
"too high value for %s: %u, capped at %u\n",
|
||||||
|
xp->prop, val, xp->max);
|
||||||
|
val = xp->max;
|
||||||
|
}
|
||||||
|
if (xp->slowreg)
|
||||||
|
slowcfg |= (val << xp->shift);
|
||||||
|
else
|
||||||
|
fastcfg |= (val << xp->shift);
|
||||||
|
dev_dbg(dev, "set %s to %u\n", xp->prop, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_info(dev, "CS%u: SLOW CFG 0x%08x, FAST CFG 0x%08x\n",
|
||||||
|
csindex, slowcfg, fastcfg);
|
||||||
|
|
||||||
|
if (slowcfg)
|
||||||
|
writel(slowcfg, ebi2_xmem + csd->slow_cfg);
|
||||||
|
if (fastcfg)
|
||||||
|
writel(fastcfg, ebi2_xmem + csd->fast_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcom_ebi2_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
struct device_node *child;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct resource *res;
|
||||||
|
void __iomem *ebi2_base;
|
||||||
|
void __iomem *ebi2_xmem;
|
||||||
|
struct clk *ebi2xclk;
|
||||||
|
struct clk *ebi2clk;
|
||||||
|
bool have_children = false;
|
||||||
|
u32 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ebi2xclk = devm_clk_get(dev, "ebi2x");
|
||||||
|
if (IS_ERR(ebi2xclk))
|
||||||
|
return PTR_ERR(ebi2xclk);
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(ebi2xclk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not enable EBI2X clk (%d)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ebi2clk = devm_clk_get(dev, "ebi2");
|
||||||
|
if (IS_ERR(ebi2clk)) {
|
||||||
|
ret = PTR_ERR(ebi2clk);
|
||||||
|
goto err_disable_2x_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(ebi2clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "could not enable EBI2 clk\n");
|
||||||
|
goto err_disable_2x_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
ebi2_base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(ebi2_base)) {
|
||||||
|
ret = PTR_ERR(ebi2_base);
|
||||||
|
goto err_disable_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
ebi2_xmem = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(ebi2_xmem)) {
|
||||||
|
ret = PTR_ERR(ebi2_xmem);
|
||||||
|
goto err_disable_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allegedly this turns the power save mode off */
|
||||||
|
writel(0UL, ebi2_xmem + EBI2_XMEM_CFG);
|
||||||
|
|
||||||
|
/* Disable all chipselects */
|
||||||
|
val = readl(ebi2_base);
|
||||||
|
val &= ~EBI2_CSN_MASK;
|
||||||
|
writel(val, ebi2_base);
|
||||||
|
|
||||||
|
/* Walk over the child nodes and see what chipselects we use */
|
||||||
|
for_each_available_child_of_node(np, child) {
|
||||||
|
u32 csindex;
|
||||||
|
|
||||||
|
/* Figure out the chipselect */
|
||||||
|
ret = of_property_read_u32(child, "reg", &csindex);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (csindex > 5) {
|
||||||
|
dev_err(dev,
|
||||||
|
"invalid chipselect %u, we only support 0-5\n",
|
||||||
|
csindex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qcom_ebi2_setup_chipselect(child,
|
||||||
|
dev,
|
||||||
|
ebi2_base,
|
||||||
|
ebi2_xmem,
|
||||||
|
csindex);
|
||||||
|
|
||||||
|
/* We have at least one child */
|
||||||
|
have_children = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_children)
|
||||||
|
return of_platform_default_populate(np, NULL, dev);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_disable_clk:
|
||||||
|
clk_disable_unprepare(ebi2clk);
|
||||||
|
err_disable_2x_clk:
|
||||||
|
clk_disable_unprepare(ebi2xclk);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id qcom_ebi2_of_match[] = {
|
||||||
|
{ .compatible = "qcom,msm8660-ebi2", },
|
||||||
|
{ .compatible = "qcom,apq8060-ebi2", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver qcom_ebi2_driver = {
|
||||||
|
.probe = qcom_ebi2_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "qcom-ebi2",
|
||||||
|
.of_match_table = qcom_ebi2_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(qcom_ebi2_driver);
|
||||||
|
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
|
||||||
|
MODULE_DESCRIPTION("Qualcomm EBI2 driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -15,24 +15,6 @@
|
||||||
#include <linux/pm_clock.h>
|
#include <linux/pm_clock.h>
|
||||||
#include <linux/pm_runtime.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)
|
static int tegra_aconnect_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -44,11 +26,11 @@ static int tegra_aconnect_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = tegra_aconnect_add_clock(&pdev->dev, "ape");
|
ret = of_pm_clk_add_clk(&pdev->dev, "ape");
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_destroy;
|
goto clk_destroy;
|
||||||
|
|
||||||
ret = tegra_aconnect_add_clock(&pdev->dev, "apb2ape");
|
ret = of_pm_clk_add_clk(&pdev->dev, "apb2ape");
|
||||||
if (ret)
|
if (ret)
|
||||||
goto clk_destroy;
|
goto clk_destroy;
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,76 @@ static const struct coreclk_ratio orion_coreclk_ratios[] __initconst = {
|
||||||
{ .id = 0, .name = "ddrclk", }
|
{ .id = 0, .name = "ddrclk", }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Orion 5181
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SAR_MV88F5181_TCLK_FREQ 8
|
||||||
|
#define SAR_MV88F5181_TCLK_FREQ_MASK 0x3
|
||||||
|
|
||||||
|
static u32 __init mv88f5181_get_tclk_freq(void __iomem *sar)
|
||||||
|
{
|
||||||
|
u32 opt = (readl(sar) >> SAR_MV88F5181_TCLK_FREQ) &
|
||||||
|
SAR_MV88F5181_TCLK_FREQ_MASK;
|
||||||
|
if (opt == 0)
|
||||||
|
return 133333333;
|
||||||
|
else if (opt == 1)
|
||||||
|
return 150000000;
|
||||||
|
else if (opt == 2)
|
||||||
|
return 166666667;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SAR_MV88F5181_CPU_FREQ 4
|
||||||
|
#define SAR_MV88F5181_CPU_FREQ_MASK 0xf
|
||||||
|
|
||||||
|
static u32 __init mv88f5181_get_cpu_freq(void __iomem *sar)
|
||||||
|
{
|
||||||
|
u32 opt = (readl(sar) >> SAR_MV88F5181_CPU_FREQ) &
|
||||||
|
SAR_MV88F5181_CPU_FREQ_MASK;
|
||||||
|
if (opt == 0)
|
||||||
|
return 333333333;
|
||||||
|
else if (opt == 1 || opt == 2)
|
||||||
|
return 400000000;
|
||||||
|
else if (opt == 3)
|
||||||
|
return 500000000;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __init mv88f5181_get_clk_ratio(void __iomem *sar, int id,
|
||||||
|
int *mult, int *div)
|
||||||
|
{
|
||||||
|
u32 opt = (readl(sar) >> SAR_MV88F5181_CPU_FREQ) &
|
||||||
|
SAR_MV88F5181_CPU_FREQ_MASK;
|
||||||
|
if (opt == 0 || opt == 1) {
|
||||||
|
*mult = 1;
|
||||||
|
*div = 2;
|
||||||
|
} else if (opt == 2 || opt == 3) {
|
||||||
|
*mult = 1;
|
||||||
|
*div = 3;
|
||||||
|
} else {
|
||||||
|
*mult = 0;
|
||||||
|
*div = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct coreclk_soc_desc mv88f5181_coreclks = {
|
||||||
|
.get_tclk_freq = mv88f5181_get_tclk_freq,
|
||||||
|
.get_cpu_freq = mv88f5181_get_cpu_freq,
|
||||||
|
.get_clk_ratio = mv88f5181_get_clk_ratio,
|
||||||
|
.ratios = orion_coreclk_ratios,
|
||||||
|
.num_ratios = ARRAY_SIZE(orion_coreclk_ratios),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __init mv88f5181_clk_init(struct device_node *np)
|
||||||
|
{
|
||||||
|
return mvebu_coreclk_setup(np, &mv88f5181_coreclks);
|
||||||
|
}
|
||||||
|
|
||||||
|
CLK_OF_DECLARE(mv88f5181_clk, "marvell,mv88f5181-core-clock", mv88f5181_clk_init);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Orion 5182
|
* Orion 5182
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -361,7 +361,7 @@ config CLKSRC_METAG_GENERIC
|
||||||
|
|
||||||
config CLKSRC_EXYNOS_MCT
|
config CLKSRC_EXYNOS_MCT
|
||||||
bool "Exynos multi core timer driver" if COMPILE_TEST
|
bool "Exynos multi core timer driver" if COMPILE_TEST
|
||||||
depends on ARM
|
depends on ARM || ARM64
|
||||||
help
|
help
|
||||||
Support for Multi Core Timer controller on Exynos SoCs.
|
Support for Multi Core Timer controller on Exynos SoCs.
|
||||||
|
|
||||||
|
|
|
@ -223,6 +223,7 @@ static u64 notrace exynos4_read_sched_clock(void)
|
||||||
return exynos4_read_count_32();
|
return exynos4_read_count_32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_ARM)
|
||||||
static struct delay_timer exynos4_delay_timer;
|
static struct delay_timer exynos4_delay_timer;
|
||||||
|
|
||||||
static cycles_t exynos4_read_current_timer(void)
|
static cycles_t exynos4_read_current_timer(void)
|
||||||
|
@ -231,14 +232,17 @@ static cycles_t exynos4_read_current_timer(void)
|
||||||
"cycles_t needs to move to 32-bit for ARM64 usage");
|
"cycles_t needs to move to 32-bit for ARM64 usage");
|
||||||
return exynos4_read_count_32();
|
return exynos4_read_count_32();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __init exynos4_clocksource_init(void)
|
static int __init exynos4_clocksource_init(void)
|
||||||
{
|
{
|
||||||
exynos4_mct_frc_start();
|
exynos4_mct_frc_start();
|
||||||
|
|
||||||
|
#if defined(CONFIG_ARM)
|
||||||
exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;
|
exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;
|
||||||
exynos4_delay_timer.freq = clk_rate;
|
exynos4_delay_timer.freq = clk_rate;
|
||||||
register_current_timer_delay(&exynos4_delay_timer);
|
register_current_timer_delay(&exynos4_delay_timer);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (clocksource_register_hz(&mct_frc, clk_rate))
|
if (clocksource_register_hz(&mct_frc, clk_rate))
|
||||||
panic("%s: can't register clocksource\n", mct_frc.name);
|
panic("%s: can't register clocksource\n", mct_frc.name);
|
||||||
|
|
|
@ -209,5 +209,6 @@ config HAVE_ARM_SMCCC
|
||||||
source "drivers/firmware/broadcom/Kconfig"
|
source "drivers/firmware/broadcom/Kconfig"
|
||||||
source "drivers/firmware/google/Kconfig"
|
source "drivers/firmware/google/Kconfig"
|
||||||
source "drivers/firmware/efi/Kconfig"
|
source "drivers/firmware/efi/Kconfig"
|
||||||
|
source "drivers/firmware/meson/Kconfig"
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -22,6 +22,7 @@ obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o
|
||||||
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
|
CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a
|
||||||
|
|
||||||
obj-y += broadcom/
|
obj-y += broadcom/
|
||||||
|
obj-y += meson/
|
||||||
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
|
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
|
||||||
obj-$(CONFIG_EFI) += efi/
|
obj-$(CONFIG_EFI) += efi/
|
||||||
obj-$(CONFIG_UEFI_CPER) += efi/
|
obj-$(CONFIG_UEFI_CPER) += efi/
|
||||||
|
|
9
drivers/firmware/meson/Kconfig
Normal file
9
drivers/firmware/meson/Kconfig
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#
|
||||||
|
# Amlogic Secure Monitor driver
|
||||||
|
#
|
||||||
|
config MESON_SM
|
||||||
|
bool
|
||||||
|
default ARCH_MESON
|
||||||
|
depends on ARM64_4K_PAGES
|
||||||
|
help
|
||||||
|
Say y here to enable the Amlogic secure monitor driver
|
1
drivers/firmware/meson/Makefile
Normal file
1
drivers/firmware/meson/Makefile
Normal file
|
@ -0,0 +1 @@
|
||||||
|
obj-$(CONFIG_MESON_SM) += meson_sm.o
|
248
drivers/firmware/meson/meson_sm.c
Normal file
248
drivers/firmware/meson/meson_sm.c
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
/*
|
||||||
|
* Amlogic Secure Monitor driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Endless Mobile, Inc.
|
||||||
|
* Author: Carlo Caione <carlo@endlessm.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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "meson-sm: " fmt
|
||||||
|
|
||||||
|
#include <linux/arm-smccc.h>
|
||||||
|
#include <linux/bug.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
|
#include <linux/firmware/meson/meson_sm.h>
|
||||||
|
|
||||||
|
struct meson_sm_cmd {
|
||||||
|
unsigned int index;
|
||||||
|
u32 smc_id;
|
||||||
|
};
|
||||||
|
#define CMD(d, s) { .index = (d), .smc_id = (s), }
|
||||||
|
|
||||||
|
struct meson_sm_chip {
|
||||||
|
unsigned int shmem_size;
|
||||||
|
u32 cmd_shmem_in_base;
|
||||||
|
u32 cmd_shmem_out_base;
|
||||||
|
struct meson_sm_cmd cmd[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct meson_sm_chip gxbb_chip = {
|
||||||
|
.shmem_size = SZ_4K,
|
||||||
|
.cmd_shmem_in_base = 0x82000020,
|
||||||
|
.cmd_shmem_out_base = 0x82000021,
|
||||||
|
.cmd = {
|
||||||
|
CMD(SM_EFUSE_READ, 0x82000030),
|
||||||
|
CMD(SM_EFUSE_WRITE, 0x82000031),
|
||||||
|
CMD(SM_EFUSE_USER_MAX, 0x82000033),
|
||||||
|
{ /* sentinel */ },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct meson_sm_firmware {
|
||||||
|
const struct meson_sm_chip *chip;
|
||||||
|
void __iomem *sm_shmem_in_base;
|
||||||
|
void __iomem *sm_shmem_out_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct meson_sm_firmware fw;
|
||||||
|
|
||||||
|
static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip,
|
||||||
|
unsigned int cmd_index)
|
||||||
|
{
|
||||||
|
const struct meson_sm_cmd *cmd = chip->cmd;
|
||||||
|
|
||||||
|
while (cmd->smc_id && cmd->index != cmd_index)
|
||||||
|
cmd++;
|
||||||
|
|
||||||
|
return cmd->smc_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2,
|
||||||
|
u32 arg3, u32 arg4)
|
||||||
|
{
|
||||||
|
struct arm_smccc_res res;
|
||||||
|
|
||||||
|
arm_smccc_smc(cmd, arg0, arg1, arg2, arg3, arg4, 0, 0, &res);
|
||||||
|
return res.a0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size)
|
||||||
|
{
|
||||||
|
u32 sm_phy_base;
|
||||||
|
|
||||||
|
sm_phy_base = __meson_sm_call(cmd_shmem, 0, 0, 0, 0, 0);
|
||||||
|
if (!sm_phy_base)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ioremap_cache(sm_phy_base, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meson_sm_call - generic SMC32 call to the secure-monitor
|
||||||
|
*
|
||||||
|
* @cmd_index: Index of the SMC32 function ID
|
||||||
|
* @ret: Returned value
|
||||||
|
* @arg0: SMC32 Argument 0
|
||||||
|
* @arg1: SMC32 Argument 1
|
||||||
|
* @arg2: SMC32 Argument 2
|
||||||
|
* @arg3: SMC32 Argument 3
|
||||||
|
* @arg4: SMC32 Argument 4
|
||||||
|
*
|
||||||
|
* Return: 0 on success, a negative value on error
|
||||||
|
*/
|
||||||
|
int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0,
|
||||||
|
u32 arg1, u32 arg2, u32 arg3, u32 arg4)
|
||||||
|
{
|
||||||
|
u32 cmd, lret;
|
||||||
|
|
||||||
|
if (!fw.chip)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
cmd = meson_sm_get_cmd(fw.chip, cmd_index);
|
||||||
|
if (!cmd)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
lret = __meson_sm_call(cmd, arg0, arg1, arg2, arg3, arg4);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
*ret = lret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(meson_sm_call);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meson_sm_call_read - retrieve data from secure-monitor
|
||||||
|
*
|
||||||
|
* @buffer: Buffer to store the retrieved data
|
||||||
|
* @cmd_index: Index of the SMC32 function ID
|
||||||
|
* @arg0: SMC32 Argument 0
|
||||||
|
* @arg1: SMC32 Argument 1
|
||||||
|
* @arg2: SMC32 Argument 2
|
||||||
|
* @arg3: SMC32 Argument 3
|
||||||
|
* @arg4: SMC32 Argument 4
|
||||||
|
*
|
||||||
|
* Return: size of read data on success, a negative value on error
|
||||||
|
*/
|
||||||
|
int meson_sm_call_read(void *buffer, unsigned int cmd_index, u32 arg0,
|
||||||
|
u32 arg1, u32 arg2, u32 arg3, u32 arg4)
|
||||||
|
{
|
||||||
|
u32 size;
|
||||||
|
|
||||||
|
if (!fw.chip)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
if (!fw.chip->cmd_shmem_out_base)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!size || size > fw.chip->shmem_size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (buffer)
|
||||||
|
memcpy(buffer, fw.sm_shmem_out_base, size);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(meson_sm_call_read);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meson_sm_call_write - send data to secure-monitor
|
||||||
|
*
|
||||||
|
* @buffer: Buffer containing data to send
|
||||||
|
* @size: Size of the data to send
|
||||||
|
* @cmd_index: Index of the SMC32 function ID
|
||||||
|
* @arg0: SMC32 Argument 0
|
||||||
|
* @arg1: SMC32 Argument 1
|
||||||
|
* @arg2: SMC32 Argument 2
|
||||||
|
* @arg3: SMC32 Argument 3
|
||||||
|
* @arg4: SMC32 Argument 4
|
||||||
|
*
|
||||||
|
* Return: size of sent data on success, a negative value on error
|
||||||
|
*/
|
||||||
|
int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index,
|
||||||
|
u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
|
||||||
|
{
|
||||||
|
u32 written;
|
||||||
|
|
||||||
|
if (!fw.chip)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
if (size > fw.chip->shmem_size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!fw.chip->cmd_shmem_in_base)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
memcpy(fw.sm_shmem_in_base, buffer, size);
|
||||||
|
|
||||||
|
if (meson_sm_call(cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!written)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(meson_sm_call_write);
|
||||||
|
|
||||||
|
static const struct of_device_id meson_sm_ids[] = {
|
||||||
|
{ .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
|
||||||
|
int __init meson_sm_init(void)
|
||||||
|
{
|
||||||
|
const struct meson_sm_chip *chip;
|
||||||
|
const struct of_device_id *matched_np;
|
||||||
|
struct device_node *np;
|
||||||
|
|
||||||
|
np = of_find_matching_node_and_match(NULL, meson_sm_ids, &matched_np);
|
||||||
|
if (!np)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
chip = matched_np->data;
|
||||||
|
if (!chip) {
|
||||||
|
pr_err("unable to setup secure-monitor data\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip->cmd_shmem_in_base) {
|
||||||
|
fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base,
|
||||||
|
chip->shmem_size);
|
||||||
|
if (WARN_ON(!fw.sm_shmem_in_base))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip->cmd_shmem_out_base) {
|
||||||
|
fw.sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base,
|
||||||
|
chip->shmem_size);
|
||||||
|
if (WARN_ON(!fw.sm_shmem_out_base))
|
||||||
|
goto out_in_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
fw.chip = chip;
|
||||||
|
pr_info("secure-monitor enabled\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_in_base:
|
||||||
|
iounmap(fw.sm_shmem_in_base);
|
||||||
|
out:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
device_initcall(meson_sm_init);
|
|
@ -1,4 +1,7 @@
|
||||||
/* Copyright (c) 2010,2015, The Linux Foundation. All rights reserved.
|
/*
|
||||||
|
* Qualcomm SCM driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010,2015, The Linux Foundation. All rights reserved.
|
||||||
* Copyright (C) 2015 Linaro Ltd.
|
* Copyright (C) 2015 Linaro Ltd.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -12,7 +15,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/module.h>
|
#include <linux/init.h>
|
||||||
#include <linux/cpumask.h>
|
#include <linux/cpumask.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
|
@ -376,8 +379,6 @@ static const struct of_device_id qcom_scm_dt_match[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(of, qcom_scm_dt_match);
|
|
||||||
|
|
||||||
static struct platform_driver qcom_scm_driver = {
|
static struct platform_driver qcom_scm_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "qcom_scm",
|
.name = "qcom_scm",
|
||||||
|
@ -414,14 +415,4 @@ static int __init qcom_scm_init(void)
|
||||||
|
|
||||||
return platform_driver_register(&qcom_scm_driver);
|
return platform_driver_register(&qcom_scm_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
subsys_initcall(qcom_scm_init);
|
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");
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#define DRIVER_NAME "meson-ir"
|
#define DRIVER_NAME "meson-ir"
|
||||||
|
|
||||||
|
/* valid on all Meson platforms */
|
||||||
#define IR_DEC_LDR_ACTIVE 0x00
|
#define IR_DEC_LDR_ACTIVE 0x00
|
||||||
#define IR_DEC_LDR_IDLE 0x04
|
#define IR_DEC_LDR_IDLE 0x04
|
||||||
#define IR_DEC_LDR_REPEAT 0x08
|
#define IR_DEC_LDR_REPEAT 0x08
|
||||||
|
@ -32,12 +33,21 @@
|
||||||
#define IR_DEC_FRAME 0x14
|
#define IR_DEC_FRAME 0x14
|
||||||
#define IR_DEC_STATUS 0x18
|
#define IR_DEC_STATUS 0x18
|
||||||
#define IR_DEC_REG1 0x1c
|
#define IR_DEC_REG1 0x1c
|
||||||
|
/* only available on Meson 8b and newer */
|
||||||
|
#define IR_DEC_REG2 0x20
|
||||||
|
|
||||||
#define REG0_RATE_MASK (BIT(11) - 1)
|
#define REG0_RATE_MASK (BIT(11) - 1)
|
||||||
|
|
||||||
#define REG1_MODE_MASK (BIT(7) | BIT(8))
|
#define DECODE_MODE_NEC 0x0
|
||||||
#define REG1_MODE_NEC (0 << 7)
|
#define DECODE_MODE_RAW 0x2
|
||||||
#define REG1_MODE_GENERAL (2 << 7)
|
|
||||||
|
/* Meson 6b uses REG1 to configure the mode */
|
||||||
|
#define REG1_MODE_MASK GENMASK(8, 7)
|
||||||
|
#define REG1_MODE_SHIFT 7
|
||||||
|
|
||||||
|
/* Meson 8b / GXBB use REG2 to configure the mode */
|
||||||
|
#define REG2_MODE_MASK GENMASK(3, 0)
|
||||||
|
#define REG2_MODE_SHIFT 0
|
||||||
|
|
||||||
#define REG1_TIME_IV_SHIFT 16
|
#define REG1_TIME_IV_SHIFT 16
|
||||||
#define REG1_TIME_IV_MASK ((BIT(13) - 1) << REG1_TIME_IV_SHIFT)
|
#define REG1_TIME_IV_MASK ((BIT(13) - 1) << REG1_TIME_IV_SHIFT)
|
||||||
|
@ -158,8 +168,15 @@ static int meson_ir_probe(struct platform_device *pdev)
|
||||||
/* Reset the decoder */
|
/* Reset the decoder */
|
||||||
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET);
|
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET);
|
||||||
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0);
|
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0);
|
||||||
/* Set general operation mode */
|
|
||||||
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, REG1_MODE_GENERAL);
|
/* Set general operation mode (= raw/software decoding) */
|
||||||
|
if (of_device_is_compatible(node, "amlogic,meson6-ir"))
|
||||||
|
meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK,
|
||||||
|
DECODE_MODE_RAW << REG1_MODE_SHIFT);
|
||||||
|
else
|
||||||
|
meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK,
|
||||||
|
DECODE_MODE_RAW << REG2_MODE_SHIFT);
|
||||||
|
|
||||||
/* Set rate */
|
/* Set rate */
|
||||||
meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1);
|
meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1);
|
||||||
/* IRQ on rising and falling edges */
|
/* IRQ on rising and falling edges */
|
||||||
|
@ -197,6 +214,8 @@ static int meson_ir_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
static const struct of_device_id meson_ir_match[] = {
|
static const struct of_device_id meson_ir_match[] = {
|
||||||
{ .compatible = "amlogic,meson6-ir" },
|
{ .compatible = "amlogic,meson6-ir" },
|
||||||
|
{ .compatible = "amlogic,meson8b-ir" },
|
||||||
|
{ .compatible = "amlogic,meson-gxbb-ir" },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -410,10 +410,7 @@ static int at91sam9_ebi_init(struct at91_ebi *ebi)
|
||||||
|
|
||||||
field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC);
|
field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC);
|
||||||
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||||
if (IS_ERR(fields->mode))
|
return PTR_ERR_OR_ZERO(fields->mode);
|
||||||
return PTR_ERR(fields->mode);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sama5d3_ebi_init(struct at91_ebi *ebi)
|
static int sama5d3_ebi_init(struct at91_ebi *ebi)
|
||||||
|
@ -441,10 +438,7 @@ static int sama5d3_ebi_init(struct at91_ebi *ebi)
|
||||||
|
|
||||||
field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC);
|
field.reg = SAMA5_SMC_MODE(SAMA5_SMC_GENERIC);
|
||||||
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
fields->mode = devm_regmap_field_alloc(ebi->dev, ebi->smc, field);
|
||||||
if (IS_ERR(fields->mode))
|
return PTR_ERR_OR_ZERO(fields->mode);
|
||||||
return PTR_ERR(fields->mode);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
|
static int at91_ebi_dev_setup(struct at91_ebi *ebi, struct device_node *np,
|
||||||
|
|
|
@ -53,12 +53,10 @@ static const struct of_device_id atmel_ramc_of_match[] = {
|
||||||
|
|
||||||
static int atmel_ramc_probe(struct platform_device *pdev)
|
static int atmel_ramc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
const struct of_device_id *match;
|
|
||||||
const struct at91_ramc_caps *caps;
|
const struct at91_ramc_caps *caps;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
match = of_match_device(atmel_ramc_of_match, &pdev->dev);
|
caps = of_device_get_match_data(&pdev->dev);
|
||||||
caps = match->data;
|
|
||||||
|
|
||||||
if (caps->has_ddrck) {
|
if (caps->has_ddrck) {
|
||||||
clk = devm_clk_get(&pdev->dev, "ddrck");
|
clk = devm_clk_get(&pdev->dev, "ddrck");
|
||||||
|
|
|
@ -350,7 +350,7 @@ static unsigned int gpmc_ps_to_ticks(unsigned int time_ps)
|
||||||
return (time_ps + tick_ps - 1) / tick_ps;
|
return (time_ps + tick_ps - 1) / tick_ps;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int gpmc_clk_ticks_to_ns(unsigned ticks, int cs,
|
static unsigned int gpmc_clk_ticks_to_ns(unsigned int ticks, int cs,
|
||||||
enum gpmc_clk_domain cd)
|
enum gpmc_clk_domain cd)
|
||||||
{
|
{
|
||||||
return ticks * gpmc_get_clk_period(cs, cd) / 1000;
|
return ticks * gpmc_get_clk_period(cs, cd) / 1000;
|
||||||
|
@ -2143,9 +2143,7 @@ err_child_fail:
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
|
|
||||||
err_cs:
|
err_cs:
|
||||||
if (waitpin_desc)
|
|
||||||
gpiochip_free_own_desc(waitpin_desc);
|
gpiochip_free_own_desc(waitpin_desc);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
gpmc_cs_free(cs);
|
gpmc_cs_free(cs);
|
||||||
|
|
||||||
|
@ -2265,7 +2263,7 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
|
||||||
gpmc->gpio_chip.get = gpmc_gpio_get;
|
gpmc->gpio_chip.get = gpmc_gpio_get;
|
||||||
gpmc->gpio_chip.base = -1;
|
gpmc->gpio_chip.base = -1;
|
||||||
|
|
||||||
ret = gpiochip_add(&gpmc->gpio_chip);
|
ret = devm_gpiochip_add_data(gpmc->dev, &gpmc->gpio_chip, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
|
dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2274,11 +2272,6 @@ static int gpmc_gpio_init(struct gpmc_device *gpmc)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpmc_gpio_exit(struct gpmc_device *gpmc)
|
|
||||||
{
|
|
||||||
gpiochip_remove(&gpmc->gpio_chip);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gpmc_probe(struct platform_device *pdev)
|
static int gpmc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -2365,15 +2358,13 @@ static int gpmc_probe(struct platform_device *pdev)
|
||||||
rc = gpmc_setup_irq(gpmc);
|
rc = gpmc_setup_irq(gpmc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_err(gpmc->dev, "gpmc_setup_irq failed\n");
|
dev_err(gpmc->dev, "gpmc_setup_irq failed\n");
|
||||||
goto setup_irq_failed;
|
goto gpio_init_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpmc_probe_dt_children(pdev);
|
gpmc_probe_dt_children(pdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
setup_irq_failed:
|
|
||||||
gpmc_gpio_exit(gpmc);
|
|
||||||
gpio_init_failed:
|
gpio_init_failed:
|
||||||
gpmc_mem_exit();
|
gpmc_mem_exit();
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
|
@ -2387,7 +2378,6 @@ static int gpmc_remove(struct platform_device *pdev)
|
||||||
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
|
struct gpmc_device *gpmc = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
gpmc_free_irq(gpmc);
|
gpmc_free_irq(gpmc);
|
||||||
gpmc_gpio_exit(gpmc);
|
|
||||||
gpmc_mem_exit();
|
gpmc_mem_exit();
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
pm_runtime_put_sync(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
|
@ -101,4 +101,14 @@ config NVMEM_VF610_OCOTP
|
||||||
This driver can also be build as a module. If so, the module will
|
This driver can also be build as a module. If so, the module will
|
||||||
be called nvmem-vf610-ocotp.
|
be called nvmem-vf610-ocotp.
|
||||||
|
|
||||||
|
config MESON_EFUSE
|
||||||
|
tristate "Amlogic eFuse Support"
|
||||||
|
depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM
|
||||||
|
help
|
||||||
|
This is a driver to retrieve specific values from the eFuse found on
|
||||||
|
the Amlogic Meson SoCs.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called nvmem_meson_efuse.
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -22,3 +22,5 @@ obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
|
||||||
nvmem_sunxi_sid-y := sunxi_sid.o
|
nvmem_sunxi_sid-y := sunxi_sid.o
|
||||||
obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
|
obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
|
||||||
nvmem-vf610-ocotp-y := vf610-ocotp.o
|
nvmem-vf610-ocotp-y := vf610-ocotp.o
|
||||||
|
obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o
|
||||||
|
nvmem_meson_efuse-y := meson-efuse.o
|
||||||
|
|
93
drivers/nvmem/meson-efuse.c
Normal file
93
drivers/nvmem/meson-efuse.c
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Amlogic eFuse Driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 Endless Computers, Inc.
|
||||||
|
* Author: Carlo Caione <carlo@endlessm.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/nvmem-provider.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#include <linux/firmware/meson/meson_sm.h>
|
||||||
|
|
||||||
|
static int meson_efuse_read(void *context, unsigned int offset,
|
||||||
|
void *val, size_t bytes)
|
||||||
|
{
|
||||||
|
u8 *buf = val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = meson_sm_call_read(buf, SM_EFUSE_READ, offset,
|
||||||
|
bytes, 0, 0, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nvmem_config econfig = {
|
||||||
|
.name = "meson-efuse",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.stride = 1,
|
||||||
|
.word_size = 1,
|
||||||
|
.read_only = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id meson_efuse_match[] = {
|
||||||
|
{ .compatible = "amlogic,meson-gxbb-efuse", },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, meson_efuse_match);
|
||||||
|
|
||||||
|
static int meson_efuse_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct nvmem_device *nvmem;
|
||||||
|
unsigned int size;
|
||||||
|
|
||||||
|
if (meson_sm_call(SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
econfig.dev = &pdev->dev;
|
||||||
|
econfig.reg_read = meson_efuse_read;
|
||||||
|
econfig.size = size;
|
||||||
|
|
||||||
|
nvmem = nvmem_register(&econfig);
|
||||||
|
if (IS_ERR(nvmem))
|
||||||
|
return PTR_ERR(nvmem);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, nvmem);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int meson_efuse_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct nvmem_device *nvmem = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
return nvmem_unregister(nvmem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver meson_efuse_driver = {
|
||||||
|
.probe = meson_efuse_probe,
|
||||||
|
.remove = meson_efuse_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "meson-efuse",
|
||||||
|
.of_match_table = meson_efuse_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(meson_efuse_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>");
|
||||||
|
MODULE_DESCRIPTION("Amlogic Meson NVMEM driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -12,4 +12,11 @@ config ARM_PMU
|
||||||
Say y if you want to use CPU performance monitors on ARM-based
|
Say y if you want to use CPU performance monitors on ARM-based
|
||||||
systems.
|
systems.
|
||||||
|
|
||||||
|
config XGENE_PMU
|
||||||
|
depends on PERF_EVENTS && ARCH_XGENE
|
||||||
|
bool "APM X-Gene SoC PMU"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Say y if you want to use APM X-Gene SoC performance monitors.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
obj-$(CONFIG_ARM_PMU) += arm_pmu.o
|
obj-$(CONFIG_ARM_PMU) += arm_pmu.o
|
||||||
|
obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
|
||||||
|
|
1398
drivers/perf/xgene_pmu.c
Normal file
1398
drivers/perf/xgene_pmu.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -64,11 +64,11 @@ static int orion_mpp_ctrl_set(unsigned pid, unsigned long config)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define V(f5181l, f5182, f5281) \
|
#define V(f5181, f5182, f5281) \
|
||||||
((f5181l << 0) | (f5182 << 1) | (f5281 << 2))
|
((f5181 << 0) | (f5182 << 1) | (f5281 << 2))
|
||||||
|
|
||||||
enum orion_variant {
|
enum orion_variant {
|
||||||
V_5181L = V(1, 0, 0),
|
V_5181 = V(1, 0, 0),
|
||||||
V_5182 = V(0, 1, 0),
|
V_5182 = V(0, 1, 0),
|
||||||
V_5281 = V(0, 0, 1),
|
V_5281 = V(0, 0, 1),
|
||||||
V_ALL = V(1, 1, 1),
|
V_ALL = V(1, 1, 1),
|
||||||
|
@ -103,13 +103,13 @@ static struct mvebu_mpp_mode orion_mpp_modes[] = {
|
||||||
MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
|
MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
|
||||||
MPP_VAR_FUNCTION(0x2, "pci", "req5", V_ALL),
|
MPP_VAR_FUNCTION(0x2, "pci", "req5", V_ALL),
|
||||||
MPP_VAR_FUNCTION(0x4, "nand", "re0", V_5182 | V_5281),
|
MPP_VAR_FUNCTION(0x4, "nand", "re0", V_5182 | V_5281),
|
||||||
MPP_VAR_FUNCTION(0x5, "pci-1", "clk", V_5181L),
|
MPP_VAR_FUNCTION(0x5, "pci-1", "clk", V_5181),
|
||||||
MPP_VAR_FUNCTION(0x5, "sata0", "act", V_5182)),
|
MPP_VAR_FUNCTION(0x5, "sata0", "act", V_5182)),
|
||||||
MPP_MODE(7,
|
MPP_MODE(7,
|
||||||
MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
|
MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
|
||||||
MPP_VAR_FUNCTION(0x2, "pci", "gnt5", V_ALL),
|
MPP_VAR_FUNCTION(0x2, "pci", "gnt5", V_ALL),
|
||||||
MPP_VAR_FUNCTION(0x4, "nand", "we0", V_5182 | V_5281),
|
MPP_VAR_FUNCTION(0x4, "nand", "we0", V_5182 | V_5281),
|
||||||
MPP_VAR_FUNCTION(0x5, "pci-1", "clk", V_5181L),
|
MPP_VAR_FUNCTION(0x5, "pci-1", "clk", V_5181),
|
||||||
MPP_VAR_FUNCTION(0x5, "sata1", "act", V_5182)),
|
MPP_VAR_FUNCTION(0x5, "sata1", "act", V_5182)),
|
||||||
MPP_MODE(8,
|
MPP_MODE(8,
|
||||||
MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
|
MPP_VAR_FUNCTION(0x0, "gpio", NULL, V_ALL),
|
||||||
|
@ -165,7 +165,7 @@ static struct mvebu_mpp_ctrl orion_mpp_controls[] = {
|
||||||
MPP_FUNC_CTRL(0, 19, NULL, orion_mpp_ctrl),
|
MPP_FUNC_CTRL(0, 19, NULL, orion_mpp_ctrl),
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pinctrl_gpio_range mv88f5181l_gpio_ranges[] = {
|
static struct pinctrl_gpio_range mv88f5181_gpio_ranges[] = {
|
||||||
MPP_GPIO_RANGE(0, 0, 0, 16),
|
MPP_GPIO_RANGE(0, 0, 0, 16),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,14 +177,14 @@ static struct pinctrl_gpio_range mv88f5281_gpio_ranges[] = {
|
||||||
MPP_GPIO_RANGE(0, 0, 0, 16),
|
MPP_GPIO_RANGE(0, 0, 0, 16),
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct mvebu_pinctrl_soc_info mv88f5181l_info = {
|
static struct mvebu_pinctrl_soc_info mv88f5181_info = {
|
||||||
.variant = V_5181L,
|
.variant = V_5181,
|
||||||
.controls = orion_mpp_controls,
|
.controls = orion_mpp_controls,
|
||||||
.ncontrols = ARRAY_SIZE(orion_mpp_controls),
|
.ncontrols = ARRAY_SIZE(orion_mpp_controls),
|
||||||
.modes = orion_mpp_modes,
|
.modes = orion_mpp_modes,
|
||||||
.nmodes = ARRAY_SIZE(orion_mpp_modes),
|
.nmodes = ARRAY_SIZE(orion_mpp_modes),
|
||||||
.gpioranges = mv88f5181l_gpio_ranges,
|
.gpioranges = mv88f5181_gpio_ranges,
|
||||||
.ngpioranges = ARRAY_SIZE(mv88f5181l_gpio_ranges),
|
.ngpioranges = ARRAY_SIZE(mv88f5181_gpio_ranges),
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct mvebu_pinctrl_soc_info mv88f5182_info = {
|
static struct mvebu_pinctrl_soc_info mv88f5182_info = {
|
||||||
|
@ -212,7 +212,8 @@ static struct mvebu_pinctrl_soc_info mv88f5281_info = {
|
||||||
* muxing, they are identical.
|
* muxing, they are identical.
|
||||||
*/
|
*/
|
||||||
static const struct of_device_id orion_pinctrl_of_match[] = {
|
static const struct of_device_id orion_pinctrl_of_match[] = {
|
||||||
{ .compatible = "marvell,88f5181l-pinctrl", .data = &mv88f5181l_info },
|
{ .compatible = "marvell,88f5181-pinctrl", .data = &mv88f5181_info },
|
||||||
|
{ .compatible = "marvell,88f5181l-pinctrl", .data = &mv88f5181_info },
|
||||||
{ .compatible = "marvell,88f5182-pinctrl", .data = &mv88f5182_info },
|
{ .compatible = "marvell,88f5182-pinctrl", .data = &mv88f5182_info },
|
||||||
{ .compatible = "marvell,88f5281-pinctrl", .data = &mv88f5281_info },
|
{ .compatible = "marvell,88f5281-pinctrl", .data = &mv88f5281_info },
|
||||||
{ }
|
{ }
|
||||||
|
|
|
@ -14,9 +14,58 @@ menuconfig RESET_CONTROLLER
|
||||||
|
|
||||||
if RESET_CONTROLLER
|
if RESET_CONTROLLER
|
||||||
|
|
||||||
|
config RESET_ATH79
|
||||||
|
bool "AR71xx Reset Driver" if COMPILE_TEST
|
||||||
|
default ATH79
|
||||||
|
help
|
||||||
|
This enables the ATH79 reset controller driver that supports the
|
||||||
|
AR71xx SoC reset controller.
|
||||||
|
|
||||||
|
config RESET_BERLIN
|
||||||
|
bool "Berlin Reset Driver" if COMPILE_TEST
|
||||||
|
default ARCH_BERLIN
|
||||||
|
help
|
||||||
|
This enables the reset controller driver for Marvell Berlin SoCs.
|
||||||
|
|
||||||
|
config RESET_LPC18XX
|
||||||
|
bool "LPC18xx/43xx Reset Driver" if COMPILE_TEST
|
||||||
|
default ARCH_LPC18XX
|
||||||
|
help
|
||||||
|
This enables the reset controller driver for NXP LPC18xx/43xx SoCs.
|
||||||
|
|
||||||
|
config RESET_MESON
|
||||||
|
bool "Meson Reset Driver" if COMPILE_TEST
|
||||||
|
default ARCH_MESON
|
||||||
|
help
|
||||||
|
This enables the reset driver for Amlogic Meson SoCs.
|
||||||
|
|
||||||
config RESET_OXNAS
|
config RESET_OXNAS
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config RESET_PISTACHIO
|
||||||
|
bool "Pistachio Reset Driver" if COMPILE_TEST
|
||||||
|
default MACH_PISTACHIO
|
||||||
|
help
|
||||||
|
This enables the reset driver for ImgTec Pistachio SoCs.
|
||||||
|
|
||||||
|
config RESET_SOCFPGA
|
||||||
|
bool "SoCFPGA Reset Driver" if COMPILE_TEST
|
||||||
|
default ARCH_SOCFPGA
|
||||||
|
help
|
||||||
|
This enables the reset controller driver for Altera SoCFPGAs.
|
||||||
|
|
||||||
|
config RESET_STM32
|
||||||
|
bool "STM32 Reset Driver" if COMPILE_TEST
|
||||||
|
default ARCH_STM32
|
||||||
|
help
|
||||||
|
This enables the RCC reset controller driver for STM32 MCUs.
|
||||||
|
|
||||||
|
config RESET_SUNXI
|
||||||
|
bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI
|
||||||
|
default ARCH_SUNXI
|
||||||
|
help
|
||||||
|
This enables the reset driver for Allwinner SoCs.
|
||||||
|
|
||||||
config TI_SYSCON_RESET
|
config TI_SYSCON_RESET
|
||||||
tristate "TI SYSCON Reset Driver"
|
tristate "TI SYSCON Reset Driver"
|
||||||
depends on HAS_IOMEM
|
depends on HAS_IOMEM
|
||||||
|
@ -27,6 +76,22 @@ config TI_SYSCON_RESET
|
||||||
you wish to use the reset framework for such memory-mapped devices,
|
you wish to use the reset framework for such memory-mapped devices,
|
||||||
say Y here. Otherwise, say N.
|
say Y here. Otherwise, say N.
|
||||||
|
|
||||||
|
config RESET_UNIPHIER
|
||||||
|
tristate "Reset controller driver for UniPhier SoCs"
|
||||||
|
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||||
|
depends on OF && MFD_SYSCON
|
||||||
|
default ARCH_UNIPHIER
|
||||||
|
help
|
||||||
|
Support for reset controllers on UniPhier SoCs.
|
||||||
|
Say Y if you want to control reset signals provided by System Control
|
||||||
|
block, Media I/O block, Peripheral Block.
|
||||||
|
|
||||||
|
config RESET_ZYNQ
|
||||||
|
bool "ZYNQ Reset Driver" if COMPILE_TEST
|
||||||
|
default ARCH_ZYNQ
|
||||||
|
help
|
||||||
|
This enables the reset controller driver for Xilinx Zynq SoCs.
|
||||||
|
|
||||||
source "drivers/reset/sti/Kconfig"
|
source "drivers/reset/sti/Kconfig"
|
||||||
source "drivers/reset/hisilicon/Kconfig"
|
source "drivers/reset/hisilicon/Kconfig"
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
obj-y += core.o
|
obj-y += core.o
|
||||||
obj-$(CONFIG_ARCH_LPC18XX) += reset-lpc18xx.o
|
obj-y += hisilicon/
|
||||||
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_STI) += sti/
|
||||||
obj-$(CONFIG_ARCH_HISI) += hisilicon/
|
obj-$(CONFIG_RESET_ATH79) += reset-ath79.o
|
||||||
obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o
|
obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
|
||||||
obj-$(CONFIG_ATH79) += reset-ath79.o
|
obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
|
||||||
|
obj-$(CONFIG_RESET_MESON) += reset-meson.o
|
||||||
obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
|
obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
|
||||||
|
obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
|
||||||
|
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
|
||||||
|
obj-$(CONFIG_RESET_STM32) += reset-stm32.o
|
||||||
|
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
|
||||||
obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o
|
obj-$(CONFIG_TI_SYSCON_RESET) += reset-ti-syscon.o
|
||||||
|
obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
|
||||||
|
obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
|
||||||
|
|
|
@ -138,7 +138,8 @@ EXPORT_SYMBOL_GPL(devm_reset_controller_register);
|
||||||
*/
|
*/
|
||||||
int reset_control_reset(struct reset_control *rstc)
|
int reset_control_reset(struct reset_control *rstc)
|
||||||
{
|
{
|
||||||
if (WARN_ON(rstc->shared))
|
if (WARN_ON(IS_ERR_OR_NULL(rstc)) ||
|
||||||
|
WARN_ON(rstc->shared))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (rstc->rcdev->ops->reset)
|
if (rstc->rcdev->ops->reset)
|
||||||
|
@ -161,6 +162,9 @@ EXPORT_SYMBOL_GPL(reset_control_reset);
|
||||||
*/
|
*/
|
||||||
int reset_control_assert(struct reset_control *rstc)
|
int reset_control_assert(struct reset_control *rstc)
|
||||||
{
|
{
|
||||||
|
if (WARN_ON(IS_ERR_OR_NULL(rstc)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!rstc->rcdev->ops->assert)
|
if (!rstc->rcdev->ops->assert)
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
@ -184,6 +188,9 @@ EXPORT_SYMBOL_GPL(reset_control_assert);
|
||||||
*/
|
*/
|
||||||
int reset_control_deassert(struct reset_control *rstc)
|
int reset_control_deassert(struct reset_control *rstc)
|
||||||
{
|
{
|
||||||
|
if (WARN_ON(IS_ERR_OR_NULL(rstc)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!rstc->rcdev->ops->deassert)
|
if (!rstc->rcdev->ops->deassert)
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
@ -204,6 +211,9 @@ EXPORT_SYMBOL_GPL(reset_control_deassert);
|
||||||
*/
|
*/
|
||||||
int reset_control_status(struct reset_control *rstc)
|
int reset_control_status(struct reset_control *rstc)
|
||||||
{
|
{
|
||||||
|
if (WARN_ON(IS_ERR_OR_NULL(rstc)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (rstc->rcdev->ops->status)
|
if (rstc->rcdev->ops->status)
|
||||||
return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
|
return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
config COMMON_RESET_HI6220
|
config COMMON_RESET_HI6220
|
||||||
tristate "Hi6220 Reset Driver"
|
tristate "Hi6220 Reset Driver"
|
||||||
depends on (ARCH_HISI && RESET_CONTROLLER)
|
depends on ARCH_HISI || COMPILE_TEST
|
||||||
|
default ARCH_HISI
|
||||||
help
|
help
|
||||||
Build the Hisilicon Hi6220 reset driver.
|
Build the Hisilicon Hi6220 reset driver.
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/io.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/reset-controller.h>
|
#include <linux/reset-controller.h>
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
struct socfpga_reset_data {
|
struct socfpga_reset_data {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
void __iomem *membase;
|
void __iomem *membase;
|
||||||
u32 modrst_offset;
|
|
||||||
struct reset_controller_dev rcdev;
|
struct reset_controller_dev rcdev;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,9 +44,8 @@ static int socfpga_reset_assert(struct reset_controller_dev *rcdev,
|
||||||
|
|
||||||
spin_lock_irqsave(&data->lock, flags);
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS));
|
reg = readl(data->membase + (bank * NR_BANKS));
|
||||||
writel(reg | BIT(offset), data->membase + data->modrst_offset +
|
writel(reg | BIT(offset), data->membase + (bank * NR_BANKS));
|
||||||
(bank * NR_BANKS));
|
|
||||||
spin_unlock_irqrestore(&data->lock, flags);
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -67,9 +65,8 @@ static int socfpga_reset_deassert(struct reset_controller_dev *rcdev,
|
||||||
|
|
||||||
spin_lock_irqsave(&data->lock, flags);
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS));
|
reg = readl(data->membase + (bank * NR_BANKS));
|
||||||
writel(reg & ~BIT(offset), data->membase + data->modrst_offset +
|
writel(reg & ~BIT(offset), data->membase + (bank * NR_BANKS));
|
||||||
(bank * NR_BANKS));
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&data->lock, flags);
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
|
||||||
|
@ -85,7 +82,7 @@ static int socfpga_reset_status(struct reset_controller_dev *rcdev,
|
||||||
int offset = id % BITS_PER_LONG;
|
int offset = id % BITS_PER_LONG;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
reg = readl(data->membase + data->modrst_offset + (bank * NR_BANKS));
|
reg = readl(data->membase + (bank * NR_BANKS));
|
||||||
|
|
||||||
return !(reg & BIT(offset));
|
return !(reg & BIT(offset));
|
||||||
}
|
}
|
||||||
|
@ -102,6 +99,7 @@ static int socfpga_reset_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
|
u32 modrst_offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The binding was mainlined without the required property.
|
* The binding was mainlined without the required property.
|
||||||
|
@ -122,10 +120,11 @@ static int socfpga_reset_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(data->membase))
|
if (IS_ERR(data->membase))
|
||||||
return PTR_ERR(data->membase);
|
return PTR_ERR(data->membase);
|
||||||
|
|
||||||
if (of_property_read_u32(np, "altr,modrst-offset", &data->modrst_offset)) {
|
if (of_property_read_u32(np, "altr,modrst-offset", &modrst_offset)) {
|
||||||
dev_warn(dev, "missing altr,modrst-offset property, assuming 0x10!\n");
|
dev_warn(dev, "missing altr,modrst-offset property, assuming 0x10!\n");
|
||||||
data->modrst_offset = 0x10;
|
modrst_offset = 0x10;
|
||||||
}
|
}
|
||||||
|
data->membase += modrst_offset;
|
||||||
|
|
||||||
spin_lock_init(&data->lock);
|
spin_lock_init(&data->lock);
|
||||||
|
|
||||||
|
|
108
drivers/reset/reset-stm32.c
Normal file
108
drivers/reset/reset-stm32.c
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) Maxime Coquelin 2015
|
||||||
|
* Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
|
||||||
|
* License terms: GNU General Public License (GPL), version 2
|
||||||
|
*
|
||||||
|
* Heavily based on sunxi driver from Maxime Ripard.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/reset-controller.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
struct stm32_reset_data {
|
||||||
|
spinlock_t lock;
|
||||||
|
void __iomem *membase;
|
||||||
|
struct reset_controller_dev rcdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stm32_reset_assert(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct stm32_reset_data *data = container_of(rcdev,
|
||||||
|
struct stm32_reset_data,
|
||||||
|
rcdev);
|
||||||
|
int bank = id / BITS_PER_LONG;
|
||||||
|
int offset = id % BITS_PER_LONG;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
|
reg = readl(data->membase + (bank * 4));
|
||||||
|
writel(reg | BIT(offset), data->membase + (bank * 4));
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stm32_reset_deassert(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct stm32_reset_data *data = container_of(rcdev,
|
||||||
|
struct stm32_reset_data,
|
||||||
|
rcdev);
|
||||||
|
int bank = id / BITS_PER_LONG;
|
||||||
|
int offset = id % BITS_PER_LONG;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
|
reg = readl(data->membase + (bank * 4));
|
||||||
|
writel(reg & ~BIT(offset), data->membase + (bank * 4));
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct reset_control_ops stm32_reset_ops = {
|
||||||
|
.assert = stm32_reset_assert,
|
||||||
|
.deassert = stm32_reset_deassert,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id stm32_reset_dt_ids[] = {
|
||||||
|
{ .compatible = "st,stm32-rcc", },
|
||||||
|
{ /* sentinel */ },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stm32_reset_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct stm32_reset_data *data;
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
data->membase = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(data->membase))
|
||||||
|
return PTR_ERR(data->membase);
|
||||||
|
|
||||||
|
spin_lock_init(&data->lock);
|
||||||
|
|
||||||
|
data->rcdev.owner = THIS_MODULE;
|
||||||
|
data->rcdev.nr_resets = resource_size(res) * 8;
|
||||||
|
data->rcdev.ops = &stm32_reset_ops;
|
||||||
|
data->rcdev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
return devm_reset_controller_register(&pdev->dev, &data->rcdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver stm32_reset_driver = {
|
||||||
|
.probe = stm32_reset_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "stm32-rcc-reset",
|
||||||
|
.of_match_table = stm32_reset_dt_ids,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
builtin_platform_driver(stm32_reset_driver);
|
440
drivers/reset/reset-uniphier.c
Normal file
440
drivers/reset/reset-uniphier.c
Normal file
|
@ -0,0 +1,440 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Socionext Inc.
|
||||||
|
* Author: Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/reset-controller.h>
|
||||||
|
|
||||||
|
struct uniphier_reset_data {
|
||||||
|
unsigned int id;
|
||||||
|
unsigned int reg;
|
||||||
|
unsigned int bit;
|
||||||
|
unsigned int flags;
|
||||||
|
#define UNIPHIER_RESET_ACTIVE_LOW BIT(0)
|
||||||
|
};
|
||||||
|
|
||||||
|
#define UNIPHIER_RESET_ID_END (unsigned int)(-1)
|
||||||
|
|
||||||
|
#define UNIPHIER_RESET_END \
|
||||||
|
{ .id = UNIPHIER_RESET_ID_END }
|
||||||
|
|
||||||
|
#define UNIPHIER_RESET(_id, _reg, _bit) \
|
||||||
|
{ \
|
||||||
|
.id = (_id), \
|
||||||
|
.reg = (_reg), \
|
||||||
|
.bit = (_bit), \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define UNIPHIER_RESETX(_id, _reg, _bit) \
|
||||||
|
{ \
|
||||||
|
.id = (_id), \
|
||||||
|
.reg = (_reg), \
|
||||||
|
.bit = (_bit), \
|
||||||
|
.flags = UNIPHIER_RESET_ACTIVE_LOW, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* System reset data */
|
||||||
|
#define UNIPHIER_SLD3_SYS_RESET_STDMAC(id) \
|
||||||
|
UNIPHIER_RESETX((id), 0x2000, 10)
|
||||||
|
|
||||||
|
#define UNIPHIER_LD11_SYS_RESET_STDMAC(id) \
|
||||||
|
UNIPHIER_RESETX((id), 0x200c, 8)
|
||||||
|
|
||||||
|
#define UNIPHIER_PRO4_SYS_RESET_GIO(id) \
|
||||||
|
UNIPHIER_RESETX((id), 0x2000, 6)
|
||||||
|
|
||||||
|
#define UNIPHIER_LD20_SYS_RESET_GIO(id) \
|
||||||
|
UNIPHIER_RESETX((id), 0x200c, 5)
|
||||||
|
|
||||||
|
#define UNIPHIER_PRO4_SYS_RESET_USB3(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x2000 + 0x4 * (ch), 17)
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_sld3_sys_reset_data[] = {
|
||||||
|
UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* Ether, HSC, MIO */
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_pro4_sys_reset_data[] = {
|
||||||
|
UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC, MIO, RLE */
|
||||||
|
UNIPHIER_PRO4_SYS_RESET_GIO(12), /* Ether, SATA, USB3 */
|
||||||
|
UNIPHIER_PRO4_SYS_RESET_USB3(14, 0),
|
||||||
|
UNIPHIER_PRO4_SYS_RESET_USB3(15, 1),
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_pro5_sys_reset_data[] = {
|
||||||
|
UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC */
|
||||||
|
UNIPHIER_PRO4_SYS_RESET_GIO(12), /* PCIe, USB3 */
|
||||||
|
UNIPHIER_PRO4_SYS_RESET_USB3(14, 0),
|
||||||
|
UNIPHIER_PRO4_SYS_RESET_USB3(15, 1),
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_pxs2_sys_reset_data[] = {
|
||||||
|
UNIPHIER_SLD3_SYS_RESET_STDMAC(8), /* HSC, RLE */
|
||||||
|
UNIPHIER_PRO4_SYS_RESET_USB3(14, 0),
|
||||||
|
UNIPHIER_PRO4_SYS_RESET_USB3(15, 1),
|
||||||
|
UNIPHIER_RESETX(16, 0x2014, 4), /* USB30-PHY0 */
|
||||||
|
UNIPHIER_RESETX(17, 0x2014, 0), /* USB30-PHY1 */
|
||||||
|
UNIPHIER_RESETX(18, 0x2014, 2), /* USB30-PHY2 */
|
||||||
|
UNIPHIER_RESETX(20, 0x2014, 5), /* USB31-PHY0 */
|
||||||
|
UNIPHIER_RESETX(21, 0x2014, 1), /* USB31-PHY1 */
|
||||||
|
UNIPHIER_RESETX(28, 0x2014, 12), /* SATA */
|
||||||
|
UNIPHIER_RESET(29, 0x2014, 8), /* SATA-PHY (active high) */
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_ld11_sys_reset_data[] = {
|
||||||
|
UNIPHIER_LD11_SYS_RESET_STDMAC(8), /* HSC, MIO */
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_ld20_sys_reset_data[] = {
|
||||||
|
UNIPHIER_LD11_SYS_RESET_STDMAC(8), /* HSC */
|
||||||
|
UNIPHIER_LD20_SYS_RESET_GIO(12), /* PCIe, USB3 */
|
||||||
|
UNIPHIER_RESETX(16, 0x200c, 12), /* USB30-PHY0 */
|
||||||
|
UNIPHIER_RESETX(17, 0x200c, 13), /* USB30-PHY1 */
|
||||||
|
UNIPHIER_RESETX(18, 0x200c, 14), /* USB30-PHY2 */
|
||||||
|
UNIPHIER_RESETX(19, 0x200c, 15), /* USB30-PHY3 */
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Media I/O reset data */
|
||||||
|
#define UNIPHIER_MIO_RESET_SD(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 0)
|
||||||
|
|
||||||
|
#define UNIPHIER_MIO_RESET_SD_BRIDGE(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 26)
|
||||||
|
|
||||||
|
#define UNIPHIER_MIO_RESET_EMMC_HW_RESET(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x80 + 0x200 * (ch), 0)
|
||||||
|
|
||||||
|
#define UNIPHIER_MIO_RESET_USB2(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x114 + 0x200 * (ch), 0)
|
||||||
|
|
||||||
|
#define UNIPHIER_MIO_RESET_USB2_BRIDGE(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x110 + 0x200 * (ch), 24)
|
||||||
|
|
||||||
|
#define UNIPHIER_MIO_RESET_DMAC(id) \
|
||||||
|
UNIPHIER_RESETX((id), 0x110, 17)
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_sld3_mio_reset_data[] = {
|
||||||
|
UNIPHIER_MIO_RESET_SD(0, 0),
|
||||||
|
UNIPHIER_MIO_RESET_SD(1, 1),
|
||||||
|
UNIPHIER_MIO_RESET_SD(2, 2),
|
||||||
|
UNIPHIER_MIO_RESET_SD_BRIDGE(3, 0),
|
||||||
|
UNIPHIER_MIO_RESET_SD_BRIDGE(4, 1),
|
||||||
|
UNIPHIER_MIO_RESET_SD_BRIDGE(5, 2),
|
||||||
|
UNIPHIER_MIO_RESET_EMMC_HW_RESET(6, 1),
|
||||||
|
UNIPHIER_MIO_RESET_DMAC(7),
|
||||||
|
UNIPHIER_MIO_RESET_USB2(8, 0),
|
||||||
|
UNIPHIER_MIO_RESET_USB2(9, 1),
|
||||||
|
UNIPHIER_MIO_RESET_USB2(10, 2),
|
||||||
|
UNIPHIER_MIO_RESET_USB2(11, 3),
|
||||||
|
UNIPHIER_MIO_RESET_USB2_BRIDGE(12, 0),
|
||||||
|
UNIPHIER_MIO_RESET_USB2_BRIDGE(13, 1),
|
||||||
|
UNIPHIER_MIO_RESET_USB2_BRIDGE(14, 2),
|
||||||
|
UNIPHIER_MIO_RESET_USB2_BRIDGE(15, 3),
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_pro5_mio_reset_data[] = {
|
||||||
|
UNIPHIER_MIO_RESET_SD(0, 0),
|
||||||
|
UNIPHIER_MIO_RESET_SD(1, 1),
|
||||||
|
UNIPHIER_MIO_RESET_EMMC_HW_RESET(6, 1),
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Peripheral reset data */
|
||||||
|
#define UNIPHIER_PERI_RESET_UART(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x114, 19 + (ch))
|
||||||
|
|
||||||
|
#define UNIPHIER_PERI_RESET_I2C(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x114, 5 + (ch))
|
||||||
|
|
||||||
|
#define UNIPHIER_PERI_RESET_FI2C(id, ch) \
|
||||||
|
UNIPHIER_RESETX((id), 0x114, 24 + (ch))
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_ld4_peri_reset_data[] = {
|
||||||
|
UNIPHIER_PERI_RESET_UART(0, 0),
|
||||||
|
UNIPHIER_PERI_RESET_UART(1, 1),
|
||||||
|
UNIPHIER_PERI_RESET_UART(2, 2),
|
||||||
|
UNIPHIER_PERI_RESET_UART(3, 3),
|
||||||
|
UNIPHIER_PERI_RESET_I2C(4, 0),
|
||||||
|
UNIPHIER_PERI_RESET_I2C(5, 1),
|
||||||
|
UNIPHIER_PERI_RESET_I2C(6, 2),
|
||||||
|
UNIPHIER_PERI_RESET_I2C(7, 3),
|
||||||
|
UNIPHIER_PERI_RESET_I2C(8, 4),
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct uniphier_reset_data uniphier_pro4_peri_reset_data[] = {
|
||||||
|
UNIPHIER_PERI_RESET_UART(0, 0),
|
||||||
|
UNIPHIER_PERI_RESET_UART(1, 1),
|
||||||
|
UNIPHIER_PERI_RESET_UART(2, 2),
|
||||||
|
UNIPHIER_PERI_RESET_UART(3, 3),
|
||||||
|
UNIPHIER_PERI_RESET_FI2C(4, 0),
|
||||||
|
UNIPHIER_PERI_RESET_FI2C(5, 1),
|
||||||
|
UNIPHIER_PERI_RESET_FI2C(6, 2),
|
||||||
|
UNIPHIER_PERI_RESET_FI2C(7, 3),
|
||||||
|
UNIPHIER_PERI_RESET_FI2C(8, 4),
|
||||||
|
UNIPHIER_PERI_RESET_FI2C(9, 5),
|
||||||
|
UNIPHIER_PERI_RESET_FI2C(10, 6),
|
||||||
|
UNIPHIER_RESET_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* core implementaton */
|
||||||
|
struct uniphier_reset_priv {
|
||||||
|
struct reset_controller_dev rcdev;
|
||||||
|
struct device *dev;
|
||||||
|
struct regmap *regmap;
|
||||||
|
const struct uniphier_reset_data *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_uniphier_reset_priv(_rcdev) \
|
||||||
|
container_of(_rcdev, struct uniphier_reset_priv, rcdev)
|
||||||
|
|
||||||
|
static int uniphier_reset_update(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id, int assert)
|
||||||
|
{
|
||||||
|
struct uniphier_reset_priv *priv = to_uniphier_reset_priv(rcdev);
|
||||||
|
const struct uniphier_reset_data *p;
|
||||||
|
|
||||||
|
for (p = priv->data; p->id != UNIPHIER_RESET_ID_END; p++) {
|
||||||
|
unsigned int mask, val;
|
||||||
|
|
||||||
|
if (p->id != id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mask = BIT(p->bit);
|
||||||
|
|
||||||
|
if (assert)
|
||||||
|
val = mask;
|
||||||
|
else
|
||||||
|
val = ~mask;
|
||||||
|
|
||||||
|
if (p->flags & UNIPHIER_RESET_ACTIVE_LOW)
|
||||||
|
val = ~val;
|
||||||
|
|
||||||
|
return regmap_write_bits(priv->regmap, p->reg, mask, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_err(priv->dev, "reset_id=%lu was not handled\n", id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uniphier_reset_assert(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
return uniphier_reset_update(rcdev, id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uniphier_reset_deassert(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
return uniphier_reset_update(rcdev, id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uniphier_reset_status(struct reset_controller_dev *rcdev,
|
||||||
|
unsigned long id)
|
||||||
|
{
|
||||||
|
struct uniphier_reset_priv *priv = to_uniphier_reset_priv(rcdev);
|
||||||
|
const struct uniphier_reset_data *p;
|
||||||
|
|
||||||
|
for (p = priv->data; p->id != UNIPHIER_RESET_ID_END; p++) {
|
||||||
|
unsigned int val;
|
||||||
|
int ret, asserted;
|
||||||
|
|
||||||
|
if (p->id != id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = regmap_read(priv->regmap, p->reg, &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
asserted = !!(val & BIT(p->bit));
|
||||||
|
|
||||||
|
if (p->flags & UNIPHIER_RESET_ACTIVE_LOW)
|
||||||
|
asserted = !asserted;
|
||||||
|
|
||||||
|
return asserted;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_err(priv->dev, "reset_id=%lu was not found\n", id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct reset_control_ops uniphier_reset_ops = {
|
||||||
|
.assert = uniphier_reset_assert,
|
||||||
|
.deassert = uniphier_reset_deassert,
|
||||||
|
.status = uniphier_reset_status,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int uniphier_reset_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct uniphier_reset_priv *priv;
|
||||||
|
const struct uniphier_reset_data *p, *data;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct device_node *parent;
|
||||||
|
unsigned int nr_resets = 0;
|
||||||
|
|
||||||
|
data = of_device_get_match_data(dev);
|
||||||
|
if (WARN_ON(!data))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
parent = of_get_parent(dev->of_node); /* parent should be syscon node */
|
||||||
|
regmap = syscon_node_to_regmap(parent);
|
||||||
|
of_node_put(parent);
|
||||||
|
if (IS_ERR(regmap)) {
|
||||||
|
dev_err(dev, "failed to get regmap (error %ld)\n",
|
||||||
|
PTR_ERR(regmap));
|
||||||
|
return PTR_ERR(regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (p = data; p->id != UNIPHIER_RESET_ID_END; p++)
|
||||||
|
nr_resets = max(nr_resets, p->id + 1);
|
||||||
|
|
||||||
|
priv->rcdev.ops = &uniphier_reset_ops;
|
||||||
|
priv->rcdev.owner = dev->driver->owner;
|
||||||
|
priv->rcdev.of_node = dev->of_node;
|
||||||
|
priv->rcdev.nr_resets = nr_resets;
|
||||||
|
priv->dev = dev;
|
||||||
|
priv->regmap = regmap;
|
||||||
|
priv->data = data;
|
||||||
|
|
||||||
|
return devm_reset_controller_register(&pdev->dev, &priv->rcdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id uniphier_reset_match[] = {
|
||||||
|
/* System reset */
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-sld3-reset",
|
||||||
|
.data = uniphier_sld3_sys_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld4-reset",
|
||||||
|
.data = uniphier_sld3_sys_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pro4-reset",
|
||||||
|
.data = uniphier_pro4_sys_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-sld8-reset",
|
||||||
|
.data = uniphier_sld3_sys_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pro5-reset",
|
||||||
|
.data = uniphier_pro5_sys_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pxs2-reset",
|
||||||
|
.data = uniphier_pxs2_sys_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld11-reset",
|
||||||
|
.data = uniphier_ld11_sys_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld20-reset",
|
||||||
|
.data = uniphier_ld20_sys_reset_data,
|
||||||
|
},
|
||||||
|
/* Media I/O reset */
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-sld3-mio-reset",
|
||||||
|
.data = uniphier_sld3_mio_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld4-mio-reset",
|
||||||
|
.data = uniphier_sld3_mio_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pro4-mio-reset",
|
||||||
|
.data = uniphier_sld3_mio_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-sld8-mio-reset",
|
||||||
|
.data = uniphier_sld3_mio_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pro5-mio-reset",
|
||||||
|
.data = uniphier_pro5_mio_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pxs2-mio-reset",
|
||||||
|
.data = uniphier_pro5_mio_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld11-mio-reset",
|
||||||
|
.data = uniphier_sld3_mio_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld20-mio-reset",
|
||||||
|
.data = uniphier_pro5_mio_reset_data,
|
||||||
|
},
|
||||||
|
/* Peripheral reset */
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld4-peri-reset",
|
||||||
|
.data = uniphier_ld4_peri_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pro4-peri-reset",
|
||||||
|
.data = uniphier_pro4_peri_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-sld8-peri-reset",
|
||||||
|
.data = uniphier_ld4_peri_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pro5-peri-reset",
|
||||||
|
.data = uniphier_pro4_peri_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-pxs2-peri-reset",
|
||||||
|
.data = uniphier_pro4_peri_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld11-peri-reset",
|
||||||
|
.data = uniphier_pro4_peri_reset_data,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "socionext,uniphier-ld20-peri-reset",
|
||||||
|
.data = uniphier_pro4_peri_reset_data,
|
||||||
|
},
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, uniphier_reset_match);
|
||||||
|
|
||||||
|
static struct platform_driver uniphier_reset_driver = {
|
||||||
|
.probe = uniphier_reset_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "uniphier-reset",
|
||||||
|
.of_match_table = uniphier_reset_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(uniphier_reset_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
|
||||||
|
MODULE_DESCRIPTION("UniPhier Reset Controller Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -583,7 +583,7 @@ static int pwrap_wait_for_state(struct pmic_wrapper *wrp,
|
||||||
{
|
{
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
timeout = jiffies + usecs_to_jiffies(255);
|
timeout = jiffies + usecs_to_jiffies(10000);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (time_after(jiffies, timeout))
|
if (time_after(jiffies, timeout))
|
||||||
|
|
|
@ -95,7 +95,7 @@ static const struct {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct qcom_smd_edge - representing a remote processor
|
* struct qcom_smd_edge - representing a remote processor
|
||||||
* @smd: handle to qcom_smd
|
* @dev: device for this edge
|
||||||
* @of_node: of_node handle for information related to this edge
|
* @of_node: of_node handle for information related to this edge
|
||||||
* @edge_id: identifier of this edge
|
* @edge_id: identifier of this edge
|
||||||
* @remote_pid: identifier of remote processor
|
* @remote_pid: identifier of remote processor
|
||||||
|
@ -111,7 +111,8 @@ static const struct {
|
||||||
* @state_work: work item for edge state changes
|
* @state_work: work item for edge state changes
|
||||||
*/
|
*/
|
||||||
struct qcom_smd_edge {
|
struct qcom_smd_edge {
|
||||||
struct qcom_smd *smd;
|
struct device dev;
|
||||||
|
|
||||||
struct device_node *of_node;
|
struct device_node *of_node;
|
||||||
unsigned edge_id;
|
unsigned edge_id;
|
||||||
unsigned remote_pid;
|
unsigned remote_pid;
|
||||||
|
@ -135,6 +136,8 @@ struct qcom_smd_edge {
|
||||||
struct work_struct state_work;
|
struct work_struct state_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SMD channel states.
|
* SMD channel states.
|
||||||
*/
|
*/
|
||||||
|
@ -197,20 +200,6 @@ struct qcom_smd_channel {
|
||||||
void *drvdata;
|
void *drvdata;
|
||||||
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct list_head dev_list;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* struct qcom_smd - smd struct
|
|
||||||
* @dev: device struct
|
|
||||||
* @num_edges: number of entries in @edges
|
|
||||||
* @edges: array of edges to be handled
|
|
||||||
*/
|
|
||||||
struct qcom_smd {
|
|
||||||
struct device *dev;
|
|
||||||
|
|
||||||
unsigned num_edges;
|
|
||||||
struct qcom_smd_edge edges[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -374,7 +363,7 @@ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel)
|
||||||
SET_TX_CHANNEL_FLAG(channel, fSTATE, 1);
|
SET_TX_CHANNEL_FLAG(channel, fSTATE, 1);
|
||||||
SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1);
|
SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1);
|
||||||
SET_TX_CHANNEL_INFO(channel, head, 0);
|
SET_TX_CHANNEL_INFO(channel, head, 0);
|
||||||
SET_TX_CHANNEL_INFO(channel, tail, 0);
|
SET_RX_CHANNEL_INFO(channel, tail, 0);
|
||||||
|
|
||||||
qcom_smd_signal_channel(channel);
|
qcom_smd_signal_channel(channel);
|
||||||
|
|
||||||
|
@ -421,7 +410,7 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel,
|
||||||
if (channel->state == state)
|
if (channel->state == state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dev_dbg(edge->smd->dev, "set_state(%s, %d)\n", channel->name, state);
|
dev_dbg(&edge->dev, "set_state(%s, %d)\n", channel->name, state);
|
||||||
|
|
||||||
SET_TX_CHANNEL_FLAG(channel, fDSR, is_open);
|
SET_TX_CHANNEL_FLAG(channel, fDSR, is_open);
|
||||||
SET_TX_CHANNEL_FLAG(channel, fCTS, is_open);
|
SET_TX_CHANNEL_FLAG(channel, fCTS, is_open);
|
||||||
|
@ -891,8 +880,6 @@ static int qcom_smd_dev_remove(struct device *dev)
|
||||||
struct qcom_smd_device *qsdev = to_smd_device(dev);
|
struct qcom_smd_device *qsdev = to_smd_device(dev);
|
||||||
struct qcom_smd_driver *qsdrv = to_smd_driver(dev);
|
struct qcom_smd_driver *qsdrv = to_smd_driver(dev);
|
||||||
struct qcom_smd_channel *channel = qsdev->channel;
|
struct qcom_smd_channel *channel = qsdev->channel;
|
||||||
struct qcom_smd_channel *tmp;
|
|
||||||
struct qcom_smd_channel *ch;
|
|
||||||
|
|
||||||
qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING);
|
qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING);
|
||||||
|
|
||||||
|
@ -911,15 +898,9 @@ static int qcom_smd_dev_remove(struct device *dev)
|
||||||
if (qsdrv->remove)
|
if (qsdrv->remove)
|
||||||
qsdrv->remove(qsdev);
|
qsdrv->remove(qsdev);
|
||||||
|
|
||||||
/*
|
/* The client is now gone, close the primary channel */
|
||||||
* The client is now gone, close and release all channels associated
|
qcom_smd_channel_close(channel);
|
||||||
* with this sdev
|
channel->qsdev = NULL;
|
||||||
*/
|
|
||||||
list_for_each_entry_safe(ch, tmp, &channel->dev_list, dev_list) {
|
|
||||||
qcom_smd_channel_close(ch);
|
|
||||||
list_del(&ch->dev_list);
|
|
||||||
ch->qsdev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -973,13 +954,12 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
|
||||||
struct qcom_smd_device *qsdev;
|
struct qcom_smd_device *qsdev;
|
||||||
struct qcom_smd_edge *edge = channel->edge;
|
struct qcom_smd_edge *edge = channel->edge;
|
||||||
struct device_node *node;
|
struct device_node *node;
|
||||||
struct qcom_smd *smd = edge->smd;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (channel->qsdev)
|
if (channel->qsdev)
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
|
|
||||||
dev_dbg(smd->dev, "registering '%s'\n", channel->name);
|
dev_dbg(&edge->dev, "registering '%s'\n", channel->name);
|
||||||
|
|
||||||
qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL);
|
qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL);
|
||||||
if (!qsdev)
|
if (!qsdev)
|
||||||
|
@ -990,7 +970,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
|
||||||
edge->of_node->name,
|
edge->of_node->name,
|
||||||
node ? node->name : channel->name);
|
node ? node->name : channel->name);
|
||||||
|
|
||||||
qsdev->dev.parent = smd->dev;
|
qsdev->dev.parent = &edge->dev;
|
||||||
qsdev->dev.bus = &qcom_smd_bus;
|
qsdev->dev.bus = &qcom_smd_bus;
|
||||||
qsdev->dev.release = qcom_smd_release_device;
|
qsdev->dev.release = qcom_smd_release_device;
|
||||||
qsdev->dev.of_node = node;
|
qsdev->dev.of_node = node;
|
||||||
|
@ -1001,7 +981,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
|
||||||
|
|
||||||
ret = device_register(&qsdev->dev);
|
ret = device_register(&qsdev->dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(smd->dev, "device_register failed: %d\n", ret);
|
dev_err(&edge->dev, "device_register failed: %d\n", ret);
|
||||||
put_device(&qsdev->dev);
|
put_device(&qsdev->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,6 +1071,8 @@ qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name)
|
||||||
*
|
*
|
||||||
* Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't
|
* Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't
|
||||||
* ready.
|
* ready.
|
||||||
|
*
|
||||||
|
* Any channels returned must be closed with a call to qcom_smd_close_channel()
|
||||||
*/
|
*/
|
||||||
struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent,
|
struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
@ -1120,15 +1102,21 @@ struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent,
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Append the list of channel to the channels associated with the sdev
|
|
||||||
*/
|
|
||||||
list_add_tail(&channel->dev_list, &sdev->channel->dev_list);
|
|
||||||
|
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qcom_smd_open_channel);
|
EXPORT_SYMBOL(qcom_smd_open_channel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qcom_smd_close_channel() - close an additionally opened channel
|
||||||
|
* @channel: channel handle, returned by qcom_smd_open_channel()
|
||||||
|
*/
|
||||||
|
void qcom_smd_close_channel(struct qcom_smd_channel *channel)
|
||||||
|
{
|
||||||
|
qcom_smd_channel_close(channel);
|
||||||
|
channel->qsdev = NULL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_smd_close_channel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate the qcom_smd_channel object for a newly found smd channel,
|
* Allocate the qcom_smd_channel object for a newly found smd channel,
|
||||||
* retrieving and validating the smem items involved.
|
* retrieving and validating the smem items involved.
|
||||||
|
@ -1139,20 +1127,18 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
|
||||||
char *name)
|
char *name)
|
||||||
{
|
{
|
||||||
struct qcom_smd_channel *channel;
|
struct qcom_smd_channel *channel;
|
||||||
struct qcom_smd *smd = edge->smd;
|
|
||||||
size_t fifo_size;
|
size_t fifo_size;
|
||||||
size_t info_size;
|
size_t info_size;
|
||||||
void *fifo_base;
|
void *fifo_base;
|
||||||
void *info;
|
void *info;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
channel = devm_kzalloc(smd->dev, sizeof(*channel), GFP_KERNEL);
|
channel = devm_kzalloc(&edge->dev, sizeof(*channel), GFP_KERNEL);
|
||||||
if (!channel)
|
if (!channel)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&channel->dev_list);
|
|
||||||
channel->edge = edge;
|
channel->edge = edge;
|
||||||
channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL);
|
channel->name = devm_kstrdup(&edge->dev, name, GFP_KERNEL);
|
||||||
if (!channel->name)
|
if (!channel->name)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
@ -1175,7 +1161,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
|
||||||
} else if (info_size == 2 * sizeof(struct smd_channel_info)) {
|
} else if (info_size == 2 * sizeof(struct smd_channel_info)) {
|
||||||
channel->info = info;
|
channel->info = info;
|
||||||
} else {
|
} else {
|
||||||
dev_err(smd->dev,
|
dev_err(&edge->dev,
|
||||||
"channel info of size %zu not supported\n", info_size);
|
"channel info of size %zu not supported\n", info_size);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto free_name_and_channel;
|
goto free_name_and_channel;
|
||||||
|
@ -1190,7 +1176,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
|
||||||
/* The channel consist of a rx and tx fifo of equal size */
|
/* The channel consist of a rx and tx fifo of equal size */
|
||||||
fifo_size /= 2;
|
fifo_size /= 2;
|
||||||
|
|
||||||
dev_dbg(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n",
|
dev_dbg(&edge->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n",
|
||||||
name, info_size, fifo_size);
|
name, info_size, fifo_size);
|
||||||
|
|
||||||
channel->tx_fifo = fifo_base;
|
channel->tx_fifo = fifo_base;
|
||||||
|
@ -1202,8 +1188,8 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
|
||||||
return channel;
|
return channel;
|
||||||
|
|
||||||
free_name_and_channel:
|
free_name_and_channel:
|
||||||
devm_kfree(smd->dev, channel->name);
|
devm_kfree(&edge->dev, channel->name);
|
||||||
devm_kfree(smd->dev, channel);
|
devm_kfree(&edge->dev, channel);
|
||||||
|
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
@ -1219,7 +1205,6 @@ static void qcom_channel_scan_worker(struct work_struct *work)
|
||||||
struct qcom_smd_alloc_entry *alloc_tbl;
|
struct qcom_smd_alloc_entry *alloc_tbl;
|
||||||
struct qcom_smd_alloc_entry *entry;
|
struct qcom_smd_alloc_entry *entry;
|
||||||
struct qcom_smd_channel *channel;
|
struct qcom_smd_channel *channel;
|
||||||
struct qcom_smd *smd = edge->smd;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned fifo_id;
|
unsigned fifo_id;
|
||||||
unsigned info_id;
|
unsigned info_id;
|
||||||
|
@ -1263,7 +1248,7 @@ static void qcom_channel_scan_worker(struct work_struct *work)
|
||||||
list_add(&channel->list, &edge->channels);
|
list_add(&channel->list, &edge->channels);
|
||||||
spin_unlock_irqrestore(&edge->channels_lock, flags);
|
spin_unlock_irqrestore(&edge->channels_lock, flags);
|
||||||
|
|
||||||
dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name);
|
dev_dbg(&edge->dev, "new channel found: '%s'\n", channel->name);
|
||||||
set_bit(i, edge->allocated[tbl]);
|
set_bit(i, edge->allocated[tbl]);
|
||||||
|
|
||||||
wake_up_interruptible(&edge->new_channel_event);
|
wake_up_interruptible(&edge->new_channel_event);
|
||||||
|
@ -1350,22 +1335,6 @@ static int qcom_smd_parse_edge(struct device *dev,
|
||||||
|
|
||||||
edge->of_node = of_node_get(node);
|
edge->of_node = of_node_get(node);
|
||||||
|
|
||||||
irq = irq_of_parse_and_map(node, 0);
|
|
||||||
if (irq < 0) {
|
|
||||||
dev_err(dev, "required smd interrupt missing\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = devm_request_irq(dev, irq,
|
|
||||||
qcom_smd_edge_intr, IRQF_TRIGGER_RISING,
|
|
||||||
node->name, edge);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(dev, "failed to request smd irq\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
edge->irq = irq;
|
|
||||||
|
|
||||||
key = "qcom,smd-edge";
|
key = "qcom,smd-edge";
|
||||||
ret = of_property_read_u32(node, key, &edge->edge_id);
|
ret = of_property_read_u32(node, key, &edge->edge_id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -1400,18 +1369,121 @@ static int qcom_smd_parse_edge(struct device *dev,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irq = irq_of_parse_and_map(node, 0);
|
||||||
|
if (irq < 0) {
|
||||||
|
dev_err(dev, "required smd interrupt missing\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, irq,
|
||||||
|
qcom_smd_edge_intr, IRQF_TRIGGER_RISING,
|
||||||
|
node->name, edge);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to request smd irq\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
edge->irq = irq;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcom_smd_probe(struct platform_device *pdev)
|
/*
|
||||||
|
* Release function for an edge.
|
||||||
|
* Reset the state of each associated channel and free the edge context.
|
||||||
|
*/
|
||||||
|
static void qcom_smd_edge_release(struct device *dev)
|
||||||
|
{
|
||||||
|
struct qcom_smd_channel *channel;
|
||||||
|
struct qcom_smd_edge *edge = to_smd_edge(dev);
|
||||||
|
|
||||||
|
list_for_each_entry(channel, &edge->channels, list) {
|
||||||
|
SET_RX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED);
|
||||||
|
SET_RX_CHANNEL_INFO(channel, head, 0);
|
||||||
|
SET_RX_CHANNEL_INFO(channel, tail, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qcom_smd_register_edge() - register an edge based on an device_node
|
||||||
|
* @parent: parent device for the edge
|
||||||
|
* @node: device_node describing the edge
|
||||||
|
*
|
||||||
|
* Returns an edge reference, or negative ERR_PTR() on failure.
|
||||||
|
*/
|
||||||
|
struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
|
||||||
|
struct device_node *node)
|
||||||
{
|
{
|
||||||
struct qcom_smd_edge *edge;
|
struct qcom_smd_edge *edge;
|
||||||
struct device_node *node;
|
|
||||||
struct qcom_smd *smd;
|
|
||||||
size_t array_size;
|
|
||||||
int num_edges;
|
|
||||||
int ret;
|
int ret;
|
||||||
int i = 0;
|
|
||||||
|
edge = kzalloc(sizeof(*edge), GFP_KERNEL);
|
||||||
|
if (!edge)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
init_waitqueue_head(&edge->new_channel_event);
|
||||||
|
|
||||||
|
edge->dev.parent = parent;
|
||||||
|
edge->dev.release = qcom_smd_edge_release;
|
||||||
|
dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name);
|
||||||
|
ret = device_register(&edge->dev);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("failed to register smd edge\n");
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qcom_smd_parse_edge(&edge->dev, node, edge);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&edge->dev, "failed to parse smd edge\n");
|
||||||
|
goto unregister_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_work(&edge->scan_work);
|
||||||
|
|
||||||
|
return edge;
|
||||||
|
|
||||||
|
unregister_dev:
|
||||||
|
put_device(&edge->dev);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_smd_register_edge);
|
||||||
|
|
||||||
|
static int qcom_smd_remove_device(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
device_unregister(dev);
|
||||||
|
of_node_put(dev->of_node);
|
||||||
|
put_device(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qcom_smd_unregister_edge() - release an edge and its children
|
||||||
|
* @edge: edge reference acquired from qcom_smd_register_edge
|
||||||
|
*/
|
||||||
|
int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
disable_irq(edge->irq);
|
||||||
|
cancel_work_sync(&edge->scan_work);
|
||||||
|
cancel_work_sync(&edge->state_work);
|
||||||
|
|
||||||
|
ret = device_for_each_child(&edge->dev, NULL, qcom_smd_remove_device);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(&edge->dev, "can't remove smd device: %d\n", ret);
|
||||||
|
|
||||||
|
device_unregister(&edge->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(qcom_smd_unregister_edge);
|
||||||
|
|
||||||
|
static int qcom_smd_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *node;
|
||||||
void *p;
|
void *p;
|
||||||
|
|
||||||
/* Wait for smem */
|
/* Wait for smem */
|
||||||
|
@ -1419,59 +1491,32 @@ static int qcom_smd_probe(struct platform_device *pdev)
|
||||||
if (PTR_ERR(p) == -EPROBE_DEFER)
|
if (PTR_ERR(p) == -EPROBE_DEFER)
|
||||||
return PTR_ERR(p);
|
return PTR_ERR(p);
|
||||||
|
|
||||||
num_edges = of_get_available_child_count(pdev->dev.of_node);
|
for_each_available_child_of_node(pdev->dev.of_node, node)
|
||||||
array_size = sizeof(*smd) + num_edges * sizeof(struct qcom_smd_edge);
|
qcom_smd_register_edge(&pdev->dev, node);
|
||||||
smd = devm_kzalloc(&pdev->dev, array_size, GFP_KERNEL);
|
|
||||||
if (!smd)
|
|
||||||
return -ENOMEM;
|
|
||||||
smd->dev = &pdev->dev;
|
|
||||||
|
|
||||||
smd->num_edges = num_edges;
|
|
||||||
for_each_available_child_of_node(pdev->dev.of_node, node) {
|
|
||||||
edge = &smd->edges[i++];
|
|
||||||
edge->smd = smd;
|
|
||||||
init_waitqueue_head(&edge->new_channel_event);
|
|
||||||
|
|
||||||
ret = qcom_smd_parse_edge(&pdev->dev, node, edge);
|
|
||||||
if (ret)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
schedule_work(&edge->scan_work);
|
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, smd);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int qcom_smd_remove_edge(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
struct qcom_smd_edge *edge = to_smd_edge(dev);
|
||||||
|
|
||||||
|
return qcom_smd_unregister_edge(edge);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shut down all smd clients by making sure that each edge stops processing
|
* Shut down all smd clients by making sure that each edge stops processing
|
||||||
* events and scanning for new channels, then call destroy on the devices.
|
* events and scanning for new channels, then call destroy on the devices.
|
||||||
*/
|
*/
|
||||||
static int qcom_smd_remove(struct platform_device *pdev)
|
static int qcom_smd_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct qcom_smd_channel *channel;
|
int ret;
|
||||||
struct qcom_smd_edge *edge;
|
|
||||||
struct qcom_smd *smd = platform_get_drvdata(pdev);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < smd->num_edges; i++) {
|
ret = device_for_each_child(&pdev->dev, NULL, qcom_smd_remove_edge);
|
||||||
edge = &smd->edges[i];
|
if (ret)
|
||||||
|
dev_warn(&pdev->dev, "can't remove smd device: %d\n", ret);
|
||||||
|
|
||||||
disable_irq(edge->irq);
|
return ret;
|
||||||
cancel_work_sync(&edge->scan_work);
|
|
||||||
cancel_work_sync(&edge->state_work);
|
|
||||||
|
|
||||||
/* No need to lock here, because the writer is gone */
|
|
||||||
list_for_each_entry(channel, &edge->channels, list) {
|
|
||||||
if (!channel->qsdev)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
qcom_smd_destroy_device(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id qcom_smd_of_match[] = {
|
static const struct of_device_id qcom_smd_of_match[] = {
|
||||||
|
|
|
@ -740,6 +740,7 @@ static int qcom_smem_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
|
hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
|
||||||
if (hwlock_id < 0) {
|
if (hwlock_id < 0) {
|
||||||
|
if (hwlock_id != -EPROBE_DEFER)
|
||||||
dev_err(&pdev->dev, "failed to retrieve hwlock\n");
|
dev_err(&pdev->dev, "failed to retrieve hwlock\n");
|
||||||
return hwlock_id;
|
return hwlock_id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ struct rockchip_domain_info {
|
||||||
int req_mask;
|
int req_mask;
|
||||||
int idle_mask;
|
int idle_mask;
|
||||||
int ack_mask;
|
int ack_mask;
|
||||||
|
bool active_wakeup;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rockchip_pmu_info {
|
struct rockchip_pmu_info {
|
||||||
|
@ -75,23 +76,24 @@ struct rockchip_pmu {
|
||||||
|
|
||||||
#define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
|
#define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
|
||||||
|
|
||||||
#define DOMAIN(pwr, status, req, idle, ack) \
|
#define DOMAIN(pwr, status, req, idle, ack, wakeup) \
|
||||||
{ \
|
{ \
|
||||||
.pwr_mask = (pwr >= 0) ? BIT(pwr) : 0, \
|
.pwr_mask = (pwr >= 0) ? BIT(pwr) : 0, \
|
||||||
.status_mask = (status >= 0) ? BIT(status) : 0, \
|
.status_mask = (status >= 0) ? BIT(status) : 0, \
|
||||||
.req_mask = (req >= 0) ? BIT(req) : 0, \
|
.req_mask = (req >= 0) ? BIT(req) : 0, \
|
||||||
.idle_mask = (idle >= 0) ? BIT(idle) : 0, \
|
.idle_mask = (idle >= 0) ? BIT(idle) : 0, \
|
||||||
.ack_mask = (ack >= 0) ? BIT(ack) : 0, \
|
.ack_mask = (ack >= 0) ? BIT(ack) : 0, \
|
||||||
|
.active_wakeup = wakeup, \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DOMAIN_RK3288(pwr, status, req) \
|
#define DOMAIN_RK3288(pwr, status, req, wakeup) \
|
||||||
DOMAIN(pwr, status, req, req, (req) + 16)
|
DOMAIN(pwr, status, req, req, (req) + 16, wakeup)
|
||||||
|
|
||||||
#define DOMAIN_RK3368(pwr, status, req) \
|
#define DOMAIN_RK3368(pwr, status, req, wakeup) \
|
||||||
DOMAIN(pwr, status, req, (req) + 16, req)
|
DOMAIN(pwr, status, req, (req) + 16, req, wakeup)
|
||||||
|
|
||||||
#define DOMAIN_RK3399(pwr, status, req) \
|
#define DOMAIN_RK3399(pwr, status, req, wakeup) \
|
||||||
DOMAIN(pwr, status, req, req, req)
|
DOMAIN(pwr, status, req, req, req, wakeup)
|
||||||
|
|
||||||
static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
|
static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
|
||||||
{
|
{
|
||||||
|
@ -295,6 +297,17 @@ static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd,
|
||||||
pm_clk_destroy(dev);
|
pm_clk_destroy(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool rockchip_active_wakeup(struct device *dev)
|
||||||
|
{
|
||||||
|
struct generic_pm_domain *genpd;
|
||||||
|
struct rockchip_pm_domain *pd;
|
||||||
|
|
||||||
|
genpd = pd_to_genpd(dev->pm_domain);
|
||||||
|
pd = container_of(genpd, struct rockchip_pm_domain, genpd);
|
||||||
|
|
||||||
|
return pd->info->active_wakeup;
|
||||||
|
}
|
||||||
|
|
||||||
static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
|
static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
|
||||||
struct device_node *node)
|
struct device_node *node)
|
||||||
{
|
{
|
||||||
|
@ -415,6 +428,7 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
|
||||||
pd->genpd.power_on = rockchip_pd_power_on;
|
pd->genpd.power_on = rockchip_pd_power_on;
|
||||||
pd->genpd.attach_dev = rockchip_pd_attach_dev;
|
pd->genpd.attach_dev = rockchip_pd_attach_dev;
|
||||||
pd->genpd.detach_dev = rockchip_pd_detach_dev;
|
pd->genpd.detach_dev = rockchip_pd_detach_dev;
|
||||||
|
pd->genpd.dev_ops.active_wakeup = rockchip_active_wakeup;
|
||||||
pd->genpd.flags = GENPD_FLAG_PM_CLK;
|
pd->genpd.flags = GENPD_FLAG_PM_CLK;
|
||||||
pm_genpd_init(&pd->genpd, NULL, false);
|
pm_genpd_init(&pd->genpd, NULL, false);
|
||||||
|
|
||||||
|
@ -623,48 +637,48 @@ err_out:
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct rockchip_domain_info rk3288_pm_domains[] = {
|
static const struct rockchip_domain_info rk3288_pm_domains[] = {
|
||||||
[RK3288_PD_VIO] = DOMAIN_RK3288(7, 7, 4),
|
[RK3288_PD_VIO] = DOMAIN_RK3288(7, 7, 4, false),
|
||||||
[RK3288_PD_HEVC] = DOMAIN_RK3288(14, 10, 9),
|
[RK3288_PD_HEVC] = DOMAIN_RK3288(14, 10, 9, false),
|
||||||
[RK3288_PD_VIDEO] = DOMAIN_RK3288(8, 8, 3),
|
[RK3288_PD_VIDEO] = DOMAIN_RK3288(8, 8, 3, false),
|
||||||
[RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2),
|
[RK3288_PD_GPU] = DOMAIN_RK3288(9, 9, 2, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct rockchip_domain_info rk3368_pm_domains[] = {
|
static const struct rockchip_domain_info rk3368_pm_domains[] = {
|
||||||
[RK3368_PD_PERI] = DOMAIN_RK3368(13, 12, 6),
|
[RK3368_PD_PERI] = DOMAIN_RK3368(13, 12, 6, true),
|
||||||
[RK3368_PD_VIO] = DOMAIN_RK3368(15, 14, 8),
|
[RK3368_PD_VIO] = DOMAIN_RK3368(15, 14, 8, false),
|
||||||
[RK3368_PD_VIDEO] = DOMAIN_RK3368(14, 13, 7),
|
[RK3368_PD_VIDEO] = DOMAIN_RK3368(14, 13, 7, false),
|
||||||
[RK3368_PD_GPU_0] = DOMAIN_RK3368(16, 15, 2),
|
[RK3368_PD_GPU_0] = DOMAIN_RK3368(16, 15, 2, false),
|
||||||
[RK3368_PD_GPU_1] = DOMAIN_RK3368(17, 16, 2),
|
[RK3368_PD_GPU_1] = DOMAIN_RK3368(17, 16, 2, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct rockchip_domain_info rk3399_pm_domains[] = {
|
static const struct rockchip_domain_info rk3399_pm_domains[] = {
|
||||||
[RK3399_PD_TCPD0] = DOMAIN_RK3399(8, 8, -1),
|
[RK3399_PD_TCPD0] = DOMAIN_RK3399(8, 8, -1, false),
|
||||||
[RK3399_PD_TCPD1] = DOMAIN_RK3399(9, 9, -1),
|
[RK3399_PD_TCPD1] = DOMAIN_RK3399(9, 9, -1, false),
|
||||||
[RK3399_PD_CCI] = DOMAIN_RK3399(10, 10, -1),
|
[RK3399_PD_CCI] = DOMAIN_RK3399(10, 10, -1, true),
|
||||||
[RK3399_PD_CCI0] = DOMAIN_RK3399(-1, -1, 15),
|
[RK3399_PD_CCI0] = DOMAIN_RK3399(-1, -1, 15, true),
|
||||||
[RK3399_PD_CCI1] = DOMAIN_RK3399(-1, -1, 16),
|
[RK3399_PD_CCI1] = DOMAIN_RK3399(-1, -1, 16, true),
|
||||||
[RK3399_PD_PERILP] = DOMAIN_RK3399(11, 11, 1),
|
[RK3399_PD_PERILP] = DOMAIN_RK3399(11, 11, 1, true),
|
||||||
[RK3399_PD_PERIHP] = DOMAIN_RK3399(12, 12, 2),
|
[RK3399_PD_PERIHP] = DOMAIN_RK3399(12, 12, 2, true),
|
||||||
[RK3399_PD_CENTER] = DOMAIN_RK3399(13, 13, 14),
|
[RK3399_PD_CENTER] = DOMAIN_RK3399(13, 13, 14, true),
|
||||||
[RK3399_PD_VIO] = DOMAIN_RK3399(14, 14, 17),
|
[RK3399_PD_VIO] = DOMAIN_RK3399(14, 14, 17, false),
|
||||||
[RK3399_PD_GPU] = DOMAIN_RK3399(15, 15, 0),
|
[RK3399_PD_GPU] = DOMAIN_RK3399(15, 15, 0, false),
|
||||||
[RK3399_PD_VCODEC] = DOMAIN_RK3399(16, 16, 3),
|
[RK3399_PD_VCODEC] = DOMAIN_RK3399(16, 16, 3, false),
|
||||||
[RK3399_PD_VDU] = DOMAIN_RK3399(17, 17, 4),
|
[RK3399_PD_VDU] = DOMAIN_RK3399(17, 17, 4, false),
|
||||||
[RK3399_PD_RGA] = DOMAIN_RK3399(18, 18, 5),
|
[RK3399_PD_RGA] = DOMAIN_RK3399(18, 18, 5, false),
|
||||||
[RK3399_PD_IEP] = DOMAIN_RK3399(19, 19, 6),
|
[RK3399_PD_IEP] = DOMAIN_RK3399(19, 19, 6, false),
|
||||||
[RK3399_PD_VO] = DOMAIN_RK3399(20, 20, -1),
|
[RK3399_PD_VO] = DOMAIN_RK3399(20, 20, -1, false),
|
||||||
[RK3399_PD_VOPB] = DOMAIN_RK3399(-1, -1, 7),
|
[RK3399_PD_VOPB] = DOMAIN_RK3399(-1, -1, 7, false),
|
||||||
[RK3399_PD_VOPL] = DOMAIN_RK3399(-1, -1, 8),
|
[RK3399_PD_VOPL] = DOMAIN_RK3399(-1, -1, 8, false),
|
||||||
[RK3399_PD_ISP0] = DOMAIN_RK3399(22, 22, 9),
|
[RK3399_PD_ISP0] = DOMAIN_RK3399(22, 22, 9, false),
|
||||||
[RK3399_PD_ISP1] = DOMAIN_RK3399(23, 23, 10),
|
[RK3399_PD_ISP1] = DOMAIN_RK3399(23, 23, 10, false),
|
||||||
[RK3399_PD_HDCP] = DOMAIN_RK3399(24, 24, 11),
|
[RK3399_PD_HDCP] = DOMAIN_RK3399(24, 24, 11, false),
|
||||||
[RK3399_PD_GMAC] = DOMAIN_RK3399(25, 25, 23),
|
[RK3399_PD_GMAC] = DOMAIN_RK3399(25, 25, 23, true),
|
||||||
[RK3399_PD_EMMC] = DOMAIN_RK3399(26, 26, 24),
|
[RK3399_PD_EMMC] = DOMAIN_RK3399(26, 26, 24, true),
|
||||||
[RK3399_PD_USB3] = DOMAIN_RK3399(27, 27, 12),
|
[RK3399_PD_USB3] = DOMAIN_RK3399(27, 27, 12, true),
|
||||||
[RK3399_PD_EDP] = DOMAIN_RK3399(28, 28, 22),
|
[RK3399_PD_EDP] = DOMAIN_RK3399(28, 28, 22, false),
|
||||||
[RK3399_PD_GIC] = DOMAIN_RK3399(29, 29, 27),
|
[RK3399_PD_GIC] = DOMAIN_RK3399(29, 29, 27, true),
|
||||||
[RK3399_PD_SD] = DOMAIN_RK3399(30, 30, 28),
|
[RK3399_PD_SD] = DOMAIN_RK3399(30, 30, 28, true),
|
||||||
[RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399(31, 31, 29),
|
[RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399(31, 31, 29, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct rockchip_pmu_info rk3288_pmu = {
|
static const struct rockchip_pmu_info rk3288_pmu = {
|
||||||
|
|
|
@ -967,8 +967,8 @@ static void tegra_io_rail_unprepare(void)
|
||||||
|
|
||||||
int tegra_io_rail_power_on(unsigned int id)
|
int tegra_io_rail_power_on(unsigned int id)
|
||||||
{
|
{
|
||||||
unsigned long request, status, value;
|
unsigned long request, status;
|
||||||
unsigned int bit, mask;
|
unsigned int bit;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
mutex_lock(&pmc->powergates_lock);
|
mutex_lock(&pmc->powergates_lock);
|
||||||
|
@ -977,15 +977,9 @@ int tegra_io_rail_power_on(unsigned int id)
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
mask = 1 << bit;
|
tegra_pmc_writel(IO_DPD_REQ_CODE_OFF | BIT(bit), request);
|
||||||
|
|
||||||
value = tegra_pmc_readl(request);
|
err = tegra_io_rail_poll(status, BIT(bit), 0, 250);
|
||||||
value |= mask;
|
|
||||||
value &= ~IO_DPD_REQ_CODE_MASK;
|
|
||||||
value |= IO_DPD_REQ_CODE_OFF;
|
|
||||||
tegra_pmc_writel(value, request);
|
|
||||||
|
|
||||||
err = tegra_io_rail_poll(status, mask, 0, 250);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_info("tegra_io_rail_poll() failed: %d\n", err);
|
pr_info("tegra_io_rail_poll() failed: %d\n", err);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1002,8 +996,8 @@ EXPORT_SYMBOL(tegra_io_rail_power_on);
|
||||||
|
|
||||||
int tegra_io_rail_power_off(unsigned int id)
|
int tegra_io_rail_power_off(unsigned int id)
|
||||||
{
|
{
|
||||||
unsigned long request, status, value;
|
unsigned long request, status;
|
||||||
unsigned int bit, mask;
|
unsigned int bit;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
mutex_lock(&pmc->powergates_lock);
|
mutex_lock(&pmc->powergates_lock);
|
||||||
|
@ -1014,15 +1008,9 @@ int tegra_io_rail_power_off(unsigned int id)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
mask = 1 << bit;
|
tegra_pmc_writel(IO_DPD_REQ_CODE_ON | BIT(bit), request);
|
||||||
|
|
||||||
value = tegra_pmc_readl(request);
|
err = tegra_io_rail_poll(status, BIT(bit), BIT(bit), 250);
|
||||||
value |= mask;
|
|
||||||
value &= ~IO_DPD_REQ_CODE_MASK;
|
|
||||||
value |= IO_DPD_REQ_CODE_ON;
|
|
||||||
tegra_pmc_writel(value, request);
|
|
||||||
|
|
||||||
err = tegra_io_rail_poll(status, mask, mask, 250);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
98
include/dt-bindings/mfd/stm32f4-rcc.h
Normal file
98
include/dt-bindings/mfd/stm32f4-rcc.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* This header provides constants for the STM32F4 RCC IP
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DT_BINDINGS_MFD_STM32F4_RCC_H
|
||||||
|
#define _DT_BINDINGS_MFD_STM32F4_RCC_H
|
||||||
|
|
||||||
|
/* AHB1 */
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOA 0
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOB 1
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOC 2
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOD 3
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOE 4
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOF 5
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOG 6
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOH 7
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOI 8
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOJ 9
|
||||||
|
#define STM32F4_RCC_AHB1_GPIOK 10
|
||||||
|
#define STM32F4_RCC_AHB1_CRC 12
|
||||||
|
#define STM32F4_RCC_AHB1_DMA1 21
|
||||||
|
#define STM32F4_RCC_AHB1_DMA2 22
|
||||||
|
#define STM32F4_RCC_AHB1_DMA2D 23
|
||||||
|
#define STM32F4_RCC_AHB1_ETHMAC 25
|
||||||
|
#define STM32F4_RCC_AHB1_OTGHS 29
|
||||||
|
|
||||||
|
#define STM32F4_AHB1_RESET(bit) (STM32F4_RCC_AHB1_##bit + (0x10 * 8))
|
||||||
|
#define STM32F4_AHB1_CLOCK(bit) (STM32F4_RCC_AHB1_##bit + (0x30 * 8))
|
||||||
|
|
||||||
|
|
||||||
|
/* AHB2 */
|
||||||
|
#define STM32F4_RCC_AHB2_DCMI 0
|
||||||
|
#define STM32F4_RCC_AHB2_CRYP 4
|
||||||
|
#define STM32F4_RCC_AHB2_HASH 5
|
||||||
|
#define STM32F4_RCC_AHB2_RNG 6
|
||||||
|
#define STM32F4_RCC_AHB2_OTGFS 7
|
||||||
|
|
||||||
|
#define STM32F4_AHB2_RESET(bit) (STM32F4_RCC_AHB2_##bit + (0x14 * 8))
|
||||||
|
#define STM32F4_AHB2_CLOCK(bit) (STM32F4_RCC_AHB2_##bit + (0x34 * 8))
|
||||||
|
|
||||||
|
/* AHB3 */
|
||||||
|
#define STM32F4_RCC_AHB3_FMC 0
|
||||||
|
|
||||||
|
#define STM32F4_AHB3_RESET(bit) (STM32F4_RCC_AHB3_##bit + (0x18 * 8))
|
||||||
|
#define STM32F4_AHB3_CLOCK(bit) (STM32F4_RCC_AHB3_##bit + (0x38 * 8))
|
||||||
|
|
||||||
|
/* APB1 */
|
||||||
|
#define STM32F4_RCC_APB1_TIM2 0
|
||||||
|
#define STM32F4_RCC_APB1_TIM3 1
|
||||||
|
#define STM32F4_RCC_APB1_TIM4 2
|
||||||
|
#define STM32F4_RCC_APB1_TIM5 3
|
||||||
|
#define STM32F4_RCC_APB1_TIM6 4
|
||||||
|
#define STM32F4_RCC_APB1_TIM7 5
|
||||||
|
#define STM32F4_RCC_APB1_TIM12 6
|
||||||
|
#define STM32F4_RCC_APB1_TIM13 7
|
||||||
|
#define STM32F4_RCC_APB1_TIM14 8
|
||||||
|
#define STM32F4_RCC_APB1_WWDG 11
|
||||||
|
#define STM32F4_RCC_APB1_SPI2 14
|
||||||
|
#define STM32F4_RCC_APB1_SPI3 15
|
||||||
|
#define STM32F4_RCC_APB1_UART2 17
|
||||||
|
#define STM32F4_RCC_APB1_UART3 18
|
||||||
|
#define STM32F4_RCC_APB1_UART4 19
|
||||||
|
#define STM32F4_RCC_APB1_UART5 20
|
||||||
|
#define STM32F4_RCC_APB1_I2C1 21
|
||||||
|
#define STM32F4_RCC_APB1_I2C2 22
|
||||||
|
#define STM32F4_RCC_APB1_I2C3 23
|
||||||
|
#define STM32F4_RCC_APB1_CAN1 25
|
||||||
|
#define STM32F4_RCC_APB1_CAN2 26
|
||||||
|
#define STM32F4_RCC_APB1_PWR 28
|
||||||
|
#define STM32F4_RCC_APB1_DAC 29
|
||||||
|
#define STM32F4_RCC_APB1_UART7 30
|
||||||
|
#define STM32F4_RCC_APB1_UART8 31
|
||||||
|
|
||||||
|
#define STM32F4_APB1_RESET(bit) (STM32F4_RCC_APB1_##bit + (0x20 * 8))
|
||||||
|
#define STM32F4_APB1_CLOCK(bit) (STM32F4_RCC_APB1_##bit + (0x40 * 8))
|
||||||
|
|
||||||
|
/* APB2 */
|
||||||
|
#define STM32F4_RCC_APB2_TIM1 0
|
||||||
|
#define STM32F4_RCC_APB2_TIM8 1
|
||||||
|
#define STM32F4_RCC_APB2_USART1 4
|
||||||
|
#define STM32F4_RCC_APB2_USART6 5
|
||||||
|
#define STM32F4_RCC_APB2_ADC 8
|
||||||
|
#define STM32F4_RCC_APB2_SDIO 11
|
||||||
|
#define STM32F4_RCC_APB2_SPI1 12
|
||||||
|
#define STM32F4_RCC_APB2_SPI4 13
|
||||||
|
#define STM32F4_RCC_APB2_SYSCFG 14
|
||||||
|
#define STM32F4_RCC_APB2_TIM9 16
|
||||||
|
#define STM32F4_RCC_APB2_TIM10 17
|
||||||
|
#define STM32F4_RCC_APB2_TIM11 18
|
||||||
|
#define STM32F4_RCC_APB2_SPI5 20
|
||||||
|
#define STM32F4_RCC_APB2_SPI6 21
|
||||||
|
#define STM32F4_RCC_APB2_SAI1 22
|
||||||
|
#define STM32F4_RCC_APB2_LTDC 26
|
||||||
|
|
||||||
|
#define STM32F4_APB2_RESET(bit) (STM32F4_RCC_APB2_##bit + (0x24 * 8))
|
||||||
|
#define STM32F4_APB2_CLOCK(bit) (STM32F4_RCC_APB2_##bit + (0x44 * 8))
|
||||||
|
|
||||||
|
#endif /* _DT_BINDINGS_MFD_STM32F4_RCC_H */
|
31
include/linux/firmware/meson/meson_sm.h
Normal file
31
include/linux/firmware/meson/meson_sm.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Endless Mobile, Inc.
|
||||||
|
* Author: Carlo Caione <carlo@endlessm.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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MESON_SM_FW_H_
|
||||||
|
#define _MESON_SM_FW_H_
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SM_EFUSE_READ,
|
||||||
|
SM_EFUSE_WRITE,
|
||||||
|
SM_EFUSE_USER_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct meson_sm_firmware;
|
||||||
|
|
||||||
|
int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, u32 arg1,
|
||||||
|
u32 arg2, u32 arg3, u32 arg4);
|
||||||
|
int meson_sm_call_write(void *buffer, unsigned int b_size, unsigned int cmd_index,
|
||||||
|
u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4);
|
||||||
|
int meson_sm_call_read(void *buffer, unsigned int cmd_index, u32 arg0, u32 arg1,
|
||||||
|
u32 arg2, u32 arg3, u32 arg4);
|
||||||
|
|
||||||
|
#endif /* _MESON_SM_FW_H_ */
|
|
@ -29,7 +29,7 @@ struct gpmc_nand_regs;
|
||||||
struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
|
struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
|
||||||
int cs);
|
int cs);
|
||||||
#else
|
#else
|
||||||
static inline gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
|
static inline struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs,
|
||||||
int cs)
|
int cs)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -55,11 +55,16 @@ void qcom_smd_driver_unregister(struct qcom_smd_driver *drv);
|
||||||
struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *channel,
|
struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *channel,
|
||||||
const char *name,
|
const char *name,
|
||||||
qcom_smd_cb_t cb);
|
qcom_smd_cb_t cb);
|
||||||
|
void qcom_smd_close_channel(struct qcom_smd_channel *channel);
|
||||||
void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel);
|
void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel);
|
||||||
void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data);
|
void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data);
|
||||||
int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
|
int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
|
||||||
|
|
||||||
|
|
||||||
|
struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
|
||||||
|
struct device_node *node);
|
||||||
|
int qcom_smd_unregister_edge(struct qcom_smd_edge *edge);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv)
|
static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv)
|
||||||
|
@ -83,14 +88,20 @@ qcom_smd_open_channel(struct qcom_smd_channel *channel,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel)
|
static inline void qcom_smd_close_channel(struct qcom_smd_channel *channel)
|
||||||
|
{
|
||||||
|
/* This shouldn't be possible */
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel)
|
||||||
{
|
{
|
||||||
/* This shouldn't be possible */
|
/* This shouldn't be possible */
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data)
|
static inline void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data)
|
||||||
{
|
{
|
||||||
/* This shouldn't be possible */
|
/* This shouldn't be possible */
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
|
@ -104,6 +115,20 @@ static inline int qcom_smd_send(struct qcom_smd_channel *channel,
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct qcom_smd_edge *
|
||||||
|
qcom_smd_register_edge(struct device *parent,
|
||||||
|
struct device_node *node)
|
||||||
|
{
|
||||||
|
return ERR_PTR(-ENXIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
|
||||||
|
{
|
||||||
|
/* This shouldn't be possible */
|
||||||
|
WARN_ON(1);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define module_qcom_smd_driver(__smd_driver) \
|
#define module_qcom_smd_driver(__smd_driver) \
|
||||||
|
|
Loading…
Add table
Reference in a new issue