forked from Minki/linux
ARM: SoC driver updates for v5.4
The branch contains driver changes that are tightly connected to SoC specific code. Aside from smaller cleanups and bug fixes, here is a list of the notable changes. New device drivers: - The Turris Mox router has a new "moxtet" bus driver for its on-board pluggable extension bus. The same platform also gains a firmware driver. - The Samsung Exynos family gains a new Chipid driver exporting using the soc device sysfs interface - A similar socinfo driver for Qualcomm Snapdragon chips. - A firmware driver for the NXP i.MX DSP IPC protocol using shared memory and a mailbox Other changes: - The i.MX reset controller driver now supports the NXP i.MX8MM chip - Amlogic SoC specific drivers gain support for the S905X3 and A311D chips - A rework of the TI Davinci framebuffer driver to allow important cleanups in the platform code - A couple of device drivers for removed ARM SoC platforms are removed. Most of the removals were picked up by other maintainers, this contains whatever was left. Signed-off-by: Arnd Bergmann <arnd@arndb.de> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJdf6SUAAoJEJpsee/mABjZAfwP/01bXBOlGVusNH2zuh8IUSHb //5sTdWpwa2ugRekLOJUOjo2p9Fu70yH6xr4RUHI0rcRjZA0xR3bZPx45gI8LRHQ tfb25LaKqfgZjWMCJ8due1Lh7B6ffOQukryMtM/LoiCtqsy7b6aThEKaLpM9/Owl t53o4wKaVQJK5He9JQom9NOZidkl7tYLHmDQTOXhX2UEA/i45vtfjdsEBvoFPbTx +bYvlqs+SWlpDJk29j+oBOeKadPF+TFboLDiUCxH44MC3OsH51zjtKVBRTtbNMkb ek/ci5x9hCeHcYSEigNq2EMzEln09Yxyvjk8U/jLiJ1h1kz3p5MjqJbVMF1rYXpe ALuAwinM8Zv2o5/UOCkiQTWq79PtpOKHZKpNBXkaJ8kyqBLMSy8Fs3hCvXrDnjnQ TC8jX7UBqHRV2rbQIYehAQAxTvcRgTbqusQGLkUJInlux6go57LoMYHPABpHftJV kRdVeT0KzdCz1pvQwyekIog5hPLNTBi4jw6eQcOgeENvAea1MJa8lMMfKcVbIdS0 ZVvxLl+K6noEKAv5lSeHAzjXq+cQFr3zDCsWy351mJETDHmE8zjsaHN1SgbRYLEk ZqzNwUYaPYBis38g85qaY/TSsJrWJ+jP8u7s9HTw3Oywg8SRy5vtW177s00/9VOd PYZ2UpqUeX8cdvggqUUU =lxFi -----END PGP SIGNATURE----- Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc Pull ARM SoC driver updates from Arnd Bergmann: "This contains driver changes that are tightly connected to SoC specific code. Aside from smaller cleanups and bug fixes, here is a list of the notable changes. New device drivers: - The Turris Mox router has a new "moxtet" bus driver for its on-board pluggable extension bus. The same platform also gains a firmware driver. - The Samsung Exynos family gains a new Chipid driver exporting using the soc device sysfs interface - A similar socinfo driver for Qualcomm Snapdragon chips. - A firmware driver for the NXP i.MX DSP IPC protocol using shared memory and a mailbox Other changes: - The i.MX reset controller driver now supports the NXP i.MX8MM chip - Amlogic SoC specific drivers gain support for the S905X3 and A311D chips - A rework of the TI Davinci framebuffer driver to allow important cleanups in the platform code - A couple of device drivers for removed ARM SoC platforms are removed. Most of the removals were picked up by other maintainers, this contains whatever was left" * tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (123 commits) bus: uniphier-system-bus: use devm_platform_ioremap_resource() soc: ti: ti_sci_pm_domains: Add support for exclusive and shared access dt-bindings: ti_sci_pm_domains: Add support for exclusive and shared access firmware: ti_sci: Allow for device shared and exclusive requests bus: imx-weim: remove incorrect __init annotations fbdev: remove w90x900/nuc900 platform drivers spi: remove w90x900 driver net: remove w90p910-ether driver net: remove ks8695 driver firmware: turris-mox-rwtm: Add sysfs documentation firmware: Add Turris Mox rWTM firmware driver dt-bindings: firmware: Document cznic,turris-mox-rwtm binding bus: moxtet: fix unsigned comparison to less than zero bus: moxtet: remove set but not used variable 'dummy' ARM: scoop: Use the right include dt-bindings: power: add Amlogic Everything-Else power domains bindings soc: amlogic: Add support for Everything-Else power domains controller fbdev: da8xx: use resource management for dma fbdev: da8xx-fb: drop a redundant if fbdev: da8xx-fb: use devm_platform_ioremap_resource() ...
This commit is contained in:
commit
399eb9b6cb
23
Documentation/ABI/testing/debugfs-moxtet
Normal file
23
Documentation/ABI/testing/debugfs-moxtet
Normal file
@ -0,0 +1,23 @@
|
||||
What: /sys/kernel/debug/moxtet/input
|
||||
Date: March 2019
|
||||
KernelVersion: 5.3
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) Read input from the shift registers, in hexadecimal.
|
||||
Returns N+1 bytes, where N is the number of Moxtet connected
|
||||
modules. The first byte is from the CPU board itself.
|
||||
Example: 101214
|
||||
10: CPU board with SD card
|
||||
12: 2 = PCIe module, 1 = IRQ not active
|
||||
14: 4 = Peridot module, 1 = IRQ not active
|
||||
|
||||
What: /sys/kernel/debug/moxtet/output
|
||||
Date: March 2019
|
||||
KernelVersion: 5.3
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (RW) Read last written value to the shift registers, in
|
||||
hexadecimal, or write values to the shift registers, also
|
||||
in hexadecimal.
|
||||
Example: 0102
|
||||
01: 01 was last written, or is to be written, to the
|
||||
first module's shift register
|
||||
02: the same for second module
|
17
Documentation/ABI/testing/sysfs-bus-moxtet-devices
Normal file
17
Documentation/ABI/testing/sysfs-bus-moxtet-devices
Normal file
@ -0,0 +1,17 @@
|
||||
What: /sys/bus/moxtet/devices/moxtet-<name>.<addr>/module_description
|
||||
Date: March 2019
|
||||
KernelVersion: 5.3
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) Moxtet module description. Format: string
|
||||
|
||||
What: /sys/bus/moxtet/devices/moxtet-<name>.<addr>/module_id
|
||||
Date: March 2019
|
||||
KernelVersion: 5.3
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) Moxtet module ID. Format: %x
|
||||
|
||||
What: /sys/bus/moxtet/devices/moxtet-<name>.<addr>/module_name
|
||||
Date: March 2019
|
||||
KernelVersion: 5.3
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) Moxtet module name. Format: string
|
@ -26,6 +26,13 @@ Description:
|
||||
Read-only attribute common to all SoCs. Contains SoC family name
|
||||
(e.g. DB8500).
|
||||
|
||||
What: /sys/devices/socX/serial_number
|
||||
Date: January 2019
|
||||
contact: Bjorn Andersson <bjorn.andersson@linaro.org>
|
||||
Description:
|
||||
Read-only attribute supported by most SoCs. Contains the SoC's
|
||||
serial number, if available.
|
||||
|
||||
What: /sys/devices/socX/soc_id
|
||||
Date: January 2012
|
||||
contact: Lee Jones <lee.jones@linaro.org>
|
||||
|
37
Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm
Normal file
37
Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm
Normal file
@ -0,0 +1,37 @@
|
||||
What: /sys/firmware/turris-mox-rwtm/board_version
|
||||
Date: August 2019
|
||||
KernelVersion: 5.4
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) Board version burned into eFuses of this Turris Mox board.
|
||||
Format: %i
|
||||
|
||||
What: /sys/firmware/turris-mox-rwtm/mac_address*
|
||||
Date: August 2019
|
||||
KernelVersion: 5.4
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) MAC addresses burned into eFuses of this Turris Mox board.
|
||||
Format: %pM
|
||||
|
||||
What: /sys/firmware/turris-mox-rwtm/pubkey
|
||||
Date: August 2019
|
||||
KernelVersion: 5.4
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) ECDSA public key (in pubkey hex compressed form) computed
|
||||
as pair to the ECDSA private key burned into eFuses of this
|
||||
Turris Mox Board.
|
||||
Format: string
|
||||
|
||||
What: /sys/firmware/turris-mox-rwtm/ram_size
|
||||
Date: August 2019
|
||||
KernelVersion: 5.4
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) RAM size in MiB of this Turris Mox board as was detected
|
||||
during manufacturing and burned into eFuses. Can be 512 or 1024.
|
||||
Format: %i
|
||||
|
||||
What: /sys/firmware/turris-mox-rwtm/serial_number
|
||||
Date: August 2019
|
||||
KernelVersion: 5.4
|
||||
Contact: Marek Behún <marek.behun@nic.cz>
|
||||
Description: (R) Serial number burned into eFuses of this Turris Mox device.
|
||||
Format: %016X
|
@ -73,6 +73,16 @@ Required properties:
|
||||
as used by the firmware. Refer to platform details
|
||||
for your implementation for the IDs to use.
|
||||
|
||||
Reset signal bindings for the reset domains based on SCMI Message Protocol
|
||||
------------------------------------------------------------
|
||||
|
||||
This binding for the SCMI reset domain providers uses the generic reset
|
||||
signal binding[5].
|
||||
|
||||
Required properties:
|
||||
- #reset-cells : Should be 1. Contains the reset domain ID value used
|
||||
by SCMI commands.
|
||||
|
||||
SRAM and Shared Memory for SCMI
|
||||
-------------------------------
|
||||
|
||||
@ -93,6 +103,7 @@ Required sub-node properties:
|
||||
[2] Documentation/devicetree/bindings/power/power_domain.txt
|
||||
[3] Documentation/devicetree/bindings/thermal/thermal.txt
|
||||
[4] Documentation/devicetree/bindings/sram/sram.txt
|
||||
[5] Documentation/devicetree/bindings/reset/reset.txt
|
||||
|
||||
Example:
|
||||
|
||||
@ -152,6 +163,11 @@ firmware {
|
||||
reg = <0x15>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
scmi_reset: protocol@16 {
|
||||
reg = <0x16>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@ -166,6 +182,7 @@ hdlcd@7ff60000 {
|
||||
reg = <0 0x7ff60000 0 0x1000>;
|
||||
clocks = <&scmi_clk 4>;
|
||||
power-domains = <&scmi_devpd 1>;
|
||||
resets = <&scmi_reset 10>;
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
|
46
Documentation/devicetree/bindings/bus/moxtet.txt
Normal file
46
Documentation/devicetree/bindings/bus/moxtet.txt
Normal file
@ -0,0 +1,46 @@
|
||||
Turris Mox module status and configuration bus (over SPI)
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "cznic,moxtet"
|
||||
- #address-cells : Has to be 1
|
||||
- #size-cells : Has to be 0
|
||||
- spi-cpol : Required inverted clock polarity
|
||||
- spi-cpha : Required shifted clock phase
|
||||
- interrupts : Must contain reference to the shared interrupt line
|
||||
- interrupt-controller : Required
|
||||
- #interrupt-cells : Has to be 1
|
||||
|
||||
For other required and optional properties of SPI slave nodes please refer to
|
||||
../spi/spi-bus.txt.
|
||||
|
||||
Required properties of subnodes:
|
||||
- reg : Should be position on the Moxtet bus (how many Moxtet
|
||||
modules are between this module and CPU module, so
|
||||
either 0 or a positive integer)
|
||||
|
||||
The driver finds the devices connected to the bus by itself, but it may be
|
||||
needed to reference some of them from other parts of the device tree. In that
|
||||
case the devices can be defined as subnodes of the moxtet node.
|
||||
|
||||
Example:
|
||||
|
||||
moxtet@1 {
|
||||
compatible = "cznic,moxtet";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <1>;
|
||||
spi-max-frequency = <10000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-parent = <&gpiosb>;
|
||||
interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
|
||||
|
||||
moxtet_sfp: gpio@0 {
|
||||
compatible = "cznic,moxtet-gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
reg = <0>;
|
||||
}
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
Turris Mox rWTM firmware driver
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "cznic,turris-mox-rwtm"
|
||||
- mboxes : Must contain a reference to associated mailbox
|
||||
|
||||
This device tree node should be used on Turris Mox, or potentially another A3700
|
||||
compatible device running the Mox's rWTM firmware in the secure processor (for
|
||||
example it is possible to flash this firmware into EspressoBin).
|
||||
|
||||
Example:
|
||||
|
||||
firmware {
|
||||
turris-mox-rwtm {
|
||||
compatible = "cznic,turris-mox-rwtm";
|
||||
mboxes = <&rwtm 0>;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
@ -9,14 +9,16 @@ Required properties:
|
||||
- compatible: must contain one of the following:
|
||||
* "qcom,scm-apq8064"
|
||||
* "qcom,scm-apq8084"
|
||||
* "qcom,scm-ipq4019"
|
||||
* "qcom,scm-msm8660"
|
||||
* "qcom,scm-msm8916"
|
||||
* "qcom,scm-msm8960"
|
||||
* "qcom,scm-msm8974"
|
||||
* "qcom,scm-msm8996"
|
||||
* "qcom,scm-msm8998"
|
||||
* "qcom,scm-ipq4019"
|
||||
* "qcom,scm-sc7180"
|
||||
* "qcom,scm-sdm845"
|
||||
* "qcom,scm-sm8150"
|
||||
and:
|
||||
* "qcom,scm"
|
||||
- clocks: Specifies clocks needed by the SCM interface, if any:
|
||||
|
18
Documentation/devicetree/bindings/gpio/gpio-moxtet.txt
Normal file
18
Documentation/devicetree/bindings/gpio/gpio-moxtet.txt
Normal file
@ -0,0 +1,18 @@
|
||||
Turris Mox Moxtet GPIO expander via Moxtet bus
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "cznic,moxtet-gpio".
|
||||
- gpio-controller : Marks the device node as a GPIO controller.
|
||||
- #gpio-cells : Should be two. For consumer use see gpio.txt.
|
||||
|
||||
Other properties are required for a Moxtet bus device, please refer to
|
||||
Documentation/devicetree/bindings/bus/moxtet.txt.
|
||||
|
||||
Example:
|
||||
|
||||
moxtet_sfp: gpio@0 {
|
||||
compatible = "cznic,moxtet-gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
reg = <0>;
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2019 BayLibre, SAS
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/power/amlogic,meson-ee-pwrc.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Amlogic Meson Everything-Else Power Domains
|
||||
|
||||
maintainers:
|
||||
- Neil Armstrong <narmstrong@baylibre.com>
|
||||
|
||||
description: |+
|
||||
The Everything-Else Power Domains node should be the child of a syscon
|
||||
node with the required property:
|
||||
|
||||
- compatible: Should be the following:
|
||||
"amlogic,meson-gx-hhi-sysctrl", "simple-mfd", "syscon"
|
||||
|
||||
Refer to the the bindings described in
|
||||
Documentation/devicetree/bindings/mfd/syscon.txt
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-g12a-pwrc
|
||||
- amlogic,meson-sm1-pwrc
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: vpu
|
||||
- const: vapb
|
||||
|
||||
resets:
|
||||
minItems: 11
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: viu
|
||||
- const: venc
|
||||
- const: vcbus
|
||||
- const: bt656
|
||||
- const: rdma
|
||||
- const: venci
|
||||
- const: vencp
|
||||
- const: vdac
|
||||
- const: vdi6
|
||||
- const: vencl
|
||||
- const: vid_lock
|
||||
|
||||
"#power-domain-cells":
|
||||
const: 1
|
||||
|
||||
amlogic,ao-sysctrl:
|
||||
description: phandle to the AO sysctrl node
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
- "#power-domain-cells"
|
||||
- amlogic,ao-sysctrl
|
||||
|
||||
examples:
|
||||
- |
|
||||
pwrc: power-controller {
|
||||
compatible = "amlogic,meson-sm1-pwrc";
|
||||
#power-domain-cells = <1>;
|
||||
amlogic,ao-sysctrl = <&rti>;
|
||||
resets = <&reset_viu>,
|
||||
<&reset_venc>,
|
||||
<&reset_vcbus>,
|
||||
<&reset_bt656>,
|
||||
<&reset_rdma>,
|
||||
<&reset_venci>,
|
||||
<&reset_vencp>,
|
||||
<&reset_vdac>,
|
||||
<&reset_vdi6>,
|
||||
<&reset_vencl>,
|
||||
<&reset_vid_lock>;
|
||||
reset-names = "viu", "venc", "vcbus", "bt656",
|
||||
"rdma", "venci", "vencp", "vdac",
|
||||
"vdi6", "vencl", "vid_lock";
|
||||
clocks = <&clk_vpu>, <&clk_vapb>;
|
||||
clock-names = "vpu", "vapb";
|
||||
};
|
@ -8,6 +8,7 @@ Required properties:
|
||||
- compatible:
|
||||
- For i.MX7 SoCs should be "fsl,imx7d-src", "syscon"
|
||||
- For i.MX8MQ SoCs should be "fsl,imx8mq-src", "syscon"
|
||||
- For i.MX8MM SoCs should be "fsl,imx8mm-src", "fsl,imx8mq-src", "syscon"
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- interrupts: Should contain SRC interrupt
|
||||
@ -46,5 +47,6 @@ Example:
|
||||
|
||||
|
||||
For list of all valid reset indices see
|
||||
<dt-bindings/reset/imx7-reset.h> for i.MX7 and
|
||||
<dt-bindings/reset/imx8mq-reset.h> for i.MX8MQ
|
||||
<dt-bindings/reset/imx7-reset.h> for i.MX7,
|
||||
<dt-bindings/reset/imx8mq-reset.h> for i.MX8MQ and
|
||||
<dt-bindings/reset/imx8mq-reset.h> for i.MX8MM
|
||||
|
30
Documentation/devicetree/bindings/reset/snps,dw-reset.txt
Normal file
30
Documentation/devicetree/bindings/reset/snps,dw-reset.txt
Normal file
@ -0,0 +1,30 @@
|
||||
Synopsys DesignWare Reset controller
|
||||
=======================================
|
||||
|
||||
Please also refer to reset.txt in this directory for common reset
|
||||
controller binding usage.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be one of the following.
|
||||
"snps,dw-high-reset" - for active high configuration
|
||||
"snps,dw-low-reset" - for active low configuration
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
|
||||
- #reset-cells: must be 1.
|
||||
|
||||
example:
|
||||
|
||||
dw_rst_1: reset-controller@0000 {
|
||||
compatible = "snps,dw-high-reset";
|
||||
reg = <0x0000 0x4>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
dw_rst_2: reset-controller@1000 {i
|
||||
compatible = "snps,dw-low-reset";
|
||||
reg = <0x1000 0x8>;
|
||||
#reset-cells = <1>;
|
||||
};
|
@ -11,6 +11,7 @@ Required properties:
|
||||
"amlogic,meson8b-clk-measure" for Meson8b SoCs
|
||||
"amlogic,meson-axg-clk-measure" for AXG SoCs
|
||||
"amlogic,meson-g12a-clk-measure" for G12a SoCs
|
||||
"amlogic,meson-sm1-clk-measure" for SM1 SoCs
|
||||
- reg: base address and size of the Clock Measurer register space.
|
||||
|
||||
Example:
|
||||
|
@ -18,7 +18,8 @@ Required properties:
|
||||
- reg : offset and length of the device registers.
|
||||
- bus-frequency : the clock frequency for QUICC Engine.
|
||||
- fsl,qe-num-riscs: define how many RISC engines the QE has.
|
||||
- fsl,qe-num-snums: define how many serial number(SNUM) the QE can use for the
|
||||
- fsl,qe-snums: This property has to be specified as '/bits/ 8' value,
|
||||
defining the array of serial number (SNUM) values for the virtual
|
||||
threads.
|
||||
|
||||
Optional properties:
|
||||
@ -34,6 +35,11 @@ Recommended properties
|
||||
- brg-frequency : the internal clock source frequency for baud-rate
|
||||
generators in Hz.
|
||||
|
||||
Deprecated properties
|
||||
- fsl,qe-num-snums: define how many serial number(SNUM) the QE can use
|
||||
for the threads. Use fsl,qe-snums instead to not only specify the
|
||||
number of snums, but also their values.
|
||||
|
||||
Example:
|
||||
qe@e0100000 {
|
||||
#address-cells = <1>;
|
||||
@ -44,6 +50,11 @@ Example:
|
||||
reg = <e0100000 480>;
|
||||
brg-frequency = <0>;
|
||||
bus-frequency = <179A7B00>;
|
||||
fsl,qe-snums = /bits/ 8 <
|
||||
0x04 0x05 0x0C 0x0D 0x14 0x15 0x1C 0x1D
|
||||
0x24 0x25 0x2C 0x2D 0x34 0x35 0x88 0x89
|
||||
0x98 0x99 0xA8 0xA9 0xB8 0xB9 0xC8 0xC9
|
||||
0xD8 0xD9 0xE8 0xE9>;
|
||||
}
|
||||
|
||||
* Multi-User RAM (MURAM)
|
||||
|
@ -15,7 +15,10 @@ power-domains.
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: must be "qcom,sdm845-aoss-qmp"
|
||||
Definition: must be one of:
|
||||
"qcom,sc7180-aoss-qmp"
|
||||
"qcom,sdm845-aoss-qmp"
|
||||
"qcom,sm8150-aoss-qmp"
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
|
@ -19,8 +19,15 @@ child of the pmmc node.
|
||||
Required Properties:
|
||||
--------------------
|
||||
- compatible: should be "ti,sci-pm-domain"
|
||||
- #power-domain-cells: Must be 1 so that an id can be provided in each
|
||||
device node.
|
||||
- #power-domain-cells: Can be one of the following:
|
||||
1: Containing the device id of each node
|
||||
2: First entry should be device id
|
||||
Second entry should be one of the floowing:
|
||||
TI_SCI_PD_EXCLUSIVE: To allow device to be
|
||||
exclusively controlled by
|
||||
the requesting hosts.
|
||||
TI_SCI_PD_SHARED: To allow device to be shared
|
||||
by multiple hosts.
|
||||
|
||||
Example (K2G):
|
||||
-------------
|
||||
|
17
MAINTAINERS
17
MAINTAINERS
@ -1617,6 +1617,21 @@ F: drivers/clocksource/timer-atlas7.c
|
||||
N: [^a-z]sirf
|
||||
X: drivers/gnss
|
||||
|
||||
ARM/CZ.NIC TURRIS MOX SUPPORT
|
||||
M: Marek Behun <marek.behun@nic.cz>
|
||||
W: http://mox.turris.cz
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/debugfs-moxtet
|
||||
F: Documentation/ABI/testing/sysfs-bus-moxtet-devices
|
||||
F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm
|
||||
F: Documentation/devicetree/bindings/bus/moxtet.txt
|
||||
F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt
|
||||
F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt
|
||||
F: include/linux/moxtet.h
|
||||
F: drivers/bus/moxtet.c
|
||||
F: drivers/firmware/turris-mox-rwtm.c
|
||||
F: drivers/gpio/gpio-moxtet.c
|
||||
|
||||
ARM/EBSA110 MACHINE SUPPORT
|
||||
M: Russell King <linux@armlinux.org.uk>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
@ -15530,6 +15545,7 @@ F: drivers/clk/clk-sc[mp]i.c
|
||||
F: drivers/cpufreq/sc[mp]i-cpufreq.c
|
||||
F: drivers/firmware/arm_scpi.c
|
||||
F: drivers/firmware/arm_scmi/
|
||||
F: drivers/reset/reset-scmi.c
|
||||
F: include/linux/sc[mp]i_protocol.h
|
||||
|
||||
SYSTEM RESET/SHUTDOWN DRIVERS
|
||||
@ -15838,6 +15854,7 @@ F: drivers/firmware/ti_sci*
|
||||
F: include/linux/soc/ti/ti_sci_protocol.h
|
||||
F: Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt
|
||||
F: drivers/soc/ti/ti_sci_pm_domains.c
|
||||
F: include/dt-bindings/soc/ti,sci_pm_domain.h
|
||||
F: Documentation/devicetree/bindings/reset/ti,sci-reset.txt
|
||||
F: Documentation/devicetree/bindings/clock/ti,sci-clk.txt
|
||||
F: drivers/clk/keystone/sci-clk.c
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/platform_data/ti-aemif.h>
|
||||
#include <linux/platform_data/spi-davinci.h>
|
||||
#include <linux/platform_data/uio_pruss.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/tps6507x.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
@ -802,38 +803,80 @@ static const short da850_evm_mmcsd0_pins[] __initconst = {
|
||||
-1
|
||||
};
|
||||
|
||||
static void da850_panel_power_ctrl(int val)
|
||||
{
|
||||
/* lcd backlight */
|
||||
gpio_set_value(DA850_LCD_BL_PIN, val);
|
||||
static struct property_entry da850_lcd_backlight_props[] = {
|
||||
PROPERTY_ENTRY_BOOL("default-on"),
|
||||
{ }
|
||||
};
|
||||
|
||||
/* lcd power */
|
||||
gpio_set_value(DA850_LCD_PWR_PIN, val);
|
||||
}
|
||||
static struct gpiod_lookup_table da850_lcd_backlight_gpio_table = {
|
||||
.dev_id = "gpio-backlight",
|
||||
.table = {
|
||||
GPIO_LOOKUP("davinci_gpio", DA850_LCD_BL_PIN, NULL, 0),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static const struct platform_device_info da850_lcd_backlight_info = {
|
||||
.name = "gpio-backlight",
|
||||
.id = PLATFORM_DEVID_NONE,
|
||||
.properties = da850_lcd_backlight_props,
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply da850_lcd_supplies[] = {
|
||||
REGULATOR_SUPPLY("lcd", NULL),
|
||||
};
|
||||
|
||||
static struct regulator_init_data da850_lcd_supply_data = {
|
||||
.consumer_supplies = da850_lcd_supplies,
|
||||
.num_consumer_supplies = ARRAY_SIZE(da850_lcd_supplies),
|
||||
.constraints = {
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
};
|
||||
|
||||
static struct fixed_voltage_config da850_lcd_supply = {
|
||||
.supply_name = "lcd",
|
||||
.microvolts = 33000000,
|
||||
.init_data = &da850_lcd_supply_data,
|
||||
};
|
||||
|
||||
static struct platform_device da850_lcd_supply_device = {
|
||||
.name = "reg-fixed-voltage",
|
||||
.id = 1, /* Dummy fixed regulator is 0 */
|
||||
.dev = {
|
||||
.platform_data = &da850_lcd_supply,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table da850_lcd_supply_gpio_table = {
|
||||
.dev_id = "reg-fixed-voltage.1",
|
||||
.table = {
|
||||
GPIO_LOOKUP("davinci_gpio", DA850_LCD_PWR_PIN, NULL, 0),
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table *da850_lcd_gpio_lookups[] = {
|
||||
&da850_lcd_backlight_gpio_table,
|
||||
&da850_lcd_supply_gpio_table,
|
||||
};
|
||||
|
||||
static int da850_lcd_hw_init(void)
|
||||
{
|
||||
struct platform_device *backlight;
|
||||
int status;
|
||||
|
||||
status = gpio_request(DA850_LCD_BL_PIN, "lcd bl");
|
||||
if (status < 0)
|
||||
gpiod_add_lookup_tables(da850_lcd_gpio_lookups,
|
||||
ARRAY_SIZE(da850_lcd_gpio_lookups));
|
||||
|
||||
backlight = platform_device_register_full(&da850_lcd_backlight_info);
|
||||
if (IS_ERR(backlight))
|
||||
return PTR_ERR(backlight);
|
||||
|
||||
status = platform_device_register(&da850_lcd_supply_device);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = gpio_request(DA850_LCD_PWR_PIN, "lcd pwr");
|
||||
if (status < 0) {
|
||||
gpio_free(DA850_LCD_BL_PIN);
|
||||
return status;
|
||||
}
|
||||
|
||||
gpio_direction_output(DA850_LCD_BL_PIN, 0);
|
||||
gpio_direction_output(DA850_LCD_PWR_PIN, 0);
|
||||
|
||||
/* Switch off panel power and backlight */
|
||||
da850_panel_power_ctrl(0);
|
||||
|
||||
/* Switch on panel power and backlight */
|
||||
da850_panel_power_ctrl(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1443,7 +1486,6 @@ static __init void da850_evm_init(void)
|
||||
if (ret)
|
||||
pr_warn("%s: LCD initialization failed: %d\n", __func__, ret);
|
||||
|
||||
sharp_lk043t1dg01_pdata.panel_power_ctrl = da850_panel_power_ctrl,
|
||||
ret = da8xx_register_lcdc(&sharp_lk043t1dg01_pdata);
|
||||
if (ret)
|
||||
pr_warn("%s: LCDC registration failed: %d\n", __func__, ret);
|
||||
|
@ -33,6 +33,7 @@ static struct bus_type soc_bus_type = {
|
||||
|
||||
static DEVICE_ATTR(machine, S_IRUGO, soc_info_get, NULL);
|
||||
static DEVICE_ATTR(family, S_IRUGO, soc_info_get, NULL);
|
||||
static DEVICE_ATTR(serial_number, S_IRUGO, soc_info_get, NULL);
|
||||
static DEVICE_ATTR(soc_id, S_IRUGO, soc_info_get, NULL);
|
||||
static DEVICE_ATTR(revision, S_IRUGO, soc_info_get, NULL);
|
||||
|
||||
@ -57,6 +58,9 @@ static umode_t soc_attribute_mode(struct kobject *kobj,
|
||||
if ((attr == &dev_attr_revision.attr)
|
||||
&& (soc_dev->attr->revision != NULL))
|
||||
return attr->mode;
|
||||
if ((attr == &dev_attr_serial_number.attr)
|
||||
&& (soc_dev->attr->serial_number != NULL))
|
||||
return attr->mode;
|
||||
if ((attr == &dev_attr_soc_id.attr)
|
||||
&& (soc_dev->attr->soc_id != NULL))
|
||||
return attr->mode;
|
||||
@ -77,6 +81,8 @@ static ssize_t soc_info_get(struct device *dev,
|
||||
return sprintf(buf, "%s\n", soc_dev->attr->family);
|
||||
if (attr == &dev_attr_revision)
|
||||
return sprintf(buf, "%s\n", soc_dev->attr->revision);
|
||||
if (attr == &dev_attr_serial_number)
|
||||
return sprintf(buf, "%s\n", soc_dev->attr->serial_number);
|
||||
if (attr == &dev_attr_soc_id)
|
||||
return sprintf(buf, "%s\n", soc_dev->attr->soc_id);
|
||||
|
||||
@ -87,6 +93,7 @@ static ssize_t soc_info_get(struct device *dev,
|
||||
static struct attribute *soc_attr[] = {
|
||||
&dev_attr_machine.attr,
|
||||
&dev_attr_family.attr,
|
||||
&dev_attr_serial_number.attr,
|
||||
&dev_attr_soc_id.attr,
|
||||
&dev_attr_revision.attr,
|
||||
NULL,
|
||||
@ -157,6 +164,7 @@ out2:
|
||||
out1:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(soc_device_register);
|
||||
|
||||
/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */
|
||||
void soc_device_unregister(struct soc_device *soc_dev)
|
||||
@ -166,6 +174,7 @@ void soc_device_unregister(struct soc_device *soc_dev)
|
||||
device_unregister(&soc_dev->dev);
|
||||
early_soc_dev_attr = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(soc_device_unregister);
|
||||
|
||||
static int __init soc_bus_register(void)
|
||||
{
|
||||
|
@ -29,6 +29,16 @@ config BRCMSTB_GISB_ARB
|
||||
arbiter. This driver provides timeout and target abort error handling
|
||||
and internal bus master decoding.
|
||||
|
||||
config MOXTET
|
||||
tristate "CZ.NIC Turris Mox module configuration bus"
|
||||
depends on SPI_MASTER && OF
|
||||
help
|
||||
Say yes here to add support for the module configuration bus found
|
||||
on CZ.NIC's Turris Mox. This is needed for the ability to discover
|
||||
the order in which the modules are connected and to get/set some of
|
||||
their settings. For example the GPIOs on Mox SFP module are
|
||||
configured through this bus.
|
||||
|
||||
config HISILICON_LPC
|
||||
bool "Support for ISA I/O space on HiSilicon Hip06/7"
|
||||
depends on ARM64 && (ARCH_HISI || COMPILE_TEST)
|
||||
|
@ -8,6 +8,7 @@ obj-$(CONFIG_ARM_CCI) += arm-cci.o
|
||||
|
||||
obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o
|
||||
obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
|
||||
obj-$(CONFIG_MOXTET) += moxtet.o
|
||||
|
||||
# DPAA2 fsl-mc bus
|
||||
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
|
||||
|
@ -330,7 +330,6 @@ void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
|
||||
|
||||
fsl_mc_resource_free(resource);
|
||||
|
||||
device_link_del(mc_adev->consumer_link);
|
||||
mc_adev->consumer_link = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_object_free);
|
||||
|
@ -255,7 +255,6 @@ void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
|
||||
fsl_destroy_mc_io(mc_io);
|
||||
fsl_mc_resource_free(resource);
|
||||
|
||||
device_link_del(dpmcp_dev->consumer_link);
|
||||
dpmcp_dev->consumer_link = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
|
||||
|
@ -19,6 +19,8 @@ struct imx_weim_devtype {
|
||||
unsigned int cs_count;
|
||||
unsigned int cs_regs_count;
|
||||
unsigned int cs_stride;
|
||||
unsigned int wcr_offset;
|
||||
unsigned int wcr_bcm;
|
||||
};
|
||||
|
||||
static const struct imx_weim_devtype imx1_weim_devtype = {
|
||||
@ -37,6 +39,8 @@ static const struct imx_weim_devtype imx50_weim_devtype = {
|
||||
.cs_count = 4,
|
||||
.cs_regs_count = 6,
|
||||
.cs_stride = 0x18,
|
||||
.wcr_offset = 0x90,
|
||||
.wcr_bcm = BIT(0),
|
||||
};
|
||||
|
||||
static const struct imx_weim_devtype imx51_weim_devtype = {
|
||||
@ -72,7 +76,7 @@ static const struct of_device_id weim_id_table[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, weim_id_table);
|
||||
|
||||
static int __init imx_weim_gpr_setup(struct platform_device *pdev)
|
||||
static int imx_weim_gpr_setup(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct property *prop;
|
||||
@ -122,10 +126,10 @@ err:
|
||||
}
|
||||
|
||||
/* Parse and set the timing for this device. */
|
||||
static int __init weim_timing_setup(struct device *dev,
|
||||
struct device_node *np, void __iomem *base,
|
||||
const struct imx_weim_devtype *devtype,
|
||||
struct cs_timing_state *ts)
|
||||
static int weim_timing_setup(struct device *dev,
|
||||
struct device_node *np, void __iomem *base,
|
||||
const struct imx_weim_devtype *devtype,
|
||||
struct cs_timing_state *ts)
|
||||
{
|
||||
u32 cs_idx, value[MAX_CS_REGS_COUNT];
|
||||
int i, ret;
|
||||
@ -183,8 +187,7 @@ static int __init weim_timing_setup(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init weim_parse_dt(struct platform_device *pdev,
|
||||
void __iomem *base)
|
||||
static int weim_parse_dt(struct platform_device *pdev, void __iomem *base)
|
||||
{
|
||||
const struct of_device_id *of_id = of_match_device(weim_id_table,
|
||||
&pdev->dev);
|
||||
@ -192,6 +195,7 @@ static int __init weim_parse_dt(struct platform_device *pdev,
|
||||
struct device_node *child;
|
||||
int ret, have_child = 0;
|
||||
struct cs_timing_state ts = {};
|
||||
u32 reg;
|
||||
|
||||
if (devtype == &imx50_weim_devtype) {
|
||||
ret = imx_weim_gpr_setup(pdev);
|
||||
@ -199,6 +203,17 @@ static int __init weim_parse_dt(struct platform_device *pdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) {
|
||||
if (devtype->wcr_bcm) {
|
||||
reg = readl(base + devtype->wcr_offset);
|
||||
writel(reg | devtype->wcr_bcm,
|
||||
base + devtype->wcr_offset);
|
||||
} else {
|
||||
dev_err(&pdev->dev, "burst clk mode not supported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(pdev->dev.of_node, child) {
|
||||
ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts);
|
||||
if (ret)
|
||||
@ -217,7 +232,7 @@ static int __init weim_parse_dt(struct platform_device *pdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init weim_probe(struct platform_device *pdev)
|
||||
static int weim_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct clk *clk;
|
||||
@ -254,8 +269,9 @@ static struct platform_driver weim_driver = {
|
||||
.name = "imx-weim",
|
||||
.of_match_table = weim_id_table,
|
||||
},
|
||||
.probe = weim_probe,
|
||||
};
|
||||
module_platform_driver_probe(weim_driver, weim_probe);
|
||||
module_platform_driver(weim_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor Inc.");
|
||||
MODULE_DESCRIPTION("i.MX EIM Controller Driver");
|
||||
|
885
drivers/bus/moxtet.c
Normal file
885
drivers/bus/moxtet.c
Normal file
@ -0,0 +1,885 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Turris Mox module configuration bus driver
|
||||
*
|
||||
* Copyright (C) 2019 Marek Behun <marek.behun@nic.cz>
|
||||
*/
|
||||
|
||||
#include <dt-bindings/bus/moxtet.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moxtet.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
/*
|
||||
* @name: module name for sysfs
|
||||
* @hwirq_base: base index for IRQ for this module (-1 if no IRQs)
|
||||
* @nirqs: how many interrupts does the shift register provide
|
||||
* @desc: module description for kernel log
|
||||
*/
|
||||
static const struct {
|
||||
const char *name;
|
||||
int hwirq_base;
|
||||
int nirqs;
|
||||
const char *desc;
|
||||
} mox_module_table[] = {
|
||||
/* do not change order of this array! */
|
||||
{ NULL, 0, 0, NULL },
|
||||
{ "sfp", -1, 0, "MOX D (SFP cage)" },
|
||||
{ "pci", MOXTET_IRQ_PCI, 1, "MOX B (Mini-PCIe)" },
|
||||
{ "topaz", MOXTET_IRQ_TOPAZ, 1, "MOX C (4 port switch)" },
|
||||
{ "peridot", MOXTET_IRQ_PERIDOT(0), 1, "MOX E (8 port switch)" },
|
||||
{ "usb3", MOXTET_IRQ_USB3, 2, "MOX F (USB 3.0)" },
|
||||
{ "pci-bridge", -1, 0, "MOX G (Mini-PCIe bridge)" },
|
||||
};
|
||||
|
||||
static inline bool mox_module_known(unsigned int id)
|
||||
{
|
||||
return id >= TURRIS_MOX_MODULE_FIRST && id <= TURRIS_MOX_MODULE_LAST;
|
||||
}
|
||||
|
||||
static inline const char *mox_module_name(unsigned int id)
|
||||
{
|
||||
if (mox_module_known(id))
|
||||
return mox_module_table[id].name;
|
||||
else
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
#define DEF_MODULE_ATTR(name, fmt, ...) \
|
||||
static ssize_t \
|
||||
module_##name##_show(struct device *dev, struct device_attribute *a, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct moxtet_device *mdev = to_moxtet_device(dev); \
|
||||
return sprintf(buf, (fmt), __VA_ARGS__); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(module_##name)
|
||||
|
||||
DEF_MODULE_ATTR(id, "0x%x\n", mdev->id);
|
||||
DEF_MODULE_ATTR(name, "%s\n", mox_module_name(mdev->id));
|
||||
DEF_MODULE_ATTR(description, "%s\n",
|
||||
mox_module_known(mdev->id) ? mox_module_table[mdev->id].desc
|
||||
: "");
|
||||
|
||||
static struct attribute *moxtet_dev_attrs[] = {
|
||||
&dev_attr_module_id.attr,
|
||||
&dev_attr_module_name.attr,
|
||||
&dev_attr_module_description.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group moxtet_dev_group = {
|
||||
.attrs = moxtet_dev_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *moxtet_dev_groups[] = {
|
||||
&moxtet_dev_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int moxtet_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct moxtet_device *mdev = to_moxtet_device(dev);
|
||||
struct moxtet_driver *tdrv = to_moxtet_driver(drv);
|
||||
const enum turris_mox_module_id *t;
|
||||
|
||||
if (of_driver_match_device(dev, drv))
|
||||
return 1;
|
||||
|
||||
if (!tdrv->id_table)
|
||||
return 0;
|
||||
|
||||
for (t = tdrv->id_table; *t; ++t)
|
||||
if (*t == mdev->id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bus_type moxtet_bus_type = {
|
||||
.name = "moxtet",
|
||||
.dev_groups = moxtet_dev_groups,
|
||||
.match = moxtet_match,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(moxtet_bus_type);
|
||||
|
||||
int __moxtet_register_driver(struct module *owner,
|
||||
struct moxtet_driver *mdrv)
|
||||
{
|
||||
mdrv->driver.owner = owner;
|
||||
mdrv->driver.bus = &moxtet_bus_type;
|
||||
return driver_register(&mdrv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__moxtet_register_driver);
|
||||
|
||||
static int moxtet_dev_check(struct device *dev, void *data)
|
||||
{
|
||||
struct moxtet_device *mdev = to_moxtet_device(dev);
|
||||
struct moxtet_device *new_dev = data;
|
||||
|
||||
if (mdev->moxtet == new_dev->moxtet && mdev->id == new_dev->id &&
|
||||
mdev->idx == new_dev->idx)
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void moxtet_dev_release(struct device *dev)
|
||||
{
|
||||
struct moxtet_device *mdev = to_moxtet_device(dev);
|
||||
|
||||
put_device(mdev->moxtet->dev);
|
||||
kfree(mdev);
|
||||
}
|
||||
|
||||
static struct moxtet_device *
|
||||
moxtet_alloc_device(struct moxtet *moxtet)
|
||||
{
|
||||
struct moxtet_device *dev;
|
||||
|
||||
if (!get_device(moxtet->dev))
|
||||
return NULL;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
put_device(moxtet->dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->moxtet = moxtet;
|
||||
dev->dev.parent = moxtet->dev;
|
||||
dev->dev.bus = &moxtet_bus_type;
|
||||
dev->dev.release = moxtet_dev_release;
|
||||
|
||||
device_initialize(&dev->dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static int moxtet_add_device(struct moxtet_device *dev)
|
||||
{
|
||||
static DEFINE_MUTEX(add_mutex);
|
||||
int ret;
|
||||
|
||||
if (dev->idx >= TURRIS_MOX_MAX_MODULES || dev->id > 0xf)
|
||||
return -EINVAL;
|
||||
|
||||
dev_set_name(&dev->dev, "moxtet-%s.%u", mox_module_name(dev->id),
|
||||
dev->idx);
|
||||
|
||||
mutex_lock(&add_mutex);
|
||||
|
||||
ret = bus_for_each_dev(&moxtet_bus_type, NULL, dev,
|
||||
moxtet_dev_check);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
ret = device_add(&dev->dev);
|
||||
if (ret < 0)
|
||||
dev_err(dev->moxtet->dev, "can't add %s, status %d\n",
|
||||
dev_name(dev->moxtet->dev), ret);
|
||||
|
||||
done:
|
||||
mutex_unlock(&add_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __unregister(struct device *dev, void *null)
|
||||
{
|
||||
if (dev->of_node) {
|
||||
of_node_clear_flag(dev->of_node, OF_POPULATED);
|
||||
of_node_put(dev->of_node);
|
||||
}
|
||||
|
||||
device_unregister(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct moxtet_device *
|
||||
of_register_moxtet_device(struct moxtet *moxtet, struct device_node *nc)
|
||||
{
|
||||
struct moxtet_device *dev;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
dev = moxtet_alloc_device(moxtet);
|
||||
if (!dev) {
|
||||
dev_err(moxtet->dev,
|
||||
"Moxtet device alloc error for %pOF\n", nc);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(nc, "reg", &val);
|
||||
if (ret) {
|
||||
dev_err(moxtet->dev, "%pOF has no valid 'reg' property (%d)\n",
|
||||
nc, ret);
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
dev->idx = val;
|
||||
|
||||
if (dev->idx >= TURRIS_MOX_MAX_MODULES) {
|
||||
dev_err(moxtet->dev, "%pOF Moxtet address 0x%x out of range\n",
|
||||
nc, dev->idx);
|
||||
ret = -EINVAL;
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
dev->id = moxtet->modules[dev->idx];
|
||||
|
||||
if (!dev->id) {
|
||||
dev_err(moxtet->dev, "%pOF Moxtet address 0x%x is empty\n", nc,
|
||||
dev->idx);
|
||||
ret = -ENODEV;
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
of_node_get(nc);
|
||||
dev->dev.of_node = nc;
|
||||
|
||||
ret = moxtet_add_device(dev);
|
||||
if (ret) {
|
||||
dev_err(moxtet->dev,
|
||||
"Moxtet device register error for %pOF\n", nc);
|
||||
of_node_put(nc);
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
||||
err_put:
|
||||
put_device(&dev->dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void of_register_moxtet_devices(struct moxtet *moxtet)
|
||||
{
|
||||
struct moxtet_device *dev;
|
||||
struct device_node *nc;
|
||||
|
||||
if (!moxtet->dev->of_node)
|
||||
return;
|
||||
|
||||
for_each_available_child_of_node(moxtet->dev->of_node, nc) {
|
||||
if (of_node_test_and_set_flag(nc, OF_POPULATED))
|
||||
continue;
|
||||
dev = of_register_moxtet_device(moxtet, nc);
|
||||
if (IS_ERR(dev)) {
|
||||
dev_warn(moxtet->dev,
|
||||
"Failed to create Moxtet device for %pOF\n",
|
||||
nc);
|
||||
of_node_clear_flag(nc, OF_POPULATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
moxtet_register_devices_from_topology(struct moxtet *moxtet)
|
||||
{
|
||||
struct moxtet_device *dev;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < moxtet->count; ++i) {
|
||||
dev = moxtet_alloc_device(moxtet);
|
||||
if (!dev) {
|
||||
dev_err(moxtet->dev, "Moxtet device %u alloc error\n",
|
||||
i);
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->idx = i;
|
||||
dev->id = moxtet->modules[i];
|
||||
|
||||
ret = moxtet_add_device(dev);
|
||||
if (ret && ret != -EBUSY) {
|
||||
put_device(&dev->dev);
|
||||
dev_err(moxtet->dev,
|
||||
"Moxtet device %u register error: %i\n", i,
|
||||
ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @nsame: how many modules with same id are already in moxtet->modules
|
||||
*/
|
||||
static int moxtet_set_irq(struct moxtet *moxtet, int idx, int id, int nsame)
|
||||
{
|
||||
int i, first;
|
||||
struct moxtet_irqpos *pos;
|
||||
|
||||
first = mox_module_table[id].hwirq_base +
|
||||
nsame * mox_module_table[id].nirqs;
|
||||
|
||||
if (first + mox_module_table[id].nirqs > MOXTET_NIRQS)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < mox_module_table[id].nirqs; ++i) {
|
||||
pos = &moxtet->irq.position[first + i];
|
||||
pos->idx = idx;
|
||||
pos->bit = i;
|
||||
moxtet->irq.exists |= BIT(first + i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int moxtet_find_topology(struct moxtet *moxtet)
|
||||
{
|
||||
u8 buf[TURRIS_MOX_MAX_MODULES];
|
||||
int cnts[TURRIS_MOX_MODULE_LAST];
|
||||
int i, ret;
|
||||
|
||||
memset(cnts, 0, sizeof(cnts));
|
||||
|
||||
ret = spi_read(to_spi_device(moxtet->dev), buf, TURRIS_MOX_MAX_MODULES);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (buf[0] == TURRIS_MOX_CPU_ID_EMMC) {
|
||||
dev_info(moxtet->dev, "Found MOX A (eMMC CPU) module\n");
|
||||
} else if (buf[0] == TURRIS_MOX_CPU_ID_SD) {
|
||||
dev_info(moxtet->dev, "Found MOX A (CPU) module\n");
|
||||
} else {
|
||||
dev_err(moxtet->dev, "Invalid Turris MOX A CPU module 0x%02x\n",
|
||||
buf[0]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
moxtet->count = 0;
|
||||
|
||||
for (i = 1; i < TURRIS_MOX_MAX_MODULES; ++i) {
|
||||
int id;
|
||||
|
||||
if (buf[i] == 0xff)
|
||||
break;
|
||||
|
||||
id = buf[i] & 0xf;
|
||||
|
||||
moxtet->modules[i-1] = id;
|
||||
++moxtet->count;
|
||||
|
||||
if (mox_module_known(id)) {
|
||||
dev_info(moxtet->dev, "Found %s module\n",
|
||||
mox_module_table[id].desc);
|
||||
|
||||
if (moxtet_set_irq(moxtet, i-1, id, cnts[id]++) < 0)
|
||||
dev_err(moxtet->dev,
|
||||
" Cannot set IRQ for module %s\n",
|
||||
mox_module_table[id].desc);
|
||||
} else {
|
||||
dev_warn(moxtet->dev,
|
||||
"Unknown Moxtet module found (ID 0x%02x)\n",
|
||||
id);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int moxtet_spi_read(struct moxtet *moxtet, u8 *buf)
|
||||
{
|
||||
struct spi_transfer xfer = {
|
||||
.rx_buf = buf,
|
||||
.tx_buf = moxtet->tx,
|
||||
.len = moxtet->count + 1
|
||||
};
|
||||
int ret;
|
||||
|
||||
mutex_lock(&moxtet->lock);
|
||||
|
||||
ret = spi_sync_transfer(to_spi_device(moxtet->dev), &xfer, 1);
|
||||
|
||||
mutex_unlock(&moxtet->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int moxtet_device_read(struct device *dev)
|
||||
{
|
||||
struct moxtet_device *mdev = to_moxtet_device(dev);
|
||||
struct moxtet *moxtet = mdev->moxtet;
|
||||
u8 buf[TURRIS_MOX_MAX_MODULES];
|
||||
int ret;
|
||||
|
||||
if (mdev->idx >= moxtet->count)
|
||||
return -EINVAL;
|
||||
|
||||
ret = moxtet_spi_read(moxtet, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return buf[mdev->idx + 1] >> 4;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(moxtet_device_read);
|
||||
|
||||
int moxtet_device_write(struct device *dev, u8 val)
|
||||
{
|
||||
struct moxtet_device *mdev = to_moxtet_device(dev);
|
||||
struct moxtet *moxtet = mdev->moxtet;
|
||||
int ret;
|
||||
|
||||
if (mdev->idx >= moxtet->count)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&moxtet->lock);
|
||||
|
||||
moxtet->tx[moxtet->count - mdev->idx] = val;
|
||||
|
||||
ret = spi_write(to_spi_device(moxtet->dev), moxtet->tx,
|
||||
moxtet->count + 1);
|
||||
|
||||
mutex_unlock(&moxtet->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(moxtet_device_write);
|
||||
|
||||
int moxtet_device_written(struct device *dev)
|
||||
{
|
||||
struct moxtet_device *mdev = to_moxtet_device(dev);
|
||||
struct moxtet *moxtet = mdev->moxtet;
|
||||
|
||||
if (mdev->idx >= moxtet->count)
|
||||
return -EINVAL;
|
||||
|
||||
return moxtet->tx[moxtet->count - mdev->idx];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(moxtet_device_written);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int moxtet_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static ssize_t input_read(struct file *file, char __user *buf, size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct moxtet *moxtet = file->private_data;
|
||||
u8 bin[TURRIS_MOX_MAX_MODULES];
|
||||
u8 hex[sizeof(buf) * 2 + 1];
|
||||
int ret, n;
|
||||
|
||||
ret = moxtet_spi_read(moxtet, bin);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
n = moxtet->count + 1;
|
||||
bin2hex(hex, bin, n);
|
||||
|
||||
hex[2*n] = '\n';
|
||||
|
||||
return simple_read_from_buffer(buf, len, ppos, hex, 2*n + 1);
|
||||
}
|
||||
|
||||
static const struct file_operations input_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = moxtet_debug_open,
|
||||
.read = input_read,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static ssize_t output_read(struct file *file, char __user *buf, size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct moxtet *moxtet = file->private_data;
|
||||
u8 hex[TURRIS_MOX_MAX_MODULES * 2 + 1];
|
||||
u8 *p = hex;
|
||||
int i;
|
||||
|
||||
mutex_lock(&moxtet->lock);
|
||||
|
||||
for (i = 0; i < moxtet->count; ++i)
|
||||
p = hex_byte_pack(p, moxtet->tx[moxtet->count - i]);
|
||||
|
||||
mutex_unlock(&moxtet->lock);
|
||||
|
||||
*p++ = '\n';
|
||||
|
||||
return simple_read_from_buffer(buf, len, ppos, hex, p - hex);
|
||||
}
|
||||
|
||||
static ssize_t output_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
struct moxtet *moxtet = file->private_data;
|
||||
u8 bin[TURRIS_MOX_MAX_MODULES];
|
||||
u8 hex[sizeof(bin) * 2 + 1];
|
||||
ssize_t res;
|
||||
loff_t dummy = 0;
|
||||
int err, i;
|
||||
|
||||
if (len > 2 * moxtet->count + 1 || len < 2 * moxtet->count)
|
||||
return -EINVAL;
|
||||
|
||||
res = simple_write_to_buffer(hex, sizeof(hex), &dummy, buf, len);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
if (len % 2 == 1 && hex[len - 1] != '\n')
|
||||
return -EINVAL;
|
||||
|
||||
err = hex2bin(bin, hex, moxtet->count);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&moxtet->lock);
|
||||
|
||||
for (i = 0; i < moxtet->count; ++i)
|
||||
moxtet->tx[moxtet->count - i] = bin[i];
|
||||
|
||||
err = spi_write(to_spi_device(moxtet->dev), moxtet->tx,
|
||||
moxtet->count + 1);
|
||||
|
||||
mutex_unlock(&moxtet->lock);
|
||||
|
||||
return err < 0 ? err : len;
|
||||
}
|
||||
|
||||
static const struct file_operations output_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = moxtet_debug_open,
|
||||
.read = output_read,
|
||||
.write = output_write,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static int moxtet_register_debugfs(struct moxtet *moxtet)
|
||||
{
|
||||
struct dentry *root, *entry;
|
||||
|
||||
root = debugfs_create_dir("moxtet", NULL);
|
||||
|
||||
if (IS_ERR(root))
|
||||
return PTR_ERR(root);
|
||||
|
||||
entry = debugfs_create_file_unsafe("input", 0444, root, moxtet,
|
||||
&input_fops);
|
||||
if (IS_ERR(entry))
|
||||
goto err_remove;
|
||||
|
||||
entry = debugfs_create_file_unsafe("output", 0644, root, moxtet,
|
||||
&output_fops);
|
||||
if (IS_ERR(entry))
|
||||
goto err_remove;
|
||||
|
||||
moxtet->debugfs_root = root;
|
||||
|
||||
return 0;
|
||||
err_remove:
|
||||
debugfs_remove_recursive(root);
|
||||
return PTR_ERR(entry);
|
||||
}
|
||||
|
||||
static void moxtet_unregister_debugfs(struct moxtet *moxtet)
|
||||
{
|
||||
debugfs_remove_recursive(moxtet->debugfs_root);
|
||||
}
|
||||
#else
|
||||
static inline int moxtet_register_debugfs(struct moxtet *moxtet)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void moxtet_unregister_debugfs(struct moxtet *moxtet)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int moxtet_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct moxtet *moxtet = d->host_data;
|
||||
|
||||
if (hw >= MOXTET_NIRQS || !(moxtet->irq.exists & BIT(hw))) {
|
||||
dev_err(moxtet->dev, "Invalid hw irq number\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_set_chip_data(irq, d->host_data);
|
||||
irq_set_chip_and_handler(irq, &moxtet->irq.chip, handle_level_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int moxtet_irq_domain_xlate(struct irq_domain *d,
|
||||
struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
unsigned long *out_hwirq,
|
||||
unsigned int *out_type)
|
||||
{
|
||||
struct moxtet *moxtet = d->host_data;
|
||||
int irq;
|
||||
|
||||
if (WARN_ON(intsize < 1))
|
||||
return -EINVAL;
|
||||
|
||||
irq = intspec[0];
|
||||
|
||||
if (irq >= MOXTET_NIRQS || !(moxtet->irq.exists & BIT(irq)))
|
||||
return -EINVAL;
|
||||
|
||||
*out_hwirq = irq;
|
||||
*out_type = IRQ_TYPE_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops moxtet_irq_domain = {
|
||||
.map = moxtet_irq_domain_map,
|
||||
.xlate = moxtet_irq_domain_xlate,
|
||||
};
|
||||
|
||||
static void moxtet_irq_mask(struct irq_data *d)
|
||||
{
|
||||
struct moxtet *moxtet = irq_data_get_irq_chip_data(d);
|
||||
|
||||
moxtet->irq.masked |= BIT(d->hwirq);
|
||||
}
|
||||
|
||||
static void moxtet_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
struct moxtet *moxtet = irq_data_get_irq_chip_data(d);
|
||||
|
||||
moxtet->irq.masked &= ~BIT(d->hwirq);
|
||||
}
|
||||
|
||||
static void moxtet_irq_print_chip(struct irq_data *d, struct seq_file *p)
|
||||
{
|
||||
struct moxtet *moxtet = irq_data_get_irq_chip_data(d);
|
||||
struct moxtet_irqpos *pos = &moxtet->irq.position[d->hwirq];
|
||||
int id;
|
||||
|
||||
id = moxtet->modules[pos->idx];
|
||||
|
||||
seq_printf(p, " moxtet-%s.%i#%i", mox_module_name(id), pos->idx,
|
||||
pos->bit);
|
||||
}
|
||||
|
||||
static const struct irq_chip moxtet_irq_chip = {
|
||||
.name = "moxtet",
|
||||
.irq_mask = moxtet_irq_mask,
|
||||
.irq_unmask = moxtet_irq_unmask,
|
||||
.irq_print_chip = moxtet_irq_print_chip,
|
||||
};
|
||||
|
||||
static int moxtet_irq_read(struct moxtet *moxtet, unsigned long *map)
|
||||
{
|
||||
struct moxtet_irqpos *pos = moxtet->irq.position;
|
||||
u8 buf[TURRIS_MOX_MAX_MODULES];
|
||||
int i, ret;
|
||||
|
||||
ret = moxtet_spi_read(moxtet, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*map = 0;
|
||||
|
||||
for_each_set_bit(i, &moxtet->irq.exists, MOXTET_NIRQS) {
|
||||
if (!(buf[pos[i].idx + 1] & BIT(4 + pos[i].bit)))
|
||||
set_bit(i, map);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t moxtet_irq_thread_fn(int irq, void *data)
|
||||
{
|
||||
struct moxtet *moxtet = data;
|
||||
unsigned long set;
|
||||
int nhandled = 0, i, sub_irq, ret;
|
||||
|
||||
ret = moxtet_irq_read(moxtet, &set);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
set &= ~moxtet->irq.masked;
|
||||
|
||||
do {
|
||||
for_each_set_bit(i, &set, MOXTET_NIRQS) {
|
||||
sub_irq = irq_find_mapping(moxtet->irq.domain, i);
|
||||
handle_nested_irq(sub_irq);
|
||||
dev_dbg(moxtet->dev, "%i irq\n", i);
|
||||
++nhandled;
|
||||
}
|
||||
|
||||
ret = moxtet_irq_read(moxtet, &set);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
set &= ~moxtet->irq.masked;
|
||||
} while (set);
|
||||
|
||||
out:
|
||||
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
|
||||
}
|
||||
|
||||
static void moxtet_irq_free(struct moxtet *moxtet)
|
||||
{
|
||||
int i, irq;
|
||||
|
||||
for (i = 0; i < MOXTET_NIRQS; ++i) {
|
||||
if (moxtet->irq.exists & BIT(i)) {
|
||||
irq = irq_find_mapping(moxtet->irq.domain, i);
|
||||
irq_dispose_mapping(irq);
|
||||
}
|
||||
}
|
||||
|
||||
irq_domain_remove(moxtet->irq.domain);
|
||||
}
|
||||
|
||||
static int moxtet_irq_setup(struct moxtet *moxtet)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
moxtet->irq.domain = irq_domain_add_simple(moxtet->dev->of_node,
|
||||
MOXTET_NIRQS, 0,
|
||||
&moxtet_irq_domain, moxtet);
|
||||
if (moxtet->irq.domain == NULL) {
|
||||
dev_err(moxtet->dev, "Could not add IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < MOXTET_NIRQS; ++i)
|
||||
if (moxtet->irq.exists & BIT(i))
|
||||
irq_create_mapping(moxtet->irq.domain, i);
|
||||
|
||||
moxtet->irq.chip = moxtet_irq_chip;
|
||||
moxtet->irq.masked = ~0;
|
||||
|
||||
ret = request_threaded_irq(moxtet->dev_irq, NULL, moxtet_irq_thread_fn,
|
||||
IRQF_ONESHOT, "moxtet", moxtet);
|
||||
if (ret < 0)
|
||||
goto err_free;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
moxtet_irq_free(moxtet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int moxtet_probe(struct spi_device *spi)
|
||||
{
|
||||
struct moxtet *moxtet;
|
||||
int ret;
|
||||
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
moxtet = devm_kzalloc(&spi->dev, sizeof(struct moxtet),
|
||||
GFP_KERNEL);
|
||||
if (!moxtet)
|
||||
return -ENOMEM;
|
||||
|
||||
moxtet->dev = &spi->dev;
|
||||
spi_set_drvdata(spi, moxtet);
|
||||
|
||||
mutex_init(&moxtet->lock);
|
||||
|
||||
moxtet->dev_irq = of_irq_get(moxtet->dev->of_node, 0);
|
||||
if (moxtet->dev_irq == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (moxtet->dev_irq <= 0) {
|
||||
dev_err(moxtet->dev, "No IRQ resource found\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = moxtet_find_topology(moxtet);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (moxtet->irq.exists) {
|
||||
ret = moxtet_irq_setup(moxtet);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
of_register_moxtet_devices(moxtet);
|
||||
moxtet_register_devices_from_topology(moxtet);
|
||||
|
||||
ret = moxtet_register_debugfs(moxtet);
|
||||
if (ret < 0)
|
||||
dev_warn(moxtet->dev, "Failed creating debugfs entries: %i\n",
|
||||
ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int moxtet_remove(struct spi_device *spi)
|
||||
{
|
||||
struct moxtet *moxtet = spi_get_drvdata(spi);
|
||||
|
||||
free_irq(moxtet->dev_irq, moxtet);
|
||||
|
||||
moxtet_irq_free(moxtet);
|
||||
|
||||
moxtet_unregister_debugfs(moxtet);
|
||||
|
||||
device_for_each_child(moxtet->dev, NULL, __unregister);
|
||||
|
||||
mutex_destroy(&moxtet->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id moxtet_dt_ids[] = {
|
||||
{ .compatible = "cznic,moxtet" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, moxtet_dt_ids);
|
||||
|
||||
static struct spi_driver moxtet_spi_driver = {
|
||||
.driver = {
|
||||
.name = "moxtet",
|
||||
.of_match_table = moxtet_dt_ids,
|
||||
},
|
||||
.probe = moxtet_probe,
|
||||
.remove = moxtet_remove,
|
||||
};
|
||||
|
||||
static int __init moxtet_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bus_register(&moxtet_bus_type);
|
||||
if (ret < 0) {
|
||||
pr_err("moxtet bus registration failed: %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = spi_register_driver(&moxtet_spi_driver);
|
||||
if (ret < 0) {
|
||||
pr_err("moxtet spi driver registration failed: %d\n", ret);
|
||||
goto error_bus;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_bus:
|
||||
bus_unregister(&moxtet_bus_type);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
postcore_initcall_sync(moxtet_init);
|
||||
|
||||
static void __exit moxtet_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&moxtet_spi_driver);
|
||||
bus_unregister(&moxtet_bus_type);
|
||||
}
|
||||
module_exit(moxtet_exit);
|
||||
|
||||
MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>");
|
||||
MODULE_DESCRIPTION("CZ.NIC's Turris Mox module configuration bus");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -651,10 +651,8 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(rsb->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "failed to retrieve irq: %d\n", irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
rsb->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(rsb->clk)) {
|
||||
|
@ -176,7 +176,6 @@ static int uniphier_system_bus_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct uniphier_system_bus_priv *priv;
|
||||
struct resource *regs;
|
||||
const __be32 *ranges;
|
||||
u32 cells, addr, size;
|
||||
u64 paddr;
|
||||
@ -186,8 +185,7 @@ static int uniphier_system_bus_probe(struct platform_device *pdev)
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->membase = devm_ioremap_resource(dev, regs);
|
||||
priv->membase = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->membase))
|
||||
return PTR_ERR(priv->membase);
|
||||
|
||||
|
@ -69,7 +69,7 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
{
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
|
||||
return clk->handle->clk_ops->rate_set(clk->handle, clk->id, 0, rate);
|
||||
return clk->handle->clk_ops->rate_set(clk->handle, clk->id, rate);
|
||||
}
|
||||
|
||||
static int scmi_clk_enable(struct clk_hw *hw)
|
||||
|
@ -271,6 +271,20 @@ config TRUSTED_FOUNDATIONS
|
||||
|
||||
Choose N if you don't know what this is about.
|
||||
|
||||
config TURRIS_MOX_RWTM
|
||||
tristate "Turris Mox rWTM secure firmware driver"
|
||||
depends on ARCH_MVEBU || COMPILE_TEST
|
||||
depends on HAS_DMA && OF
|
||||
depends on MAILBOX
|
||||
select HW_RANDOM
|
||||
select ARMADA_37XX_RWTM_MBOX
|
||||
help
|
||||
This driver communicates with the firmware on the Cortex-M3 secure
|
||||
processor of the Turris Mox router. Enable if you are building for
|
||||
Turris Mox, and you will be able to read the device serial number and
|
||||
other manufacturing data and also utilize the Entropy Bit Generator
|
||||
for hardware random number generation.
|
||||
|
||||
config HAVE_ARM_SMCCC
|
||||
bool
|
||||
|
||||
|
@ -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
|
||||
obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o
|
||||
obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
|
||||
obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o
|
||||
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/
|
||||
obj-y += psci/
|
||||
|
@ -2,5 +2,5 @@
|
||||
obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o
|
||||
scmi-bus-y = bus.o
|
||||
scmi-driver-y = driver.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o sensors.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
|
||||
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
|
||||
|
@ -204,7 +204,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(id);
|
||||
put_unaligned_le32(id, t->tx.buf);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret)
|
||||
|
@ -56,7 +56,7 @@ struct scmi_msg_resp_clock_describe_rates {
|
||||
struct scmi_clock_set_rate {
|
||||
__le32 flags;
|
||||
#define CLOCK_SET_ASYNC BIT(0)
|
||||
#define CLOCK_SET_DELAYED BIT(1)
|
||||
#define CLOCK_SET_IGNORE_RESP BIT(1)
|
||||
#define CLOCK_SET_ROUND_UP BIT(2)
|
||||
#define CLOCK_SET_ROUND_AUTO BIT(3)
|
||||
__le32 id;
|
||||
@ -67,6 +67,7 @@ struct scmi_clock_set_rate {
|
||||
struct clock_info {
|
||||
int num_clocks;
|
||||
int max_async_req;
|
||||
atomic_t cur_async_req;
|
||||
struct scmi_clock_info *clk;
|
||||
};
|
||||
|
||||
@ -106,7 +107,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
|
||||
put_unaligned_le32(clk_id, t->tx.buf);
|
||||
attr = t->rx.buf;
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
@ -203,39 +204,47 @@ scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
|
||||
put_unaligned_le32(clk_id, t->tx.buf);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret) {
|
||||
__le32 *pval = t->rx.buf;
|
||||
|
||||
*value = le32_to_cpu(*pval);
|
||||
*value |= (u64)le32_to_cpu(*(pval + 1)) << 32;
|
||||
}
|
||||
if (!ret)
|
||||
*value = get_unaligned_le64(t->rx.buf);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id,
|
||||
u32 config, u64 rate)
|
||||
u64 rate)
|
||||
{
|
||||
int ret;
|
||||
u32 flags = 0;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_clock_set_rate *cfg;
|
||||
struct clock_info *ci = handle->clk_priv;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
|
||||
sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ci->max_async_req &&
|
||||
atomic_inc_return(&ci->cur_async_req) < ci->max_async_req)
|
||||
flags |= CLOCK_SET_ASYNC;
|
||||
|
||||
cfg = t->tx.buf;
|
||||
cfg->flags = cpu_to_le32(config);
|
||||
cfg->flags = cpu_to_le32(flags);
|
||||
cfg->id = cpu_to_le32(clk_id);
|
||||
cfg->value_low = cpu_to_le32(rate & 0xffffffff);
|
||||
cfg->value_high = cpu_to_le32(rate >> 32);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (flags & CLOCK_SET_ASYNC)
|
||||
ret = scmi_do_xfer_with_response(handle, t);
|
||||
else
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
|
||||
if (ci->max_async_req)
|
||||
atomic_dec(&ci->cur_async_req);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0)
|
||||
#define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16)
|
||||
#define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x)))
|
||||
@ -48,11 +50,11 @@ struct scmi_msg_resp_prot_version {
|
||||
/**
|
||||
* struct scmi_msg_hdr - Message(Tx/Rx) header
|
||||
*
|
||||
* @id: The identifier of the command being sent
|
||||
* @protocol_id: The identifier of the protocol used to send @id command
|
||||
* @seq: The token to identify the message. when a message/command returns,
|
||||
* the platform returns the whole message header unmodified including
|
||||
* the token
|
||||
* @id: The identifier of the message being sent
|
||||
* @protocol_id: The identifier of the protocol used to send @id message
|
||||
* @seq: The token to identify the message. When a message returns, the
|
||||
* platform returns the whole message header unmodified including the
|
||||
* token
|
||||
* @status: Status of the transfer once it's complete
|
||||
* @poll_completion: Indicate if the transfer needs to be polled for
|
||||
* completion or interrupt mode is used
|
||||
@ -84,17 +86,21 @@ struct scmi_msg {
|
||||
* @rx: Receive message, the buffer should be pre-allocated to store
|
||||
* message. If request-ACK protocol is used, we can reuse the same
|
||||
* buffer for the rx path as we use for the tx path.
|
||||
* @done: completion event
|
||||
* @done: command message transmit completion event
|
||||
* @async: pointer to delayed response message received event completion
|
||||
*/
|
||||
struct scmi_xfer {
|
||||
struct scmi_msg_hdr hdr;
|
||||
struct scmi_msg tx;
|
||||
struct scmi_msg rx;
|
||||
struct completion done;
|
||||
struct completion *async_done;
|
||||
};
|
||||
|
||||
void scmi_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer);
|
||||
int scmi_do_xfer(const struct scmi_handle *h, struct scmi_xfer *xfer);
|
||||
int scmi_do_xfer_with_response(const struct scmi_handle *h,
|
||||
struct scmi_xfer *xfer);
|
||||
int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
|
||||
size_t tx_size, size_t rx_size, struct scmi_xfer **p);
|
||||
int scmi_handle_put(const struct scmi_handle *handle);
|
||||
|
@ -30,8 +30,14 @@
|
||||
#include "common.h"
|
||||
|
||||
#define MSG_ID_MASK GENMASK(7, 0)
|
||||
#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr))
|
||||
#define MSG_TYPE_MASK GENMASK(9, 8)
|
||||
#define MSG_XTRACT_TYPE(hdr) FIELD_GET(MSG_TYPE_MASK, (hdr))
|
||||
#define MSG_TYPE_COMMAND 0
|
||||
#define MSG_TYPE_DELAYED_RESP 2
|
||||
#define MSG_TYPE_NOTIFICATION 3
|
||||
#define MSG_PROTOCOL_ID_MASK GENMASK(17, 10)
|
||||
#define MSG_XTRACT_PROT_ID(hdr) FIELD_GET(MSG_PROTOCOL_ID_MASK, (hdr))
|
||||
#define MSG_TOKEN_ID_MASK GENMASK(27, 18)
|
||||
#define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr))
|
||||
#define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1)
|
||||
@ -86,7 +92,7 @@ struct scmi_desc {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_chan_info - Structure representing a SCMI channel informfation
|
||||
* struct scmi_chan_info - Structure representing a SCMI channel information
|
||||
*
|
||||
* @cl: Mailbox Client
|
||||
* @chan: Transmit/Receive mailbox channel
|
||||
@ -111,8 +117,9 @@ struct scmi_chan_info {
|
||||
* @handle: Instance of SCMI handle to send to clients
|
||||
* @version: SCMI revision information containing protocol version,
|
||||
* implementation version and (sub-)vendor identification.
|
||||
* @minfo: Message info
|
||||
* @tx_idr: IDR object to map protocol id to channel info pointer
|
||||
* @tx_minfo: Universal Transmit Message management info
|
||||
* @tx_idr: IDR object to map protocol id to Tx channel info pointer
|
||||
* @rx_idr: IDR object to map protocol id to Rx channel info pointer
|
||||
* @protocols_imp: List of protocols implemented, currently maximum of
|
||||
* MAX_PROTOCOLS_IMP elements allocated by the base protocol
|
||||
* @node: List head
|
||||
@ -123,8 +130,9 @@ struct scmi_info {
|
||||
const struct scmi_desc *desc;
|
||||
struct scmi_revision_info version;
|
||||
struct scmi_handle handle;
|
||||
struct scmi_xfers_info minfo;
|
||||
struct scmi_xfers_info tx_minfo;
|
||||
struct idr tx_idr;
|
||||
struct idr rx_idr;
|
||||
u8 *protocols_imp;
|
||||
struct list_head node;
|
||||
int users;
|
||||
@ -182,7 +190,7 @@ static inline int scmi_to_linux_errno(int errno)
|
||||
static inline void scmi_dump_header_dbg(struct device *dev,
|
||||
struct scmi_msg_hdr *hdr)
|
||||
{
|
||||
dev_dbg(dev, "Command ID: %x Sequence ID: %x Protocol: %x\n",
|
||||
dev_dbg(dev, "Message ID: %x Sequence ID: %x Protocol: %x\n",
|
||||
hdr->id, hdr->seq, hdr->protocol_id);
|
||||
}
|
||||
|
||||
@ -190,64 +198,20 @@ static void scmi_fetch_response(struct scmi_xfer *xfer,
|
||||
struct scmi_shared_mem __iomem *mem)
|
||||
{
|
||||
xfer->hdr.status = ioread32(mem->msg_payload);
|
||||
/* Skip the length of header and statues in payload area i.e 8 bytes*/
|
||||
/* Skip the length of header and status in payload area i.e 8 bytes */
|
||||
xfer->rx.len = min_t(size_t, xfer->rx.len, ioread32(&mem->length) - 8);
|
||||
|
||||
/* Take a copy to the rx buffer.. */
|
||||
memcpy_fromio(xfer->rx.buf, mem->msg_payload + 4, xfer->rx.len);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_rx_callback() - mailbox client callback for receive messages
|
||||
*
|
||||
* @cl: client pointer
|
||||
* @m: mailbox message
|
||||
*
|
||||
* Processes one received message to appropriate transfer information and
|
||||
* signals completion of the transfer.
|
||||
*
|
||||
* NOTE: This function will be invoked in IRQ context, hence should be
|
||||
* as optimal as possible.
|
||||
*/
|
||||
static void scmi_rx_callback(struct mbox_client *cl, void *m)
|
||||
{
|
||||
u16 xfer_id;
|
||||
struct scmi_xfer *xfer;
|
||||
struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl);
|
||||
struct device *dev = cinfo->dev;
|
||||
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
|
||||
struct scmi_xfers_info *minfo = &info->minfo;
|
||||
struct scmi_shared_mem __iomem *mem = cinfo->payload;
|
||||
|
||||
xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header));
|
||||
|
||||
/* Are we even expecting this? */
|
||||
if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
|
||||
dev_err(dev, "message for %d is not expected!\n", xfer_id);
|
||||
return;
|
||||
}
|
||||
|
||||
xfer = &minfo->xfer_block[xfer_id];
|
||||
|
||||
scmi_dump_header_dbg(dev, &xfer->hdr);
|
||||
/* Is the message of valid length? */
|
||||
if (xfer->rx.len > info->desc->max_msg_size) {
|
||||
dev_err(dev, "unable to handle %zu xfer(max %d)\n",
|
||||
xfer->rx.len, info->desc->max_msg_size);
|
||||
return;
|
||||
}
|
||||
|
||||
scmi_fetch_response(xfer, mem);
|
||||
complete(&xfer->done);
|
||||
}
|
||||
|
||||
/**
|
||||
* pack_scmi_header() - packs and returns 32-bit header
|
||||
*
|
||||
* @hdr: pointer to header containing all the information on message id,
|
||||
* protocol id and sequence id.
|
||||
*
|
||||
* Return: 32-bit packed command header to be sent to the platform.
|
||||
* Return: 32-bit packed message header to be sent to the platform.
|
||||
*/
|
||||
static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
|
||||
{
|
||||
@ -256,6 +220,18 @@ static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
|
||||
FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* unpack_scmi_header() - unpacks and records message and protocol id
|
||||
*
|
||||
* @msg_hdr: 32-bit packed message header sent from the platform
|
||||
* @hdr: pointer to header to fetch message and protocol id.
|
||||
*/
|
||||
static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
|
||||
{
|
||||
hdr->id = MSG_XTRACT_ID(msg_hdr);
|
||||
hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_tx_prepare() - mailbox client callback to prepare for the transfer
|
||||
*
|
||||
@ -271,6 +247,14 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m)
|
||||
struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl);
|
||||
struct scmi_shared_mem __iomem *mem = cinfo->payload;
|
||||
|
||||
/*
|
||||
* Ideally channel must be free by now unless OS timeout last
|
||||
* request and platform continued to process the same, wait
|
||||
* until it releases the shared memory, otherwise we may endup
|
||||
* overwriting its response with new message payload or vice-versa
|
||||
*/
|
||||
spin_until_cond(ioread32(&mem->channel_status) &
|
||||
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
|
||||
/* Mark channel busy + clear error */
|
||||
iowrite32(0x0, &mem->channel_status);
|
||||
iowrite32(t->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED,
|
||||
@ -285,8 +269,9 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m)
|
||||
* scmi_xfer_get() - Allocate one message
|
||||
*
|
||||
* @handle: Pointer to SCMI entity handle
|
||||
* @minfo: Pointer to Tx/Rx Message management info based on channel type
|
||||
*
|
||||
* Helper function which is used by various command functions that are
|
||||
* Helper function which is used by various message functions that are
|
||||
* exposed to clients of this driver for allocating a message traffic event.
|
||||
*
|
||||
* This function can sleep depending on pending requests already in the system
|
||||
@ -295,13 +280,13 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m)
|
||||
*
|
||||
* Return: 0 if all went fine, else corresponding error.
|
||||
*/
|
||||
static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle)
|
||||
static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
|
||||
struct scmi_xfers_info *minfo)
|
||||
{
|
||||
u16 xfer_id;
|
||||
struct scmi_xfer *xfer;
|
||||
unsigned long flags, bit_pos;
|
||||
struct scmi_info *info = handle_to_scmi_info(handle);
|
||||
struct scmi_xfers_info *minfo = &info->minfo;
|
||||
|
||||
/* Keep the locked section as small as possible */
|
||||
spin_lock_irqsave(&minfo->xfer_lock, flags);
|
||||
@ -324,18 +309,17 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle)
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_xfer_put() - Release a message
|
||||
* __scmi_xfer_put() - Release a message
|
||||
*
|
||||
* @handle: Pointer to SCMI entity handle
|
||||
* @minfo: Pointer to Tx/Rx Message management info based on channel type
|
||||
* @xfer: message that was reserved by scmi_xfer_get
|
||||
*
|
||||
* This holds a spinlock to maintain integrity of internal data structures.
|
||||
*/
|
||||
void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
|
||||
static void
|
||||
__scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct scmi_info *info = handle_to_scmi_info(handle);
|
||||
struct scmi_xfers_info *minfo = &info->minfo;
|
||||
|
||||
/*
|
||||
* Keep the locked section as small as possible
|
||||
@ -347,6 +331,68 @@ void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
|
||||
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_rx_callback() - mailbox client callback for receive messages
|
||||
*
|
||||
* @cl: client pointer
|
||||
* @m: mailbox message
|
||||
*
|
||||
* Processes one received message to appropriate transfer information and
|
||||
* signals completion of the transfer.
|
||||
*
|
||||
* NOTE: This function will be invoked in IRQ context, hence should be
|
||||
* as optimal as possible.
|
||||
*/
|
||||
static void scmi_rx_callback(struct mbox_client *cl, void *m)
|
||||
{
|
||||
u8 msg_type;
|
||||
u32 msg_hdr;
|
||||
u16 xfer_id;
|
||||
struct scmi_xfer *xfer;
|
||||
struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl);
|
||||
struct device *dev = cinfo->dev;
|
||||
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
|
||||
struct scmi_xfers_info *minfo = &info->tx_minfo;
|
||||
struct scmi_shared_mem __iomem *mem = cinfo->payload;
|
||||
|
||||
msg_hdr = ioread32(&mem->msg_header);
|
||||
msg_type = MSG_XTRACT_TYPE(msg_hdr);
|
||||
xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
|
||||
|
||||
if (msg_type == MSG_TYPE_NOTIFICATION)
|
||||
return; /* Notifications not yet supported */
|
||||
|
||||
/* Are we even expecting this? */
|
||||
if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
|
||||
dev_err(dev, "message for %d is not expected!\n", xfer_id);
|
||||
return;
|
||||
}
|
||||
|
||||
xfer = &minfo->xfer_block[xfer_id];
|
||||
|
||||
scmi_dump_header_dbg(dev, &xfer->hdr);
|
||||
|
||||
scmi_fetch_response(xfer, mem);
|
||||
|
||||
if (msg_type == MSG_TYPE_DELAYED_RESP)
|
||||
complete(xfer->async_done);
|
||||
else
|
||||
complete(&xfer->done);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_xfer_put() - Release a transmit message
|
||||
*
|
||||
* @handle: Pointer to SCMI entity handle
|
||||
* @xfer: message that was reserved by scmi_xfer_get
|
||||
*/
|
||||
void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
|
||||
{
|
||||
struct scmi_info *info = handle_to_scmi_info(handle);
|
||||
|
||||
__scmi_xfer_put(&info->tx_minfo, xfer);
|
||||
}
|
||||
|
||||
static bool
|
||||
scmi_xfer_poll_done(const struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)
|
||||
{
|
||||
@ -435,8 +481,36 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define SCMI_MAX_RESPONSE_TIMEOUT (2 * MSEC_PER_SEC)
|
||||
|
||||
/**
|
||||
* scmi_xfer_get_init() - Allocate and initialise one message
|
||||
* scmi_do_xfer_with_response() - Do one transfer and wait until the delayed
|
||||
* response is received
|
||||
*
|
||||
* @handle: Pointer to SCMI entity handle
|
||||
* @xfer: Transfer to initiate and wait for response
|
||||
*
|
||||
* Return: -ETIMEDOUT in case of no delayed response, if transmit error,
|
||||
* return corresponding error, else if all goes well, return 0.
|
||||
*/
|
||||
int scmi_do_xfer_with_response(const struct scmi_handle *handle,
|
||||
struct scmi_xfer *xfer)
|
||||
{
|
||||
int ret, timeout = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT);
|
||||
DECLARE_COMPLETION_ONSTACK(async_response);
|
||||
|
||||
xfer->async_done = &async_response;
|
||||
|
||||
ret = scmi_do_xfer(handle, xfer);
|
||||
if (!ret && !wait_for_completion_timeout(xfer->async_done, timeout))
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
xfer->async_done = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_xfer_get_init() - Allocate and initialise one message for transmit
|
||||
*
|
||||
* @handle: Pointer to SCMI entity handle
|
||||
* @msg_id: Message identifier
|
||||
@ -457,6 +531,7 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id,
|
||||
int ret;
|
||||
struct scmi_xfer *xfer;
|
||||
struct scmi_info *info = handle_to_scmi_info(handle);
|
||||
struct scmi_xfers_info *minfo = &info->tx_minfo;
|
||||
struct device *dev = info->dev;
|
||||
|
||||
/* Ensure we have sane transfer sizes */
|
||||
@ -464,7 +539,7 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id,
|
||||
tx_size > info->desc->max_msg_size)
|
||||
return -ERANGE;
|
||||
|
||||
xfer = scmi_xfer_get(handle);
|
||||
xfer = scmi_xfer_get(handle, minfo);
|
||||
if (IS_ERR(xfer)) {
|
||||
ret = PTR_ERR(xfer);
|
||||
dev_err(dev, "failed to get free message slot(%d)\n", ret);
|
||||
@ -597,27 +672,13 @@ int scmi_handle_put(const struct scmi_handle *handle)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct scmi_desc scmi_generic_desc = {
|
||||
.max_rx_timeout_ms = 30, /* We may increase this if required */
|
||||
.max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */
|
||||
.max_msg_size = 128,
|
||||
};
|
||||
|
||||
/* Each compatible listed below must have descriptor associated with it */
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "arm,scmi", .data = &scmi_generic_desc },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, scmi_of_match);
|
||||
|
||||
static int scmi_xfer_info_init(struct scmi_info *sinfo)
|
||||
{
|
||||
int i;
|
||||
struct scmi_xfer *xfer;
|
||||
struct device *dev = sinfo->dev;
|
||||
const struct scmi_desc *desc = sinfo->desc;
|
||||
struct scmi_xfers_info *info = &sinfo->minfo;
|
||||
struct scmi_xfers_info *info = &sinfo->tx_minfo;
|
||||
|
||||
/* Pre-allocated messages, no more than what hdr.seq can support */
|
||||
if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) {
|
||||
@ -652,9 +713,189 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_mailbox_check(struct device_node *np)
|
||||
static int scmi_mailbox_check(struct device_node *np, int idx)
|
||||
{
|
||||
return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL);
|
||||
return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells",
|
||||
idx, NULL);
|
||||
}
|
||||
|
||||
static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev,
|
||||
int prot_id, bool tx)
|
||||
{
|
||||
int ret, idx;
|
||||
struct resource res;
|
||||
resource_size_t size;
|
||||
struct device_node *shmem, *np = dev->of_node;
|
||||
struct scmi_chan_info *cinfo;
|
||||
struct mbox_client *cl;
|
||||
struct idr *idr;
|
||||
const char *desc = tx ? "Tx" : "Rx";
|
||||
|
||||
/* Transmit channel is first entry i.e. index 0 */
|
||||
idx = tx ? 0 : 1;
|
||||
idr = tx ? &info->tx_idr : &info->rx_idr;
|
||||
|
||||
if (scmi_mailbox_check(np, idx)) {
|
||||
cinfo = idr_find(idr, SCMI_PROTOCOL_BASE);
|
||||
if (unlikely(!cinfo)) /* Possible only if platform has no Rx */
|
||||
return -EINVAL;
|
||||
goto idr_alloc;
|
||||
}
|
||||
|
||||
cinfo = devm_kzalloc(info->dev, sizeof(*cinfo), GFP_KERNEL);
|
||||
if (!cinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
cinfo->dev = dev;
|
||||
|
||||
cl = &cinfo->cl;
|
||||
cl->dev = dev;
|
||||
cl->rx_callback = scmi_rx_callback;
|
||||
cl->tx_prepare = tx ? scmi_tx_prepare : NULL;
|
||||
cl->tx_block = false;
|
||||
cl->knows_txdone = tx;
|
||||
|
||||
shmem = of_parse_phandle(np, "shmem", idx);
|
||||
ret = of_address_to_resource(shmem, 0, &res);
|
||||
of_node_put(shmem);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get SCMI %s payload memory\n", desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size = resource_size(&res);
|
||||
cinfo->payload = devm_ioremap(info->dev, res.start, size);
|
||||
if (!cinfo->payload) {
|
||||
dev_err(dev, "failed to ioremap SCMI %s payload\n", desc);
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
cinfo->chan = mbox_request_channel(cl, idx);
|
||||
if (IS_ERR(cinfo->chan)) {
|
||||
ret = PTR_ERR(cinfo->chan);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to request SCMI %s mailbox\n",
|
||||
desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
idr_alloc:
|
||||
ret = idr_alloc(idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL);
|
||||
if (ret != prot_id) {
|
||||
dev_err(dev, "unable to allocate SCMI idr slot err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cinfo->handle = &info->handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
scmi_mbox_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id)
|
||||
{
|
||||
int ret = scmi_mbox_chan_setup(info, dev, prot_id, true);
|
||||
|
||||
if (!ret) /* Rx is optional, hence no error check */
|
||||
scmi_mbox_chan_setup(info, dev, prot_id, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
|
||||
int prot_id)
|
||||
{
|
||||
struct scmi_device *sdev;
|
||||
|
||||
sdev = scmi_device_create(np, info->dev, prot_id);
|
||||
if (!sdev) {
|
||||
dev_err(info->dev, "failed to create %d protocol device\n",
|
||||
prot_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scmi_mbox_txrx_setup(info, &sdev->dev, prot_id)) {
|
||||
dev_err(&sdev->dev, "failed to setup transport\n");
|
||||
scmi_device_destroy(sdev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup handle now as the transport is ready */
|
||||
scmi_set_handle(sdev);
|
||||
}
|
||||
|
||||
static int scmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_handle *handle;
|
||||
const struct scmi_desc *desc;
|
||||
struct scmi_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child, *np = dev->of_node;
|
||||
|
||||
/* Only mailbox method supported, check for the presence of one */
|
||||
if (scmi_mailbox_check(np, 0)) {
|
||||
dev_err(dev, "no mailbox found in %pOF\n", np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc = of_device_get_match_data(dev);
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->dev = dev;
|
||||
info->desc = desc;
|
||||
INIT_LIST_HEAD(&info->node);
|
||||
|
||||
ret = scmi_xfer_info_init(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
idr_init(&info->tx_idr);
|
||||
idr_init(&info->rx_idr);
|
||||
|
||||
handle = &info->handle;
|
||||
handle->dev = info->dev;
|
||||
handle->version = &info->version;
|
||||
|
||||
ret = scmi_mbox_txrx_setup(info, dev, SCMI_PROTOCOL_BASE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = scmi_base_protocol_init(handle);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&scmi_list_mutex);
|
||||
list_add_tail(&info->node, &scmi_list);
|
||||
mutex_unlock(&scmi_list_mutex);
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
u32 prot_id;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &prot_id))
|
||||
continue;
|
||||
|
||||
if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id))
|
||||
dev_err(dev, "Out of range protocol %d\n", prot_id);
|
||||
|
||||
if (!scmi_is_protocol_implemented(handle, prot_id)) {
|
||||
dev_err(dev, "SCMI protocol %d not implemented\n",
|
||||
prot_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
scmi_create_protocol_device(child, info, prot_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_mbox_free_channel(int id, void *p, void *data)
|
||||
@ -692,167 +933,26 @@ static int scmi_remove(struct platform_device *pdev)
|
||||
ret = idr_for_each(idr, scmi_mbox_free_channel, idr);
|
||||
idr_destroy(&info->tx_idr);
|
||||
|
||||
idr = &info->rx_idr;
|
||||
ret = idr_for_each(idr, scmi_mbox_free_channel, idr);
|
||||
idr_destroy(&info->rx_idr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, int prot_id)
|
||||
{
|
||||
int ret;
|
||||
struct resource res;
|
||||
resource_size_t size;
|
||||
struct device_node *shmem, *np = dev->of_node;
|
||||
struct scmi_chan_info *cinfo;
|
||||
struct mbox_client *cl;
|
||||
static const struct scmi_desc scmi_generic_desc = {
|
||||
.max_rx_timeout_ms = 30, /* We may increase this if required */
|
||||
.max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */
|
||||
.max_msg_size = 128,
|
||||
};
|
||||
|
||||
if (scmi_mailbox_check(np)) {
|
||||
cinfo = idr_find(&info->tx_idr, SCMI_PROTOCOL_BASE);
|
||||
goto idr_alloc;
|
||||
}
|
||||
/* Each compatible listed below must have descriptor associated with it */
|
||||
static const struct of_device_id scmi_of_match[] = {
|
||||
{ .compatible = "arm,scmi", .data = &scmi_generic_desc },
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
cinfo = devm_kzalloc(info->dev, sizeof(*cinfo), GFP_KERNEL);
|
||||
if (!cinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
cinfo->dev = dev;
|
||||
|
||||
cl = &cinfo->cl;
|
||||
cl->dev = dev;
|
||||
cl->rx_callback = scmi_rx_callback;
|
||||
cl->tx_prepare = scmi_tx_prepare;
|
||||
cl->tx_block = false;
|
||||
cl->knows_txdone = true;
|
||||
|
||||
shmem = of_parse_phandle(np, "shmem", 0);
|
||||
ret = of_address_to_resource(shmem, 0, &res);
|
||||
of_node_put(shmem);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get SCMI Tx payload mem resource\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
size = resource_size(&res);
|
||||
cinfo->payload = devm_ioremap(info->dev, res.start, size);
|
||||
if (!cinfo->payload) {
|
||||
dev_err(dev, "failed to ioremap SCMI Tx payload\n");
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
/* Transmit channel is first entry i.e. index 0 */
|
||||
cinfo->chan = mbox_request_channel(cl, 0);
|
||||
if (IS_ERR(cinfo->chan)) {
|
||||
ret = PTR_ERR(cinfo->chan);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to request SCMI Tx mailbox\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
idr_alloc:
|
||||
ret = idr_alloc(&info->tx_idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL);
|
||||
if (ret != prot_id) {
|
||||
dev_err(dev, "unable to allocate SCMI idr slot err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cinfo->handle = &info->handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
|
||||
int prot_id)
|
||||
{
|
||||
struct scmi_device *sdev;
|
||||
|
||||
sdev = scmi_device_create(np, info->dev, prot_id);
|
||||
if (!sdev) {
|
||||
dev_err(info->dev, "failed to create %d protocol device\n",
|
||||
prot_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id)) {
|
||||
dev_err(&sdev->dev, "failed to setup transport\n");
|
||||
scmi_device_destroy(sdev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* setup handle now as the transport is ready */
|
||||
scmi_set_handle(sdev);
|
||||
}
|
||||
|
||||
static int scmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_handle *handle;
|
||||
const struct scmi_desc *desc;
|
||||
struct scmi_info *info;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *child, *np = dev->of_node;
|
||||
|
||||
/* Only mailbox method supported, check for the presence of one */
|
||||
if (scmi_mailbox_check(np)) {
|
||||
dev_err(dev, "no mailbox found in %pOF\n", np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc = of_device_get_match_data(dev);
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->dev = dev;
|
||||
info->desc = desc;
|
||||
INIT_LIST_HEAD(&info->node);
|
||||
|
||||
ret = scmi_xfer_info_init(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
idr_init(&info->tx_idr);
|
||||
|
||||
handle = &info->handle;
|
||||
handle->dev = info->dev;
|
||||
handle->version = &info->version;
|
||||
|
||||
ret = scmi_mbox_chan_setup(info, dev, SCMI_PROTOCOL_BASE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = scmi_base_protocol_init(handle);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&scmi_list_mutex);
|
||||
list_add_tail(&info->node, &scmi_list);
|
||||
mutex_unlock(&scmi_list_mutex);
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
u32 prot_id;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &prot_id))
|
||||
continue;
|
||||
|
||||
if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id))
|
||||
dev_err(dev, "Out of range protocol %d\n", prot_id);
|
||||
|
||||
if (!scmi_is_protocol_implemented(handle, prot_id)) {
|
||||
dev_err(dev, "SCMI protocol %d not implemented\n",
|
||||
prot_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
scmi_create_protocol_device(child, info, prot_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
MODULE_DEVICE_TABLE(of, scmi_of_match);
|
||||
|
||||
static struct platform_driver scmi_driver = {
|
||||
.driver = {
|
||||
|
@ -5,7 +5,10 @@
|
||||
* Copyright (C) 2018 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/sort.h>
|
||||
@ -21,6 +24,7 @@ enum scmi_performance_protocol_cmd {
|
||||
PERF_LEVEL_GET = 0x8,
|
||||
PERF_NOTIFY_LIMITS = 0x9,
|
||||
PERF_NOTIFY_LEVEL = 0xa,
|
||||
PERF_DESCRIBE_FASTCHANNEL = 0xb,
|
||||
};
|
||||
|
||||
struct scmi_opp {
|
||||
@ -44,6 +48,7 @@ struct scmi_msg_resp_perf_domain_attributes {
|
||||
#define SUPPORTS_SET_PERF_LVL(x) ((x) & BIT(30))
|
||||
#define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29))
|
||||
#define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28))
|
||||
#define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27))
|
||||
__le32 rate_limit_us;
|
||||
__le32 sustained_freq_khz;
|
||||
__le32 sustained_perf_level;
|
||||
@ -87,17 +92,56 @@ struct scmi_msg_resp_perf_describe_levels {
|
||||
} opp[0];
|
||||
};
|
||||
|
||||
struct scmi_perf_get_fc_info {
|
||||
__le32 domain;
|
||||
__le32 message_id;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_perf_desc_fc {
|
||||
__le32 attr;
|
||||
#define SUPPORTS_DOORBELL(x) ((x) & BIT(0))
|
||||
#define DOORBELL_REG_WIDTH(x) FIELD_GET(GENMASK(2, 1), (x))
|
||||
__le32 rate_limit;
|
||||
__le32 chan_addr_low;
|
||||
__le32 chan_addr_high;
|
||||
__le32 chan_size;
|
||||
__le32 db_addr_low;
|
||||
__le32 db_addr_high;
|
||||
__le32 db_set_lmask;
|
||||
__le32 db_set_hmask;
|
||||
__le32 db_preserve_lmask;
|
||||
__le32 db_preserve_hmask;
|
||||
};
|
||||
|
||||
struct scmi_fc_db_info {
|
||||
int width;
|
||||
u64 set;
|
||||
u64 mask;
|
||||
void __iomem *addr;
|
||||
};
|
||||
|
||||
struct scmi_fc_info {
|
||||
void __iomem *level_set_addr;
|
||||
void __iomem *limit_set_addr;
|
||||
void __iomem *level_get_addr;
|
||||
void __iomem *limit_get_addr;
|
||||
struct scmi_fc_db_info *level_set_db;
|
||||
struct scmi_fc_db_info *limit_set_db;
|
||||
};
|
||||
|
||||
struct perf_dom_info {
|
||||
bool set_limits;
|
||||
bool set_perf;
|
||||
bool perf_limit_notify;
|
||||
bool perf_level_notify;
|
||||
bool perf_fastchannels;
|
||||
u32 opp_count;
|
||||
u32 sustained_freq_khz;
|
||||
u32 sustained_perf_level;
|
||||
u32 mult_factor;
|
||||
char name[SCMI_MAX_STR_SIZE];
|
||||
struct scmi_opp opp[MAX_OPPS];
|
||||
struct scmi_fc_info *fc_info;
|
||||
};
|
||||
|
||||
struct scmi_perf_info {
|
||||
@ -151,7 +195,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(domain);
|
||||
put_unaligned_le32(domain, t->tx.buf);
|
||||
attr = t->rx.buf;
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
@ -162,6 +206,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
|
||||
dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
|
||||
dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
|
||||
dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
|
||||
dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
|
||||
dom_info->sustained_freq_khz =
|
||||
le32_to_cpu(attr->sustained_freq_khz);
|
||||
dom_info->sustained_perf_level =
|
||||
@ -249,8 +294,42 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
|
||||
u32 max_perf, u32 min_perf)
|
||||
#define SCMI_PERF_FC_RING_DB(w) \
|
||||
do { \
|
||||
u##w val = 0; \
|
||||
\
|
||||
if (db->mask) \
|
||||
val = ioread##w(db->addr) & db->mask; \
|
||||
iowrite##w((u##w)db->set | val, db->addr); \
|
||||
} while (0)
|
||||
|
||||
static void scmi_perf_fc_ring_db(struct scmi_fc_db_info *db)
|
||||
{
|
||||
if (!db || !db->addr)
|
||||
return;
|
||||
|
||||
if (db->width == 1)
|
||||
SCMI_PERF_FC_RING_DB(8);
|
||||
else if (db->width == 2)
|
||||
SCMI_PERF_FC_RING_DB(16);
|
||||
else if (db->width == 4)
|
||||
SCMI_PERF_FC_RING_DB(32);
|
||||
else /* db->width == 8 */
|
||||
#ifdef CONFIG_64BIT
|
||||
SCMI_PERF_FC_RING_DB(64);
|
||||
#else
|
||||
{
|
||||
u64 val = 0;
|
||||
|
||||
if (db->mask)
|
||||
val = ioread64_hi_lo(db->addr) & db->mask;
|
||||
iowrite64_hi_lo(db->set, db->addr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int scmi_perf_mb_limits_set(const struct scmi_handle *handle, u32 domain,
|
||||
u32 max_perf, u32 min_perf)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
@ -272,8 +351,24 @@ static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
|
||||
u32 *max_perf, u32 *min_perf)
|
||||
static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
|
||||
u32 max_perf, u32 min_perf)
|
||||
{
|
||||
struct scmi_perf_info *pi = handle->perf_priv;
|
||||
struct perf_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
if (dom->fc_info && dom->fc_info->limit_set_addr) {
|
||||
iowrite32(max_perf, dom->fc_info->limit_set_addr);
|
||||
iowrite32(min_perf, dom->fc_info->limit_set_addr + 4);
|
||||
scmi_perf_fc_ring_db(dom->fc_info->limit_set_db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return scmi_perf_mb_limits_set(handle, domain, max_perf, min_perf);
|
||||
}
|
||||
|
||||
static int scmi_perf_mb_limits_get(const struct scmi_handle *handle, u32 domain,
|
||||
u32 *max_perf, u32 *min_perf)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
@ -284,7 +379,7 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(domain);
|
||||
put_unaligned_le32(domain, t->tx.buf);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret) {
|
||||
@ -298,8 +393,23 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
|
||||
u32 level, bool poll)
|
||||
static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
|
||||
u32 *max_perf, u32 *min_perf)
|
||||
{
|
||||
struct scmi_perf_info *pi = handle->perf_priv;
|
||||
struct perf_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
if (dom->fc_info && dom->fc_info->limit_get_addr) {
|
||||
*max_perf = ioread32(dom->fc_info->limit_get_addr);
|
||||
*min_perf = ioread32(dom->fc_info->limit_get_addr + 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return scmi_perf_mb_limits_get(handle, domain, max_perf, min_perf);
|
||||
}
|
||||
|
||||
static int scmi_perf_mb_level_set(const struct scmi_handle *handle, u32 domain,
|
||||
u32 level, bool poll)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
@ -321,8 +431,23 @@ static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
|
||||
u32 *level, bool poll)
|
||||
static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
|
||||
u32 level, bool poll)
|
||||
{
|
||||
struct scmi_perf_info *pi = handle->perf_priv;
|
||||
struct perf_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
if (dom->fc_info && dom->fc_info->level_set_addr) {
|
||||
iowrite32(level, dom->fc_info->level_set_addr);
|
||||
scmi_perf_fc_ring_db(dom->fc_info->level_set_db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return scmi_perf_mb_level_set(handle, domain, level, poll);
|
||||
}
|
||||
|
||||
static int scmi_perf_mb_level_get(const struct scmi_handle *handle, u32 domain,
|
||||
u32 *level, bool poll)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
@ -333,16 +458,128 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
|
||||
return ret;
|
||||
|
||||
t->hdr.poll_completion = poll;
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(domain);
|
||||
put_unaligned_le32(domain, t->tx.buf);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret)
|
||||
*level = le32_to_cpu(*(__le32 *)t->rx.buf);
|
||||
*level = get_unaligned_le32(t->rx.buf);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
|
||||
u32 *level, bool poll)
|
||||
{
|
||||
struct scmi_perf_info *pi = handle->perf_priv;
|
||||
struct perf_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
if (dom->fc_info && dom->fc_info->level_get_addr) {
|
||||
*level = ioread32(dom->fc_info->level_get_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return scmi_perf_mb_level_get(handle, domain, level, poll);
|
||||
}
|
||||
|
||||
static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size)
|
||||
{
|
||||
if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4)
|
||||
return true;
|
||||
if ((msg == PERF_LIMITS_GET || msg == PERF_LIMITS_SET) && size == 8)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
|
||||
u32 message_id, void __iomem **p_addr,
|
||||
struct scmi_fc_db_info **p_db)
|
||||
{
|
||||
int ret;
|
||||
u32 flags;
|
||||
u64 phys_addr;
|
||||
u8 size;
|
||||
void __iomem *addr;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_fc_db_info *db;
|
||||
struct scmi_perf_get_fc_info *info;
|
||||
struct scmi_msg_resp_perf_desc_fc *resp;
|
||||
|
||||
if (!p_addr)
|
||||
return;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, PERF_DESCRIBE_FASTCHANNEL,
|
||||
SCMI_PROTOCOL_PERF,
|
||||
sizeof(*info), sizeof(*resp), &t);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
info = t->tx.buf;
|
||||
info->domain = cpu_to_le32(domain);
|
||||
info->message_id = cpu_to_le32(message_id);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (ret)
|
||||
goto err_xfer;
|
||||
|
||||
resp = t->rx.buf;
|
||||
flags = le32_to_cpu(resp->attr);
|
||||
size = le32_to_cpu(resp->chan_size);
|
||||
if (!scmi_perf_fc_size_is_valid(message_id, size))
|
||||
goto err_xfer;
|
||||
|
||||
phys_addr = le32_to_cpu(resp->chan_addr_low);
|
||||
phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32;
|
||||
addr = devm_ioremap(handle->dev, phys_addr, size);
|
||||
if (!addr)
|
||||
goto err_xfer;
|
||||
*p_addr = addr;
|
||||
|
||||
if (p_db && SUPPORTS_DOORBELL(flags)) {
|
||||
db = devm_kzalloc(handle->dev, sizeof(*db), GFP_KERNEL);
|
||||
if (!db)
|
||||
goto err_xfer;
|
||||
|
||||
size = 1 << DOORBELL_REG_WIDTH(flags);
|
||||
phys_addr = le32_to_cpu(resp->db_addr_low);
|
||||
phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32;
|
||||
addr = devm_ioremap(handle->dev, phys_addr, size);
|
||||
if (!addr)
|
||||
goto err_xfer;
|
||||
|
||||
db->addr = addr;
|
||||
db->width = size;
|
||||
db->set = le32_to_cpu(resp->db_set_lmask);
|
||||
db->set |= (u64)le32_to_cpu(resp->db_set_hmask) << 32;
|
||||
db->mask = le32_to_cpu(resp->db_preserve_lmask);
|
||||
db->mask |= (u64)le32_to_cpu(resp->db_preserve_hmask) << 32;
|
||||
*p_db = db;
|
||||
}
|
||||
err_xfer:
|
||||
scmi_xfer_put(handle, t);
|
||||
}
|
||||
|
||||
static void scmi_perf_domain_init_fc(const struct scmi_handle *handle,
|
||||
u32 domain, struct scmi_fc_info **p_fc)
|
||||
{
|
||||
struct scmi_fc_info *fc;
|
||||
|
||||
fc = devm_kzalloc(handle->dev, sizeof(*fc), GFP_KERNEL);
|
||||
if (!fc)
|
||||
return;
|
||||
|
||||
scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_SET,
|
||||
&fc->level_set_addr, &fc->level_set_db);
|
||||
scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_GET,
|
||||
&fc->level_get_addr, NULL);
|
||||
scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_SET,
|
||||
&fc->limit_set_addr, &fc->limit_set_db);
|
||||
scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_GET,
|
||||
&fc->limit_get_addr, NULL);
|
||||
*p_fc = fc;
|
||||
}
|
||||
|
||||
/* Device specific ops */
|
||||
static int scmi_dev_domain_id(struct device *dev)
|
||||
{
|
||||
@ -494,6 +731,9 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
|
||||
|
||||
scmi_perf_domain_attributes_get(handle, domain, dom);
|
||||
scmi_perf_describe_levels_get(handle, domain, dom);
|
||||
|
||||
if (dom->perf_fastchannels)
|
||||
scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
|
||||
}
|
||||
|
||||
handle->perf_ops = &perf_ops;
|
||||
|
@ -96,7 +96,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(domain);
|
||||
put_unaligned_le32(domain, t->tx.buf);
|
||||
attr = t->rx.buf;
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
@ -147,11 +147,11 @@ scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(domain);
|
||||
put_unaligned_le32(domain, t->tx.buf);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret)
|
||||
*state = le32_to_cpu(*(__le32 *)t->rx.buf);
|
||||
*state = get_unaligned_le32(t->rx.buf);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
|
231
drivers/firmware/arm_scmi/reset.c
Normal file
231
drivers/firmware/arm_scmi/reset.c
Normal file
@ -0,0 +1,231 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Reset Protocol
|
||||
*
|
||||
* Copyright (C) 2019 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
enum scmi_reset_protocol_cmd {
|
||||
RESET_DOMAIN_ATTRIBUTES = 0x3,
|
||||
RESET = 0x4,
|
||||
RESET_NOTIFY = 0x5,
|
||||
};
|
||||
|
||||
enum scmi_reset_protocol_notify {
|
||||
RESET_ISSUED = 0x0,
|
||||
};
|
||||
|
||||
#define NUM_RESET_DOMAIN_MASK 0xffff
|
||||
#define RESET_NOTIFY_ENABLE BIT(0)
|
||||
|
||||
struct scmi_msg_resp_reset_domain_attributes {
|
||||
__le32 attributes;
|
||||
#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))
|
||||
#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))
|
||||
__le32 latency;
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
};
|
||||
|
||||
struct scmi_msg_reset_domain_reset {
|
||||
__le32 domain_id;
|
||||
__le32 flags;
|
||||
#define AUTONOMOUS_RESET BIT(0)
|
||||
#define EXPLICIT_RESET_ASSERT BIT(1)
|
||||
#define ASYNCHRONOUS_RESET BIT(2)
|
||||
__le32 reset_state;
|
||||
#define ARCH_RESET_TYPE BIT(31)
|
||||
#define COLD_RESET_STATE BIT(0)
|
||||
#define ARCH_COLD_RESET (ARCH_RESET_TYPE | COLD_RESET_STATE)
|
||||
};
|
||||
|
||||
struct reset_dom_info {
|
||||
bool async_reset;
|
||||
bool reset_notify;
|
||||
u32 latency_us;
|
||||
char name[SCMI_MAX_STR_SIZE];
|
||||
};
|
||||
|
||||
struct scmi_reset_info {
|
||||
int num_domains;
|
||||
struct reset_dom_info *dom_info;
|
||||
};
|
||||
|
||||
static int scmi_reset_attributes_get(const struct scmi_handle *handle,
|
||||
struct scmi_reset_info *pi)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
u32 attr;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
|
||||
SCMI_PROTOCOL_RESET, 0, sizeof(attr), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret) {
|
||||
attr = get_unaligned_le32(t->rx.buf);
|
||||
pi->num_domains = attr & NUM_RESET_DOMAIN_MASK;
|
||||
}
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_reset_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
|
||||
struct reset_dom_info *dom_info)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_resp_reset_domain_attributes *attr;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, RESET_DOMAIN_ATTRIBUTES,
|
||||
SCMI_PROTOCOL_RESET, sizeof(domain),
|
||||
sizeof(*attr), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(domain, t->tx.buf);
|
||||
attr = t->rx.buf;
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret) {
|
||||
u32 attributes = le32_to_cpu(attr->attributes);
|
||||
|
||||
dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
|
||||
dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes);
|
||||
dom_info->latency_us = le32_to_cpu(attr->latency);
|
||||
if (dom_info->latency_us == U32_MAX)
|
||||
dom_info->latency_us = 0;
|
||||
strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
|
||||
}
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_reset_num_domains_get(const struct scmi_handle *handle)
|
||||
{
|
||||
struct scmi_reset_info *pi = handle->reset_priv;
|
||||
|
||||
return pi->num_domains;
|
||||
}
|
||||
|
||||
static char *scmi_reset_name_get(const struct scmi_handle *handle, u32 domain)
|
||||
{
|
||||
struct scmi_reset_info *pi = handle->reset_priv;
|
||||
struct reset_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
return dom->name;
|
||||
}
|
||||
|
||||
static int scmi_reset_latency_get(const struct scmi_handle *handle, u32 domain)
|
||||
{
|
||||
struct scmi_reset_info *pi = handle->reset_priv;
|
||||
struct reset_dom_info *dom = pi->dom_info + domain;
|
||||
|
||||
return dom->latency_us;
|
||||
}
|
||||
|
||||
static int scmi_domain_reset(const struct scmi_handle *handle, u32 domain,
|
||||
u32 flags, u32 state)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_reset_domain_reset *dom;
|
||||
struct scmi_reset_info *pi = handle->reset_priv;
|
||||
struct reset_dom_info *rdom = pi->dom_info + domain;
|
||||
|
||||
if (rdom->async_reset)
|
||||
flags |= ASYNCHRONOUS_RESET;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, RESET, SCMI_PROTOCOL_RESET,
|
||||
sizeof(*dom), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dom = t->tx.buf;
|
||||
dom->domain_id = cpu_to_le32(domain);
|
||||
dom->flags = cpu_to_le32(flags);
|
||||
dom->domain_id = cpu_to_le32(state);
|
||||
|
||||
if (rdom->async_reset)
|
||||
ret = scmi_do_xfer_with_response(handle, t);
|
||||
else
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_reset_domain_reset(const struct scmi_handle *handle, u32 domain)
|
||||
{
|
||||
return scmi_domain_reset(handle, domain, AUTONOMOUS_RESET,
|
||||
ARCH_COLD_RESET);
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_reset_domain_assert(const struct scmi_handle *handle, u32 domain)
|
||||
{
|
||||
return scmi_domain_reset(handle, domain, EXPLICIT_RESET_ASSERT,
|
||||
ARCH_COLD_RESET);
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_reset_domain_deassert(const struct scmi_handle *handle, u32 domain)
|
||||
{
|
||||
return scmi_domain_reset(handle, domain, 0, ARCH_COLD_RESET);
|
||||
}
|
||||
|
||||
static struct scmi_reset_ops reset_ops = {
|
||||
.num_domains_get = scmi_reset_num_domains_get,
|
||||
.name_get = scmi_reset_name_get,
|
||||
.latency_get = scmi_reset_latency_get,
|
||||
.reset = scmi_reset_domain_reset,
|
||||
.assert = scmi_reset_domain_assert,
|
||||
.deassert = scmi_reset_domain_deassert,
|
||||
};
|
||||
|
||||
static int scmi_reset_protocol_init(struct scmi_handle *handle)
|
||||
{
|
||||
int domain;
|
||||
u32 version;
|
||||
struct scmi_reset_info *pinfo;
|
||||
|
||||
scmi_version_get(handle, SCMI_PROTOCOL_RESET, &version);
|
||||
|
||||
dev_dbg(handle->dev, "Reset Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
||||
pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
scmi_reset_attributes_get(handle, pinfo);
|
||||
|
||||
pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
|
||||
sizeof(*pinfo->dom_info), GFP_KERNEL);
|
||||
if (!pinfo->dom_info)
|
||||
return -ENOMEM;
|
||||
|
||||
for (domain = 0; domain < pinfo->num_domains; domain++) {
|
||||
struct reset_dom_info *dom = pinfo->dom_info + domain;
|
||||
|
||||
scmi_reset_domain_attributes_get(handle, domain, dom);
|
||||
}
|
||||
|
||||
handle->reset_ops = &reset_ops;
|
||||
handle->reset_priv = pinfo;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init scmi_reset_init(void)
|
||||
{
|
||||
return scmi_protocol_register(SCMI_PROTOCOL_RESET,
|
||||
&scmi_reset_protocol_init);
|
||||
}
|
||||
subsys_initcall(scmi_reset_init);
|
@ -9,8 +9,8 @@
|
||||
|
||||
enum scmi_sensor_protocol_cmd {
|
||||
SENSOR_DESCRIPTION_GET = 0x3,
|
||||
SENSOR_CONFIG_SET = 0x4,
|
||||
SENSOR_TRIP_POINT_SET = 0x5,
|
||||
SENSOR_TRIP_POINT_NOTIFY = 0x4,
|
||||
SENSOR_TRIP_POINT_CONFIG = 0x5,
|
||||
SENSOR_READING_GET = 0x6,
|
||||
};
|
||||
|
||||
@ -42,9 +42,10 @@ struct scmi_msg_resp_sensor_description {
|
||||
} desc[0];
|
||||
};
|
||||
|
||||
struct scmi_msg_set_sensor_config {
|
||||
struct scmi_msg_sensor_trip_point_notify {
|
||||
__le32 id;
|
||||
__le32 event_control;
|
||||
#define SENSOR_TP_NOTIFY_ALL BIT(0)
|
||||
};
|
||||
|
||||
struct scmi_msg_set_sensor_trip_point {
|
||||
@ -119,7 +120,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
|
||||
|
||||
do {
|
||||
/* Set the number of sensors to be skipped/already read */
|
||||
*(__le32 *)t->tx.buf = cpu_to_le32(desc_index);
|
||||
put_unaligned_le32(desc_index, t->tx.buf);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (ret)
|
||||
@ -135,9 +136,10 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < num_returned; cnt++) {
|
||||
u32 attrh;
|
||||
u32 attrh, attrl;
|
||||
struct scmi_sensor_info *s;
|
||||
|
||||
attrl = le32_to_cpu(buf->desc[cnt].attributes_low);
|
||||
attrh = le32_to_cpu(buf->desc[cnt].attributes_high);
|
||||
s = &si->sensors[desc_index + cnt];
|
||||
s->id = le32_to_cpu(buf->desc[cnt].id);
|
||||
@ -146,6 +148,8 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
|
||||
/* Sign extend to a full s8 */
|
||||
if (s->scale & SENSOR_SCALE_SIGN)
|
||||
s->scale |= SENSOR_SCALE_EXTEND;
|
||||
s->async = SUPPORTS_ASYNC_READ(attrl);
|
||||
s->num_trip_points = NUM_TRIP_POINTS(attrl);
|
||||
strlcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
|
||||
}
|
||||
|
||||
@ -160,15 +164,15 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id)
|
||||
static int scmi_sensor_trip_point_notify(const struct scmi_handle *handle,
|
||||
u32 sensor_id, bool enable)
|
||||
{
|
||||
int ret;
|
||||
u32 evt_cntl = BIT(0);
|
||||
u32 evt_cntl = enable ? SENSOR_TP_NOTIFY_ALL : 0;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_set_sensor_config *cfg;
|
||||
struct scmi_msg_sensor_trip_point_notify *cfg;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, SENSOR_CONFIG_SET,
|
||||
ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_NOTIFY,
|
||||
SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -183,15 +187,16 @@ scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_sensor_trip_point_set(const struct scmi_handle *handle,
|
||||
u32 sensor_id, u8 trip_id, u64 trip_value)
|
||||
static int
|
||||
scmi_sensor_trip_point_config(const struct scmi_handle *handle, u32 sensor_id,
|
||||
u8 trip_id, u64 trip_value)
|
||||
{
|
||||
int ret;
|
||||
u32 evt_cntl = SENSOR_TP_BOTH;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_set_sensor_trip_point *trip;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_SET,
|
||||
ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_CONFIG,
|
||||
SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -209,11 +214,13 @@ static int scmi_sensor_trip_point_set(const struct scmi_handle *handle,
|
||||
}
|
||||
|
||||
static int scmi_sensor_reading_get(const struct scmi_handle *handle,
|
||||
u32 sensor_id, bool async, u64 *value)
|
||||
u32 sensor_id, u64 *value)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct scmi_msg_sensor_reading_get *sensor;
|
||||
struct sensors_info *si = handle->sensor_priv;
|
||||
struct scmi_sensor_info *s = si->sensors + sensor_id;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, SENSOR_READING_GET,
|
||||
SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
|
||||
@ -223,14 +230,18 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle,
|
||||
|
||||
sensor = t->tx.buf;
|
||||
sensor->id = cpu_to_le32(sensor_id);
|
||||
sensor->flags = cpu_to_le32(async ? SENSOR_READ_ASYNC : 0);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret) {
|
||||
__le32 *pval = t->rx.buf;
|
||||
|
||||
*value = le32_to_cpu(*pval);
|
||||
*value |= (u64)le32_to_cpu(*(pval + 1)) << 32;
|
||||
if (s->async) {
|
||||
sensor->flags = cpu_to_le32(SENSOR_READ_ASYNC);
|
||||
ret = scmi_do_xfer_with_response(handle, t);
|
||||
if (!ret)
|
||||
*value = get_unaligned_le64((void *)
|
||||
((__le32 *)t->rx.buf + 1));
|
||||
} else {
|
||||
sensor->flags = cpu_to_le32(0);
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret)
|
||||
*value = get_unaligned_le64(t->rx.buf);
|
||||
}
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
@ -255,8 +266,8 @@ static int scmi_sensor_count_get(const struct scmi_handle *handle)
|
||||
static struct scmi_sensor_ops sensor_ops = {
|
||||
.count_get = scmi_sensor_count_get,
|
||||
.info_get = scmi_sensor_info_get,
|
||||
.configuration_set = scmi_sensor_configuration_set,
|
||||
.trip_point_set = scmi_sensor_trip_point_set,
|
||||
.trip_point_notify = scmi_sensor_trip_point_notify,
|
||||
.trip_point_config = scmi_sensor_trip_point_config,
|
||||
.reading_get = scmi_sensor_reading_get,
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,15 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config IMX_DSP
|
||||
bool "IMX DSP Protocol driver"
|
||||
depends on IMX_MBOX
|
||||
help
|
||||
This enables DSP IPC protocol between host AP (Linux)
|
||||
and the firmware running on DSP.
|
||||
DSP exists on some i.MX8 processors (e.g i.MX8QM, i.MX8QXP).
|
||||
|
||||
It acts like a doorbell. Client might use shared memory to
|
||||
exchange information with DSP side.
|
||||
|
||||
config IMX_SCU
|
||||
bool "IMX SCU Protocol driver"
|
||||
depends on IMX_MBOX
|
||||
|
@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_IMX_DSP) += imx-dsp.o
|
||||
obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o
|
||||
obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o
|
||||
|
155
drivers/firmware/imx/imx-dsp.c
Normal file
155
drivers/firmware/imx/imx-dsp.c
Normal file
@ -0,0 +1,155 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
* Author: Daniel Baluta <daniel.baluta@nxp.com>
|
||||
*
|
||||
* Implementation of the DSP IPC interface (host side)
|
||||
*/
|
||||
|
||||
#include <linux/firmware/imx/dsp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP)
|
||||
*
|
||||
* @dsp: DSP IPC handle
|
||||
* @chan_idx: index of the channel where to trigger the interrupt
|
||||
*
|
||||
* Returns non-negative value for success, negative value for error
|
||||
*/
|
||||
int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx)
|
||||
{
|
||||
int ret;
|
||||
struct imx_dsp_chan *dsp_chan;
|
||||
|
||||
if (idx >= DSP_MU_CHAN_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
dsp_chan = &ipc->chans[idx];
|
||||
ret = mbox_send_message(dsp_chan->ch, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(imx_dsp_ring_doorbell);
|
||||
|
||||
/*
|
||||
* imx_dsp_handle_rx - rx callback used by imx mailbox
|
||||
*
|
||||
* @c: mbox client
|
||||
* @msg: message received
|
||||
*
|
||||
* Users of DSP IPC will need to privde handle_reply and handle_request
|
||||
* callbacks.
|
||||
*/
|
||||
static void imx_dsp_handle_rx(struct mbox_client *c, void *msg)
|
||||
{
|
||||
struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl);
|
||||
|
||||
if (chan->idx == 0) {
|
||||
chan->ipc->ops->handle_reply(chan->ipc);
|
||||
} else {
|
||||
chan->ipc->ops->handle_request(chan->ipc);
|
||||
imx_dsp_ring_doorbell(chan->ipc, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int imx_dsp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct imx_dsp_ipc *dsp_ipc;
|
||||
struct imx_dsp_chan *dsp_chan;
|
||||
struct mbox_client *cl;
|
||||
char *chan_name;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
|
||||
|
||||
dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL);
|
||||
if (!dsp_ipc)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < DSP_MU_CHAN_NUM; i++) {
|
||||
if (i < 2)
|
||||
chan_name = kasprintf(GFP_KERNEL, "txdb%d", i);
|
||||
else
|
||||
chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2);
|
||||
|
||||
if (!chan_name)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp_chan = &dsp_ipc->chans[i];
|
||||
cl = &dsp_chan->cl;
|
||||
cl->dev = dev;
|
||||
cl->tx_block = false;
|
||||
cl->knows_txdone = true;
|
||||
cl->rx_callback = imx_dsp_handle_rx;
|
||||
|
||||
dsp_chan->ipc = dsp_ipc;
|
||||
dsp_chan->idx = i % 2;
|
||||
dsp_chan->ch = mbox_request_channel_byname(cl, chan_name);
|
||||
if (IS_ERR(dsp_chan->ch)) {
|
||||
ret = PTR_ERR(dsp_chan->ch);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to request mbox chan %s ret %d\n",
|
||||
chan_name, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "request mbox chan %s\n", chan_name);
|
||||
/* chan_name is not used anymore by framework */
|
||||
kfree(chan_name);
|
||||
}
|
||||
|
||||
dsp_ipc->dev = dev;
|
||||
|
||||
dev_set_drvdata(dev, dsp_ipc);
|
||||
|
||||
dev_info(dev, "NXP i.MX DSP IPC initialized\n");
|
||||
|
||||
return devm_of_platform_populate(dev);
|
||||
out:
|
||||
kfree(chan_name);
|
||||
for (j = 0; j < i; j++) {
|
||||
dsp_chan = &dsp_ipc->chans[j];
|
||||
mbox_free_channel(dsp_chan->ch);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int imx_dsp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_dsp_chan *dsp_chan;
|
||||
struct imx_dsp_ipc *dsp_ipc;
|
||||
int i;
|
||||
|
||||
dsp_ipc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
for (i = 0; i < DSP_MU_CHAN_NUM; i++) {
|
||||
dsp_chan = &dsp_ipc->chans[i];
|
||||
mbox_free_channel(dsp_chan->ch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver imx_dsp_driver = {
|
||||
.driver = {
|
||||
.name = "imx-dsp",
|
||||
},
|
||||
.probe = imx_dsp_probe,
|
||||
.remove = imx_dsp_remove,
|
||||
};
|
||||
builtin_platform_driver(imx_dsp_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
|
||||
MODULE_DESCRIPTION("IMX DSP IPC protocol driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -92,7 +92,8 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
||||
{ "gpt", IMX_SC_R_GPT_0, 5, true, 0 },
|
||||
{ "kpp", IMX_SC_R_KPP, 1, false, 0 },
|
||||
{ "fspi", IMX_SC_R_FSPI_0, 2, true, 0 },
|
||||
{ "mu", IMX_SC_R_MU_0A, 14, true, 0 },
|
||||
{ "mu_a", IMX_SC_R_MU_0A, 14, true, 0 },
|
||||
{ "mu_b", IMX_SC_R_MU_13B, 1, true, 13 },
|
||||
|
||||
/* CONN SS */
|
||||
{ "usb", IMX_SC_R_USB_0, 2, true, 0 },
|
||||
@ -130,6 +131,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
|
||||
{ "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 },
|
||||
{ "lpuart", IMX_SC_R_UART_0, 4, true, 0 },
|
||||
{ "lpspi", IMX_SC_R_SPI_0, 4, true, 0 },
|
||||
{ "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 },
|
||||
|
||||
/* VPU SS */
|
||||
{ "vpu", IMX_SC_R_VPU, 1, false, 0 },
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
@ -425,21 +426,23 @@ EXPORT_SYMBOL(qcom_scm_set_remote_state);
|
||||
* @mem_sz: size of the region.
|
||||
* @srcvm: vmid for current set of owners, each set bit in
|
||||
* flag indicate a unique owner
|
||||
* @newvm: array having new owners and corrsponding permission
|
||||
* @newvm: array having new owners and corresponding permission
|
||||
* flags
|
||||
* @dest_cnt: number of owners in next set.
|
||||
*
|
||||
* Return negative errno on failure, 0 on success, with @srcvm updated.
|
||||
* Return negative errno on failure or 0 on success with @srcvm updated.
|
||||
*/
|
||||
int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
|
||||
unsigned int *srcvm,
|
||||
struct qcom_scm_vmperm *newvm, int dest_cnt)
|
||||
const struct qcom_scm_vmperm *newvm,
|
||||
unsigned int dest_cnt)
|
||||
{
|
||||
struct qcom_scm_current_perm_info *destvm;
|
||||
struct qcom_scm_mem_map_info *mem_to_map;
|
||||
phys_addr_t mem_to_map_phys;
|
||||
phys_addr_t dest_phys;
|
||||
phys_addr_t ptr_phys;
|
||||
dma_addr_t ptr_dma;
|
||||
size_t mem_to_map_sz;
|
||||
size_t dest_sz;
|
||||
size_t src_sz;
|
||||
@ -447,52 +450,50 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
|
||||
int next_vm;
|
||||
__le32 *src;
|
||||
void *ptr;
|
||||
int ret;
|
||||
int len;
|
||||
int i;
|
||||
int ret, i, b;
|
||||
unsigned long srcvm_bits = *srcvm;
|
||||
|
||||
src_sz = hweight_long(*srcvm) * sizeof(*src);
|
||||
src_sz = hweight_long(srcvm_bits) * sizeof(*src);
|
||||
mem_to_map_sz = sizeof(*mem_to_map);
|
||||
dest_sz = dest_cnt * sizeof(*destvm);
|
||||
ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) +
|
||||
ALIGN(dest_sz, SZ_64);
|
||||
|
||||
ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL);
|
||||
ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_dma, GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
ptr_phys = dma_to_phys(__scm->dev, ptr_dma);
|
||||
|
||||
/* Fill source vmid detail */
|
||||
src = ptr;
|
||||
len = hweight_long(*srcvm);
|
||||
for (i = 0; i < len; i++) {
|
||||
src[i] = cpu_to_le32(ffs(*srcvm) - 1);
|
||||
*srcvm ^= 1 << (ffs(*srcvm) - 1);
|
||||
}
|
||||
i = 0;
|
||||
for_each_set_bit(b, &srcvm_bits, BITS_PER_LONG)
|
||||
src[i++] = cpu_to_le32(b);
|
||||
|
||||
/* Fill details of mem buff to map */
|
||||
mem_to_map = ptr + ALIGN(src_sz, SZ_64);
|
||||
mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64);
|
||||
mem_to_map[0].mem_addr = cpu_to_le64(mem_addr);
|
||||
mem_to_map[0].mem_size = cpu_to_le64(mem_sz);
|
||||
mem_to_map->mem_addr = cpu_to_le64(mem_addr);
|
||||
mem_to_map->mem_size = cpu_to_le64(mem_sz);
|
||||
|
||||
next_vm = 0;
|
||||
/* Fill details of next vmid detail */
|
||||
destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64);
|
||||
dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64);
|
||||
for (i = 0; i < dest_cnt; i++) {
|
||||
destvm[i].vmid = cpu_to_le32(newvm[i].vmid);
|
||||
destvm[i].perm = cpu_to_le32(newvm[i].perm);
|
||||
destvm[i].ctx = 0;
|
||||
destvm[i].ctx_size = 0;
|
||||
next_vm |= BIT(newvm[i].vmid);
|
||||
for (i = 0; i < dest_cnt; i++, destvm++, newvm++) {
|
||||
destvm->vmid = cpu_to_le32(newvm->vmid);
|
||||
destvm->perm = cpu_to_le32(newvm->perm);
|
||||
destvm->ctx = 0;
|
||||
destvm->ctx_size = 0;
|
||||
next_vm |= BIT(newvm->vmid);
|
||||
}
|
||||
|
||||
ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz,
|
||||
ptr_phys, src_sz, dest_phys, dest_sz);
|
||||
dma_free_coherent(__scm->dev, ALIGN(ptr_sz, SZ_64), ptr, ptr_phys);
|
||||
dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_dma);
|
||||
if (ret) {
|
||||
dev_err(__scm->dev,
|
||||
"Assign memory protection call failed %d.\n", ret);
|
||||
"Assign memory protection call failed %d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -635,6 +635,7 @@ fail:
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_get_device() - command to request for device managed by TISCI
|
||||
* that can be shared with other hosts.
|
||||
* @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
|
||||
* @id: Device Identifier
|
||||
*
|
||||
@ -642,11 +643,29 @@ fail:
|
||||
* usage count by balancing get_device with put_device. No refcounting is
|
||||
* managed by driver for that purpose.
|
||||
*
|
||||
* NOTE: The request is for exclusive access for the processor.
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id)
|
||||
{
|
||||
return ti_sci_set_device_state(handle, id, 0,
|
||||
MSG_DEVICE_SW_STATE_ON);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_get_device_exclusive() - command to request for device managed by
|
||||
* TISCI that is exclusively owned by the
|
||||
* requesting host.
|
||||
* @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
|
||||
* @id: Device Identifier
|
||||
*
|
||||
* Request for the device - NOTE: the client MUST maintain integrity of
|
||||
* usage count by balancing get_device with put_device. No refcounting is
|
||||
* managed by driver for that purpose.
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_get_device_exclusive(const struct ti_sci_handle *handle,
|
||||
u32 id)
|
||||
{
|
||||
return ti_sci_set_device_state(handle, id,
|
||||
MSG_FLAG_DEVICE_EXCLUSIVE,
|
||||
@ -665,6 +684,26 @@ static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id)
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_idle_device(const struct ti_sci_handle *handle, u32 id)
|
||||
{
|
||||
return ti_sci_set_device_state(handle, id, 0,
|
||||
MSG_DEVICE_SW_STATE_RETENTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_cmd_idle_device_exclusive() - Command to idle a device managed by
|
||||
* TISCI that is exclusively owned by
|
||||
* requesting host.
|
||||
* @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle
|
||||
* @id: Device Identifier
|
||||
*
|
||||
* Request for the device - NOTE: the client MUST maintain integrity of
|
||||
* usage count by balancing get_device with put_device. No refcounting is
|
||||
* managed by driver for that purpose.
|
||||
*
|
||||
* Return: 0 if all went fine, else return appropriate error.
|
||||
*/
|
||||
static int ti_sci_cmd_idle_device_exclusive(const struct ti_sci_handle *handle,
|
||||
u32 id)
|
||||
{
|
||||
return ti_sci_set_device_state(handle, id,
|
||||
MSG_FLAG_DEVICE_EXCLUSIVE,
|
||||
@ -2894,7 +2933,9 @@ static void ti_sci_setup_ops(struct ti_sci_info *info)
|
||||
core_ops->reboot_device = ti_sci_cmd_core_reboot;
|
||||
|
||||
dops->get_device = ti_sci_cmd_get_device;
|
||||
dops->get_device_exclusive = ti_sci_cmd_get_device_exclusive;
|
||||
dops->idle_device = ti_sci_cmd_idle_device;
|
||||
dops->idle_device_exclusive = ti_sci_cmd_idle_device_exclusive;
|
||||
dops->put_device = ti_sci_cmd_put_device;
|
||||
|
||||
dops->is_valid = ti_sci_cmd_dev_is_valid;
|
||||
|
384
drivers/firmware/turris-mox-rwtm.c
Normal file
384
drivers/firmware/turris-mox-rwtm.c
Normal file
@ -0,0 +1,384 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Turris Mox rWTM firmware driver
|
||||
*
|
||||
* Copyright (C) 2019 Marek Behun <marek.behun@nic.cz>
|
||||
*/
|
||||
|
||||
#include <linux/armada-37xx-rwtm-mailbox.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/mailbox_client.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DRIVER_NAME "turris-mox-rwtm"
|
||||
|
||||
/*
|
||||
* The macros and constants below come from Turris Mox's rWTM firmware code.
|
||||
* This firmware is open source and it's sources can be found at
|
||||
* https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi.
|
||||
*/
|
||||
|
||||
#define MBOX_STS_SUCCESS (0 << 30)
|
||||
#define MBOX_STS_FAIL (1 << 30)
|
||||
#define MBOX_STS_BADCMD (2 << 30)
|
||||
#define MBOX_STS_ERROR(s) ((s) & (3 << 30))
|
||||
#define MBOX_STS_VALUE(s) (((s) >> 10) & 0xfffff)
|
||||
#define MBOX_STS_CMD(s) ((s) & 0x3ff)
|
||||
|
||||
enum mbox_cmd {
|
||||
MBOX_CMD_GET_RANDOM = 1,
|
||||
MBOX_CMD_BOARD_INFO = 2,
|
||||
MBOX_CMD_ECDSA_PUB_KEY = 3,
|
||||
MBOX_CMD_HASH = 4,
|
||||
MBOX_CMD_SIGN = 5,
|
||||
MBOX_CMD_VERIFY = 6,
|
||||
|
||||
MBOX_CMD_OTP_READ = 7,
|
||||
MBOX_CMD_OTP_WRITE = 8,
|
||||
};
|
||||
|
||||
struct mox_kobject;
|
||||
|
||||
struct mox_rwtm {
|
||||
struct device *dev;
|
||||
struct mbox_client mbox_client;
|
||||
struct mbox_chan *mbox;
|
||||
struct mox_kobject *kobj;
|
||||
struct hwrng hwrng;
|
||||
|
||||
struct armada_37xx_rwtm_rx_msg reply;
|
||||
|
||||
void *buf;
|
||||
dma_addr_t buf_phys;
|
||||
|
||||
struct mutex busy;
|
||||
struct completion cmd_done;
|
||||
|
||||
/* board information */
|
||||
int has_board_info;
|
||||
u64 serial_number;
|
||||
int board_version, ram_size;
|
||||
u8 mac_address1[6], mac_address2[6];
|
||||
|
||||
/* public key burned in eFuse */
|
||||
int has_pubkey;
|
||||
u8 pubkey[135];
|
||||
};
|
||||
|
||||
struct mox_kobject {
|
||||
struct kobject kobj;
|
||||
struct mox_rwtm *rwtm;
|
||||
};
|
||||
|
||||
static inline struct kobject *rwtm_to_kobj(struct mox_rwtm *rwtm)
|
||||
{
|
||||
return &rwtm->kobj->kobj;
|
||||
}
|
||||
|
||||
static inline struct mox_rwtm *to_rwtm(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct mox_kobject, kobj)->rwtm;
|
||||
}
|
||||
|
||||
static void mox_kobj_release(struct kobject *kobj)
|
||||
{
|
||||
kfree(to_rwtm(kobj)->kobj);
|
||||
}
|
||||
|
||||
static struct kobj_type mox_kobj_ktype = {
|
||||
.release = mox_kobj_release,
|
||||
.sysfs_ops = &kobj_sysfs_ops,
|
||||
};
|
||||
|
||||
static int mox_kobj_create(struct mox_rwtm *rwtm)
|
||||
{
|
||||
rwtm->kobj = kzalloc(sizeof(*rwtm->kobj), GFP_KERNEL);
|
||||
if (!rwtm->kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
kobject_init(rwtm_to_kobj(rwtm), &mox_kobj_ktype);
|
||||
if (kobject_add(rwtm_to_kobj(rwtm), firmware_kobj, "turris-mox-rwtm")) {
|
||||
kobject_put(rwtm_to_kobj(rwtm));
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
rwtm->kobj->rwtm = rwtm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MOX_ATTR_RO(name, format, cat) \
|
||||
static ssize_t \
|
||||
name##_show(struct kobject *kobj, struct kobj_attribute *a, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct mox_rwtm *rwtm = to_rwtm(kobj); \
|
||||
if (!rwtm->has_##cat) \
|
||||
return -ENODATA; \
|
||||
return sprintf(buf, format, rwtm->name); \
|
||||
} \
|
||||
static struct kobj_attribute mox_attr_##name = __ATTR_RO(name)
|
||||
|
||||
MOX_ATTR_RO(serial_number, "%016llX\n", board_info);
|
||||
MOX_ATTR_RO(board_version, "%i\n", board_info);
|
||||
MOX_ATTR_RO(ram_size, "%i\n", board_info);
|
||||
MOX_ATTR_RO(mac_address1, "%pM\n", board_info);
|
||||
MOX_ATTR_RO(mac_address2, "%pM\n", board_info);
|
||||
MOX_ATTR_RO(pubkey, "%s\n", pubkey);
|
||||
|
||||
static int mox_get_status(enum mbox_cmd cmd, u32 retval)
|
||||
{
|
||||
if (MBOX_STS_CMD(retval) != cmd ||
|
||||
MBOX_STS_ERROR(retval) != MBOX_STS_SUCCESS)
|
||||
return -EIO;
|
||||
else if (MBOX_STS_ERROR(retval) == MBOX_STS_FAIL)
|
||||
return -(int)MBOX_STS_VALUE(retval);
|
||||
else
|
||||
return MBOX_STS_VALUE(retval);
|
||||
}
|
||||
|
||||
static const struct attribute *mox_rwtm_attrs[] = {
|
||||
&mox_attr_serial_number.attr,
|
||||
&mox_attr_board_version.attr,
|
||||
&mox_attr_ram_size.attr,
|
||||
&mox_attr_mac_address1.attr,
|
||||
&mox_attr_mac_address2.attr,
|
||||
&mox_attr_pubkey.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void mox_rwtm_rx_callback(struct mbox_client *cl, void *data)
|
||||
{
|
||||
struct mox_rwtm *rwtm = dev_get_drvdata(cl->dev);
|
||||
struct armada_37xx_rwtm_rx_msg *msg = data;
|
||||
|
||||
rwtm->reply = *msg;
|
||||
complete(&rwtm->cmd_done);
|
||||
}
|
||||
|
||||
static void reply_to_mac_addr(u8 *mac, u32 t1, u32 t2)
|
||||
{
|
||||
mac[0] = t1 >> 8;
|
||||
mac[1] = t1;
|
||||
mac[2] = t2 >> 24;
|
||||
mac[3] = t2 >> 16;
|
||||
mac[4] = t2 >> 8;
|
||||
mac[5] = t2;
|
||||
}
|
||||
|
||||
static int mox_get_board_info(struct mox_rwtm *rwtm)
|
||||
{
|
||||
struct armada_37xx_rwtm_tx_msg msg;
|
||||
struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply;
|
||||
int ret;
|
||||
|
||||
msg.command = MBOX_CMD_BOARD_INFO;
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mox_get_status(MBOX_CMD_BOARD_INFO, reply->retval);
|
||||
if (ret < 0 && ret != -ENODATA) {
|
||||
return ret;
|
||||
} else if (ret == -ENODATA) {
|
||||
dev_warn(rwtm->dev,
|
||||
"Board does not have manufacturing information burned!\n");
|
||||
} else {
|
||||
rwtm->serial_number = reply->status[1];
|
||||
rwtm->serial_number <<= 32;
|
||||
rwtm->serial_number |= reply->status[0];
|
||||
rwtm->board_version = reply->status[2];
|
||||
rwtm->ram_size = reply->status[3];
|
||||
reply_to_mac_addr(rwtm->mac_address1, reply->status[4],
|
||||
reply->status[5]);
|
||||
reply_to_mac_addr(rwtm->mac_address2, reply->status[6],
|
||||
reply->status[7]);
|
||||
rwtm->has_board_info = 1;
|
||||
|
||||
pr_info("Turris Mox serial number %016llX\n",
|
||||
rwtm->serial_number);
|
||||
pr_info(" board version %i\n", rwtm->board_version);
|
||||
pr_info(" burned RAM size %i MiB\n", rwtm->ram_size);
|
||||
}
|
||||
|
||||
msg.command = MBOX_CMD_ECDSA_PUB_KEY;
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mox_get_status(MBOX_CMD_ECDSA_PUB_KEY, reply->retval);
|
||||
if (ret < 0 && ret != -ENODATA) {
|
||||
return ret;
|
||||
} else if (ret == -ENODATA) {
|
||||
dev_warn(rwtm->dev, "Board has no public key burned!\n");
|
||||
} else {
|
||||
u32 *s = reply->status;
|
||||
|
||||
rwtm->has_pubkey = 1;
|
||||
sprintf(rwtm->pubkey,
|
||||
"%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
|
||||
ret, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],
|
||||
s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
||||
{
|
||||
struct mox_rwtm *rwtm = (struct mox_rwtm *) rng->priv;
|
||||
struct armada_37xx_rwtm_tx_msg msg;
|
||||
int ret;
|
||||
|
||||
if (max > 4096)
|
||||
max = 4096;
|
||||
|
||||
msg.command = MBOX_CMD_GET_RANDOM;
|
||||
msg.args[0] = 1;
|
||||
msg.args[1] = rwtm->buf_phys;
|
||||
msg.args[2] = (max + 3) & ~3;
|
||||
|
||||
if (!wait) {
|
||||
if (!mutex_trylock(&rwtm->busy))
|
||||
return -EBUSY;
|
||||
} else {
|
||||
mutex_lock(&rwtm->busy);
|
||||
}
|
||||
|
||||
ret = mbox_send_message(rwtm->mbox, &msg);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
ret = wait_for_completion_interruptible(&rwtm->cmd_done);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
ret = mox_get_status(MBOX_CMD_GET_RANDOM, rwtm->reply.retval);
|
||||
if (ret < 0)
|
||||
goto unlock_mutex;
|
||||
|
||||
memcpy(data, rwtm->buf, max);
|
||||
ret = max;
|
||||
|
||||
unlock_mutex:
|
||||
mutex_unlock(&rwtm->busy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int turris_mox_rwtm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mox_rwtm *rwtm;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
rwtm = devm_kzalloc(dev, sizeof(*rwtm), GFP_KERNEL);
|
||||
if (!rwtm)
|
||||
return -ENOMEM;
|
||||
|
||||
rwtm->dev = dev;
|
||||
rwtm->buf = dmam_alloc_coherent(dev, PAGE_SIZE, &rwtm->buf_phys,
|
||||
GFP_KERNEL);
|
||||
if (!rwtm->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mox_kobj_create(rwtm);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot create turris-mox-rwtm kobject!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sysfs_create_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot create sysfs files!\n");
|
||||
goto put_kobj;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rwtm);
|
||||
|
||||
mutex_init(&rwtm->busy);
|
||||
|
||||
rwtm->mbox_client.dev = dev;
|
||||
rwtm->mbox_client.rx_callback = mox_rwtm_rx_callback;
|
||||
|
||||
rwtm->mbox = mbox_request_channel(&rwtm->mbox_client, 0);
|
||||
if (IS_ERR(rwtm->mbox)) {
|
||||
ret = PTR_ERR(rwtm->mbox);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Cannot request mailbox channel: %i\n",
|
||||
ret);
|
||||
goto remove_files;
|
||||
}
|
||||
|
||||
init_completion(&rwtm->cmd_done);
|
||||
|
||||
ret = mox_get_board_info(rwtm);
|
||||
if (ret < 0)
|
||||
dev_warn(dev, "Cannot read board information: %i\n", ret);
|
||||
|
||||
rwtm->hwrng.name = DRIVER_NAME "_hwrng";
|
||||
rwtm->hwrng.read = mox_hwrng_read;
|
||||
rwtm->hwrng.priv = (unsigned long) rwtm;
|
||||
rwtm->hwrng.quality = 1024;
|
||||
|
||||
ret = devm_hwrng_register(dev, &rwtm->hwrng);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot register HWRNG: %i\n", ret);
|
||||
goto free_channel;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_channel:
|
||||
mbox_free_channel(rwtm->mbox);
|
||||
remove_files:
|
||||
sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
|
||||
put_kobj:
|
||||
kobject_put(rwtm_to_kobj(rwtm));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int turris_mox_rwtm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mox_rwtm *rwtm = platform_get_drvdata(pdev);
|
||||
|
||||
sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs);
|
||||
kobject_put(rwtm_to_kobj(rwtm));
|
||||
mbox_free_channel(rwtm->mbox);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id turris_mox_rwtm_match[] = {
|
||||
{ .compatible = "cznic,turris-mox-rwtm", },
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, turris_mox_rwtm_match);
|
||||
|
||||
static struct platform_driver turris_mox_rwtm_driver = {
|
||||
.probe = turris_mox_rwtm_probe,
|
||||
.remove = turris_mox_rwtm_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = turris_mox_rwtm_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(turris_mox_rwtm_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Turris Mox rWTM firmware driver");
|
||||
MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>");
|
@ -1453,6 +1453,15 @@ config GPIO_XRA1403
|
||||
help
|
||||
GPIO driver for EXAR XRA1403 16-bit SPI-based GPIO expander.
|
||||
|
||||
config GPIO_MOXTET
|
||||
tristate "Turris Mox Moxtet bus GPIO expander"
|
||||
depends on MOXTET
|
||||
help
|
||||
Say yes here if you are building for the Turris Mox router.
|
||||
This is the driver needed for configuring the GPIOs via the Moxtet
|
||||
bus. For example the Mox module with SFP cage needs this driver
|
||||
so that phylink can use corresponding GPIOs.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "USB GPIO expanders"
|
||||
|
@ -92,6 +92,7 @@ obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
|
||||
obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o
|
||||
obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o
|
||||
obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o
|
||||
obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o
|
||||
obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
|
||||
obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o
|
||||
obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o
|
||||
|
179
drivers/gpio/gpio-moxtet.c
Normal file
179
drivers/gpio/gpio-moxtet.c
Normal file
@ -0,0 +1,179 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Turris Mox Moxtet GPIO expander
|
||||
*
|
||||
* Copyright (C) 2018 Marek Behun <marek.behun@nic.cz>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/moxtet.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define MOXTET_GPIO_NGPIOS 12
|
||||
#define MOXTET_GPIO_INPUTS 4
|
||||
|
||||
struct moxtet_gpio_desc {
|
||||
u16 in_mask;
|
||||
u16 out_mask;
|
||||
};
|
||||
|
||||
static const struct moxtet_gpio_desc descs[] = {
|
||||
[TURRIS_MOX_MODULE_SFP] = {
|
||||
.in_mask = GENMASK(2, 0),
|
||||
.out_mask = GENMASK(5, 4),
|
||||
},
|
||||
};
|
||||
|
||||
struct moxtet_gpio_chip {
|
||||
struct device *dev;
|
||||
struct gpio_chip gpio_chip;
|
||||
const struct moxtet_gpio_desc *desc;
|
||||
};
|
||||
|
||||
static int moxtet_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
if (chip->desc->in_mask & BIT(offset)) {
|
||||
ret = moxtet_device_read(chip->dev);
|
||||
} else if (chip->desc->out_mask & BIT(offset)) {
|
||||
ret = moxtet_device_written(chip->dev);
|
||||
if (ret >= 0)
|
||||
ret <<= MOXTET_GPIO_INPUTS;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(ret & BIT(offset));
|
||||
}
|
||||
|
||||
static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
|
||||
int val)
|
||||
{
|
||||
struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
int state;
|
||||
|
||||
state = moxtet_device_written(chip->dev);
|
||||
if (state < 0)
|
||||
return;
|
||||
|
||||
offset -= MOXTET_GPIO_INPUTS;
|
||||
|
||||
if (val)
|
||||
state |= BIT(offset);
|
||||
else
|
||||
state &= ~BIT(offset);
|
||||
|
||||
moxtet_device_write(chip->dev, state);
|
||||
}
|
||||
|
||||
static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
|
||||
/* All lines are hard wired to be either input or output, not both. */
|
||||
if (chip->desc->in_mask & BIT(offset))
|
||||
return 1;
|
||||
else if (chip->desc->out_mask & BIT(offset))
|
||||
return 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int moxtet_gpio_direction_input(struct gpio_chip *gc,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
|
||||
if (chip->desc->in_mask & BIT(offset))
|
||||
return 0;
|
||||
else if (chip->desc->out_mask & BIT(offset))
|
||||
return -ENOTSUPP;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int moxtet_gpio_direction_output(struct gpio_chip *gc,
|
||||
unsigned int offset, int val)
|
||||
{
|
||||
struct moxtet_gpio_chip *chip = gpiochip_get_data(gc);
|
||||
|
||||
if (chip->desc->out_mask & BIT(offset))
|
||||
moxtet_gpio_set_value(gc, offset, val);
|
||||
else if (chip->desc->in_mask & BIT(offset))
|
||||
return -ENOTSUPP;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int moxtet_gpio_probe(struct device *dev)
|
||||
{
|
||||
struct moxtet_gpio_chip *chip;
|
||||
struct device_node *nc = dev->of_node;
|
||||
int id;
|
||||
|
||||
id = to_moxtet_device(dev)->id;
|
||||
|
||||
if (id >= ARRAY_SIZE(descs)) {
|
||||
dev_err(dev, "%pOF Moxtet device id 0x%x is not supported by gpio-moxtet driver\n",
|
||||
nc, id);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->dev = dev;
|
||||
chip->gpio_chip.parent = dev;
|
||||
chip->desc = &descs[id];
|
||||
|
||||
dev_set_drvdata(dev, chip);
|
||||
|
||||
chip->gpio_chip.label = dev_name(dev);
|
||||
chip->gpio_chip.get_direction = moxtet_gpio_get_direction;
|
||||
chip->gpio_chip.direction_input = moxtet_gpio_direction_input;
|
||||
chip->gpio_chip.direction_output = moxtet_gpio_direction_output;
|
||||
chip->gpio_chip.get = moxtet_gpio_get_value;
|
||||
chip->gpio_chip.set = moxtet_gpio_set_value;
|
||||
chip->gpio_chip.base = -1;
|
||||
|
||||
chip->gpio_chip.ngpio = MOXTET_GPIO_NGPIOS;
|
||||
|
||||
chip->gpio_chip.can_sleep = true;
|
||||
chip->gpio_chip.owner = THIS_MODULE;
|
||||
|
||||
return devm_gpiochip_add_data(dev, &chip->gpio_chip, chip);
|
||||
}
|
||||
|
||||
static const struct of_device_id moxtet_gpio_dt_ids[] = {
|
||||
{ .compatible = "cznic,moxtet-gpio", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, moxtet_gpio_dt_ids);
|
||||
|
||||
static const enum turris_mox_module_id moxtet_gpio_module_table[] = {
|
||||
TURRIS_MOX_MODULE_SFP,
|
||||
0,
|
||||
};
|
||||
|
||||
static struct moxtet_driver moxtet_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "moxtet-gpio",
|
||||
.of_match_table = moxtet_gpio_dt_ids,
|
||||
.probe = moxtet_gpio_probe,
|
||||
},
|
||||
.id_table = moxtet_gpio_module_table,
|
||||
};
|
||||
module_moxtet_driver(moxtet_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>");
|
||||
MODULE_DESCRIPTION("Turris Mox Moxtet GPIO expander");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -72,7 +72,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
const struct scmi_handle *h = scmi_sensors->handle;
|
||||
|
||||
sensor = *(scmi_sensors->info[type] + channel);
|
||||
ret = h->sensor_ops->reading_get(h, sensor->id, false, &value);
|
||||
ret = h->sensor_ops->reading_get(h, sensor->id, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -151,7 +151,6 @@ config NET_NETX
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called netx-eth.
|
||||
|
||||
source "drivers/net/ethernet/nuvoton/Kconfig"
|
||||
source "drivers/net/ethernet/nvidia/Kconfig"
|
||||
source "drivers/net/ethernet/nxp/Kconfig"
|
||||
source "drivers/net/ethernet/oki-semi/Kconfig"
|
||||
|
@ -65,7 +65,6 @@ obj-$(CONFIG_NET_VENDOR_NETERION) += neterion/
|
||||
obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/
|
||||
obj-$(CONFIG_NET_VENDOR_NI) += ni/
|
||||
obj-$(CONFIG_NET_NETX) += netx-eth.o
|
||||
obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/
|
||||
obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/
|
||||
obj-$(CONFIG_LPC_ENET) += nxp/
|
||||
obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/
|
||||
|
@ -6,8 +6,7 @@
|
||||
config NET_VENDOR_MICREL
|
||||
bool "Micrel devices"
|
||||
default y
|
||||
depends on (HAS_IOMEM && DMA_ENGINE) || SPI || PCI || HAS_IOMEM || \
|
||||
(ARM && ARCH_KS8695)
|
||||
depends on (HAS_IOMEM && DMA_ENGINE) || SPI || PCI || HAS_IOMEM
|
||||
---help---
|
||||
If you have a network (Ethernet) card belonging to this class, say Y.
|
||||
|
||||
@ -18,14 +17,6 @@ config NET_VENDOR_MICREL
|
||||
|
||||
if NET_VENDOR_MICREL
|
||||
|
||||
config ARM_KS8695_ETHER
|
||||
tristate "KS8695 Ethernet support"
|
||||
depends on ARM && ARCH_KS8695
|
||||
select MII
|
||||
---help---
|
||||
If you wish to compile a kernel for the KS8695 and want to
|
||||
use the internal ethernet then you should answer Y to this.
|
||||
|
||||
config KS8842
|
||||
tristate "Micrel KSZ8841/42 with generic bus interface"
|
||||
depends on HAS_IOMEM && DMA_ENGINE
|
||||
|
@ -3,7 +3,6 @@
|
||||
# Makefile for the Micrel network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
|
||||
obj-$(CONFIG_KS8842) += ks8842.o
|
||||
obj-$(CONFIG_KS8851) += ks8851.o
|
||||
obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,108 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Micrel KS8695 (Centaur) Ethernet.
|
||||
*
|
||||
* Copyright 2008 Simtec Electronics
|
||||
* Daniel Silverstone <dsilvers@simtec.co.uk>
|
||||
* Vincent Sanders <vince@simtec.co.uk>
|
||||
*/
|
||||
|
||||
#ifndef KS8695NET_H
|
||||
#define KS8695NET_H
|
||||
|
||||
/* Receive descriptor flags */
|
||||
#define RDES_OWN (1 << 31) /* Ownership */
|
||||
#define RDES_FS (1 << 30) /* First Descriptor */
|
||||
#define RDES_LS (1 << 29) /* Last Descriptor */
|
||||
#define RDES_IPE (1 << 28) /* IP Checksum error */
|
||||
#define RDES_TCPE (1 << 27) /* TCP Checksum error */
|
||||
#define RDES_UDPE (1 << 26) /* UDP Checksum error */
|
||||
#define RDES_ES (1 << 25) /* Error summary */
|
||||
#define RDES_MF (1 << 24) /* Multicast Frame */
|
||||
#define RDES_RE (1 << 19) /* MII Error reported */
|
||||
#define RDES_TL (1 << 18) /* Frame too Long */
|
||||
#define RDES_RF (1 << 17) /* Runt Frame */
|
||||
#define RDES_CE (1 << 16) /* CRC error */
|
||||
#define RDES_FT (1 << 15) /* Frame Type */
|
||||
#define RDES_FLEN (0x7ff) /* Frame Length */
|
||||
|
||||
#define RDES_RER (1 << 25) /* Receive End of Ring */
|
||||
#define RDES_RBS (0x7ff) /* Receive Buffer Size */
|
||||
|
||||
/* Transmit descriptor flags */
|
||||
|
||||
#define TDES_OWN (1 << 31) /* Ownership */
|
||||
|
||||
#define TDES_IC (1 << 31) /* Interrupt on Completion */
|
||||
#define TDES_FS (1 << 30) /* First Segment */
|
||||
#define TDES_LS (1 << 29) /* Last Segment */
|
||||
#define TDES_IPCKG (1 << 28) /* IP Checksum generate */
|
||||
#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */
|
||||
#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */
|
||||
#define TDES_TER (1 << 25) /* Transmit End of Ring */
|
||||
#define TDES_TBS (0x7ff) /* Transmit Buffer Size */
|
||||
|
||||
/*
|
||||
* Network controller register offsets
|
||||
*/
|
||||
#define KS8695_DTXC (0x00) /* DMA Transmit Control */
|
||||
#define KS8695_DRXC (0x04) /* DMA Receive Control */
|
||||
#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */
|
||||
#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */
|
||||
#define KS8695_TDLB (0x10) /* Transmit Descriptor List
|
||||
* Base Address
|
||||
*/
|
||||
#define KS8695_RDLB (0x14) /* Receive Descriptor List
|
||||
* Base Address
|
||||
*/
|
||||
#define KS8695_MAL (0x18) /* MAC Station Address Low */
|
||||
#define KS8695_MAH (0x1c) /* MAC Station Address High */
|
||||
#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional
|
||||
* Station Address
|
||||
* (0..15) Low
|
||||
*/
|
||||
#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional
|
||||
* Station Address
|
||||
* (0..15) High
|
||||
*/
|
||||
|
||||
|
||||
/* DMA Transmit Control Register */
|
||||
#define DTXC_TRST (1 << 31) /* Soft Reset */
|
||||
#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */
|
||||
#define DTXC_TUCG (1 << 18) /* Transmit UDP
|
||||
* Checksum Generate
|
||||
*/
|
||||
#define DTXC_TTCG (1 << 17) /* Transmit TCP
|
||||
* Checksum Generate
|
||||
*/
|
||||
#define DTXC_TICG (1 << 16) /* Transmit IP
|
||||
* Checksum Generate
|
||||
*/
|
||||
#define DTXC_TFCE (1 << 9) /* Transmit Flow
|
||||
* Control Enable
|
||||
*/
|
||||
#define DTXC_TLB (1 << 8) /* Loopback mode */
|
||||
#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */
|
||||
#define DTXC_TAC (1 << 1) /* Transmit Add CRC */
|
||||
#define DTXC_TE (1 << 0) /* TX Enable */
|
||||
|
||||
/* DMA Receive Control Register */
|
||||
#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */
|
||||
#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */
|
||||
#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */
|
||||
#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */
|
||||
#define DRXC_RFCE (1 << 9) /* Receive Flow Control
|
||||
* Enable
|
||||
*/
|
||||
#define DRXC_RB (1 << 6) /* Receive Broadcast */
|
||||
#define DRXC_RM (1 << 5) /* Receive Multicast */
|
||||
#define DRXC_RU (1 << 4) /* Receive Unicast */
|
||||
#define DRXC_RERR (1 << 3) /* Receive Error Frame */
|
||||
#define DRXC_RA (1 << 2) /* Receive All */
|
||||
#define DRXC_RE (1 << 0) /* RX Enable */
|
||||
|
||||
/* Additional Station Address High */
|
||||
#define AAH_E (1 << 31) /* Address Enabled */
|
||||
|
||||
#endif /* KS8695NET_H */
|
@ -1,29 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Nuvoton network device configuration
|
||||
#
|
||||
|
||||
config NET_VENDOR_NUVOTON
|
||||
bool "Nuvoton devices"
|
||||
default y
|
||||
depends on ARM && ARCH_W90X900
|
||||
---help---
|
||||
If you have a network (Ethernet) card belonging to this class, say Y.
|
||||
|
||||
Note that the answer to this question doesn't directly affect the
|
||||
kernel: saying N will just cause the configurator to skip all
|
||||
the questions about Nuvoton cards. If you say Y, you will be asked
|
||||
for your specific card in the following questions.
|
||||
|
||||
if NET_VENDOR_NUVOTON
|
||||
|
||||
config W90P910_ETH
|
||||
tristate "Nuvoton w90p910 Ethernet support"
|
||||
depends on ARM && ARCH_W90X900
|
||||
select PHYLIB
|
||||
select MII
|
||||
---help---
|
||||
Say Y here if you want to use built-in Ethernet ports
|
||||
on w90p910 processor.
|
||||
|
||||
endif # NET_VENDOR_NUVOTON
|
@ -1,6 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Makefile for the Nuvoton network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o
|
File diff suppressed because it is too large
Load Diff
@ -116,9 +116,20 @@ config RESET_QCOM_PDC
|
||||
to control reset signals provided by PDC for Modem, Compute,
|
||||
Display, GPU, Debug, AOP, Sensors, Audio, SP and APPS.
|
||||
|
||||
config RESET_SCMI
|
||||
tristate "Reset driver controlled via ARM SCMI interface"
|
||||
depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
|
||||
default ARM_SCMI_PROTOCOL
|
||||
help
|
||||
This driver provides support for reset signal/domains that are
|
||||
controlled by firmware that implements the SCMI interface.
|
||||
|
||||
This driver uses SCMI Message Protocol to interact with the
|
||||
firmware controlling all the reset signals.
|
||||
|
||||
config RESET_SIMPLE
|
||||
bool "Simple Reset Controller Driver" if COMPILE_TEST
|
||||
default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN
|
||||
default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN || ARC
|
||||
help
|
||||
This enables a simple reset controller driver for reset lines that
|
||||
that can be asserted and deasserted by toggling bits in a contiguous,
|
||||
|
@ -18,6 +18,7 @@ obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o
|
||||
obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o
|
||||
obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o
|
||||
obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o
|
||||
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
|
||||
obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
|
||||
obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o
|
||||
obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
|
||||
|
@ -169,9 +169,9 @@ static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = {
|
||||
[IMX8MQ_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) },
|
||||
[IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N] = { SRC_MIPIPHY_RCR, BIT(1) },
|
||||
[IMX8MQ_RESET_MIPI_DSI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(2) },
|
||||
[IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) },
|
||||
[IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) },
|
||||
[IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) },
|
||||
[IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) },
|
||||
[IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) },
|
||||
[IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) },
|
||||
[IMX8MQ_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR,
|
||||
BIT(2) | BIT(1) },
|
||||
[IMX8MQ_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) },
|
||||
@ -220,9 +220,9 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev,
|
||||
|
||||
case IMX8MQ_RESET_PCIE_CTRL_APPS_EN:
|
||||
case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: /* fallthrough */
|
||||
case IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N: /* fallthrough */
|
||||
case IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N: /* fallthrough */
|
||||
case IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N: /* fallthrough */
|
||||
case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N: /* fallthrough */
|
||||
case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N: /* fallthrough */
|
||||
case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N: /* fallthrough */
|
||||
case IMX8MQ_RESET_MIPI_DSI_RESET_N: /* fallthrough */
|
||||
case IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N: /* fallthrough */
|
||||
value = assert ? 0 : bit;
|
||||
|
@ -1,58 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Amlogic Meson Reset Controller driver
|
||||
*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* GPL LICENSE SUMMARY
|
||||
*
|
||||
* Copyright (c) 2016 BayLibre, SAS.
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
* The full GNU General Public License is included in this distribution
|
||||
* in the file called COPYING.
|
||||
*
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) 2016 BayLibre, SAS.
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
|
124
drivers/reset/reset-scmi.c
Normal file
124
drivers/reset/reset-scmi.c
Normal file
@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ARM System Control and Management Interface (ARM SCMI) reset driver
|
||||
*
|
||||
* Copyright (C) 2019 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
/**
|
||||
* struct scmi_reset_data - reset controller information structure
|
||||
* @rcdev: reset controller entity
|
||||
* @handle: ARM SCMI handle used for communication with system controller
|
||||
*/
|
||||
struct scmi_reset_data {
|
||||
struct reset_controller_dev rcdev;
|
||||
const struct scmi_handle *handle;
|
||||
};
|
||||
|
||||
#define to_scmi_reset_data(p) container_of((p), struct scmi_reset_data, rcdev)
|
||||
#define to_scmi_handle(p) (to_scmi_reset_data(p)->handle)
|
||||
|
||||
/**
|
||||
* scmi_reset_assert() - assert device reset
|
||||
* @rcdev: reset controller entity
|
||||
* @id: ID of the reset to be asserted
|
||||
*
|
||||
* This function implements the reset driver op to assert a device's reset
|
||||
* using the ARM SCMI protocol.
|
||||
*
|
||||
* Return: 0 for successful request, else a corresponding error value
|
||||
*/
|
||||
static int
|
||||
scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
const struct scmi_handle *handle = to_scmi_handle(rcdev);
|
||||
|
||||
return handle->reset_ops->assert(handle, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_reset_deassert() - deassert device reset
|
||||
* @rcdev: reset controller entity
|
||||
* @id: ID of the reset to be deasserted
|
||||
*
|
||||
* This function implements the reset driver op to deassert a device's reset
|
||||
* using the ARM SCMI protocol.
|
||||
*
|
||||
* Return: 0 for successful request, else a corresponding error value
|
||||
*/
|
||||
static int
|
||||
scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
const struct scmi_handle *handle = to_scmi_handle(rcdev);
|
||||
|
||||
return handle->reset_ops->deassert(handle, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_reset_reset() - reset the device
|
||||
* @rcdev: reset controller entity
|
||||
* @id: ID of the reset signal to be reset(assert + deassert)
|
||||
*
|
||||
* This function implements the reset driver op to trigger a device's
|
||||
* reset signal using the ARM SCMI protocol.
|
||||
*
|
||||
* Return: 0 for successful request, else a corresponding error value
|
||||
*/
|
||||
static int
|
||||
scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
const struct scmi_handle *handle = to_scmi_handle(rcdev);
|
||||
|
||||
return handle->reset_ops->reset(handle, id);
|
||||
}
|
||||
|
||||
static const struct reset_control_ops scmi_reset_ops = {
|
||||
.assert = scmi_reset_assert,
|
||||
.deassert = scmi_reset_deassert,
|
||||
.reset = scmi_reset_reset,
|
||||
};
|
||||
|
||||
static int scmi_reset_probe(struct scmi_device *sdev)
|
||||
{
|
||||
struct scmi_reset_data *data;
|
||||
struct device *dev = &sdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
|
||||
if (!handle || !handle->reset_ops)
|
||||
return -ENODEV;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->rcdev.ops = &scmi_reset_ops;
|
||||
data->rcdev.owner = THIS_MODULE;
|
||||
data->rcdev.of_node = np;
|
||||
data->rcdev.nr_resets = handle->reset_ops->num_domains_get(handle);
|
||||
|
||||
return devm_reset_controller_register(dev, &data->rcdev);
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_id_table[] = {
|
||||
{ SCMI_PROTOCOL_RESET },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
||||
|
||||
static struct scmi_driver scmi_reset_driver = {
|
||||
.name = "scmi-reset",
|
||||
.probe = scmi_reset_probe,
|
||||
.id_table = scmi_id_table,
|
||||
};
|
||||
module_scmi_driver(scmi_reset_driver);
|
||||
|
||||
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
|
||||
MODULE_DESCRIPTION("ARM SCMI reset controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -127,6 +127,9 @@ static const struct of_device_id reset_simple_dt_ids[] = {
|
||||
{ .compatible = "aspeed,ast2500-lpc-reset" },
|
||||
{ .compatible = "bitmain,bm1880-reset",
|
||||
.data = &reset_simple_active_low },
|
||||
{ .compatible = "snps,dw-high-reset" },
|
||||
{ .compatible = "snps,dw-low-reset",
|
||||
.data = &reset_simple_active_low },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
|
@ -37,6 +37,17 @@ config MESON_GX_PM_DOMAINS
|
||||
Say yes to expose Amlogic Meson GX Power Domains as
|
||||
Generic Power Domains.
|
||||
|
||||
config MESON_EE_PM_DOMAINS
|
||||
bool "Amlogic Meson Everything-Else Power Domains driver"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
depends on PM && OF
|
||||
default ARCH_MESON
|
||||
select PM_GENERIC_DOMAINS
|
||||
select PM_GENERIC_DOMAINS_OF
|
||||
help
|
||||
Say yes to expose Amlogic Meson Everything-Else Power Domains as
|
||||
Generic Power Domains.
|
||||
|
||||
config MESON_MX_SOCINFO
|
||||
bool "Amlogic Meson MX SoC Information driver"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
|
@ -4,3 +4,4 @@ obj-$(CONFIG_MESON_CLK_MEASURE) += meson-clk-measure.o
|
||||
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
|
||||
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
|
||||
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
|
||||
obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
static DEFINE_MUTEX(measure_lock);
|
||||
|
||||
#define MSR_CLK_DUTY 0x0
|
||||
#define MSR_CLK_REG0 0x4
|
||||
#define MSR_CLK_REG1 0x8
|
||||
@ -322,6 +324,8 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = {
|
||||
CLK_MSR_ID(84, "co_tx"),
|
||||
CLK_MSR_ID(89, "hdmi_todig"),
|
||||
CLK_MSR_ID(90, "hdmitx_sys"),
|
||||
CLK_MSR_ID(91, "sys_cpub_div16"),
|
||||
CLK_MSR_ID(92, "sys_pll_cpub_div16"),
|
||||
CLK_MSR_ID(94, "eth_phy_rx"),
|
||||
CLK_MSR_ID(95, "eth_phy_pll"),
|
||||
CLK_MSR_ID(96, "vpu_b"),
|
||||
@ -353,6 +357,136 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = {
|
||||
CLK_MSR_ID(122, "audio_pdm_dclk"),
|
||||
};
|
||||
|
||||
static struct meson_msr_id clk_msr_sm1[CLK_MSR_MAX] = {
|
||||
CLK_MSR_ID(0, "ring_osc_out_ee_0"),
|
||||
CLK_MSR_ID(1, "ring_osc_out_ee_1"),
|
||||
CLK_MSR_ID(2, "ring_osc_out_ee_2"),
|
||||
CLK_MSR_ID(3, "ring_osc_out_ee_3"),
|
||||
CLK_MSR_ID(4, "gp0_pll"),
|
||||
CLK_MSR_ID(5, "gp1_pll"),
|
||||
CLK_MSR_ID(6, "enci"),
|
||||
CLK_MSR_ID(7, "clk81"),
|
||||
CLK_MSR_ID(8, "encp"),
|
||||
CLK_MSR_ID(9, "encl"),
|
||||
CLK_MSR_ID(10, "vdac"),
|
||||
CLK_MSR_ID(11, "eth_tx"),
|
||||
CLK_MSR_ID(12, "hifi_pll"),
|
||||
CLK_MSR_ID(13, "mod_tcon"),
|
||||
CLK_MSR_ID(14, "fec_0"),
|
||||
CLK_MSR_ID(15, "fec_1"),
|
||||
CLK_MSR_ID(16, "fec_2"),
|
||||
CLK_MSR_ID(17, "sys_pll_div16"),
|
||||
CLK_MSR_ID(18, "sys_cpu_div16"),
|
||||
CLK_MSR_ID(19, "lcd_an_ph2"),
|
||||
CLK_MSR_ID(20, "rtc_osc_out"),
|
||||
CLK_MSR_ID(21, "lcd_an_ph3"),
|
||||
CLK_MSR_ID(22, "eth_phy_ref"),
|
||||
CLK_MSR_ID(23, "mpll_50m"),
|
||||
CLK_MSR_ID(24, "eth_125m"),
|
||||
CLK_MSR_ID(25, "eth_rmii"),
|
||||
CLK_MSR_ID(26, "sc_int"),
|
||||
CLK_MSR_ID(27, "in_mac"),
|
||||
CLK_MSR_ID(28, "sar_adc"),
|
||||
CLK_MSR_ID(29, "pcie_inp"),
|
||||
CLK_MSR_ID(30, "pcie_inn"),
|
||||
CLK_MSR_ID(31, "mpll_test_out"),
|
||||
CLK_MSR_ID(32, "vdec"),
|
||||
CLK_MSR_ID(34, "eth_mpll_50m"),
|
||||
CLK_MSR_ID(35, "mali"),
|
||||
CLK_MSR_ID(36, "hdmi_tx_pixel"),
|
||||
CLK_MSR_ID(37, "cdac"),
|
||||
CLK_MSR_ID(38, "vdin_meas"),
|
||||
CLK_MSR_ID(39, "bt656"),
|
||||
CLK_MSR_ID(40, "arm_ring_osc_out_4"),
|
||||
CLK_MSR_ID(41, "eth_rx_or_rmii"),
|
||||
CLK_MSR_ID(42, "mp0_out"),
|
||||
CLK_MSR_ID(43, "fclk_div5"),
|
||||
CLK_MSR_ID(44, "pwm_b"),
|
||||
CLK_MSR_ID(45, "pwm_a"),
|
||||
CLK_MSR_ID(46, "vpu"),
|
||||
CLK_MSR_ID(47, "ddr_dpll_pt"),
|
||||
CLK_MSR_ID(48, "mp1_out"),
|
||||
CLK_MSR_ID(49, "mp2_out"),
|
||||
CLK_MSR_ID(50, "mp3_out"),
|
||||
CLK_MSR_ID(51, "sd_emmc_c"),
|
||||
CLK_MSR_ID(52, "sd_emmc_b"),
|
||||
CLK_MSR_ID(53, "sd_emmc_a"),
|
||||
CLK_MSR_ID(54, "vpu_clkc"),
|
||||
CLK_MSR_ID(55, "vid_pll_div_out"),
|
||||
CLK_MSR_ID(56, "wave420l_a"),
|
||||
CLK_MSR_ID(57, "wave420l_c"),
|
||||
CLK_MSR_ID(58, "wave420l_b"),
|
||||
CLK_MSR_ID(59, "hcodec"),
|
||||
CLK_MSR_ID(60, "arm_ring_osc_out_5"),
|
||||
CLK_MSR_ID(61, "gpio_msr"),
|
||||
CLK_MSR_ID(62, "hevcb"),
|
||||
CLK_MSR_ID(63, "dsi_meas"),
|
||||
CLK_MSR_ID(64, "spicc_1"),
|
||||
CLK_MSR_ID(65, "spicc_0"),
|
||||
CLK_MSR_ID(66, "vid_lock"),
|
||||
CLK_MSR_ID(67, "dsi_phy"),
|
||||
CLK_MSR_ID(68, "hdcp22_esm"),
|
||||
CLK_MSR_ID(69, "hdcp22_skp"),
|
||||
CLK_MSR_ID(70, "pwm_f"),
|
||||
CLK_MSR_ID(71, "pwm_e"),
|
||||
CLK_MSR_ID(72, "pwm_d"),
|
||||
CLK_MSR_ID(73, "pwm_c"),
|
||||
CLK_MSR_ID(74, "arm_ring_osc_out_6"),
|
||||
CLK_MSR_ID(75, "hevcf"),
|
||||
CLK_MSR_ID(76, "arm_ring_osc_out_7"),
|
||||
CLK_MSR_ID(77, "rng_ring_osc_0"),
|
||||
CLK_MSR_ID(78, "rng_ring_osc_1"),
|
||||
CLK_MSR_ID(79, "rng_ring_osc_2"),
|
||||
CLK_MSR_ID(80, "rng_ring_osc_3"),
|
||||
CLK_MSR_ID(81, "vapb"),
|
||||
CLK_MSR_ID(82, "ge2d"),
|
||||
CLK_MSR_ID(83, "co_rx"),
|
||||
CLK_MSR_ID(84, "co_tx"),
|
||||
CLK_MSR_ID(85, "arm_ring_osc_out_8"),
|
||||
CLK_MSR_ID(86, "arm_ring_osc_out_9"),
|
||||
CLK_MSR_ID(87, "mipi_dsi_phy"),
|
||||
CLK_MSR_ID(88, "cis2_adapt"),
|
||||
CLK_MSR_ID(89, "hdmi_todig"),
|
||||
CLK_MSR_ID(90, "hdmitx_sys"),
|
||||
CLK_MSR_ID(91, "nna_core"),
|
||||
CLK_MSR_ID(92, "nna_axi"),
|
||||
CLK_MSR_ID(93, "vad"),
|
||||
CLK_MSR_ID(94, "eth_phy_rx"),
|
||||
CLK_MSR_ID(95, "eth_phy_pll"),
|
||||
CLK_MSR_ID(96, "vpu_b"),
|
||||
CLK_MSR_ID(97, "cpu_b_tmp"),
|
||||
CLK_MSR_ID(98, "ts"),
|
||||
CLK_MSR_ID(99, "arm_ring_osc_out_10"),
|
||||
CLK_MSR_ID(100, "arm_ring_osc_out_11"),
|
||||
CLK_MSR_ID(101, "arm_ring_osc_out_12"),
|
||||
CLK_MSR_ID(102, "arm_ring_osc_out_13"),
|
||||
CLK_MSR_ID(103, "arm_ring_osc_out_14"),
|
||||
CLK_MSR_ID(104, "arm_ring_osc_out_15"),
|
||||
CLK_MSR_ID(105, "arm_ring_osc_out_16"),
|
||||
CLK_MSR_ID(106, "ephy_test"),
|
||||
CLK_MSR_ID(107, "au_dac_g128x"),
|
||||
CLK_MSR_ID(108, "audio_locker_out"),
|
||||
CLK_MSR_ID(109, "audio_locker_in"),
|
||||
CLK_MSR_ID(110, "audio_tdmout_c_sclk"),
|
||||
CLK_MSR_ID(111, "audio_tdmout_b_sclk"),
|
||||
CLK_MSR_ID(112, "audio_tdmout_a_sclk"),
|
||||
CLK_MSR_ID(113, "audio_tdmin_lb_sclk"),
|
||||
CLK_MSR_ID(114, "audio_tdmin_c_sclk"),
|
||||
CLK_MSR_ID(115, "audio_tdmin_b_sclk"),
|
||||
CLK_MSR_ID(116, "audio_tdmin_a_sclk"),
|
||||
CLK_MSR_ID(117, "audio_resample"),
|
||||
CLK_MSR_ID(118, "audio_pdm_sys"),
|
||||
CLK_MSR_ID(119, "audio_spdifout_b"),
|
||||
CLK_MSR_ID(120, "audio_spdifout"),
|
||||
CLK_MSR_ID(121, "audio_spdifin"),
|
||||
CLK_MSR_ID(122, "audio_pdm_dclk"),
|
||||
CLK_MSR_ID(123, "audio_resampled"),
|
||||
CLK_MSR_ID(124, "earcrx_pll"),
|
||||
CLK_MSR_ID(125, "earcrx_pll_test"),
|
||||
CLK_MSR_ID(126, "csi_phy0"),
|
||||
CLK_MSR_ID(127, "csi2_data"),
|
||||
};
|
||||
|
||||
static int meson_measure_id(struct meson_msr_id *clk_msr_id,
|
||||
unsigned int duration)
|
||||
{
|
||||
@ -360,6 +494,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id,
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&measure_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, MSR_CLK_REG0, 0);
|
||||
|
||||
/* Set measurement duration */
|
||||
@ -377,8 +515,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id,
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, MSR_CLK_REG0,
|
||||
val, !(val & MSR_BUSY), 10, 10000);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
mutex_unlock(&measure_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable */
|
||||
regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_ENABLE, 0);
|
||||
@ -386,6 +526,8 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id,
|
||||
/* Get the value in multiple of gate time counts */
|
||||
regmap_read(priv->regmap, MSR_CLK_REG2, &val);
|
||||
|
||||
mutex_unlock(&measure_lock);
|
||||
|
||||
if (val >= MSR_VAL_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
@ -533,6 +675,10 @@ static const struct of_device_id meson_msr_match_table[] = {
|
||||
.compatible = "amlogic,meson-g12a-clk-measure",
|
||||
.data = (void *)clk_msr_g12a,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-sm1-clk-measure",
|
||||
.data = (void *)clk_msr_sm1,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
492
drivers/soc/amlogic/meson-ee-pwrc.c
Normal file
492
drivers/soc/amlogic/meson-ee-pwrc.c
Normal file
@ -0,0 +1,492 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/clk.h>
|
||||
#include <dt-bindings/power/meson-g12a-power.h>
|
||||
#include <dt-bindings/power/meson-sm1-power.h>
|
||||
|
||||
/* AO Offsets */
|
||||
|
||||
#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2)
|
||||
#define AO_RTI_GEN_PWR_ISO0 (0x3b << 2)
|
||||
|
||||
/* HHI Offsets */
|
||||
|
||||
#define HHI_MEM_PD_REG0 (0x40 << 2)
|
||||
#define HHI_VPU_MEM_PD_REG0 (0x41 << 2)
|
||||
#define HHI_VPU_MEM_PD_REG1 (0x42 << 2)
|
||||
#define HHI_VPU_MEM_PD_REG3 (0x43 << 2)
|
||||
#define HHI_VPU_MEM_PD_REG4 (0x44 << 2)
|
||||
#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2)
|
||||
#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2)
|
||||
#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2)
|
||||
#define HHI_VPU_MEM_PD_REG2 (0x4d << 2)
|
||||
|
||||
struct meson_ee_pwrc;
|
||||
struct meson_ee_pwrc_domain;
|
||||
|
||||
struct meson_ee_pwrc_mem_domain {
|
||||
unsigned int reg;
|
||||
unsigned int mask;
|
||||
};
|
||||
|
||||
struct meson_ee_pwrc_top_domain {
|
||||
unsigned int sleep_reg;
|
||||
unsigned int sleep_mask;
|
||||
unsigned int iso_reg;
|
||||
unsigned int iso_mask;
|
||||
};
|
||||
|
||||
struct meson_ee_pwrc_domain_desc {
|
||||
char *name;
|
||||
unsigned int reset_names_count;
|
||||
unsigned int clk_names_count;
|
||||
struct meson_ee_pwrc_top_domain *top_pd;
|
||||
unsigned int mem_pd_count;
|
||||
struct meson_ee_pwrc_mem_domain *mem_pd;
|
||||
bool (*get_power)(struct meson_ee_pwrc_domain *pwrc_domain);
|
||||
};
|
||||
|
||||
struct meson_ee_pwrc_domain_data {
|
||||
unsigned int count;
|
||||
struct meson_ee_pwrc_domain_desc *domains;
|
||||
};
|
||||
|
||||
/* TOP Power Domains */
|
||||
|
||||
static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = {
|
||||
.sleep_reg = AO_RTI_GEN_PWR_SLEEP0,
|
||||
.sleep_mask = BIT(8),
|
||||
.iso_reg = AO_RTI_GEN_PWR_SLEEP0,
|
||||
.iso_mask = BIT(9),
|
||||
};
|
||||
|
||||
#define SM1_EE_PD(__bit) \
|
||||
{ \
|
||||
.sleep_reg = AO_RTI_GEN_PWR_SLEEP0, \
|
||||
.sleep_mask = BIT(__bit), \
|
||||
.iso_reg = AO_RTI_GEN_PWR_ISO0, \
|
||||
.iso_mask = BIT(__bit), \
|
||||
}
|
||||
|
||||
static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8);
|
||||
static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16);
|
||||
static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17);
|
||||
static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18);
|
||||
static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19);
|
||||
|
||||
/* Memory PD Domains */
|
||||
|
||||
#define VPU_MEMPD(__reg) \
|
||||
{ __reg, GENMASK(1, 0) }, \
|
||||
{ __reg, GENMASK(3, 2) }, \
|
||||
{ __reg, GENMASK(5, 4) }, \
|
||||
{ __reg, GENMASK(7, 6) }, \
|
||||
{ __reg, GENMASK(9, 8) }, \
|
||||
{ __reg, GENMASK(11, 10) }, \
|
||||
{ __reg, GENMASK(13, 12) }, \
|
||||
{ __reg, GENMASK(15, 14) }, \
|
||||
{ __reg, GENMASK(17, 16) }, \
|
||||
{ __reg, GENMASK(19, 18) }, \
|
||||
{ __reg, GENMASK(21, 20) }, \
|
||||
{ __reg, GENMASK(23, 22) }, \
|
||||
{ __reg, GENMASK(25, 24) }, \
|
||||
{ __reg, GENMASK(27, 26) }, \
|
||||
{ __reg, GENMASK(29, 28) }, \
|
||||
{ __reg, GENMASK(31, 30) }
|
||||
|
||||
#define VPU_HHI_MEMPD(__reg) \
|
||||
{ __reg, BIT(8) }, \
|
||||
{ __reg, BIT(9) }, \
|
||||
{ __reg, BIT(10) }, \
|
||||
{ __reg, BIT(11) }, \
|
||||
{ __reg, BIT(12) }, \
|
||||
{ __reg, BIT(13) }, \
|
||||
{ __reg, BIT(14) }, \
|
||||
{ __reg, BIT(15) }
|
||||
|
||||
static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = {
|
||||
VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
|
||||
VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
|
||||
VPU_MEMPD(HHI_VPU_MEM_PD_REG2),
|
||||
VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_eth[] = {
|
||||
{ HHI_MEM_PD_REG0, GENMASK(3, 2) },
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = {
|
||||
VPU_MEMPD(HHI_VPU_MEM_PD_REG0),
|
||||
VPU_MEMPD(HHI_VPU_MEM_PD_REG1),
|
||||
VPU_MEMPD(HHI_VPU_MEM_PD_REG2),
|
||||
VPU_MEMPD(HHI_VPU_MEM_PD_REG3),
|
||||
{ HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) },
|
||||
{ HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) },
|
||||
{ HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) },
|
||||
{ HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) },
|
||||
VPU_HHI_MEMPD(HHI_MEM_PD_REG0),
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = {
|
||||
{ HHI_NANOQ_MEM_PD_REG0, 0xff },
|
||||
{ HHI_NANOQ_MEM_PD_REG1, 0xff },
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = {
|
||||
{ HHI_MEM_PD_REG0, GENMASK(31, 30) },
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = {
|
||||
{ HHI_MEM_PD_REG0, GENMASK(29, 26) },
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = {
|
||||
{ HHI_MEM_PD_REG0, GENMASK(25, 18) },
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = {
|
||||
{ HHI_MEM_PD_REG0, GENMASK(5, 4) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) },
|
||||
{ HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) },
|
||||
};
|
||||
|
||||
#define VPU_PD(__name, __top_pd, __mem, __get_power, __resets, __clks) \
|
||||
{ \
|
||||
.name = __name, \
|
||||
.reset_names_count = __resets, \
|
||||
.clk_names_count = __clks, \
|
||||
.top_pd = __top_pd, \
|
||||
.mem_pd_count = ARRAY_SIZE(__mem), \
|
||||
.mem_pd = __mem, \
|
||||
.get_power = __get_power, \
|
||||
}
|
||||
|
||||
#define TOP_PD(__name, __top_pd, __mem, __get_power) \
|
||||
{ \
|
||||
.name = __name, \
|
||||
.top_pd = __top_pd, \
|
||||
.mem_pd_count = ARRAY_SIZE(__mem), \
|
||||
.mem_pd = __mem, \
|
||||
.get_power = __get_power, \
|
||||
}
|
||||
|
||||
#define MEM_PD(__name, __mem) \
|
||||
TOP_PD(__name, NULL, __mem, NULL)
|
||||
|
||||
static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain);
|
||||
|
||||
static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = {
|
||||
[PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu,
|
||||
pwrc_ee_get_power, 11, 2),
|
||||
[PWRC_G12A_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth),
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = {
|
||||
[PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu,
|
||||
pwrc_ee_get_power, 11, 2),
|
||||
[PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna,
|
||||
pwrc_ee_get_power),
|
||||
[PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb,
|
||||
pwrc_ee_get_power),
|
||||
[PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie,
|
||||
pwrc_ee_get_power),
|
||||
[PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d,
|
||||
pwrc_ee_get_power),
|
||||
[PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio),
|
||||
[PWRC_SM1_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth),
|
||||
};
|
||||
|
||||
struct meson_ee_pwrc_domain {
|
||||
struct generic_pm_domain base;
|
||||
bool enabled;
|
||||
struct meson_ee_pwrc *pwrc;
|
||||
struct meson_ee_pwrc_domain_desc desc;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
struct reset_control *rstc;
|
||||
int num_rstc;
|
||||
};
|
||||
|
||||
struct meson_ee_pwrc {
|
||||
struct regmap *regmap_ao;
|
||||
struct regmap *regmap_hhi;
|
||||
struct meson_ee_pwrc_domain *domains;
|
||||
struct genpd_onecell_data xlate;
|
||||
};
|
||||
|
||||
static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
regmap_read(pwrc_domain->pwrc->regmap_ao,
|
||||
pwrc_domain->desc.top_pd->sleep_reg, ®);
|
||||
|
||||
return (reg & pwrc_domain->desc.top_pd->sleep_mask);
|
||||
}
|
||||
|
||||
static int meson_ee_pwrc_off(struct generic_pm_domain *domain)
|
||||
{
|
||||
struct meson_ee_pwrc_domain *pwrc_domain =
|
||||
container_of(domain, struct meson_ee_pwrc_domain, base);
|
||||
int i;
|
||||
|
||||
if (pwrc_domain->desc.top_pd)
|
||||
regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
|
||||
pwrc_domain->desc.top_pd->sleep_reg,
|
||||
pwrc_domain->desc.top_pd->sleep_mask,
|
||||
pwrc_domain->desc.top_pd->sleep_mask);
|
||||
udelay(20);
|
||||
|
||||
for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i)
|
||||
regmap_update_bits(pwrc_domain->pwrc->regmap_hhi,
|
||||
pwrc_domain->desc.mem_pd[i].reg,
|
||||
pwrc_domain->desc.mem_pd[i].mask,
|
||||
pwrc_domain->desc.mem_pd[i].mask);
|
||||
|
||||
udelay(20);
|
||||
|
||||
if (pwrc_domain->desc.top_pd)
|
||||
regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
|
||||
pwrc_domain->desc.top_pd->iso_reg,
|
||||
pwrc_domain->desc.top_pd->iso_mask,
|
||||
pwrc_domain->desc.top_pd->iso_mask);
|
||||
|
||||
if (pwrc_domain->num_clks) {
|
||||
msleep(20);
|
||||
clk_bulk_disable_unprepare(pwrc_domain->num_clks,
|
||||
pwrc_domain->clks);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_ee_pwrc_on(struct generic_pm_domain *domain)
|
||||
{
|
||||
struct meson_ee_pwrc_domain *pwrc_domain =
|
||||
container_of(domain, struct meson_ee_pwrc_domain, base);
|
||||
int i, ret;
|
||||
|
||||
if (pwrc_domain->desc.top_pd)
|
||||
regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
|
||||
pwrc_domain->desc.top_pd->sleep_reg,
|
||||
pwrc_domain->desc.top_pd->sleep_mask, 0);
|
||||
udelay(20);
|
||||
|
||||
for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i)
|
||||
regmap_update_bits(pwrc_domain->pwrc->regmap_hhi,
|
||||
pwrc_domain->desc.mem_pd[i].reg,
|
||||
pwrc_domain->desc.mem_pd[i].mask, 0);
|
||||
|
||||
udelay(20);
|
||||
|
||||
ret = reset_control_assert(pwrc_domain->rstc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pwrc_domain->desc.top_pd)
|
||||
regmap_update_bits(pwrc_domain->pwrc->regmap_ao,
|
||||
pwrc_domain->desc.top_pd->iso_reg,
|
||||
pwrc_domain->desc.top_pd->iso_mask, 0);
|
||||
|
||||
ret = reset_control_deassert(pwrc_domain->rstc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clk_bulk_prepare_enable(pwrc_domain->num_clks,
|
||||
pwrc_domain->clks);
|
||||
}
|
||||
|
||||
static int meson_ee_pwrc_init_domain(struct platform_device *pdev,
|
||||
struct meson_ee_pwrc *pwrc,
|
||||
struct meson_ee_pwrc_domain *dom)
|
||||
{
|
||||
dom->pwrc = pwrc;
|
||||
dom->num_rstc = dom->desc.reset_names_count;
|
||||
dom->num_clks = dom->desc.clk_names_count;
|
||||
|
||||
if (dom->num_rstc) {
|
||||
int count = reset_control_get_count(&pdev->dev);
|
||||
|
||||
if (count != dom->num_rstc)
|
||||
dev_warn(&pdev->dev, "Invalid resets count %d for domain %s\n",
|
||||
count, dom->desc.name);
|
||||
|
||||
dom->rstc = devm_reset_control_array_get(&pdev->dev, false,
|
||||
false);
|
||||
if (IS_ERR(dom->rstc))
|
||||
return PTR_ERR(dom->rstc);
|
||||
}
|
||||
|
||||
if (dom->num_clks) {
|
||||
int ret = devm_clk_bulk_get_all(&pdev->dev, &dom->clks);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (dom->num_clks != ret) {
|
||||
dev_warn(&pdev->dev, "Invalid clocks count %d for domain %s\n",
|
||||
ret, dom->desc.name);
|
||||
dom->num_clks = ret;
|
||||
}
|
||||
}
|
||||
|
||||
dom->base.name = dom->desc.name;
|
||||
dom->base.power_on = meson_ee_pwrc_on;
|
||||
dom->base.power_off = meson_ee_pwrc_off;
|
||||
|
||||
/*
|
||||
* TOFIX: This is a special case for the VPU power domain, which can
|
||||
* be enabled previously by the bootloader. In this case the VPU
|
||||
* pipeline may be functional but no driver maybe never attach
|
||||
* to this power domain, and if the domain is disabled it could
|
||||
* cause system errors. This is why the pm_domain_always_on_gov
|
||||
* is used here.
|
||||
* For the same reason, the clocks should be enabled in case
|
||||
* we need to power the domain off, otherwise the internal clocks
|
||||
* prepare/enable counters won't be in sync.
|
||||
*/
|
||||
if (dom->num_clks && dom->desc.get_power && !dom->desc.get_power(dom)) {
|
||||
int ret = clk_bulk_prepare_enable(dom->num_clks, dom->clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_genpd_init(&dom->base, &pm_domain_always_on_gov, false);
|
||||
} else
|
||||
pm_genpd_init(&dom->base, NULL,
|
||||
(dom->desc.get_power ?
|
||||
dom->desc.get_power(dom) : true));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_ee_pwrc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct meson_ee_pwrc_domain_data *match;
|
||||
struct regmap *regmap_ao, *regmap_hhi;
|
||||
struct meson_ee_pwrc *pwrc;
|
||||
int i, ret;
|
||||
|
||||
match = of_device_get_match_data(&pdev->dev);
|
||||
if (!match) {
|
||||
dev_err(&pdev->dev, "failed to get match data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL);
|
||||
if (!pwrc)
|
||||
return -ENOMEM;
|
||||
|
||||
pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count,
|
||||
sizeof(*pwrc->xlate.domains),
|
||||
GFP_KERNEL);
|
||||
if (!pwrc->xlate.domains)
|
||||
return -ENOMEM;
|
||||
|
||||
pwrc->domains = devm_kcalloc(&pdev->dev, match->count,
|
||||
sizeof(*pwrc->domains), GFP_KERNEL);
|
||||
if (!pwrc->domains)
|
||||
return -ENOMEM;
|
||||
|
||||
pwrc->xlate.num_domains = match->count;
|
||||
|
||||
regmap_hhi = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node));
|
||||
if (IS_ERR(regmap_hhi)) {
|
||||
dev_err(&pdev->dev, "failed to get HHI regmap\n");
|
||||
return PTR_ERR(regmap_hhi);
|
||||
}
|
||||
|
||||
regmap_ao = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"amlogic,ao-sysctrl");
|
||||
if (IS_ERR(regmap_ao)) {
|
||||
dev_err(&pdev->dev, "failed to get AO regmap\n");
|
||||
return PTR_ERR(regmap_ao);
|
||||
}
|
||||
|
||||
pwrc->regmap_ao = regmap_ao;
|
||||
pwrc->regmap_hhi = regmap_hhi;
|
||||
|
||||
platform_set_drvdata(pdev, pwrc);
|
||||
|
||||
for (i = 0 ; i < match->count ; ++i) {
|
||||
struct meson_ee_pwrc_domain *dom = &pwrc->domains[i];
|
||||
|
||||
memcpy(&dom->desc, &match->domains[i], sizeof(dom->desc));
|
||||
|
||||
ret = meson_ee_pwrc_init_domain(pdev, pwrc, dom);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pwrc->xlate.domains[i] = &dom->base;
|
||||
}
|
||||
|
||||
of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_ee_pwrc_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_ee_pwrc *pwrc = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < pwrc->xlate.num_domains ; ++i) {
|
||||
struct meson_ee_pwrc_domain *dom = &pwrc->domains[i];
|
||||
|
||||
if (dom->desc.get_power && !dom->desc.get_power(dom))
|
||||
meson_ee_pwrc_off(&dom->base);
|
||||
}
|
||||
}
|
||||
|
||||
static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = {
|
||||
.count = ARRAY_SIZE(g12a_pwrc_domains),
|
||||
.domains = g12a_pwrc_domains,
|
||||
};
|
||||
|
||||
static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = {
|
||||
.count = ARRAY_SIZE(sm1_pwrc_domains),
|
||||
.domains = sm1_pwrc_domains,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_ee_pwrc_match_table[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson-g12a-pwrc",
|
||||
.data = &meson_ee_g12a_pwrc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson-sm1-pwrc",
|
||||
.data = &meson_ee_sm1_pwrc_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver meson_ee_pwrc_driver = {
|
||||
.probe = meson_ee_pwrc_probe,
|
||||
.shutdown = meson_ee_pwrc_shutdown,
|
||||
.driver = {
|
||||
.name = "meson_ee_pwrc",
|
||||
.of_match_table = meson_ee_pwrc_match_table,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(meson_ee_pwrc_driver);
|
@ -39,6 +39,7 @@ static const struct meson_gx_soc_id {
|
||||
{ "TXHD", 0x27 },
|
||||
{ "G12A", 0x28 },
|
||||
{ "G12B", 0x29 },
|
||||
{ "SM1", 0x2b },
|
||||
};
|
||||
|
||||
static const struct meson_gx_package_id {
|
||||
@ -65,6 +66,8 @@ static const struct meson_gx_package_id {
|
||||
{ "S905D2", 0x28, 0x10, 0xf0 },
|
||||
{ "S905X2", 0x28, 0x40, 0xf0 },
|
||||
{ "S922X", 0x29, 0x40, 0xf0 },
|
||||
{ "A311D", 0x29, 0x10, 0xf0 },
|
||||
{ "S905X3", 0x2b, 0x5, 0xf },
|
||||
};
|
||||
|
||||
static inline unsigned int socinfo_to_major(u32 socinfo)
|
||||
@ -138,8 +141,10 @@ static int __init meson_gx_socinfo_init(void)
|
||||
}
|
||||
|
||||
/* check if chip-id is available */
|
||||
if (!of_property_read_bool(np, "amlogic,has-chip-id"))
|
||||
if (!of_property_read_bool(np, "amlogic,has-chip-id")) {
|
||||
of_node_put(np);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* node should be a syscon */
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
|
@ -73,7 +73,7 @@ static u64 get_mc_fw_base_address(void)
|
||||
|
||||
mcfbaregs = ioremap(mc_base_addr.start, resource_size(&mc_base_addr));
|
||||
if (!mcfbaregs) {
|
||||
pr_err("could not map MC Firmaware Base registers\n");
|
||||
pr_err("could not map MC Firmware Base registers\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -305,8 +305,6 @@ void dpaa2_io_service_deregister(struct dpaa2_io *service,
|
||||
list_del(&ctx->node);
|
||||
spin_unlock_irqrestore(&d->lock_notifications, irqflags);
|
||||
|
||||
if (dev)
|
||||
device_link_remove(dev, d->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dpaa2_io_service_deregister);
|
||||
|
||||
|
@ -102,6 +102,11 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = {
|
||||
.svr = 0x87360000,
|
||||
.mask = 0xff3f0000,
|
||||
},
|
||||
/* Die: LS1028A, SoC: LS1028A */
|
||||
{ .die = "LS1028A",
|
||||
.svr = 0x870b0000,
|
||||
.mask = 0xff3f0000,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
@ -224,6 +229,7 @@ static const struct of_device_id fsl_guts_of_match[] = {
|
||||
{ .compatible = "fsl,ls1012a-dcfg", },
|
||||
{ .compatible = "fsl,ls1046a-dcfg", },
|
||||
{ .compatible = "fsl,lx2160a-dcfg", },
|
||||
{ .compatible = "fsl,ls1028a-dcfg", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_guts_of_match);
|
||||
|
@ -635,30 +635,31 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bm_shutdown_pool(u32 bpid)
|
||||
int bm_shutdown_pool(u32 bpid)
|
||||
{
|
||||
int err = 0;
|
||||
struct bm_mc_command *bm_cmd;
|
||||
union bm_mc_result *bm_res;
|
||||
|
||||
|
||||
struct bman_portal *p = get_affine_portal();
|
||||
while (1) {
|
||||
struct bman_portal *p = get_affine_portal();
|
||||
/* Acquire buffers until empty */
|
||||
bm_cmd = bm_mc_start(&p->p);
|
||||
bm_cmd->bpid = bpid;
|
||||
bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE | 1);
|
||||
if (!bm_mc_result_timeout(&p->p, &bm_res)) {
|
||||
put_affine_portal();
|
||||
pr_crit("BMan Acquire Command timedout\n");
|
||||
return -ETIMEDOUT;
|
||||
err = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) {
|
||||
put_affine_portal();
|
||||
/* Pool is empty */
|
||||
return 0;
|
||||
goto done;
|
||||
}
|
||||
put_affine_portal();
|
||||
}
|
||||
|
||||
done:
|
||||
put_affine_portal();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -97,17 +97,40 @@ static void bm_get_version(u16 *id, u8 *major, u8 *minor)
|
||||
/* signal transactions for FBPRs with higher priority */
|
||||
#define FBPR_AR_RPRIO_HI BIT(30)
|
||||
|
||||
static void bm_set_memory(u64 ba, u32 size)
|
||||
/* Track if probe has occurred and if cleanup is required */
|
||||
static int __bman_probed;
|
||||
static int __bman_requires_cleanup;
|
||||
|
||||
|
||||
static int bm_set_memory(u64 ba, u32 size)
|
||||
{
|
||||
u32 bar, bare;
|
||||
u32 exp = ilog2(size);
|
||||
/* choke if size isn't within range */
|
||||
DPAA_ASSERT(size >= 4096 && size <= 1024*1024*1024 &&
|
||||
is_power_of_2(size));
|
||||
/* choke if '[e]ba' has lower-alignment than 'size' */
|
||||
DPAA_ASSERT(!(ba & (size - 1)));
|
||||
|
||||
/* Check to see if BMan has already been initialized */
|
||||
bar = bm_ccsr_in(REG_FBPR_BAR);
|
||||
if (bar) {
|
||||
/* Maker sure ba == what was programmed) */
|
||||
bare = bm_ccsr_in(REG_FBPR_BARE);
|
||||
if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) {
|
||||
pr_err("Attempted to reinitialize BMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n",
|
||||
ba, bare, bar);
|
||||
return -ENOMEM;
|
||||
}
|
||||
pr_info("BMan BAR already configured\n");
|
||||
__bman_requires_cleanup = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
bm_ccsr_out(REG_FBPR_BARE, upper_32_bits(ba));
|
||||
bm_ccsr_out(REG_FBPR_BAR, lower_32_bits(ba));
|
||||
bm_ccsr_out(REG_FBPR_AR, exp - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -120,7 +143,6 @@ static void bm_set_memory(u64 ba, u32 size)
|
||||
*/
|
||||
static dma_addr_t fbpr_a;
|
||||
static size_t fbpr_sz;
|
||||
static int __bman_probed;
|
||||
|
||||
static int bman_fbpr(struct reserved_mem *rmem)
|
||||
{
|
||||
@ -173,6 +195,16 @@ int bman_is_probed(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bman_is_probed);
|
||||
|
||||
int bman_requires_cleanup(void)
|
||||
{
|
||||
return __bman_requires_cleanup;
|
||||
}
|
||||
|
||||
void bman_done_cleanup(void)
|
||||
{
|
||||
__bman_requires_cleanup = 0;
|
||||
}
|
||||
|
||||
static int fsl_bman_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret, err_irq;
|
||||
|
@ -100,7 +100,7 @@ static int bman_portal_probe(struct platform_device *pdev)
|
||||
struct device_node *node = dev->of_node;
|
||||
struct bm_portal_config *pcfg;
|
||||
struct resource *addr_phys[2];
|
||||
int irq, cpu, err;
|
||||
int irq, cpu, err, i;
|
||||
|
||||
err = bman_is_probed();
|
||||
if (!err)
|
||||
@ -135,10 +135,8 @@ static int bman_portal_probe(struct platform_device *pdev)
|
||||
pcfg->cpu = -1;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "Can't get %pOF IRQ'\n", node);
|
||||
if (irq <= 0)
|
||||
goto err_ioremap1;
|
||||
}
|
||||
pcfg->irq = irq;
|
||||
|
||||
pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
|
||||
@ -178,6 +176,22 @@ static int bman_portal_probe(struct platform_device *pdev)
|
||||
if (!cpu_online(cpu))
|
||||
bman_offline_cpu(cpu);
|
||||
|
||||
if (__bman_portals_probed == 1 && bman_requires_cleanup()) {
|
||||
/*
|
||||
* BMan wasn't reset prior to boot (Kexec for example)
|
||||
* Empty all the buffer pools so they are in reset state
|
||||
*/
|
||||
for (i = 0; i < BM_POOL_MAX; i++) {
|
||||
err = bm_shutdown_pool(i);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to shutdown bpool %d\n",
|
||||
i);
|
||||
goto err_portal_init;
|
||||
}
|
||||
}
|
||||
bman_done_cleanup();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_portal_init:
|
||||
|
@ -76,3 +76,8 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits);
|
||||
|
||||
const struct bm_portal_config *
|
||||
bman_get_bm_portal_config(const struct bman_portal *portal);
|
||||
|
||||
int bman_requires_cleanup(void);
|
||||
void bman_done_cleanup(void);
|
||||
|
||||
int bm_shutdown_pool(u32 bpid);
|
||||
|
@ -37,42 +37,53 @@
|
||||
int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr,
|
||||
size_t *size)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *mem_node;
|
||||
u64 size64;
|
||||
struct reserved_mem *rmem;
|
||||
struct property *prop;
|
||||
int len, err;
|
||||
__be32 *res_array;
|
||||
|
||||
ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"of_reserved_mem_device_init_by_idx(%d) failed 0x%x\n",
|
||||
idx, ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
mem_node = of_parse_phandle(dev->of_node, "memory-region", 0);
|
||||
if (mem_node) {
|
||||
ret = of_property_read_u64(mem_node, "size", &size64);
|
||||
if (ret) {
|
||||
dev_err(dev, "of_address_to_resource fails 0x%x\n",
|
||||
ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
*size = size64;
|
||||
} else {
|
||||
mem_node = of_parse_phandle(dev->of_node, "memory-region", idx);
|
||||
if (!mem_node) {
|
||||
dev_err(dev, "No memory-region found for index %d\n", idx);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!dma_alloc_coherent(dev, *size, addr, 0)) {
|
||||
dev_err(dev, "DMA Alloc memory failed\n");
|
||||
rmem = of_reserved_mem_lookup(mem_node);
|
||||
if (!rmem) {
|
||||
dev_err(dev, "of_reserved_mem_lookup() returned NULL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
*addr = rmem->base;
|
||||
*size = rmem->size;
|
||||
|
||||
/*
|
||||
* Disassociate the reserved memory area from the device
|
||||
* because a device can only have one DMA memory area. This
|
||||
* should be fine since the memory is allocated and initialized
|
||||
* and only ever accessed by the QBMan device from now on
|
||||
* Check if the reg property exists - if not insert the node
|
||||
* so upon kexec() the same memory region address will be preserved.
|
||||
* This is needed because QBMan HW does not allow the base address/
|
||||
* size to be modified once set.
|
||||
*/
|
||||
of_reserved_mem_device_release(dev);
|
||||
prop = of_find_property(mem_node, "reg", &len);
|
||||
if (!prop) {
|
||||
prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL);
|
||||
if (!prop)
|
||||
return -ENOMEM;
|
||||
prop->value = res_array = devm_kzalloc(dev, sizeof(__be32) * 4,
|
||||
GFP_KERNEL);
|
||||
if (!prop->value)
|
||||
return -ENOMEM;
|
||||
res_array[0] = cpu_to_be32(upper_32_bits(*addr));
|
||||
res_array[1] = cpu_to_be32(lower_32_bits(*addr));
|
||||
res_array[2] = cpu_to_be32(upper_32_bits(*size));
|
||||
res_array[3] = cpu_to_be32(lower_32_bits(*size));
|
||||
prop->length = sizeof(__be32) * 4;
|
||||
prop->name = devm_kstrdup(dev, "reg", GFP_KERNEL);
|
||||
if (!prop->name)
|
||||
return -ENOMEM;
|
||||
err = of_add_property(mem_node, prop);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1018,6 +1018,20 @@ static inline void put_affine_portal(void)
|
||||
put_cpu_var(qman_affine_portal);
|
||||
}
|
||||
|
||||
|
||||
static inline struct qman_portal *get_portal_for_channel(u16 channel)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_possible_cpus(); i++) {
|
||||
if (affine_portals[i] &&
|
||||
affine_portals[i]->config->channel == channel)
|
||||
return affine_portals[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct workqueue_struct *qm_portal_wq;
|
||||
|
||||
int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh)
|
||||
@ -1070,6 +1084,20 @@ int qman_wq_alloc(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void qman_enable_irqs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_possible_cpus(); i++) {
|
||||
if (affine_portals[i]) {
|
||||
qm_out(&affine_portals[i]->p, QM_REG_ISR, 0xffffffff);
|
||||
qm_out(&affine_portals[i]->p, QM_REG_IIR, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is what everything can wait on, even if it migrates to a different cpu
|
||||
* to the one whose affine portal it is waiting on.
|
||||
@ -1164,6 +1192,7 @@ static int drain_mr_fqrni(struct qm_portal *p)
|
||||
{
|
||||
const union qm_mr_entry *msg;
|
||||
loop:
|
||||
qm_mr_pvb_update(p);
|
||||
msg = qm_mr_current(p);
|
||||
if (!msg) {
|
||||
/*
|
||||
@ -1180,7 +1209,8 @@ loop:
|
||||
* entries well before the ring has been fully consumed, so
|
||||
* we're being *really* paranoid here.
|
||||
*/
|
||||
msleep(1);
|
||||
mdelay(1);
|
||||
qm_mr_pvb_update(p);
|
||||
msg = qm_mr_current(p);
|
||||
if (!msg)
|
||||
return 0;
|
||||
@ -1267,8 +1297,8 @@ static int qman_create_portal(struct qman_portal *portal,
|
||||
qm_out(p, QM_REG_ISDR, isdr);
|
||||
portal->irq_sources = 0;
|
||||
qm_out(p, QM_REG_IER, 0);
|
||||
qm_out(p, QM_REG_ISR, 0xffffffff);
|
||||
snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu);
|
||||
qm_out(p, QM_REG_IIR, 1);
|
||||
if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) {
|
||||
dev_err(c->dev, "request_irq() failed\n");
|
||||
goto fail_irq;
|
||||
@ -1288,7 +1318,7 @@ static int qman_create_portal(struct qman_portal *portal,
|
||||
isdr &= ~(QM_PIRQ_DQRI | QM_PIRQ_MRI);
|
||||
qm_out(p, QM_REG_ISDR, isdr);
|
||||
if (qm_dqrr_current(p)) {
|
||||
dev_err(c->dev, "DQRR unclean\n");
|
||||
dev_dbg(c->dev, "DQRR unclean\n");
|
||||
qm_dqrr_cdc_consume_n(p, 0xffff);
|
||||
}
|
||||
if (qm_mr_current(p) && drain_mr_fqrni(p)) {
|
||||
@ -1301,8 +1331,10 @@ static int qman_create_portal(struct qman_portal *portal,
|
||||
}
|
||||
/* Success */
|
||||
portal->config = c;
|
||||
qm_out(p, QM_REG_ISR, 0xffffffff);
|
||||
qm_out(p, QM_REG_ISDR, 0);
|
||||
qm_out(p, QM_REG_IIR, 0);
|
||||
if (!qman_requires_cleanup())
|
||||
qm_out(p, QM_REG_IIR, 0);
|
||||
/* Write a sane SDQCR */
|
||||
qm_dqrr_sdqcr_set(p, portal->sdqcr);
|
||||
return 0;
|
||||
@ -2581,9 +2613,9 @@ static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s,
|
||||
#define qm_dqrr_drain_nomatch(p) \
|
||||
_qm_dqrr_consume_and_match(p, 0, 0, false)
|
||||
|
||||
static int qman_shutdown_fq(u32 fqid)
|
||||
int qman_shutdown_fq(u32 fqid)
|
||||
{
|
||||
struct qman_portal *p;
|
||||
struct qman_portal *p, *channel_portal;
|
||||
struct device *dev;
|
||||
union qm_mc_command *mcc;
|
||||
union qm_mc_result *mcr;
|
||||
@ -2623,17 +2655,28 @@ static int qman_shutdown_fq(u32 fqid)
|
||||
channel = qm_fqd_get_chan(&mcr->queryfq.fqd);
|
||||
wq = qm_fqd_get_wq(&mcr->queryfq.fqd);
|
||||
|
||||
if (channel < qm_channel_pool1) {
|
||||
channel_portal = get_portal_for_channel(channel);
|
||||
if (channel_portal == NULL) {
|
||||
dev_err(dev, "Can't find portal for dedicated channel 0x%x\n",
|
||||
channel);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
channel_portal = p;
|
||||
|
||||
switch (state) {
|
||||
case QM_MCR_NP_STATE_TEN_SCHED:
|
||||
case QM_MCR_NP_STATE_TRU_SCHED:
|
||||
case QM_MCR_NP_STATE_ACTIVE:
|
||||
case QM_MCR_NP_STATE_PARKED:
|
||||
orl_empty = 0;
|
||||
mcc = qm_mc_start(&p->p);
|
||||
mcc = qm_mc_start(&channel_portal->p);
|
||||
qm_fqid_set(&mcc->fq, fqid);
|
||||
qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE);
|
||||
if (!qm_mc_result_timeout(&p->p, &mcr)) {
|
||||
dev_err(dev, "QUERYFQ_NP timeout\n");
|
||||
qm_mc_commit(&channel_portal->p, QM_MCC_VERB_ALTER_RETIRE);
|
||||
if (!qm_mc_result_timeout(&channel_portal->p, &mcr)) {
|
||||
dev_err(dev, "ALTER_RETIRE timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
@ -2641,6 +2684,9 @@ static int qman_shutdown_fq(u32 fqid)
|
||||
QM_MCR_VERB_ALTER_RETIRE);
|
||||
res = mcr->result; /* Make a copy as we reuse MCR below */
|
||||
|
||||
if (res == QM_MCR_RESULT_OK)
|
||||
drain_mr_fqrni(&channel_portal->p);
|
||||
|
||||
if (res == QM_MCR_RESULT_PENDING) {
|
||||
/*
|
||||
* Need to wait for the FQRN in the message ring, which
|
||||
@ -2670,21 +2716,25 @@ static int qman_shutdown_fq(u32 fqid)
|
||||
}
|
||||
/* Set the sdqcr to drain this channel */
|
||||
if (channel < qm_channel_pool1)
|
||||
qm_dqrr_sdqcr_set(&p->p,
|
||||
qm_dqrr_sdqcr_set(&channel_portal->p,
|
||||
QM_SDQCR_TYPE_ACTIVE |
|
||||
QM_SDQCR_CHANNELS_DEDICATED);
|
||||
else
|
||||
qm_dqrr_sdqcr_set(&p->p,
|
||||
qm_dqrr_sdqcr_set(&channel_portal->p,
|
||||
QM_SDQCR_TYPE_ACTIVE |
|
||||
QM_SDQCR_CHANNELS_POOL_CONV
|
||||
(channel));
|
||||
do {
|
||||
/* Keep draining DQRR while checking the MR*/
|
||||
qm_dqrr_drain_nomatch(&p->p);
|
||||
qm_dqrr_drain_nomatch(&channel_portal->p);
|
||||
/* Process message ring too */
|
||||
found_fqrn = qm_mr_drain(&p->p, FQRN);
|
||||
found_fqrn = qm_mr_drain(&channel_portal->p,
|
||||
FQRN);
|
||||
cpu_relax();
|
||||
} while (!found_fqrn);
|
||||
/* Restore SDQCR */
|
||||
qm_dqrr_sdqcr_set(&channel_portal->p,
|
||||
channel_portal->sdqcr);
|
||||
|
||||
}
|
||||
if (res != QM_MCR_RESULT_OK &&
|
||||
@ -2715,9 +2765,8 @@ static int qman_shutdown_fq(u32 fqid)
|
||||
* Wait for a dequeue and process the dequeues,
|
||||
* making sure to empty the ring completely
|
||||
*/
|
||||
} while (qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
|
||||
} while (!qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
|
||||
}
|
||||
qm_dqrr_sdqcr_set(&p->p, 0);
|
||||
|
||||
while (!orl_empty) {
|
||||
/* Wait for the ORL to have been completely drained */
|
||||
@ -2754,7 +2803,7 @@ static int qman_shutdown_fq(u32 fqid)
|
||||
|
||||
DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
|
||||
QM_MCR_VERB_ALTER_OOS);
|
||||
if (mcr->result) {
|
||||
if (mcr->result != QM_MCR_RESULT_OK) {
|
||||
dev_err(dev, "OOS fail: FQ 0x%x (0x%x)\n",
|
||||
fqid, mcr->result);
|
||||
ret = -EIO;
|
||||
|
@ -274,6 +274,7 @@ static u32 __iomem *qm_ccsr_start;
|
||||
/* A SDQCR mask comprising all the available/visible pool channels */
|
||||
static u32 qm_pools_sdqcr;
|
||||
static int __qman_probed;
|
||||
static int __qman_requires_cleanup;
|
||||
|
||||
static inline u32 qm_ccsr_in(u32 offset)
|
||||
{
|
||||
@ -340,19 +341,55 @@ static void qm_get_version(u16 *id, u8 *major, u8 *minor)
|
||||
}
|
||||
|
||||
#define PFDR_AR_EN BIT(31)
|
||||
static void qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
|
||||
static int qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
|
||||
{
|
||||
void *ptr;
|
||||
u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE;
|
||||
u32 exp = ilog2(size);
|
||||
u32 bar, bare;
|
||||
|
||||
/* choke if size isn't within range */
|
||||
DPAA_ASSERT((size >= 4096) && (size <= 1024*1024*1024) &&
|
||||
is_power_of_2(size));
|
||||
/* choke if 'ba' has lower-alignment than 'size' */
|
||||
DPAA_ASSERT(!(ba & (size - 1)));
|
||||
|
||||
/* Check to see if QMan has already been initialized */
|
||||
bar = qm_ccsr_in(offset + REG_offset_BAR);
|
||||
if (bar) {
|
||||
/* Maker sure ba == what was programmed) */
|
||||
bare = qm_ccsr_in(offset);
|
||||
if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) {
|
||||
pr_err("Attempted to reinitialize QMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n",
|
||||
ba, bare, bar);
|
||||
return -ENOMEM;
|
||||
}
|
||||
__qman_requires_cleanup = 1;
|
||||
/* Return 1 to indicate memory was previously programmed */
|
||||
return 1;
|
||||
}
|
||||
/* Need to temporarily map the area to make sure it is zeroed */
|
||||
ptr = memremap(ba, size, MEMREMAP_WB);
|
||||
if (!ptr) {
|
||||
pr_crit("memremap() of QMan private memory failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ptr, 0, size);
|
||||
|
||||
#ifdef CONFIG_PPC
|
||||
/*
|
||||
* PPC doesn't appear to flush the cache on memunmap() but the
|
||||
* cache must be flushed since QMan does non coherent accesses
|
||||
* to this memory
|
||||
*/
|
||||
flush_dcache_range((unsigned long) ptr, (unsigned long) ptr+size);
|
||||
#endif
|
||||
memunmap(ptr);
|
||||
|
||||
qm_ccsr_out(offset, upper_32_bits(ba));
|
||||
qm_ccsr_out(offset + REG_offset_BAR, lower_32_bits(ba));
|
||||
qm_ccsr_out(offset + REG_offset_AR, PFDR_AR_EN | (exp - 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qm_set_pfdr_threshold(u32 th, u8 k)
|
||||
@ -455,7 +492,7 @@ RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr);
|
||||
|
||||
#endif
|
||||
|
||||
static unsigned int qm_get_fqid_maxcnt(void)
|
||||
unsigned int qm_get_fqid_maxcnt(void)
|
||||
{
|
||||
return fqd_sz / 64;
|
||||
}
|
||||
@ -571,12 +608,19 @@ static int qman_init_ccsr(struct device *dev)
|
||||
int i, err;
|
||||
|
||||
/* FQD memory */
|
||||
qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
|
||||
/* PFDR memory */
|
||||
qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
|
||||
err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
|
||||
if (err)
|
||||
err = qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* PFDR memory */
|
||||
err = qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* Only initialize PFDRs if the QMan was not initialized before */
|
||||
if (err == 0) {
|
||||
err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
/* thresholds */
|
||||
qm_set_pfdr_threshold(512, 64);
|
||||
qm_set_sfdr_threshold(128);
|
||||
@ -693,6 +737,18 @@ int qman_is_probed(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qman_is_probed);
|
||||
|
||||
int qman_requires_cleanup(void)
|
||||
{
|
||||
return __qman_requires_cleanup;
|
||||
}
|
||||
|
||||
void qman_done_cleanup(void)
|
||||
{
|
||||
qman_enable_irqs();
|
||||
__qman_requires_cleanup = 0;
|
||||
}
|
||||
|
||||
|
||||
static int fsl_qman_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -233,7 +233,7 @@ static int qman_portal_probe(struct platform_device *pdev)
|
||||
struct device_node *node = dev->of_node;
|
||||
struct qm_portal_config *pcfg;
|
||||
struct resource *addr_phys[2];
|
||||
int irq, cpu, err;
|
||||
int irq, cpu, err, i;
|
||||
u32 val;
|
||||
|
||||
err = qman_is_probed();
|
||||
@ -275,10 +275,8 @@ static int qman_portal_probe(struct platform_device *pdev)
|
||||
pcfg->channel = val;
|
||||
pcfg->cpu = -1;
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "Can't get %pOF IRQ\n", node);
|
||||
if (irq <= 0)
|
||||
goto err_ioremap1;
|
||||
}
|
||||
pcfg->irq = irq;
|
||||
|
||||
pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
|
||||
@ -325,6 +323,22 @@ static int qman_portal_probe(struct platform_device *pdev)
|
||||
if (!cpu_online(cpu))
|
||||
qman_offline_cpu(cpu);
|
||||
|
||||
if (__qman_portals_probed == 1 && qman_requires_cleanup()) {
|
||||
/*
|
||||
* QMan wasn't reset prior to boot (Kexec for example)
|
||||
* Empty all the frame queues so they are in reset state
|
||||
*/
|
||||
for (i = 0; i < qm_get_fqid_maxcnt(); i++) {
|
||||
err = qman_shutdown_fq(i);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to shutdown frame queue %d\n",
|
||||
i);
|
||||
goto err_portal_init;
|
||||
}
|
||||
}
|
||||
qman_done_cleanup();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_portal_init:
|
||||
|
@ -272,3 +272,11 @@ extern struct qman_portal *affine_portals[NR_CPUS];
|
||||
extern struct qman_portal *qman_dma_portal;
|
||||
const struct qm_portal_config *qman_get_qm_portal_config(
|
||||
struct qman_portal *portal);
|
||||
|
||||
unsigned int qm_get_fqid_maxcnt(void);
|
||||
|
||||
int qman_shutdown_fq(u32 fqid);
|
||||
|
||||
int qman_requires_cleanup(void);
|
||||
void qman_done_cleanup(void);
|
||||
void qman_enable_irqs(void);
|
||||
|
@ -10,6 +10,7 @@
|
||||
* General Purpose functions for the global management of the
|
||||
* QUICC Engine (QE).
|
||||
*/
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -39,29 +40,32 @@ static DEFINE_SPINLOCK(qe_lock);
|
||||
DEFINE_SPINLOCK(cmxgcr_lock);
|
||||
EXPORT_SYMBOL(cmxgcr_lock);
|
||||
|
||||
/* QE snum state */
|
||||
enum qe_snum_state {
|
||||
QE_SNUM_STATE_USED,
|
||||
QE_SNUM_STATE_FREE
|
||||
};
|
||||
|
||||
/* QE snum */
|
||||
struct qe_snum {
|
||||
u8 num;
|
||||
enum qe_snum_state state;
|
||||
};
|
||||
|
||||
/* We allocate this here because it is used almost exclusively for
|
||||
* the communication processor devices.
|
||||
*/
|
||||
struct qe_immap __iomem *qe_immr;
|
||||
EXPORT_SYMBOL(qe_immr);
|
||||
|
||||
static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */
|
||||
static u8 snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */
|
||||
static DECLARE_BITMAP(snum_state, QE_NUM_OF_SNUM);
|
||||
static unsigned int qe_num_of_snum;
|
||||
|
||||
static phys_addr_t qebase = -1;
|
||||
|
||||
static struct device_node *qe_get_device_node(void)
|
||||
{
|
||||
struct device_node *qe;
|
||||
|
||||
/*
|
||||
* Newer device trees have an "fsl,qe" compatible property for the QE
|
||||
* node, but we still need to support older device trees.
|
||||
*/
|
||||
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
|
||||
if (qe)
|
||||
return qe;
|
||||
return of_find_node_by_type(NULL, "qe");
|
||||
}
|
||||
|
||||
static phys_addr_t get_qe_base(void)
|
||||
{
|
||||
struct device_node *qe;
|
||||
@ -71,12 +75,9 @@ static phys_addr_t get_qe_base(void)
|
||||
if (qebase != -1)
|
||||
return qebase;
|
||||
|
||||
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
|
||||
if (!qe) {
|
||||
qe = of_find_node_by_type(NULL, "qe");
|
||||
if (!qe)
|
||||
return qebase;
|
||||
}
|
||||
qe = qe_get_device_node();
|
||||
if (!qe)
|
||||
return qebase;
|
||||
|
||||
ret = of_address_to_resource(qe, 0, &res);
|
||||
if (!ret)
|
||||
@ -170,12 +171,9 @@ unsigned int qe_get_brg_clk(void)
|
||||
if (brg_clk)
|
||||
return brg_clk;
|
||||
|
||||
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
|
||||
if (!qe) {
|
||||
qe = of_find_node_by_type(NULL, "qe");
|
||||
if (!qe)
|
||||
return brg_clk;
|
||||
}
|
||||
qe = qe_get_device_node();
|
||||
if (!qe)
|
||||
return brg_clk;
|
||||
|
||||
prop = of_get_property(qe, "brg-frequency", &size);
|
||||
if (prop && size == sizeof(*prop))
|
||||
@ -281,7 +279,6 @@ EXPORT_SYMBOL(qe_clock_source);
|
||||
*/
|
||||
static void qe_snums_init(void)
|
||||
{
|
||||
int i;
|
||||
static const u8 snum_init_76[] = {
|
||||
0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D,
|
||||
0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89,
|
||||
@ -302,19 +299,39 @@ static void qe_snums_init(void)
|
||||
0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59,
|
||||
0x68, 0x69, 0x78, 0x79, 0x80, 0x81,
|
||||
};
|
||||
static const u8 *snum_init;
|
||||
struct device_node *qe;
|
||||
const u8 *snum_init;
|
||||
int i;
|
||||
|
||||
qe_num_of_snum = qe_get_num_of_snums();
|
||||
|
||||
if (qe_num_of_snum == 76)
|
||||
snum_init = snum_init_76;
|
||||
else
|
||||
snum_init = snum_init_46;
|
||||
|
||||
for (i = 0; i < qe_num_of_snum; i++) {
|
||||
snums[i].num = snum_init[i];
|
||||
snums[i].state = QE_SNUM_STATE_FREE;
|
||||
bitmap_zero(snum_state, QE_NUM_OF_SNUM);
|
||||
qe_num_of_snum = 28; /* The default number of snum for threads is 28 */
|
||||
qe = qe_get_device_node();
|
||||
if (qe) {
|
||||
i = of_property_read_variable_u8_array(qe, "fsl,qe-snums",
|
||||
snums, 1, QE_NUM_OF_SNUM);
|
||||
if (i > 0) {
|
||||
of_node_put(qe);
|
||||
qe_num_of_snum = i;
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Fall back to legacy binding of using the value of
|
||||
* fsl,qe-num-snums to choose one of the static arrays
|
||||
* above.
|
||||
*/
|
||||
of_property_read_u32(qe, "fsl,qe-num-snums", &qe_num_of_snum);
|
||||
of_node_put(qe);
|
||||
}
|
||||
|
||||
if (qe_num_of_snum == 76) {
|
||||
snum_init = snum_init_76;
|
||||
} else if (qe_num_of_snum == 28 || qe_num_of_snum == 46) {
|
||||
snum_init = snum_init_46;
|
||||
} else {
|
||||
pr_err("QE: unsupported value of fsl,qe-num-snums: %u\n", qe_num_of_snum);
|
||||
return;
|
||||
}
|
||||
memcpy(snums, snum_init, qe_num_of_snum);
|
||||
}
|
||||
|
||||
int qe_get_snum(void)
|
||||
@ -324,12 +341,10 @@ int qe_get_snum(void)
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&qe_lock, flags);
|
||||
for (i = 0; i < qe_num_of_snum; i++) {
|
||||
if (snums[i].state == QE_SNUM_STATE_FREE) {
|
||||
snums[i].state = QE_SNUM_STATE_USED;
|
||||
snum = snums[i].num;
|
||||
break;
|
||||
}
|
||||
i = find_first_zero_bit(snum_state, qe_num_of_snum);
|
||||
if (i < qe_num_of_snum) {
|
||||
set_bit(i, snum_state);
|
||||
snum = snums[i];
|
||||
}
|
||||
spin_unlock_irqrestore(&qe_lock, flags);
|
||||
|
||||
@ -339,14 +354,10 @@ EXPORT_SYMBOL(qe_get_snum);
|
||||
|
||||
void qe_put_snum(u8 snum)
|
||||
{
|
||||
int i;
|
||||
const u8 *p = memchr(snums, snum, qe_num_of_snum);
|
||||
|
||||
for (i = 0; i < qe_num_of_snum; i++) {
|
||||
if (snums[i].num == snum) {
|
||||
snums[i].state = QE_SNUM_STATE_FREE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (p)
|
||||
clear_bit(p - snums, snum_state);
|
||||
}
|
||||
EXPORT_SYMBOL(qe_put_snum);
|
||||
|
||||
@ -572,16 +583,9 @@ struct qe_firmware_info *qe_get_firmware_info(void)
|
||||
|
||||
initialized = 1;
|
||||
|
||||
/*
|
||||
* Newer device trees have an "fsl,qe" compatible property for the QE
|
||||
* node, but we still need to support older device trees.
|
||||
*/
|
||||
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
|
||||
if (!qe) {
|
||||
qe = of_find_node_by_type(NULL, "qe");
|
||||
if (!qe)
|
||||
return NULL;
|
||||
}
|
||||
qe = qe_get_device_node();
|
||||
if (!qe)
|
||||
return NULL;
|
||||
|
||||
/* Find the 'firmware' child node */
|
||||
fw = of_get_child_by_name(qe, "firmware");
|
||||
@ -627,16 +631,9 @@ unsigned int qe_get_num_of_risc(void)
|
||||
unsigned int num_of_risc = 0;
|
||||
const u32 *prop;
|
||||
|
||||
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
|
||||
if (!qe) {
|
||||
/* Older devices trees did not have an "fsl,qe"
|
||||
* compatible property, so we need to look for
|
||||
* the QE node by name.
|
||||
*/
|
||||
qe = of_find_node_by_type(NULL, "qe");
|
||||
if (!qe)
|
||||
return num_of_risc;
|
||||
}
|
||||
qe = qe_get_device_node();
|
||||
if (!qe)
|
||||
return num_of_risc;
|
||||
|
||||
prop = of_get_property(qe, "fsl,qe-num-riscs", &size);
|
||||
if (prop && size == sizeof(*prop))
|
||||
@ -650,37 +647,7 @@ EXPORT_SYMBOL(qe_get_num_of_risc);
|
||||
|
||||
unsigned int qe_get_num_of_snums(void)
|
||||
{
|
||||
struct device_node *qe;
|
||||
int size;
|
||||
unsigned int num_of_snums;
|
||||
const u32 *prop;
|
||||
|
||||
num_of_snums = 28; /* The default number of snum for threads is 28 */
|
||||
qe = of_find_compatible_node(NULL, NULL, "fsl,qe");
|
||||
if (!qe) {
|
||||
/* Older devices trees did not have an "fsl,qe"
|
||||
* compatible property, so we need to look for
|
||||
* the QE node by name.
|
||||
*/
|
||||
qe = of_find_node_by_type(NULL, "qe");
|
||||
if (!qe)
|
||||
return num_of_snums;
|
||||
}
|
||||
|
||||
prop = of_get_property(qe, "fsl,qe-num-snums", &size);
|
||||
if (prop && size == sizeof(*prop)) {
|
||||
num_of_snums = *prop;
|
||||
if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) {
|
||||
/* No QE ever has fewer than 28 SNUMs */
|
||||
pr_err("QE: number of snum is invalid\n");
|
||||
of_node_put(qe);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
of_node_put(qe);
|
||||
|
||||
return num_of_snums;
|
||||
return qe_num_of_snum;
|
||||
}
|
||||
EXPORT_SYMBOL(qe_get_num_of_snums);
|
||||
|
||||
|
@ -198,7 +198,7 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
|
||||
err = regulator_disable(domain->regulator);
|
||||
if (err)
|
||||
dev_err(domain->dev,
|
||||
"failed to disable regulator: %d\n", ret);
|
||||
"failed to disable regulator: %d\n", err);
|
||||
/* Preserve earlier error code */
|
||||
ret = ret ?: err;
|
||||
}
|
||||
|
@ -27,6 +27,40 @@ struct imx_sc_msg_misc_get_soc_id {
|
||||
} data;
|
||||
} __packed;
|
||||
|
||||
struct imx_sc_msg_misc_get_soc_uid {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
u32 uid_low;
|
||||
u32 uid_high;
|
||||
} __packed;
|
||||
|
||||
static ssize_t soc_uid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct imx_sc_msg_misc_get_soc_uid msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
u64 soc_uid;
|
||||
int ret;
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_MISC;
|
||||
hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID;
|
||||
hdr->size = 1;
|
||||
|
||||
ret = imx_scu_call_rpc(soc_ipc_handle, &msg, false);
|
||||
if (ret) {
|
||||
pr_err("%s: get soc uid failed, ret %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
soc_uid = msg.uid_high;
|
||||
soc_uid <<= 32;
|
||||
soc_uid |= msg.uid_low;
|
||||
|
||||
return sprintf(buf, "%016llX\n", soc_uid);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(soc_uid);
|
||||
|
||||
static int imx_scu_soc_id(void)
|
||||
{
|
||||
struct imx_sc_msg_misc_get_soc_id msg;
|
||||
@ -102,6 +136,11 @@ static int imx_scu_soc_probe(struct platform_device *pdev)
|
||||
goto free_revision;
|
||||
}
|
||||
|
||||
ret = device_create_file(soc_device_to_device(soc_dev),
|
||||
&dev_attr_soc_uid);
|
||||
if (ret)
|
||||
goto free_revision;
|
||||
|
||||
return 0;
|
||||
|
||||
free_revision:
|
||||
|
@ -16,6 +16,9 @@
|
||||
#define IMX8MQ_SW_INFO_B1 0x40
|
||||
#define IMX8MQ_SW_MAGIC_B1 0xff0055aa
|
||||
|
||||
#define OCOTP_UID_LOW 0x410
|
||||
#define OCOTP_UID_HIGH 0x420
|
||||
|
||||
/* Same as ANADIG_DIGPROG_IMX7D */
|
||||
#define ANADIG_DIGPROG_IMX8MM 0x800
|
||||
|
||||
@ -24,6 +27,16 @@ struct imx8_soc_data {
|
||||
u32 (*soc_revision)(void);
|
||||
};
|
||||
|
||||
static u64 soc_uid;
|
||||
|
||||
static ssize_t soc_uid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%016llX\n", soc_uid);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(soc_uid);
|
||||
|
||||
static u32 __init imx8mq_soc_revision(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
@ -42,6 +55,10 @@ static u32 __init imx8mq_soc_revision(void)
|
||||
if (magic == IMX8MQ_SW_MAGIC_B1)
|
||||
rev = REV_B1;
|
||||
|
||||
soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
|
||||
soc_uid <<= 32;
|
||||
soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
|
||||
|
||||
iounmap(ocotp_base);
|
||||
|
||||
out:
|
||||
@ -49,6 +66,26 @@ out:
|
||||
return rev;
|
||||
}
|
||||
|
||||
static void __init imx8mm_soc_uid(void)
|
||||
{
|
||||
void __iomem *ocotp_base;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp");
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
ocotp_base = of_iomap(np, 0);
|
||||
WARN_ON(!ocotp_base);
|
||||
|
||||
soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
|
||||
soc_uid <<= 32;
|
||||
soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);
|
||||
|
||||
iounmap(ocotp_base);
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
static u32 __init imx8mm_soc_revision(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
@ -66,6 +103,9 @@ static u32 __init imx8mm_soc_revision(void)
|
||||
|
||||
iounmap(anatop_base);
|
||||
of_node_put(np);
|
||||
|
||||
imx8mm_soc_uid();
|
||||
|
||||
return rev;
|
||||
}
|
||||
|
||||
@ -140,6 +180,11 @@ static int __init imx8_soc_init(void)
|
||||
goto free_rev;
|
||||
}
|
||||
|
||||
ret = device_create_file(soc_device_to_device(soc_dev),
|
||||
&dev_attr_soc_uid);
|
||||
if (ret)
|
||||
goto free_rev;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
|
||||
platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);
|
||||
|
||||
|
@ -136,7 +136,7 @@ static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset)
|
||||
int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value)
|
||||
{
|
||||
u32 arg_a = (offset & CMDQ_ARG_A_WRITE_MASK) |
|
||||
(subsys << CMDQ_SUBSYS_SHIFT);
|
||||
@ -145,8 +145,8 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset)
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_write);
|
||||
|
||||
int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value,
|
||||
u32 subsys, u32 offset, u32 mask)
|
||||
int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
|
||||
u16 offset, u32 value, u32 mask)
|
||||
{
|
||||
u32 offset_mask = offset;
|
||||
int err = 0;
|
||||
@ -161,7 +161,7 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value,
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_write_mask);
|
||||
|
||||
int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event)
|
||||
int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event)
|
||||
{
|
||||
u32 arg_b;
|
||||
|
||||
@ -181,7 +181,7 @@ int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event)
|
||||
}
|
||||
EXPORT_SYMBOL(cmdq_pkt_wfe);
|
||||
|
||||
int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u32 event)
|
||||
int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event)
|
||||
{
|
||||
if (event >= CMDQ_MAX_EVENT)
|
||||
return -EINVAL;
|
||||
|
@ -175,6 +175,14 @@ config QCOM_SMSM
|
||||
Say yes here to support the Qualcomm Shared Memory State Machine.
|
||||
The state machine is represented by bits in shared memory.
|
||||
|
||||
config QCOM_SOCINFO
|
||||
tristate "Qualcomm socinfo driver"
|
||||
depends on QCOM_SMEM
|
||||
select SOC_BUS
|
||||
help
|
||||
Say yes here to support the Qualcomm socinfo driver, providing
|
||||
information about the SoC to user space.
|
||||
|
||||
config QCOM_WCNSS_CTRL
|
||||
tristate "Qualcomm WCNSS control driver"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
|
@ -18,6 +18,7 @@ obj-$(CONFIG_QCOM_SMEM) += smem.o
|
||||
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
|
||||
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
|
||||
obj-$(CONFIG_QCOM_SMSM) += smsm.o
|
||||
obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o
|
||||
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
|
||||
obj-$(CONFIG_QCOM_APR) += apr.o
|
||||
obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define QMP_DESC_MAGIC 0x0
|
||||
#define QMP_DESC_VERSION 0x4
|
||||
@ -40,6 +42,17 @@
|
||||
/* 64 bytes is enough to store the requests and provides padding to 4 bytes */
|
||||
#define QMP_MSG_LEN 64
|
||||
|
||||
#define QMP_NUM_COOLING_RESOURCES 2
|
||||
|
||||
static bool qmp_cdev_init_state = 1;
|
||||
|
||||
struct qmp_cooling_device {
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct qmp *qmp;
|
||||
char *name;
|
||||
bool state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qmp - driver state for QMP implementation
|
||||
* @msgram: iomem referencing the message RAM used for communication
|
||||
@ -69,6 +82,7 @@ struct qmp {
|
||||
|
||||
struct clk_hw qdss_clk;
|
||||
struct genpd_onecell_data pd_data;
|
||||
struct qmp_cooling_device *cooling_devs;
|
||||
};
|
||||
|
||||
struct qmp_pd {
|
||||
@ -385,6 +399,118 @@ static void qmp_pd_remove(struct qmp *qmp)
|
||||
pm_genpd_remove(data->domains[i]);
|
||||
}
|
||||
|
||||
static int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
*state = qmp_cdev_init_state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmp_cdev_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct qmp_cooling_device *qmp_cdev = cdev->devdata;
|
||||
|
||||
*state = qmp_cdev->state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct qmp_cooling_device *qmp_cdev = cdev->devdata;
|
||||
char buf[QMP_MSG_LEN] = {};
|
||||
bool cdev_state;
|
||||
int ret;
|
||||
|
||||
/* Normalize state */
|
||||
cdev_state = !!state;
|
||||
|
||||
if (qmp_cdev->state == state)
|
||||
return 0;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{class: volt_flr, event:zero_temp, res:%s, value:%s}",
|
||||
qmp_cdev->name,
|
||||
cdev_state ? "off" : "on");
|
||||
|
||||
ret = qmp_send(qmp_cdev->qmp, buf, sizeof(buf));
|
||||
|
||||
if (!ret)
|
||||
qmp_cdev->state = cdev_state;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_cooling_device_ops qmp_cooling_device_ops = {
|
||||
.get_max_state = qmp_cdev_get_max_state,
|
||||
.get_cur_state = qmp_cdev_get_cur_state,
|
||||
.set_cur_state = qmp_cdev_set_cur_state,
|
||||
};
|
||||
|
||||
static int qmp_cooling_device_add(struct qmp *qmp,
|
||||
struct qmp_cooling_device *qmp_cdev,
|
||||
struct device_node *node)
|
||||
{
|
||||
char *cdev_name = (char *)node->name;
|
||||
|
||||
qmp_cdev->qmp = qmp;
|
||||
qmp_cdev->state = qmp_cdev_init_state;
|
||||
qmp_cdev->name = cdev_name;
|
||||
qmp_cdev->cdev = devm_thermal_of_cooling_device_register
|
||||
(qmp->dev, node,
|
||||
cdev_name,
|
||||
qmp_cdev, &qmp_cooling_device_ops);
|
||||
|
||||
if (IS_ERR(qmp_cdev->cdev))
|
||||
dev_err(qmp->dev, "unable to register %s cooling device\n",
|
||||
cdev_name);
|
||||
|
||||
return PTR_ERR_OR_ZERO(qmp_cdev->cdev);
|
||||
}
|
||||
|
||||
static int qmp_cooling_devices_register(struct qmp *qmp)
|
||||
{
|
||||
struct device_node *np, *child;
|
||||
int count = QMP_NUM_COOLING_RESOURCES;
|
||||
int ret;
|
||||
|
||||
np = qmp->dev->of_node;
|
||||
|
||||
qmp->cooling_devs = devm_kcalloc(qmp->dev, count,
|
||||
sizeof(*qmp->cooling_devs),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!qmp->cooling_devs)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
if (!of_find_property(child, "#cooling-cells", NULL))
|
||||
continue;
|
||||
ret = qmp_cooling_device_add(qmp, &qmp->cooling_devs[count++],
|
||||
child);
|
||||
if (ret)
|
||||
goto unroll;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unroll:
|
||||
while (--count >= 0)
|
||||
thermal_cooling_device_unregister
|
||||
(qmp->cooling_devs[count].cdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qmp_cooling_devices_remove(struct qmp *qmp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < QMP_NUM_COOLING_RESOURCES; i++)
|
||||
thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev);
|
||||
}
|
||||
|
||||
static int qmp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
@ -433,6 +559,10 @@ static int qmp_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto err_remove_qdss_clk;
|
||||
|
||||
ret = qmp_cooling_devices_register(qmp);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "failed to register aoss cooling devices\n");
|
||||
|
||||
platform_set_drvdata(pdev, qmp);
|
||||
|
||||
return 0;
|
||||
@ -453,6 +583,7 @@ static int qmp_remove(struct platform_device *pdev)
|
||||
|
||||
qmp_qdss_clk_remove(qmp);
|
||||
qmp_pd_remove(qmp);
|
||||
qmp_cooling_devices_remove(qmp);
|
||||
|
||||
qmp_close(qmp);
|
||||
mbox_free_channel(qmp->mbox_chan);
|
||||
@ -461,7 +592,9 @@ static int qmp_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id qmp_dt_match[] = {
|
||||
{ .compatible = "qcom,sc7180-aoss-qmp", },
|
||||
{ .compatible = "qcom,sdm845-aoss-qmp", },
|
||||
{ .compatible = "qcom,sm8150-aoss-qmp", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qmp_dt_match);
|
||||
|
@ -84,7 +84,7 @@
|
||||
#define SMEM_GLOBAL_HOST 0xfffe
|
||||
|
||||
/* Max number of processors/hosts in a system */
|
||||
#define SMEM_HOST_COUNT 10
|
||||
#define SMEM_HOST_COUNT 11
|
||||
|
||||
/**
|
||||
* struct smem_proc_comm - proc_comm communication struct (legacy)
|
||||
@ -268,6 +268,7 @@ struct qcom_smem {
|
||||
struct smem_partition_header *partitions[SMEM_HOST_COUNT];
|
||||
size_t cacheline[SMEM_HOST_COUNT];
|
||||
u32 item_count;
|
||||
struct platform_device *socinfo;
|
||||
|
||||
unsigned num_regions;
|
||||
struct smem_region regions[];
|
||||
@ -963,11 +964,19 @@ static int qcom_smem_probe(struct platform_device *pdev)
|
||||
|
||||
__smem = smem;
|
||||
|
||||
smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
|
||||
PLATFORM_DEVID_NONE, NULL,
|
||||
0);
|
||||
if (IS_ERR(smem->socinfo))
|
||||
dev_dbg(&pdev->dev, "failed to register socinfo device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_smem_remove(struct platform_device *pdev)
|
||||
{
|
||||
platform_device_unregister(__smem->socinfo);
|
||||
|
||||
hwspin_lock_free(__smem->hwlock);
|
||||
__smem = NULL;
|
||||
|
||||
|
476
drivers/soc/qcom/socinfo.c
Normal file
476
drivers/soc/qcom/socinfo.c
Normal file
@ -0,0 +1,476 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2017-2019, Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/qcom/smem.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* SoC version type with major number in the upper 16 bits and minor
|
||||
* number in the lower 16 bits.
|
||||
*/
|
||||
#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff)
|
||||
#define SOCINFO_MINOR(ver) ((ver) & 0xffff)
|
||||
#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
|
||||
|
||||
#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
|
||||
|
||||
/*
|
||||
* SMEM item id, used to acquire handles to respective
|
||||
* SMEM region.
|
||||
*/
|
||||
#define SMEM_HW_SW_BUILD_ID 137
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
#define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32
|
||||
#define SMEM_IMAGE_VERSION_SIZE 4096
|
||||
#define SMEM_IMAGE_VERSION_NAME_SIZE 75
|
||||
#define SMEM_IMAGE_VERSION_VARIANT_SIZE 20
|
||||
#define SMEM_IMAGE_VERSION_OEM_SIZE 32
|
||||
|
||||
/*
|
||||
* SMEM Image table indices
|
||||
*/
|
||||
#define SMEM_IMAGE_TABLE_BOOT_INDEX 0
|
||||
#define SMEM_IMAGE_TABLE_TZ_INDEX 1
|
||||
#define SMEM_IMAGE_TABLE_RPM_INDEX 3
|
||||
#define SMEM_IMAGE_TABLE_APPS_INDEX 10
|
||||
#define SMEM_IMAGE_TABLE_MPSS_INDEX 11
|
||||
#define SMEM_IMAGE_TABLE_ADSP_INDEX 12
|
||||
#define SMEM_IMAGE_TABLE_CNSS_INDEX 13
|
||||
#define SMEM_IMAGE_TABLE_VIDEO_INDEX 14
|
||||
#define SMEM_IMAGE_VERSION_TABLE 469
|
||||
|
||||
/*
|
||||
* SMEM Image table names
|
||||
*/
|
||||
static const char *const socinfo_image_names[] = {
|
||||
[SMEM_IMAGE_TABLE_ADSP_INDEX] = "adsp",
|
||||
[SMEM_IMAGE_TABLE_APPS_INDEX] = "apps",
|
||||
[SMEM_IMAGE_TABLE_BOOT_INDEX] = "boot",
|
||||
[SMEM_IMAGE_TABLE_CNSS_INDEX] = "cnss",
|
||||
[SMEM_IMAGE_TABLE_MPSS_INDEX] = "mpss",
|
||||
[SMEM_IMAGE_TABLE_RPM_INDEX] = "rpm",
|
||||
[SMEM_IMAGE_TABLE_TZ_INDEX] = "tz",
|
||||
[SMEM_IMAGE_TABLE_VIDEO_INDEX] = "video",
|
||||
};
|
||||
|
||||
static const char *const pmic_models[] = {
|
||||
[0] = "Unknown PMIC model",
|
||||
[9] = "PM8994",
|
||||
[11] = "PM8916",
|
||||
[13] = "PM8058",
|
||||
[14] = "PM8028",
|
||||
[15] = "PM8901",
|
||||
[16] = "PM8027",
|
||||
[17] = "ISL9519",
|
||||
[18] = "PM8921",
|
||||
[19] = "PM8018",
|
||||
[20] = "PM8015",
|
||||
[21] = "PM8014",
|
||||
[22] = "PM8821",
|
||||
[23] = "PM8038",
|
||||
[24] = "PM8922",
|
||||
[25] = "PM8917",
|
||||
};
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
/* Socinfo SMEM item structure */
|
||||
struct socinfo {
|
||||
__le32 fmt;
|
||||
__le32 id;
|
||||
__le32 ver;
|
||||
char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH];
|
||||
/* Version 2 */
|
||||
__le32 raw_id;
|
||||
__le32 raw_ver;
|
||||
/* Version 3 */
|
||||
__le32 hw_plat;
|
||||
/* Version 4 */
|
||||
__le32 plat_ver;
|
||||
/* Version 5 */
|
||||
__le32 accessory_chip;
|
||||
/* Version 6 */
|
||||
__le32 hw_plat_subtype;
|
||||
/* Version 7 */
|
||||
__le32 pmic_model;
|
||||
__le32 pmic_die_rev;
|
||||
/* Version 8 */
|
||||
__le32 pmic_model_1;
|
||||
__le32 pmic_die_rev_1;
|
||||
__le32 pmic_model_2;
|
||||
__le32 pmic_die_rev_2;
|
||||
/* Version 9 */
|
||||
__le32 foundry_id;
|
||||
/* Version 10 */
|
||||
__le32 serial_num;
|
||||
/* Version 11 */
|
||||
__le32 num_pmics;
|
||||
__le32 pmic_array_offset;
|
||||
/* Version 12 */
|
||||
__le32 chip_family;
|
||||
__le32 raw_device_family;
|
||||
__le32 raw_device_num;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct socinfo_params {
|
||||
u32 raw_device_family;
|
||||
u32 hw_plat_subtype;
|
||||
u32 accessory_chip;
|
||||
u32 raw_device_num;
|
||||
u32 chip_family;
|
||||
u32 foundry_id;
|
||||
u32 plat_ver;
|
||||
u32 raw_ver;
|
||||
u32 hw_plat;
|
||||
u32 fmt;
|
||||
};
|
||||
|
||||
struct smem_image_version {
|
||||
char name[SMEM_IMAGE_VERSION_NAME_SIZE];
|
||||
char variant[SMEM_IMAGE_VERSION_VARIANT_SIZE];
|
||||
char pad;
|
||||
char oem[SMEM_IMAGE_VERSION_OEM_SIZE];
|
||||
};
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
struct qcom_socinfo {
|
||||
struct soc_device *soc_dev;
|
||||
struct soc_device_attribute attr;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dbg_root;
|
||||
struct socinfo_params info;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
};
|
||||
|
||||
struct soc_id {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static const struct soc_id soc_id[] = {
|
||||
{ 87, "MSM8960" },
|
||||
{ 109, "APQ8064" },
|
||||
{ 122, "MSM8660A" },
|
||||
{ 123, "MSM8260A" },
|
||||
{ 124, "APQ8060A" },
|
||||
{ 126, "MSM8974" },
|
||||
{ 130, "MPQ8064" },
|
||||
{ 138, "MSM8960AB" },
|
||||
{ 139, "APQ8060AB" },
|
||||
{ 140, "MSM8260AB" },
|
||||
{ 141, "MSM8660AB" },
|
||||
{ 178, "APQ8084" },
|
||||
{ 184, "APQ8074" },
|
||||
{ 185, "MSM8274" },
|
||||
{ 186, "MSM8674" },
|
||||
{ 194, "MSM8974PRO" },
|
||||
{ 206, "MSM8916" },
|
||||
{ 208, "APQ8074-AA" },
|
||||
{ 209, "APQ8074-AB" },
|
||||
{ 210, "APQ8074PRO" },
|
||||
{ 211, "MSM8274-AA" },
|
||||
{ 212, "MSM8274-AB" },
|
||||
{ 213, "MSM8274PRO" },
|
||||
{ 214, "MSM8674-AA" },
|
||||
{ 215, "MSM8674-AB" },
|
||||
{ 216, "MSM8674PRO" },
|
||||
{ 217, "MSM8974-AA" },
|
||||
{ 218, "MSM8974-AB" },
|
||||
{ 246, "MSM8996" },
|
||||
{ 247, "APQ8016" },
|
||||
{ 248, "MSM8216" },
|
||||
{ 249, "MSM8116" },
|
||||
{ 250, "MSM8616" },
|
||||
{ 291, "APQ8096" },
|
||||
{ 305, "MSM8996SG" },
|
||||
{ 310, "MSM8996AU" },
|
||||
{ 311, "APQ8096AU" },
|
||||
{ 312, "APQ8096SG" },
|
||||
};
|
||||
|
||||
static const char *socinfo_machine(struct device *dev, unsigned int id)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(soc_id); idx++) {
|
||||
if (soc_id[idx].id == id)
|
||||
return soc_id[idx].name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
#define QCOM_OPEN(name, _func) \
|
||||
static int qcom_open_##name(struct inode *inode, struct file *file) \
|
||||
{ \
|
||||
return single_open(file, _func, inode->i_private); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations qcom_ ##name## _ops = { \
|
||||
.open = qcom_open_##name, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
}
|
||||
|
||||
#define DEBUGFS_ADD(info, name) \
|
||||
debugfs_create_file(__stringify(name), 0400, \
|
||||
qcom_socinfo->dbg_root, \
|
||||
info, &qcom_ ##name## _ops)
|
||||
|
||||
|
||||
static int qcom_show_build_id(struct seq_file *seq, void *p)
|
||||
{
|
||||
struct socinfo *socinfo = seq->private;
|
||||
|
||||
seq_printf(seq, "%s\n", socinfo->build_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_show_pmic_model(struct seq_file *seq, void *p)
|
||||
{
|
||||
struct socinfo *socinfo = seq->private;
|
||||
int model = SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_model));
|
||||
|
||||
if (model < 0)
|
||||
return -EINVAL;
|
||||
|
||||
seq_printf(seq, "%s\n", pmic_models[model]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_show_pmic_die_revision(struct seq_file *seq, void *p)
|
||||
{
|
||||
struct socinfo *socinfo = seq->private;
|
||||
|
||||
seq_printf(seq, "%u.%u\n",
|
||||
SOCINFO_MAJOR(le32_to_cpu(socinfo->pmic_die_rev)),
|
||||
SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_die_rev)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QCOM_OPEN(build_id, qcom_show_build_id);
|
||||
QCOM_OPEN(pmic_model, qcom_show_pmic_model);
|
||||
QCOM_OPEN(pmic_die_rev, qcom_show_pmic_die_revision);
|
||||
|
||||
#define DEFINE_IMAGE_OPS(type) \
|
||||
static int show_image_##type(struct seq_file *seq, void *p) \
|
||||
{ \
|
||||
struct smem_image_version *image_version = seq->private; \
|
||||
seq_puts(seq, image_version->type); \
|
||||
seq_puts(seq, "\n"); \
|
||||
return 0; \
|
||||
} \
|
||||
static int open_image_##type(struct inode *inode, struct file *file) \
|
||||
{ \
|
||||
return single_open(file, show_image_##type, inode->i_private); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations qcom_image_##type##_ops = { \
|
||||
.open = open_image_##type, \
|
||||
.read = seq_read, \
|
||||
.llseek = seq_lseek, \
|
||||
.release = single_release, \
|
||||
}
|
||||
|
||||
DEFINE_IMAGE_OPS(name);
|
||||
DEFINE_IMAGE_OPS(variant);
|
||||
DEFINE_IMAGE_OPS(oem);
|
||||
|
||||
static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo,
|
||||
struct socinfo *info)
|
||||
{
|
||||
struct smem_image_version *versions;
|
||||
struct dentry *dentry;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
qcom_socinfo->dbg_root = debugfs_create_dir("qcom_socinfo", NULL);
|
||||
|
||||
qcom_socinfo->info.fmt = __le32_to_cpu(info->fmt);
|
||||
|
||||
switch (qcom_socinfo->info.fmt) {
|
||||
case SOCINFO_VERSION(0, 12):
|
||||
qcom_socinfo->info.chip_family =
|
||||
__le32_to_cpu(info->chip_family);
|
||||
qcom_socinfo->info.raw_device_family =
|
||||
__le32_to_cpu(info->raw_device_family);
|
||||
qcom_socinfo->info.raw_device_num =
|
||||
__le32_to_cpu(info->raw_device_num);
|
||||
|
||||
debugfs_create_x32("chip_family", 0400, qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.chip_family);
|
||||
debugfs_create_x32("raw_device_family", 0400,
|
||||
qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.raw_device_family);
|
||||
debugfs_create_x32("raw_device_number", 0400,
|
||||
qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.raw_device_num);
|
||||
/* Fall through */
|
||||
case SOCINFO_VERSION(0, 11):
|
||||
case SOCINFO_VERSION(0, 10):
|
||||
case SOCINFO_VERSION(0, 9):
|
||||
qcom_socinfo->info.foundry_id = __le32_to_cpu(info->foundry_id);
|
||||
|
||||
debugfs_create_u32("foundry_id", 0400, qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.foundry_id);
|
||||
/* Fall through */
|
||||
case SOCINFO_VERSION(0, 8):
|
||||
case SOCINFO_VERSION(0, 7):
|
||||
DEBUGFS_ADD(info, pmic_model);
|
||||
DEBUGFS_ADD(info, pmic_die_rev);
|
||||
/* Fall through */
|
||||
case SOCINFO_VERSION(0, 6):
|
||||
qcom_socinfo->info.hw_plat_subtype =
|
||||
__le32_to_cpu(info->hw_plat_subtype);
|
||||
|
||||
debugfs_create_u32("hardware_platform_subtype", 0400,
|
||||
qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.hw_plat_subtype);
|
||||
/* Fall through */
|
||||
case SOCINFO_VERSION(0, 5):
|
||||
qcom_socinfo->info.accessory_chip =
|
||||
__le32_to_cpu(info->accessory_chip);
|
||||
|
||||
debugfs_create_u32("accessory_chip", 0400,
|
||||
qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.accessory_chip);
|
||||
/* Fall through */
|
||||
case SOCINFO_VERSION(0, 4):
|
||||
qcom_socinfo->info.plat_ver = __le32_to_cpu(info->plat_ver);
|
||||
|
||||
debugfs_create_u32("platform_version", 0400,
|
||||
qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.plat_ver);
|
||||
/* Fall through */
|
||||
case SOCINFO_VERSION(0, 3):
|
||||
qcom_socinfo->info.hw_plat = __le32_to_cpu(info->hw_plat);
|
||||
|
||||
debugfs_create_u32("hardware_platform", 0400,
|
||||
qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.hw_plat);
|
||||
/* Fall through */
|
||||
case SOCINFO_VERSION(0, 2):
|
||||
qcom_socinfo->info.raw_ver = __le32_to_cpu(info->raw_ver);
|
||||
|
||||
debugfs_create_u32("raw_version", 0400, qcom_socinfo->dbg_root,
|
||||
&qcom_socinfo->info.raw_ver);
|
||||
/* Fall through */
|
||||
case SOCINFO_VERSION(0, 1):
|
||||
DEBUGFS_ADD(info, build_id);
|
||||
break;
|
||||
}
|
||||
|
||||
versions = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE,
|
||||
&size);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(socinfo_image_names); i++) {
|
||||
if (!socinfo_image_names[i])
|
||||
continue;
|
||||
|
||||
dentry = debugfs_create_dir(socinfo_image_names[i],
|
||||
qcom_socinfo->dbg_root);
|
||||
debugfs_create_file("name", 0400, dentry, &versions[i],
|
||||
&qcom_image_name_ops);
|
||||
debugfs_create_file("variant", 0400, dentry, &versions[i],
|
||||
&qcom_image_variant_ops);
|
||||
debugfs_create_file("oem", 0400, dentry, &versions[i],
|
||||
&qcom_image_oem_ops);
|
||||
}
|
||||
}
|
||||
|
||||
static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo)
|
||||
{
|
||||
debugfs_remove_recursive(qcom_socinfo->dbg_root);
|
||||
}
|
||||
#else
|
||||
static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo,
|
||||
struct socinfo *info)
|
||||
{
|
||||
}
|
||||
static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) { }
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
static int qcom_socinfo_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_socinfo *qs;
|
||||
struct socinfo *info;
|
||||
size_t item_size;
|
||||
|
||||
info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID,
|
||||
&item_size);
|
||||
if (IS_ERR(info)) {
|
||||
dev_err(&pdev->dev, "Couldn't find socinfo\n");
|
||||
return PTR_ERR(info);
|
||||
}
|
||||
|
||||
qs = devm_kzalloc(&pdev->dev, sizeof(*qs), GFP_KERNEL);
|
||||
if (!qs)
|
||||
return -ENOMEM;
|
||||
|
||||
qs->attr.family = "Snapdragon";
|
||||
qs->attr.machine = socinfo_machine(&pdev->dev,
|
||||
le32_to_cpu(info->id));
|
||||
qs->attr.revision = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u.%u",
|
||||
SOCINFO_MAJOR(le32_to_cpu(info->ver)),
|
||||
SOCINFO_MINOR(le32_to_cpu(info->ver)));
|
||||
if (offsetof(struct socinfo, serial_num) <= item_size)
|
||||
qs->attr.serial_number = devm_kasprintf(&pdev->dev, GFP_KERNEL,
|
||||
"%u",
|
||||
le32_to_cpu(info->serial_num));
|
||||
|
||||
qs->soc_dev = soc_device_register(&qs->attr);
|
||||
if (IS_ERR(qs->soc_dev))
|
||||
return PTR_ERR(qs->soc_dev);
|
||||
|
||||
socinfo_debugfs_init(qs, info);
|
||||
|
||||
/* Feed the soc specific unique data into entropy pool */
|
||||
add_device_randomness(info, item_size);
|
||||
|
||||
platform_set_drvdata(pdev, qs->soc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_socinfo_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_socinfo *qs = platform_get_drvdata(pdev);
|
||||
|
||||
soc_device_unregister(qs->soc_dev);
|
||||
|
||||
socinfo_debugfs_exit(qs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver qcom_socinfo_driver = {
|
||||
.probe = qcom_socinfo_probe,
|
||||
.remove = qcom_socinfo_remove,
|
||||
.driver = {
|
||||
.name = "qcom-socinfo",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(qcom_socinfo_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm SoCinfo driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:qcom-socinfo");
|
@ -55,6 +55,7 @@ config ARCH_EMEV2
|
||||
|
||||
config ARCH_R7S72100
|
||||
bool "RZ/A1H (R7S72100)"
|
||||
select ARM_ERRATA_754322
|
||||
select PM
|
||||
select PM_GENERIC_DOMAINS
|
||||
select RENESAS_OSTM
|
||||
@ -72,12 +73,14 @@ config ARCH_R8A73A4
|
||||
bool "R-Mobile APE6 (R8A73A40)"
|
||||
select ARCH_RMOBILE
|
||||
select ARM_ERRATA_798181 if SMP
|
||||
select ARM_ERRATA_814220
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
select RENESAS_IRQC
|
||||
|
||||
config ARCH_R8A7740
|
||||
bool "R-Mobile A1 (R8A77400)"
|
||||
select ARCH_RMOBILE
|
||||
select ARM_ERRATA_754322
|
||||
select RENESAS_INTC_IRQPIN
|
||||
|
||||
config ARCH_R8A7743
|
||||
@ -95,20 +98,24 @@ config ARCH_R8A7744
|
||||
config ARCH_R8A7745
|
||||
bool "RZ/G1E (R8A77450)"
|
||||
select ARCH_RCAR_GEN2
|
||||
select ARM_ERRATA_814220
|
||||
select SYSC_R8A7745
|
||||
|
||||
config ARCH_R8A77470
|
||||
bool "RZ/G1C (R8A77470)"
|
||||
select ARCH_RCAR_GEN2
|
||||
select ARM_ERRATA_814220
|
||||
select SYSC_R8A77470
|
||||
|
||||
config ARCH_R8A7778
|
||||
bool "R-Car M1A (R8A77781)"
|
||||
select ARCH_RCAR_GEN1
|
||||
select ARM_ERRATA_754322
|
||||
|
||||
config ARCH_R8A7779
|
||||
bool "R-Car H1 (R8A77790)"
|
||||
select ARCH_RCAR_GEN1
|
||||
select ARM_ERRATA_754322
|
||||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_ARM_TWD if SMP
|
||||
select SYSC_R8A7779
|
||||
@ -117,6 +124,7 @@ config ARCH_R8A7790
|
||||
bool "R-Car H2 (R8A77900)"
|
||||
select ARCH_RCAR_GEN2
|
||||
select ARM_ERRATA_798181 if SMP
|
||||
select ARM_ERRATA_814220
|
||||
select I2C
|
||||
select SYSC_R8A7790
|
||||
|
||||
@ -143,15 +151,18 @@ config ARCH_R8A7793
|
||||
config ARCH_R8A7794
|
||||
bool "R-Car E2 (R8A77940)"
|
||||
select ARCH_RCAR_GEN2
|
||||
select ARM_ERRATA_814220
|
||||
select SYSC_R8A7794
|
||||
|
||||
config ARCH_R9A06G032
|
||||
bool "RZ/N1D (R9A06G032)"
|
||||
select ARCH_RZN1
|
||||
select ARM_ERRATA_814220
|
||||
|
||||
config ARCH_SH73A0
|
||||
bool "SH-Mobile AG5 (R8A73A00)"
|
||||
select ARCH_RMOBILE
|
||||
select ARM_ERRATA_754322
|
||||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_ARM_TWD if SMP
|
||||
select RENESAS_INTC_IRQPIN
|
||||
|
@ -170,7 +170,7 @@ struct rcar_sysc_pd {
|
||||
struct generic_pm_domain genpd;
|
||||
struct rcar_sysc_ch ch;
|
||||
unsigned int flags;
|
||||
char name[0];
|
||||
char name[];
|
||||
};
|
||||
|
||||
static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d)
|
||||
@ -200,7 +200,6 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
|
||||
{
|
||||
struct generic_pm_domain *genpd = &pd->genpd;
|
||||
const char *name = pd->genpd.name;
|
||||
struct dev_power_governor *gov = &simple_qos_governor;
|
||||
int error;
|
||||
|
||||
if (pd->flags & PD_CPU) {
|
||||
@ -254,7 +253,7 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)
|
||||
rcar_sysc_power(&pd->ch, true);
|
||||
|
||||
finalize:
|
||||
error = pm_genpd_init(genpd, gov, false);
|
||||
error = pm_genpd_init(genpd, &simple_qos_governor, false);
|
||||
if (error)
|
||||
pr_err("Failed to init PM domain %s: %d\n", name, error);
|
||||
|
||||
@ -346,7 +345,7 @@ static int __init rcar_sysc_pd_init(void)
|
||||
if (info->init) {
|
||||
error = info->init();
|
||||
if (error)
|
||||
return error;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
has_cpg_mstp = of_find_compatible_node(NULL, NULL,
|
||||
|
@ -48,12 +48,8 @@ struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d)
|
||||
static int rmobile_pd_power_down(struct generic_pm_domain *genpd)
|
||||
{
|
||||
struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd);
|
||||
unsigned int mask;
|
||||
unsigned int mask = BIT(rmobile_pd->bit_shift);
|
||||
|
||||
if (rmobile_pd->bit_shift == ~0)
|
||||
return -EBUSY;
|
||||
|
||||
mask = BIT(rmobile_pd->bit_shift);
|
||||
if (rmobile_pd->suspend) {
|
||||
int ret = rmobile_pd->suspend();
|
||||
|
||||
@ -80,14 +76,10 @@ static int rmobile_pd_power_down(struct generic_pm_domain *genpd)
|
||||
|
||||
static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd)
|
||||
{
|
||||
unsigned int mask;
|
||||
unsigned int mask = BIT(rmobile_pd->bit_shift);
|
||||
unsigned int retry_count;
|
||||
int ret = 0;
|
||||
|
||||
if (rmobile_pd->bit_shift == ~0)
|
||||
return 0;
|
||||
|
||||
mask = BIT(rmobile_pd->bit_shift);
|
||||
if (__raw_readl(rmobile_pd->base + PSTR) & mask)
|
||||
return ret;
|
||||
|
||||
@ -122,11 +114,15 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
|
||||
struct dev_power_governor *gov = rmobile_pd->gov;
|
||||
|
||||
genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
|
||||
genpd->power_off = rmobile_pd_power_down;
|
||||
genpd->power_on = rmobile_pd_power_up;
|
||||
genpd->attach_dev = cpg_mstp_attach_dev;
|
||||
genpd->detach_dev = cpg_mstp_detach_dev;
|
||||
__rmobile_pd_power_up(rmobile_pd);
|
||||
genpd->attach_dev = cpg_mstp_attach_dev;
|
||||
genpd->detach_dev = cpg_mstp_detach_dev;
|
||||
|
||||
if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) {
|
||||
genpd->power_off = rmobile_pd_power_down;
|
||||
genpd->power_on = rmobile_pd_power_up;
|
||||
__rmobile_pd_power_up(rmobile_pd);
|
||||
}
|
||||
|
||||
pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
|
||||
}
|
||||
|
||||
@ -270,6 +266,11 @@ static void __init rmobile_setup_pm_domain(struct device_node *np,
|
||||
break;
|
||||
|
||||
case PD_NORMAL:
|
||||
if (pd->bit_shift == ~0) {
|
||||
/* Top-level always-on domain */
|
||||
pr_debug("PM domain %s is always-on domain\n", name);
|
||||
pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,12 @@ menuconfig SOC_SAMSUNG
|
||||
|
||||
if SOC_SAMSUNG
|
||||
|
||||
config EXYNOS_CHIPID
|
||||
bool "Exynos Chipid controller driver" if COMPILE_TEST
|
||||
depends on ARCH_EXYNOS || COMPILE_TEST
|
||||
select MFD_SYSCON
|
||||
select SOC_BUS
|
||||
|
||||
config EXYNOS_PMU
|
||||
bool "Exynos PMU controller driver" if COMPILE_TEST
|
||||
depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST)
|
||||
|
@ -1,4 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o
|
||||
obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
|
||||
|
||||
obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \
|
||||
|
105
drivers/soc/samsung/exynos-chipid.c
Normal file
105
drivers/soc/samsung/exynos-chipid.c
Normal file
@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2019 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com/
|
||||
*
|
||||
* EXYNOS - CHIP ID support
|
||||
* Author: Pankaj Dubey <pankaj.dubey@samsung.com>
|
||||
* Author: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/samsung/exynos-chipid.h>
|
||||
#include <linux/sys_soc.h>
|
||||
|
||||
static const struct exynos_soc_id {
|
||||
const char *name;
|
||||
unsigned int id;
|
||||
} soc_ids[] = {
|
||||
{ "EXYNOS3250", 0xE3472000 },
|
||||
{ "EXYNOS4210", 0x43200000 }, /* EVT0 revision */
|
||||
{ "EXYNOS4210", 0x43210000 },
|
||||
{ "EXYNOS4212", 0x43220000 },
|
||||
{ "EXYNOS4412", 0xE4412000 },
|
||||
{ "EXYNOS5250", 0x43520000 },
|
||||
{ "EXYNOS5260", 0xE5260000 },
|
||||
{ "EXYNOS5410", 0xE5410000 },
|
||||
{ "EXYNOS5420", 0xE5420000 },
|
||||
{ "EXYNOS5440", 0xE5440000 },
|
||||
{ "EXYNOS5800", 0xE5422000 },
|
||||
{ "EXYNOS7420", 0xE7420000 },
|
||||
{ "EXYNOS5433", 0xE5433000 },
|
||||
};
|
||||
|
||||
static const char * __init product_id_to_soc_id(unsigned int product_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(soc_ids); i++)
|
||||
if ((product_id & EXYNOS_MASK) == soc_ids[i].id)
|
||||
return soc_ids[i].name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int __init exynos_chipid_early_init(void)
|
||||
{
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
struct soc_device *soc_dev;
|
||||
struct device_node *root;
|
||||
struct regmap *regmap;
|
||||
u32 product_id;
|
||||
u32 revision;
|
||||
int ret;
|
||||
|
||||
regmap = syscon_regmap_lookup_by_compatible("samsung,exynos4210-chipid");
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
revision = product_id & EXYNOS_REV_MASK;
|
||||
|
||||
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
|
||||
if (!soc_dev_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
soc_dev_attr->family = "Samsung Exynos";
|
||||
|
||||
root = of_find_node_by_path("/");
|
||||
of_property_read_string(root, "model", &soc_dev_attr->machine);
|
||||
of_node_put(root);
|
||||
|
||||
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x", revision);
|
||||
soc_dev_attr->soc_id = product_id_to_soc_id(product_id);
|
||||
if (!soc_dev_attr->soc_id) {
|
||||
pr_err("Unknown SoC\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* please note that the actual registration will be deferred */
|
||||
soc_dev = soc_device_register(soc_dev_attr);
|
||||
if (IS_ERR(soc_dev)) {
|
||||
ret = PTR_ERR(soc_dev);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* it is too early to use dev_info() here (soc_dev is NULL) */
|
||||
pr_info("soc soc0: Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
|
||||
soc_dev_attr->soc_id, product_id, revision);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(soc_dev_attr->revision);
|
||||
kfree(soc_dev_attr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
early_initcall(exynos_chipid_early_init);
|
@ -15,15 +15,19 @@
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/ti/ti_sci_protocol.h>
|
||||
#include <dt-bindings/soc/ti,sci_pm_domain.h>
|
||||
|
||||
/**
|
||||
* struct ti_sci_genpd_dev_data: holds data needed for every device attached
|
||||
* to this genpd
|
||||
* @idx: index of the device that identifies it with the system
|
||||
* control processor.
|
||||
* @exclusive: Permissions for exclusive request or shared request of the
|
||||
* device.
|
||||
*/
|
||||
struct ti_sci_genpd_dev_data {
|
||||
int idx;
|
||||
u8 exclusive;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -55,6 +59,14 @@ static int ti_sci_dev_id(struct device *dev)
|
||||
return sci_dev_data->idx;
|
||||
}
|
||||
|
||||
static u8 is_ti_sci_dev_exclusive(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev);
|
||||
struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data;
|
||||
|
||||
return sci_dev_data->exclusive;
|
||||
}
|
||||
|
||||
/**
|
||||
* ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle
|
||||
* @dev: pointer to device associated with this genpd
|
||||
@ -79,7 +91,10 @@ static int ti_sci_dev_start(struct device *dev)
|
||||
const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev);
|
||||
int idx = ti_sci_dev_id(dev);
|
||||
|
||||
return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
|
||||
if (is_ti_sci_dev_exclusive(dev))
|
||||
return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, idx);
|
||||
else
|
||||
return ti_sci->ops.dev_ops.get_device(ti_sci, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,7 +125,7 @@ static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (pd_args.args_count != 1)
|
||||
if (pd_args.args_count != 1 && pd_args.args_count != 2)
|
||||
return -EINVAL;
|
||||
|
||||
idx = pd_args.args[0];
|
||||
@ -128,6 +143,10 @@ static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain,
|
||||
return -ENOMEM;
|
||||
|
||||
sci_dev_data->idx = idx;
|
||||
/* Enable the exclusive permissions by default */
|
||||
sci_dev_data->exclusive = TI_SCI_PD_EXCLUSIVE;
|
||||
if (pd_args.args_count == 2)
|
||||
sci_dev_data->exclusive = pd_args.args[1] & 0x1;
|
||||
|
||||
genpd_data = dev_gpd_data(dev);
|
||||
genpd_data->data = sci_dev_data;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user