From: thead_admin Date: Tue, 14 Nov 2023 01:42:19 +0000 (+0000) Subject: Linux_SDK_V1.3.3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7feb4352184b6d5ef9e403717142ab3b32a79e9f;p=platform%2Fkernel%2Flinux-thead.git Linux_SDK_V1.3.3 Signed-off-by: thead_admin (cherry picked from commit e17ac7bab2079beba2376f104279619e037e9e2c) Signed-off-by: Jaehoon Chung --- diff --git a/Documentation/devicetree/bindings/arm/idle-states.yaml b/Documentation/devicetree/bindings/arm/idle-states.yaml deleted file mode 100644 index ea805c1e6b20..000000000000 --- a/Documentation/devicetree/bindings/arm/idle-states.yaml +++ /dev/null @@ -1,661 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/arm/idle-states.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: ARM idle states binding description - -maintainers: - - Lorenzo Pieralisi - -description: |+ - ========================================== - 1 - Introduction - ========================================== - - ARM systems contain HW capable of managing power consumption dynamically, - where cores can be put in different low-power states (ranging from simple wfi - to power gating) according to OS PM policies. The CPU states representing the - range of dynamic idle states that a processor can enter at run-time, can be - specified through device tree bindings representing the parameters required to - enter/exit specific idle states on a given processor. - - According to the Server Base System Architecture document (SBSA, [3]), the - power states an ARM CPU can be put into are identified by the following list: - - - Running - - Idle_standby - - Idle_retention - - Sleep - - Off - - The power states described in the SBSA document define the basic CPU states on - top of which ARM platforms implement power management schemes that allow an OS - PM implementation to put the processor in different idle states (which include - states listed above; "off" state is not an idle state since it does not have - wake-up capabilities, hence it is not considered in this document). - - Idle state parameters (e.g. entry latency) are platform specific and need to - be characterized with bindings that provide the required information to OS PM - code so that it can build the required tables and use them at runtime. - - The device tree binding definition for ARM idle states is the subject of this - document. - - =========================================== - 2 - idle-states definitions - =========================================== - - Idle states are characterized for a specific system through a set of - timing and energy related properties, that underline the HW behaviour - triggered upon idle states entry and exit. - - The following diagram depicts the CPU execution phases and related timing - properties required to enter and exit an idle state: - - ..__[EXEC]__|__[PREP]__|__[ENTRY]__|__[IDLE]__|__[EXIT]__|__[EXEC]__.. - | | | | | - - |<------ entry ------->| - | latency | - |<- exit ->| - | latency | - |<-------- min-residency -------->| - |<------- wakeup-latency ------->| - - Diagram 1: CPU idle state execution phases - - EXEC: Normal CPU execution. - - PREP: Preparation phase before committing the hardware to idle mode - like cache flushing. This is abortable on pending wake-up - event conditions. The abort latency is assumed to be negligible - (i.e. less than the ENTRY + EXIT duration). If aborted, CPU - goes back to EXEC. This phase is optional. If not abortable, - this should be included in the ENTRY phase instead. - - ENTRY: The hardware is committed to idle mode. This period must run - to completion up to IDLE before anything else can happen. - - IDLE: This is the actual energy-saving idle period. This may last - between 0 and infinite time, until a wake-up event occurs. - - EXIT: Period during which the CPU is brought back to operational - mode (EXEC). - - entry-latency: Worst case latency required to enter the idle state. The - exit-latency may be guaranteed only after entry-latency has passed. - - min-residency: Minimum period, including preparation and entry, for a given - idle state to be worthwhile energywise. - - wakeup-latency: Maximum delay between the signaling of a wake-up event and the - CPU being able to execute normal code again. If not specified, this is assumed - to be entry-latency + exit-latency. - - These timing parameters can be used by an OS in different circumstances. - - An idle CPU requires the expected min-residency time to select the most - appropriate idle state based on the expected expiry time of the next IRQ - (i.e. wake-up) that causes the CPU to return to the EXEC phase. - - An operating system scheduler may need to compute the shortest wake-up delay - for CPUs in the system by detecting how long will it take to get a CPU out - of an idle state, e.g.: - - wakeup-delay = exit-latency + max(entry-latency - (now - entry-timestamp), 0) - - In other words, the scheduler can make its scheduling decision by selecting - (e.g. waking-up) the CPU with the shortest wake-up delay. - The wake-up delay must take into account the entry latency if that period - has not expired. The abortable nature of the PREP period can be ignored - if it cannot be relied upon (e.g. the PREP deadline may occur much sooner than - the worst case since it depends on the CPU operating conditions, i.e. caches - state). - - An OS has to reliably probe the wakeup-latency since some devices can enforce - latency constraint guarantees to work properly, so the OS has to detect the - worst case wake-up latency it can incur if a CPU is allowed to enter an - idle state, and possibly to prevent that to guarantee reliable device - functioning. - - The min-residency time parameter deserves further explanation since it is - expressed in time units but must factor in energy consumption coefficients. - - The energy consumption of a cpu when it enters a power state can be roughly - characterised by the following graph: - - | - | - | - e | - n | /--- - e | /------ - r | /------ - g | /----- - y | /------ - | ---- - | /| - | / | - | / | - | / | - | / | - | / | - |/ | - -----|-------+---------------------------------- - 0| 1 time(ms) - - Graph 1: Energy vs time example - - The graph is split in two parts delimited by time 1ms on the X-axis. - The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope - and denotes the energy costs incurred while entering and leaving the idle - state. - The graph curve in the area delimited by X-axis values = {x | x > 1ms } has - shallower slope and essentially represents the energy consumption of the idle - state. - - min-residency is defined for a given idle state as the minimum expected - residency time for a state (inclusive of preparation and entry) after - which choosing that state become the most energy efficient option. A good - way to visualise this, is by taking the same graph above and comparing some - states energy consumptions plots. - - For sake of simplicity, let's consider a system with two idle states IDLE1, - and IDLE2: - - | - | - | - | /-- IDLE1 - e | /--- - n | /---- - e | /--- - r | /-----/--------- IDLE2 - g | /-------/--------- - y | ------------ /---| - | / /---- | - | / /--- | - | / /---- | - | / /--- | - | --- | - | / | - | / | - |/ | time - ---/----------------------------+------------------------ - |IDLE1-energy < IDLE2-energy | IDLE2-energy < IDLE1-energy - | - IDLE2-min-residency - - Graph 2: idle states min-residency example - - In graph 2 above, that takes into account idle states entry/exit energy - costs, it is clear that if the idle state residency time (i.e. time till next - wake-up IRQ) is less than IDLE2-min-residency, IDLE1 is the better idle state - choice energywise. - - This is mainly down to the fact that IDLE1 entry/exit energy costs are lower - than IDLE2. - - However, the lower power consumption (i.e. shallower energy curve slope) of - idle state IDLE2 implies that after a suitable time, IDLE2 becomes more energy - efficient. - - The time at which IDLE2 becomes more energy efficient than IDLE1 (and other - shallower states in a system with multiple idle states) is defined - IDLE2-min-residency and corresponds to the time when energy consumption of - IDLE1 and IDLE2 states breaks even. - - The definitions provided in this section underpin the idle states - properties specification that is the subject of the following sections. - - =========================================== - 3 - idle-states node - =========================================== - - ARM processor idle states are defined within the idle-states node, which is - a direct child of the cpus node [1] and provides a container where the - processor idle states, defined as device tree nodes, are listed. - - On ARM systems, it is a container of processor idle states nodes. If the - system does not provide CPU power management capabilities, or the processor - just supports idle_standby, an idle-states node is not required. - - =========================================== - 4 - References - =========================================== - - [1] ARM Linux Kernel documentation - CPUs bindings - Documentation/devicetree/bindings/arm/cpus.yaml - - [2] ARM Linux Kernel documentation - PSCI bindings - Documentation/devicetree/bindings/arm/psci.yaml - - [3] ARM Server Base System Architecture (SBSA) - http://infocenter.arm.com/help/index.jsp - - [4] ARM Architecture Reference Manuals - http://infocenter.arm.com/help/index.jsp - - [6] ARM Linux Kernel documentation - Booting AArch64 Linux - Documentation/arm64/booting.rst - -properties: - $nodename: - const: idle-states - - entry-method: - description: | - Usage and definition depend on ARM architecture version. - - On ARM v8 64-bit this property is required. - On ARM 32-bit systems this property is optional - - This assumes that the "enable-method" property is set to "psci" in the cpu - node[6] that is responsible for setting up CPU idle management in the OS - implementation. - const: psci - -patternProperties: - "^(cpu|cluster)-": - type: object - description: | - Each state node represents an idle state description and must be defined - as follows. - - The idle state entered by executing the wfi instruction (idle_standby - SBSA,[3][4]) is considered standard on all ARM platforms and therefore - must not be listed. - - In addition to the properties listed above, a state node may require - additional properties specific to the entry-method defined in the - idle-states node. Please refer to the entry-method bindings - documentation for properties definitions. - - properties: - compatible: - const: arm,idle-state - - local-timer-stop: - description: - If present the CPU local timer control logic is - lost on state entry, otherwise it is retained. - type: boolean - - entry-latency-us: - description: - Worst case latency in microseconds required to enter the idle state. - - exit-latency-us: - description: - Worst case latency in microseconds required to exit the idle state. - The exit-latency-us duration may be guaranteed only after - entry-latency-us has passed. - - min-residency-us: - description: - Minimum residency duration in microseconds, inclusive of preparation - and entry, for this idle state to be considered worthwhile energy wise - (refer to section 2 of this document for a complete description). - - wakeup-latency-us: - description: | - Maximum delay between the signaling of a wake-up event and the CPU - being able to execute normal code again. If omitted, this is assumed - to be equal to: - - entry-latency-us + exit-latency-us - - It is important to supply this value on systems where the duration of - PREP phase (see diagram 1, section 2) is non-neglibigle. In such - systems entry-latency-us + exit-latency-us will exceed - wakeup-latency-us by this duration. - - idle-state-name: - $ref: /schemas/types.yaml#definitions/string - description: - A string used as a descriptive name for the idle state. - - required: - - compatible - - entry-latency-us - - exit-latency-us - - min-residency-us - -additionalProperties: false - -examples: - - | - - cpus { - #size-cells = <0>; - #address-cells = <2>; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x0 0x0>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 - &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; - }; - - cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x0 0x1>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 - &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; - }; - - cpu@100 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x0 0x100>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 - &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; - }; - - cpu@101 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x0 0x101>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 - &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; - }; - - cpu@10000 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x0 0x10000>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 - &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; - }; - - cpu@10001 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x0 0x10001>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 - &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; - }; - - cpu@10100 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x0 0x10100>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 - &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; - }; - - cpu@10101 { - device_type = "cpu"; - compatible = "arm,cortex-a57"; - reg = <0x0 0x10101>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 - &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; - }; - - cpu@100000000 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1 0x0>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 - &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; - }; - - cpu@100000001 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1 0x1>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 - &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; - }; - - cpu@100000100 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1 0x100>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 - &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; - }; - - cpu@100000101 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1 0x101>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 - &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; - }; - - cpu@100010000 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1 0x10000>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 - &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; - }; - - cpu@100010001 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1 0x10001>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 - &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; - }; - - cpu@100010100 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1 0x10100>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 - &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; - }; - - cpu@100010101 { - device_type = "cpu"; - compatible = "arm,cortex-a53"; - reg = <0x1 0x10101>; - enable-method = "psci"; - cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 - &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; - }; - - idle-states { - entry-method = "psci"; - - CPU_RETENTION_0_0: cpu-retention-0-0 { - compatible = "arm,idle-state"; - arm,psci-suspend-param = <0x0010000>; - entry-latency-us = <20>; - exit-latency-us = <40>; - min-residency-us = <80>; - }; - - CLUSTER_RETENTION_0: cluster-retention-0 { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x1010000>; - entry-latency-us = <50>; - exit-latency-us = <100>; - min-residency-us = <250>; - wakeup-latency-us = <130>; - }; - - CPU_SLEEP_0_0: cpu-sleep-0-0 { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x0010000>; - entry-latency-us = <250>; - exit-latency-us = <500>; - min-residency-us = <950>; - }; - - CLUSTER_SLEEP_0: cluster-sleep-0 { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x1010000>; - entry-latency-us = <600>; - exit-latency-us = <1100>; - min-residency-us = <2700>; - wakeup-latency-us = <1500>; - }; - - CPU_RETENTION_1_0: cpu-retention-1-0 { - compatible = "arm,idle-state"; - arm,psci-suspend-param = <0x0010000>; - entry-latency-us = <20>; - exit-latency-us = <40>; - min-residency-us = <90>; - }; - - CLUSTER_RETENTION_1: cluster-retention-1 { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x1010000>; - entry-latency-us = <50>; - exit-latency-us = <100>; - min-residency-us = <270>; - wakeup-latency-us = <100>; - }; - - CPU_SLEEP_1_0: cpu-sleep-1-0 { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x0010000>; - entry-latency-us = <70>; - exit-latency-us = <100>; - min-residency-us = <300>; - wakeup-latency-us = <150>; - }; - - CLUSTER_SLEEP_1: cluster-sleep-1 { - compatible = "arm,idle-state"; - local-timer-stop; - arm,psci-suspend-param = <0x1010000>; - entry-latency-us = <500>; - exit-latency-us = <1200>; - min-residency-us = <3500>; - wakeup-latency-us = <1300>; - }; - }; - }; - - - | - // Example 2 (ARM 32-bit, 8-cpu system, two clusters): - - cpus { - #size-cells = <0>; - #address-cells = <1>; - - cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a15"; - reg = <0x0>; - cpu-idle-states = <&cpu_sleep_0_0 &cluster_sleep_0>; - }; - - cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a15"; - reg = <0x1>; - cpu-idle-states = <&cpu_sleep_0_0 &cluster_sleep_0>; - }; - - cpu@2 { - device_type = "cpu"; - compatible = "arm,cortex-a15"; - reg = <0x2>; - cpu-idle-states = <&cpu_sleep_0_0 &cluster_sleep_0>; - }; - - cpu@3 { - device_type = "cpu"; - compatible = "arm,cortex-a15"; - reg = <0x3>; - cpu-idle-states = <&cpu_sleep_0_0 &cluster_sleep_0>; - }; - - cpu@100 { - device_type = "cpu"; - compatible = "arm,cortex-a7"; - reg = <0x100>; - cpu-idle-states = <&cpu_sleep_1_0 &cluster_sleep_1>; - }; - - cpu@101 { - device_type = "cpu"; - compatible = "arm,cortex-a7"; - reg = <0x101>; - cpu-idle-states = <&cpu_sleep_1_0 &cluster_sleep_1>; - }; - - cpu@102 { - device_type = "cpu"; - compatible = "arm,cortex-a7"; - reg = <0x102>; - cpu-idle-states = <&cpu_sleep_1_0 &cluster_sleep_1>; - }; - - cpu@103 { - device_type = "cpu"; - compatible = "arm,cortex-a7"; - reg = <0x103>; - cpu-idle-states = <&cpu_sleep_1_0 &cluster_sleep_1>; - }; - - idle-states { - cpu_sleep_0_0: cpu-sleep-0-0 { - compatible = "arm,idle-state"; - local-timer-stop; - entry-latency-us = <200>; - exit-latency-us = <100>; - min-residency-us = <400>; - wakeup-latency-us = <250>; - }; - - cluster_sleep_0: cluster-sleep-0 { - compatible = "arm,idle-state"; - local-timer-stop; - entry-latency-us = <500>; - exit-latency-us = <1500>; - min-residency-us = <2500>; - wakeup-latency-us = <1700>; - }; - - cpu_sleep_1_0: cpu-sleep-1-0 { - compatible = "arm,idle-state"; - local-timer-stop; - entry-latency-us = <300>; - exit-latency-us = <500>; - min-residency-us = <900>; - wakeup-latency-us = <600>; - }; - - cluster_sleep_1: cluster-sleep-1 { - compatible = "arm,idle-state"; - local-timer-stop; - entry-latency-us = <800>; - exit-latency-us = <2000>; - min-residency-us = <6500>; - wakeup-latency-us = <2300>; - }; - }; - }; - -... diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt b/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt index 6ce0b212ec6d..606b4b1b709d 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt +++ b/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt @@ -81,4 +81,4 @@ Example: }; }; -[1]. Documentation/devicetree/bindings/arm/idle-states.yaml +[1]. Documentation/devicetree/bindings/cpu/idle-states.yaml diff --git a/Documentation/devicetree/bindings/arm/psci.yaml b/Documentation/devicetree/bindings/arm/psci.yaml index 8b77cf83a095..dd83ef278af0 100644 --- a/Documentation/devicetree/bindings/arm/psci.yaml +++ b/Documentation/devicetree/bindings/arm/psci.yaml @@ -101,7 +101,7 @@ properties: bindings in [1]) must specify this property. [1] Kernel documentation - ARM idle states bindings - Documentation/devicetree/bindings/arm/idle-states.yaml + Documentation/devicetree/bindings/cpu/idle-states.yaml patternProperties: "^power-domain-": diff --git a/Documentation/devicetree/bindings/cpu/idle-states.yaml b/Documentation/devicetree/bindings/cpu/idle-states.yaml new file mode 100644 index 000000000000..4c8f82509e15 --- /dev/null +++ b/Documentation/devicetree/bindings/cpu/idle-states.yaml @@ -0,0 +1,855 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/cpu/idle-states.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Idle states binding description + +maintainers: + - Lorenzo Pieralisi + - Anup Patel + +description: |+ + ========================================== + 1 - Introduction + ========================================== + + ARM and RISC-V systems contain HW capable of managing power consumption + dynamically, where cores can be put in different low-power states (ranging + from simple wfi to power gating) according to OS PM policies. The CPU states + representing the range of dynamic idle states that a processor can enter at + run-time, can be specified through device tree bindings representing the + parameters required to enter/exit specific idle states on a given processor. + + ========================================== + 2 - ARM idle states + ========================================== + + According to the Server Base System Architecture document (SBSA, [3]), the + power states an ARM CPU can be put into are identified by the following list: + + - Running + - Idle_standby + - Idle_retention + - Sleep + - Off + + The power states described in the SBSA document define the basic CPU states on + top of which ARM platforms implement power management schemes that allow an OS + PM implementation to put the processor in different idle states (which include + states listed above; "off" state is not an idle state since it does not have + wake-up capabilities, hence it is not considered in this document). + + Idle state parameters (e.g. entry latency) are platform specific and need to + be characterized with bindings that provide the required information to OS PM + code so that it can build the required tables and use them at runtime. + + The device tree binding definition for ARM idle states is the subject of this + document. + + ========================================== + 3 - RISC-V idle states + ========================================== + + On RISC-V systems, the HARTs (or CPUs) [6] can be put in platform specific + suspend (or idle) states (ranging from simple WFI, power gating, etc). The + RISC-V SBI v0.3 (or higher) [7] hart state management extension provides a + standard mechanism for OS to request HART state transitions. + + The platform specific suspend (or idle) states of a hart can be either + retentive or non-rententive in nature. A retentive suspend state will + preserve HART registers and CSR values for all privilege modes whereas + a non-retentive suspend state will not preserve HART registers and CSR + values. + + =========================================== + 4 - idle-states definitions + =========================================== + + Idle states are characterized for a specific system through a set of + timing and energy related properties, that underline the HW behaviour + triggered upon idle states entry and exit. + + The following diagram depicts the CPU execution phases and related timing + properties required to enter and exit an idle state: + + ..__[EXEC]__|__[PREP]__|__[ENTRY]__|__[IDLE]__|__[EXIT]__|__[EXEC]__.. + | | | | | + + |<------ entry ------->| + | latency | + |<- exit ->| + | latency | + |<-------- min-residency -------->| + |<------- wakeup-latency ------->| + + Diagram 1: CPU idle state execution phases + + EXEC: Normal CPU execution. + + PREP: Preparation phase before committing the hardware to idle mode + like cache flushing. This is abortable on pending wake-up + event conditions. The abort latency is assumed to be negligible + (i.e. less than the ENTRY + EXIT duration). If aborted, CPU + goes back to EXEC. This phase is optional. If not abortable, + this should be included in the ENTRY phase instead. + + ENTRY: The hardware is committed to idle mode. This period must run + to completion up to IDLE before anything else can happen. + + IDLE: This is the actual energy-saving idle period. This may last + between 0 and infinite time, until a wake-up event occurs. + + EXIT: Period during which the CPU is brought back to operational + mode (EXEC). + + entry-latency: Worst case latency required to enter the idle state. The + exit-latency may be guaranteed only after entry-latency has passed. + + min-residency: Minimum period, including preparation and entry, for a given + idle state to be worthwhile energywise. + + wakeup-latency: Maximum delay between the signaling of a wake-up event and the + CPU being able to execute normal code again. If not specified, this is assumed + to be entry-latency + exit-latency. + + These timing parameters can be used by an OS in different circumstances. + + An idle CPU requires the expected min-residency time to select the most + appropriate idle state based on the expected expiry time of the next IRQ + (i.e. wake-up) that causes the CPU to return to the EXEC phase. + + An operating system scheduler may need to compute the shortest wake-up delay + for CPUs in the system by detecting how long will it take to get a CPU out + of an idle state, e.g.: + + wakeup-delay = exit-latency + max(entry-latency - (now - entry-timestamp), 0) + + In other words, the scheduler can make its scheduling decision by selecting + (e.g. waking-up) the CPU with the shortest wake-up delay. + The wake-up delay must take into account the entry latency if that period + has not expired. The abortable nature of the PREP period can be ignored + if it cannot be relied upon (e.g. the PREP deadline may occur much sooner than + the worst case since it depends on the CPU operating conditions, i.e. caches + state). + + An OS has to reliably probe the wakeup-latency since some devices can enforce + latency constraint guarantees to work properly, so the OS has to detect the + worst case wake-up latency it can incur if a CPU is allowed to enter an + idle state, and possibly to prevent that to guarantee reliable device + functioning. + + The min-residency time parameter deserves further explanation since it is + expressed in time units but must factor in energy consumption coefficients. + + The energy consumption of a cpu when it enters a power state can be roughly + characterised by the following graph: + + | + | + | + e | + n | /--- + e | /------ + r | /------ + g | /----- + y | /------ + | ---- + | /| + | / | + | / | + | / | + | / | + | / | + |/ | + -----|-------+---------------------------------- + 0| 1 time(ms) + + Graph 1: Energy vs time example + + The graph is split in two parts delimited by time 1ms on the X-axis. + The graph curve with X-axis values = { x | 0 < x < 1ms } has a steep slope + and denotes the energy costs incurred while entering and leaving the idle + state. + The graph curve in the area delimited by X-axis values = {x | x > 1ms } has + shallower slope and essentially represents the energy consumption of the idle + state. + + min-residency is defined for a given idle state as the minimum expected + residency time for a state (inclusive of preparation and entry) after + which choosing that state become the most energy efficient option. A good + way to visualise this, is by taking the same graph above and comparing some + states energy consumptions plots. + + For sake of simplicity, let's consider a system with two idle states IDLE1, + and IDLE2: + + | + | + | + | /-- IDLE1 + e | /--- + n | /---- + e | /--- + r | /-----/--------- IDLE2 + g | /-------/--------- + y | ------------ /---| + | / /---- | + | / /--- | + | / /---- | + | / /--- | + | --- | + | / | + | / | + |/ | time + ---/----------------------------+------------------------ + |IDLE1-energy < IDLE2-energy | IDLE2-energy < IDLE1-energy + | + IDLE2-min-residency + + Graph 2: idle states min-residency example + + In graph 2 above, that takes into account idle states entry/exit energy + costs, it is clear that if the idle state residency time (i.e. time till next + wake-up IRQ) is less than IDLE2-min-residency, IDLE1 is the better idle state + choice energywise. + + This is mainly down to the fact that IDLE1 entry/exit energy costs are lower + than IDLE2. + + However, the lower power consumption (i.e. shallower energy curve slope) of + idle state IDLE2 implies that after a suitable time, IDLE2 becomes more energy + efficient. + + The time at which IDLE2 becomes more energy efficient than IDLE1 (and other + shallower states in a system with multiple idle states) is defined + IDLE2-min-residency and corresponds to the time when energy consumption of + IDLE1 and IDLE2 states breaks even. + + The definitions provided in this section underpin the idle states + properties specification that is the subject of the following sections. + + =========================================== + 5 - idle-states node + =========================================== + + The processor idle states are defined within the idle-states node, which is + a direct child of the cpus node [1] and provides a container where the + processor idle states, defined as device tree nodes, are listed. + + On ARM systems, it is a container of processor idle states nodes. If the + system does not provide CPU power management capabilities, or the processor + just supports idle_standby, an idle-states node is not required. + + =========================================== + 6 - References + =========================================== + + [1] ARM Linux Kernel documentation - CPUs bindings + Documentation/devicetree/bindings/arm/cpus.yaml + + [2] ARM Linux Kernel documentation - PSCI bindings + Documentation/devicetree/bindings/arm/psci.yaml + + [3] ARM Server Base System Architecture (SBSA) + http://infocenter.arm.com/help/index.jsp + + [4] ARM Architecture Reference Manuals + http://infocenter.arm.com/help/index.jsp + + [5] ARM Linux Kernel documentation - Booting AArch64 Linux + Documentation/arm64/booting.rst + + [6] RISC-V Linux Kernel documentation - CPUs bindings + Documentation/devicetree/bindings/riscv/cpus.yaml + + [7] RISC-V Supervisor Binary Interface (SBI) + http://github.com/riscv/riscv-sbi-doc/riscv-sbi.adoc + +properties: + $nodename: + const: idle-states + + entry-method: + description: | + Usage and definition depend on ARM architecture version. + + On ARM v8 64-bit this property is required. + On ARM 32-bit systems this property is optional + + This assumes that the "enable-method" property is set to "psci" in the cpu + node[5] that is responsible for setting up CPU idle management in the OS + implementation. + const: psci + +patternProperties: + "^(cpu|cluster)-": + type: object + description: | + Each state node represents an idle state description and must be defined + as follows. + + The idle state entered by executing the wfi instruction (idle_standby + SBSA,[3][4]) is considered standard on all ARM and RISC-V platforms and + therefore must not be listed. + + In addition to the properties listed above, a state node may require + additional properties specific to the entry-method defined in the + idle-states node. Please refer to the entry-method bindings + documentation for properties definitions. + + properties: + compatible: + enum: + - arm,idle-state + - riscv,idle-state + + arm,psci-suspend-param: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + power_state parameter to pass to the ARM PSCI suspend call. + + Device tree nodes that require usage of PSCI CPU_SUSPEND function + (i.e. idle states node with entry-method property is set to "psci") + must specify this property. + + riscv,sbi-suspend-param: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + suspend_type parameter to pass to the RISC-V SBI HSM suspend call. + + This property is required in idle state nodes of device tree meant + for RISC-V systems. For more details on the suspend_type parameter + refer the SBI specifiation v0.3 (or higher) [7]. + + local-timer-stop: + description: + If present the CPU local timer control logic is + lost on state entry, otherwise it is retained. + type: boolean + + entry-latency-us: + description: + Worst case latency in microseconds required to enter the idle state. + + exit-latency-us: + description: + Worst case latency in microseconds required to exit the idle state. + The exit-latency-us duration may be guaranteed only after + entry-latency-us has passed. + + min-residency-us: + description: + Minimum residency duration in microseconds, inclusive of preparation + and entry, for this idle state to be considered worthwhile energy wise + (refer to section 2 of this document for a complete description). + + wakeup-latency-us: + description: | + Maximum delay between the signaling of a wake-up event and the CPU + being able to execute normal code again. If omitted, this is assumed + to be equal to: + + entry-latency-us + exit-latency-us + + It is important to supply this value on systems where the duration of + PREP phase (see diagram 1, section 2) is non-neglibigle. In such + systems entry-latency-us + exit-latency-us will exceed + wakeup-latency-us by this duration. + + idle-state-name: + $ref: /schemas/types.yaml#definitions/string + description: + A string used as a descriptive name for the idle state. + + additionalProperties: false + + required: + - compatible + - entry-latency-us + - exit-latency-us + - min-residency-us + +additionalProperties: false + +examples: + - | + + cpus { + #size-cells = <0>; + #address-cells = <2>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x0>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 + &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x1>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 + &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; + }; + + cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x100>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 + &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; + }; + + cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x101>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 + &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; + }; + + cpu@10000 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10000>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 + &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; + }; + + cpu@10001 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10001>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 + &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; + }; + + cpu@10100 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10100>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 + &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; + }; + + cpu@10101 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x10101>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_0_0 &CPU_SLEEP_0_0 + &CLUSTER_RETENTION_0 &CLUSTER_SLEEP_0>; + }; + + cpu@100000000 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1 0x0>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 + &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; + }; + + cpu@100000001 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1 0x1>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 + &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; + }; + + cpu@100000100 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1 0x100>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 + &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; + }; + + cpu@100000101 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1 0x101>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 + &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; + }; + + cpu@100010000 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1 0x10000>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 + &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; + }; + + cpu@100010001 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1 0x10001>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 + &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; + }; + + cpu@100010100 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1 0x10100>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 + &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; + }; + + cpu@100010101 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1 0x10101>; + enable-method = "psci"; + cpu-idle-states = <&CPU_RETENTION_1_0 &CPU_SLEEP_1_0 + &CLUSTER_RETENTION_1 &CLUSTER_SLEEP_1>; + }; + + idle-states { + entry-method = "psci"; + + CPU_RETENTION_0_0: cpu-retention-0-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; + entry-latency-us = <20>; + exit-latency-us = <40>; + min-residency-us = <80>; + }; + + CLUSTER_RETENTION_0: cluster-retention-0 { + compatible = "arm,idle-state"; + local-timer-stop; + arm,psci-suspend-param = <0x1010000>; + entry-latency-us = <50>; + exit-latency-us = <100>; + min-residency-us = <250>; + wakeup-latency-us = <130>; + }; + + CPU_SLEEP_0_0: cpu-sleep-0-0 { + compatible = "arm,idle-state"; + local-timer-stop; + arm,psci-suspend-param = <0x0010000>; + entry-latency-us = <250>; + exit-latency-us = <500>; + min-residency-us = <950>; + }; + + CLUSTER_SLEEP_0: cluster-sleep-0 { + compatible = "arm,idle-state"; + local-timer-stop; + arm,psci-suspend-param = <0x1010000>; + entry-latency-us = <600>; + exit-latency-us = <1100>; + min-residency-us = <2700>; + wakeup-latency-us = <1500>; + }; + + CPU_RETENTION_1_0: cpu-retention-1-0 { + compatible = "arm,idle-state"; + arm,psci-suspend-param = <0x0010000>; + entry-latency-us = <20>; + exit-latency-us = <40>; + min-residency-us = <90>; + }; + + CLUSTER_RETENTION_1: cluster-retention-1 { + compatible = "arm,idle-state"; + local-timer-stop; + arm,psci-suspend-param = <0x1010000>; + entry-latency-us = <50>; + exit-latency-us = <100>; + min-residency-us = <270>; + wakeup-latency-us = <100>; + }; + + CPU_SLEEP_1_0: cpu-sleep-1-0 { + compatible = "arm,idle-state"; + local-timer-stop; + arm,psci-suspend-param = <0x0010000>; + entry-latency-us = <70>; + exit-latency-us = <100>; + min-residency-us = <300>; + wakeup-latency-us = <150>; + }; + + CLUSTER_SLEEP_1: cluster-sleep-1 { + compatible = "arm,idle-state"; + local-timer-stop; + arm,psci-suspend-param = <0x1010000>; + entry-latency-us = <500>; + exit-latency-us = <1200>; + min-residency-us = <3500>; + wakeup-latency-us = <1300>; + }; + }; + }; + + - | + // Example 2 (ARM 32-bit, 8-cpu system, two clusters): + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x0>; + cpu-idle-states = <&cpu_sleep_0_0 &cluster_sleep_0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x1>; + cpu-idle-states = <&cpu_sleep_0_0 &cluster_sleep_0>; + }; + + cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x2>; + cpu-idle-states = <&cpu_sleep_0_0 &cluster_sleep_0>; + }; + + cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0x3>; + cpu-idle-states = <&cpu_sleep_0_0 &cluster_sleep_0>; + }; + + cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x100>; + cpu-idle-states = <&cpu_sleep_1_0 &cluster_sleep_1>; + }; + + cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x101>; + cpu-idle-states = <&cpu_sleep_1_0 &cluster_sleep_1>; + }; + + cpu@102 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x102>; + cpu-idle-states = <&cpu_sleep_1_0 &cluster_sleep_1>; + }; + + cpu@103 { + device_type = "cpu"; + compatible = "arm,cortex-a7"; + reg = <0x103>; + cpu-idle-states = <&cpu_sleep_1_0 &cluster_sleep_1>; + }; + + idle-states { + cpu_sleep_0_0: cpu-sleep-0-0 { + compatible = "arm,idle-state"; + local-timer-stop; + entry-latency-us = <200>; + exit-latency-us = <100>; + min-residency-us = <400>; + wakeup-latency-us = <250>; + }; + + cluster_sleep_0: cluster-sleep-0 { + compatible = "arm,idle-state"; + local-timer-stop; + entry-latency-us = <500>; + exit-latency-us = <1500>; + min-residency-us = <2500>; + wakeup-latency-us = <1700>; + }; + + cpu_sleep_1_0: cpu-sleep-1-0 { + compatible = "arm,idle-state"; + local-timer-stop; + entry-latency-us = <300>; + exit-latency-us = <500>; + min-residency-us = <900>; + wakeup-latency-us = <600>; + }; + + cluster_sleep_1: cluster-sleep-1 { + compatible = "arm,idle-state"; + local-timer-stop; + entry-latency-us = <800>; + exit-latency-us = <2000>; + min-residency-us = <6500>; + wakeup-latency-us = <2300>; + }; + }; + }; + + - | + // Example 3 (RISC-V 64-bit, 4-cpu systems, two clusters): + + cpus { + #size-cells = <0>; + #address-cells = <1>; + + cpu@0 { + device_type = "cpu"; + compatible = "riscv"; + reg = <0x0>; + riscv,isa = "rv64imafdc"; + mmu-type = "riscv,sv48"; + cpu-idle-states = <&CPU_RET_0_0 &CPU_NONRET_0_0 + &CLUSTER_RET_0 &CLUSTER_NONRET_0>; + + cpu_intc0: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "riscv"; + reg = <0x1>; + riscv,isa = "rv64imafdc"; + mmu-type = "riscv,sv48"; + cpu-idle-states = <&CPU_RET_0_0 &CPU_NONRET_0_0 + &CLUSTER_RET_0 &CLUSTER_NONRET_0>; + + cpu_intc1: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu@10 { + device_type = "cpu"; + compatible = "riscv"; + reg = <0x10>; + riscv,isa = "rv64imafdc"; + mmu-type = "riscv,sv48"; + cpu-idle-states = <&CPU_RET_1_0 &CPU_NONRET_1_0 + &CLUSTER_RET_1 &CLUSTER_NONRET_1>; + + cpu_intc10: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu@11 { + device_type = "cpu"; + compatible = "riscv"; + reg = <0x11>; + riscv,isa = "rv64imafdc"; + mmu-type = "riscv,sv48"; + cpu-idle-states = <&CPU_RET_1_0 &CPU_NONRET_1_0 + &CLUSTER_RET_1 &CLUSTER_NONRET_1>; + + cpu_intc11: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + idle-states { + CPU_RET_0_0: cpu-retentive-0-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x10000000>; + entry-latency-us = <20>; + exit-latency-us = <40>; + min-residency-us = <80>; + }; + + CPU_NONRET_0_0: cpu-nonretentive-0-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x90000000>; + entry-latency-us = <250>; + exit-latency-us = <500>; + min-residency-us = <950>; + }; + + CLUSTER_RET_0: cluster-retentive-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x11000000>; + local-timer-stop; + entry-latency-us = <50>; + exit-latency-us = <100>; + min-residency-us = <250>; + wakeup-latency-us = <130>; + }; + + CLUSTER_NONRET_0: cluster-nonretentive-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x91000000>; + local-timer-stop; + entry-latency-us = <600>; + exit-latency-us = <1100>; + min-residency-us = <2700>; + wakeup-latency-us = <1500>; + }; + + CPU_RET_1_0: cpu-retentive-1-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x10000010>; + entry-latency-us = <20>; + exit-latency-us = <40>; + min-residency-us = <80>; + }; + + CPU_NONRET_1_0: cpu-nonretentive-1-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x90000010>; + entry-latency-us = <250>; + exit-latency-us = <500>; + min-residency-us = <950>; + }; + + CLUSTER_RET_1: cluster-retentive-1 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x11000010>; + local-timer-stop; + entry-latency-us = <50>; + exit-latency-us = <100>; + min-residency-us = <250>; + wakeup-latency-us = <130>; + }; + + CLUSTER_NONRET_1: cluster-nonretentive-1 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x91000010>; + local-timer-stop; + entry-latency-us = <600>; + exit-latency-us = <1100>; + min-residency-us = <2700>; + wakeup-latency-us = <1500>; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index c6925e0b16e4..d2ac84955e17 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -87,6 +87,12 @@ properties: - compatible - interrupt-controller + cpu-idle-states: + $ref: '/schemas/types.yaml#/definitions/phandle-array' + description: | + List of phandles to idle state nodes supported + by this hart (see ./idle-states.yaml). + required: - riscv,isa - interrupt-controller diff --git a/MAINTAINERS b/MAINTAINERS index e492b0e3a4c5..a7407dd25454 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4614,6 +4614,20 @@ S: Supported F: drivers/cpuidle/cpuidle-psci.h F: drivers/cpuidle/cpuidle-psci-domain.c +CPUIDLE DRIVER - DT IDLE PM DOMAIN +M: Ulf Hansson +L: linux-pm@vger.kernel.org +S: Supported +F: drivers/cpuidle/dt_idle_genpd.c +F: drivers/cpuidle/dt_idle_genpd.h + +CPUIDLE DRIVER - RISC-V SBI +M: Anup Patel +L: linux-pm@vger.kernel.org +L: linux-riscv@lists.infradead.org +S: Maintained +F: drivers/cpuidle/cpuidle-riscv-sbi.c + CRAMFS FILESYSTEM M: Nicolas Pitre S: Maintained diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index ce6945da7682..168603c2c9da 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -44,7 +44,7 @@ config RISCV select CLONE_BACKWARDS select CLINT_TIMER if !MMU select COMMON_CLK - select CPU_PM if (SUSPEND || CPU_IDLE) + select CPU_PM if CPU_IDLE select COMPAT_BINFMT_ELF if BINFMT_ELF && COMPAT select EDAC_SUPPORT select DMA_DIRECT_REMAP @@ -551,5 +551,11 @@ config ARCH_SUSPEND_POSSIBLE endmenu +menu "CPU Power Management" + +source "drivers/cpuidle/Kconfig" + +endmenu + source "arch/riscv/kvm/Kconfig" source "drivers/firmware/Kconfig" diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 7718166b00dc..d008cf550c25 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -19,6 +19,9 @@ config SOC_VIRT select GOLDFISH select RTC_DRV_GOLDFISH if RTC_CLASS select SIFIVE_PLIC + select PM_GENERIC_DOMAINS if PM + select PM_GENERIC_DOMAINS_OF if PM && OF + select RISCV_SBI_CPUIDLE if CPU_IDLE help This enables support for QEMU Virt Machine. diff --git a/arch/riscv/boot/dts/thead/Makefile b/arch/riscv/boot/dts/thead/Makefile index b20b8a212b37..75a6eb36da0d 100644 --- a/arch/riscv/boot/dts/thead/Makefile +++ b/arch/riscv/boot/dts/thead/Makefile @@ -38,7 +38,7 @@ dtb-$(CONFIG_SOC_THEAD) += light-lpi4a.dtb light-lpi4a-ddr2G.dtb light-lpi4a-16g dtb-$(CONFIG_SOC_THEAD) += light-lpi4a-cluster.dtb light-lpi4a-cluster-16gb.dtb dtb-$(CONFIG_SOC_THEAD) += light-a-ref.dtb light-a-ref-dsi0.dtb light-a-ref-dsi0-hdmi.dtb dtb-$(CONFIG_SOC_THEAD) += light-b-ref.dtb -dtb-$(CONFIG_SOC_THEAD) += light-a-val-crash.dtb light-b-product-crash.dtb light-ant-ref-crash.dtb light-ant-discrete-crash.dtb +dtb-$(CONFIG_SOC_THEAD) += light-a-val-crash.dtb light-b-product-crash.dtb light-ant-ref-crash.dtb light-ant-discrete-crash.dtb light-lpi4a-crash.dtb light-lpi4a-camera-tuning.dtb light-lpi4a-hdmi.dtb dtb-$(CONFIG_SOC_THEAD) += light-b-power.dtb dtb-$(CONFIG_SOC_THEAD) += light-a-val-android.dtb diff --git a/arch/riscv/boot/dts/thead/fire-crash.dts b/arch/riscv/boot/dts/thead/fire-crash.dts index 730dd5c80b42..17e501a10cf8 100644 --- a/arch/riscv/boot/dts/thead/fire-crash.dts +++ b/arch/riscv/boot/dts/thead/fire-crash.dts @@ -306,15 +306,6 @@ pagesize = <32>; }; - codec: wm8960@1a { - #sound-dai-cells = <0>; - compatible = "wlf,wm8960"; - reg = <0x1a>; - wlf,shared-lrclk; - wlf,hp-cfg = <3 2 3>; - wlf,gpio-cfg = <1 3>; - }; - touch@5d { #gpio-cells = <2>; compatible = "goodix,gt911"; diff --git a/arch/riscv/boot/dts/thead/fire-emu.dts b/arch/riscv/boot/dts/thead/fire-emu.dts index 63fd37546ac5..f6dc93d752d0 100644 --- a/arch/riscv/boot/dts/thead/fire-emu.dts +++ b/arch/riscv/boot/dts/thead/fire-emu.dts @@ -311,15 +311,6 @@ pagesize = <32>; }; - codec: wm8960@1a { - #sound-dai-cells = <0>; - compatible = "wlf,wm8960"; - reg = <0x1a>; - wlf,shared-lrclk; - wlf,hp-cfg = <3 2 3>; - wlf,gpio-cfg = <1 3>; - }; - touch@5d { #gpio-cells = <2>; compatible = "goodix,gt911"; diff --git a/arch/riscv/boot/dts/thead/light-a-product.dts b/arch/riscv/boot/dts/thead/light-a-product.dts index ca550a804973..38dde44bfc0f 100644 --- a/arch/riscv/boot/dts/thead/light-a-product.dts +++ b/arch/riscv/boot/dts/thead/light-a-product.dts @@ -162,6 +162,14 @@ iopmp_dsp1: IOPMP_DSP1 { is_default_region; }; + + iopmp_audio0: IOPMP_AUDIO0 { + is_default_region; + }; + + iopmp_audio1: IOPMP_AUDIO1 { + is_default_region; + }; }; mbox_910t_client1: mbox_910t_client1 { @@ -517,15 +525,6 @@ pagesize = <32>; }; - codec: wm8960@1a { - #sound-dai-cells = <0>; - compatible = "wlf,wm8960"; - reg = <0x1a>; - wlf,shared-lrclk; - wlf,hp-cfg = <3 2 3>; - wlf,gpio-cfg = <1 3>; - }; - touch@5d { #gpio-cells = <2>; compatible = "goodix,gt911"; diff --git a/arch/riscv/boot/dts/thead/light-a-val-android.dts b/arch/riscv/boot/dts/thead/light-a-val-android.dts index ef36055468b0..e4504f3e5e0f 100644 --- a/arch/riscv/boot/dts/thead/light-a-val-android.dts +++ b/arch/riscv/boot/dts/thead/light-a-val-android.dts @@ -30,45 +30,54 @@ &lightsound { status = "okay"; - - simple-audio-card,widgets = - "Microphone", "Mic Jack", - "Speaker", "Speaker", - "Headphone", "Headphone Jack"; - simple-audio-card,routing = - "Headphone Jack", "HP_L", - "Headphone Jack", "HP_R", - "Speaker", "SPK_LP", - "Speaker", "SPK_LN", - "Speaker", "SPK_RP", - "Speaker", "SPK_RN", - "Mic Jack","MICB", - "LINPUT1", "Mic Jack", - "LINPUT3", "Mic Jack"; - - simple-audio-card,dai-link@0 { /* I2S - CODEC */ - reg = <0>; - format = "i2s"; - cpu { - sound-dai = <&light_i2s 0>; - }; - codec { - sound-dai = <&codec>; - }; - }; - simple-audio-card,dai-link@1 { /* I2S - HDMI */ + + simple-audio-card,dai-link@0 { /* I2S - AUDIO SYS CODEC 8156*/ + reg = <0>; + format = "i2s"; + cpu { + sound-dai = <&i2s0 0>; + }; + codec { + sound-dai = <&es8156_audio_codec>; + }; + }; + + simple-audio-card,dai-link@1 { /* I2S - AUDIO SYS CODEC 7210*/ reg = <1>; format = "i2s"; cpu { - sound-dai = <&light_i2s 1>; + sound-dai = <&i2s_8ch_sd2 2>; }; codec { - sound-dai = <&dummy_codec>; + sound-dai = <&es7210_audio_codec_adc0>; }; - }; + }; + + simple-audio-card,dai-link@2 { /* I2S - HDMI */ + reg = <2>; + format = "i2s"; + cpu { + sound-dai = <&light_i2s 1>; + }; + codec { + sound-dai = <&dummy_codec>; + }; + }; }; &light_i2s { + status = "okay"; +}; + +&i2s0 { + status = "okay"; +}; + +&i2s_8ch_sd2 { + status = "okay"; +}; + +&es7210_audio_codec_adc0 { status = "okay"; }; diff --git a/arch/riscv/boot/dts/thead/light-a-val-sec.dts b/arch/riscv/boot/dts/thead/light-a-val-sec.dts index 8417982584bf..19cff7bdeee6 100644 --- a/arch/riscv/boot/dts/thead/light-a-val-sec.dts +++ b/arch/riscv/boot/dts/thead/light-a-val-sec.dts @@ -15,3 +15,6 @@ status = "disabled"; }; +&eip_28 { + status = "disabled"; +}; diff --git a/arch/riscv/boot/dts/thead/light-a-val.dts b/arch/riscv/boot/dts/thead/light-a-val.dts index d7fa8033becf..65c3e46e66fa 100644 --- a/arch/riscv/boot/dts/thead/light-a-val.dts +++ b/arch/riscv/boot/dts/thead/light-a-val.dts @@ -16,7 +16,7 @@ chosen { bootargs = "console=ttyS0,115200 crashkernel=256M-:128M earlycon clk_ignore_unused sram=0xffe0000000,0x180000"; - stdout-path = "serial0:115200n8"; + stdout-path = "serial0"; }; leds { @@ -165,6 +165,14 @@ iopmp_dsp1: IOPMP_DSP1 { is_default_region; }; + + iopmp_audio0: IOPMP_AUDIO0 { + is_default_region; + }; + + iopmp_audio1: IOPMP_AUDIO1 { + is_default_region; + }; }; mbox_910t_client1: mbox_910t_client1 { @@ -322,12 +330,14 @@ status = "disabled"; key-volumedown { label = "Volume Down Key"; + wakeup-source; linux,code = ; debounce-interval = <2>; gpios = <&ao_gpio_porta 4 GPIO_ACTIVE_LOW>; }; key-volumeup { label = "Volume Up Key"; + wakeup-source; linux,code = ; debounce-interval = <2>; gpios = <&ao_gpio_porta 5 GPIO_ACTIVE_LOW>; @@ -564,22 +574,14 @@ &i2c0 { clock-frequency = <400000>; status = "okay"; - + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c0>; eeprom@50 { compatible = "atmel,24c32"; reg = <0x50>; pagesize = <32>; }; - codec: wm8960@1a { - #sound-dai-cells = <0>; - compatible = "wlf,wm8960"; - reg = <0x1a>; - wlf,shared-lrclk; - wlf,hp-cfg = <3 2 3>; - wlf,gpio-cfg = <1 3>; - }; - touch@5d { #gpio-cells = <2>; compatible = "goodix,gt911"; @@ -671,6 +673,8 @@ &i2c1 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; touch1@5d { #gpio-cells = <2>; compatible = "goodix,gt911"; @@ -689,9 +693,9 @@ num-cs = <1>; cs-gpios = <&gpio2_porta 15 0>; // GPIO_ACTIVE_HIGH: 0 rx-sample-delay-ns = <10>; - status = "okay"; spi_norflash@0 { + status = "okay"; #address-cells = <1>; #size-cells = <1>; compatible = "winbond,w25q64jwm", "jedec,spi-nor"; @@ -701,6 +705,7 @@ }; spidev@1 { + status = "disable"; compatible = "spidev"; #address-cells = <0x1>; #size-cells = <0x1>; @@ -711,13 +716,30 @@ &uart0 { clock-frequency = <100000000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart0>; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3>; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; }; &qspi0 { num-cs = <1>; cs-gpios = <&gpio2_porta 3 0>; - rx-sample-dly = <4>; - status = "disabled"; + rx-sample-dly = <5>; + status = "okay"; spi-flash@0 { #address-cells = <1>; @@ -738,7 +760,8 @@ &qspi1 { num-cs = <1>; cs-gpios = <&gpio0_porta 1 0>; - status = "disabled"; + rx-sample-dly = <5>; + status = "okay"; spi-flash@0 { #address-cells = <1>; @@ -761,6 +784,8 @@ rx-clk-delay = <0x00>; /* for RGMII */ tx-clk-delay = <0x00>; /* for RGMII */ phy-handle = <&phy_88E1111_0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gmac0>; status = "okay"; mdio0 { @@ -783,6 +808,8 @@ rx-clk-delay = <0x00>; /* for RGMII */ tx-clk-delay = <0x00>; /* for RGMII */ phy-handle = <&phy_88E1111_1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gmac1>; status = "okay"; }; @@ -804,6 +831,8 @@ bus-width = <4>; pull_up; wprtn_ignore; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdio0>; status = "okay"; }; @@ -832,17 +861,8 @@ */ pinctrl_uart0: uart0grp { thead,pins = < - FM_UART0_TXD 0x0 0x72 - FM_UART0_RXD 0x0 0x72 - >; - }; - - pinctrl_spi0: spi0grp { - thead,pins = < - FM_SPI_CSN 0x3 0x20a - FM_SPI_SCLK 0x0 0x20a - FM_SPI_MISO 0x0 0x23a - FM_SPI_MOSI 0x0 0x23a + FM_UART0_TXD 0x0 0x234 + FM_UART0_RXD 0x0 0x234 >; }; @@ -870,37 +890,96 @@ >; }; + pinctrl_i2c2: i2c2grp { + thead,pins = < + FM_I2C2_SCL 0x0 0x204 + FM_I2C2_SDA 0x0 0x204 + >; + }; + + pinctrl_i2c3: i2c3grp { + thead,pins = < + FM_I2C3_SCL 0x0 0x204 + FM_I2C3_SDA 0x0 0x204 + >; + }; + + pinctrl_spi0: spi0grp { + thead,pins = < + FM_SPI_CSN 0x3 0x20a + FM_SPI_SCLK 0x0 0x20a + FM_SPI_MISO 0x0 0x23a + FM_SPI_MOSI 0x0 0x23a + >; + }; + + pinctrl_gmac1: gmac1grp { + thead,pins = < + FM_GPIO2_18 0x1 0x20f /* GMAC1_TX_CLK */ + FM_GPIO2_19 0x1 0x20f /* GMAC1_RX_CLK */ + FM_GPIO2_20 0x1 0x20f /* GMAC1_TXEN */ + FM_GPIO2_21 0x1 0x20f /* GMAC1_TXD0 */ + FM_GPIO2_22 0x1 0x20f /* GMAC1_TXD1 */ + FM_GPIO2_23 0x1 0x20f /* GMAC1_TXD2 */ + FM_GPIO2_24 0x1 0x20f /* GMAC1_TXD3 */ + FM_GPIO2_25 0x1 0x20f /* GMAC1_RXDV */ + FM_GPIO2_30 0x1 0x20f /* GMAC1_RXD0 */ + FM_GPIO2_31 0x1 0x20f /* GMAC1_RXD1 */ + FM_GPIO3_0 0x1 0x20f /* GMAC1_RXD2 */ + FM_GPIO3_1 0x1 0x20f /* GMAC1_RXD3 */ + >; + }; + + pinctrl_sdio0: sdio0grp { + thead,pins = < + FM_SDIO0_DETN 0x0 0x208 + >; + }; + pinctrl_pwm: pwmgrp { thead,pins = < FM_GPIO3_2 0x1 0x208 /* pwm0 */ FM_GPIO3_3 0x1 0x208 /* pwm1 */ >; }; - }; -}; -&padctrl1_apsys { /* left-pinctrl */ - light-evb-padctrl1 { - /* - * Pin Configuration Node: - * Format: - */ - pinctrl_uart3: uart3grp { + pinctrl_hdmi: hdmigrp { thead,pins = < - FM_UART3_TXD 0x0 0x72 - FM_UART3_RXD 0x0 0x72 + FM_HDMI_SCL 0x0 0x208 + FM_HDMI_SDA 0x0 0x208 + FM_HDMI_CEC 0x0 0x208 >; }; - pinctrl_uart4: uart4grp { + pinctrl_gmac0: gmac0grp { thead,pins = < - FM_UART4_TXD 0x0 0x72 - FM_UART4_RXD 0x0 0x72 - FM_UART4_CTSN 0x0 0x72 - FM_UART4_RTSN 0x0 0x72 + FM_GMAC0_TX_CLK 0x0 0x20f /* GMAC0_TX_CLK */ + FM_GMAC0_RX_CLK 0x0 0x20f /* GMAC0_RX_CLK */ + FM_GMAC0_TXEN 0x0 0x20f /* GMAC0_TXEN */ + FM_GMAC0_TXD0 0x0 0x20f /* GMAC0_TXD0 */ + FM_GMAC0_TXD1 0x0 0x20f /* GMAC0_TXD1 */ + FM_GMAC0_TXD2 0x0 0x20f /* GMAC0_TXD2 */ + FM_GMAC0_TXD3 0x0 0x20f /* GMAC0_TXD3 */ + FM_GMAC0_RXDV 0x0 0x20f /* GMAC0_RXDV */ + FM_GMAC0_RXD0 0x0 0x20f /* GMAC0_RXD0 */ + FM_GMAC0_RXD1 0x0 0x20f /* GMAC0_RXD1 */ + FM_GMAC0_RXD2 0x0 0x20f /* GMAC0_RXD2 */ + FM_GMAC0_RXD3 0x0 0x20f /* GMAC0_RXD3 */ + FM_GMAC0_MDC 0x0 0x208 /* GMAC0_MDC */ + FM_GMAC0_MDIO 0x0 0x208 /* GMAC0_MDIO */ + FM_GMAC0_COL 0x3 0x232 /* PHY0_nRST */ + FM_GMAC0_CRS 0x3 0x232 /* PHY0_nINT */ >; }; + }; +}; +&padctrl1_apsys { /* left-pinctrl */ + light-evb-padctrl1 { + /* + * Pin Configuration Node: + * Format: + */ pinctrl_qspi1: qspi1grp { thead,pins = < FM_QSPI1_SCLK 0x0 0x20a @@ -912,7 +991,6 @@ >; }; - pinctrl_iso7816: iso7816grp { thead,pins = < FM_QSPI1_SCLK 0x1 0x208 @@ -923,6 +1001,51 @@ >; }; + pinctrl_i2c0: i2c0grp { + thead,pins = < + FM_I2C0_SCL 0x0 0x204 + FM_I2C0_SDA 0x0 0x204 + >; + }; + + pinctrl_i2c1: i2c1grp { + thead,pins = < + FM_I2C1_SCL 0x0 0x204 + FM_I2C1_SDA 0x0 0x204 + >; + }; + + pinctrl_uart1: uart1grp { + thead,pins = < + FM_UART1_TXD 0x0 0x234 + FM_UART1_RXD 0x0 0x234 + >; + }; + + pinctrl_uart4: uart4grp { + thead,pins = < + FM_UART4_TXD 0x0 0x208 + FM_UART4_RXD 0x0 0x208 + FM_UART4_CTSN 0x0 0x208 + FM_UART4_RTSN 0x0 0x208 + >; + }; + + pinctrl_uart3: uart3grp { + thead,pins = < + FM_UART3_TXD 0x1 0x202 + FM_UART3_RXD 0x1 0x202 + FM_GPIO0_20 0x2 0x202 /* UART3_IR_OUT */ + FM_GPIO0_21 0x2 0x202 /* UART3_IR_IN */ + >; + }; + + pinctrl_i2c4: i2c4grp { + thead,pins = < + FM_GPIO0_18 0x1 0x204 /* I2C4_SCL */ + FM_GPIO0_19 0x1 0x204 /* I2C4_SDA */ + >; + }; }; }; @@ -1086,6 +1209,8 @@ &i2c2 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; eeprom@50 { compatible = "atmel,24c32"; reg = <0x50>; @@ -1096,6 +1221,8 @@ &i2c3 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; eeprom@50 { compatible = "atmel,24c32"; reg = <0x50>; @@ -1106,6 +1233,8 @@ &i2c4 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c4>; eeprom@50 { compatible = "atmel,24c32"; reg = <0x50>; @@ -1328,7 +1457,7 @@ DOVDD18_RGB-supply = <&soc_dovdd18_rgb_reg>; DVDD12_RGB-supply = <&soc_dvdd12_rgb_reg>; AVDD28_RGB-supply = <&soc_avdd28_rgb_reg>; - i2c_reg_width = /bits/ 8 <2>; + i2c_reg_width = /bits/ 8 <2>; i2c_data_width = /bits/ 8 <1>; i2c_addr = /bits/ 8 <0x1a>; i2c_bus = /bits/ 8 <3>; @@ -1336,6 +1465,7 @@ }; &video0{ + status = "okay"; vi_mem_pool_region = <2>; // vi_mem: framebuffer, region[2] channel0 { sensor0 { @@ -1422,6 +1552,7 @@ &video1{ + status = "okay"; vi_mem_pool_region = <2>; // vi_mem: framebuffer, region[2] channel0 { sensor0 { @@ -1525,6 +1656,7 @@ }; &video2{ + status = "okay"; vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] channel0 { sensor0 { @@ -1617,6 +1749,7 @@ }; &video3{ + status = "okay"; vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] channel0 { sensor0 { @@ -1720,6 +1853,7 @@ }; &video4{ + status = "okay"; vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] channel0 { sensor0 { @@ -1871,6 +2005,7 @@ }; &video5{ + status = "okay"; vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] channel0 { sensor0 { @@ -2040,6 +2175,7 @@ }; &video6{ + status = "okay"; vi_mem_pool_region = <1>; // vi_mem: framebuffer, region[1] channel0 { sensor0 { @@ -2080,6 +2216,7 @@ }; &video7{ + status = "okay"; channel0 { sensor0 { subdev_name = "vivcam"; @@ -2249,6 +2386,7 @@ &video8{ + status = "okay"; vi_mem_pool_region = <1>; // vi_mem: framebuffer, region[1] channel0 { sensor0 { @@ -2280,6 +2418,7 @@ }; &video9{ + status = "okay"; channel0 { sensor0 { subdev_name = "vivcam"; @@ -2301,6 +2440,7 @@ &video10{ + status = "okay"; channel0 { sensor0 { subdev_name = "vivcam"; @@ -2322,6 +2462,7 @@ }; &video11{ + status = "okay"; channel0 { channel_id = <0>; status = "okay"; @@ -2348,6 +2489,7 @@ }; &video12{ // TUNINGTOOL + status = "okay"; channel0 { // CSI2 sensor0 { subdev_name = "vivcam"; @@ -2368,6 +2510,35 @@ }; }; +&video15{ + status = "okay"; + vi_mem_pool_region = <0>; + channel0 { + channel_id = <0>; + status = "okay"; + sensor0 { + subdev_name = "vivcam"; + idx = <0>; //<0>=vivcam0 :2310 + csi_idx = <0>; //<0>=CSI2 + flash_led_idx = <0>; + mode_idx = <1>; + path_type = "SENSOR_1920X1088_26FPS_RAW12_LINER"; + }; + sensor1 { + subdev_name = "vivcam"; + idx = <7>; //imx334 + csi_idx = <0>; //<0>=CSI2 + mode_idx = <0>; + path_type = "SENSOR_3840x2180_RAW12_LINER"; + }; + dma { + subdev_name = "vipre"; + idx = <0>; + path_type = "VIPRE_CSI0_DDR"; + }; + }; +}; + &trng { status = "disabled"; }; @@ -2513,3 +2684,8 @@ >; }; }; + +&hdmi_tx { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi>; +}; diff --git a/arch/riscv/boot/dts/thead/light-ant-ref.dts b/arch/riscv/boot/dts/thead/light-ant-ref.dts index 8ac65fecad13..49321fbe55ba 100644 --- a/arch/riscv/boot/dts/thead/light-ant-ref.dts +++ b/arch/riscv/boot/dts/thead/light-ant-ref.dts @@ -166,6 +166,14 @@ iopmp_dsp1: IOPMP_DSP1 { is_default_region; }; + + iopmp_audio0: IOPMP_AUDIO0 { + is_default_region; + }; + + iopmp_audio1: IOPMP_AUDIO1 { + is_default_region; + }; }; mbox_910t_client1: mbox_910t_client1 { @@ -2271,6 +2279,9 @@ &hdmi_tx { status = "okay"; + max_width = /bits/ 16 <1280>; + max_height = /bits/ 16 <720>; + port@0 { /* input */ hdmi_tx_in: endpoint { diff --git a/arch/riscv/boot/dts/thead/light-b-audio-hdmi.dts b/arch/riscv/boot/dts/thead/light-b-audio-hdmi.dts index f4e4f958ae82..bacc501154e1 100644 --- a/arch/riscv/boot/dts/thead/light-b-audio-hdmi.dts +++ b/arch/riscv/boot/dts/thead/light-b-audio-hdmi.dts @@ -166,6 +166,14 @@ iopmp_dsp1: IOPMP_DSP1 { is_default_region; }; + + iopmp_audio0: IOPMP_AUDIO0 { + is_default_region; + }; + + iopmp_audio1: IOPMP_AUDIO1 { + is_default_region; + }; }; mbox_910t_client1: mbox_910t_client1 { @@ -258,12 +266,14 @@ pinctrl-names = "default"; key-volumedown { label = "Volume Down Key"; + wakeup-source; linux,code = ; debounce-interval = <1>; gpios = <&ao_gpio_porta 11 0x1>; }; key-volumeup { label = "Volume Up Key"; + wakeup-source; linux,code = ; debounce-interval = <1>; gpios = <&ao_gpio_porta 10 0x1>; diff --git a/arch/riscv/boot/dts/thead/light-b-product.dts b/arch/riscv/boot/dts/thead/light-b-product.dts index 3b0d92ec7ea5..2c74164e9f83 100644 --- a/arch/riscv/boot/dts/thead/light-b-product.dts +++ b/arch/riscv/boot/dts/thead/light-b-product.dts @@ -166,6 +166,14 @@ iopmp_dsp1: IOPMP_DSP1 { is_default_region; }; + + iopmp_audio0: IOPMP_AUDIO0 { + is_default_region; + }; + + iopmp_audio1: IOPMP_AUDIO1 { + is_default_region; + }; }; mbox_910t_client1: mbox_910t_client1 { @@ -239,6 +247,7 @@ ref-clock-frequency = <24000000>; keep_wifi_power_on; pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wifi>; wifi_chip_type = "rtl8723ds"; WIFI,poweren_gpio = <&gpio2_porta 29 0>; WIFI,reset_n = <&gpio2_porta 24 0>; @@ -247,7 +256,8 @@ wcn_bt: wireless-bluetooth { compatible = "bluetooth-platdata"; - pinctrl-names = "default", "rts_gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_bt>; BT,power_gpio = <&gpio2_porta 25 0>; status = "okay"; }; @@ -258,12 +268,14 @@ pinctrl-names = "default"; key-volumedown { label = "Volume Down Key"; + wakeup-source; linux,code = ; debounce-interval = <1>; gpios = <&ao_gpio_porta 11 0x1>; }; key-volumeup { label = "Volume Up Key"; + wakeup-source; linux,code = ; debounce-interval = <1>; gpios = <&ao_gpio_porta 10 0x1>; @@ -699,6 +711,23 @@ &uart0 { clock-frequency = <100000000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart0>; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3>; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; }; &qspi0 { @@ -744,6 +773,8 @@ tx-clk-delay = <0x00>; /* for RGMII */ phy-handle = <&phy_88E1111_0>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gmac0>; mdio0 { #address-cells = <1>; @@ -787,6 +818,8 @@ pull_up; wprtn_ignore; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdio0>; }; &sdhci1 { @@ -814,17 +847,8 @@ */ pinctrl_uart0: uart0grp { thead,pins = < - FM_UART0_TXD 0x0 0x72 - FM_UART0_RXD 0x0 0x72 - >; - }; - - pinctrl_spi0: spi0grp { - thead,pins = < - FM_SPI_CSN 0x3 0x20a - FM_SPI_SCLK 0x0 0x20a - FM_SPI_MISO 0x0 0x23a - FM_SPI_MOSI 0x0 0x23a + FM_UART0_TXD 0x0 0x234 + FM_UART0_RXD 0x0 0x234 >; }; @@ -852,36 +876,92 @@ >; }; + pinctrl_i2c2: i2c2grp { + thead,pins = < + FM_I2C2_SCL 0x0 0x204 + FM_I2C2_SDA 0x0 0x204 + >; + }; + + pinctrl_i2c3: i2c3grp { + thead,pins = < + FM_I2C3_SCL 0x0 0x204 + FM_I2C3_SDA 0x0 0x204 + >; + }; + + pinctrl_spi0: spi0grp { + thead,pins = < + FM_SPI_CSN 0x3 0x20a + FM_SPI_SCLK 0x0 0x20a + FM_SPI_MISO 0x0 0x23a + FM_SPI_MOSI 0x0 0x23a + >; + }; + + pinctrl_wifi: wifi_grp { + thead,pins = < + FM_GPIO2_22 0x0 0x202 + FM_GPIO2_24 0x0 0x202 + >; + }; + + pinctrl_bt: bt_grp { + thead,pins = < + FM_GPIO2_23 0x0 0x202 + FM_GPIO2_25 0x0 0x202 + >; + }; + + pinctrl_sdio0: sdio0grp { + thead,pins = < + FM_SDIO0_DETN 0x0 0x208 + >; + }; + pinctrl_pwm: pwmgrp { thead,pins = < - FM_GPIO3_2 0x1 0x208 /* pwm0 */ + FM_GPIO3_2 0x1 0x20f /* pwm0 */ >; }; - }; -}; -&padctrl1_apsys { /* left-pinctrl */ - light-evb-padctrl1 { - /* - * Pin Configuration Node: - * Format: - */ - pinctrl_uart3: uart3grp { + pinctrl_hdmi: hdmigrp { thead,pins = < - FM_UART3_TXD 0x0 0x72 - FM_UART3_RXD 0x0 0x72 + FM_HDMI_SCL 0x0 0x208 + FM_HDMI_SDA 0x0 0x208 + FM_HDMI_CEC 0x0 0x208 >; }; - pinctrl_uart4: uart4grp { + pinctrl_gmac0: gmac0grp { thead,pins = < - FM_UART4_TXD 0x0 0x72 - FM_UART4_RXD 0x0 0x72 - FM_UART4_CTSN 0x0 0x72 - FM_UART4_RTSN 0x0 0x72 + FM_GMAC0_TX_CLK 0x0 0x20f /* GMAC0_TX_CLK */ + FM_GMAC0_RX_CLK 0x0 0x20f /* GMAC0_RX_CLK */ + FM_GMAC0_TXEN 0x0 0x20f /* GMAC0_TXEN */ + FM_GMAC0_TXD0 0x0 0x20f /* GMAC0_TXD0 */ + FM_GMAC0_TXD1 0x0 0x20f /* GMAC0_TXD1 */ + FM_GMAC0_TXD2 0x0 0x20f /* GMAC0_TXD2 */ + FM_GMAC0_TXD3 0x0 0x20f /* GMAC0_TXD3 */ + FM_GMAC0_RXDV 0x0 0x20f /* GMAC0_RXDV */ + FM_GMAC0_RXD0 0x0 0x20f /* GMAC0_RXD0 */ + FM_GMAC0_RXD1 0x0 0x20f /* GMAC0_RXD1 */ + FM_GMAC0_RXD2 0x0 0x20f /* GMAC0_RXD2 */ + FM_GMAC0_RXD3 0x0 0x20f /* GMAC0_RXD3 */ + FM_GMAC0_MDC 0x0 0x208 /* GMAC0_MDC */ + FM_GMAC0_MDIO 0x0 0x208 /* GMAC0_MDIO */ + FM_GMAC0_COL 0x3 0x232 /* PHY0_nRST */ + FM_GMAC0_CRS 0x3 0x232 /* PHY0_nINT */ >; }; + }; +}; +&padctrl1_apsys { /* left-pinctrl */ + light-evb-padctrl1 { + /* + * Pin Configuration Node: + * Format: + */ pinctrl_qspi1: qspi1grp { thead,pins = < FM_QSPI1_SCLK 0x0 0x20a @@ -893,7 +973,6 @@ >; }; - pinctrl_iso7816: iso7816grp { thead,pins = < FM_QSPI1_SCLK 0x1 0x208 @@ -904,6 +983,49 @@ >; }; + pinctrl_i2c0: i2c0grp { + thead,pins = < + FM_I2C0_SCL 0x0 0x204 + FM_I2C0_SDA 0x0 0x204 + >; + }; + + pinctrl_i2c1: i2c1grp { + thead,pins = < + FM_I2C1_SCL 0x0 0x204 + FM_I2C1_SDA 0x0 0x204 + >; + }; + + pinctrl_uart1: uart1grp { + thead,pins = < + FM_UART1_TXD 0x0 0x234 + FM_UART1_RXD 0x0 0x234 + >; + }; + + pinctrl_uart4: uart4grp { + thead,pins = < + FM_UART4_TXD 0x0 0x208 + FM_UART4_RXD 0x0 0x208 + FM_UART4_CTSN 0x0 0x208 + FM_UART4_RTSN 0x0 0x208 + >; + }; + + pinctrl_uart3: uart3grp { + thead,pins = < + FM_UART3_TXD 0x1 0x202 + FM_UART3_RXD 0x1 0x202 + >; + }; + + pinctrl_i2c4: i2c4grp { + thead,pins = < + FM_GPIO0_18 0x1 0x204 /* I2C4_SCL */ + FM_GPIO0_19 0x1 0x204 /* I2C4_SDA */ + >; + }; }; }; @@ -1014,26 +1136,36 @@ &i2c0 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c0>; }; &i2c1 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; }; &i2c2 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; }; &i2c3 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; }; &i2c4 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c4>; }; &isp0 { @@ -1240,6 +1372,7 @@ }; &video0{ + status = "okay"; vi_mem_pool_region = <2>; // vi_mem: framebuffer, region[2] channel0 { sensor0 { @@ -1326,6 +1459,7 @@ &video1{ + status = "okay"; vi_mem_pool_region = <2>; // vi_mem: framebuffer, region[2] channel0 { sensor0 { @@ -1429,6 +1563,7 @@ }; &video2{ + status = "okay"; vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] channel0 { sensor0 { @@ -1445,20 +1580,13 @@ mode_idx = <0>; path_type = "SENSOR_1600x1200_RAW10_LINER"; }; - sensor2 { - subdev_name = "vivcam"; - idx = <7>; //imx334 - csi_idx = <0>; //<0>=CSI2 - mode_idx = <0>; - path_type = "SENSOR_3840x2180_RAW12_LINER"; - }; isp { subdev_name = "isp"; idx = <1>; path_type = "ISP_MI_PATH_MP"; output { - max_width = <3840>; - max_height = <2180>; + max_width = <1920>; + max_height = <1088>; bit_per_pixel = <16>; frame_count = <3>; }; @@ -1521,6 +1649,7 @@ }; &video3{ + status = "okay"; vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] channel0 { sensor0 { @@ -1624,6 +1753,7 @@ }; &video4{ + status = "okay"; vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] channel0 { sensor0 { @@ -1775,6 +1905,7 @@ }; &video5{ + status = "okay"; vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] channel0 { sensor0 { @@ -1944,6 +2075,7 @@ }; &video6{ + status = "okay"; vi_mem_pool_region = <1>; // vi_mem: framebuffer, region[1] channel0 { sensor0 { @@ -1985,6 +2117,7 @@ }; &video7{ + status = "okay"; channel0 { sensor0 { subdev_name = "vivcam"; @@ -2154,6 +2287,7 @@ &video8{ + status = "okay"; vi_mem_pool_region = <1>; // vi_mem: framebuffer, region[1] channel0 { sensor0 { @@ -2185,6 +2319,7 @@ }; &video9{ + status = "okay"; channel0 { sensor0 { subdev_name = "vivcam"; @@ -2206,6 +2341,7 @@ &video10{ // TUNINGTOOL + status = "okay"; channel0 { sensor0 { subdev_name = "vivcam"; @@ -2227,6 +2363,7 @@ }; &video11{ + status = "okay"; channel0 { channel_id = <0>; status = "okay"; @@ -2253,6 +2390,7 @@ }; &video12{ // TUNINGTOOL + status = "okay"; channel0 { // CSI2 sensor0 { subdev_name = "vivcam"; @@ -2274,6 +2412,7 @@ }; &video14{ + status = "okay"; vi_mem_pool_region = <2>; // vi_mem: framebuffer, region[0] status = "okay"; channel0 { @@ -2440,6 +2579,8 @@ &hdmi_tx { status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi>; port@0 { /* input */ diff --git a/arch/riscv/boot/dts/thead/light-beagle-ref.dts b/arch/riscv/boot/dts/thead/light-beagle-ref.dts index a22548e2867d..c51a610a6a3b 100644 --- a/arch/riscv/boot/dts/thead/light-beagle-ref.dts +++ b/arch/riscv/boot/dts/thead/light-beagle-ref.dts @@ -194,6 +194,14 @@ iopmp_dsp1: IOPMP_DSP1 { is_default_region; }; + + iopmp_audio0: IOPMP_AUDIO0 { + is_default_region; + }; + + iopmp_audio1: IOPMP_AUDIO1 { + is_default_region; + }; }; mbox_910t_client1: mbox_910t_client1 { diff --git a/arch/riscv/boot/dts/thead/light-crash.dts b/arch/riscv/boot/dts/thead/light-crash.dts index 03aa9967b05c..d94f63911983 100644 --- a/arch/riscv/boot/dts/thead/light-crash.dts +++ b/arch/riscv/boot/dts/thead/light-crash.dts @@ -163,6 +163,14 @@ iopmp_dsp1: IOPMP_DSP1 { is_default_region; }; + + iopmp_audio0: IOPMP_AUDIO0 { + is_default_region; + }; + + iopmp_audio1: IOPMP_AUDIO1 { + is_default_region; + }; }; mbox_910t_client1: mbox_910t_client1 { diff --git a/arch/riscv/boot/dts/thead/light-lpi4a-camera-tuning.dts b/arch/riscv/boot/dts/thead/light-lpi4a-camera-tuning.dts new file mode 100644 index 000000000000..317295b7b935 --- /dev/null +++ b/arch/riscv/boot/dts/thead/light-lpi4a-camera-tuning.dts @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Alibaba Group Holding Limited. + */ + +/dts-v1/; + +#include "light-lpi4a.dts" + + + +&video10{ // TUNINGTOOL + status = "okay"; + channel0 { + sensor1 { + subdev_name = "vivcam"; + idx = <3>; + csi_idx = <0>; + mode_idx = <1>; // 0=640 480 1=2592x1944 + path_type = "SENSOR_2592x1944_LINER"; + }; + dma { + path_type = "VIPRE_CSI0_ISP0"; + }; + }; +}; + +&video15{ + status = "okay"; + vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] + channel0 { + status = "okay"; + sensor0 { + subdev_name = "vivcam"; + idx = <0>; + csi_idx = <0>; + mode_idx = <0>; + path_type = "SENSOR_VGA_RAW12_LINER"; + }; + sensor1 { + subdev_name = "vivcam"; + idx = <3>; + csi_idx = <0>; + mode_idx = <1>; + path_type = "SENSOR_2592x1944_LINER"; + }; + dma { + subdev_name = "vipre"; + idx = <0>; + path_type = "VIPRE_CSI0_DDR"; + }; + }; +}; diff --git a/arch/riscv/boot/dts/thead/light-lpi4a-crash.dts b/arch/riscv/boot/dts/thead/light-lpi4a-crash.dts new file mode 100644 index 000000000000..117fe33d2820 --- /dev/null +++ b/arch/riscv/boot/dts/thead/light-lpi4a-crash.dts @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Alibaba Group Holding Limited. + */ + +#include "light-crash.dts" + +&aon { + aon_reg_dialog: light-dialog-reg { + compatible = "thead,light-dialog-pmic-ant"; + status = "okay"; + + dvdd_cpu_reg: appcpu_dvdd { + regulator-name = "appcpu_dvdd"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1570000>; + regulator-boot-on; + regulator-always-on; + }; + + dvddm_cpu_reg: appcpu_dvddm { + regulator-name = "appcpu_dvddm"; + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1570000>; + regulator-boot-on; + regulator-always-on; + }; + }; +}; + +&cpus { + c910_0: cpu@0 { + operating-points = < + /* kHz uV */ + 300000 600000 + 800000 700000 + 1500000 800000 + 1848000 1000000 + >; + light,dvddm-operating-points = < + /* kHz uV */ + 300000 800000 + 800000 800000 + 1500000 800000 + 1848000 1000000 + >; + }; + c910_1: cpu@1 { + operating-points = < + /* kHz uV */ + 300000 600000 + 800000 700000 + 1500000 800000 + 1848000 1000000 + >; + light,dvddm-operating-points = < + /* kHz uV */ + 300000 800000 + 800000 800000 + 1500000 800000 + 1848000 1000000 + >; + }; + c910_2: cpu@2 { + + operating-points = < + /* kHz uV */ + 300000 600000 + 800000 700000 + 1500000 800000 + 1848000 1000000 + >; + light,dvddm-operating-points = < + /* kHz uV */ + 300000 800000 + 800000 800000 + 1500000 800000 + 1848000 1000000 + >; + }; + c910_3: cpu@3 { + + operating-points = < + /* kHz uV */ + 300000 600000 + 800000 700000 + 1500000 800000 + 1848000 1000000 + >; + light,dvddm-operating-points = < + /* kHz uV */ + 300000 800000 + 800000 800000 + 1500000 800000 + 1848000 1000000 + >; + }; +}; diff --git a/arch/riscv/boot/dts/thead/light-lpi4a-hdmi.dts b/arch/riscv/boot/dts/thead/light-lpi4a-hdmi.dts new file mode 100644 index 000000000000..4ef7487d57ef --- /dev/null +++ b/arch/riscv/boot/dts/thead/light-lpi4a-hdmi.dts @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Alibaba Group Holding Limited. + */ + +/dts-v1/; + +#include "light-lpi4a.dts" + +&lightsound { + status = "okay"; + simple-audio-card,dai-link@0 { /* I2S - HDMI*/ + reg = <0>; + format = "i2s"; + cpu { + sound-dai = <&light_i2s 1>; + }; + codec { + sound-dai = <&dummy_codec>; + }; + }; + simple-audio-card,dai-link@1 { /* I2S - AUDIO SYS CODEC 7210*/ + reg = <1>; + format = "i2s"; + cpu { + sound-dai = <&i2s1 0>; + }; + codec { + sound-dai = <&es7210_audio_codec>; + }; + }; + simple-audio-card,dai-link@2 { /* I2S - AUDIO SYS CODEC 8156*/ + reg = <2>; + format = "i2s"; + cpu { + sound-dai = <&i2s1 0>; + }; + codec { + sound-dai = <&es8156_audio_codec>; + }; + }; +}; + +&dpu_enc0 { + status = "disabled"; +}; + +&dsi0 { + status = "disabled"; +}; diff --git a/arch/riscv/boot/dts/thead/light-lpi4a-hx8279.dts b/arch/riscv/boot/dts/thead/light-lpi4a-hx8279.dts new file mode 100644 index 000000000000..d940542f72ec --- /dev/null +++ b/arch/riscv/boot/dts/thead/light-lpi4a-hx8279.dts @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022-2023 Alibaba Group Holding Limited. + */ + +#include "light-lpi4a-ref.dts" + +/ { + model = "T-HEAD Light Lichee Pi 4A configuration for 8GB DDR board"; + compatible = "thead,light-val", "thead,light-lpi4a", "thead,light"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x200000 0x1 0xffe00000>; + }; +}; + +&cmamem { + size = <0 0x20000000>; // 512MB on lpi4a (SOM) + alloc-ranges = <0 0xd8000000 0 0x20000000>; // [0x0D800_0000 ~ 0x0F800_0000] +}; + +&i2c3 { + touch@14 { + #gpio-cells = <2>; + compatible = "goodix,gt9271"; + reg = <0x14>; + interrupt-parent = <&ao_gpio_porta>; + interrupts = <3 0>; + irq-gpios = <&ao_gpio_porta 3 0>; + reset-gpios = <&pcal6408ahk_d 0 0>; + AVDD28-supply = <®_tp_pwr_en>; + touchscreen-size-x = <1200>; + touchscreen-size-y = <1920>; + tp-size = <9271>; + status = "okay"; + }; +}; + +&dsi0 { + status = "okay"; +}; + + +&dhost_0 { + panel0@0 { + compatible = "himax,hx8279"; + reg = <0>; + backlight = <&lcd0_backlight>; + reset-gpio = <&pcal6408ahk_d 7 0>; /* active low */ + hsvcc-supply = <&soc_vdd18_lcd0_en_reg>; + vspn3v3-supply = <&soc_vdd33_lcd0_en_reg>; + + port { + panel0_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/thead/light-lpi4a-ref.dts b/arch/riscv/boot/dts/thead/light-lpi4a-ref.dts index a7e88a301463..58aa809b6d81 100644 --- a/arch/riscv/boot/dts/thead/light-lpi4a-ref.dts +++ b/arch/riscv/boot/dts/thead/light-lpi4a-ref.dts @@ -158,6 +158,14 @@ iopmp_dsp1: IOPMP_DSP1 { is_default_region; }; + + iopmp_audio0: IOPMP_AUDIO0 { + is_default_region; + }; + + iopmp_audio1: IOPMP_AUDIO1 { + is_default_region; + }; }; mbox_910t_client1: mbox_910t_client1 { @@ -258,15 +266,17 @@ ref-clock-frequency = <24000000>; keep_wifi_power_on; pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_wifi_wake>; wifi_chip_type = "rtl8723ds"; - WIFI,poweren_gpio = <&pcal6408ahk_c 5 0>; + WIFI,poweren_gpio = <&pcal6408ahk_c 4 0>; status = "okay"; }; wcn_bt: wireless-bluetooth { compatible = "bluetooth-platdata"; - pinctrl-names = "default", "rts_gpio"; - BT,power_gpio = <&pcal6408ahk_c 6 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_bt_wake>; + BT,power_gpio = <&pcal6408ahk_c 5 0>; status = "okay"; }; @@ -651,9 +661,9 @@ no-map; }; vi_mem: framebuffer@10000000 { - reg = <0x0 0x10000000 0x0 0x02C00000 /* vi_mem_pool_region[0] 44 MB (default) */ - 0x0 0x12C00000 0x0 0x01D00000 /* vi_mem_pool_region[1] 29 MB */ - 0x0 0x14900000 0x0 0x01E00000>; /* vi_mem_pool_region[2] 30 MB */ + reg = <0x0 0x10000000 0x0 0x6700000>; /* vi_mem_pool_region[0] 44 MB (default) */ + //0x0 0x12C00000 0x0 0x01D00000 /* vi_mem_pool_region[1] 29 MB */ + //0x0 0x14900000 0x0 0x01E00000>; /* vi_mem_pool_region[2] 30 MB */ no-map; }; facelib_mem: memory@17000000 { @@ -759,9 +769,9 @@ num-cs = <1>; cs-gpios = <&gpio2_porta 15 0>; // GPIO_ACTIVE_HIGH: 0 rx-sample-delay-ns = <10>; - status = "okay"; spi_norflash@0 { + status = "okay"; #address-cells = <1>; #size-cells = <1>; compatible = "winbond,w25q64jwm", "jedec,spi-nor"; @@ -771,6 +781,7 @@ }; spidev@1 { + status = "disable"; compatible = "spidev"; #address-cells = <0x1>; #size-cells = <0x1>; @@ -781,6 +792,23 @@ &uart0 { clock-frequency = <100000000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart0>; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3>; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; }; &qspi0 { @@ -827,6 +855,8 @@ tx-clk-delay = <0x00>; /* for RGMII */ phy-handle = <&phy_88E1111_0>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gmac0>; mdio0 { #address-cells = <1>; @@ -849,6 +879,8 @@ tx-clk-delay = <0x00>; /* for RGMII */ phy-handle = <&phy_88E1111_1>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gmac1>; }; &emmc { @@ -870,6 +902,8 @@ pull_up; wprtn_ignore; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdio0>; }; &sdhci1 { @@ -896,8 +930,22 @@ */ pinctrl_uart0: uart0grp { thead,pins = < - FM_UART0_TXD 0x0 0x72 - FM_UART0_RXD 0x0 0x72 + FM_UART0_TXD 0x0 0x202 + FM_UART0_RXD 0x0 0x202 + >; + }; + + pinctrl_i2c2: i2c2grp { + thead,pins = < + FM_I2C2_SCL 0x0 0x204 + FM_I2C2_SDA 0x0 0x204 + >; + }; + + pinctrl_i2c3: i2c3grp { + thead,pins = < + FM_I2C3_SCL 0x0 0x204 + FM_I2C3_SDA 0x0 0x204 >; }; @@ -934,9 +982,62 @@ >; }; + pinctrl_gmac1: gmac1grp { + thead,pins = < + FM_GPIO2_18 0x1 0x20f /* GMAC1_TX_CLK */ + FM_GPIO2_19 0x1 0x20f /* GMAC1_RX_CLK */ + FM_GPIO2_20 0x1 0x20f /* GMAC1_TXEN */ + FM_GPIO2_21 0x1 0x20f /* GMAC1_TXD0 */ + FM_GPIO2_22 0x1 0x20f /* GMAC1_TXD1 */ + FM_GPIO2_23 0x1 0x20f /* GMAC1_TXD2 */ + FM_GPIO2_24 0x1 0x20f /* GMAC1_TXD3 */ + FM_GPIO2_25 0x1 0x20f /* GMAC1_RXDV */ + FM_GPIO2_30 0x1 0x20f /* GMAC1_RXD0 */ + FM_GPIO2_31 0x1 0x20f /* GMAC1_RXD1 */ + FM_GPIO3_0 0x1 0x20f /* GMAC1_RXD2 */ + FM_GPIO3_1 0x1 0x20f /* GMAC1_RXD3 */ + >; + }; + + pinctrl_sdio0: sdio0grp { + thead,pins = < + FM_SDIO0_DETN 0x0 0x202 + >; + }; + pinctrl_pwm: pwmgrp { thead,pins = < - FM_GPIO3_2 0x1 0x208 /* pwm0 */ + FM_GPIO3_2 0x1 0x20f /* pwm0 */ + FM_GPIO3_3 0x1 0x20f /* pwm1 */ + >; + }; + + pinctrl_hdmi: hdmigrp { + thead,pins = < + FM_HDMI_SCL 0x0 0x202 + FM_HDMI_SDA 0x0 0x202 + FM_HDMI_CEC 0x0 0x202 + >; + }; + + pinctrl_gmac0: gmac0grp { + thead,pins = < + FM_GMAC0_TX_CLK 0x0 0x20f /* GMAC0_TX_CLK */ + FM_GMAC0_RX_CLK 0x0 0x20f /* GMAC0_RX_CLK */ + FM_GMAC0_TXEN 0x0 0x20f /* GMAC0_TXEN */ + FM_GMAC0_TXD0 0x0 0x20f /* GMAC0_TXD0 */ + FM_GMAC0_TXD1 0x0 0x20f /* GMAC0_TXD1 */ + FM_GMAC0_TXD2 0x0 0x20f /* GMAC0_TXD2 */ + FM_GMAC0_TXD3 0x0 0x20f /* GMAC0_TXD3 */ + FM_GMAC0_RXDV 0x0 0x20f /* GMAC0_RXDV */ + FM_GMAC0_RXD0 0x0 0x20f /* GMAC0_RXD0 */ + FM_GMAC0_RXD1 0x0 0x20f /* GMAC0_RXD1 */ + FM_GMAC0_RXD2 0x0 0x20f /* GMAC0_RXD2 */ + FM_GMAC0_RXD3 0x0 0x20f /* GMAC0_RXD3 */ + FM_GMAC0_MDC 0x0 0x208 /* GMAC0_MDC */ + FM_GMAC0_MDIO 0x0 0x208 /* GMAC0_MDIO */ + FM_GMAC0_COL 0x3 0x232 /* PHY0_nRST */ + FM_GMAC0_CRS 0x3 0x232 /* PHY0_nINT */ >; }; }; @@ -948,31 +1049,63 @@ * Pin Configuration Node: * Format: */ - pinctrl_uart3: uart3grp { + pinctrl_qspi1: qspi1grp { thead,pins = < - FM_UART3_TXD 0x0 0x72 - FM_UART3_RXD 0x0 0x72 + FM_QSPI1_SCLK 0x0 0x20a + FM_QSPI1_CSN0 0x3 0x20a + FM_QSPI1_D0_MOSI 0x0 0x23a + FM_QSPI1_D1_MISO 0x0 0x23a + >; + }; + + pinctrl_i2c0: i2c0grp { + thead,pins = < + FM_I2C0_SCL 0x0 0x204 + FM_I2C0_SDA 0x0 0x204 + >; + }; + + pinctrl_i2c1: i2c1grp { + thead,pins = < + FM_I2C1_SCL 0x0 0x204 + FM_I2C1_SDA 0x0 0x204 + >; + }; + + pinctrl_uart1: uart1grp { + thead,pins = < + FM_UART1_TXD 0x0 0x202 + FM_UART1_RXD 0x0 0x202 >; }; pinctrl_uart4: uart4grp { thead,pins = < - FM_UART4_TXD 0x0 0x72 - FM_UART4_RXD 0x0 0x72 - FM_UART4_CTSN 0x0 0x72 - FM_UART4_RTSN 0x0 0x72 + FM_UART4_TXD 0x0 0x202 + FM_UART4_RXD 0x0 0x202 + FM_UART4_CTSN 0x0 0x202 + FM_UART4_RTSN 0x0 0x202 >; }; - pinctrl_qspi1: qspi1grp { + pinctrl_uart3: uart3grp { thead,pins = < - FM_QSPI1_SCLK 0x0 0x20a - FM_QSPI1_CSN0 0x3 0x20a - FM_QSPI1_D0_MOSI 0x0 0x23a - FM_QSPI1_D1_MISO 0x0 0x23a + FM_UART3_TXD 0x1 0x202 + FM_UART3_RXD 0x1 0x202 + >; + }; + + pinctrl_wifi_wake: wifi_grp { + thead,pins = < + FM_GPIO0_27 0x0 0x202 >; }; + pinctrl_bt_wake: bt_grp { + thead,pins = < + FM_GPIO0_28 0x0 0x202 + >; + }; pinctrl_iso7816: iso7816grp { thead,pins = < @@ -1094,6 +1227,8 @@ &i2c0 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c0>; pcal6408ahk_b: gpio@20 { compatible = "nxp,pca9557"; @@ -1106,6 +1241,8 @@ &i2c1 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; pcal6408ahk_c: gpio@20 { compatible = "nxp,pca9557"; @@ -1118,11 +1255,15 @@ &i2c2 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; }; &i2c3 { clock-frequency = <400000>; status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; pcal6408ahk_d: gpio@20 { compatible = "nxp,pca9557"; @@ -1275,13 +1416,6 @@ channel0 { channel_id = <0>; status = "okay"; - sensor0 { - subdev_name = "vivcam"; - idx = <0>; - csi_idx = <0>; - mode_idx = <0>; - path_type = "SENSOR_VGA_RAW12_LINER"; - }; sensor1 { subdev_name = "vivcam"; idx = <3>; @@ -1306,21 +1440,52 @@ }; }; }; + channel1 { + sensor1 { + subdev_name = "vivcam"; + idx = <3>; + csi_idx = <0>; + mode_idx = <1>; + path_type = "SENSOR_2592x1944_LINER"; + }; + isp { + subdev_name = "isp"; + idx = <1>; + path_type = "ISP_MI_PATH_SP"; + output { + max_width = <2600>; + max_height = <2000>; + bit_per_pixel = <12>; + frame_count = <3>; + }; + }; + }; + channel2 { + sensor1 { + subdev_name = "vivcam"; + idx = <3>; + csi_idx = <0>; + mode_idx = <1>; + path_type = "SENSOR_2592x1944_LINER"; + }; + isp { + subdev_name = "isp"; + idx = <1>; + path_type = "ISP_MI_PATH_SP2_BP"; + output { + max_width = <2600>; + max_height = <2000>; + bit_per_pixel = <12>; + frame_count = <3>; + }; + }; + }; }; - &video3{ vi_mem_pool_region = <0>; // vi_mem: framebuffer, region[0] status = "okay"; channel0 { - sensor0 { - subdev_name = "vivcam"; - idx = <0>; - csi_idx = <0>; - mode_idx = <0>; - path_type = "SENSOR_VGA_RAW12_LINER"; - - }; sensor1 { subdev_name = "vivcam"; idx = <3>; @@ -1339,8 +1504,8 @@ idx = <1>; path_type = "ISP_MI_PATH_MP"; output { - max_width = <1920>; - max_height = <1088>; + max_width = <2600>; + max_height = <2000>; bit_per_pixel = <12>; frame_count = <3>; }; @@ -1353,13 +1518,12 @@ }; }; channel1 { - sensor0 { - subdev_name = "vivcam"; - idx = <0>; + sensor1 { + subdev_name = "vivcam"; + idx = <3>; csi_idx = <0>; - mode_idx = <0>; - path_type = "SENSOR_VGA_RAW12_LINER"; - + mode_idx = <1>; + path_type = "SENSOR_2592x1944_LINER"; }; dma { subdev_name = "vipre"; @@ -1372,8 +1536,8 @@ idx = <1>; path_type = "ISP_MI_PATH_MP"; output { - max_width = <1920>; - max_height = <1088>; + max_width = <2600>; + max_height = <2000>; bit_per_pixel = <12>; frame_count = <3>; }; @@ -1386,13 +1550,12 @@ }; }; channel2 { - sensor0 { - subdev_name = "vivcam"; - idx = <0>; + sensor1 { + subdev_name = "vivcam"; + idx = <3>; csi_idx = <0>; - mode_idx = <0>; - path_type = "SENSOR_VGA_RAW12_LINER"; - + mode_idx = <1>; + path_type = "SENSOR_2592x1944_LINER"; }; dma { subdev_name = "vipre"; @@ -1405,8 +1568,8 @@ idx = <1>; path_type = "ISP_MI_PATH_MP"; output { - max_width = <1920>; - max_height = <1088>; + max_width = <2600>; + max_height = <2000>; bit_per_pixel = <12>; frame_count = <3>; }; @@ -1420,70 +1583,6 @@ }; }; -&video9{ - status = "okay"; - channel0 { - sensor0 { - subdev_name = "vivcam"; - idx = <1>; //vivcam1 sc132gs - csi_idx = <2>; //<2>=CSI2X2_A - mode_idx = <0>; - path_type = "SENSOR_1080X1280_30FPS_RAW10_LINER"; - }; - dsp{ - output { - max_width = <1080>; - max_height = <1280>; - bit_per_pixel = <16>; - frame_count = <3>; - }; - }; - }; -}; - - -&video10{ // TUNINGTOOL - status = "okay"; - channel0 { - sensor0 { - subdev_name = "vivcam"; - idx = <0>; //<0>=vivcam0 : ov12870 - csi_idx = <0>; //<0>=CSI2 - mode_idx = <1>; - path_type = "SENSOR_1080P_RAW10_LINER"; //SENSOR_VGA_RAW10_LINER// - skip_init = <1>; - }; - dma { - path_type = "VIPRE_CSI0_ISP0"; - }; - }; -}; - -&video15{ - status = "okay"; - channel0 { - status = "okay"; - sensor0 { - subdev_name = "vivcam"; - idx = <0>; - csi_idx = <0>; - mode_idx = <0>; - path_type = "SENSOR_VGA_RAW12_LINER"; - }; - sensor1 { - subdev_name = "vivcam"; - idx = <3>; - csi_idx = <0>; - mode_idx = <1>; - path_type = "SENSOR_2592x1944_LINER"; - }; - dma { - subdev_name = "vipre"; - idx = <0>; - path_type = "VIPRE_CSI0_DDR"; - }; - }; -}; &trng { status = "disabled"; @@ -1534,12 +1633,37 @@ status = "okay"; }; +&dhost_0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + dsi0_in: endpoint { + remote-endpoint = <&enc0_out>; + }; + }; + + port@1 { + reg = <1>; + + dsi0_out: endpoint { + remote-endpoint = <&panel0_in>; + }; + }; + }; +}; + &disp1_out { remote-endpoint = <&hdmi_tx_in>; }; &hdmi_tx { status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi>; port@0 { /* input */ @@ -1593,13 +1717,13 @@ &i2s1 { status = "okay"; - dmas = <&dmac2 11>, <&dmac2 10>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_audiopa14>, <&pinctrl_audiopa15>, <&pinctrl_audiopa16>, <&pinctrl_audiopa17>, <&pinctrl_audio_i2s1>; + light,mclk_keepon = <1>; }; &i2s2 { diff --git a/arch/riscv/boot/dts/thead/light-lpi4a-sec.dts b/arch/riscv/boot/dts/thead/light-lpi4a-sec.dts index cefaf9105f52..5a5aa7911350 100644 --- a/arch/riscv/boot/dts/thead/light-lpi4a-sec.dts +++ b/arch/riscv/boot/dts/thead/light-lpi4a-sec.dts @@ -5,7 +5,7 @@ /dts-v1/; -#include "light-lpi4a.dts" +#include "light-lpi4a-hdmi.dts" &light_iopmp { diff --git a/arch/riscv/boot/dts/thead/light-lpi4a.dts b/arch/riscv/boot/dts/thead/light-lpi4a.dts index 0591f6ce163c..9883f90651f0 100644 --- a/arch/riscv/boot/dts/thead/light-lpi4a.dts +++ b/arch/riscv/boot/dts/thead/light-lpi4a.dts @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2022 Alibaba Group Holding Limited. + * Copyright (C) 2022-2023 Alibaba Group Holding Limited. */ #include "light-lpi4a-ref.dts" @@ -16,5 +16,44 @@ }; &cmamem { - alloc-ranges = <0 0xe4000000 0 0x14000000>; // [0x0E400_0000 ~ 0x0F800_0000] + size = <0 0x20000000>; // 512MB on lpi4a (SOM) + alloc-ranges = <0 0xd8000000 0 0x20000000>; // [0x0D800_0000 ~ 0x0F800_0000] +}; + +&i2c3 { + touch@14 { + #gpio-cells = <2>; + compatible = "goodix,gt9271"; + reg = <0x14>; + interrupt-parent = <&ao_gpio_porta>; + interrupts = <3 0>; + irq-gpios = <&ao_gpio_porta 3 0>; + reset-gpios = <&pcal6408ahk_d 0 0>; + AVDD28-supply = <®_tp_pwr_en>; + touchscreen-size-x = <800>; + touchscreen-size-y = <1200>; + tp-size = <9271>; + status = "okay"; + }; +}; + +&dsi0 { + status = "okay"; +}; + +&dhost_0 { + panel0@0 { + compatible = "chongzhou,cz101b4001", "jadard,jd9365da-h3"; + reg = <0>; + backlight = <&lcd0_backlight>; + reset-gpio = <&pcal6408ahk_d 7 0>; /* active low */ + hsvcc-supply = <&soc_vdd18_lcd0_en_reg>; + vspn3v3-supply = <&soc_vdd33_lcd0_en_reg>; + + port { + panel0_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; }; diff --git a/arch/riscv/boot/dts/thead/light-vi-devices.dtsi b/arch/riscv/boot/dts/thead/light-vi-devices.dtsi index 4bd13a4199a6..f3f7c00bf8a5 100644 --- a/arch/riscv/boot/dts/thead/light-vi-devices.dtsi +++ b/arch/riscv/boot/dts/thead/light-vi-devices.dtsi @@ -4,7 +4,7 @@ */ &video0{ - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -86,7 +86,7 @@ }; &video1{ - status = "okay"; + status = "disabled"; channel0 { // VSE0 channel_id = <0>; status = "okay"; @@ -183,7 +183,7 @@ }; &video2 { - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -272,7 +272,7 @@ }; &video3 { - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -370,7 +370,7 @@ }; &video4 { - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -483,7 +483,7 @@ }; &video5 { - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -612,7 +612,7 @@ &video6 { - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -669,7 +669,7 @@ &video7{ - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -798,7 +798,7 @@ &video8{ - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -829,7 +829,7 @@ &video9 { //IR debug - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -862,7 +862,7 @@ &video10{ // TUNING TOOL - status = "okay"; + status = "disabled"; channel0 { // CSI2X2_B status = "okay"; sensor0 { @@ -889,7 +889,7 @@ &video11{ - status = "okay"; + status = "disabled"; channel0 { channel_id = <0>; status = "okay"; @@ -920,7 +920,7 @@ &video12{ // TUNING TOOL - status = "okay"; + status = "disabled"; channel0 { // CSI2 status = "okay"; sensor0 { diff --git a/arch/riscv/boot/dts/thead/light.dtsi b/arch/riscv/boot/dts/thead/light.dtsi index e66b5ab35c16..292168ae0355 100644 --- a/arch/riscv/boot/dts/thead/light.dtsi +++ b/arch/riscv/boot/dts/thead/light.dtsi @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -186,6 +187,7 @@ interrupt-controller; }; }; + c910_1: cpu@1 { device_type = "cpu"; reg = <1>; @@ -324,6 +326,44 @@ interrupt-controller; }; }; + + idle_states: idle-states { + CPU_RET_0_0: cpu-retentive-0-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x10000000>; + entry-latency-us = <20>; + exit-latency-us = <40>; + min-residency-us = <80>; + }; + + CPU_NONRET_0_0: cpu-nonretentive-0-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x90000000>; + entry-latency-us = <250>; + exit-latency-us = <500>; + min-residency-us = <950>; + }; + + CLUSTER_RET_0: cluster-retentive-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x11000000>; + local-timer-stop; + entry-latency-us = <50>; + exit-latency-us = <100>; + min-residency-us = <250>; + wakeup-latency-us = <130>; + }; + + CLUSTER_NONRET_0: cluster-nonretentive-0 { + compatible = "riscv,idle-state"; + riscv,sbi-suspend-param = <0x91000000>; + local-timer-stop; + entry-latency-us = <600>; + exit-latency-us = <1100>; + min-residency-us = <2700>; + wakeup-latency-us = <1500>; + }; + }; }; display-subsystem { @@ -632,6 +672,18 @@ status = "okay"; }; + miscsys_reg: miscsys-reg@ffec02c000 { + compatible = "thead,light-miscsys-reg", "syscon"; + reg = <0xff 0xec02c000 0x0 0x1000>; + status = "okay"; + }; + + tee_miscsys_reg: tee_miscsys-reg@fffc02d000 { + compatible = "thead,light-miscsys-reg", "syscon"; + reg = <0xff 0xfc02d000 0x0 0x1000>; + status = "okay"; + }; + audio_ioctrl: audio_ioctrl@ffcb01d000 { compatible = "thead,light-audio-ioctrl-reg", "syscon"; reg = <0xff 0xcb01d000 0x0 0x1000>; @@ -656,6 +708,8 @@ thead,teesys = <&teesys_syscon>; #address-cells = <1>; #size-cells = <1>; + clocks = <&miscsys_clk_gate CLKGEN_MISCSYS_EFUSE_PCLK>; + clock-names = "pclk"; gmac0_mac_address: mac-address@176 { reg = <0xb0 6>; @@ -683,7 +737,9 @@ reg = <0xff 0xec005000 0x0 0x1000>; #address-cells = <1>; #size-cells = <0>; - + clocks = <&clk CLKGEN_GPIO0_PCLK>, + <&clk CLKGEN_GPIO0_DBCLK>; + clock-names = "bus", "db"; gpio0_porta: gpio0-controller@0 { compatible = "snps,dw-apb-gpio-port"; gpio-controller; @@ -703,7 +759,9 @@ reg = <0xff 0xec006000 0x0 0x1000>; #address-cells = <1>; #size-cells = <0>; - + clocks = <&clk CLKGEN_GPIO1_PCLK>, + <&clk CLKGEN_GPIO1_DBCLK>; + clock-names = "bus", "db"; gpio1_porta: gpio1-controller@0 { compatible = "snps,dw-apb-gpio-port"; gpio-controller; @@ -723,7 +781,9 @@ reg = <0xff 0xe7f34000 0x0 0x1000>; #address-cells = <1>; #size-cells = <0>; - + clocks = <&clk CLKGEN_GPIO2_PCLK>, + <&clk CLKGEN_GPIO2_DBCLK>; + clock-names = "bus", "db"; gpio2_porta: gpio2-controller@0 { compatible = "snps,dw-apb-gpio-port"; gpio-controller; @@ -743,7 +803,9 @@ reg = <0xff 0xe7f38000 0x0 0x1000>; #address-cells = <1>; #size-cells = <0>; - + clocks = <&clk CLKGEN_GPIO3_PCLK>, + <&clk CLKGEN_GPIO3_DBCLK>; + clock-names = "bus", "db"; gpio3_porta: gpio3-controller@0 { compatible = "snps,dw-apb-gpio-port"; gpio-controller; @@ -798,15 +860,19 @@ }; }; - padctrl1_apsys: pinctrl1-apsys@ffe7f3c000 { + padctrl1_apsys: padctrl1-apsys@ffe7f3c000 { compatible = "thead,light-fm-left-pinctrl"; reg = <0xff 0xe7f3c000 0x0 0x1000>; + clocks = <&clk CLKGEN_PADCTRL1_APSYS_PCLK>; + clock-names = "pclk"; status = "okay"; }; padctrl0_apsys: padctrl0-apsys@ffec007000 { compatible = "thead,light-fm-right-pinctrl"; reg = <0xff 0xec007000 0x0 0x1000>; + clocks = <&clk CLKGEN_PADCTRL0_APSYS_PCLK>; + clock-names = "pclk"; status = "okay"; }; @@ -922,7 +988,7 @@ }; uart0: serial@ffe7014000 { /* Normal serial, for C910 log */ - compatible = "snps,dw-apb-uart"; + compatible = "snps,dw-apb-uart", "light,uart0"; reg = <0xff 0xe7014000 0x0 0x4000>; interrupt-parent = <&intc>; interrupts = <36>; @@ -1017,7 +1083,9 @@ pinctrl-0 = <&pinctrl_spi0>; interrupt-parent = <&intc>; interrupts = <54>; - clocks = <&dummy_clock_spi>; + clocks = <&clk CLKGEN_SPI_SSI_CLK>, + <&clk CLKGEN_SPI_PCLK>; + clock-names = "sclk", "pclk"; num-cs = <2>; #address-cells = <1>; #size-cells = <0>; @@ -1030,7 +1098,9 @@ pinctrl-0 = <&pinctrl_qspi0>; interrupt-parent = <&intc>; interrupts = <52>; - clocks = <&dummy_clock_qspi>; + clocks = <&clk CLKGEN_QSPI0_SSI_CLK>, + <&clk CLKGEN_QSPI0_PCLK>; + clock-names = "sclk", "pclk"; #address-cells = <1>; #size-cells = <0>; }; @@ -1042,7 +1112,9 @@ pinctrl-0 = <&pinctrl_qspi1>; interrupt-parent = <&intc>; interrupts = <53>; - clocks = <&dummy_clock_spi>; + clocks = <&clk CLKGEN_QSPI1_SSI_CLK>, + <&clk CLKGEN_QSPI1_PCLK>; + clock-names = "sclk", "pclk"; #address-cells = <1>; #size-cells = <0>; }; @@ -1153,9 +1225,8 @@ clocks = <&vosys_clk_gate LIGHT_CLKGEN_HDMI_PCLK>, <&vosys_clk_gate LIGHT_CLKGEN_HDMI_SFR_CLK>, <&vosys_clk_gate LIGHT_CLKGEN_HDMI_CEC_CLK>, - <&vosys_clk_gate LIGHT_CLKGEN_HDMI_PIXCLK>, - <&vosys_clk_gate LIGHT_CLKGEN_HDMI_I2S_CLK>; - clock-names = "iahb", "isfr", "cec", "pixclk", "i2s"; + <&vosys_clk_gate LIGHT_CLKGEN_HDMI_PIXCLK>; + clock-names = "iahb", "isfr", "cec", "pixclk"; reg-io-width = <4>; phy_version = <301>; /* TODO: add phy property */ @@ -1171,6 +1242,7 @@ <0xff 0xef630010 0x0 0x60>; interrupt-parent = <&intc>; interrupts = <93>; + vosys-regmap = <&vosys_reg>; clocks = <&vosys_clk_gate LIGHT_CLKGEN_DPU_CCLK>, <&vosys_clk_gate LIGHT_CLKGEN_DPU_PIXCLK0>, <&vosys_clk_gate LIGHT_CLKGEN_DPU_PIXCLK1>, @@ -1232,6 +1304,7 @@ interrupts = <74>; clocks = <&dummy_clock_rtc>; clock-names = "rtc"; + wakeup-source; status = "okay"; }; @@ -1239,6 +1312,11 @@ compatible = "thead,dwc3"; usb3-misc-regmap = <&misc_sysreg>; usb3-drd-regmap = <&usb3_drd>; + clocks = <&miscsys_clk_gate CLKGEN_MISCSYS_USB3_DRD_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_USB3_DRD_CTRL_REF_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_USB3_DRD_PHY_REF_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_USB3_DRD_SUSPEND_CLK>; + clock-names = "drd", "ctrl", "phy", "suspend"; #address-cells = <2>; #size-cells = <2>; ranges; @@ -1248,8 +1326,6 @@ reg = <0xff 0xe7040000 0x0 0x10000>; interrupt-parent = <&intc>; interrupts = <68>; - clocks = <&dummy_clock_ref>, <&dummy_clock_apb>, <&dummy_clock_suspend>; - clock-names = "ref", "bus_early", "suspend"; reg-shift = <2>; reg-io-width = <4>; maximum-speed = "super-speed"; @@ -1301,7 +1377,7 @@ reg = <0xff 0xefc00000 0x0 0x1000>; interrupt-parent = <&intc>; interrupts = <27>; - clocks = <&dummy_clock_apb>, <&dummy_clock_apb>; + clocks = <&clk CLKGEN_DMAC_CPUSYS_ACLK>, <&clk CLKGEN_DMAC_CPUSYS_HCLK>; clock-names = "core-clk", "cfgr-clk"; #dma-cells = <1>; dma-channels = <4>; @@ -1318,7 +1394,7 @@ reg = <0xff 0xff340000 0x0 0x1000>; interrupt-parent = <&intc>; interrupts = <150>; - clocks = <&dummy_clock_apb>, <&dummy_clock_apb>; + clocks = <&clk CLKGEN_DMAC_CPUSYS_ACLK>, <&clk CLKGEN_DMAC_CPUSYS_HCLK>; clock-names = "core-clk", "cfgr-clk"; #dma-cells = <1>; dma-channels = <4>; @@ -1335,7 +1411,7 @@ reg = <0xff 0xc8000000 0x0 0x2000>; interrupt-parent = <&intc>; interrupts = <167>; - clocks = <&dummy_clock_apb>, <&dummy_clock_apb>; + clocks = <&clk CLKGEN_DMAC_CPUSYS_ACLK>, <&clk CLKGEN_DMAC_CPUSYS_HCLK>; clock-names = "core-clk", "cfgr-clk"; #dma-cells = <1>; dma-channels = <16>; @@ -1366,8 +1442,11 @@ interrupt-parent = <&intc>; interrupts = <66>; interrupt-names = "macirq"; - clocks = <&clk CLKGEN_GMAC0_CCLK>; - clock-names = "gmac_pll_clk"; + clocks = <&clk CLKGEN_GMAC0_CCLK>, + <&clk CLKGEN_GMAC0_PCLK>, + <&clk CLKGEN_GMAC_AXI_ACLK>, + <&clk CLKGEN_GMAC_AXI_PCLK>; + clock-names = "gmac_pll_clk","pclk","axi_aclk","axi_pclk"; snps,pbl = <32>; snps,fixed-burst; snps,axi-config = <&stmmac_axi_setup>; @@ -1385,8 +1464,11 @@ interrupt-parent = <&intc>; interrupts = <67>; interrupt-names = "macirq"; - clocks = <&clk CLKGEN_GMAC1_CCLK>; - clock-names = "gmac_pll_clk"; + clocks = <&clk CLKGEN_GMAC1_CCLK>, + <&clk CLKGEN_GMAC1_PCLK>, + <&clk CLKGEN_GMAC_AXI_ACLK>, + <&clk CLKGEN_GMAC_AXI_PCLK>; + clock-names = "gmac_pll_clk","pclk","axi_aclk","axi_pclk"; snps,pbl = <32>; snps,fixed-burst; snps,axi-config = <&stmmac_axi_setup>; @@ -1400,8 +1482,9 @@ interrupt-parent = <&intc>; interrupts = <62>; interrupt-names = "sdhciirq"; - clocks = <&dummy_clock_sdhci>; - clock-names = "core"; + clocks = <&clk CLKGEN_EMMC_SDIO_REF_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_EMMC_CLK>; + clock-names = "core", "bus"; }; sdhci0: sd@ffe7090000 { @@ -1410,18 +1493,20 @@ interrupt-parent = <&intc>; interrupts = <64>; interrupt-names = "sdhci0irq"; - clocks = <&dummy_clock_sdhci>; - clock-names = "core"; + clocks = <&clk CLKGEN_EMMC_SDIO_REF_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_EMMC_CLK>; + clock-names = "core", "bus"; }; sdhci1: sd@ffe70a0000 { - compatible = "snps,dwcmshc-sdhci"; - reg = <0xff 0xe70a0000 0x0 0x10000>; - interrupt-parent = <&intc>; - interrupts = <71>; - interrupt-names = "sdhci1irq"; - clocks = <&dummy_clock_sdhci>; - clock-names = "core"; + compatible = "snps,dwcmshc-sdhci"; + reg = <0xff 0xe70a0000 0x0 0x10000>; + interrupt-parent = <&intc>; + interrupts = <71>; + interrupt-names = "sdhci1irq"; + clocks = <&clk CLKGEN_EMMC_SDIO_REF_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_EMMC_CLK>; + clock-names = "core", "bus"; }; hwspinlock: hwspinlock@ffefc10000 { @@ -1521,8 +1606,6 @@ #sound-dai-cells = <1>; compatible = "light,light-i2s"; reg = <0xff 0xe7034000 0x0 0x4000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_light_i2s0>; light,mode = "i2s-master"; light,sel = "ap_i2s"; interrupt-parent = <&intc>; @@ -1531,7 +1614,7 @@ dma-names = "tx", "rx"; light,dma_maxburst = <4>; #dma-cells = <1>; - clocks = <&dummy_clock_apb>; + clocks = <&vosys_clk_gate LIGHT_CLKGEN_HDMI_I2S_CLK>; clock-names = "pclk"; status = "disabled"; }; @@ -1551,7 +1634,7 @@ dma-names = "tx", "rx"; light,dma_maxburst = <4>; #dma-cells = <1>; - clocks = <&dummy_clock_apb>; + clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_I2S0>; clock-names = "pclk"; status = "disabled"; }; @@ -1567,11 +1650,11 @@ light,sel = "i2s1"; interrupt-parent = <&intc>; interrupts = <175>; - dmas = <&dmac2 11>, <&dmac2 17>; + dmas = <&dmac2 11>, <&dmac2 10>; dma-names = "tx", "rx"; light,dma_maxburst = <4>; #dma-cells = <1>; - clocks = <&dummy_clock_apb>; + clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_I2S1>; clock-names = "pclk"; status = "disabled"; }; @@ -1591,7 +1674,7 @@ dma-names = "tx", "rx"; light,dma_maxburst = <4>; #dma-cells = <1>; - clocks = <&dummy_clock_apb>; + clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_I2S2>; clock-names = "pclk"; status = "disabled"; }; @@ -1610,7 +1693,7 @@ dma-names = "tx", "rx"; light,dma_maxburst = <4>; #dma-cells = <1>; - clocks = <&dummy_clock_apb>; + clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_I2S8CH>; clock-names = "pclk"; status = "disabled"; }; @@ -1629,7 +1712,7 @@ dma-names = "tx", "rx"; light,dma_maxburst = <4>; #dma-cells = <1>; - clocks = <&dummy_clock_apb>; + clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_I2S8CH>; clock-names = "pclk"; status = "disabled"; }; @@ -1648,7 +1731,7 @@ dma-names = "tx", "rx"; light,dma_maxburst = <4>; #dma-cells = <1>; - clocks = <&dummy_clock_apb>; + clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_I2S8CH>; clock-names = "pclk"; status = "disabled"; }; @@ -1667,7 +1750,7 @@ dma-names = "tx", "rx"; light,dma_maxburst = <4>; #dma-cells = <1>; - clocks = <&dummy_clock_apb>; + clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_I2S8CH>; clock-names = "pclk"; status = "disabled"; }; @@ -1855,6 +1938,7 @@ #dma-cells = <1>; clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_SPDIF0>; clock-names = "pclk"; + id = <0>; status = "disabled"; }; @@ -1873,6 +1957,7 @@ #dma-cells = <1>; clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_SPDIF1>; clock-names = "pclk"; + id = <1>; status = "disabled"; }; @@ -1893,7 +1978,8 @@ reg = <0xff 0xe7f20000 0x0 0x4000>; interrupt-parent = <&intc>; interrupts = <44>; - clocks = <&dummy_clock_apb>; + clocks = <&clk CLKGEN_I2C0_PCLK>; + clock-names = "pclk"; clock-frequency = <100000>; i2c_mode = "dma"; dmas = <&dmac0 12>, <&dmac0 13>; @@ -1916,7 +2002,8 @@ reg = <0xff 0xe7f24000 0x0 0x4000>; interrupt-parent = <&intc>; interrupts = <45>; - clocks = <&dummy_clock_apb>; + clocks = <&clk CLKGEN_I2C1_PCLK>; + clock-names = "pclk"; clock-frequency = <100000>; i2c_mode = "dma"; dmas = <&dmac0 14>, <&dmac0 15>; @@ -1939,7 +2026,8 @@ reg = <0xff 0xec00c000 0x0 0x4000>; interrupt-parent = <&intc>; interrupts = <46>; - clocks = <&dummy_clock_apb>; + clocks = <&clk CLKGEN_I2C2_PCLK>; + clock-names = "pclk"; clock-frequency = <100000>; i2c_mode = "dma"; dmas = <&dmac0 16>, <&dmac0 17>; @@ -1964,7 +2052,8 @@ reg = <0xff 0xec014000 0x0 0x4000>; interrupt-parent = <&intc>; interrupts = <47>; - clocks = <&dummy_clock_apb>; + clocks = <&clk CLKGEN_I2C3_PCLK>; + clock-names = "pclk"; clock-frequency = <100000>; i2c_mode = "dma"; dmas = <&dmac0 18>, <&dmac0 19>; @@ -1989,7 +2078,8 @@ reg = <0xff 0xe7f28000 0x0 0x4000>; interrupt-parent = <&intc>; interrupts = <48>; - clocks = <&dummy_clock_apb>; + clocks = <&clk CLKGEN_I2C4_PCLK>; + clock-names = "pclk"; clock-frequency = <100000>; i2c_mode = "dma"; dmas = <&dmac0 20>, <&dmac0 21>; @@ -2480,7 +2570,11 @@ reg = <0xff 0xff300000 0x0 0x40000>; interrupt-parent = <&intc>; interrupts = <144>,<145>,<146>,<147>; - clocks = <&dummy_clock_eip>; + clocks = <&miscsys_clk_gate CLKGEN_MISCSYS_EIP120SI_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_EIP120SII_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_EIP120SIII_CLK>, + <&miscsys_clk_gate CLKGEN_MISCSYS_EIP150B_HCLK>; + clock-names = "120si_clk","120sii_clk","120siii_clk","hclk"; status = "disabled"; }; @@ -2496,6 +2590,11 @@ status = "okay"; }; + aon_suspend_ctrl: aon_suspend_ctrl { + compatible = "thead,light-aon-suspend-ctrl"; + status = "okay"; + }; + visys_clk_gate: visys-clk-gate { /* VI_SYSREG_R */ compatible = "thead,visys-gate-controller"; visys-regmap = <&visys_reg>; @@ -2530,6 +2629,15 @@ #clock-cells = <1>; status = "okay"; }; + + miscsys_clk_gate: miscsys-clk-gate { + compatible = "thead,miscsys-gate-controller"; + miscsys-regmap = <&miscsys_reg>; + tee-miscsys-regmap = <&tee_miscsys_reg>; + #clock-cells = <1>; + status = "okay"; + }; + }; }; diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index 35db128d726f..10e1987e5a22 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -17,6 +17,12 @@ CONFIG_PERF_EVENTS=y CONFIG_SOC_THEAD=y CONFIG_SMP=y CONFIG_VECTOR=y +CONFIG_HOTPLUG_CPU=y +CONFIG_PM=y +CONFIG_CPU_IDLE=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=m +CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_PARTITION_ADVANCED=y diff --git a/arch/riscv/configs/light_defconfig b/arch/riscv/configs/light_defconfig index 28c91fb562ca..f9783656bee2 100644 --- a/arch/riscv/configs/light_defconfig +++ b/arch/riscv/configs/light_defconfig @@ -106,6 +106,7 @@ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_GOODIX=y +CONFIG_TOUCHSCREEN_GT9XX=y CONFIG_INPUT_MISC=y CONFIG_INPUT_UINPUT=y CONFIG_SERIAL_8250=y @@ -200,6 +201,8 @@ CONFIG_DRM_PANEL_SIMPLE=y CONFIG_DRM_PANEL_ILITEK_ILI9881C=y CONFIG_DRM_PANEL_ILI9881D=y CONFIG_DRM_PANEL_HX8394=y +CONFIG_DRM_PANEL_JADARD_JD9365DA_H3=y +CONFIG_DRM_PANEL_HX8279=y CONFIG_DRM_VERISILICON=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_PWM=y @@ -337,3 +340,11 @@ CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 CONFIG_PM=y # CONFIG_SUSPEND is not set # CONFIG_PM_SLEEP is not set +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_PM_DEVFREQ_EVENT=y + diff --git a/arch/riscv/configs/rv32_defconfig b/arch/riscv/configs/rv32_defconfig index 2c2cda6cc1c5..75583d95885a 100644 --- a/arch/riscv/configs/rv32_defconfig +++ b/arch/riscv/configs/rv32_defconfig @@ -18,6 +18,11 @@ CONFIG_SOC_SIFIVE=y CONFIG_SOC_VIRT=y CONFIG_ARCH_RV32I=y CONFIG_SMP=y +CONFIG_HOTPLUG_CPU=y +CONFIG_PM=y +CONFIG_CPU_IDLE=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=m CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h index 9c992a88d858..002464eef45b 100644 --- a/arch/riscv/include/asm/asm.h +++ b/arch/riscv/include/asm/asm.h @@ -66,4 +66,31 @@ #error "Unexpected __SIZEOF_SHORT__" #endif +#ifdef __ASSEMBLY__ + +/* Common assembly source macros */ + +#ifdef CONFIG_XIP_KERNEL +.macro XIP_FIXUP_OFFSET reg + REG_L t0, _xip_fixup + add \reg, \reg, t0 +.endm +.macro XIP_FIXUP_FLASH_OFFSET reg + la t1, __data_loc + li t0, XIP_OFFSET_MASK + and t1, t1, t0 + li t1, XIP_OFFSET + sub t0, t0, t1 + sub \reg, \reg, t0 +.endm +_xip_fixup: .dword CONFIG_PHYS_RAM_BASE - CONFIG_XIP_PHYS_ADDR - XIP_OFFSET +#else +.macro XIP_FIXUP_OFFSET reg +.endm +.macro XIP_FIXUP_FLASH_OFFSET reg +.endm +#endif /* CONFIG_XIP_KERNEL */ + +#endif /* __ASSEMBLY__ */ + #endif /* _ASM_RISCV_ASM_H */ diff --git a/arch/riscv/include/asm/cpu_ops_sbi.h b/arch/riscv/include/asm/cpu_ops_sbi.h new file mode 100644 index 000000000000..56e4b76d09ff --- /dev/null +++ b/arch/riscv/include/asm/cpu_ops_sbi.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 by Rivos Inc. + */ +#ifndef __ASM_CPU_OPS_SBI_H +#define __ASM_CPU_OPS_SBI_H + +#ifndef __ASSEMBLY__ +#include +#include +#include + +/** + * struct sbi_hart_boot_data - Hart specific boot used during booting and + * cpu hotplug. + * @task_ptr: A pointer to the hart specific tp + * @stack_ptr: A pointer to the hart specific sp + */ +struct sbi_hart_boot_data { + void *task_ptr; + void *stack_ptr; +}; +#endif + +#endif /* ifndef __ASM_CPU_OPS_SBI_H */ diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 1dcd1e1ad1ee..9c715283b12b 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -446,7 +446,7 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot) return __pgprot(prot); } - +#define pgprot_dmacoherent pgprot_writecombine /* * Encode and decode a swap entry * diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h index 072c91f65da8..81c999a3eed4 100644 --- a/arch/riscv/include/asm/sbi.h +++ b/arch/riscv/include/asm/sbi.h @@ -61,13 +61,45 @@ enum sbi_ext_hsm_fid { SBI_EXT_HSM_HART_START = 0, SBI_EXT_HSM_HART_STOP, SBI_EXT_HSM_HART_STATUS, + SBI_EXT_HSM_HART_SUSPEND, }; -enum sbi_hsm_hart_status { - SBI_HSM_HART_STATUS_STARTED = 0, - SBI_HSM_HART_STATUS_STOPPED, - SBI_HSM_HART_STATUS_START_PENDING, - SBI_HSM_HART_STATUS_STOP_PENDING, +enum sbi_hsm_hart_state { + SBI_HSM_STATE_STARTED = 0, + SBI_HSM_STATE_STOPPED, + SBI_HSM_STATE_START_PENDING, + SBI_HSM_STATE_STOP_PENDING, + SBI_HSM_STATE_SUSPENDED, + SBI_HSM_STATE_SUSPEND_PENDING, + SBI_HSM_STATE_RESUME_PENDING, +}; + +#define SBI_HSM_SUSP_BASE_MASK 0x7fffffff +#define SBI_HSM_SUSP_NON_RET_BIT 0x80000000 +#define SBI_HSM_SUSP_PLAT_BASE 0x10000000 + +#define SBI_HSM_SUSPEND_RET_DEFAULT 0x00000000 +#define SBI_HSM_SUSPEND_RET_PLATFORM SBI_HSM_SUSP_PLAT_BASE +#define SBI_HSM_SUSPEND_RET_LAST SBI_HSM_SUSP_BASE_MASK +#define SBI_HSM_SUSPEND_NON_RET_DEFAULT SBI_HSM_SUSP_NON_RET_BIT +#define SBI_HSM_SUSPEND_NON_RET_PLATFORM (SBI_HSM_SUSP_NON_RET_BIT | \ + SBI_HSM_SUSP_PLAT_BASE) +#define SBI_HSM_SUSPEND_NON_RET_LAST (SBI_HSM_SUSP_NON_RET_BIT | \ + SBI_HSM_SUSP_BASE_MASK) + +enum sbi_ext_srst_fid { + SBI_EXT_SRST_RESET = 0, +}; + +enum sbi_srst_reset_type { + SBI_SRST_RESET_TYPE_SHUTDOWN = 0, + SBI_SRST_RESET_TYPE_COLD_REBOOT, + SBI_SRST_RESET_TYPE_WARM_REBOOT, +}; + +enum sbi_srst_reset_reason { + SBI_SRST_RESET_REASON_NONE = 0, + SBI_SRST_RESET_REASON_SYS_FAILURE, }; #define SBI_SPEC_VERSION_DEFAULT 0x1 diff --git a/arch/riscv/include/asm/suspend.h b/arch/riscv/include/asm/suspend.h new file mode 100644 index 000000000000..8be391c2aecb --- /dev/null +++ b/arch/riscv/include/asm/suspend.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#ifndef _ASM_RISCV_SUSPEND_H +#define _ASM_RISCV_SUSPEND_H + +#include + +struct suspend_context { + /* Saved and restored by low-level functions */ + struct pt_regs regs; + /* Saved and restored by high-level functions */ + unsigned long scratch; + unsigned long tvec; + unsigned long ie; +#ifdef CONFIG_MMU + unsigned long satp; +#endif +}; + +/* Low-level CPU suspend entry function */ +int __cpu_suspend_enter(struct suspend_context *context); + +/* High-level CPU suspend which will save context and call finish() */ +int cpu_suspend(unsigned long arg, + int (*finish)(unsigned long arg, + unsigned long entry, + unsigned long context)); + +/* Low-level CPU resume entry function */ +int __cpu_resume_enter(unsigned long hartid, unsigned long context); + +#endif diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 7f25f2d8c0ce..ba6c1520b0d1 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -48,6 +48,8 @@ obj-$(CONFIG_SMP) += cpu_ops_spinwait.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o +obj-$(CONFIG_CPU_PM) += suspend_entry.o suspend.o + obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index 431afa56cb52..9691d08bdd8c 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -10,6 +10,10 @@ #include #include #include +#include +#include + +void asm_offsets(void); void asm_offsets(void) { @@ -149,6 +153,162 @@ void asm_offsets(void) OFFSET(PT_BADADDR, pt_regs, badaddr); OFFSET(PT_CAUSE, pt_regs, cause); + OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs); +#if 0 + OFFSET(KVM_ARCH_GUEST_ZERO, kvm_vcpu_arch, guest_context.zero); + OFFSET(KVM_ARCH_GUEST_RA, kvm_vcpu_arch, guest_context.ra); + OFFSET(KVM_ARCH_GUEST_SP, kvm_vcpu_arch, guest_context.sp); + OFFSET(KVM_ARCH_GUEST_GP, kvm_vcpu_arch, guest_context.gp); + OFFSET(KVM_ARCH_GUEST_TP, kvm_vcpu_arch, guest_context.tp); + OFFSET(KVM_ARCH_GUEST_T0, kvm_vcpu_arch, guest_context.t0); + OFFSET(KVM_ARCH_GUEST_T1, kvm_vcpu_arch, guest_context.t1); + OFFSET(KVM_ARCH_GUEST_T2, kvm_vcpu_arch, guest_context.t2); + OFFSET(KVM_ARCH_GUEST_S0, kvm_vcpu_arch, guest_context.s0); + OFFSET(KVM_ARCH_GUEST_S1, kvm_vcpu_arch, guest_context.s1); + OFFSET(KVM_ARCH_GUEST_A0, kvm_vcpu_arch, guest_context.a0); + OFFSET(KVM_ARCH_GUEST_A1, kvm_vcpu_arch, guest_context.a1); + OFFSET(KVM_ARCH_GUEST_A2, kvm_vcpu_arch, guest_context.a2); + OFFSET(KVM_ARCH_GUEST_A3, kvm_vcpu_arch, guest_context.a3); + OFFSET(KVM_ARCH_GUEST_A4, kvm_vcpu_arch, guest_context.a4); + OFFSET(KVM_ARCH_GUEST_A5, kvm_vcpu_arch, guest_context.a5); + OFFSET(KVM_ARCH_GUEST_A6, kvm_vcpu_arch, guest_context.a6); + OFFSET(KVM_ARCH_GUEST_A7, kvm_vcpu_arch, guest_context.a7); + OFFSET(KVM_ARCH_GUEST_S2, kvm_vcpu_arch, guest_context.s2); + OFFSET(KVM_ARCH_GUEST_S3, kvm_vcpu_arch, guest_context.s3); + OFFSET(KVM_ARCH_GUEST_S4, kvm_vcpu_arch, guest_context.s4); + OFFSET(KVM_ARCH_GUEST_S5, kvm_vcpu_arch, guest_context.s5); + OFFSET(KVM_ARCH_GUEST_S6, kvm_vcpu_arch, guest_context.s6); + OFFSET(KVM_ARCH_GUEST_S7, kvm_vcpu_arch, guest_context.s7); + OFFSET(KVM_ARCH_GUEST_S8, kvm_vcpu_arch, guest_context.s8); + OFFSET(KVM_ARCH_GUEST_S9, kvm_vcpu_arch, guest_context.s9); + OFFSET(KVM_ARCH_GUEST_S10, kvm_vcpu_arch, guest_context.s10); + OFFSET(KVM_ARCH_GUEST_S11, kvm_vcpu_arch, guest_context.s11); + OFFSET(KVM_ARCH_GUEST_T3, kvm_vcpu_arch, guest_context.t3); + OFFSET(KVM_ARCH_GUEST_T4, kvm_vcpu_arch, guest_context.t4); + OFFSET(KVM_ARCH_GUEST_T5, kvm_vcpu_arch, guest_context.t5); + OFFSET(KVM_ARCH_GUEST_T6, kvm_vcpu_arch, guest_context.t6); + OFFSET(KVM_ARCH_GUEST_SEPC, kvm_vcpu_arch, guest_context.sepc); + OFFSET(KVM_ARCH_GUEST_SSTATUS, kvm_vcpu_arch, guest_context.sstatus); + OFFSET(KVM_ARCH_GUEST_HSTATUS, kvm_vcpu_arch, guest_context.hstatus); + OFFSET(KVM_ARCH_GUEST_SCOUNTEREN, kvm_vcpu_arch, guest_csr.scounteren); + + OFFSET(KVM_ARCH_HOST_ZERO, kvm_vcpu_arch, host_context.zero); + OFFSET(KVM_ARCH_HOST_RA, kvm_vcpu_arch, host_context.ra); + OFFSET(KVM_ARCH_HOST_SP, kvm_vcpu_arch, host_context.sp); + OFFSET(KVM_ARCH_HOST_GP, kvm_vcpu_arch, host_context.gp); + OFFSET(KVM_ARCH_HOST_TP, kvm_vcpu_arch, host_context.tp); + OFFSET(KVM_ARCH_HOST_T0, kvm_vcpu_arch, host_context.t0); + OFFSET(KVM_ARCH_HOST_T1, kvm_vcpu_arch, host_context.t1); + OFFSET(KVM_ARCH_HOST_T2, kvm_vcpu_arch, host_context.t2); + OFFSET(KVM_ARCH_HOST_S0, kvm_vcpu_arch, host_context.s0); + OFFSET(KVM_ARCH_HOST_S1, kvm_vcpu_arch, host_context.s1); + OFFSET(KVM_ARCH_HOST_A0, kvm_vcpu_arch, host_context.a0); + OFFSET(KVM_ARCH_HOST_A1, kvm_vcpu_arch, host_context.a1); + OFFSET(KVM_ARCH_HOST_A2, kvm_vcpu_arch, host_context.a2); + OFFSET(KVM_ARCH_HOST_A3, kvm_vcpu_arch, host_context.a3); + OFFSET(KVM_ARCH_HOST_A4, kvm_vcpu_arch, host_context.a4); + OFFSET(KVM_ARCH_HOST_A5, kvm_vcpu_arch, host_context.a5); + OFFSET(KVM_ARCH_HOST_A6, kvm_vcpu_arch, host_context.a6); + OFFSET(KVM_ARCH_HOST_A7, kvm_vcpu_arch, host_context.a7); + OFFSET(KVM_ARCH_HOST_S2, kvm_vcpu_arch, host_context.s2); + OFFSET(KVM_ARCH_HOST_S3, kvm_vcpu_arch, host_context.s3); + OFFSET(KVM_ARCH_HOST_S4, kvm_vcpu_arch, host_context.s4); + OFFSET(KVM_ARCH_HOST_S5, kvm_vcpu_arch, host_context.s5); + OFFSET(KVM_ARCH_HOST_S6, kvm_vcpu_arch, host_context.s6); + OFFSET(KVM_ARCH_HOST_S7, kvm_vcpu_arch, host_context.s7); + OFFSET(KVM_ARCH_HOST_S8, kvm_vcpu_arch, host_context.s8); + OFFSET(KVM_ARCH_HOST_S9, kvm_vcpu_arch, host_context.s9); + OFFSET(KVM_ARCH_HOST_S10, kvm_vcpu_arch, host_context.s10); + OFFSET(KVM_ARCH_HOST_S11, kvm_vcpu_arch, host_context.s11); + OFFSET(KVM_ARCH_HOST_T3, kvm_vcpu_arch, host_context.t3); + OFFSET(KVM_ARCH_HOST_T4, kvm_vcpu_arch, host_context.t4); + OFFSET(KVM_ARCH_HOST_T5, kvm_vcpu_arch, host_context.t5); + OFFSET(KVM_ARCH_HOST_T6, kvm_vcpu_arch, host_context.t6); + OFFSET(KVM_ARCH_HOST_SEPC, kvm_vcpu_arch, host_context.sepc); + OFFSET(KVM_ARCH_HOST_SSTATUS, kvm_vcpu_arch, host_context.sstatus); + OFFSET(KVM_ARCH_HOST_HSTATUS, kvm_vcpu_arch, host_context.hstatus); + OFFSET(KVM_ARCH_HOST_SSCRATCH, kvm_vcpu_arch, host_sscratch); + OFFSET(KVM_ARCH_HOST_STVEC, kvm_vcpu_arch, host_stvec); + OFFSET(KVM_ARCH_HOST_SCOUNTEREN, kvm_vcpu_arch, host_scounteren); + + OFFSET(KVM_ARCH_TRAP_SEPC, kvm_cpu_trap, sepc); + OFFSET(KVM_ARCH_TRAP_SCAUSE, kvm_cpu_trap, scause); + OFFSET(KVM_ARCH_TRAP_STVAL, kvm_cpu_trap, stval); + OFFSET(KVM_ARCH_TRAP_HTVAL, kvm_cpu_trap, htval); + OFFSET(KVM_ARCH_TRAP_HTINST, kvm_cpu_trap, htinst); + + /* F extension */ + + OFFSET(KVM_ARCH_FP_F_F0, kvm_cpu_context, fp.f.f[0]); + OFFSET(KVM_ARCH_FP_F_F1, kvm_cpu_context, fp.f.f[1]); + OFFSET(KVM_ARCH_FP_F_F2, kvm_cpu_context, fp.f.f[2]); + OFFSET(KVM_ARCH_FP_F_F3, kvm_cpu_context, fp.f.f[3]); + OFFSET(KVM_ARCH_FP_F_F4, kvm_cpu_context, fp.f.f[4]); + OFFSET(KVM_ARCH_FP_F_F5, kvm_cpu_context, fp.f.f[5]); + OFFSET(KVM_ARCH_FP_F_F6, kvm_cpu_context, fp.f.f[6]); + OFFSET(KVM_ARCH_FP_F_F7, kvm_cpu_context, fp.f.f[7]); + OFFSET(KVM_ARCH_FP_F_F8, kvm_cpu_context, fp.f.f[8]); + OFFSET(KVM_ARCH_FP_F_F9, kvm_cpu_context, fp.f.f[9]); + OFFSET(KVM_ARCH_FP_F_F10, kvm_cpu_context, fp.f.f[10]); + OFFSET(KVM_ARCH_FP_F_F11, kvm_cpu_context, fp.f.f[11]); + OFFSET(KVM_ARCH_FP_F_F12, kvm_cpu_context, fp.f.f[12]); + OFFSET(KVM_ARCH_FP_F_F13, kvm_cpu_context, fp.f.f[13]); + OFFSET(KVM_ARCH_FP_F_F14, kvm_cpu_context, fp.f.f[14]); + OFFSET(KVM_ARCH_FP_F_F15, kvm_cpu_context, fp.f.f[15]); + OFFSET(KVM_ARCH_FP_F_F16, kvm_cpu_context, fp.f.f[16]); + OFFSET(KVM_ARCH_FP_F_F17, kvm_cpu_context, fp.f.f[17]); + OFFSET(KVM_ARCH_FP_F_F18, kvm_cpu_context, fp.f.f[18]); + OFFSET(KVM_ARCH_FP_F_F19, kvm_cpu_context, fp.f.f[19]); + OFFSET(KVM_ARCH_FP_F_F20, kvm_cpu_context, fp.f.f[20]); + OFFSET(KVM_ARCH_FP_F_F21, kvm_cpu_context, fp.f.f[21]); + OFFSET(KVM_ARCH_FP_F_F22, kvm_cpu_context, fp.f.f[22]); + OFFSET(KVM_ARCH_FP_F_F23, kvm_cpu_context, fp.f.f[23]); + OFFSET(KVM_ARCH_FP_F_F24, kvm_cpu_context, fp.f.f[24]); + OFFSET(KVM_ARCH_FP_F_F25, kvm_cpu_context, fp.f.f[25]); + OFFSET(KVM_ARCH_FP_F_F26, kvm_cpu_context, fp.f.f[26]); + OFFSET(KVM_ARCH_FP_F_F27, kvm_cpu_context, fp.f.f[27]); + OFFSET(KVM_ARCH_FP_F_F28, kvm_cpu_context, fp.f.f[28]); + OFFSET(KVM_ARCH_FP_F_F29, kvm_cpu_context, fp.f.f[29]); + OFFSET(KVM_ARCH_FP_F_F30, kvm_cpu_context, fp.f.f[30]); + OFFSET(KVM_ARCH_FP_F_F31, kvm_cpu_context, fp.f.f[31]); + OFFSET(KVM_ARCH_FP_F_FCSR, kvm_cpu_context, fp.f.fcsr); + + /* D extension */ + + OFFSET(KVM_ARCH_FP_D_F0, kvm_cpu_context, fp.d.f[0]); + OFFSET(KVM_ARCH_FP_D_F1, kvm_cpu_context, fp.d.f[1]); + OFFSET(KVM_ARCH_FP_D_F2, kvm_cpu_context, fp.d.f[2]); + OFFSET(KVM_ARCH_FP_D_F3, kvm_cpu_context, fp.d.f[3]); + OFFSET(KVM_ARCH_FP_D_F4, kvm_cpu_context, fp.d.f[4]); + OFFSET(KVM_ARCH_FP_D_F5, kvm_cpu_context, fp.d.f[5]); + OFFSET(KVM_ARCH_FP_D_F6, kvm_cpu_context, fp.d.f[6]); + OFFSET(KVM_ARCH_FP_D_F7, kvm_cpu_context, fp.d.f[7]); + OFFSET(KVM_ARCH_FP_D_F8, kvm_cpu_context, fp.d.f[8]); + OFFSET(KVM_ARCH_FP_D_F9, kvm_cpu_context, fp.d.f[9]); + OFFSET(KVM_ARCH_FP_D_F10, kvm_cpu_context, fp.d.f[10]); + OFFSET(KVM_ARCH_FP_D_F11, kvm_cpu_context, fp.d.f[11]); + OFFSET(KVM_ARCH_FP_D_F12, kvm_cpu_context, fp.d.f[12]); + OFFSET(KVM_ARCH_FP_D_F13, kvm_cpu_context, fp.d.f[13]); + OFFSET(KVM_ARCH_FP_D_F14, kvm_cpu_context, fp.d.f[14]); + OFFSET(KVM_ARCH_FP_D_F15, kvm_cpu_context, fp.d.f[15]); + OFFSET(KVM_ARCH_FP_D_F16, kvm_cpu_context, fp.d.f[16]); + OFFSET(KVM_ARCH_FP_D_F17, kvm_cpu_context, fp.d.f[17]); + OFFSET(KVM_ARCH_FP_D_F18, kvm_cpu_context, fp.d.f[18]); + OFFSET(KVM_ARCH_FP_D_F19, kvm_cpu_context, fp.d.f[19]); + OFFSET(KVM_ARCH_FP_D_F20, kvm_cpu_context, fp.d.f[20]); + OFFSET(KVM_ARCH_FP_D_F21, kvm_cpu_context, fp.d.f[21]); + OFFSET(KVM_ARCH_FP_D_F22, kvm_cpu_context, fp.d.f[22]); + OFFSET(KVM_ARCH_FP_D_F23, kvm_cpu_context, fp.d.f[23]); + OFFSET(KVM_ARCH_FP_D_F24, kvm_cpu_context, fp.d.f[24]); + OFFSET(KVM_ARCH_FP_D_F25, kvm_cpu_context, fp.d.f[25]); + OFFSET(KVM_ARCH_FP_D_F26, kvm_cpu_context, fp.d.f[26]); + OFFSET(KVM_ARCH_FP_D_F27, kvm_cpu_context, fp.d.f[27]); + OFFSET(KVM_ARCH_FP_D_F28, kvm_cpu_context, fp.d.f[28]); + OFFSET(KVM_ARCH_FP_D_F29, kvm_cpu_context, fp.d.f[29]); + OFFSET(KVM_ARCH_FP_D_F30, kvm_cpu_context, fp.d.f[30]); + OFFSET(KVM_ARCH_FP_D_F31, kvm_cpu_context, fp.d.f[31]); + OFFSET(KVM_ARCH_FP_D_FCSR, kvm_cpu_context, fp.d.fcsr); +#endif /* * THREAD_{F,X}* might be larger than a S-type offset can handle, but * these are used in performance-sensitive assembly so we can't resort @@ -500,4 +660,9 @@ void asm_offsets(void) * ensures the alignment is sane. */ DEFINE(PT_SIZE_ON_STACK, ALIGN(sizeof(struct pt_regs), STACK_ALIGN)); +#if 0 + OFFSET(KERNEL_MAP_VIRT_ADDR, kernel_mapping, virt_addr); +#endif + OFFSET(SBI_HART_BOOT_TASK_PTR_OFFSET, sbi_hart_boot_data, task_ptr); + OFFSET(SBI_HART_BOOT_STACK_PTR_OFFSET, sbi_hart_boot_data, stack_ptr); } diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c index 685fae72b7f5..2e16f6732cdf 100644 --- a/arch/riscv/kernel/cpu_ops_sbi.c +++ b/arch/riscv/kernel/cpu_ops_sbi.c @@ -7,13 +7,22 @@ #include #include +#include #include +#include #include #include extern char secondary_start_sbi[]; const struct cpu_operations cpu_ops_sbi; +/* + * Ordered booting via HSM brings one cpu at a time. However, cpu hotplug can + * be invoked from multiple threads in parallel. Define a per cpu data + * to handle that. + */ +DEFINE_PER_CPU(struct sbi_hart_boot_data, boot_data); + static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr, unsigned long priv) { @@ -55,14 +64,19 @@ static int sbi_hsm_hart_get_status(unsigned long hartid) static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle) { - int rc; unsigned long boot_addr = __pa_symbol(secondary_start_sbi); int hartid = cpuid_to_hartid_map(cpuid); - - cpu_update_secondary_bootdata(cpuid, tidle); - rc = sbi_hsm_hart_start(hartid, boot_addr, 0); - - return rc; + unsigned long hsm_data; + struct sbi_hart_boot_data *bdata = &per_cpu(boot_data, cpuid); + + /* Make sure tidle is updated */ + smp_mb(); + bdata->task_ptr = tidle; + bdata->stack_ptr = task_stack_page(tidle) + THREAD_SIZE; + /* Make sure boot data is updated */ + smp_mb(); + hsm_data = __pa(bdata); + return sbi_hsm_hart_start(hartid, boot_addr, hsm_data); } static int sbi_cpu_prepare(unsigned int cpuid) @@ -97,7 +111,7 @@ static int sbi_cpu_is_stopped(unsigned int cpuid) rc = sbi_hsm_hart_get_status(hartid); - if (rc == SBI_HSM_HART_STATUS_STOPPED) + if (rc == SBI_HSM_STATE_STOPPED) return 0; return rc; } diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index 6c11fd130a0f..acafb4cf4b0d 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include "efi-header.S" @@ -67,7 +68,8 @@ pe_head_start: .align 2 #ifdef CONFIG_MMU -relocate: + .global relocate_enable_mmu +relocate_enable_mmu: /* Relocate return address */ li a1, PAGE_OFFSET la a2, _start @@ -142,13 +144,15 @@ secondary_start_sbi: la a3, .Lsecondary_park csrw CSR_TVEC, a3 - slli a3, a0, LGREG - la a4, __cpu_up_stack_pointer - la a5, __cpu_up_task_pointer - add a4, a3, a4 - add a5, a3, a5 - REG_L sp, (a4) - REG_L tp, (a5) + /* a0 contains the hartid & a1 contains boot data */ + li a2, SBI_HART_BOOT_TASK_PTR_OFFSET + XIP_FIXUP_OFFSET a2 + add a2, a2, a1 + REG_L tp, (a2) + li a3, SBI_HART_BOOT_STACK_PTR_OFFSET + XIP_FIXUP_OFFSET a3 + add a3, a3, a1 + REG_L sp, (a3) .global secondary_start_common secondary_start_common: @@ -156,7 +160,8 @@ secondary_start_common: #ifdef CONFIG_MMU /* Enable virtual memory and relocate to virtual address */ la a0, swapper_pg_dir - call relocate + XIP_FIXUP_OFFSET a0 + call relocate_enable_mmu #endif call setup_trap_vector tail smp_callin @@ -266,7 +271,8 @@ clear_bss_done: call setup_vm #ifdef CONFIG_MMU la a0, early_pg_dir - call relocate + XIP_FIXUP_OFFSET a0 + call relocate_enable_mmu #endif /* CONFIG_MMU */ call setup_trap_vector diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_callchain.c index e0cf246d4864..a6c8b8c304b3 100644 --- a/arch/riscv/kernel/perf_callchain.c +++ b/arch/riscv/kernel/perf_callchain.c @@ -77,7 +77,7 @@ void perf_callchain_user(struct perf_callchain_entry_ctx *entry, bool fill_callchain(unsigned long pc, unsigned long regs, void *entry) { - return perf_callchain_store(entry, pc) == 0; + return perf_callchain_store(entry, pc); } void notrace walk_stackframe(struct task_struct *task, diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 0d6f79dc36c0..8ad2ab08da01 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -23,6 +23,7 @@ #include #include #include +#include register unsigned long gp_in_global __asm__("gp"); @@ -37,7 +38,7 @@ extern asmlinkage void ret_from_kernel_thread(void); void arch_cpu_idle(void) { - wait_for_interrupt(); + cpu_do_idle(); raw_local_irq_enable(); } diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 85c81d65f694..bd82edbbd1d2 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -97,6 +97,7 @@ static int riscv_vr_get(struct task_struct *target, struct __riscv_v_state *vstate = &target->thread.vstate; membuf_write(&to, vstate, offsetof(struct __riscv_v_state, vtype)); + membuf_store(&to, vstate->vtype); return membuf_zero(&to, 4); // explicitly pad } diff --git a/arch/riscv/kernel/suspend.c b/arch/riscv/kernel/suspend.c new file mode 100644 index 000000000000..9ba24fb8cc93 --- /dev/null +++ b/arch/riscv/kernel/suspend.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#include +#include +#include + +static void suspend_save_csrs(struct suspend_context *context) +{ + context->scratch = csr_read(CSR_SCRATCH); + context->tvec = csr_read(CSR_TVEC); + context->ie = csr_read(CSR_IE); + + /* + * No need to save/restore IP CSR (i.e. MIP or SIP) because: + * + * 1. For no-MMU (M-mode) kernel, the bits in MIP are set by + * external devices (such as interrupt controller, timer, etc). + * 2. For MMU (S-mode) kernel, the bits in SIP are set by + * M-mode firmware and external devices (such as interrupt + * controller, etc). + */ + +#ifdef CONFIG_MMU + context->satp = csr_read(CSR_SATP); +#endif +} + +static void suspend_restore_csrs(struct suspend_context *context) +{ + csr_write(CSR_SCRATCH, context->scratch); + csr_write(CSR_TVEC, context->tvec); + csr_write(CSR_IE, context->ie); + +#ifdef CONFIG_MMU + csr_write(CSR_SATP, context->satp); +#endif +} + +int cpu_suspend(unsigned long arg, + int (*finish)(unsigned long arg, + unsigned long entry, + unsigned long context)) +{ + int rc = 0; + struct suspend_context context = { 0 }; + + /* Finisher should be non-NULL */ + if (!finish) + return -EINVAL; + + /* Save additional CSRs*/ + suspend_save_csrs(&context); + + /* + * Function graph tracer state gets incosistent when the kernel + * calls functions that never return (aka finishers) hence disable + * graph tracing during their execution. + */ + pause_graph_tracing(); + + /* Save context on stack */ + if (__cpu_suspend_enter(&context)) { + /* Call the finisher */ + rc = finish(arg, __pa_symbol(__cpu_resume_enter), + (ulong)&context); + + /* + * Should never reach here, unless the suspend finisher + * fails. Successful cpu_suspend() should return from + * __cpu_resume_entry() + */ + if (!rc) + rc = -EOPNOTSUPP; + } + + /* Enable function graph tracer */ + unpause_graph_tracing(); + + /* Restore additional CSRs */ + suspend_restore_csrs(&context); + + return rc; +} diff --git a/arch/riscv/kernel/suspend_entry.S b/arch/riscv/kernel/suspend_entry.S new file mode 100644 index 000000000000..4b07b809a2b8 --- /dev/null +++ b/arch/riscv/kernel/suspend_entry.S @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#include +#include +#include +#include + + .text + .altmacro + .option norelax + +ENTRY(__cpu_suspend_enter) + /* Save registers (except A0 and T0-T6) */ + REG_S ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0) + REG_S sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0) + REG_S gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0) + REG_S tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0) + REG_S s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0) + REG_S s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0) + REG_S a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0) + REG_S a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0) + REG_S a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0) + REG_S a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0) + REG_S a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0) + REG_S a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0) + REG_S a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0) + REG_S s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0) + REG_S s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0) + REG_S s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0) + REG_S s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0) + REG_S s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0) + REG_S s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0) + REG_S s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0) + REG_S s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0) + REG_S s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0) + REG_S s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0) + + /* Save CSRs */ + csrr t0, CSR_EPC + REG_S t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0) + csrr t0, CSR_STATUS + REG_S t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0) + csrr t0, CSR_TVAL + REG_S t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0) + csrr t0, CSR_CAUSE + REG_S t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0) + + /* Return non-zero value */ + li a0, 1 + + /* Return to C code */ + ret +END(__cpu_suspend_enter) + +ENTRY(__cpu_resume_enter) + /* Load the global pointer */ + .option push + .option norelax + la gp, __global_pointer$ + .option pop + +#ifdef CONFIG_MMU + /* Save A0 and A1 */ + add t0, a0, zero + add t1, a1, zero + + /* Enable MMU */ + la a0, swapper_pg_dir + XIP_FIXUP_OFFSET a0 + call relocate_enable_mmu + + /* Restore A0 and A1 */ + add a0, t0, zero + add a1, t1, zero +#endif + + /* Make A0 point to suspend context */ + add a0, a1, zero + + /* Restore CSRs */ + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0) + csrw CSR_EPC, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0) + csrw CSR_STATUS, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0) + csrw CSR_TVAL, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0) + csrw CSR_CAUSE, t0 + + /* Restore registers (except A0 and T0-T6) */ + REG_L ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0) + REG_L sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0) + REG_L gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0) + REG_L tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0) + REG_L s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0) + REG_L s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0) + REG_L a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0) + REG_L a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0) + REG_L a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0) + REG_L a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0) + REG_L a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0) + REG_L a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0) + REG_L a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0) + REG_L s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0) + REG_L s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0) + REG_L s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0) + REG_L s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0) + REG_L s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0) + REG_L s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0) + REG_L s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0) + REG_L s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0) + REG_L s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0) + REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0) + + /* Return zero value */ + add a0, zero, zero + + /* Return to C code */ + ret +END(__cpu_resume_enter) diff --git a/arch/riscv/kvm/vcpu_sbi_hsm.c b/arch/riscv/kvm/vcpu_sbi_hsm.c new file mode 100644 index 000000000000..1ac4b2e8e4ec --- /dev/null +++ b/arch/riscv/kvm/vcpu_sbi_hsm.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * Authors: + * Atish Patra + */ + +#include +#include +#include +#include +#include +#include + +static int kvm_sbi_hsm_vcpu_start(struct kvm_vcpu *vcpu) +{ + struct kvm_cpu_context *reset_cntx; + struct kvm_cpu_context *cp = &vcpu->arch.guest_context; + struct kvm_vcpu *target_vcpu; + unsigned long target_vcpuid = cp->a0; + + target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid); + if (!target_vcpu) + return -EINVAL; + if (!target_vcpu->arch.power_off) + return -EALREADY; + + reset_cntx = &target_vcpu->arch.guest_reset_context; + /* start address */ + reset_cntx->sepc = cp->a1; + /* target vcpu id to start */ + reset_cntx->a0 = target_vcpuid; + /* private data passed from kernel */ + reset_cntx->a1 = cp->a2; + kvm_make_request(KVM_REQ_VCPU_RESET, target_vcpu); + + kvm_riscv_vcpu_power_on(target_vcpu); + + return 0; +} + +static int kvm_sbi_hsm_vcpu_stop(struct kvm_vcpu *vcpu) +{ + if (vcpu->arch.power_off) + return -EINVAL; + + kvm_riscv_vcpu_power_off(vcpu); + + return 0; +} + +static int kvm_sbi_hsm_vcpu_get_status(struct kvm_vcpu *vcpu) +{ + struct kvm_cpu_context *cp = &vcpu->arch.guest_context; + unsigned long target_vcpuid = cp->a0; + struct kvm_vcpu *target_vcpu; + + target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid); + if (!target_vcpu) + return -EINVAL; + if (!target_vcpu->arch.power_off) + return SBI_HSM_STATE_STARTED; + else + return SBI_HSM_STATE_STOPPED; +} + +static int kvm_sbi_ext_hsm_handler(struct kvm_vcpu *vcpu, struct kvm_run *run, + unsigned long *out_val, + struct kvm_cpu_trap *utrap, + bool *exit) +{ + int ret = 0; + struct kvm_cpu_context *cp = &vcpu->arch.guest_context; + struct kvm *kvm = vcpu->kvm; + unsigned long funcid = cp->a6; + + switch (funcid) { + case SBI_EXT_HSM_HART_START: + mutex_lock(&kvm->lock); + ret = kvm_sbi_hsm_vcpu_start(vcpu); + mutex_unlock(&kvm->lock); + break; + case SBI_EXT_HSM_HART_STOP: + ret = kvm_sbi_hsm_vcpu_stop(vcpu); + break; + case SBI_EXT_HSM_HART_STATUS: + ret = kvm_sbi_hsm_vcpu_get_status(vcpu); + if (ret >= 0) { + *out_val = ret; + ret = 0; + } + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm = { + .extid_start = SBI_EXT_HSM, + .extid_end = SBI_EXT_HSM, + .handler = kvm_sbi_ext_hsm_handler, +}; diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index a8b853880368..c8dc775dbaf2 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -1385,14 +1385,13 @@ compress_again: __GFP_KSWAPD_RECLAIM | __GFP_NOWARN | __GFP_HIGHMEM | - __GFP_MOVABLE | - __GFP_CMA); + __GFP_MOVABLE); if (!handle) { zcomp_stream_put(zram->comp); atomic64_inc(&zram->stats.writestall); handle = zs_malloc(zram->mem_pool, comp_len, GFP_NOIO | __GFP_HIGHMEM | - __GFP_MOVABLE | __GFP_CMA); + __GFP_MOVABLE); if (handle) goto compress_again; return -ENOMEM; diff --git a/drivers/clk/thead/clk-light-fm.c b/drivers/clk/thead/clk-light-fm.c index 2fe47c063a53..fb33e7560454 100644 --- a/drivers/clk/thead/clk-light-fm.c +++ b/drivers/clk/thead/clk-light-fm.c @@ -50,6 +50,7 @@ static u32 share_cnt_spi_clk_en; static u32 share_cnt_uart0_clk_en; static u32 share_cnt_uart2_clk_en; static u32 share_cnt_i2c2_clk_en; +static u32 share_cnt_i2c3_clk_en; static u32 share_cnt_peri_i2s_clk_en; static u32 share_cnt_qspi1_clk_en; static u32 share_cnt_uart1_clk_en; @@ -378,31 +379,31 @@ static int light_clocks_probe(struct platform_device *pdev) clks[AONSYS_BUS_CLK] = thead_clk_fixed("aonsys_hclk", 101606400); //from sys_pll, maybe change ? /* Light Fullmask AP MUX */ - clks[CPU_PLL0_BYPASS] = thead_light_clk_mux_flags("cpu_pll0_bypass", ap_base + 0x4, 30, 1, cpu_pll0_bypass_sels, ARRAY_SIZE(cpu_pll0_bypass_sels), CLK_SET_RATE_PARENT); - clks[CPU_PLL1_BYPASS] = thead_light_clk_mux_flags("cpu_pll1_bypass", ap_base + 0x14, 30, 1, cpu_pll1_bypass_sels, ARRAY_SIZE(cpu_pll1_bypass_sels), CLK_SET_RATE_PARENT); - clks[GMAC_PLL_BYPASS] = thead_light_clk_mux_flags("gmac_pll_bypass", ap_base + 0x24, 30, 1, gmac_pll_bypass_sels, ARRAY_SIZE(gmac_pll_bypass_sels), CLK_SET_RATE_PARENT); - clks[VIDEO_PLL_BYPASS] = thead_light_clk_mux_flags("video_pll_bypass", ap_base + 0x34, 30, 1, video_pll_bypass_sels, ARRAY_SIZE(video_pll_bypass_sels), CLK_SET_RATE_PARENT); - clks[TEE_PLL_BYPASS] = thead_light_clk_mux_flags("tee_pll_bypass", ap_base + 0x64, 30, 1, tee_pll_bypass_sels, ARRAY_SIZE(tee_pll_bypass_sels), CLK_SET_RATE_PARENT); - clks[DPU0_PLL_BYPASS] = thead_light_clk_mux_flags("dpu0_pll_bypass", ap_base + 0x44, 30, 1, dpu0_pll_bypass_sels, ARRAY_SIZE(dpu0_pll_bypass_sels), CLK_SET_RATE_PARENT); - clks[DPU1_PLL_BYPASS] = thead_light_clk_mux_flags("dpu1_pll_bypass", ap_base + 0x54, 30, 1, dpu1_pll_bypass_sels, ARRAY_SIZE(dpu1_pll_bypass_sels), CLK_SET_RATE_PARENT); - - clks[AHB2_CPUSYS_HCLK] = thead_light_clk_mux_flags("ahb2_cpusys_hclk", ap_base + 0x120, 5, 1, ahb2_cpusys_hclk_sels, ARRAY_SIZE(ahb2_cpusys_hclk_sels), CLK_SET_RATE_PARENT); - clks[C910_CCLK_I0] = thead_light_clk_mux_flags("c910_cclk_i0", ap_base + 0x100, 1, 1, c910_cclk_i0_sels, ARRAY_SIZE(c910_cclk_i0_sels), CLK_SET_RATE_PARENT); - clks[C910_CCLK] = thead_light_clk_mux_flags("c910_cclk", ap_base + 0x100, 0, 1, c910_cclk_sels, ARRAY_SIZE(c910_cclk_sels), CLK_SET_RATE_PARENT); - clks[CFG_AXI_ACLK] = thead_light_clk_mux_flags("cfg_axi_aclk", ap_base + 0x138, 5, 1, cfg_axi_aclk_sels, ARRAY_SIZE(cfg_axi_aclk_sels), CLK_SET_RATE_PARENT); + clks[CPU_PLL0_BYPASS] = thead_light_clk_mux_flags("cpu_pll0_bypass", ap_base + 0x4, 30, 1, cpu_pll0_bypass_sels, ARRAY_SIZE(cpu_pll0_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[CPU_PLL1_BYPASS] = thead_light_clk_mux_flags("cpu_pll1_bypass", ap_base + 0x14, 30, 1, cpu_pll1_bypass_sels, ARRAY_SIZE(cpu_pll1_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[GMAC_PLL_BYPASS] = thead_light_clk_mux_flags("gmac_pll_bypass", ap_base + 0x24, 30, 1, gmac_pll_bypass_sels, ARRAY_SIZE(gmac_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[VIDEO_PLL_BYPASS] = thead_light_clk_mux_flags("video_pll_bypass", ap_base + 0x34, 30, 1, video_pll_bypass_sels, ARRAY_SIZE(video_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[TEE_PLL_BYPASS] = thead_light_clk_mux_flags("tee_pll_bypass", ap_base + 0x64, 30, 1, tee_pll_bypass_sels, ARRAY_SIZE(tee_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[DPU0_PLL_BYPASS] = thead_light_clk_mux_flags("dpu0_pll_bypass", ap_base + 0x44, 30, 1, dpu0_pll_bypass_sels, ARRAY_SIZE(dpu0_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[DPU1_PLL_BYPASS] = thead_light_clk_mux_flags("dpu1_pll_bypass", ap_base + 0x54, 30, 1, dpu1_pll_bypass_sels, ARRAY_SIZE(dpu1_pll_bypass_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + + clks[AHB2_CPUSYS_HCLK] = thead_light_clk_mux_flags("ahb2_cpusys_hclk", ap_base + 0x120, 5, 1, ahb2_cpusys_hclk_sels, ARRAY_SIZE(ahb2_cpusys_hclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[C910_CCLK_I0] = thead_light_clk_mux_flags("c910_cclk_i0", ap_base + 0x100, 1, 1, c910_cclk_i0_sels, ARRAY_SIZE(c910_cclk_i0_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[C910_CCLK] = thead_light_clk_mux_flags("c910_cclk", ap_base + 0x100, 0, 1, c910_cclk_sels, ARRAY_SIZE(c910_cclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[CFG_AXI_ACLK] = thead_light_clk_mux_flags("cfg_axi_aclk", ap_base + 0x138, 5, 1, cfg_axi_aclk_sels, ARRAY_SIZE(cfg_axi_aclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); if (teesys) - clks[TEESYS_HCLK] = thead_light_clk_mux_flags("teesys_hclk", ap_base + 0x1cc, 13, 1, teesys_hclk_sels, ARRAY_SIZE(teesys_hclk_sels), CLK_SET_RATE_PARENT); //just for teesys!!! - - clks[PERISYS_AHB_HCLK] = thead_light_clk_mux_flags("perisys_ahb_hclk", ap_base + 0x140, 5, 1, perisys_ahb_hclk_sels, ARRAY_SIZE(perisys_ahb_hclk_sels), CLK_SET_RATE_PARENT); - clks[CLK_OUT_1] = thead_light_clk_mux_flags("clk_out_1", ap_base + 0x1b4, 4, 1, clk_out_1_sels, ARRAY_SIZE(clk_out_1_sels), CLK_SET_RATE_PARENT); - clks[CLK_OUT_2] = thead_light_clk_mux_flags("clk_out_2", ap_base + 0x1b8, 4, 1, clk_out_2_sels, ARRAY_SIZE(clk_out_2_sels), CLK_SET_RATE_PARENT); - clks[CLK_OUT_3] = thead_light_clk_mux_flags("clk_out_3", ap_base + 0x1bc, 4, 1, clk_out_3_sels, ARRAY_SIZE(clk_out_3_sels), CLK_SET_RATE_PARENT); - clks[CLK_OUT_4] = thead_light_clk_mux_flags("clk_out_4", ap_base + 0x1c0, 4, 1, clk_out_4_sels, ARRAY_SIZE(clk_out_4_sels), CLK_SET_RATE_PARENT); - clks[PERI_I2S_SRC_CLK] = thead_light_clk_mux_flags("peri_i2s_src_clk", ap_base + 0x1f0, 0, 1, peri_i2s_src_clk_sels, ARRAY_SIZE(peri_i2s_src_clk_sels), CLK_SET_RATE_PARENT); + clks[TEESYS_HCLK] = thead_light_clk_mux_flags("teesys_hclk", ap_base + 0x1cc, 13, 1, teesys_hclk_sels, ARRAY_SIZE(teesys_hclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); //just for teesys!!! + + clks[PERISYS_AHB_HCLK] = thead_light_clk_mux_flags("perisys_ahb_hclk", ap_base + 0x140, 5, 1, perisys_ahb_hclk_sels, ARRAY_SIZE(perisys_ahb_hclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[CLK_OUT_1] = thead_light_clk_mux_flags("clk_out_1", ap_base + 0x1b4, 4, 1, clk_out_1_sels, ARRAY_SIZE(clk_out_1_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[CLK_OUT_2] = thead_light_clk_mux_flags("clk_out_2", ap_base + 0x1b8, 4, 1, clk_out_2_sels, ARRAY_SIZE(clk_out_2_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[CLK_OUT_3] = thead_light_clk_mux_flags("clk_out_3", ap_base + 0x1bc, 4, 1, clk_out_3_sels, ARRAY_SIZE(clk_out_3_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[CLK_OUT_4] = thead_light_clk_mux_flags("clk_out_4", ap_base + 0x1c0, 4, 1, clk_out_4_sels, ARRAY_SIZE(clk_out_4_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[PERI_I2S_SRC_CLK] = thead_light_clk_mux_flags("peri_i2s_src_clk", ap_base + 0x1f0, 0, 1, peri_i2s_src_clk_sels, ARRAY_SIZE(peri_i2s_src_clk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); clks[NPU_CCLK] = thead_light_clk_mux_flags("npu_cclk", ap_base + 0x1c8, 6, 1, npu_cclk_sels, ARRAY_SIZE(npu_cclk_sels), CLK_SET_RATE_PARENT); - clks[CFG_APB_PCLK] = thead_light_clk_mux_flags("cfg_apb_pclk", ap_base + 0x1c4, 7, 1, cfg_apb_pclk_sels, ARRAY_SIZE(cfg_apb_pclk_sels), CLK_SET_RATE_PARENT); - clks[UART_SCLK] = thead_light_clk_mux_flags("uart_sclk", ap_base + 0x210, 0, 1, uart_sclk_sels, ARRAY_SIZE(uart_sclk_sels), CLK_SET_RATE_PARENT); + clks[CFG_APB_PCLK] = thead_light_clk_mux_flags("cfg_apb_pclk", ap_base + 0x1c4, 7, 1, cfg_apb_pclk_sels, ARRAY_SIZE(cfg_apb_pclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); + clks[UART_SCLK] = thead_light_clk_mux_flags("uart_sclk", ap_base + 0x210, 0, 1, uart_sclk_sels, ARRAY_SIZE(uart_sclk_sels), CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); /* Light Fullmask AP Divider */ clks[AHB2_CPUSYS_HCLK_OUT_DIV] = thead_clk_light_divider("ahb2_cpusys_hclk_out_div", "gmac_pll_fout1ph0", ap_base + 0x120, 0, 3, 4, MUX_TYPE_DIV, 2, 7); @@ -436,7 +437,7 @@ static int light_clocks_probe(struct platform_device *pdev) /* Light Fullmask PLL FOUT */ clks[GMAC_PLL_FOUT1PH0] = thead_light_clk_fixed_factor("gmac_pll_fout1ph0", "gmac_pll_bypass", 1, 2); clks[GMAC_PLL_FOUT4] = thead_light_clk_fixed_factor("gmac_pll_fout4", "gmac_pll_bypass", 1, 8); - clks[VIDEO_PLL_FOUT1PH0] = thead_light_clk_fixed_factor("video_pll_fout1ph0", "video_pll_bybass", 1, 2); + clks[VIDEO_PLL_FOUT1PH0] = thead_light_clk_fixed_factor("video_pll_fout1ph0", "video_pll_bypass", 1, 2); clks[VIDEO_PLL_FOUT4] = thead_light_clk_fixed_factor("video_pll_fout4", "video_pll_bypass", 1, 8); clks[TEE_PLL_FOUT4] = thead_light_clk_fixed_factor("tee_pll_fout4", "tee_pll_bypass", 1, 8); clks[CPU_PLL0_FOUT4] = thead_light_clk_fixed_factor("cpu_pll0_fout4", "cpu_pll0_bypass", 1, 8); @@ -450,7 +451,7 @@ static int light_clocks_probe(struct platform_device *pdev) clks[QSPI0_SSI_CLK] = thead_light_clk_fixed_factor("qspi0_ssi_clk", "qspi_ssi_clk", 1, 1); clks[QSPI1_SSI_CLK] = thead_light_clk_fixed_factor("qspi1_ssi_clk", "video_pll_fout1ph0", 1, 1); clks[SPI_SSI_CLK] = thead_light_clk_fixed_factor("spi_ssi_clk", "video_pll_fout1ph0", 1, 1); - clks[EMMC_SDIO_REF_CLK] = thead_light_clk_fixed_factor("emmc_sdio_ref_clk", "video_pll_foutpostdiv", 1, 1); /* Note: no mux to select, use default value */ + clks[EMMC_SDIO_REF_CLK] = thead_light_clk_fixed_factor("emmc_sdio_ref_clk", "video_pll_foutpostdiv", 1, 4); /* Note: base clk is div 4 to 198M*/ clks[PWM_CCLK] = thead_light_clk_fixed_factor("pwm_cclk", "osc_24m", 1, 1); clks[CHIP_DBG_CCLK] = thead_light_clk_fixed_factor("chip_dbg_cclk", "osc_24m", 1, 1); clks[GMAC_CCLK] = thead_light_clk_fixed_factor("gmac_cclk", "gmac_pll_fout1ph0", 1, 1); @@ -568,8 +569,8 @@ static int light_clocks_probe(struct platform_device *pdev) clks[CLKGEN_UART2_SCLK] = thead_clk_light_gate_shared("clkgen_uart2_sclk", "uart_sclk", ap_base + 0x204, 12, &share_cnt_uart2_clk_en); clks[CLKGEN_I2C2_PCLK] = thead_clk_light_gate_shared("clkgen_i2c2_pclk", "perisys_apb_pclk", ap_base + 0x204, 3, &share_cnt_i2c2_clk_en); clks[CLKGEN_I2C2_IC_CLK] = thead_clk_light_gate_shared("clkgen_i2c2_ic_clk", "i2c_ic_clk", ap_base + 0x204, 3, &share_cnt_i2c2_clk_en); - clks[CLKGEN_I2C3_PCLK] = thead_clk_light_gate_shared("clkgen_i2c3_pclk", "perisys_apb_pclk", ap_base + 0x204, 2, &share_cnt_i2c2_clk_en); - clks[CLKGEN_I2C3_IC_CLK] = thead_clk_light_gate_shared("clkgen_i2c3_ic_clk", "i2c_ic_clk", ap_base + 0x204, 2, &share_cnt_i2c2_clk_en); + clks[CLKGEN_I2C3_PCLK] = thead_clk_light_gate_shared("clkgen_i2c3_pclk", "perisys_apb_pclk", ap_base + 0x204, 2, &share_cnt_i2c3_clk_en); + clks[CLKGEN_I2C3_IC_CLK] = thead_clk_light_gate_shared("clkgen_i2c3_ic_clk", "i2c_ic_clk", ap_base + 0x204, 2, &share_cnt_i2c3_clk_en); clks[CLKGEN_I2S_PCLK] = thead_clk_light_gate_shared("clkgen_i2s_pclk", "perisys_apb_pclk", ap_base + 0x1f0, 1, &share_cnt_peri_i2s_clk_en); clks[CLKGEN_I2S_SRC_CLK] = thead_clk_light_gate_shared("clkgen_i2s_src_clk", "peri_i2s_src_clk", ap_base + 0x1f0, 1, &share_cnt_peri_i2s_clk_en); clks[CLKGEN_QSPI1_PCLK] = thead_clk_light_gate_shared("clkgen_qspi1_pclk", "peri2sys_apb_pclk", ap_base + 0x204, 16, &share_cnt_qspi1_clk_en); diff --git a/drivers/clk/thead/clk.h b/drivers/clk/thead/clk.h index cad975e8ede4..6b6368ac0b63 100644 --- a/drivers/clk/thead/clk.h +++ b/drivers/clk/thead/clk.h @@ -111,7 +111,7 @@ static inline struct clk *thead_light_clk_mux_flags(const char *name, unsigned long flags) { return clk_register_mux(NULL, name, parents, num_parents, - flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0, + flags , reg, shift, width, 0, &thead_light_clk_lock); } #endif diff --git a/drivers/clk/thead/gate/Makefile b/drivers/clk/thead/gate/Makefile index 07be9f11aeaf..03db9d44d3bb 100644 --- a/drivers/clk/thead/gate/Makefile +++ b/drivers/clk/thead/gate/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_CLK_LIGHT_FM) += thead-gate.o visys-gate.o vpsys-gate.o vosys-gate.o dspsys-gate.o audiosys-gate.o +obj-$(CONFIG_CLK_LIGHT_FM) += thead-gate.o visys-gate.o vpsys-gate.o vosys-gate.o dspsys-gate.o audiosys-gate.o miscsys-gate.o diff --git a/drivers/clk/thead/gate/dspsys-gate.c b/drivers/clk/thead/gate/dspsys-gate.c index e68a5d4e6151..54cb54439410 100644 --- a/drivers/clk/thead/gate/dspsys-gate.c +++ b/drivers/clk/thead/gate/dspsys-gate.c @@ -20,12 +20,16 @@ static struct clk *gates[LIGHT_CLKGEN_DSPSYS_CLK_END]; static struct clk_onecell_data clk_gate_data; +static const char * const dsp0_cclk_sels[] = {"gmac_pll_foutpostdiv", "dspsys_dsp_clk"}; +static const char * const dsp1_cclk_sels[] = {"gmac_pll_foutpostdiv", "dspsys_dsp_clk"}; static int light_dspsys_clk_probe(struct platform_device *pdev) { struct regmap *dspsys_regmap, *tee_dspsys_regmap; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; + struct device_node *np_reg = of_parse_phandle(np, "dspsys-regmap", 0); + void __iomem *gate_base; int ret; dspsys_regmap = syscon_regmap_lookup_by_phandle(np, "dspsys-regmap"); @@ -39,14 +43,24 @@ static int light_dspsys_clk_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "cannot find regmap for tee dsp system register\n"); tee_dspsys_regmap = NULL; } + gate_base = of_iomap(np_reg,0); + // MUX + gates[DSPSYS_DSP0_CLK_SWITCH] = thead_light_clk_mux_flags("dspsys_dsp0_clk_switch", gate_base + 0x1c, 0, 1, dsp0_cclk_sels, ARRAY_SIZE(dsp0_cclk_sels), 0); + gates[DSPSYS_DSP1_CLK_SWITCH] = thead_light_clk_mux_flags("dspsys_dsp1_clk_switch", gate_base + 0x20, 0, 1, dsp1_cclk_sels, ARRAY_SIZE(dsp1_cclk_sels), 0); + // DIV & CDE + gates[DSPSYS_DSP_CLK] = thead_light_clk_fixed_factor("dspsys_dsp_clk", "video_pll_foutvco", 1, 3); + gates[DSPSYS_DSP0_CLK_CDE] = thead_clk_light_divider("dspsys_dsp0_clk_cde", "dspsys_dsp0_clk_switch", gate_base + 0x0, 0, 3, 4, MUX_TYPE_CDE, 0, 7); + gates[DSPSYS_DSP1_CLK_CDE] = thead_clk_light_divider("dspsys_dsp1_clk_cde", "dspsys_dsp1_clk_switch", gate_base + 0x4, 0, 3, 4, MUX_TYPE_CDE, 0, 7); + + // gate gates[CLKGEN_DSP0_PCLK] = thead_gate_clk_register("clkgen_dsp0_pclk", NULL, dspsys_regmap, 0x24, 0, GATE_NOT_SHARED, NULL, dev); gates[CLKGEN_DSP1_PCLK] = thead_gate_clk_register("clkgen_dsp1_pclk", NULL, dspsys_regmap, 0x24, 1, GATE_NOT_SHARED, NULL, dev); - gates[CLKGEN_DSP1_CCLK] = thead_gate_clk_register("clkgen_dsp1_cclk", NULL, dspsys_regmap, + gates[CLKGEN_DSP1_CCLK] = thead_gate_clk_register("clkgen_dsp1_cclk", "dspsys_dsp1_clk_cde", dspsys_regmap, 0x24, 2, GATE_NOT_SHARED, NULL, dev); - gates[CLKGEN_DSP0_CCLK] = thead_gate_clk_register("clkgen_dsp0_cclk", NULL, dspsys_regmap, + gates[CLKGEN_DSP0_CCLK] = thead_gate_clk_register("clkgen_dsp0_cclk", "dspsys_dsp0_clk_cde", dspsys_regmap, 0x24, 3, GATE_NOT_SHARED, NULL, dev); gates[CLKGEN_X2X_DSP2_ACLK_S] = thead_gate_clk_register("clkgen_x2x_dsp2_aclk_s", NULL, dspsys_regmap, 0x24, 4, GATE_NOT_SHARED, NULL, dev); diff --git a/drivers/clk/thead/gate/miscsys-gate.c b/drivers/clk/thead/gate/miscsys-gate.c new file mode 100644 index 000000000000..37a000f2d93e --- /dev/null +++ b/drivers/clk/thead/gate/miscsys-gate.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Alibaba Group Holding Limited. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clk-gate.h" +#include "../clk.h" +static struct clk *gates[CLKGEN_MISCSYS_CLK_END]; +static struct clk_onecell_data clk_gate_data; +static int light_miscsys_clk_probe(struct platform_device *pdev) +{ + struct regmap *miscsys_regmap, *tee_miscsys_regmap = NULL; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret; + + miscsys_regmap = syscon_regmap_lookup_by_phandle(np, "miscsys-regmap"); + if (IS_ERR(miscsys_regmap)) { + dev_err(&pdev->dev, "cannot find regmap for misc system register\n"); + return PTR_ERR(miscsys_regmap); + } + tee_miscsys_regmap = syscon_regmap_lookup_by_phandle(np, "tee-miscsys-regmap"); + if (IS_ERR(tee_miscsys_regmap)) { + dev_err(&pdev->dev, "cannot find regmap for tee misc system register\n"); + return PTR_ERR(tee_miscsys_regmap); + } + /* we assume that the gate clock is a root clock */ + gates[CLKGEN_MISCSYS_MISCSYS_ACLK] = thead_gate_clk_register("clkgen_missys_aclk", NULL, + miscsys_regmap, 0x100, 0, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_USB3_DRD_CLK] = thead_gate_clk_register("clkgen_usb3_drd_clk", NULL, + miscsys_regmap, 0x104, 0, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_USB3_DRD_CTRL_REF_CLK] = thead_gate_clk_register("clkgen_usb3_drd_ctrl_ref_clk", "osc_24m", + miscsys_regmap, 0x104, 1, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_USB3_DRD_PHY_REF_CLK] = thead_gate_clk_register("clkgen_usb3_drd_phy_ref_clk", "osc_24m", + miscsys_regmap, 0x104, 2, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_USB3_DRD_SUSPEND_CLK] = thead_gate_clk_register("clkgen_usb3_drd_suspend_clk", NULL, + miscsys_regmap, 0x104, 3, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_EMMC_CLK] = thead_gate_clk_register("clkgen_emmc_clk", "osc_24m", + miscsys_regmap, 0x108, 0, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_SDIO0_CLK] = thead_gate_clk_register("clkgen_sdio0_clk", "osc_24m", + miscsys_regmap, 0x10c, 0, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_SDIO1_CLK] = thead_gate_clk_register("clkgen_sdio1_clk", "osc_24m", + miscsys_regmap, 0x110, 0, GATE_NOT_SHARED, NULL, dev); + if (tee_miscsys_regmap) { + gates[CLKGEN_MISCSYS_AHB2_TEESYS_HCLK] = thead_gate_clk_register("clkgen_ahb2_teesys_hclk", NULL, + tee_miscsys_regmap, 0x120, 0, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_APB3_TEESYS_HCLK] = thead_gate_clk_register("clkgen_apb3_teesys_hclk", NULL, + tee_miscsys_regmap, 0x120, 1, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_AXI4_TEESYS_ACLK] = thead_gate_clk_register("clkgen_axi4_teesys_aclk", NULL, + tee_miscsys_regmap, 0x120, 2, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_EIP120SI_CLK] = thead_gate_clk_register("clkgen_eip120si_clk", NULL, + tee_miscsys_regmap, 0x120, 3, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_EIP120SII_CLK] = thead_gate_clk_register("clkgen_eip120sii_clk", NULL, + tee_miscsys_regmap, 0x120, 4, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_EIP120SIII_CLK] = thead_gate_clk_register("clkgen_eip120siii_clk", NULL, + tee_miscsys_regmap, 0x120, 5, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_TEEDMAC_CLK] = thead_gate_clk_register("clkgen_teedmac_clk", NULL, + tee_miscsys_regmap, 0x120, 6, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_EIP150B_HCLK] = thead_gate_clk_register("clkgen_eip150b_hclk", NULL, + tee_miscsys_regmap, 0x120, 7, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_OCRAM_HCLK] = thead_gate_clk_register("clkgen_ocram_hclk", NULL, + tee_miscsys_regmap, 0x120, 8, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_EFUSE_PCLK] = thead_gate_clk_register("clkgen_efuse_pclk", NULL, + tee_miscsys_regmap, 0x120, 9, GATE_NOT_SHARED, NULL, dev); + gates[CLKGEN_MISCSYS_TEE_SYSREG_PCLK] = thead_gate_clk_register("clkgen_tee_sysreg_pclk", NULL, + tee_miscsys_regmap, 0x120, 10, GATE_NOT_SHARED, NULL, dev); + } + clk_gate_data.clks = gates; + clk_gate_data.clk_num = ARRAY_SIZE(gates); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_gate_data); + if (ret < 0) { + dev_err(dev, "failed to register gate clks for light miscsys\n"); + goto unregister_clks; + } + dev_info(dev, "succeed to register miscsys gate clock provider\n"); + return 0; +unregister_clks: + thead_unregister_clocks(gates, ARRAY_SIZE(gates)); + return ret; +} +static const struct of_device_id miscsys_clk_gate_of_match[] = { + { .compatible = "thead,miscsys-gate-controller" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, miscsys_clk_gate_of_match); +static struct platform_driver light_miscsys_clk_driver = { + .probe = light_miscsys_clk_probe, + .driver = { + .name = "miscsys-clk-gate-provider", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(miscsys_clk_gate_of_match), + }, +}; +module_platform_driver(light_miscsys_clk_driver); +MODULE_AUTHOR("wei.liu "); +MODULE_AUTHOR("Esther.Z "); +MODULE_DESCRIPTION("Thead Light Fullmask miscsys clock gate provider"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/thead/gate/vpsys-gate.c b/drivers/clk/thead/gate/vpsys-gate.c index 78613188da70..1a01df1f2b3f 100644 --- a/drivers/clk/thead/gate/vpsys-gate.c +++ b/drivers/clk/thead/gate/vpsys-gate.c @@ -34,15 +34,18 @@ static int light_vpsys_clk_probe(struct platform_device *pdev) if (WARN_ON(IS_ERR(gate_base))) return PTR_ERR(gate_base); - /* we assume that the gate clock is a root clock */ + // DIV & CDE + gates[LIGHT_VPSYS_G2D_CCLK_DIV] = thead_clk_light_divider("light_vpsys_g2d_cclk_div", "video_pll_foutvco", gate_base + 0x30, 0, 4, 4, MUX_TYPE_DIV, 3, 9); + + /* G2D clock configuration : Completed the upward configuration of CCLK */ gates[LIGHT_VPSYS_G2D_PCLK] = thead_clk_light_gate_shared("clkgen_vpsys_g2d_pclk", NULL, gate_base + 0x20, 3, &share_cnt_g2d_clk_en); gates[LIGHT_VPSYS_G2D_ACLK] = thead_clk_light_gate_shared("clkgen_vpsys_g2d_aclk", NULL, gate_base + 0x20, 3, &share_cnt_g2d_clk_en); - gates[LIGHT_VPSYS_G2D_CCLK] = thead_clk_light_gate_shared("clkgen_vpsys_g2d_cclk", NULL, + gates[LIGHT_VPSYS_G2D_CCLK] = thead_clk_light_gate_shared("clkgen_vpsys_g2d_cclk", "light_vpsys_g2d_cclk_div", gate_base + 0x20, 3, &share_cnt_g2d_clk_en); - + /* we assume that the gate clock is a root clock */ gates[LIGHT_VPSYS_FCE_PCLK] = thead_clk_light_gate_shared("clkgen_vpsys_fce_pclk", NULL, gate_base + 0x20, 2, &share_cnt_fce_clk_en); gates[LIGHT_VPSYS_FCE_ACLK] = thead_clk_light_gate_shared("clkgen_vpsys_fce_aclk", NULL, diff --git a/drivers/cpufreq/light-mpw-cpufreq.c b/drivers/cpufreq/light-mpw-cpufreq.c index 0a7eaa60f17c..df9ef3baa377 100644 --- a/drivers/cpufreq/light-mpw-cpufreq.c +++ b/drivers/cpufreq/light-mpw-cpufreq.c @@ -40,6 +40,9 @@ enum LIGHT_MPW_CPUFREQ_CLKS { #define LIGHT_C910_BUS_CLK_DIV_RATIO_2 0x100 #define LIGHT_C910_BUS_CLK_DIV_RATIO_3 0x200 +#define LIGHT_CPU_PLL_IDX(x) (x) +#define LIGHT_CPU_PLL_COUNT 2 + static int num_clks; static struct clk_bulk_data clks[] = { { .id = "c910_cclk" }, @@ -51,6 +54,7 @@ static struct clk_bulk_data clks[] = { static struct device *cpu_dev; static struct cpufreq_frequency_table *freq_table; static unsigned int max_freq; +static unsigned int min_freq; static unsigned int transition_latency; static void __iomem *ap_sys_reg; static bool light_dvfs_sv = false; @@ -58,6 +62,40 @@ static bool light_dvfs_sv = false; static u32 *light_dvddm_volt; static u32 soc_opp_count = 0; +static int _light_get_pllid(void) +{ + int ret; + + if (!strcmp(__clk_get_name(clk_get_parent(clks[LIGHT_C910_CCLK].clk)), + __clk_get_name(clks[LIGHT_C910_CCLK_I0].clk))) // pll index 0 + ret = LIGHT_CPU_PLL_IDX(0); + else // pll index 1 + ret = LIGHT_CPU_PLL_IDX(1); + + return ret; +} + +static int _light_switch_pllid(int pllid, int target_freq) +{ + pr_debug("[%s] switchto pll[%d], freq[%u]\n", __func__, pllid, target_freq); + if (pllid == LIGHT_CPU_PLL_IDX(1)) { + clk_prepare_enable(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk); + clk_set_rate(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk, target_freq * 1000); + clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk); + udelay(1); + clk_disable_unprepare(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk); + } else { + clk_prepare_enable(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk); + clk_set_rate(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk, target_freq * 1000); + clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_C910_CCLK_I0].clk); + udelay(1); + clk_disable_unprepare(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk); + } + + return 0; +} + + static int light_set_target(struct cpufreq_policy *policy, unsigned int index) { struct dev_pm_opp *opp; @@ -140,20 +178,8 @@ static int light_set_target(struct cpufreq_policy *policy, unsigned int index) } } - if (!strcmp(__clk_get_name(clk_get_parent(clks[LIGHT_C910_CCLK].clk)), - __clk_get_name(clks[LIGHT_C910_CCLK_I0].clk))) { - clk_prepare_enable(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk); - clk_set_rate(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk, new_freq * 1000); - ret = clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk); - udelay(1); - clk_disable_unprepare(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk); - } else { - clk_prepare_enable(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk); - clk_set_rate(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk, new_freq * 1000); - ret = clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_C910_CCLK_I0].clk); - udelay(1); - clk_disable_unprepare(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk); - } + /* switch pll */ + _light_switch_pllid((_light_get_pllid()+1)&(LIGHT_CPU_PLL_COUNT-1), new_freq); /*add delay for clk-switch*/ udelay(1); @@ -200,6 +226,35 @@ static int light_set_target(struct cpufreq_policy *policy, unsigned int index) return 0; } +static int light_cpufreq_suspend(struct cpufreq_policy *policy) +{ + int ret; + int index; + + pr_debug("%s: cpu: %d, %u KHz to %u KHz\n", + __func__, policy->cpu, policy->cur, policy->suspend_freq); + + ret = cpufreq_generic_suspend(policy); + if (ret) { + pr_err("%s: failed\n", __func__); + return ret; + } + + /* + * Only CPU PLL0 would be active after STR resume. We should switch + * CPU PLL to be PLL0 after policy stopped. + */ + if (_light_get_pllid() == LIGHT_CPU_PLL_IDX(1)) + _light_switch_pllid(LIGHT_CPU_PLL_IDX(0), policy->suspend_freq); + + return 0; +} + +static int light_cpufreq_resume(struct cpufreq_policy *policy) +{ + return 0; +} + static int light_cpufreq_init(struct cpufreq_policy *policy) { policy->clk = clks[LIGHT_C910_CCLK].clk; @@ -234,7 +289,8 @@ static struct cpufreq_driver light_cpufreq_driver = { .init = light_cpufreq_init, .name = "light-cpufreq", .attr = cpufreq_generic_attr, - .suspend = cpufreq_generic_suspend, + .suspend = light_cpufreq_suspend, + .resume = light_cpufreq_resume, }; static int light_cpufreq_pm_notify(struct notifier_block *nb, @@ -274,15 +330,9 @@ static int panic_cpufreq_notifier_call(struct notifier_block *nb, * set CPU PLL1's frequency as minimum to compatible voltage * becarefull if the PLL1 is serving the cpu core, swith to PLL0 first */ - if (strcmp(__clk_get_name(clk_get_parent(clks[LIGHT_C910_CCLK].clk)), - __clk_get_name(clks[LIGHT_C910_CCLK_I0].clk))) { - pr_debug("[%s,%d]\n", __func__, __LINE__); - clk_prepare_enable(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk); - clk_set_rate(clks[LIGHT_CPU_PLL0_FOUTPOSTDIV].clk, policy->min * 1000); - udelay(1); - clk_set_parent(clks[LIGHT_C910_CCLK].clk, clks[LIGHT_C910_CCLK_I0].clk); - + if (_light_get_pllid() == LIGHT_CPU_PLL_IDX(1)) { pr_debug("[%s,%d]\n", __func__, __LINE__); + _light_switch_pllid(LIGHT_CPU_PLL_IDX(0), policy->min); } pr_debug("[%s,%d]\n", __func__, __LINE__); @@ -292,9 +342,7 @@ static int panic_cpufreq_notifier_call(struct notifier_block *nb, * set the CPU PLL1's frequency as minimum in advance, otherwise the * system may crash in crash kernel stage. */ - clk_prepare_enable(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk); - clk_set_rate(clks[LIGHT_CPU_PLL1_FOUTPOSTDIV].clk, policy->min * 1000); - udelay(1); + _light_switch_pllid(LIGHT_CPU_PLL_IDX(1), policy->min); pr_info("finish to execute cpufreq notifier callback on panic\n"); @@ -411,6 +459,7 @@ soc_opp_out: transition_latency = CPUFREQ_ETERNAL; max_freq = freq_table[--num].frequency; + min_freq = freq_table[0].frequency; ret = cpufreq_register_driver(&light_cpufreq_driver); if (ret) { diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index b7a1aa31bbd3..ff71dd662880 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -47,6 +47,10 @@ config CPU_IDLE_GOV_HALTPOLL config DT_IDLE_STATES bool +config DT_IDLE_GENPD + depends on PM_GENERIC_DOMAINS_OF + bool + menu "ARM CPU Idle Drivers" depends on ARM || ARM64 source "drivers/cpuidle/Kconfig.arm" diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 334f83e56120..be12a9ca78f0 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -27,6 +27,7 @@ config ARM_PSCI_CPUIDLE_DOMAIN bool "PSCI CPU idle Domain" depends on ARM_PSCI_CPUIDLE depends on PM_GENERIC_DOMAINS_OF + select DT_IDLE_GENPD default y help Select this to enable the PSCI based CPUidle driver to use PM domains, diff --git a/drivers/cpuidle/Kconfig.riscv b/drivers/cpuidle/Kconfig.riscv index ded4ede19adf..a5609bf07572 100644 --- a/drivers/cpuidle/Kconfig.riscv +++ b/drivers/cpuidle/Kconfig.riscv @@ -11,3 +11,13 @@ config LIGHT_CPUIDLE Select this option to enable processor idle state management through cpuidle subsystem. +config RISCV_SBI_CPUIDLE + bool "RISC-V SBI CPU idle Driver" + depends on RISCV_SBI + select DT_IDLE_STATES + select CPU_IDLE_MULTIPLE_DRIVERS + select DT_IDLE_GENPD if PM_GENERIC_DOMAINS_OF + help + Select this option to enable RISC-V SBI firmware based CPU idle + driver for RISC-V systems. This drivers also supports hierarchical + DT based layout of the idle state. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index d5d77f8d6c9f..1516ab9807ee 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -6,6 +6,7 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o +obj-$(CONFIG_DT_IDLE_GENPD) += dt_idle_genpd.o obj-$(CONFIG_ARCH_HAS_CPU_RELAX) += poll_state.o obj-$(CONFIG_HALTPOLL_CPUIDLE) += cpuidle-haltpoll.o @@ -34,6 +35,8 @@ obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o # POWERPC drivers obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o + ############################################################################### # RISC-V drivers obj-$(CONFIG_LIGHT_CPUIDLE) += cpuidle-light.o +obj-$(CONFIG_RISCV_SBI_CPUIDLE) += cpuidle-riscv-sbi.o diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index ff2c3f8e4668..755bbdfc5b82 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -47,73 +47,14 @@ static int psci_pd_power_off(struct generic_pm_domain *pd) return 0; } -static int psci_pd_parse_state_nodes(struct genpd_power_state *states, - int state_count) -{ - int i, ret; - u32 psci_state, *psci_state_buf; - - for (i = 0; i < state_count; i++) { - ret = psci_dt_parse_state_node(to_of_node(states[i].fwnode), - &psci_state); - if (ret) - goto free_state; - - psci_state_buf = kmalloc(sizeof(u32), GFP_KERNEL); - if (!psci_state_buf) { - ret = -ENOMEM; - goto free_state; - } - *psci_state_buf = psci_state; - states[i].data = psci_state_buf; - } - - return 0; - -free_state: - i--; - for (; i >= 0; i--) - kfree(states[i].data); - return ret; -} - -static int psci_pd_parse_states(struct device_node *np, - struct genpd_power_state **states, int *state_count) -{ - int ret; - - /* Parse the domain idle states. */ - ret = of_genpd_parse_idle_states(np, states, state_count); - if (ret) - return ret; - - /* Fill out the PSCI specifics for each found state. */ - ret = psci_pd_parse_state_nodes(*states, *state_count); - if (ret) - kfree(*states); - - return ret; -} - -static void psci_pd_free_states(struct genpd_power_state *states, - unsigned int state_count) -{ - int i; - - for (i = 0; i < state_count; i++) - kfree(states[i].data); - kfree(states); -} - static int psci_pd_init(struct device_node *np, bool use_osi) { struct generic_pm_domain *pd; struct psci_pd_provider *pd_provider; struct dev_power_governor *pd_gov; - struct genpd_power_state *states = NULL; int ret = -ENOMEM, state_count = 0; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); + pd = dt_idle_pd_alloc(np, psci_dt_parse_state_node); if (!pd) goto out; @@ -121,22 +62,6 @@ static int psci_pd_init(struct device_node *np, bool use_osi) if (!pd_provider) goto free_pd; - pd->name = kasprintf(GFP_KERNEL, "%pOF", np); - if (!pd->name) - goto free_pd_prov; - - /* - * Parse the domain idle states and let genpd manage the state selection - * for those being compatible with "domain-idle-state". - */ - ret = psci_pd_parse_states(np, &states, &state_count); - if (ret) - goto free_name; - - pd->free_states = psci_pd_free_states; - pd->name = kbasename(pd->name); - pd->states = states; - pd->state_count = state_count; pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN; /* Allow power off when OSI has been successfully enabled. */ @@ -149,10 +74,8 @@ static int psci_pd_init(struct device_node *np, bool use_osi) pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL; ret = pm_genpd_init(pd, pd_gov, false); - if (ret) { - psci_pd_free_states(states, state_count); - goto free_name; - } + if (ret) + goto free_pd_prov; ret = of_genpd_add_provider_simple(np, pd); if (ret) @@ -166,12 +89,10 @@ static int psci_pd_init(struct device_node *np, bool use_osi) remove_pd: pm_genpd_remove(pd); -free_name: - kfree(pd->name); free_pd_prov: kfree(pd_provider); free_pd: - kfree(pd); + dt_idle_pd_free(pd); out: pr_err("failed to init PM domain ret=%d %pOF\n", ret, np); return ret; @@ -195,30 +116,6 @@ static void psci_pd_remove(void) } } -static int psci_pd_init_topology(struct device_node *np) -{ - struct device_node *node; - struct of_phandle_args child, parent; - int ret; - - for_each_child_of_node(np, node) { - if (of_parse_phandle_with_args(node, "power-domains", - "#power-domain-cells", 0, &parent)) - continue; - - child.np = node; - child.args_count = 0; - ret = of_genpd_add_subdomain(&parent, &child); - of_node_put(parent.np); - if (ret) { - of_node_put(node); - return ret; - } - } - - return 0; -} - static bool psci_pd_try_set_osi_mode(void) { int ret; @@ -282,7 +179,7 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev) goto no_pd; /* Link genpd masters/subdomains to model the CPU topology. */ - ret = psci_pd_init_topology(np); + ret = dt_idle_pd_init_topology(np); if (ret) goto remove_pd; @@ -314,28 +211,3 @@ static int __init psci_idle_init_domains(void) return platform_driver_register(&psci_cpuidle_domain_driver); } subsys_initcall(psci_idle_init_domains); - -struct device *psci_dt_attach_cpu(int cpu) -{ - struct device *dev; - - dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), "psci"); - if (IS_ERR_OR_NULL(dev)) - return dev; - - pm_runtime_irq_safe(dev); - if (cpu_online(cpu)) - pm_runtime_get_sync(dev); - - dev_pm_syscore_device(dev, true); - - return dev; -} - -void psci_dt_detach_cpu(struct device *dev) -{ - if (IS_ERR_OR_NULL(dev)) - return; - - dev_pm_domain_detach(dev, false); -} diff --git a/drivers/cpuidle/cpuidle-psci.h b/drivers/cpuidle/cpuidle-psci.h index d8e925e84c27..4e132640ed64 100644 --- a/drivers/cpuidle/cpuidle-psci.h +++ b/drivers/cpuidle/cpuidle-psci.h @@ -10,8 +10,19 @@ void psci_set_domain_state(u32 state); int psci_dt_parse_state_node(struct device_node *np, u32 *state); #ifdef CONFIG_ARM_PSCI_CPUIDLE_DOMAIN -struct device *psci_dt_attach_cpu(int cpu); -void psci_dt_detach_cpu(struct device *dev); + +#include "dt_idle_genpd.h" + +static inline struct device *psci_dt_attach_cpu(int cpu) +{ + return dt_idle_attach_cpu(cpu, "psci"); +} + +static inline void psci_dt_detach_cpu(struct device *dev) +{ + dt_idle_detach_cpu(dev); +} + #else static inline struct device *psci_dt_attach_cpu(int cpu) { return NULL; } static inline void psci_dt_detach_cpu(struct device *dev) { } diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c new file mode 100644 index 000000000000..019eac15dd04 --- /dev/null +++ b/drivers/cpuidle/cpuidle-riscv-sbi.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RISC-V SBI CPU idle driver. + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#define pr_fmt(fmt) "cpuidle-riscv-sbi: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dt_idle_states.h" +#include "dt_idle_genpd.h" + +struct sbi_cpuidle_data { + u32 *states; + struct device *dev; +}; + +struct sbi_domain_state { + bool available; + u32 state; +}; + +static DEFINE_PER_CPU_READ_MOSTLY(struct sbi_cpuidle_data, sbi_cpuidle_data); +static DEFINE_PER_CPU(struct sbi_domain_state, domain_state); +static bool sbi_cpuidle_use_osi; +static bool sbi_cpuidle_use_cpuhp; +static bool sbi_cpuidle_pd_allow_domain_state; + +extern void arch_cpu_idle(void); + +static inline void sbi_set_domain_state(u32 state) +{ + struct sbi_domain_state *data = this_cpu_ptr(&domain_state); + + data->available = true; + data->state = state; +} + +static inline u32 sbi_get_domain_state(void) +{ + struct sbi_domain_state *data = this_cpu_ptr(&domain_state); + + return data->state; +} + +static inline void sbi_clear_domain_state(void) +{ + struct sbi_domain_state *data = this_cpu_ptr(&domain_state); + + data->available = false; +} + +static inline bool sbi_is_domain_state_available(void) +{ + struct sbi_domain_state *data = this_cpu_ptr(&domain_state); + + return data->available; +} + +/* Actual code that puts the SoC in different idle states */ +static int light_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + arch_cpu_idle(); + return index; +} + +static int sbi_suspend_finisher(unsigned long suspend_type, + unsigned long resume_addr, + unsigned long opaque) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND, + suspend_type, resume_addr, opaque, 0, 0, 0); + + return (ret.error) ? sbi_err_map_linux_errno(ret.error) : 0; +} + +static int sbi_suspend(u32 state) +{ + if (state & SBI_HSM_SUSP_NON_RET_BIT) + return cpu_suspend(state, sbi_suspend_finisher); + else + return sbi_suspend_finisher(state, 0, 0); +} + +static int sbi_cpuidle_enter_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx) +{ + u32 *states = __this_cpu_read(sbi_cpuidle_data.states); + + return CPU_PM_CPU_IDLE_ENTER_PARAM(sbi_suspend, idx, states[idx]); +} + +static int __sbi_enter_domain_idle_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx, + bool s2idle) +{ + struct sbi_cpuidle_data *data = this_cpu_ptr(&sbi_cpuidle_data); + u32 *states = data->states; + struct device *pd_dev = data->dev; + u32 state; + int ret; + + ret = cpu_pm_enter(); + if (ret) + return -1; + + /* Do runtime PM to manage a hierarchical CPU toplogy. */ + rcu_irq_enter_irqson(); + if (s2idle) + dev_pm_genpd_suspend(pd_dev); + else + pm_runtime_put_sync_suspend(pd_dev); + rcu_irq_exit_irqson(); + + if (sbi_is_domain_state_available()) + state = sbi_get_domain_state(); + else + state = states[idx]; + + ret = sbi_suspend(state) ? -1 : idx; + + rcu_irq_enter_irqson(); + if (s2idle) + dev_pm_genpd_resume(pd_dev); + else + pm_runtime_get_sync(pd_dev); + rcu_irq_exit_irqson(); + + cpu_pm_exit(); + + /* Clear the domain state to start fresh when back from idle. */ + sbi_clear_domain_state(); + return ret; +} + +static int sbi_enter_domain_idle_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx) +{ + return __sbi_enter_domain_idle_state(dev, drv, idx, false); +} + +static int sbi_enter_s2idle_domain_idle_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int idx) +{ + return __sbi_enter_domain_idle_state(dev, drv, idx, true); +} + +static int sbi_cpuidle_cpuhp_up(unsigned int cpu) +{ + struct device *pd_dev = __this_cpu_read(sbi_cpuidle_data.dev); + + if (pd_dev) + pm_runtime_get_sync(pd_dev); + + return 0; +} + +static int sbi_cpuidle_cpuhp_down(unsigned int cpu) +{ + struct device *pd_dev = __this_cpu_read(sbi_cpuidle_data.dev); + + if (pd_dev) { + pm_runtime_put_sync(pd_dev); + /* Clear domain state to start fresh at next online. */ + sbi_clear_domain_state(); + } + + return 0; +} + +static void sbi_idle_init_cpuhp(void) +{ + int err; + + if (!sbi_cpuidle_use_cpuhp) + return; + + err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING, + "cpuidle/sbi:online", + sbi_cpuidle_cpuhp_up, + sbi_cpuidle_cpuhp_down); + if (err) + pr_warn("Failed %d while setup cpuhp state\n", err); +} + +static const struct of_device_id sbi_cpuidle_state_match[] = { + { .compatible = "riscv,idle-state", + .data = sbi_cpuidle_enter_state }, + { }, +}; + +static bool sbi_suspend_state_is_valid(u32 state) +{ + if (state > SBI_HSM_SUSPEND_RET_DEFAULT && + state < SBI_HSM_SUSPEND_RET_PLATFORM) + return false; + if (state > SBI_HSM_SUSPEND_NON_RET_DEFAULT && + state < SBI_HSM_SUSPEND_NON_RET_PLATFORM) + return false; + return true; +} + +static int sbi_dt_parse_state_node(struct device_node *np, u32 *state) +{ + int err = of_property_read_u32(np, "riscv,sbi-suspend-param", state); + + if (err) { + pr_warn("%pOF missing riscv,sbi-suspend-param property\n", np); + return err; + } + + if (!sbi_suspend_state_is_valid(*state)) { + pr_warn("Invalid SBI suspend state %#x\n", *state); + return -EINVAL; + } + + return 0; +} + +static int sbi_dt_cpu_init_topology(struct cpuidle_driver *drv, + struct sbi_cpuidle_data *data, + unsigned int state_count, int cpu) +{ + /* Currently limit the hierarchical topology to be used in OSI mode. */ + if (!sbi_cpuidle_use_osi) + return 0; + + data->dev = dt_idle_attach_cpu(cpu, "sbi"); + if (IS_ERR_OR_NULL(data->dev)) + return PTR_ERR_OR_ZERO(data->dev); + + /* + * Using the deepest state for the CPU to trigger a potential selection + * of a shared state for the domain, assumes the domain states are all + * deeper states. + */ + drv->states[state_count - 1].enter = sbi_enter_domain_idle_state; + drv->states[state_count - 1].enter_s2idle = + sbi_enter_s2idle_domain_idle_state; + sbi_cpuidle_use_cpuhp = true; + + return 0; +} + +static int sbi_cpuidle_dt_init_states(struct device *dev, + struct cpuidle_driver *drv, + unsigned int cpu, + unsigned int state_count) +{ + struct sbi_cpuidle_data *data = per_cpu_ptr(&sbi_cpuidle_data, cpu); + struct device_node *state_node; + struct device_node *cpu_node; + u32 *states; + int i, ret; + + cpu_node = of_cpu_device_node_get(cpu); + if (!cpu_node) + return -ENODEV; + + states = devm_kcalloc(dev, state_count, sizeof(*states), GFP_KERNEL); + if (!states) { + ret = -ENOMEM; + goto fail; + } + + /* Parse SBI specific details from state DT nodes */ + for (i = 1; i < state_count; i++) { + state_node = of_get_cpu_state_node(cpu_node, i - 1); + if (!state_node) + break; + + ret = sbi_dt_parse_state_node(state_node, &states[i]); + of_node_put(state_node); + + if (ret) + return ret; + + pr_debug("sbi-state %#x index %d\n", states[i], i); + } + if (i != state_count) { + ret = -ENODEV; + goto fail; + } + + /* Initialize optional data, used for the hierarchical topology. */ + ret = sbi_dt_cpu_init_topology(drv, data, state_count, cpu); + if (ret < 0) + return ret; + + /* Store states in the per-cpu struct. */ + data->states = states; + +fail: + of_node_put(cpu_node); + + return ret; +} + +static void sbi_cpuidle_deinit_cpu(int cpu) +{ + struct sbi_cpuidle_data *data = per_cpu_ptr(&sbi_cpuidle_data, cpu); + + dt_idle_detach_cpu(data->dev); + sbi_cpuidle_use_cpuhp = false; +} + +static int sbi_cpuidle_init_cpu(struct device *dev, int cpu) +{ + struct cpuidle_driver *drv; + unsigned int state_count = 0; + int ret = 0; + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->name = "sbi_cpuidle"; + drv->owner = THIS_MODULE; + drv->cpumask = (struct cpumask *)cpumask_of(cpu); + + /* RISC-V architectural WFI to be represented as state index 0. */ + drv->states[0].enter = sbi_cpuidle_enter_state; + drv->states[0].exit_latency = 1; + drv->states[0].target_residency = 1; + drv->states[0].power_usage = UINT_MAX; + strcpy(drv->states[0].name, "WFI"); + strcpy(drv->states[0].desc, "RISC-V WFI"); + + /* + * If no DT idle states are detected (ret == 0) let the driver + * initialization fail accordingly since there is no reason to + * initialize the idle driver if only wfi is supported, the + * default archictectural back-end already executes wfi + * on idle entry. + */ + ret = dt_init_idle_driver(drv, sbi_cpuidle_state_match, 1); + if (ret <= 0) { + pr_debug("HART%ld: failed to parse DT idle states\n", + cpuid_to_hartid_map(cpu)); + return ret ? : -ENODEV; + } + state_count = ret + 1; /* Include WFI state as well */ + + /* Initialize idle states from DT. */ + ret = sbi_cpuidle_dt_init_states(dev, drv, cpu, state_count); + if (ret) { + pr_err("HART%ld: failed to init idle states\n", + cpuid_to_hartid_map(cpu)); + return ret; + } + + ret = cpuidle_register(drv, NULL); + if (ret) + goto deinit; + + cpuidle_cooling_register(drv); + + return 0; +deinit: + sbi_cpuidle_deinit_cpu(cpu); + return ret; +} + +static void sbi_cpuidle_domain_sync_state(struct device *dev) +{ + /* + * All devices have now been attached/probed to the PM domain + * topology, hence it's fine to allow domain states to be picked. + */ + sbi_cpuidle_pd_allow_domain_state = true; +} + +#ifdef CONFIG_DT_IDLE_GENPD + +static int sbi_cpuidle_pd_power_off(struct generic_pm_domain *pd) +{ + struct genpd_power_state *state = &pd->states[pd->state_idx]; + u32 *pd_state; + + if (!state->data) + return 0; + + if (!sbi_cpuidle_pd_allow_domain_state) + return -EBUSY; + + /* OSI mode is enabled, set the corresponding domain state. */ + pd_state = state->data; + sbi_set_domain_state(*pd_state); + + return 0; +} + +struct sbi_pd_provider { + struct list_head link; + struct device_node *node; +}; + +static LIST_HEAD(sbi_pd_providers); + +static int sbi_pd_init(struct device_node *np) +{ + struct generic_pm_domain *pd; + struct sbi_pd_provider *pd_provider; + struct dev_power_governor *pd_gov; + int ret = -ENOMEM, state_count = 0; + + pd = dt_idle_pd_alloc(np, sbi_dt_parse_state_node); + if (!pd) + goto out; + + pd_provider = kzalloc(sizeof(*pd_provider), GFP_KERNEL); + if (!pd_provider) + goto free_pd; + + pd->flags |= GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_CPU_DOMAIN; + + /* Allow power off when OSI is available. */ + if (sbi_cpuidle_use_osi) + pd->power_off = sbi_cpuidle_pd_power_off; + else + pd->flags |= GENPD_FLAG_ALWAYS_ON; + + /* Use governor for CPU PM domains if it has some states to manage. */ + pd_gov = state_count > 0 ? &pm_domain_cpu_gov : NULL; + + ret = pm_genpd_init(pd, pd_gov, false); + if (ret) + goto free_pd_prov; + + ret = of_genpd_add_provider_simple(np, pd); + if (ret) + goto remove_pd; + + pd_provider->node = of_node_get(np); + list_add(&pd_provider->link, &sbi_pd_providers); + + pr_debug("init PM domain %s\n", pd->name); + return 0; + +remove_pd: + pm_genpd_remove(pd); +free_pd_prov: + kfree(pd_provider); +free_pd: + dt_idle_pd_free(pd); +out: + pr_err("failed to init PM domain ret=%d %pOF\n", ret, np); + return ret; +} + +static void sbi_pd_remove(void) +{ + struct sbi_pd_provider *pd_provider, *it; + struct generic_pm_domain *genpd; + + list_for_each_entry_safe(pd_provider, it, &sbi_pd_providers, link) { + of_genpd_del_provider(pd_provider->node); + + genpd = of_genpd_remove_last(pd_provider->node); + if (!IS_ERR(genpd)) + kfree(genpd); + + of_node_put(pd_provider->node); + list_del(&pd_provider->link); + kfree(pd_provider); + } +} + +static int sbi_genpd_probe(struct device_node *np) +{ + struct device_node *node; + int ret = 0, pd_count = 0; + + if (!np) + return -ENODEV; + + /* + * Parse child nodes for the "#power-domain-cells" property and + * initialize a genpd/genpd-of-provider pair when it's found. + */ + for_each_child_of_node(np, node) { + if (!of_find_property(node, "#power-domain-cells", NULL)) + continue; + + ret = sbi_pd_init(node); + if (ret) + goto put_node; + + pd_count++; + } + + /* Bail out if not using the hierarchical CPU topology. */ + if (!pd_count) + goto no_pd; + + /* Link genpd masters/subdomains to model the CPU topology. */ + ret = dt_idle_pd_init_topology(np); + if (ret) + goto remove_pd; + + return 0; + +put_node: + of_node_put(node); +remove_pd: + sbi_pd_remove(); + pr_err("failed to create CPU PM domains ret=%d\n", ret); +no_pd: + return ret; +} + +#else + +static inline int sbi_genpd_probe(struct device_node *np) +{ + return 0; +} + +#endif + +static int sbi_cpuidle_probe(struct platform_device *pdev) +{ + int cpu, ret; + struct cpuidle_driver *drv; + struct cpuidle_device *dev; + struct device_node *np, *pds_node; + + /* Detect OSI support based on CPU DT nodes */ + sbi_cpuidle_use_osi = true; + for_each_possible_cpu(cpu) { + np = of_cpu_device_node_get(cpu); + if (np && + of_find_property(np, "power-domains", NULL) && + of_find_property(np, "power-domain-names", NULL)) { + continue; + } else { + sbi_cpuidle_use_osi = false; + break; + } + } + + /* Populate generic power domains from DT nodes */ + pds_node = of_find_node_by_path("/cpus/power-domains"); + if (pds_node) { + ret = sbi_genpd_probe(pds_node); + of_node_put(pds_node); + if (ret) + return ret; + } + + /* Initialize CPU idle driver for each CPU */ + for_each_possible_cpu(cpu) { + ret = sbi_cpuidle_init_cpu(&pdev->dev, cpu); + if (ret) { + pr_debug("HART%ld: idle driver init failed\n", + cpuid_to_hartid_map(cpu)); + goto out_fail; + } + } + + /* Setup CPU hotplut notifiers */ + sbi_idle_init_cpuhp(); + + pr_info("idle driver registered for all CPUs\n"); + + return 0; + +out_fail: + while (--cpu >= 0) { + dev = per_cpu(cpuidle_devices, cpu); + drv = cpuidle_get_cpu_driver(dev); + cpuidle_unregister(drv); + sbi_cpuidle_deinit_cpu(cpu); + } + + return ret; +} + +static struct platform_driver sbi_cpuidle_driver = { + .probe = sbi_cpuidle_probe, + .driver = { + .name = "sbi-cpuidle", + .sync_state = sbi_cpuidle_domain_sync_state, + }, +}; + +static int __init sbi_cpuidle_init(void) +{ + int ret; + struct platform_device *pdev; + +#if 0 + /* + * The SBI HSM suspend function is only available when: + * 1) SBI version is 0.3 or higher + * 2) SBI HSM extension is available + */ + if ((sbi_spec_version < sbi_mk_version(0, 3)) || + sbi_probe_extension(SBI_EXT_HSM) <= 0) { + pr_info("HSM suspend not available\n"); + return 0; + } +#endif + ret = platform_driver_register(&sbi_cpuidle_driver); + if (ret) + return ret; + + pdev = platform_device_register_simple("sbi-cpuidle", + -1, NULL, 0); + if (IS_ERR(pdev)) { + platform_driver_unregister(&sbi_cpuidle_driver); + return PTR_ERR(pdev); + } + + return 0; +} +device_initcall(sbi_cpuidle_init); diff --git a/drivers/cpuidle/dt_idle_genpd.c b/drivers/cpuidle/dt_idle_genpd.c new file mode 100644 index 000000000000..b37165514d4e --- /dev/null +++ b/drivers/cpuidle/dt_idle_genpd.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PM domains for CPUs via genpd. + * + * Copyright (C) 2019 Linaro Ltd. + * Author: Ulf Hansson + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#define pr_fmt(fmt) "dt-idle-genpd: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "dt_idle_genpd.h" + +static int pd_parse_state_nodes( + int (*parse_state)(struct device_node *, u32 *), + struct genpd_power_state *states, int state_count) +{ + int i, ret; + u32 state, *state_buf; + + for (i = 0; i < state_count; i++) { + ret = parse_state(to_of_node(states[i].fwnode), &state); + if (ret) + goto free_state; + + state_buf = kmalloc(sizeof(u32), GFP_KERNEL); + if (!state_buf) { + ret = -ENOMEM; + goto free_state; + } + *state_buf = state; + states[i].data = state_buf; + } + + return 0; + +free_state: + i--; + for (; i >= 0; i--) + kfree(states[i].data); + return ret; +} + +static int pd_parse_states(struct device_node *np, + int (*parse_state)(struct device_node *, u32 *), + struct genpd_power_state **states, + int *state_count) +{ + int ret; + + /* Parse the domain idle states. */ + ret = of_genpd_parse_idle_states(np, states, state_count); + if (ret) + return ret; + + /* Fill out the dt specifics for each found state. */ + ret = pd_parse_state_nodes(parse_state, *states, *state_count); + if (ret) + kfree(*states); + + return ret; +} + +static void pd_free_states(struct genpd_power_state *states, + unsigned int state_count) +{ + int i; + + for (i = 0; i < state_count; i++) + kfree(states[i].data); + kfree(states); +} + +void dt_idle_pd_free(struct generic_pm_domain *pd) +{ + pd_free_states(pd->states, pd->state_count); + kfree(pd->name); + kfree(pd); +} + +struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np, + int (*parse_state)(struct device_node *, u32 *)) +{ + struct generic_pm_domain *pd; + struct genpd_power_state *states = NULL; + int ret, state_count = 0; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + goto out; + + pd->name = kasprintf(GFP_KERNEL, "%pOF", np); + if (!pd->name) + goto free_pd; + + /* + * Parse the domain idle states and let genpd manage the state selection + * for those being compatible with "domain-idle-state". + */ + ret = pd_parse_states(np, parse_state, &states, &state_count); + if (ret) + goto free_name; + + pd->free_states = pd_free_states; + pd->name = kbasename(pd->name); + pd->states = states; + pd->state_count = state_count; + + pr_debug("alloc PM domain %s\n", pd->name); + return pd; + +free_name: + kfree(pd->name); +free_pd: + kfree(pd); +out: + pr_err("failed to alloc PM domain %pOF\n", np); + return NULL; +} + +int dt_idle_pd_init_topology(struct device_node *np) +{ + struct device_node *node; + struct of_phandle_args child, parent; + int ret; + + for_each_child_of_node(np, node) { + if (of_parse_phandle_with_args(node, "power-domains", + "#power-domain-cells", 0, &parent)) + continue; + + child.np = node; + child.args_count = 0; + ret = of_genpd_add_subdomain(&parent, &child); + of_node_put(parent.np); + if (ret) { + of_node_put(node); + return ret; + } + } + + return 0; +} + +struct device *dt_idle_attach_cpu(int cpu, const char *name) +{ + struct device *dev; + + dev = dev_pm_domain_attach_by_name(get_cpu_device(cpu), name); + if (IS_ERR_OR_NULL(dev)) + return dev; + + pm_runtime_irq_safe(dev); + if (cpu_online(cpu)) + pm_runtime_get_sync(dev); + + dev_pm_syscore_device(dev, true); + + return dev; +} + +void dt_idle_detach_cpu(struct device *dev) +{ + if (IS_ERR_OR_NULL(dev)) + return; + + dev_pm_domain_detach(dev, false); +} diff --git a/drivers/cpuidle/dt_idle_genpd.h b/drivers/cpuidle/dt_idle_genpd.h new file mode 100644 index 000000000000..a95483d08a02 --- /dev/null +++ b/drivers/cpuidle/dt_idle_genpd.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_IDLE_GENPD +#define __DT_IDLE_GENPD + +struct device_node; +struct generic_pm_domain; + +#ifdef CONFIG_DT_IDLE_GENPD + +void dt_idle_pd_free(struct generic_pm_domain *pd); + +struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np, + int (*parse_state)(struct device_node *, u32 *)); + +int dt_idle_pd_init_topology(struct device_node *np); + +struct device *dt_idle_attach_cpu(int cpu, const char *name); + +void dt_idle_detach_cpu(struct device *dev); + +#else + +static inline void dt_idle_pd_free(struct generic_pm_domain *pd) +{ +} + +static inline struct generic_pm_domain *dt_idle_pd_alloc( + struct device_node *np, + int (*parse_state)(struct device_node *, u32 *)) +{ + return NULL; +} + +static inline int dt_idle_pd_init_topology(struct device_node *np) +{ + return 0; +} + +static inline struct device *dt_idle_attach_cpu(int cpu, const char *name) +{ + return NULL; +} + +static inline void dt_idle_detach_cpu(struct device *dev) +{ +} + +#endif + +#endif diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index d83ab5017564..c2928a8ac2cf 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -467,7 +467,7 @@ static int dma_chan_alloc_chan_resources(struct dma_chan *dchan) } dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan)); - pm_runtime_get(chan->chip->dev); + pm_runtime_get_sync(chan->chip->dev); return 0; } @@ -492,7 +492,7 @@ static void dma_chan_free_chan_resources(struct dma_chan *dchan) "%s: free resources, descriptor still allocated: %u\n", axi_chan_name(chan), atomic_read(&chan->descs_allocated)); - pm_runtime_put(chan->chip->dev); + pm_runtime_put_sync(chan->chip->dev); } static void dw_axi_dma_set_hw_channel(struct axi_dma_chan *chan, bool set) @@ -1111,7 +1111,8 @@ static int dma_chan_terminate_all(struct dma_chan *dchan) axi_chan_disable(chan); ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val, - !(val & chan_active), 1000, 10000); + !(val & chan_active), 1000, 100000); + if (ret == -ETIMEDOUT) dev_warn(dchan2dev(dchan), "%s failed to stop\n", axi_chan_name(chan)); @@ -1141,6 +1142,8 @@ static int dma_chan_pause(struct dma_chan *dchan) unsigned long flags; unsigned int timeout = 20; /* timeout iterations */ u32 val; + int ret; + u32 chan_active = BIT(chan->id) << DMAC_CHAN_EN_SHIFT; spin_lock_irqsave(&chan->vc.lock, flags); @@ -1168,13 +1171,48 @@ static int dma_chan_pause(struct dma_chan *dchan) spin_unlock_irqrestore(&chan->vc.lock, flags); + chan->ch_sar = axi_chan_ioread32(chan, CH_SAR); + chan->ch_dar = axi_chan_ioread32(chan, CH_DAR); + chan->ch_dar_h = axi_chan_ioread32(chan, CH_DAR_H); + chan->ch_block_ts = axi_chan_ioread32(chan, CH_BLOCK_TS); + chan->ch_ctl_l = axi_chan_ioread32(chan, CH_CTL_L); + chan->ch_ctl_h = axi_chan_ioread32(chan, CH_CTL_H); + chan->ch_cfg_l = axi_chan_ioread32(chan, CH_CFG_L); + chan->ch_cfg_h = axi_chan_ioread32(chan, CH_CFG_H); + chan->ch_llp = axi_chan_ioread32(chan, CH_LLP); + //printk("%s for %s ch_sar=0x%x ch_dar=0x%x ch_dar_h=0x%x ch_block_ts=0x%x ch_ctl_l=0x%x ch_ctl_h=0x%x ch_cfg_l=0x%x ch_cfg_h=0x%x ch_llp=0x%x\n", __func__, + // axi_chan_name(chan), chan->ch_sar, chan->ch_dar, chan->ch_dar_h, chan->ch_block_ts, chan->ch_ctl_l, chan->ch_ctl_h, chan->ch_cfg_l, chan->ch_cfg_h, chan->ch_llp); + + axi_chan_disable(chan); + ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val, + !(val & chan_active), 1000, 100000); + if (ret == -ETIMEDOUT) + printk("%s %s failed to stop\n", __func__, axi_chan_name(chan)); + return timeout ? 0 : -EAGAIN; } /* Called in chan locked context */ static inline void axi_chan_resume(struct axi_dma_chan *chan) { - u32 val; + u32 val, irq_mask; + struct axi_dma_desc *desc = chan->desc; + struct axi_dma_hw_desc *hw_desc = desc->hw_desc; + + axi_chan_iowrite32(chan, CH_SAR, chan->ch_sar); + axi_chan_iowrite32(chan, CH_DAR, chan->ch_dar); + axi_chan_iowrite32(chan, CH_DAR_H, chan->ch_dar_h); + axi_chan_iowrite32(chan, CH_BLOCK_TS, chan->ch_block_ts); + axi_chan_iowrite32(chan, CH_CTL_L, chan->ch_ctl_l); + axi_chan_iowrite32(chan, CH_CTL_H, chan->ch_ctl_h); + axi_chan_iowrite32(chan, CH_CFG_L, chan->ch_cfg_l); + axi_chan_iowrite32(chan, CH_CFG_H, chan->ch_cfg_h); + axi_chan_iowrite32(chan, CH_LLP, chan->ch_llp); + irq_mask = DWAXIDMAC_IRQ_DMA_TRF | DWAXIDMAC_IRQ_ALL_ERR; + axi_chan_irq_sig_set(chan, irq_mask); + /* Generate 'suspend' status but don't generate interrupt */ + irq_mask |= DWAXIDMAC_IRQ_SUSPENDED; + axi_chan_irq_set(chan, irq_mask); val = axi_dma_ioread32(chan->chip, DMAC_CHEN); if (chan->chip->dw->hdata->reg_map_8_channels) { @@ -1187,7 +1225,11 @@ static inline void axi_chan_resume(struct axi_dma_chan *chan) axi_dma_iowrite32(chan->chip, DMAC_CHSUSPREG, val); } + axi_chan_enable(chan); + chan->is_paused = false; + + return; } static int dma_chan_resume(struct dma_chan *dchan) @@ -1234,6 +1276,21 @@ static int axi_dma_resume(struct axi_dma_chip *chip) return 0; } +static void axi_dma_dump(struct axi_dma_chip *chip) +{ + struct dw_axi_dma *dw = chip->dw; + struct axi_dma_chan *chan; + u32 i; + struct virt_dma_desc *vd; + for (i = 0; i < dw->hdata->nr_channels; i++) { + chan = &dw->chan[i]; + printk("%s chan name %s\n", __func__, axi_chan_name(chan)); + vd = vchan_next_desc(&chan->vc); + axi_chan_list_dump_lli(chan, vd_to_axi_desc(vd)); + } + return; +} + static int __maybe_unused axi_dma_runtime_suspend(struct device *dev) { struct axi_dma_chip *chip = dev_get_drvdata(dev); @@ -1248,6 +1305,42 @@ static int __maybe_unused axi_dma_runtime_resume(struct device *dev) return axi_dma_resume(chip); } +static int __maybe_unused axi_dma_sleep_suspend(struct device *dev) +{ + + //struct axi_dma_chip *chip = dev_get_drvdata(dev); + //axi_dma_irq_disable(chip); + //axi_dma_disable(chip); + + //clk_disable_unprepare(chip->core_clk); + //clk_disable_unprepare(chip->cfgr_clk); + + + dev_err(dev, "%s, %d\n", __func__, __LINE__); + + return 0; +} + +static int __maybe_unused axi_dma_sleep_resume(struct device *dev) +{ + struct axi_dma_chip *chip = dev_get_drvdata(dev); + int ret = 0; + + ret = clk_prepare_enable(chip->cfgr_clk); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(chip->core_clk); + if (ret < 0) + return ret; + + axi_dma_enable(chip); + axi_dma_irq_enable(chip); + dev_err(dev, "%s, %d\n", __func__, __LINE__); + + return 0; +} + static struct dma_chan *dw_axi_dma_of_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { @@ -1521,9 +1614,16 @@ static int dw_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static const struct dev_pm_ops dw_axi_dma_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(axi_dma_sleep_suspend, axi_dma_sleep_resume) + SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL) +}; +#else static const struct dev_pm_ops dw_axi_dma_pm_ops = { - SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL) + SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL) }; +#endif static const struct of_device_id dw_dma_of_id_table[] = { { .compatible = "snps,axi-dma-1.01a" }, diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index 803e98258b83..fba1ecd3f038 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -51,6 +51,15 @@ struct axi_dma_chan { bool cyclic; /* these other elements are all protected by vc.lock */ bool is_paused; + u32 ch_sar; + u32 ch_dar; + u32 ch_dar_h; + u32 ch_block_ts; + u32 ch_ctl_l; + u32 ch_ctl_h; + u32 ch_cfg_l; + u32 ch_cfg_h; + u32 ch_llp; }; struct dw_axi_dma { @@ -153,6 +162,7 @@ static inline struct axi_dma_chan *dchan_to_axi_dma_chan(struct dma_chan *dchan) /* DMA channel registers offset */ #define CH_SAR 0x000 /* R/W Chan Source Address */ #define CH_DAR 0x008 /* R/W Chan Destination Address */ +#define CH_DAR_H 0x00C #define CH_BLOCK_TS 0x010 /* R/W Chan Block Transfer Size */ #define CH_CTL 0x018 /* R/W Chan Control */ #define CH_CTL_L 0x018 /* R/W Chan Control 00-31 */ diff --git a/drivers/firmware/thead/light_aon.c b/drivers/firmware/thead/light_aon.c index 8fbd808d73be..ad2d49a0845c 100644 --- a/drivers/firmware/thead/light_aon.c +++ b/drivers/firmware/thead/light_aon.c @@ -230,10 +230,26 @@ static const struct of_device_id light_aon_match[] = { { /* Sentinel */ } }; +static int __maybe_unused light_aon_resume_noirq(struct device *dev) +{ + struct light_aon_chan *aon_chan; + int ret; + + aon_chan = &light_aon_ipc_handle->chans; + + complete(&aon_chan->tx_done); + return 0; +} + +static const struct dev_pm_ops light_aon_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, + light_aon_resume_noirq) +}; static struct platform_driver light_aon_driver = { .driver = { .name = "light-aon", .of_match_table = light_aon_match, + .pm = &light_aon_pm_ops, }, .probe = light_aon_probe, }; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index a78167b2c9ca..ba490848f523 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -1242,12 +1242,20 @@ static const struct of_device_id pca953x_dt_ids[] = { MODULE_DEVICE_TABLE(of, pca953x_dt_ids); -static SIMPLE_DEV_PM_OPS(pca953x_pm_ops, pca953x_suspend, pca953x_resume); +#ifdef CONFIG_PM_SLEEP +static const struct dev_pm_ops pca953x_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(pca953x_suspend, + pca953x_resume) +}; +#define PCA593X_PM_OPS &pca953x_pm_ops +#else +#define PCA593X_PM_OPS NULL +#endif static struct i2c_driver pca953x_driver = { .driver = { .name = "pca953x", - .pm = &pca953x_pm_ops, + .pm = PCA593X_PM_OPS, .of_match_table = pca953x_dt_ids, .acpi_match_table = pca953x_acpi_ids, }, diff --git a/drivers/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c b/drivers/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c index edc3112cf15e..1010faa99167 100644 --- a/drivers/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c +++ b/drivers/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c @@ -1380,6 +1380,7 @@ static int gc_poweroff_timeout_show(void* m, void* data) gckGALDEVICE device = galDevice; gckHARDWARE hardware; int len = 0; + int i; #ifdef CONFIG_DEBUG_FS void* ptr = m; #else @@ -1389,14 +1390,19 @@ static int gc_poweroff_timeout_show(void* m, void* data) if (!device) return -ENXIO; - hardware = device->kernels[0]->hardware; - + for (i = 0; i < gcvCORE_COUNT; ++i) + { + if (!device->kernels[i]) + { + continue; + } + hardware = device->kernels[i]->hardware; #ifdef CONFIG_DEBUG_FS - len += fs_printf(ptr + len, "power off timeout: %d ms.\n", hardware->powerOffTimeout); + len += fs_printf(ptr + len, "power off timeout: %d ms.\n", hardware->powerOffTimeout); #else - len += sprintf(ptr + len, "power off timeout: %d ms.\n", hardware->powerOffTimeout); + len += sprintf(ptr + len, "power off timeout: %d ms.\n", hardware->powerOffTimeout); #endif - + } return len; } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 619f4620d376..6d06a60a8041 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -20,7 +20,6 @@ #include #include #include - #include #include @@ -3399,13 +3398,6 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, goto err_res; } - hdmi->i2s_clk = devm_clk_get_optional(hdmi->dev, "i2s"); - if (IS_ERR(hdmi->i2s_clk)) { - ret = PTR_ERR(hdmi->i2s_clk); - dev_err(hdmi->dev, "Unable to get HDMI i2s clk: %d\n", ret); - goto err_res; - } - clk_prepare_enable(hdmi->iahb_clk); clk_prepare_enable(hdmi->isfr_clk); @@ -3618,16 +3610,15 @@ EXPORT_SYMBOL_GPL(dw_hdmi_unbind); void dw_hdmi_resume(struct dw_hdmi *hdmi) { dw_hdmi_init_hw(hdmi); + hdmi_init_clk_regenerator(hdmi); } EXPORT_SYMBOL_GPL(dw_hdmi_resume); #ifdef CONFIG_PM int dw_hdmi_runtime_suspend(struct dw_hdmi *hdmi) { - clk_disable_unprepare(hdmi->i2s_clk); clk_disable_unprepare(hdmi->pix_clk); clk_disable_unprepare(hdmi->cec_clk); - return 0; } EXPORT_SYMBOL_GPL(dw_hdmi_runtime_suspend); @@ -3636,13 +3627,12 @@ int dw_hdmi_runtime_resume(struct dw_hdmi *hdmi) { clk_prepare_enable(hdmi->cec_clk); clk_prepare_enable(hdmi->pix_clk); - clk_prepare_enable(hdmi->i2s_clk); - return 0; } EXPORT_SYMBOL_GPL(dw_hdmi_runtime_resume); #endif + MODULE_AUTHOR("Sascha Hauer "); MODULE_AUTHOR("Andy Yan "); MODULE_AUTHOR("Yakir Yang "); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index d131b7e7ec25..2ca5f4b6d81b 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -538,4 +538,14 @@ config DRM_PANEL_MINGJUN_070BI30IA2 Say Y here if you want to enable support for MingJun 070BI30IA2 MIPI DSI panel. The panel support TFT dot matrix LCD with 800RGBx1280 dots at maximum. + +config DRM_PANEL_HX8279 + tristate "HX8279-based panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y if you want to enable support for panels based on the + HX8279 controller. + endmenu diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index fabee1b790ab..1ca5576d6d70 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_DRM_PANEL_ILI9881D) += panel-ili9881d.o obj-$(CONFIG_DRM_PANEL_HX8394) += panel-himax8394.o obj-$(CONFIG_DRM_PANEL_JADARD_JD9365DA_H3) += panel-jadard-jd9365da-h3.o obj-$(CONFIG_DRM_PANEL_MINGJUN_070BI30IA2) += panel-mingjun-070bi30ia2.o +obj-$(CONFIG_DRM_PANEL_HX8279) += panel-hx8279.o diff --git a/drivers/gpu/drm/panel/panel-hx8279.c b/drivers/gpu/drm/panel/panel-hx8279.c new file mode 100644 index 000000000000..03f1595b2998 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-hx8279.c @@ -0,0 +1,328 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include