Linux_SDK_V1.3.3
authorthead_admin <occ_thead@service.alibaba.com>
Tue, 14 Nov 2023 01:42:19 +0000 (01:42 +0000)
committerJaehoon Chung <jh80.chung@samsung.com>
Wed, 13 Mar 2024 06:59:01 +0000 (15:59 +0900)
Signed-off-by: thead_admin <occ_thead@service.alibaba.com>
(cherry picked from commit e17ac7bab2079beba2376f104279619e037e9e2c)
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
128 files changed:
Documentation/devicetree/bindings/arm/idle-states.yaml [deleted file]
Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
Documentation/devicetree/bindings/arm/psci.yaml
Documentation/devicetree/bindings/cpu/idle-states.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/riscv/cpus.yaml
MAINTAINERS
arch/riscv/Kconfig
arch/riscv/Kconfig.socs
arch/riscv/boot/dts/thead/Makefile
arch/riscv/boot/dts/thead/fire-crash.dts
arch/riscv/boot/dts/thead/fire-emu.dts
arch/riscv/boot/dts/thead/light-a-product.dts
arch/riscv/boot/dts/thead/light-a-val-android.dts
arch/riscv/boot/dts/thead/light-a-val-sec.dts
arch/riscv/boot/dts/thead/light-a-val.dts
arch/riscv/boot/dts/thead/light-ant-ref.dts
arch/riscv/boot/dts/thead/light-b-audio-hdmi.dts
arch/riscv/boot/dts/thead/light-b-product.dts
arch/riscv/boot/dts/thead/light-beagle-ref.dts
arch/riscv/boot/dts/thead/light-crash.dts
arch/riscv/boot/dts/thead/light-lpi4a-camera-tuning.dts [new file with mode: 0644]
arch/riscv/boot/dts/thead/light-lpi4a-crash.dts [new file with mode: 0644]
arch/riscv/boot/dts/thead/light-lpi4a-hdmi.dts [new file with mode: 0644]
arch/riscv/boot/dts/thead/light-lpi4a-hx8279.dts [new file with mode: 0644]
arch/riscv/boot/dts/thead/light-lpi4a-ref.dts
arch/riscv/boot/dts/thead/light-lpi4a-sec.dts
arch/riscv/boot/dts/thead/light-lpi4a.dts
arch/riscv/boot/dts/thead/light-vi-devices.dtsi
arch/riscv/boot/dts/thead/light.dtsi
arch/riscv/configs/defconfig
arch/riscv/configs/light_defconfig
arch/riscv/configs/rv32_defconfig
arch/riscv/include/asm/asm.h
arch/riscv/include/asm/cpu_ops_sbi.h [new file with mode: 0644]
arch/riscv/include/asm/pgtable.h
arch/riscv/include/asm/sbi.h
arch/riscv/include/asm/suspend.h [new file with mode: 0644]
arch/riscv/kernel/Makefile
arch/riscv/kernel/asm-offsets.c
arch/riscv/kernel/cpu_ops_sbi.c
arch/riscv/kernel/head.S
arch/riscv/kernel/perf_callchain.c
arch/riscv/kernel/process.c
arch/riscv/kernel/ptrace.c
arch/riscv/kernel/suspend.c [new file with mode: 0644]
arch/riscv/kernel/suspend_entry.S [new file with mode: 0644]
arch/riscv/kvm/vcpu_sbi_hsm.c [new file with mode: 0644]
drivers/block/zram/zram_drv.c
drivers/clk/thead/clk-light-fm.c
drivers/clk/thead/clk.h
drivers/clk/thead/gate/Makefile
drivers/clk/thead/gate/dspsys-gate.c
drivers/clk/thead/gate/miscsys-gate.c [new file with mode: 0644]
drivers/clk/thead/gate/vpsys-gate.c
drivers/cpufreq/light-mpw-cpufreq.c
drivers/cpuidle/Kconfig
drivers/cpuidle/Kconfig.arm
drivers/cpuidle/Kconfig.riscv
drivers/cpuidle/Makefile
drivers/cpuidle/cpuidle-psci-domain.c
drivers/cpuidle/cpuidle-psci.h
drivers/cpuidle/cpuidle-riscv-sbi.c [new file with mode: 0644]
drivers/cpuidle/dt_idle_genpd.c [new file with mode: 0644]
drivers/cpuidle/dt_idle_genpd.h [new file with mode: 0644]
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
drivers/dma/dw-axi-dmac/dw-axi-dmac.h
drivers/firmware/thead/light_aon.c
drivers/gpio/gpio-pca953x.c
drivers/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/Makefile
drivers/gpu/drm/panel/panel-hx8279.c [new file with mode: 0644]
drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
drivers/gpu/drm/verisilicon/dw_hdmi-light.c
drivers/gpu/drm/verisilicon/dw_hdmi_tx_phy_gen2.h
drivers/gpu/drm/verisilicon/vs_dc.c
drivers/gpu/drm/verisilicon/vs_dc.h
drivers/gpu/drm/verisilicon/vs_dc_hw.h
drivers/hwmon/mr75203.c
drivers/i2c/busses/i2c-designware-master_dma.c
drivers/i2c/busses/i2c-designware-platdrv.c
drivers/irqchip/irq-sifive-plic.c
drivers/mailbox/light-mailbox.c
drivers/net/ethernet/stmicro/stmmac/dwmac-light.c
drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
drivers/nvmem/light-efuse.c
drivers/phy/synopsys/phy-dw-mipi-dphy.c
drivers/pinctrl/thead/pinctrl-light.c
drivers/rpmsg/light_rpmsg.c
drivers/soc/thead/light-iopmp.c
drivers/soc/thead/light_event.c
drivers/soc/thead/pm-light.c
drivers/spi/spi-dw-core.c
drivers/spi/spi-dw-mmio-quad.c
drivers/spi/spi-dw-mmio.c
drivers/spi/spi-dw-quad.c
drivers/spi/spi-dw-quad.h
drivers/spi/spi-dw.h
drivers/usb/dwc3/dwc3-thead.c
drivers/vhost/vdmabuf.c
drivers/virtio/virtio_vdmabuf.c
include/dt-bindings/clock/light-dspsys.h
include/dt-bindings/clock/light-miscsys.h [new file with mode: 0644]
include/dt-bindings/clock/light-vpsys.h
include/dt-bindings/soc/thead,light-iopmp.h
include/linux/firmware/thead/ipc.h
include/linux/gfp.h
include/linux/highmem.h
include/linux/light_rpmsg.h
include/linux/mmzone.h
mm/cma.c
mm/page_alloc.c
mm/zsmalloc.c
net/rfkill/rfkill-bt.c
net/rfkill/rfkill-wlan.c
sound/soc/thead/aw87519_audio.c
sound/soc/thead/light-audio-cpr.h
sound/soc/thead/light-i2s-8ch.c
sound/soc/thead/light-i2s.c
sound/soc/thead/light-i2s.h
sound/soc/thead/light-spdif.c
sound/soc/thead/light-spdif.h
sound/soc/thead/light-tdm.c
sound/soc/thead/light-tdm.h
tools/perf/tests/perf-record.c

diff --git a/Documentation/devicetree/bindings/arm/idle-states.yaml b/Documentation/devicetree/bindings/arm/idle-states.yaml
deleted file mode 100644 (file)
index ea805c1..0000000
+++ /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 <lorenzo.pieralisi@arm.com>
-
-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>;
-            };
-        };
-    };
-
-...
index 6ce0b212ec6d6447f4ef037ec2588278d72d0ef4..606b4b1b709da89c6c28f346b6b194c444c146d7 100644 (file)
@@ -81,4 +81,4 @@ Example:
                };
        };
 
-[1]. Documentation/devicetree/bindings/arm/idle-states.yaml
+[1]. Documentation/devicetree/bindings/cpu/idle-states.yaml
index 8b77cf83a095a3a5553c107a57ef0ab0093e4b57..dd83ef278af03b7c4bb4c29d075c901324debb2c 100644 (file)
@@ -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 (file)
index 0000000..4c8f825
--- /dev/null
@@ -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 <lorenzo.pieralisi@arm.com>
+  - Anup Patel <anup@brainfault.org>
+
+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>;
+            };
+        };
+    };
+
+...
index c6925e0b16e46bb546d04fc3dd53900126ff310d..d2ac84955e17a55fd2e50a636f3bd16fa31271f3 100644 (file)
@@ -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
index e492b0e3a4c5e070335b1f3bf31f2bf90f34d13f..a7407dd25454818d1edd5443af45f62b45152caf 100644 (file)
@@ -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 <ulf.hansson@linaro.org>
+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 <anup@brainfault.org>
+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 <nico@fluxnic.net>
 S:     Maintained
index ce6945da7682ab226fd3423bba92d59c46c231e7..168603c2c9da801e2012899782409bef0fdc97de 100644 (file)
@@ -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"
index 7718166b00dc56823da21d673b67791ec7ed9f3d..d008cf550c25606734b1ce4db08e88a54542562d 100644 (file)
@@ -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.
 
index b20b8a212b374692211a210a4285b5e9b8649299..75a6eb36da0d8e7402f511623502016c2a958d0f 100644 (file)
@@ -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
index 730dd5c80b4201bd36af6ac4978a8cec7402bbbe..17e501a10cf88b75549f49e59d4a7a33646c7a58 100644 (file)
                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";
index 63fd37546ac5661b186983c2b368453b8783a988..f6dc93d752d08025c9a2ee7e2946888b7ef180e0 100644 (file)
                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";
index ca550a804973473defee28211bb7121b50e4e54c..38dde44bfc0fdf20e575eb1eea8442b00ab475d1 100644 (file)
                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 {
                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";
index ef36055468b0d64d73af8d8bdc7914533a8eaf13..e4504f3e5e0faecb600c1f50fcc53c7ab661c9d7 100644 (file)
 
 &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";
 };
 
index 8417982584bfc1b7cd3f75bdb887cdb75b85bd9e..19cff7bdeee64d936684f69cb00f5855aecd88c7 100644 (file)
@@ -15,3 +15,6 @@
         status = "disabled";
 };
 
+&eip_28 {
+        status = "disabled";
+};
index d7fa8033becfe0c1146eecc467e817d03eacfa07..65c3e46e66fa0beea9775643266941fb291e7f85 100644 (file)
@@ -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 {
                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 {
                status = "disabled";
                key-volumedown {
                        label = "Volume Down Key";
+                       wakeup-source;
                        linux,code = <KEY_1>;
                        debounce-interval = <2>;
                        gpios = <&ao_gpio_porta 4 GPIO_ACTIVE_LOW>;
                };
                key-volumeup {
                        label = "Volume Up Key";
+                       wakeup-source;
                        linux,code = <KEY_2>;
                        debounce-interval = <2>;
                        gpios = <&ao_gpio_porta 5 GPIO_ACTIVE_LOW>;
 &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";
 &i2c1 {
         clock-frequency = <400000>;
         status = "okay";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_i2c1>;
         touch1@5d {
                 #gpio-cells = <2>;
                 compatible = "goodix,gt911";
        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";
        };
 
        spidev@1 {
+               status = "disable";
                compatible = "spidev";
                #address-cells = <0x1>;
                #size-cells = <0x1>;
 
 &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>;
 &qspi1 {
        num-cs = <1>;
        cs-gpios = <&gpio0_porta 1 0>;
-       status = "disabled";
+       rx-sample-dly = <5>;
+       status = "okay";
 
        spi-flash@0 {
                #address-cells = <1>;
        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 {
        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";
 };
 
        bus-width = <4>;
        pull_up;
        wprtn_ignore;
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_sdio0>;
        status = "okay";
 };
 
                         */
                        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
                                >;
                        };
 
                                >;
                        };
 
+                       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: <pin_id mux_node config>
-                        */
-                       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: <pin_id mux_node config>
+                        */
                        pinctrl_qspi1: qspi1grp {
                                thead,pins = <
                                        FM_QSPI1_SCLK    0x0    0x20a
                                >;
                        };
 
-
                        pinctrl_iso7816: iso7816grp {
                                thead,pins = <
                                        FM_QSPI1_SCLK           0x1     0x208
                                >;
                        };
 
+                       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  */
+                               >;
+                       };
                };
 };
 
 &i2c2 {
     clock-frequency = <400000>;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_i2c2>;
        eeprom@50 {
                compatible = "atmel,24c32";
                reg = <0x50>;
 &i2c3 {
     clock-frequency = <400000>;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_i2c3>;
        eeprom@50 {
                compatible = "atmel,24c32";
                reg = <0x50>;
 &i2c4 {
     clock-frequency = <400000>;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_i2c4>;
        eeprom@50 {
                compatible = "atmel,24c32";
                reg = <0x50>;
     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>;
 };
 
 &video0{
+       status = "okay";
        vi_mem_pool_region = <2>;       // vi_mem: framebuffer, region[2]
        channel0 {
                sensor0 {
 
 
 &video1{
+       status = "okay";
        vi_mem_pool_region = <2>;       // vi_mem: framebuffer, region[2]
        channel0 {
                sensor0 {
 };
 
 &video2{
+       status = "okay";
     vi_mem_pool_region = <0>;  // vi_mem: framebuffer, region[0]
        channel0 {
                sensor0 {
 };
 
 &video3{
+       status = "okay";
     vi_mem_pool_region = <0>;  // vi_mem: framebuffer, region[0]
        channel0 {
                sensor0 {
 };
 
 &video4{
+       status = "okay";
     vi_mem_pool_region = <0>;  // vi_mem: framebuffer, region[0]
        channel0 {
                sensor0 {
 };
 
 &video5{
+       status = "okay";
        vi_mem_pool_region = <0>;       // vi_mem: framebuffer, region[0]
        channel0 {
                sensor0 {
 };
 
 &video6{
+       status = "okay";
        vi_mem_pool_region = <1>;       // vi_mem: framebuffer, region[1]
        channel0 {
                sensor0 {
 };
 
 &video7{
+       status = "okay";
        channel0 {
                sensor0 {
                        subdev_name = "vivcam";
 
 
 &video8{
+       status = "okay";
     vi_mem_pool_region = <1>;  // vi_mem: framebuffer, region[1]
        channel0 {
                sensor0 {
 };
 
 &video9{
+       status = "okay";
        channel0 {
                sensor0 {
                        subdev_name = "vivcam";
 
 
 &video10{
+       status = "okay";
        channel0 {
                sensor0 {
                        subdev_name = "vivcam";
 };
 
 &video11{
+       status = "okay";
        channel0 {
                channel_id = <0>;
                status = "okay";
 };
 
 &video12{ // TUNINGTOOL
+       status = "okay";
        channel0 { // CSI2
                sensor0 {
                        subdev_name = "vivcam";
        };
 };
 
+&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";
 };
                >;
        };
 };
+
+&hdmi_tx {
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_hdmi>;
+};
index 8ac65fecad13e65d1510f0cfcf93ee8c2b8e6b9a..49321fbe55bacc238b24a22b1f6f6fa8dd1c6a2a 100644 (file)
                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 {
 &hdmi_tx {
        status = "okay";
 
+       max_width = /bits/ 16 <1280>;
+       max_height = /bits/ 16 <720>;
+
        port@0 {
                /* input */
                hdmi_tx_in: endpoint {
index f4e4f958ae82eadadb8b4634ae8b3bfd6b80e66d..bacc501154e1fc7a06e2cdf8b9042ea83086ee7d 100644 (file)
                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 {
                pinctrl-names = "default";
                key-volumedown {
                        label = "Volume Down Key";
+                       wakeup-source;
                        linux,code = <KEY_VOLUMEDOWN>;
                        debounce-interval = <1>;
                        gpios = <&ao_gpio_porta 11 0x1>;
                };
                key-volumeup {
                        label = "Volume Up Key";
+                       wakeup-source;
                        linux,code = <KEY_VOLUMEUP>;
                        debounce-interval = <1>;
                        gpios = <&ao_gpio_porta 10 0x1>;
index 3b0d92ec7ea572a8652493db0b2e76b65b1d4b33..2c74164e9f83d69008b6916cde77e35dfb140849 100644 (file)
                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 {
                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>;
 
        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";
        };
                pinctrl-names = "default";
                key-volumedown {
                        label = "Volume Down Key";
+                       wakeup-source;
                        linux,code = <KEY_VOLUMEDOWN>;
                        debounce-interval = <1>;
                        gpios = <&ao_gpio_porta 11 0x1>;
                };
                key-volumeup {
                        label = "Volume Up Key";
+                       wakeup-source;
                        linux,code = <KEY_VOLUMEUP>;
                        debounce-interval = <1>;
                        gpios = <&ao_gpio_porta 10 0x1>;
 
 &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 {
        tx-clk-delay = <0x00>; /* for RGMII */
        phy-handle = <&phy_88E1111_0>;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_gmac0>;
 
        mdio0 {
                #address-cells = <1>;
        pull_up;
        wprtn_ignore;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_sdio0>;
 };
 
 &sdhci1 {
                         */
                        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
                                >;
                        };
 
                                >;
                        };
 
+                       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: <pin_id mux_node config>
-                        */
-                       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: <pin_id mux_node config>
+                        */
                        pinctrl_qspi1: qspi1grp {
                                thead,pins = <
                                        FM_QSPI1_SCLK    0x0    0x20a
                                >;
                        };
 
-
                        pinctrl_iso7816: iso7816grp {
                                thead,pins = <
                                        FM_QSPI1_SCLK           0x1     0x208
                                >;
                        };
 
+                       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  */
+                               >;
+                       };
                };
 };
 
 &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 {
 };
 
 &video0{
+       status = "okay";
        vi_mem_pool_region = <2>;       // vi_mem: framebuffer, region[2]
        channel0 {
                sensor0 {
 
 
 &video1{
+       status = "okay";
        vi_mem_pool_region = <2>;       // vi_mem: framebuffer, region[2]
        channel0 {
                sensor0 {
 };
 
 &video2{
+       status = "okay";
     vi_mem_pool_region = <0>;  // vi_mem: framebuffer, region[0]
        channel0 {
                sensor0 {
                        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>;
                        };
 };
 
 &video3{
+       status = "okay";
     vi_mem_pool_region = <0>;  // vi_mem: framebuffer, region[0]
        channel0 {
                sensor0 {
 };
 
 &video4{
+       status = "okay";
     vi_mem_pool_region = <0>;  // vi_mem: framebuffer, region[0]
        channel0 {
                sensor0 {
 };
 
 &video5{
+       status = "okay";
        vi_mem_pool_region = <0>;       // vi_mem: framebuffer, region[0]
        channel0 {
                sensor0 {
 };
 
 &video6{
+       status = "okay";
        vi_mem_pool_region = <1>;       // vi_mem: framebuffer, region[1]
        channel0 {
                sensor0 {
 };
 
 &video7{
+       status = "okay";
        channel0 {
                sensor0 {
                        subdev_name = "vivcam";
 
 
 &video8{
+       status = "okay";
        vi_mem_pool_region = <1>;       // vi_mem: framebuffer, region[1]
     channel0 {
                sensor0 {
 };
 
 &video9{
+       status = "okay";
        channel0 {
                sensor0 {
                        subdev_name = "vivcam";
 
 
 &video10{ // TUNINGTOOL
+       status = "okay";
        channel0 {
                sensor0 {
                        subdev_name = "vivcam";
 };
 
 &video11{
+       status = "okay";
        channel0 { 
                channel_id = <0>;
                status = "okay";
 };
 
 &video12{ // TUNINGTOOL
+       status = "okay";
        channel0 { // CSI2
                sensor0 {
                        subdev_name = "vivcam";
 };
 
 &video14{
+       status = "okay";
     vi_mem_pool_region = <2>;  // vi_mem: framebuffer, region[0]
        status = "okay";
        channel0 {
 
 &hdmi_tx {
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_hdmi>;
 
        port@0 {
                /* input */
index a22548e2867db7440207281272e463462243cc79..c51a610a6a3bead23dd804806e003b34b7cb235a 100644 (file)
                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 {
index 03aa9967b05c93b9788d62fe9880948d601ab042..d94f639119838601728e4faf8f4f4255144ad888 100644 (file)
                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 (file)
index 0000000..317295b
--- /dev/null
@@ -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 (file)
index 0000000..117fe33
--- /dev/null
@@ -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 (file)
index 0000000..4ef7487
--- /dev/null
@@ -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 (file)
index 0000000..d940542
--- /dev/null
@@ -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 = <&reg_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>;
+                        };
+                };
+        };
+};
index a7e88a30146319040e6425236bd0e40e5aca9f3b..58aa809b6d81501f1a02b90abe972f1d902a8230 100644 (file)
                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 {
                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";
        };
 
         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 {
        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";
        };
 
        spidev@1 {
+               status = "disable";
                compatible = "spidev";
                #address-cells = <0x1>;
                #size-cells = <0x1>;
 
 &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 {
        tx-clk-delay = <0x00>; /* for RGMII */
        phy-handle = <&phy_88E1111_0>;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_gmac0>;
 
        mdio0 {
                #address-cells = <1>;
        tx-clk-delay = <0x00>; /* for RGMII */
        phy-handle = <&phy_88E1111_1>;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_gmac1>;
 };
 
 &emmc {
        pull_up;
        wprtn_ignore;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_sdio0>;
 };
 
 &sdhci1 {
                         */
                        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
                                >;
                        };
 
                                >;
                        };
 
+                       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  */
                                >;
                        };
                };
                         * Pin Configuration Node:
                         * Format: <pin_id mux_node config>
                         */
-                       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 = <
 &i2c0 {
     clock-frequency = <400000>;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_i2c0>;
 
        pcal6408ahk_b: gpio@20 {
                compatible = "nxp,pca9557";
 &i2c1 {
     clock-frequency = <400000>;
        status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_i2c1>;
 
        pcal6408ahk_c: gpio@20 {
                compatible = "nxp,pca9557";
 &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";
        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>;
                        };
                };
        };
+       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>;
                        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>;
                        };
                };
        };
        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";
                        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>;
                        };
                };
        };
        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";
                        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>;
                        };
        };
 };
 
-&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";
         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 */
 
 &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 {
index cefaf9105f52686b12f4c9ab553a7e92fbeb26e7..5a5aa79113502cad4c1678ce9dfc41bc26b1c5a6 100644 (file)
@@ -5,7 +5,7 @@
 
 /dts-v1/;
 
-#include "light-lpi4a.dts"
+#include "light-lpi4a-hdmi.dts"
 
 
 &light_iopmp {
index 0591f6ce163c754a2a7531e628ef2fb3753200cd..9883f90651f0a223a5b67bd03e0a0e6bdc2811d4 100644 (file)
@@ -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"
 };
 
 &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 = <&reg_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>;
+                        };
+                };
+        };
 };
index 4bd13a4199a63c786b6ba5394689e88856bedf38..f3f7c00bf8a537fbf0aa8f2003405351fa6b63a9 100644 (file)
@@ -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";
 };
 
 &video2 {
-       status = "okay";
+       status = "disabled";
        channel0 {
                channel_id = <0>;
                status = "okay";
 };
 
 &video3 {
-       status = "okay";
+       status = "disabled";
                channel0 {
                channel_id = <0>;
                status = "okay";
 };
 
 &video4 {
-       status = "okay";
+       status = "disabled";
        channel0 {
                channel_id = <0>;
                status = "okay";
 };
 
 &video5 {
-       status = "okay";
+       status = "disabled";
        channel0 {
                channel_id = <0>;
                status = "okay";
 
 
 &video6 {
-       status = "okay";
+       status = "disabled";
        channel0 {
                channel_id = <0>;
                status = "okay";
 
 
 &video7{
-       status = "okay";
+       status = "disabled";
        channel0 {
                channel_id = <0>;
                status = "okay";
 
 
 &video8{
-       status = "okay";
+       status = "disabled";
        channel0 {
                channel_id = <0>;
                status = "okay";
 
 
 &video9 { //IR debug
-       status = "okay";
+       status = "disabled";
        channel0 {
                channel_id = <0>;
                status = "okay";
 
 
 &video10{ // TUNING TOOL
-       status = "okay";
+       status = "disabled";
        channel0 { // CSI2X2_B
                status = "okay";
                sensor0 {
 
 
 &video11{
-       status = "okay";
+       status = "disabled";
        channel0 {
                channel_id = <0>;
                status = "okay";
 
 
 &video12{ // TUNING TOOL
-       status = "okay";
+       status = "disabled";
        channel0 { // CSI2
                status = "okay";
                sensor0 {
index e66b5ab35c16436d16c509003cfc7b669b09efe4..292168ae0355d5a5f74a3069f0f227c9870e15b7 100644 (file)
@@ -16,6 +16,7 @@
 #include <dt-bindings/clock/light-dspsys.h>
 #include <dt-bindings/clock/light-audiosys.h>
 #include <dt-bindings/firmware/thead/rsrc.h>
+#include <dt-bindings/clock/light-miscsys.h>
 #include <dt-bindings/soc/thead,light-iopmp.h>
 #include <dt-bindings/thermal/thermal.h>
 #include <dt-bindings/reset/light-reset.h>
                                interrupt-controller;
                        };
                };
+
                c910_1: cpu@1 {
                        device_type = "cpu";
                        reg = <1>;
                                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 {
                        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>;
                        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>;
                        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;
                        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;
                        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;
                        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;
                        };
                };
 
-               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";
                };
 
                };
 
                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>;
                        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>;
                        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>;
                };
                        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>;
                };
                        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 */
                              <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>,
                        interrupts = <74>;
                        clocks = <&dummy_clock_rtc>;
                        clock-names = "rtc";
+                       wakeup-source;
                        status = "okay";
                };
 
                        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;
                                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";
                        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>;
                        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>;
                         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>;
                        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>;
                        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>;
                        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 {
                        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 {
                        #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>;
                        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";
                };
                        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";
                };
                        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";
                };
                        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";
                };
                        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";
                };
                        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";
                };
                        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";
                };
                        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";
                };
                        #dma-cells = <1>;
                        clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_SPDIF0>;
                        clock-names = "pclk";
+                       id = <0>;
                        status = "disabled";
                };
 
                        #dma-cells = <1>;
                        clocks = <&audiosys_clk_gate LIGHT_CLKGEN_AUDIO_SPDIF1>;
                        clock-names = "pclk";
+                       id = <1>;
                        status = "disabled";
                };
 
                        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>;
                        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>;
                        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>;
                        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>;
                        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>;
                        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";
                };
 
                 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>;
                        #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";
+               };
+
        };
 
 };
index 35db128d726f12c9b0183f62fc017b78d68dd983..10e1987e5a2254cdd60c0ffebb30a9c708fd64a6 100644 (file)
@@ -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
index 28c91fb562ca992f652b1cbe82f2e2256d48a04b..f9783656bee277bfa4802db9c68960cc94752704 100644 (file)
@@ -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
+
index 2c2cda6cc1c5352ef794e0db7d941c72f6229383..75583d95885aa5d90abc30c16bbf7af406fa3f71 100644 (file)
@@ -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
index 9c992a88d858fe6105f16978849f3a564d42b85f..002464eef45b76e177e7eebdb90c71eb759eeb54 100644 (file)
 #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 (file)
index 0000000..56e4b76
--- /dev/null
@@ -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 <linux/init.h>
+#include <linux/sched.h>
+#include <linux/threads.h>
+
+/**
+ * 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 */
index 1dcd1e1ad1eef43b10b4999b902ed1db1a6fea24..9c715283b12b6096049905de75ad670dc518dd31 100644 (file)
@@ -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
  *
index 072c91f65da8cd5db5a242f4a9597f40875b74d1..81c999a3eed42568f94b70529b4a32555fe4431c 100644 (file)
@@ -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 (file)
index 0000000..8be391c
--- /dev/null
@@ -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 <asm/ptrace.h>
+
+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
index 7f25f2d8c0ce6d981179bb16c592d71eaf862440..ba6c1520b0d1e62a49555391e5b646cbe5a94ac6 100644 (file)
@@ -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
 
index 431afa56cb52d2aad910756d72455c68fb3f615a..9691d08bdd8c48f07a13c69b569a10e8f07eb38f 100644 (file)
 #include <linux/sched.h>
 #include <asm/thread_info.h>
 #include <asm/ptrace.h>
+#include <asm/cpu_ops_sbi.h>
+#include <asm/suspend.h>
+
+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);
 }
index 685fae72b7f5c758d83e1ebe4fdf40eff7235848..2e16f6732cdf8fba7232942f8238bc82ed26a618 100644 (file)
@@ -7,13 +7,22 @@
 
 #include <linux/init.h>
 #include <linux/mm.h>
+#include <linux/sched/task_stack.h>
 #include <asm/cpu_ops.h>
+#include <asm/cpu_ops_sbi.h>
 #include <asm/sbi.h>
 #include <asm/smp.h>
 
 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;
 }
index 6c11fd130a0f3cde34b701cd69589bfef769a34c..acafb4cf4b0d582e27e8a8fb11ace483c3ff653f 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/thread_info.h>
 #include <asm/page.h>
 #include <asm/csr.h>
+#include <asm/cpu_ops_sbi.h>
 #include <asm/hwcap.h>
 #include <asm/image.h>
 #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
index e0cf246d4864464394644e5675f4eb6febf41d04..a6c8b8c304b36a741db4a32f9c1d1588fe74058e 100644 (file)
@@ -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,
index 0d6f79dc36c075633d7a5b70668b8cd8373bc7d1..8ad2ab08da01ef2776599c115b8cf3790dce44cc 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/string.h>
 #include <asm/switch_to.h>
 #include <asm/thread_info.h>
+#include <asm/cpuidle.h>
 
 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();
 }
 
index 85c81d65f69413d643aa24ac0ba599e78eab319d..bd82edbbd1d2eb65e48128ac53510747c1daa44b 100644 (file)
@@ -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 (file)
index 0000000..9ba24fb
--- /dev/null
@@ -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 <linux/ftrace.h>
+#include <asm/csr.h>
+#include <asm/suspend.h>
+
+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 (file)
index 0000000..4b07b80
--- /dev/null
@@ -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 <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/csr.h>
+
+       .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 (file)
index 0000000..1ac4b2e
--- /dev/null
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ *     Atish Patra <atish.patra@wdc.com>
+ */
+
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/kvm_host.h>
+#include <asm/csr.h>
+#include <asm/sbi.h>
+#include <asm/kvm_vcpu_sbi.h>
+
+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,
+};
index a8b8538803683a3202c82dce9a483f2a9fa39f8d..c8dc775dbaf255e73e0dbce98211f36d623840b8 100644 (file)
@@ -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;
index 2fe47c063a5370c52c0992c03f3d9226ac292021..fb33e7560454bc08b0061e14452259dcfdfce847 100644 (file)
@@ -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);
index cad975e8ede45557b7a6261635512742c56452fc..6b6368ac0b630168963a2210a65fc309b0e9a778 100644 (file)
@@ -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
index 07be9f11aeaf09259db63e48a2acb14469559a56..03db9d44d3bb8a4234d49f157f4627230d12572a 100644 (file)
@@ -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
index e68a5d4e6151e18b2191ef94dbeadfe82c6caf3f..54cb544394101ff72683f9ab7041e04625c710e0 100644 (file)
 
 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 (file)
index 0000000..37a000f
--- /dev/null
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Alibaba Group Holding Limited.
+ */
+#include <dt-bindings/clock/light-miscsys.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#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 <lw312886@linux.alibaba.com>");
+MODULE_AUTHOR("Esther.Z <Esther.Z@linux.alibaba.com>");
+MODULE_DESCRIPTION("Thead Light Fullmask miscsys clock gate provider");
+MODULE_LICENSE("GPL v2");
index 78613188da70aa2a7dd3b07540f8383e09fb5486..1a01df1f2b3f25abb71dc915cafb7c4aaa50e140 100644 (file)
@@ -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,
index 0a7eaa60f17c8a08fcb74a6bfb2c69ef5b409117..df9ef3baa377e7c0fcc06b99c6fc7bde0fc1b173 100644 (file)
@@ -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) {
index b7a1aa31bbd3030a9da5717aebec42d74532dd51..ff71dd662880d9c6874962c3201dd38f9944665a 100644 (file)
@@ -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"
index 334f83e56120c0022311c9f3f11ecaad5d0e67d7..be12a9ca78f029bd484757c9734cbbd936cacb6a 100644 (file)
@@ -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,
index ded4ede19adf70ebb627e35d9b7c6ae0101a4b65..a5609bf075723c0c6b536201bbc03ed63d4e3da3 100644 (file)
@@ -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.
index d5d77f8d6c9fc9e4c0852dcfe8f091f582e19530..1516ab9807eea3418875213be089377f99afb71f 100644 (file)
@@ -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
index ff2c3f8e4668ae07b674b6d4a1ce036c563d6762..755bbdfc5b82ff67cdd3878b6083113181758a9b 100644 (file)
@@ -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);
-}
index d8e925e84c27af3d400f6f972d4ffe24dcf0c54b..4e132640ed64851a5990fca21eab08142639d095 100644 (file)
@@ -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 (file)
index 0000000..019eac1
--- /dev/null
@@ -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 <linux/cpuidle.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_pm.h>
+#include <linux/cpu_cooling.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <asm/cpuidle.h>
+#include <asm/sbi.h>
+#include <asm/suspend.h>
+
+#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 (file)
index 0000000..b371655
--- /dev/null
@@ -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 <ulf.hansson@linaro.org>
+ *
+ * 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 <linux/cpu.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#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 (file)
index 0000000..a95483d
--- /dev/null
@@ -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
index d83ab5017564cfcf33961e4497ba1beb912fdbed..c2928a8ac2cf0dd9e0d31618fd25d4c130a5df46 100644 (file)
@@ -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" },
index 803e98258b839ba6821eb77a776a1c015dd413f1..fba1ecd3f038db0ac92c42a01c0aa888cea5a83a 100644 (file)
@@ -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 */
index 8fbd808d73be0a2a834c04d99be8ba2e42ba84a4..ad2d49a0845c9866e85fbc7f82a754cbb23f16b1 100644 (file)
@@ -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,
 };
index a78167b2c9ca2a5dee7566d5f4f776a2fb30882f..ba490848f52379367ef311900a4f7e98998b7794 100644 (file)
@@ -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,
        },
index edc3112cf15ecafde2ecd83ea7e73dbf9a7f5c3b..1010faa99167d1f9c4672347c5891db68a80a55a 100644 (file)
@@ -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;
 }
 
index 619f4620d3761943f68587b6e5bd65bcf88a8c91..6d06a60a8041fcc1246a2774ae798c0ed2a7d00c 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/regmap.h>
 #include <linux/dma-mapping.h>
 #include <linux/spinlock.h>
-
 #include <media/cec-notifier.h>
 
 #include <uapi/linux/media-bus-format.h>
@@ -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 <s.hauer@pengutronix.de>");
 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
 MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
index d131b7e7ec2515f3e8462094856bb00483716322..2ca5f4b6d81bf083f4c27b8c68f271905d1bd44b 100644 (file)
@@ -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
index fabee1b790abc0154d973b71e9fc60f77b261c9e..1ca5576d6d70b12cbda2708d7c2f04ab9e83c7ca 100644 (file)
@@ -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 (file)
index 0000000..03f1595
--- /dev/null
@@ -0,0 +1,328 @@
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+struct hx8279_panel_desc {
+       const struct drm_display_mode *display_mode;
+
+       unsigned long mode_flags;
+       enum mipi_dsi_pixel_format format;
+       unsigned int lanes;
+       // const struct jadard_panel_cmd *on_cmds;
+       // unsigned int on_cmds_num;
+};
+
+struct panel_info {
+       struct drm_panel base;
+       struct mipi_dsi_device *link;
+       const struct hx8279_panel_desc *desc;
+
+       struct gpio_desc        *reset;
+       struct regulator        *hsvcc;
+       struct regulator        *vspn3v3;
+
+       bool prepared;
+       bool enabled;
+};
+
+static inline struct panel_info *to_panel_info(struct drm_panel *panel)
+{
+       return container_of(panel, struct panel_info, base);
+}
+
+static int hx8279_panel_disable(struct drm_panel *panel)
+{
+       struct panel_info *pinfo = to_panel_info(panel);
+       int err;
+
+       if (!pinfo->enabled)
+               return 0;
+
+       err = mipi_dsi_dcs_set_display_off(pinfo->link);
+       if (err < 0) {
+               dev_err(panel->dev, "failed to set display off: %d\n", err);
+               return err;
+       }
+
+       pinfo->enabled = false;
+
+       return 0;
+}
+
+static int hx8279_panel_unprepare(struct drm_panel *panel)
+{
+       struct panel_info *pinfo = to_panel_info(panel);
+       int err;
+
+       if (!pinfo->prepared)
+               return 0;
+
+       err = mipi_dsi_dcs_set_display_off(pinfo->link);
+       if (err < 0)
+               dev_err(panel->dev, "failed to set display off: %d\n", err);
+
+       err = mipi_dsi_dcs_enter_sleep_mode(pinfo->link);
+       if (err < 0)
+               dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);
+
+       /* sleep_mode_delay: 1ms - 2ms */
+       usleep_range(1000, 2000);
+
+       gpiod_set_value(pinfo->reset, 1);
+       regulator_disable(pinfo->hsvcc);
+       regulator_disable(pinfo->vspn3v3);
+
+       pinfo->prepared = false;
+
+       return 0;
+}
+
+static int hx8279_panel_prepare(struct drm_panel *panel)
+{
+       struct panel_info *pinfo = to_panel_info(panel);
+       int ret;
+
+       if (pinfo->prepared)
+               return 0;
+       gpiod_set_value(pinfo->reset, 0);
+
+       /* Power the panel */
+       ret = regulator_enable(pinfo->hsvcc);
+       if (ret) {
+               dev_err(pinfo->base.dev, "Failed to enable hsvcc supply: %d\n", ret);
+               return ret;
+       }
+
+       usleep_range(1000, 2000);
+       ret = regulator_enable(pinfo->vspn3v3);
+       if (ret) {
+               dev_err(pinfo->base.dev, "Failed to enable vspn3v3 supply: %d\n", ret);
+               goto fail;
+       }
+       usleep_range(5000, 6000);
+
+       gpiod_set_value(pinfo->reset, 1);
+       msleep(180);
+
+       pinfo->prepared = true;
+
+       return 0;
+
+fail:
+       gpiod_set_value(pinfo->reset, 1);
+       regulator_disable(pinfo->hsvcc);
+       return ret;
+}
+
+static int hx8279_panel_enable(struct drm_panel *panel)
+{
+       struct panel_info *pinfo = to_panel_info(panel);
+       int ret;
+       u8 id1;
+
+       if (pinfo->enabled)
+               return 0;
+
+       ret = mipi_dsi_dcs_exit_sleep_mode(pinfo->link);
+       if (ret < 0) {
+               dev_err(panel->dev, "failed to exit sleep mode: %d\n", ret);
+               return ret;
+       }
+
+       msleep(120);
+
+       ret = mipi_dsi_dcs_set_display_on(pinfo->link);
+       if (ret < 0) {
+               dev_err(panel->dev, "failed to set display on: %d\n", ret);
+               return ret;
+       }
+
+       pinfo->enabled = true;
+
+       return 0;
+}
+
+static int hx8279_panel_get_modes(struct drm_panel *panel,
+                              struct drm_connector *connector)
+{
+       struct panel_info *pinfo = to_panel_info(panel);
+       const struct drm_display_mode *m = pinfo->desc->display_mode;
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_duplicate(connector->dev, m);
+       if (!mode) {
+               dev_err(pinfo->base.dev, "failed to add mode %ux%u@%u\n",
+                       m->hdisplay, m->vdisplay, drm_mode_vrefresh(m));
+               return -ENOMEM;
+       }
+
+       drm_mode_set_name(mode);
+
+       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+       drm_mode_probed_add(connector, mode);
+
+       connector->display_info.width_mm = mode->width_mm;
+       connector->display_info.height_mm = mode->height_mm;
+
+       return 1;
+}
+
+static const struct drm_panel_funcs panel_funcs = {
+       .disable = hx8279_panel_disable,
+       .unprepare = hx8279_panel_unprepare,
+       .prepare = hx8279_panel_prepare,
+       .enable = hx8279_panel_enable,
+       .get_modes = hx8279_panel_get_modes,
+};
+
+static const struct drm_display_mode hx8279_default_mode = {
+       .clock          = 196500,
+       .hdisplay       = 1200,
+       .hsync_start    = 1200 + 96,
+       .hsync_end      = 1200 + 96 + 128,
+       .htotal         = 1200 + 96 + 128 + 224,
+
+       .vdisplay       = 1920,
+       .vsync_start    = 1920 + 3,
+       .vsync_end      = 1920 + 3 + 10,
+       .vtotal         = 1920 + 3 + 10 + 56,
+
+       .width_mm       = 62,
+       .height_mm      = 110,
+       .flags          = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static const struct hx8279_panel_desc hx8279_desc = {
+       .display_mode = &hx8279_default_mode,
+       .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO_BURST,
+       .format = MIPI_DSI_FMT_RGB888,
+       .lanes = 4,
+//     .on_cmds = hx8279_on_cmds,
+//     .on_cmds_num = ARRAY_SIZE(hx8279_on_cmds),
+};
+
+static const struct of_device_id panel_of_match[] = {
+       {
+               .compatible = "himax,hx8279",
+               .data = &hx8279_desc,
+       },
+       {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, panel_of_match);
+
+static int hx8279_panel_add(struct panel_info *pinfo)
+{
+       struct device *dev = &pinfo->link->dev;
+       int ret;
+
+       pinfo->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(pinfo->reset))
+               return dev_err_probe(dev, PTR_ERR(pinfo->reset),
+                               "Couldn't get our reset GPIO\n");
+
+       pinfo->hsvcc =  devm_regulator_get(dev, "hsvcc");
+       if (IS_ERR(pinfo->hsvcc))
+               return dev_err_probe(dev, PTR_ERR(pinfo->hsvcc),
+                               "Failed to request hsvcc regulator\n");
+
+       pinfo->vspn3v3 =  devm_regulator_get(dev, "vspn3v3");
+       if (IS_ERR(pinfo->vspn3v3))
+               return dev_err_probe(dev, PTR_ERR(pinfo->vspn3v3),
+                               "Failed to request vspn3v3 regulator\n");
+
+       drm_panel_init(&pinfo->base, dev, &panel_funcs,
+                      DRM_MODE_CONNECTOR_DSI);
+
+       ret = drm_panel_of_backlight(&pinfo->base);
+       if (ret)
+               return ret;
+
+       drm_panel_add(&pinfo->base);
+
+       return 0;
+}
+
+static int hx8279_panel_probe(struct mipi_dsi_device *dsi)
+{
+       struct panel_info *pinfo;
+       const struct hx8279_panel_desc *desc;
+       int err;
+
+       pinfo = devm_kzalloc(&dsi->dev, sizeof(*pinfo), GFP_KERNEL);
+       if (!pinfo)
+               return -ENOMEM;
+
+       desc = of_device_get_match_data(&dsi->dev);
+       dsi->mode_flags = desc->mode_flags;
+       dsi->format = desc->format;
+       dsi->lanes = desc->lanes;
+       pinfo->desc = desc;
+
+       pinfo->link = dsi;
+       mipi_dsi_set_drvdata(dsi, pinfo);
+
+       err = hx8279_panel_add(pinfo);
+       if (err < 0)
+               return err;
+
+       err = mipi_dsi_attach(dsi);
+       if (err < 0)
+               drm_panel_remove(&pinfo->base);
+
+       return err;
+}
+
+static int hx8279_panel_remove(struct mipi_dsi_device *dsi)
+{
+       struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
+       int err;
+
+       err = hx8279_panel_disable(&pinfo->base);
+       if (err < 0)
+               dev_err(&dsi->dev, "failed to disable panel: %d\n", err);
+
+       err = hx8279_panel_unprepare(&pinfo->base);
+       if (err < 0)
+               dev_err(&dsi->dev, "failed to unprepare panel: %d\n", err);
+
+       err = mipi_dsi_detach(dsi);
+       if (err < 0)
+               dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
+
+       drm_panel_remove(&pinfo->base);
+
+       return 0;
+}
+
+static void hx8279_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+       struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
+
+       hx8279_panel_disable(&pinfo->base);
+       hx8279_panel_unprepare(&pinfo->base);
+}
+
+static struct mipi_dsi_driver panel_driver = {
+       .driver = {
+               .name = "panel-himax8279",
+               .of_match_table = panel_of_match,
+       },
+       .probe = hx8279_panel_probe,
+       .remove = hx8279_panel_remove,
+       .shutdown = hx8279_panel_shutdown,
+};
+module_mipi_dsi_driver(panel_driver);
index 1e4dd015635abebcfb089d21f2aa12f07c335f07..19f3d003acd99943b49b8d30f352f934c4e5a1d9 100644 (file)
@@ -30,7 +30,7 @@ struct jadard_panel_desc {
        unsigned long mode_flags;
        enum mipi_dsi_pixel_format format;
        unsigned int lanes;
-//     const struct jadard_panel_cmd *on_cmds;
+       //const struct jadard_panel_cmd *on_cmds;
        unsigned int on_cmds_num;
 };
 
@@ -52,20 +52,20 @@ static inline struct panel_info *to_panel_info(struct drm_panel *panel)
        return container_of(panel, struct panel_info, base);
 }
 
-//static int jadard_send_mipi_cmds(struct drm_panel *panel, const struct jadard_panel_cmd *cmds)
-//{
-//     struct panel_info *pinfo = to_panel_info(panel);
-//     unsigned int i = 0;
-//     int err;
-//
-//     for (i = 0; i < pinfo->desc->on_cmds_num; i++) {
-//             err = mipi_dsi_dcs_write_buffer(pinfo->link, &(cmds[i].cmddata[0]), cmds[i].cmdlen);
-//             if (err < 0)
-//                     return err;
-//     }
-//
-//     return 0;
-//}
+// static int jadard_send_mipi_cmds(struct drm_panel *panel, const struct jadard_panel_cmd *cmds)
+// {
+//     struct panel_info *pinfo = to_panel_info(panel);
+//     unsigned int i = 0;
+//     int err;
+
+//     for (i = 0; i < pinfo->desc->on_cmds_num; i++) {
+//             err = mipi_dsi_dcs_write_buffer(pinfo->link, &(cmds[i].cmddata[0]), cmds[i].cmdlen);
+//             if (err < 0)
+//                     return err;
+//     }
+
+//     return 0;
+// }
 
 static int jadard_disable(struct drm_panel *panel)
 {
@@ -353,4 +353,4 @@ module_mipi_dsi_driver(jadard_driver);
 MODULE_AUTHOR("Jagan Teki <jagan@edgeble.ai>");
 MODULE_AUTHOR("Stephen Chen <stephen@radxa.com>");
 MODULE_DESCRIPTION("Jadard JD9365DA-H3 WXGA DSI panel");
-MODULE_LICENSE("GPL");
\ No newline at end of file
+MODULE_LICENSE("GPL");
index 745fa7e8fa07320001aa479e049fa07f3c185a04..2106bb41edd636eeb06838f103ecdccbb3f6e0ec 100644 (file)
@@ -54,10 +54,20 @@ static const struct drm_encoder_helper_funcs dw_hdmi_light_encoder_helper_funcs
        .atomic_check   = dw_hdmi_light_encoder_atomic_check,
 };
 
+struct dw_hdmi_light_private dw_hdmi_priv_data = {
+       .max_pixclock = 594000,
+       .max_width    = 0,
+       .max_height   = 0,
+};
+
 static int dw_hdmi_light_bind(struct device *dev, struct device *master,
                              void *data)
 {
        int ret = 0;
+       u32 max_pixclock = 0;
+       u16 max_width    = 0;
+       u16 max_height   = 0;
+       int property_ret = 0;
        struct platform_device *pdev = to_platform_device(dev);
        struct device_node *np = dev->of_node;
        const struct of_device_id *match;
@@ -74,6 +84,30 @@ static int dw_hdmi_light_bind(struct device *dev, struct device *master,
        if (unlikely(!match))
                return -ENODEV;
 
+       property_ret = of_property_read_u32(np, "max_pixclock", &max_pixclock);
+       if(property_ret == 0){
+               printk(KERN_INFO "Hdmi max pixel clock = %d\n", max_pixclock);
+               if(dw_hdmi_priv_data.max_pixclock > max_pixclock){
+                       dw_hdmi_priv_data.max_pixclock = max_pixclock;
+                       printk(KERN_INFO"Hdmi max pixel clock adjust to %d\n", max_pixclock);
+               }
+               else{
+                       printk(KERN_INFO"Hdmi max pixel clock not take effect\n");
+               }
+       }
+
+       property_ret = of_property_read_u16(np, "max_width", &max_width);
+       if(property_ret == 0){
+               dw_hdmi_priv_data.max_width = max_width;
+       }
+
+       property_ret = of_property_read_u16(np, "max_height", &max_height);
+       if(property_ret == 0){
+               dw_hdmi_priv_data.max_height = max_height;
+       }
+
+       light_hdmi_drv_data.priv_data = (void*)&dw_hdmi_priv_data;
+
        plat_data = match->data;
        hdmi->dev = &pdev->dev;
        encoder = &hdmi->encoder;
@@ -90,7 +124,6 @@ static int dw_hdmi_light_bind(struct device *dev, struct device *master,
                ret = PTR_ERR(hdmi->dw_hdmi);
                drm_encoder_cleanup(encoder);
        }
-
        pm_runtime_enable(dev);
 
        return ret;
@@ -114,7 +147,6 @@ static const struct component_ops dw_hdmi_light_ops = {
 static int dw_hdmi_light_probe(struct platform_device *pdev)
 {
        struct light_hdmi *hdmi;
-
        hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
        if (!hdmi)
                return -ENOMEM;
@@ -146,11 +178,22 @@ static int hdmi_runtime_resume(struct device *dev)
        return dw_hdmi_runtime_resume(hdmi->dw_hdmi);
 }
 #endif
-
+#ifdef CONFIG_PM_SLEEP
+static int hdmi_resume(struct device *dev)
+{
+       struct light_hdmi *hdmi = dev_get_drvdata(dev);
+        dev_info(dev,"hdmi resume\n");
+        dw_hdmi_resume(hdmi->dw_hdmi);
+       return 0;
+}
+#endif
 static const struct dev_pm_ops dw_hdmi_pm_ops = {
     SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
                                 pm_runtime_force_resume)
     SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL)
+    #ifdef CONFIG_PM_SLEEP
+     SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL,hdmi_resume)
+    #endif
 };
 
 struct platform_driver dw_hdmi_light_platform_driver = {
index 11379e5120726700164cc4011a7ceb6aedc5e1bb..1d346efcb0dd3f6b3356e20e4e76e06e10349a54 100644 (file)
@@ -126,6 +126,12 @@ struct dw_hdmi_mpll_gen_config{
        } voltage;
 };
 
+struct dw_hdmi_light_private {
+       u32 max_pixclock;
+       u16 max_width;
+       u16 max_height;
+};
+
 static const struct dw_hdmi_mpll_gen_config mpll_configs[] = {
        {
                .pixelclock = 25175,
@@ -587,13 +593,36 @@ dw_hdmi_tx_phy_gen2_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
 {
        int i;
 
-        if (mode->clock < 13500)
-                return MODE_CLOCK_LOW;
-        else if (mode->clock > 594000)
-                return MODE_CLOCK_HIGH;
+       u32 max_clock  = 594000;
+       u16 max_height = 0;
+       u16 max_width  = 0;
+
+       struct dw_hdmi_light_private *dw_hdmi_private = (struct dw_hdmi_light_private*)data;
+       if(dw_hdmi_private != NULL){
+               max_clock  = dw_hdmi_private->max_pixclock;
+               max_height = dw_hdmi_private->max_height;
+               max_width  = dw_hdmi_private->max_width;
+       }
+
+       if (mode->clock < 13500)
+       {
+               return MODE_CLOCK_LOW;
+       }
+       else if (mode->clock > max_clock)
+       {
+               return MODE_CLOCK_HIGH;
+       }
+
+       if((max_width > 0) && (mode->hdisplay > max_width)){
+               return MODE_H_ILLEGAL;
+       }
+
+       if((max_height > 0) && (mode->vdisplay > max_height)){
+               return MODE_V_ILLEGAL;
+       }
 
        for (i = 0; i < ARRAY_SIZE(mpll_configs); i++) {
-               if (abs(mode->clock - mpll_configs[i].pixelclock) <= 100)
+       if (abs(mode->clock - mpll_configs[i].pixelclock) <= 100)
                        return MODE_OK;
                else if (mode->clock < mpll_configs[i].pixelclock)
                        break;
index 71a52cbf43cb4605a82c87a47d535fe598849a4b..7d0a903ca8a9a7422683374f5569511a33c12a7d 100644 (file)
@@ -11,6 +11,8 @@
 #include <linux/of_graph.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
 
 #include <drm/drm_framebuffer.h>
 #include <drm/drm_atomic.h>
@@ -345,7 +347,7 @@ static void vs_dc_enable(struct device *dev, struct drm_crtc *crtc)
     display.enable = true;
 
     if (dc->pix_clk_rate[display.id] != mode->clock) {
-       clk_set_rate(dc->pixclk[display.id], mode->clock * 1000);
+           clk_set_rate(dc->pixclk[display.id], mode->clock * 1000);
         dc->pix_clk_rate[display.id] = mode->clock;
     }
 
@@ -970,7 +972,6 @@ static int dc_bind(struct device *dev, struct device *master, void *data)
     struct vs_plane_info *plane_info;
     int i, ret;
     u32 ctrc_mask = 0;
-
     if (!drm_dev || !dc) {
         dev_err(dev, "devices are not created.\n");
         return -ENODEV;
@@ -1081,7 +1082,6 @@ static int dc_bind(struct device *dev, struct device *master, void *data)
     }
 
     dc->funcs = &dc_funcs;
-
     vs_drm_update_pitch_alignment(drm_dev, dc_info->pitch_alignment);
     return 0;
 
@@ -1156,6 +1156,7 @@ static int dc_probe(struct platform_device *pdev)
     struct vs_dc *dc;
     int irq, ret, i;
     char pixclk[16];
+    struct device_node *np = dev->of_node;
 
     dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL);
     if (!dc)
@@ -1175,6 +1176,13 @@ static int dc_probe(struct platform_device *pdev)
         return PTR_ERR(dc->hw.mmu_base);
 #endif
 
+    dc->hw.vosys_regmap = syscon_regmap_lookup_by_phandle(np, "vosys-regmap");
+    if (IS_ERR(dc->hw.vosys_regmap))
+    {
+        dev_err(dev, "Failed to remap vosys\n");
+        return PTR_ERR(dc->hw.vosys_regmap);
+    }
+
     irq = platform_get_irq(pdev, 0);
     ret = devm_request_irq(dev, irq, dc_isr, 0, dev_name(dev), dc);
     if (ret < 0) {
@@ -1189,20 +1197,20 @@ static int dc_probe(struct platform_device *pdev)
     }
 
     for (i = 0; i < DC_DISPLAY_NUM; i++) {
-       snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pix_clk", i);
-       dc->pix_clk[i] = devm_clk_get_optional(dev, pixclk);
+        snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pix_clk", i);
+        dc->pix_clk[i] = devm_clk_get_optional(dev, pixclk);
 
-       if (IS_ERR(dc->pix_clk[i])) {
-           dev_err(dev, "failed to get pix_clk %d source\n", i);
-           return PTR_ERR(dc->pix_clk[i]);
-       }
+        if (IS_ERR(dc->pix_clk[i])) {
+            dev_err(dev, "failed to get pix_clk %d source\n", i);
+            return PTR_ERR(dc->pix_clk[i]);
+        }
 
-       snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pixclk", i);
-       dc->pixclk[i] = devm_clk_get_optional(dev, pixclk);
-       if (IS_ERR(dc->pixclk[i])) {
-           dev_err(dev, "failed to get pixclk %d source\n", i);
-           return PTR_ERR(dc->pixclk[i]);
-       }
+        snprintf(pixclk, ARRAY_SIZE(pixclk), "%s%d", "pixclk", i);
+        dc->pixclk[i] = devm_clk_get_optional(dev, pixclk);
+        if (IS_ERR(dc->pixclk[i])) {
+            dev_err(dev, "failed to get pixclk %d source\n", i);
+            return PTR_ERR(dc->pixclk[i]);
+        }
     }
 
     dc->axi_clk = devm_clk_get_optional(dev, "axi_clk");
@@ -1229,6 +1237,9 @@ static int dc_probe(struct platform_device *pdev)
         return PTR_ERR(dc->dpu1pll_clk);
     }
 
+    dc->def_pix_clk_rate[0] = clk_get_rate(dc->dpu0pll_clk)/1000;
+    dc->def_pix_clk_rate[1] = clk_get_rate(dc->dpu1pll_clk)/1000;
+
     dc_get_display_pll(dev, dc);
 
     dev_set_drvdata(dev, dc);
@@ -1259,8 +1270,10 @@ static int dc_runtime_suspend(struct device *dev)
        dev_dbg(dev, "%s\n", __func__);
        clk_disable_unprepare(dc->axi_clk);
 
-       for (i = 0; i < DC_DISPLAY_NUM; i++)
+       for (i = 0; i < DC_DISPLAY_NUM; i++){
                clk_disable_unprepare(dc->pix_clk[i]);
+        clk_disable_unprepare(dc->pixclk[i]);
+    }
 
        clk_disable_unprepare(dc->core_clk);
 
@@ -1299,9 +1312,14 @@ static int dc_runtime_resume(struct device *dev)
 
        for (i = 0; i < DC_DISPLAY_NUM; i++) {
                ret = clk_prepare_enable(dc->pix_clk[i]);
+        if (ret < 0) {
+                       dev_err(dev, "failed to prepare/enable pix_clk %d\n", i);
+                       return ret;
+               }
 
+        ret = clk_prepare_enable(dc->pixclk[i]);
                if (ret < 0) {
-                       dev_err(dev, "failed to prepare/enable pix_clk %d\n", i);
+                       dev_err(dev, "failed to prepare/enable pixclk %d\n", i);
                        return ret;
                }
        }
@@ -1315,11 +1333,42 @@ static int dc_runtime_resume(struct device *dev)
        return 0;
 }
 #endif
+#ifdef CONFIG_PM_SLEEP
 
+static int dc_suspend(struct device *dev)
+{
+    int i;
+       struct vs_dc *dc = dev_get_drvdata(dev);
+
+    for (i = 0; i < DC_DISPLAY_NUM; i++){
+        dc->pix_clk_rate[i] = dc->def_pix_clk_rate[i];
+        clk_set_rate(dc->pixclk[i], dc->pix_clk_rate[i] * 1000);
+    }
+
+    return 0;
+}
+
+static int dc_resume(struct device *dev)
+{
+    int i, ret;
+    struct vs_dc *dc = dev_get_drvdata(dev);
+    dev_info(dev,"dc resume\n");
+    regmap_write(dc->hw.vosys_regmap,0x4,0x7);  //release dpu reset
+
+    ret = dc_hw_init(&dc->hw);
+    if (ret) 
+        dev_err(dev, "failed to init DC HW\n");
+    return 0;
+}
+
+#endif
 static const struct dev_pm_ops dc_pm_ops = {
     SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
                                 pm_runtime_force_resume)
     SET_RUNTIME_PM_OPS(dc_runtime_suspend, dc_runtime_resume, NULL)
+    #ifdef CONFIG_PM_SLEEP
+        SET_LATE_SYSTEM_SLEEP_PM_OPS(dc_suspend, dc_resume)
+    #endif
 };
 
 struct platform_driver dc_platform_driver = {
index 78d4f1778f94b8f2803c331ac2da334f0dd518b2..95cf95724a484a09f904c9300864742f3d1ffdfe 100644 (file)
@@ -48,6 +48,7 @@ struct vs_dc {
     struct clk      *dpu1pll_clk;
 
     unsigned int    pix_clk_rate[DC_DISPLAY_NUM]; /* in KHz */
+    unsigned int    def_pix_clk_rate[DC_DISPLAY_NUM];
 
     bool            first_frame;
     bool            dpu0pll_on;
index 90c63e8642d9770798a625369336fa8498b5f757..37399f804e5a9fc860fb20e71c9f216800d9e6c2 100644 (file)
@@ -540,6 +540,7 @@ struct dc_hw {
     struct dc_hw_qos     qos;
     struct dc_hw_funcs   *func;
     struct vs_dc_info    *info;
+    struct regmap *vosys_regmap;
 };
 
 int dc_hw_init(struct dc_hw *hw);
index 1fa01b599180345013e2ec742c9468dc73eaf418..aa6c0221736bfbfc21a34fa1ccab9665a17b15dd 100644 (file)
@@ -220,7 +220,6 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val)
                n &= SAMPLE_DATA_MSK;
                /* Convert the N bitstream count into voltage */
                *val = (PVT_N_CONST * n - PVT_R_CONST) >> PVT_CONV_BITS;
-
                return 0;
        default:
                return -EOPNOTSUPP;
@@ -654,6 +653,8 @@ static int mr75203_probe(struct platform_device *pdev)
                return ret;
        }
 
+       platform_set_drvdata(pdev, pvt);
+
        pvt_chip_info.info = pvt_info;
        hwmon_dev = devm_hwmon_device_register_with_info(dev, "pvt",
                                                         pvt,
@@ -662,6 +663,31 @@ static int mr75203_probe(struct platform_device *pdev)
 
        return PTR_ERR_OR_ZERO(hwmon_dev);
 }
+#ifdef CONFIG_PM
+
+static int mr75203_suspend(struct device *dev)
+{
+       /* nothing to do */
+       return 0;
+}
+
+static int mr75203_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct pvt_device *pvt = platform_get_drvdata(pdev);
+       pvt_init(pvt);
+       return 0;
+}
+
+static const struct dev_pm_ops mr75203_dev_pm_ops = {
+       .suspend = mr75203_suspend,
+       .resume = mr75203_resume,
+};
+
+#define MR75203_DEV_PM_OPS (&mr75203_dev_pm_ops)
+#else
+#define MR75203_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
 
 static const struct of_device_id moortec_pvt_of_match[] = {
        { .compatible = "moortec,mr75203" },
@@ -672,6 +698,7 @@ MODULE_DEVICE_TABLE(of, moortec_pvt_of_match);
 static struct platform_driver moortec_pvt_driver = {
        .driver = {
                .name = "moortec-pvt",
+               .pm = MR75203_DEV_PM_OPS,
                .of_match_table = moortec_pvt_of_match,
        },
        .probe = mr75203_probe,
index 8f370696d4cbff28d270bfab6df0c0cb7965bd0f..0c5a904032d936901fdca1e528c43a9ac022112d 100644 (file)
@@ -202,7 +202,6 @@ int i2c_dw_dma_tx_transfer(struct dw_i2c_dev *dev, unsigned int timeout)
 {
     int ret = 0;
     struct i2c_dw_dma *dma = &dev->dma;
-    unsigned long start_jiffies = 0;
     u32 stat;
 
     __dev_vdgb(dev->dev, "%s, %d, enter\n", __func__, __LINE__);
index 80da3aaa7b4c2118099ffc08090759d98d8820e5..cfdad95e87bbb364710e72604bdc867c523f54a3 100644 (file)
@@ -206,6 +206,7 @@ static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
 
 static int dw_i2c_plat_probe(struct platform_device *pdev)
 {
+       dev_info(&pdev->dev, "going to probe light i2c driver\n");
        struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct i2c_adapter *adap;
        struct dw_i2c_dev *dev;
@@ -377,6 +378,7 @@ static void dw_i2c_plat_complete(struct device *dev)
 #ifdef CONFIG_PM
 static int dw_i2c_plat_suspend(struct device *dev)
 {
+       dev_info(dev, "light i2c suspend\n");
        struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
 
        i_dev->suspended = true;
@@ -391,6 +393,35 @@ static int dw_i2c_plat_suspend(struct device *dev)
 }
 
 static int dw_i2c_plat_resume(struct device *dev)
+{
+       dev_info(dev, "light i2c resume\n");
+       struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+       if (!i_dev->shared_with_punit)
+               i2c_dw_prepare_clk(i_dev, true);
+
+       i_dev->init(i_dev);
+       i_dev->suspended = false;
+
+       return 0;
+}
+
+static int dw_i2c_plat_runtime_suspend(struct device *dev)
+{
+       struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
+
+       i_dev->suspended = true;
+
+       if (i_dev->shared_with_punit)
+               return 0;
+
+       i_dev->disable(i_dev);
+       i2c_dw_prepare_clk(i_dev, false);
+
+       return 0;
+}
+
+static int dw_i2c_plat_runtime_resume(struct device *dev)
 {
        struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
 
@@ -407,7 +438,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
        .prepare = dw_i2c_plat_prepare,
        .complete = dw_i2c_plat_complete,
        SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
-       SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
+       SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL)
 };
 
 #define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
index aaefd5ac5d3dc0ebd7668ef24c922778bd70948b..dc3e6328096b5567b85b5f777d8497e0b2aaa43b 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
 #include <asm/smp.h>
 
 /*
@@ -65,6 +66,7 @@ struct plic_priv {
        struct irq_domain *irqdomain;
        void __iomem *regs;
        unsigned int nr_irqs;
+       unsigned long *prio_save;
 };
 
 struct plic_handler {
@@ -76,8 +78,10 @@ struct plic_handler {
         */
        raw_spinlock_t          enable_lock;
        void __iomem            *enable_base;
+       u32                     *enable_save;
        struct plic_priv        *priv;
 };
+
 static int plic_parent_irq;
 static bool plic_cpuhp_setup_done;
 static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
@@ -183,6 +187,71 @@ static struct irq_chip plic_chip = {
 #endif
 };
 
+static int plic_irq_suspend(void)
+{
+       unsigned int i, cpu;
+       u32 __iomem *reg;
+       struct plic_priv *priv;
+
+       priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
+
+       for (i = 0; i < priv->nr_irqs; i++)
+               if (readl(priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID))
+                       __set_bit(i, priv->prio_save);
+               else
+                       __clear_bit(i, priv->prio_save);
+
+       for_each_cpu(cpu, cpu_present_mask) {
+               struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);
+
+               if (!handler->present)
+                       continue;
+
+               raw_spin_lock(&handler->enable_lock);
+               for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
+                       reg = handler->enable_base + i * sizeof(u32);
+                       handler->enable_save[i] = readl(reg);
+               }
+               raw_spin_unlock(&handler->enable_lock);
+       }
+
+       return 0;
+}
+
+static void plic_irq_resume(void)
+{
+       unsigned int i, index, cpu;
+       u32 __iomem *reg;
+       struct plic_priv *priv;
+
+       priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
+
+       for (i = 0; i < priv->nr_irqs; i++) {
+               index = BIT_WORD(i);
+               writel((priv->prio_save[index] & BIT_MASK(i)) ? 1 : 0,
+                      priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID);
+       }
+
+       for_each_cpu(cpu, cpu_present_mask) {
+               struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);
+
+               if (!handler->present)
+                       continue;
+
+               raw_spin_lock(&handler->enable_lock);
+               for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
+                       reg = handler->enable_base + i * sizeof(u32);
+                       writel(handler->enable_save[i], reg);
+               }
+               raw_spin_unlock(&handler->enable_lock);
+       }
+}
+
+static struct syscore_ops plic_irq_syscore_ops = {
+       .suspend        = plic_irq_suspend,
+       .resume         = plic_irq_resume,
+};
+
 static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq,
                              irq_hw_number_t hwirq)
 {
@@ -294,8 +363,10 @@ static int __init plic_init(struct device_node *node,
                struct device_node *parent)
 {
        int error = 0, nr_contexts, nr_handlers = 0, i;
+       u32 nr_irqs;
        struct plic_priv *priv;
        struct plic_handler *handler;
+       unsigned int cpu;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -308,19 +379,25 @@ static int __init plic_init(struct device_node *node,
        }
 
        error = -EINVAL;
-       of_property_read_u32(node, "riscv,ndev", &priv->nr_irqs);
-       if (WARN_ON(!priv->nr_irqs))
+       of_property_read_u32(node, "riscv,ndev", &nr_irqs);
+       if (WARN_ON(!nr_irqs))
                goto out_iounmap;
 
+       priv->nr_irqs = nr_irqs;
+
+       priv->prio_save = bitmap_alloc(nr_irqs, GFP_KERNEL);
+       if (!priv->prio_save)
+               goto out_free_priority_reg;
+
        nr_contexts = of_irq_count(node);
        if (WARN_ON(!nr_contexts))
-               goto out_iounmap;
+               goto out_free_priority_reg;
 
        error = -ENOMEM;
-       priv->irqdomain = irq_domain_add_linear(node, priv->nr_irqs + 1,
+       priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
                        &plic_irqdomain_ops, priv);
        if (WARN_ON(!priv->irqdomain))
-               goto out_iounmap;
+               goto out_free_priority_reg;
 
        for (i = 0; i < nr_contexts; i++) {
                struct of_phandle_args parent;
@@ -379,6 +456,11 @@ static int __init plic_init(struct device_node *node,
                handler->enable_base =
                        priv->regs + ENABLE_BASE + i * ENABLE_PER_HART;
                handler->priv = priv;
+
+               handler->enable_save =  kcalloc(DIV_ROUND_UP(nr_irqs, 32),
+                                               sizeof(*handler->enable_save), GFP_KERNEL);
+               if (!handler->enable_save)
+                       goto out_free_enable_reg;
 done:
                nr_handlers++;
        }
@@ -395,10 +477,18 @@ done:
                plic_cpuhp_setup_done = true;
        }
 
+       register_syscore_ops(&plic_irq_syscore_ops);
        pr_info("%pOFP: mapped %d interrupts with %d handlers for"
                " %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts);
        return 0;
 
+out_free_enable_reg:
+       for_each_cpu(cpu, cpu_present_mask) {
+               handler = per_cpu_ptr(&plic_handlers, cpu);
+               kfree(handler->enable_save);
+       }
+out_free_priority_reg:
+       kfree(priv->prio_save);
 out_iounmap:
        iounmap(priv->regs);
 out_free_priv:
index f3d34d947ec48d08796361a265192b22c3e4539a..5d0185caface67fce19f7ccd188996ae99779aeb 100644 (file)
 
 #define LIGHT_MBOX_ACK_MAGIC           0xdeadbeaf
 
+#ifdef CONFIG_PM_SLEEP
+/* store MBOX context across system-wide suspend/resume transitions */
+struct light_mbox_context{
+       u32 intr_mask[LIGHT_MBOX_CHANS - 1];
+};
+
+#endif
 enum light_mbox_chan_type {
        LIGHT_MBOX_TYPE_TXRX,   /* Tx & Rx chan */
        LIGHT_MBOX_TYPE_DB,     /* Tx & Rx doorbell */
@@ -70,9 +77,12 @@ struct light_mbox_priv {
        struct mbox_controller          mbox;
        struct mbox_chan                mbox_chans[LIGHT_MBOX_CHANS];
 
-       struct light_mbox_con_priv      con_priv[LIGHT_MBOX_CHANS];
+       struct light_mbox_con_priv      con_priv[LIGHT_MBOX_CHANS];
        struct clk                      *clk;
        int                             irq;
+#ifdef CONFIG_PM_SLEEP
+       struct light_mbox_context       *ctx;
+#endif
 };
 
 static struct light_mbox_priv *to_light_mbox_priv(struct mbox_controller *mbox)
@@ -327,11 +337,17 @@ static const struct mbox_chan_ops light_mbox_ops = {
        .shutdown = light_mbox_shutdown,
 };
 
-static void light_mbox_init_generic(struct light_mbox_priv *priv)
+static int light_mbox_init_generic(struct light_mbox_priv *priv)
 {
+#ifdef CONFIG_PM_SLEEP
+       priv->ctx = devm_kzalloc(priv->dev, sizeof(*priv->ctx), GFP_KERNEL);
+       if(!priv->ctx)
+               return -ENOMEM;
+#endif
        /* Set default configuration */
        light_mbox_write(priv, 0xff, LIGHT_MBOX_CLR);
        light_mbox_write(priv, 0x0, LIGHT_MBOX_MASK);
+       return 0;
 }
 
 static struct mbox_chan *light_mbox_xlate(struct mbox_controller *mbox,
@@ -472,7 +488,11 @@ static int light_mbox_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, priv);
 
-       light_mbox_init_generic(priv);
+       ret = light_mbox_init_generic(priv);
+       if (ret) {
+               dev_err(dev, "Failed to init mailbox context\n");
+               return ret;
+       }
 
        return devm_mbox_controller_register(dev, &priv->mbox);
 }
@@ -492,12 +512,75 @@ static const struct of_device_id light_mbox_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, light_mbox_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int __maybe_unused light_mbox_suspend_noirq(struct device *dev)
+{
+       struct light_mbox_priv *priv = dev_get_drvdata(dev);
+       struct light_mbox_context *ctx = priv->ctx;
+       u32 i;
+       /*
+        * ONLY interrupt mask bit should be stored and restores.
+        * INFO data all assumed to be lost.
+        */
+       for(i = 0 ; i < LIGHT_MBOX_CHANS; i++) {
+               ctx->intr_mask[i] = ioread32(priv->local_icu[i] + LIGHT_MBOX_MASK);
+       }
+       return 0;
+}
+
+static int __maybe_unused light_mbox_resume_noirq(struct device *dev)
+{
+       struct light_mbox_priv *priv = dev_get_drvdata(dev);
+       struct light_mbox_context *ctx = priv->ctx;
+       u32 i;
+
+       for(i = 0 ; i < LIGHT_MBOX_CHANS; i++) {
+               iowrite32(ctx->intr_mask[i], priv->local_icu[i] + LIGHT_MBOX_MASK);
+       }
+       return 0;
+}
+
+#endif
+
+
+static int __maybe_unused light_mbox_runtime_suspend(struct device *dev)
+{
+       struct light_mbox_priv *priv = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static int __maybe_unused light_mbox_runtime_resume(struct device *dev)
+{
+       struct light_mbox_priv *priv = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret)
+               dev_err(dev, "failed to enable clock\n");
+
+       return ret;
+}
+
+static const struct dev_pm_ops light_mbox_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+        SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(light_mbox_suspend_noirq,
+                                      light_mbox_resume_noirq)
+#endif
+        SET_RUNTIME_PM_OPS(light_mbox_runtime_suspend,
+                           light_mbox_runtime_resume, NULL)
+};
+
+
 static struct platform_driver light_mbox_driver = {
        .probe          = light_mbox_probe,
        .remove         = light_mbox_remove,
        .driver = {
                .name   = "light_mbox",
                .of_match_table = light_mbox_dt_ids,
+               .pm = &light_mbox_pm_ops,
        },
 };
 module_platform_driver(light_mbox_driver);
index dd488e56075ce0f2aaab333f1eb8fcc56bf27c93..fe31a27605e6e33ccdf39c95ace296d8040f48f2 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/of_device.h>
 #include <linux/of_net.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include "stmmac_platform.h"
 
@@ -52,11 +53,14 @@ struct thead_dwmac_priv_data {
        phy_interface_t interface;
        struct clk *gmac_pll_clk;
        unsigned long gmac_pll_clk_freq;
-
+       struct clk *gmac_axi_aclk;
+       struct clk *gmac_axi_pclk;
        const struct thead_dwmac_ops *ops;
        struct plat_stmmacenet_data *plat_dat;
 };
 
+#define  pm_debug dev_dbg      // for suspend/resume interface debug info show,replace to dev_info
+
 /* set GMAC PHY interface, 0:MII/GMII, 1:RGMII, 4:RMII */
 static void thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat_dat)
 {
@@ -64,7 +68,7 @@ static void thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat_dat)
        void __iomem *phy_if_reg = thead_plat_dat->phy_if_reg;
        phy_interface_t interface = thead_plat_dat->interface;
        struct device *dev = thead_plat_dat->dev;
-       int devid = thead_plat_dat->id;
+       //int devid = thead_plat_dat->id;
        unsigned int phyif = PHY_INTERFACE_MODE_MII;
        uint32_t reg;
 
@@ -92,8 +96,10 @@ static void thead_dwmac_set_phy_if(struct plat_stmmacenet_data *plat_dat)
        };
 
        reg = readl(phy_if_reg);
-       reg &= ~(DWMAC_PHYIF_MASK << (DWMAC_PHYIF_BIT_WIDTH * devid));
-       reg |= (phyif & DWMAC_PHYIF_MASK) << (DWMAC_PHYIF_BIT_WIDTH * devid);
+       //This reg defined bit not related to devid
+       reg &= ~(DWMAC_PHYIF_MASK );
+       reg |= (phyif & DWMAC_PHYIF_MASK) ;
+       dev_info(dev,"set phy_if_reg val 0x%x \n",reg);
        writel(reg, phy_if_reg);
 }
 
@@ -428,13 +434,13 @@ static void thead_dwmac_light_enable_clk(struct plat_stmmacenet_data *plat_dat)
 static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
 {
        struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
-       struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
+       //struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
        struct device *dev = &pdev->dev;
        struct device_node *np = pdev->dev.of_node;
        struct resource *res;
        void __iomem *ptr;
-       struct clk *clktmp;
-       int ret;
+       //struct clk *clktmp;
+       //int ret;
 
        thead_plat_dat->id = of_alias_get_id(np, "ethernet");
        if (thead_plat_dat->id < 0) {
@@ -472,7 +478,7 @@ static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
        } else {
                thead_plat_dat->gmac_clk_reg = ptr;
        }
-
+#if 0
        /* get gmac pll clk */
        clktmp = devm_clk_get(dev, "gmac_pll_clk");
        if (IS_ERR(clktmp)) {
@@ -490,6 +496,7 @@ static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
                                clk_get_rate(thead_plat_dat->gmac_pll_clk);
        }
 
+
        thead_dwmac_set_phy_if(plat_dat);
        thead_dwmac_set_txclk_dir(plat_dat);
 
@@ -506,7 +513,7 @@ static int thead_dwmac_init(struct platform_device *pdev, void *bsp_priv)
 
        if (thead_plat_dat->ops->enable_clk)
                thead_plat_dat->ops->enable_clk(plat_dat);
-
+#endif
        return 0;
 }
 
@@ -577,6 +584,97 @@ static int dwmac1000_validate_ucast_entries(int ucast_entries)
        }
        return x;
 }
+static void __maybe_unused thead_dwmac_dump_priv_reg(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct device *dev = &pdev->dev;
+       void __iomem *gmac_clk_reg = thead_plat_dat->gmac_clk_reg;
+       unsigned int reg = 0;
+       int i;
+       dev_info(dev,"dump gmac_clk_reg %p\n",gmac_clk_reg);
+       if(gmac_clk_reg == NULL)
+               return ;
+       for(i=0; i< 0x1c; i+=4)
+       {
+               reg = readl(gmac_clk_reg + GMAC_CLK_CFG0+i);
+               pr_info("%08x ",reg);
+       }
+       pr_info("\n");
+       reg = readl(thead_plat_dat->phy_if_reg);
+       pr_info("phy_if_reg %08x ",reg);
+       reg = readl(thead_plat_dat->txclk_dir_reg);
+       pr_info("txclk_dir_reg %08x ",reg);
+}
+
+int thead_dwmac_clk_enable(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct device *dev = &pdev->dev;
+       int ret;
+       pm_debug(dev,"enter %s()\n",__func__);
+       ret = clk_prepare_enable(thead_plat_dat->gmac_pll_clk);
+       if (ret) {
+               dev_err(dev, "Failed to enable clk 'gmac_pll_clk'\n");
+               return -1;
+       }
+       ret = clk_prepare_enable(thead_plat_dat->gmac_axi_aclk);
+       if (ret) {
+               clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
+               dev_err(dev, "Failed to enable clk 'gmac_axi_aclk'\n");
+               return -1;
+       }
+       ret = clk_prepare_enable(thead_plat_dat->gmac_axi_pclk);
+       if (ret) {
+               clk_disable_unprepare(thead_plat_dat->gmac_axi_aclk);
+               clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
+               dev_err(dev, "Failed to enable clk 'gmac_axi_pclk'\n");
+               return -1;
+       }
+       
+       return ret;
+}
+
+int thead_dwmac_clk_init(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct device *dev = &pdev->dev;
+       struct plat_stmmacenet_data *plat_dat = thead_plat_dat->plat_dat;
+       int ret = 0;
+       pm_debug(dev,"enter %s()\n",__func__);
+
+       thead_dwmac_set_phy_if(plat_dat);
+       thead_dwmac_set_txclk_dir(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_source)
+               thead_plat_dat->ops->set_clk_source(plat_dat);
+
+       thead_dwmac_set_clock_delay(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_pll)
+               thead_plat_dat->ops->set_clk_pll(plat_dat);
+
+       if (thead_plat_dat->ops->set_clk_div)
+               thead_plat_dat->ops->set_clk_div(plat_dat, SPEED_1000);
+
+       if (thead_plat_dat->ops->enable_clk)
+               thead_plat_dat->ops->enable_clk(plat_dat);
+       
+       //thead_dwmac_dump_priv_reg(pdev,bsp_priv);
+       return ret;
+}
+void thead_dwmac_clk_disable(struct platform_device *pdev, void *bsp_priv)
+{
+       struct thead_dwmac_priv_data *thead_plat_dat = bsp_priv;
+       struct device *dev = &pdev->dev;
+       pm_debug(dev,"enter %s()\n",__func__);
+       //thead_dwmac_dump_priv_reg(pdev,bsp_priv);
+       
+       clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
+       clk_disable_unprepare(thead_plat_dat->gmac_axi_aclk);
+       clk_disable_unprepare(thead_plat_dat->gmac_pll_clk);
+
+       return ;
+}
 
 static int thead_dwmac_probe(struct platform_device *pdev)
 {
@@ -627,17 +725,13 @@ static int thead_dwmac_probe(struct platform_device *pdev)
                plat_dat->unicast_filter_entries = 1;
        }
 
-       /* Custom initialisation (if needed) */
-       if (plat_dat->init) {
-               ret = plat_dat->init(pdev, plat_dat->bsp_priv);
-               if (ret)
-                       goto err_remove_config_dt;
-       }
+
 
        /* populate bsp private data */
        thead_plat_dat->dev = &pdev->dev;
        plat_dat->bsp_priv = thead_plat_dat;
        plat_dat->fix_mac_speed = thead_dwmac_fix_speed;
+       plat_dat->init = thead_dwmac_clk_init;
        of_property_read_u32(np, "max-frame-size", &plat_dat->maxmtu);
        of_property_read_u32(np, "snps,multicast-filter-bins",
                             &plat_dat->multicast_filter_bins);
@@ -651,9 +745,41 @@ static int thead_dwmac_probe(struct platform_device *pdev)
        plat_dat->pmt = 1;
        thead_plat_dat->plat_dat = plat_dat;
 
+       /* get gmac pll clk */
+       thead_plat_dat->gmac_pll_clk = devm_clk_get(dev, "gmac_pll_clk");
+       if (IS_ERR(thead_plat_dat->gmac_pll_clk)) {
+               dev_err(dev, "gmac_pll_clk not exist, dts error\n");
+               goto err_remove_config_dt;
+       }
+       
+       thead_plat_dat->gmac_axi_aclk = devm_clk_get(dev, "axi_aclk");
+       if (IS_ERR(thead_plat_dat->gmac_axi_aclk)) {
+               dev_err(dev, "gmac axi_aclk not exist, skipped it\n");
+       }
+       thead_plat_dat->gmac_axi_pclk = devm_clk_get(dev, "axi_pclk");
+       if (IS_ERR(thead_plat_dat->gmac_axi_pclk)) {
+               dev_err(dev, "gmac axi_pclk not exist, skipped it\n");
+       }
+       
+
+       thead_plat_dat->gmac_pll_clk_freq =
+                       clk_get_rate(thead_plat_dat->gmac_pll_clk);
+       dev_info(dev,"get_rate gmac_pll_clk_freq %ld \n",thead_plat_dat->gmac_pll_clk_freq);
+
        ret = thead_dwmac_init(pdev, plat_dat->bsp_priv);
        if (ret)
-               goto err_exit;
+               goto err_remove_config_dt;
+       
+       ret = thead_dwmac_clk_enable(pdev, plat_dat->bsp_priv);
+       if (ret)
+                       goto err_remove_config_dt;
+       
+       /* Custom initialisation (if needed) -- init clks*/
+       if (plat_dat->init) {
+               ret = plat_dat->init(pdev, plat_dat->bsp_priv);
+               if (ret)
+                       goto err_exit;
+       }
 
        ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
        if (ret)
@@ -664,6 +790,7 @@ static int thead_dwmac_probe(struct platform_device *pdev)
 err_exit:
        if (plat_dat->exit)
                plat_dat->exit(pdev, plat_dat->bsp_priv);
+       thead_dwmac_clk_disable(pdev, plat_dat->bsp_priv);
 err_remove_config_dt:
        if (pdev->dev.of_node)
                stmmac_remove_config_dt(pdev, plat_dat);
@@ -671,6 +798,130 @@ err_remove_config_dt:
        return ret;
 }
 
+/**
+ * stmmac_pltfr_suspend
+ * @dev: device pointer
+ * Description: this function is invoked when suspend the driver and it direcly
+ * call the main suspend function and then, if required, on some platform, it
+ * can call an exit helper.
+ */
+static int __maybe_unused thead_dwmac_suspend(struct device *dev)
+{
+       int ret;
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct platform_device *pdev = to_platform_device(dev);
+       pm_debug(dev,"enter %s()\n",__func__);
+       ret = stmmac_suspend(dev);
+       if (priv->plat->exit)
+               priv->plat->exit(pdev, priv->plat->bsp_priv);
+       
+       return ret;
+}
+
+/**
+ * thead_dwmac_resume
+ * @dev: device pointer
+ * Description: this function is invoked when resume the driver before calling
+ * the main resume function, on some platforms, it can call own init helper
+ * if required.
+ */
+static int __maybe_unused thead_dwmac_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct platform_device *pdev = to_platform_device(dev);
+       pm_debug(dev,"enter %s()\n",__func__);
+
+       if (priv->plat->init)
+               priv->plat->init(pdev, priv->plat->bsp_priv);
+
+       return stmmac_resume(dev);
+}
+
+static int __maybe_unused thead_dwmac_runtime_suspend(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct platform_device *pdev = to_platform_device(dev);
+       pm_debug(dev,"enter %s()\n",__func__);
+       stmmac_bus_clks_config(priv, false);
+       thead_dwmac_clk_disable(pdev, priv->plat->bsp_priv);
+       return 0;
+}
+
+static int __maybe_unused thead_dwmac_runtime_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct platform_device *pdev = to_platform_device(dev);
+       int ret;
+       pm_debug(dev,"enter %s()\n",__func__);
+       ret = stmmac_bus_clks_config(priv, true);
+       if(ret)
+               return ret;
+       ret = thead_dwmac_clk_enable(pdev, priv->plat->bsp_priv);
+       if(ret)
+               return ret;
+       return 0;
+}
+
+static int __maybe_unused thead_dwmac_noirq_suspend(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       int ret;
+       pm_debug(dev,"enter %s()\n",__func__);
+       if (!netif_running(ndev))
+               return 0;
+
+       if (!device_may_wakeup(priv->device) || !priv->plat->pmt) {
+               /* Disable clock in case of PWM is off */
+               clk_disable_unprepare(priv->plat->clk_ptp_ref);
+
+               ret = pm_runtime_force_suspend(dev);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused thead_dwmac_noirq_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       int ret;
+       pm_debug(dev,"enter %s()\n",__func__);
+       if (!netif_running(ndev))
+               return 0;
+
+       if (!device_may_wakeup(priv->device) || !priv->plat->pmt) {
+               /* enable the clk previously disabled */
+               ret = pm_runtime_force_resume(dev);
+               if (ret)
+                       return ret;
+
+               ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
+               if (ret < 0) {
+                       netdev_warn(priv->dev,
+                                   "failed to enable PTP reference clock: %pe\n",
+                                   ERR_PTR(ret));
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/*similar with stmmac_pltfr_pm_ops,but clks enable/disable add this drv need */
+const struct dev_pm_ops thead_dwmac_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(thead_dwmac_suspend, thead_dwmac_resume)
+       SET_RUNTIME_PM_OPS(thead_dwmac_runtime_suspend, thead_dwmac_runtime_resume, NULL)
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(thead_dwmac_noirq_suspend, thead_dwmac_noirq_resume)
+};
+
+
 static struct thead_dwmac_ops thead_ice_dwmac_data = {
        .set_clk_source = thead_dwmac_ice_set_clk_source,
        .set_clk_pll = thead_dwmac_ice_set_pll,
@@ -695,7 +946,7 @@ static struct platform_driver thead_dwmac_driver = {
        .remove = stmmac_pltfr_remove,
        .driver = {
                .name           = "light_dwmac_eth",
-               .pm             = &stmmac_pltfr_pm_ops,
+               .pm             = &thead_dwmac_pm_ops,
                .of_match_table = of_match_ptr(thead_dwmac_match),
        },
 };
index 57a53a600aa556672c82b12d40f4a35bc572187d..f4e5a356fa73f76efc2dfd1239154c1aaa82d611 100644 (file)
@@ -23,7 +23,7 @@ int dwmac_dma_reset(void __iomem *ioaddr)
 
        return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
                                 !(value & DMA_BUS_MODE_SFT_RESET),
-                                10000, 200000);
+                                10000, 1000000); //inspired from : net: stmmac: increase the timeout for dma reset
 }
 
 /* CSR1 enables the transmit DMA to check for new descriptor */
index 27a377467192b5d111680823eda11c9c938cd22a..c875310a2ffc19aabb055beb5cb00332f6a22d44 100644 (file)
@@ -738,19 +738,10 @@ int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags)
        struct timespec64 now;
        u32 sec_inc = 0;
        u64 temp = 0;
-       int ret;
 
        if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
                return -EOPNOTSUPP;
 
-       ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
-       if (ret < 0) {
-               netdev_warn(priv->dev,
-                           "failed to enable PTP reference clock: %pe\n",
-                           ERR_PTR(ret));
-               return ret;
-       }
-
        stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
        priv->systime_flags = systime_flags;
 
@@ -2881,6 +2872,14 @@ static int stmmac_hw_setup(struct net_device *dev, bool ptp_register)
 
        stmmac_mmc_setup(priv);
 
+       if (ptp_register) {
+               ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
+               if (ret < 0)
+                       netdev_warn(priv->dev,
+                                   "failed to enable PTP reference clock: %pe\n",
+                                   ERR_PTR(ret));
+       }
+
        ret = stmmac_init_ptp(priv);
        if (ret == -EOPNOTSUPP)
                netdev_warn(priv->dev, "PTP not supported by HW\n");
index b40b962055fa5b3be835e0c6b5910ff06c98881f..f70d8d1ce3298f54a4b5b4bbb6e51521627cb69c 100644 (file)
@@ -814,7 +814,13 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)
                if (ret)
                        return ret;
 
-               stmmac_init_tstamp_counter(priv, priv->systime_flags);
+               ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
+               if (ret < 0) {
+                       netdev_warn(priv->dev,
+                                   "failed to enable PTP reference clock: %pe\n",
+                                   ERR_PTR(ret));
+                       return ret;
+               }
        }
 
        return 0;
index 90e528312733fb8aee9defbb5c5b8e96f274cf8c..3660840368aeb4bd12671f6432931128fb116fd2 100644 (file)
@@ -1005,6 +1005,8 @@ static int __maybe_unused light_efuse_runtime_suspend(struct device *dev)
 
        dev_dbg(dev, "[%s,%d] ret = %d, pd status: 0x%lx\n", __func__, __LINE__, ret,
                        readl(priv->base + CON) & EFUSE_CON_POWER_MSK);
+       
+       clk_disable_unprepare(priv->clk);
 
        return ret;
 }
@@ -1012,9 +1014,59 @@ static int __maybe_unused light_efuse_runtime_suspend(struct device *dev)
 static int __maybe_unused light_efuse_runtime_resume(struct device *dev)
 {
        struct light_efuse_priv *priv = dev_get_drvdata(dev);
+       int ret;
 
        dev_dbg(dev, "[%s,%d]efuse runtime power on\n", __func__, __LINE__);
 
+       ret = clk_prepare_enable(priv->clk);
+       if (ret) {
+               dev_err(dev, "failed to get efuse clk\n");
+               return ret;
+       }
+               
+       return efuse_poweron(priv->base);
+}
+
+static int __maybe_unused light_efuse_suspend(struct device *dev)
+{
+       struct light_efuse_priv *priv = dev_get_drvdata(dev);
+       u32 data;
+       int ret;
+
+       dev_dbg(dev, "[%s,%d]efuse suspend enter\n", __func__, __LINE__);
+
+       if (!efuse_poweron_status_get(priv->base))
+               return 0;
+
+       data = readl(priv->base + CON);
+       data |= EFUSE_CON_POWER_MSK;
+       writel(data, priv->base + CON);
+
+       ret = efuse_idle_check(priv->base);
+
+       ret |= efuse_status_check(priv->base);
+
+       dev_dbg(dev, "[%s,%d] ret = %d, pd status: 0x%lx\n", __func__, __LINE__, ret,
+                       readl(priv->base + CON) & EFUSE_CON_POWER_MSK);
+       
+       clk_disable_unprepare(priv->clk);
+
+       return ret;
+}
+
+static int __maybe_unused light_efuse_resume(struct device *dev)
+{
+       struct light_efuse_priv *priv = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(dev, "[%s,%d]efuse resume enter\n", __func__, __LINE__);
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret) {
+               dev_err(dev, "failed to get efuse clk\n");
+               return ret;
+       }
+               
        return efuse_poweron(priv->base);
 }
 
@@ -1035,9 +1087,18 @@ static int light_efuse_probe(struct platform_device *pdev)
                return PTR_ERR(priv->base);
 
        /* optional clock, default open */
-       priv->clk = devm_clk_get(dev, NULL);
-       if (IS_ERR(priv->clk))
-               priv->clk = NULL;
+       priv->clk = devm_clk_get(dev, "pclk");
+       if (IS_ERR_OR_NULL(priv->clk)) {
+               if (PTR_ERR(priv->clk) != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "failed to get efuse clk\n");
+               return PTR_ERR(priv->clk);
+       }
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret) {
+               dev_err(dev, "failed to get efuse clk\n");
+               return ret;
+       }
 
        priv->teesys_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "thead,teesys");
        if (IS_ERR(priv->teesys_regs)) {
@@ -1049,6 +1110,14 @@ static int light_efuse_probe(struct platform_device *pdev)
 
        dev_set_drvdata(dev, priv);
 
+       pm_runtime_enable(dev);
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               dev_err(dev, "failed to get the efuse device(%d)\n", ret);
+               pm_runtime_put_noidle(dev);
+               return ret;
+       }
+
        ret = sysfs_create_group(&dev->kobj, &dev_attr_efuse_sysfs_group);
        if (ret) {
                dev_err(dev, "failed to create efuse debug sysfs\n");
@@ -1070,7 +1139,7 @@ static int light_efuse_probe(struct platform_device *pdev)
        if (IS_ERR(nvmem))
                return PTR_ERR_OR_ZERO(nvmem);
 
-       pm_runtime_enable(dev);
+       pm_runtime_put_sync(dev);
 
        dev_info(dev, "succeed to register light efuse driver\n");
 
@@ -1079,6 +1148,7 @@ static int light_efuse_probe(struct platform_device *pdev)
 
 static const struct dev_pm_ops efuse_runtime_pm_ops = {
        SET_RUNTIME_PM_OPS(light_efuse_runtime_suspend, light_efuse_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(light_efuse_suspend, light_efuse_resume)
 };
 
 static struct platform_driver light_efuse_driver = {
index 8d8b254e8e1fab10904c992544ac2f08071e9108..3834871ce713775f116e20732c88128e91aa01b6 100644 (file)
@@ -748,21 +748,35 @@ static int dw_dphy_runtime_resume(struct device *dev)
 
        return 0;
 }
-#endif
+
+static int dw_dphy_resume(struct device *dev)
+{
+       struct dw_dphy *dphy = dev_get_drvdata(dev);
+
+       dw_dphy_init(dphy->phy);
+
+       return 0;
+}
 
 static const struct dev_pm_ops dw_dphy_pm_ops = {
-    SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-                                pm_runtime_force_resume)
-    SET_RUNTIME_PM_OPS(dw_dphy_runtime_suspend, dw_dphy_runtime_resume, NULL)
+       SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL,
+                                dw_dphy_resume)
+       SET_RUNTIME_PM_OPS(dw_dphy_runtime_suspend, dw_dphy_runtime_resume, NULL)
 };
 
+#define DW_DPHY_PM_OPS &dw_dphy_pm_ops
+#else
+#define DW_DPHY_PM_OPS NULL
+#endif
+
+
 static struct platform_driver dw_dphy_driver = {
        .probe  = dw_dphy_probe,
        .remove = dw_dphy_remove,
        .driver = {
                .name = "dw-mipi-dphy",
                .of_match_table = dw_dphy_of_match,
-               .pm = &dw_dphy_pm_ops,
+               .pm = DW_DPHY_PM_OPS,
        },
 };
 module_platform_driver(dw_dphy_driver);
index 5c50ab40daa9c51633e8658e6fb140dc6a43b8d3..303c555609cde899e7e4b832b12d2f0184a19a4c 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (C) 2021 Alibaba Group Holding Limited.
  *
  */
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/io.h>
@@ -67,10 +68,29 @@ struct light_pinctrl_soc_info {
        int (*covert_pin_off)(const unsigned int pin_id);
 };
 
+#ifdef CONFIG_PM_SLEEP
+#define MAX_CFG_REG_NUMS       32
+#define MAX_MUX_REG_NUMS       8
+#define LIGHT_PADCTRL0_CFG_REG_NUMS    28
+#define LIGHT_PADCTRL0_MUX_REG_NUMS    7
+#define LIGHT_PADCTRL1_CFG_REG_NUMS    32
+#define LIGHT_PADCTRL1_MUX_REG_NUMS    8
+#define LIGHT_AUDIO_CFG_REG_NUMS       16
+#define LIGHT_AUDIO_MUX_REG_NUMS       2
+#define LIGHT_AUDIO_IO_SEL_IDX         2
+#define LIGHT_PM_PAD_CFG(idx)          (priv->base + priv->info->cfg_off + idx * 4)
+#define LIGHT_PM_PAD_MUX(idx)          (priv->base +  priv->info->mux_off + idx * 4)
+#endif
+
 struct light_pinctrl {
        struct device *dev;
        struct pinctrl_dev *pctl;
        void __iomem *base;
+       struct clk      *clk;
+#ifdef CONFIG_PM_SLEEP
+       unsigned int    cfg_bak[MAX_CFG_REG_NUMS];
+       unsigned int    mux_bak[MAX_MUX_REG_NUMS];
+#endif
        const struct light_pinctrl_soc_info *info;
 };
 
@@ -598,6 +618,20 @@ static int light_pinctrl_probe(struct platform_device *pdev)
 
        priv->info = info;
        priv->dev = info->dev;
+       if ((priv->info->type == LIGHT_FM_LEFT) ||
+                       (priv->info->type == LIGHT_FM_RIGHT)) {
+               priv->clk = devm_clk_get_optional(&pdev->dev, "pclk");
+               if(priv->clk == NULL) {
+                       dev_err(&pdev->dev, "could not get padctrl clk\n");
+                       return -EINVAL;
+               }
+               ret = clk_prepare_enable(priv->clk);
+               if (ret) {
+                       dev_err(&pdev->dev, "could not enable padctrl clk\n");
+                       return -EINVAL;
+               }
+       }
+
        platform_set_drvdata(pdev, priv);
        priv->pctl = pinctrl_register(info->desc, &pdev->dev, priv);
        if (!priv->pctl) {
@@ -610,6 +644,98 @@ static int light_pinctrl_probe(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int light_pinctrl_backup_regs(struct light_pinctrl *priv, unsigned int cfg_reg_nums,
+                                       unsigned int mux_reg_nums)
+{
+       int i;
+
+       for (i = 0; i < cfg_reg_nums; i++)
+               priv->cfg_bak[i] = readl(LIGHT_PM_PAD_CFG(i));
+       for (i = 0; i < mux_reg_nums; i++)
+               priv->mux_bak[i] = readl(LIGHT_PM_PAD_MUX(i));
+
+       return 0;
+}
+
+static int light_pinctrl_restore_regs(struct light_pinctrl *priv, unsigned int cfg_reg_nums,
+                               unsigned int mux_reg_nums)
+{
+       int i;
+
+       for (i = 0; i < cfg_reg_nums; i++)
+               writel(priv->cfg_bak[i], LIGHT_PM_PAD_CFG(i));
+       for (i = 0; i < mux_reg_nums; i++)
+               writel(priv->mux_bak[i], LIGHT_PM_PAD_MUX(i));
+
+       return 0;
+}
+
+static int light_pinctrl_suspend(struct device *dev)
+{
+       dev_info(dev, "light pinctrl suspend\n");
+       struct light_pinctrl *priv = dev_get_drvdata(dev);
+       int ret = 0;
+
+       switch(priv->info->type) {
+               case LIGHT_FM_RIGHT:
+                       ret = light_pinctrl_backup_regs(priv, LIGHT_PADCTRL0_CFG_REG_NUMS, LIGHT_PADCTRL0_MUX_REG_NUMS);
+                       clk_disable_unprepare(priv->clk);
+                       break;
+               case LIGHT_FM_LEFT:
+                       ret = light_pinctrl_backup_regs(priv, LIGHT_PADCTRL1_CFG_REG_NUMS, LIGHT_PADCTRL1_MUX_REG_NUMS);
+                       clk_disable_unprepare(priv->clk);
+                       break;
+               case LIGHT_FM_AON:
+                       break;
+               case LIGHT_FM_AUDIO:
+                       ret = light_pinctrl_backup_regs(priv, LIGHT_AUDIO_CFG_REG_NUMS, LIGHT_AUDIO_MUX_REG_NUMS);
+                       priv->mux_bak[LIGHT_AUDIO_IO_SEL_IDX] = readl(priv->base);
+                       break;
+               default:
+                       break;
+       }
+
+       return ret;
+}
+
+static int light_pinctrl_resume(struct device *dev)
+{
+       dev_info(dev, "light pinctrl resume\n");
+       struct light_pinctrl *priv = dev_get_drvdata(dev);
+       int ret = 0;
+
+       switch(priv->info->type) {
+               case LIGHT_FM_RIGHT:
+                       ret = clk_prepare_enable(priv->clk);
+                       if (ret) {
+                               dev_err(dev, "could not enable padctrl clk\n");
+                               return -EINVAL;
+                       }
+                       ret = light_pinctrl_restore_regs(priv, LIGHT_PADCTRL0_CFG_REG_NUMS, LIGHT_PADCTRL0_MUX_REG_NUMS);
+                       break;
+               case LIGHT_FM_LEFT:
+                       ret = clk_prepare_enable(priv->clk);
+                       if (ret) {
+                               dev_err(dev, "could not enable padctrl clk\n");
+                               return -EINVAL;
+                       }
+                       ret = light_pinctrl_restore_regs(priv, LIGHT_PADCTRL1_CFG_REG_NUMS, LIGHT_PADCTRL1_MUX_REG_NUMS);
+                       break;
+               case LIGHT_FM_AON:
+                       break;
+               case LIGHT_FM_AUDIO:
+                       ret = light_pinctrl_restore_regs(priv, LIGHT_AUDIO_CFG_REG_NUMS, LIGHT_AUDIO_MUX_REG_NUMS);
+                       writel(priv->mux_bak[LIGHT_AUDIO_IO_SEL_IDX], priv->base);
+                       break;
+               default:
+                       break;
+       }
+
+       return ret;
+}
+#endif //CONFIG_PM_SLEEP
+
 /* Pad names for the pinmux subsystem */
 static const struct pinctrl_pin_desc light_mpw_pinctrl_pads[] = {
        LIGHT_PINCTRL_PIN(BOOT_SEL0),
@@ -977,6 +1103,13 @@ static struct light_pinctrl_soc_info light_fm_audio_pinctrl_info = {
        .mux_off = 0x4,
 };
 
+//static SIMPLE_DEV_PM_OPS(light_pinctrl_dev_pm_ops,
+//                      light_pinctrl_suspend, light_pinctrl_resume);
+
+static const struct dev_pm_ops light_pinctrl_dev_pm_ops = {
+       SET_LATE_SYSTEM_SLEEP_PM_OPS(light_pinctrl_suspend, light_pinctrl_resume)
+};
+
 static const struct of_device_id light_pinctrl_of_match[] = {
        { .compatible = "thead,light-mpw-pinctrl", .data = &light_mpw_pinctrl_info},
        { .compatible = "thead,light-fm-right-pinctrl", .data = &light_fm_right_pinctrl_info},
@@ -991,6 +1124,7 @@ static struct platform_driver light_pinctrl_driver = {
        .driver = {
                .name = "light-pinctrl",
                .owner = THIS_MODULE,
+               .pm = &light_pinctrl_dev_pm_ops,
                .of_match_table = light_pinctrl_of_match,
                .suppress_bind_attrs = true,
        },
index 493ea8c0940191742c0ba74a8b8d72e835c7818b..c6e888dccfa7253d6a0f86f328813bf85b277169 100644 (file)
@@ -126,6 +126,13 @@ static bool light_rpmsg_notify(struct virtqueue *vq)
        int ret;
        struct light_rpmsg_vq_info *rpvq = vq->priv;
 
+#ifdef CONFIG_PM_SLEEP
+    if(rpvq->rpdev->sleep_flag) {
+        dev_err(tdev_priv->dev, "dev in deep sleep, Channel cannot do Tx+++\n");
+               return -EINVAL;
+       }
+#endif
+
        mu_rpmsg = rpvq->vq_id << 16;
        mutex_lock(&rpvq->rpdev->lock);
 
@@ -433,7 +440,9 @@ static int light_rpmsg_probe(struct platform_device *pdev)
 
        INIT_DELAYED_WORK(&(rpdev->rpmsg_work), rpmsg_work_handler);
        BLOCKING_INIT_NOTIFIER_HEAD(&(rpdev->notifier));
-
+#ifdef  CONFIG_PM_SLEEP
+    sema_init(&rpdev->pm_sem, 0);
+#endif
        pr_info("light rpmsg: Ready for cross core communication!\n");
 
        ret = of_property_read_u32(np, "vdev-nums", &rpdev->vdev_nums);
@@ -485,28 +494,111 @@ static int light_rpmsg_probe(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int light_rpmsg_suspend(struct device *dev)
-{
-       struct light_rpmsg_vproc *rpdev = dev_get_drvdata(dev);
-
-       clk_disable_unprepare(rpdev->mu_clk);
 
+typedef enum {
+   RPMSG_MAILBOX_TYPE_PM = 0xA0,
+   RPMSG_MAILBOX_TYPE_MAX
+} rpmsg_mailbox_message_type_en;
+
+typedef enum {
+   RPMSG_PM_CTRL = 0x50,
+   RPMSG_PM_GET,
+   RPMSG_PM_STATUS,
+   RPMSG_PM_MAX
+} rpmsg_pm_message_type_en;
+
+typedef enum {
+   LIGHT_PM_DISABLE = 0xA0,
+   LIGHT_PM_OFF,
+   LIGHT_PM_HW_VAD,
+   LIGHT_PM_TYPE_MAX
+} light_pm_type_en;
+
+typedef enum {
+   LIGHT_PM_WAKEUP = 0x50,
+   LIGHT_PM_SLEEP,
+   LIGHT_PM_STATUS_MAX
+} light_pm_status_en;
+
+#define MAX_PM_NOTIFY_TIME 10
+#define MAX_PM_ASK_TIME 10
+
+static int light_rpmsg_sleep_notify(struct virtqueue *vq, light_pm_type_en type)
+{
+       int ret;
+       struct light_rpmsg_vq_info *rpvq = vq->priv;
+       uint8_t sleep_ctrl[4] = {RPMSG_MAILBOX_TYPE_PM, RPMSG_PM_CTRL, type, '\n'};
+       mutex_lock(&rpvq->rpdev->lock);
+       ret = mbox_send_message(tdev_priv->tx_channel, sleep_ctrl);
+       if(ret < 0) {
+       pr_err("sleep notify faild %d", ret);
+          mutex_unlock(&rpvq->rpdev->lock);
+       return ret;
+       }
+       mutex_unlock(&rpvq->rpdev->lock);
        return 0;
 }
 
-static int light_rpmsg_resume(struct device *dev)
+static int light_rpmsg_sleep_ask(struct virtqueue *vq)
 {
-       struct light_rpmsg_vproc *rpdev = dev_get_drvdata(dev);
        int ret;
+       struct light_rpmsg_vq_info *rpvq = vq->priv;
+       uint8_t sleep_get[3] = {RPMSG_MAILBOX_TYPE_PM, RPMSG_PM_GET, '\n'};
+       mutex_lock(&rpvq->rpdev->lock);
+       ret = mbox_send_message(tdev_priv->tx_channel, sleep_get);
+       if(ret < 0) {
+       pr_err("sleep ask send faild %d", ret);
+          mutex_unlock(&rpvq->rpdev->lock);
+       return ret;
+       }
+       mutex_unlock(&rpvq->rpdev->lock);
+       return 0;
+}
 
-       ret = clk_prepare_enable(rpdev->mu_clk);
-       if (ret) {
-               pr_err("unable to enable mu clock\n");
-               return ret;
+static int light_rpmsg_suspend(struct device *dev)
+
+{
+  int ret;
+  int try_num = 0;
+  struct light_rpmsg_vproc *rpdev = dev_get_drvdata(dev);
+
+  //clk_disable_unprepare(rpdev->mu_clk);
+  printk("%s,%d,enter",__func__,__LINE__);
+  light_rpmsg_sleep_notify(rpdev->ivdev[0].vq[0], LIGHT_PM_OFF);
+  try_num++;
+  down_timeout(&rpdev->pm_sem, msecs_to_jiffies(200));
+  while(!rpdev->sleep_flag) {
+    light_rpmsg_sleep_notify(rpdev->ivdev[0].vq[0], LIGHT_PM_OFF);
+       down_timeout(&rpdev->pm_sem, msecs_to_jiffies(200));
+       if(try_num++ > MAX_PM_NOTIFY_TIME) {
+         pr_err("sleep notify faild after try %d time", MAX_PM_NOTIFY_TIME);
+                printk("%s,%d,try %d times, exist",__func__,__LINE__, try_num);
+                return -1;
        }
+  }
+  printk("%s,%d,try %d times, exist",__func__,__LINE__, try_num);
+  return 0;
+}
 
-       return ret;
+static int light_rpmsg_resume(struct device *dev)
+{
+  struct light_rpmsg_vproc *rpdev = dev_get_drvdata(dev);
+  int ret;
+  int try_num = 0;
+  printk("%s,%d,enter",__func__,__LINE__);
+  while(rpdev->sleep_flag) {
+    ret = light_rpmsg_sleep_ask(rpdev->ivdev[0].vq[0]);
+    down_timeout(&rpdev->pm_sem, msecs_to_jiffies(200));
+       if(try_num++ > MAX_PM_ASK_TIME) {
+         pr_err("sleep status check faild after try %d time", MAX_PM_ASK_TIME);
+                printk("%s,%d,try %d times, exist",__func__,__LINE__, try_num);
+                return -1;
+       }
+  }
+  printk("%s,%d,try %d times, exist",__func__,__LINE__, try_num);
+  return ret;
 }
+
 #endif
 
 static SIMPLE_DEV_PM_OPS(light_rpmsg_pm_ops, light_rpmsg_suspend, light_rpmsg_resume);
@@ -643,7 +735,19 @@ static void mbox_client_light_receive_message(struct mbox_client *client,
 
        //printk("mbox_client receive rpmsg_work\n");
        schedule_delayed_work(&(pri_rpdev->rpmsg_work), 0);
-
+#ifdef CONFIG_PM_SLEEP
+     if(data[0] == RPMSG_MAILBOX_TYPE_PM && data[1] == RPMSG_PM_STATUS) {
+        if(data[2] == LIGHT_PM_WAKEUP) {
+            pri_rpdev->sleep_flag = 0;
+                       up(&pri_rpdev->pm_sem);
+                       printk("audio wakeup");
+               } else if(data[2] == LIGHT_PM_SLEEP) {
+            pri_rpdev->sleep_flag = 1;
+                       up(&pri_rpdev->pm_sem);
+                       printk("audio sleep");
+               }
+        }
+#endif
        //print_hex_dump(KERN_INFO, __func__, DUMP_PREFIX_NONE, 16, 1, tdev->rx_buffer, MBOX_MAX_MSG_LEN, true);
 }
 
index 1286a2ed3ea86450f299bf53461103a4d4676dc7..13462cfc07b938ae8a926a9f25dba7248cf515d9 100644 (file)
@@ -53,7 +53,6 @@ struct light_iopmp_cur_sys_entry {
 
 struct light_iopmp_info {
        struct device *dev;
-
        int entries;
        struct light_iopmp_list *iopmp_list;
        struct light_iopmp_cur_sys_entry cur_entry;
@@ -64,35 +63,41 @@ static struct light_iopmp_list {
        int iopmp_type;
        resource_size_t offset;
        void __iomem *base;
+       bool bypass_en;
+       bool is_default_region;
+       u32 dummy_slave;
+       u32 attr;
+       int lock;
+       struct light_iopmp_entry iopmp_entry;
 } light_iopmp_lists[] = {
-       {IOPMP_EMMC, 0xFFFC028000, NULL},
-       {IOPMP_SDIO0, 0xFFFC029000, NULL},
-       {IOPMP_SDIO1, 0xFFFC02A000, NULL},
-       {IOPMP_USB0, 0xFFFC02E000, NULL},
-       {IOPMP_AO, 0xFFFFC21000, NULL},
-       {IOPMP_AUD, 0xFFFFC22000, NULL},
-       {IOPMP_CHIP_DBG, 0xFFFFC37000, NULL},
-       {IOPMP_EIP120I, 0xFFFF220000, NULL},
-       {IOPMP_EIP120II, 0xFFFF230000, NULL},
-       {IOPMP_EIP120III, 0xFFFF240000, NULL},
-       {IOPMP_ISP0, 0xFFF4080000, NULL},
-       {IOPMP_ISP1, 0xFFF4081000, NULL},
-       {IOPMP_DW200, 0xFFF4082000, NULL},
-       {IOPMP_VIPRE, 0xFFF4083000, NULL},
-       {IOPMP_VENC, 0xFFFCC60000, NULL},
-       {IOPMP_VDEC, 0xFFFCC61000, NULL},
-       {IOPMP_G2D, 0xFFFCC62000, NULL},
-       {IOPMP_FCE, 0xFFFCC63000, NULL},
-       {IOPMP_NPU, 0xFFFF01C000, NULL},
-       {IOPMP0_DPU, 0xFFFF520000, NULL},
-       {IOPMP1_DPU, 0xFFFF521000, NULL},
-       {IOPMP_GPU, 0xFFFF522000, NULL},
-       {IOPMP_GMAC1, 0xFFFC001000, NULL},
-       {IOPMP_GMAC2, 0xFFFC002000, NULL},
-       {IOPMP_DMAC, 0xFFFFC20000, NULL},
-       {IOPMP_TEE_DMAC, 0xFFFF250000, NULL},
-       {IOPMP_DSP0, 0xFFFF058000, NULL},
-       {IOPMP_DSP1, 0xFFFF059000, NULL},
+       {IOPMP_EMMC, 0xFFFC028000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_SDIO0, 0xFFFC029000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_SDIO1, 0xFFFC02A000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_USB0, 0xFFFC02E000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_AO, 0xFFFFC21000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_AUD, 0xFFFFC22000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_CHIP_DBG, 0xFFFFC37000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_EIP120I, 0xFFFF220000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_EIP120II, 0xFFFF230000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_EIP120III, 0xFFFF240000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_ISP0, 0xFFF4080000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_ISP1, 0xFFF4081000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_DW200, 0xFFF4082000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_VIPRE, 0xFFF4083000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_VENC, 0xFFFCC60000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_VDEC, 0xFFFCC61000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_G2D, 0xFFFCC62000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_FCE, 0xFFFCC63000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_NPU, 0xFFFF01C000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP0_DPU, 0xFFFF520000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP1_DPU, 0xFFFF521000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_GPU, 0xFFFF522000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_GMAC1, 0xFFFC001000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_GMAC2, 0xFFFC002000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_DMAC, 0xFFFFC20000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_TEE_DMAC, 0xFFFF250000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_DSP0, 0xFFFF058000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
+       {IOPMP_DSP1, 0xFFFF059000, NULL, false, false, LIGHT_IOPMP_DUMMY_ADDR, 0xFFFFFFFF, 0x7FFFF, {{0,},{0,},0} },
 };
 
 static const struct light_iopmp_driver_data {
@@ -481,6 +486,35 @@ static ssize_t light_iopmp_set_store(struct device *dev,
        return count;
 }
 
+
+static int light_iopmp_config(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct light_iopmp_info *info = platform_get_drvdata(pdev);
+       struct light_iopmp_list *list = info->iopmp_list;
+       int i, j;
+
+       for (i = 0; i < info->entries; i++) {
+               if (list[i].iopmp_entry.entry_valid_num > 0) {
+                       /* config the iopmp entry */
+                       light_iopmp_set_attr(info, i,
+                               list[i].attr,
+                               list[i].dummy_slave,
+                               &list[i].iopmp_entry);
+
+               } else {
+                       /* config the iopmp entry */
+                       if (list[i].bypass_en)
+                               light_iopmp_set_attr_bypass(info, i);
+                       else if (list[i].is_default_region)
+                               light_iopmp_set_attr_default(info, i, list[i].attr);
+               }
+       }
+
+       return 0;
+}
+
+
 static DEVICE_ATTR(light_iopmp_tap, 0644, light_iopmp_tap_show, light_iopmp_tap_store);
 static DEVICE_ATTR(light_iopmp_start_addr, 0644, light_iopmp_start_addr_show, light_iopmp_start_addr_store);
 static DEVICE_ATTR(light_iopmp_end_addr, 0644, light_iopmp_end_addr_show, light_iopmp_end_addr_store);
@@ -506,19 +540,20 @@ static int light_iopmp_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        const struct light_iopmp_driver_data *drvdata = NULL;
-       struct light_iopmp_list *match;
+       struct light_iopmp_list *list;
        struct light_iopmp_info *info;
        struct device_node *entry_np;
        int size, entries;
        int ret;
+       int i, j;
 
        drvdata = device_get_match_data(dev);
        if (!drvdata) {
                dev_err(dev, "cannot get driver data\n");
                return -ENOENT;
        }
-       match = drvdata->iopmp_list;
-       entries = light_iopmp_get_entries(match);
+       list = drvdata->iopmp_list;
+       entries = light_iopmp_get_entries(list);
        if (entries <= 0)
                return -ENOENT;
 
@@ -528,7 +563,7 @@ static int light_iopmp_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        info->entries = entries;
-       info->iopmp_list = match;
+       info->iopmp_list = list;
        info->dev = dev;
 
        for_each_child_of_node(dev->of_node, entry_np) {
@@ -538,7 +573,6 @@ static int light_iopmp_probe(struct platform_device *pdev)
                bool bypass_en = false;
                int region_size;
                int type;
-               int i = 0, j;
 
                if (!of_device_is_available(entry_np))
                        continue;
@@ -566,13 +600,6 @@ static int light_iopmp_probe(struct platform_device *pdev)
                                dev_err(dev, "missing/invalid reg property\n");
                                continue;
                        }
-
-                       /* config the iopmp entry */
-                       if (bypass_en)
-                               light_iopmp_set_attr_bypass(info, type);
-                       else if (is_default_region)
-                               light_iopmp_set_attr_default(info, type, attr);
-
                } else {
                        region_size >>= 1;
                        if (region_size > LIGHT_IOPMP_REGION_NUM) {
@@ -582,28 +609,30 @@ static int light_iopmp_probe(struct platform_device *pdev)
 
                        for (j = 0; j < region_size; j++) {
                                ret = of_property_read_u32_index(entry_np, "regions",
-                                               j << 1, &info->iopmp_entry[i].reg_start[j]);
+                                               j << 1, &list[type].iopmp_entry.reg_start[j]);
                                if (ret)
                                        break;
                                ret = of_property_read_u32_index(entry_np, "regions",
-                                               (j << 1) + 1, &info->iopmp_entry[i].reg_end[j]);
+                                               (j << 1) + 1, &list[type].iopmp_entry.reg_end[j]);
                                if (ret)
                                        break;
 
                                /* check the region valid, otherwise drop the iopmp setting */
-                               if (j && info->iopmp_entry[i].reg_end[j - 1] > info->iopmp_entry[i].reg_start[j])
+                               if (j && list[type].iopmp_entry.reg_end[j - 1] > list[type].iopmp_entry.reg_start[j])
                                        break;
                        }
 
-                       if (j == region_size) {
-                               info->iopmp_entry[i].entry_valid_num = region_size;
+                       if (j < region_size)
+                               continue;
 
-                               /* config the iopmp entry */
-                               light_iopmp_set_attr(info, type, attr, dummy_slave, &info->iopmp_entry[i]);
-                       }
+                       list[type].iopmp_entry.entry_valid_num = region_size;
                }
 
-               i++;
+               // store valid config
+               list[type].attr = attr;
+               list[type].dummy_slave = dummy_slave;
+               list[type].is_default_region = is_default_region;
+               list[type].bypass_en = bypass_en;
        }
 
        /* init the cur entry config */
@@ -615,6 +644,8 @@ static int light_iopmp_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, info);
 
+       light_iopmp_config(&pdev->dev);
+
        ret = sysfs_create_group(&pdev->dev.kobj, &dev_attr_light_iopmp_group);
        if (ret) {
                dev_err(&pdev->dev, "Failed to create light iopmp debug sysfs.\n");
@@ -632,17 +663,17 @@ static int light_iopmp_remove(struct platform_device *pdev)
 
 static int __maybe_unused light_iopmp_noirq_suspend(struct device *dev)
 {
-       /* the iopmp clocks depend on module theirself,
-        * we should keep them always enabled in clock's driver
-        */
+       // nothing to do
        return 0;
 }
 
 static int __maybe_unused light_iopmp_noirq_resume(struct device *dev)
 {
-       /* TBD: restore IOPMP in noirq stage is too late ?
-        * we should restore these registers setting in early arch resume ?
-        */
+       /* reinit iopmp register setting
+          ensure system clock enabled before here
+       */
+       light_iopmp_config(dev);
+
        return 0;
 }
 
index c6151e1c037f700dff7ce893230cda4e7dcba81f..f8f6292bac6e380c48a5ae46c222980182fe8426 100644 (file)
@@ -63,14 +63,14 @@ static int light_event_aon_reservemem(struct light_event *event)
        struct light_aon_ipc *ipc = event->ipc_handle;
        int ret = 0;
 
-       dev_dbg(light_event->dev, "aon reservemem...\n");
+       dev_dbg(event->dev, "aon reservemem...\n");
 
        light_event_msg_hdr_fill(&event->msg.hdr, LIGHT_AON_MISC_FUNC_AON_RESERVE_MEM);
        event->msg.reserve_offset = LIGHT_EVENT_OFFSET;
 
        ret = light_aon_call_rpc(ipc, &event->msg, true);
        if (ret)
-               dev_err(light_event->dev, "failed to set aon reservemem\n");
+               dev_err(event->dev, "failed to set aon reservemem\n");
 
        return ret;
 }
@@ -79,7 +79,7 @@ int light_event_set_rebootmode(enum light_rebootmode_index mode)
 {
        int ret;
 
-       if (!light_event->init)
+       if (!light_event || !light_event->init)
                return -EINVAL;
 
        ret = regmap_write(light_event->aon_iram, LIGHT_EVENT_OFFSET, mode);
@@ -98,7 +98,7 @@ int light_event_get_rebootmode(enum light_rebootmode_index *mode)
 {
        int ret;
 
-       if (!light_event->init)
+       if (!light_event || !light_event->init)
                return -EINVAL;
 
        ret = regmap_read(light_event->aon_iram, LIGHT_EVENT_OFFSET, mode);
@@ -215,7 +215,6 @@ static int light_event_probe(struct platform_device *pdev)
        thead = devm_kzalloc(&pdev->dev, sizeof(*thead), GFP_KERNEL);
        if (!thead)
                return -ENOMEM;
-       light_event = thead;
 
        ret = light_aon_get_handle(&(thead->ipc_handle));
        if (ret == -EPROBE_DEFER)
@@ -238,10 +237,12 @@ static int light_event_probe(struct platform_device *pdev)
                return -EPERM;
        }
        thead->init = true;
+       light_event = thead;
 
        ret = light_event_check_powerup();
        if (ret) {
                dev_err(dev, "check powerup failed!\n");
+               light_event = NULL;
                return -EPERM;
        }
        dev_info(dev, "light-event driver init successfully\n");
index 60158671b9333cf186f226dc97b79925e3b44a2c..ba89975c8cbc2d26e09314e82dc3552970e4b7a7 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) 2022 Alibaba Group Holding Limited.
  */
 #include <linux/of_address.h>
 #include <linux/of.h>
 #include <linux/types.h>
+#include <asm/sbi.h>
+#include <asm/suspend.h>
+#include <linux/firmware/thead/ipc.h>
+#include <linux/platform_device.h>
 
 #undef pr_fmt
 #define pr_fmt(fmt) "light-system-suspend" ": " fmt
 
-#ifdef CONFIG_PLIC_INT_CLEAR
-void __iomem *hart0_sbase;
-/*
- * Each hart context has a set of control registers associated with it.  Right
- * now there's only two: a source priority threshold over which the hart will
- * take an interrupt, and a register to claim interrupts.
- */
-#define CONTEXT_BASE                   0x200000
-#define CONTEXT_PER_HART               0x1000
-#define CONTEXT_THRESHOLD              0x00
-#define CONTEXT_CLAIM                  0x04
-#endif
+struct rpc_msg_cpu_info{
+       u16 cpu_id;
+       u16 status;
+       u32 reserved[5];
+} __packed __aligned(4);
+
+struct light_aon_msg_pm_ctrl {
+       struct light_aon_rpc_msg_hdr hdr;
+       union rpc_func_t {
+       struct rpc_msg_cpu_info cpu_info;
+       } __packed __aligned(4) rpc;
+} __packed __aligned(4);
+
+struct light_aon_pm_ctrl {
+       struct device                   *dev;
+       struct light_aon_ipc            *ipc_handle;
+       struct light_aon_msg_pm_ctrl msg;
+       bool   suspend_flag;
+};
 
-static int light_suspend_prepare_late(void)
+static struct light_aon_pm_ctrl *aon_pm_ctrl;
+
+static int light_require_state_pm_ctrl(struct light_aon_msg_pm_ctrl *msg, enum light_aon_misc_func func, bool ack)
 {
-#ifdef CONFIG_PLIC_INT_CLEAR
-       void __iomem *claim = hart0_sbase + CONTEXT_CLAIM;
-       irq_hw_number_t hwirq;
+       pr_debug("notify aon subsys...\n");
+       struct light_aon_ipc *ipc = aon_pm_ctrl->ipc_handle;
+       struct light_aon_rpc_msg_hdr *hdr = &msg->hdr;
 
-       pr_debug("clear plic pending interrupt\n");
-       pr_debug("claim base = 0x%lx\n", (unsigned long)claim);
+       hdr->ver = LIGHT_AON_RPC_VERSION;
+       hdr->svc = (uint8_t)LIGHT_AON_RPC_SVC_MISC;
+       hdr->func = (uint8_t)func;
+       hdr->size = LIGHT_AON_RPC_MSG_NUM;
 
-       while ((hwirq = readl(claim))) {
-               pr_debug("claim hwirq%ld\n", hwirq);
-               writel(hwirq, claim);
-       }
-#endif
-       /*
-        * Two-level switch for interrupt control: it needs to disable interrupts in SIE, not just in SSTATUS,
-        * otherwise the pending interrupts will wakup the cpu immediately after wfi.
-        */
-       csr_write(CSR_IE, 0);
+       return light_aon_call_rpc(ipc, msg, ack);
+}
 
-       return 0;
+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 light_suspend_enter(suspend_state_t state)
 {
+       struct light_aon_msg_pm_ctrl msg = {0};
+       unsigned long suspend_type;
+
        if (!IS_ENABLED(CONFIG_PM))
                return 0;
+       pr_debug("[%s,%d]enter platform system suspend... state:%d\n", __func__, __LINE__, state);
 
-       switch (state) {
-       case PM_SUSPEND_MEM:
-               pr_debug("enter platform system suspend...\n");
-               cpu_do_idle();
-               pr_debug("wakeup from wfi...\n");
-       break;
-       default:
+       if (state == PM_SUSPEND_MEM)
+               suspend_type = SBI_HSM_SUSP_NON_RET_BIT;
+       else
                return -EINVAL;
-       }
 
+       cpu_suspend(suspend_type, sbi_suspend_finisher);
+       pr_debug("[%s,%d]wakeup from system suspend\n",__func__, __LINE__);
        return 0;
 }
 
-static const struct platform_suspend_ops light_suspend_ops = {
-       .enter = light_suspend_enter,
-       .valid = suspend_valid_only_mem,
-       .prepare_late = light_suspend_prepare_late,
-};
+static int light_suspend_prepare(void)
+{
+       int ret;
+       aon_pm_ctrl->suspend_flag = true;
+       struct light_aon_msg_pm_ctrl msg = {0};
+       ret = light_require_state_pm_ctrl(&msg, LIGHT_AON_MISC_FUNC_REQUIRE_STR, false);
+       if (ret) {
+               pr_err("[%s,%d]failed to initiate Suspend to Ram process to AON subsystem\n",__func__, __LINE__);
+               return ret;
+       }
+       return 0;
+}
 
-static int __init pm_light_init(void)
+static void light_resume_wake(void)
 {
-#ifdef CONFIG_PLIC_INT_CLEAR
-       struct device_node *np;
-       void __iomem *regs;
+       aon_pm_ctrl->suspend_flag = false;
+}
 
-       np = of_find_node_by_path("/soc/interrupt-controller@ffd8000000");
-       if (!np) {
-               pr_err("no plic interrupt controller found\n");
-               return -EINVAL;
+static int thead_cpuhp_offline(unsigned int cpu)
+{
+       int ret;
+       if(!aon_pm_ctrl->suspend_flag)
+       {
+               struct light_aon_msg_pm_ctrl msg = {0};
+               msg.rpc.cpu_info.cpu_id = (u16)cpu;
+               msg.rpc.cpu_info.status = 0;
+               ret = light_require_state_pm_ctrl(&msg, LIGHT_AON_MISC_FUNC_CPUHP, false);
+               if (ret) {
+                       pr_info("failed to notify aon subsys with cpuhp...%08x\n", ret);
+                       return ret;
+               }
        }
+       return 0;
+}
 
-       regs = of_iomap(np, 0);
-       if (WARN_ON(!regs)) {
-               pr_err("failed to ioremap interrupt regs\n");
-               return -EIO;
+static int thead_cpuhp_online(unsigned int cpu)
+{
+       int ret;
+       if(!aon_pm_ctrl->suspend_flag)
+       {
+               struct light_aon_msg_pm_ctrl msg = {0};
+               msg.rpc.cpu_info.cpu_id = (u16)cpu;
+               msg.rpc.cpu_info.status = 1;
+               ret = light_require_state_pm_ctrl(&msg, LIGHT_AON_MISC_FUNC_CPUHP, false);
+               if (ret) {
+                       pr_info("[%s,%d]failed to bring up aon subsys with cpuhp...%08x\n", __func__, __LINE__, ret);
+                       return ret;
+               }
        }
+       return 0;
+}
 
-       hart0_sbase = regs + CONTEXT_BASE + 1 * CONTEXT_PER_HART; /* 1 means s mode */
+static const struct of_device_id aon_ctrl_ids[] = {
+       { .compatible = "thead,light-aon-suspend-ctrl" },
+       {}
+};
 
-       pr_debug("hart0_sbase = 0x%lx\n", (unsigned long)hart0_sbase);
-#endif
+static const struct platform_suspend_ops light_suspend_ops = {
+       .enter = light_suspend_enter,
+       .valid = suspend_valid_only_mem,
+       .prepare_late = light_suspend_prepare,
+       .wake = light_resume_wake,
+};
 
-       pr_info("set system suspend platform callbacks\n");
+static int light_pm_probe(struct platform_device *pdev)
+{
+       struct device                   *dev = &pdev->dev;
+       int ret;
+       struct light_aon_pm_ctrl        *pm_ctrl;
+
+       pm_ctrl = devm_kzalloc(&pdev->dev, sizeof(*aon_pm_ctrl), GFP_KERNEL);
+       if (!pm_ctrl)
+               return -ENOMEM;
+       aon_pm_ctrl = pm_ctrl;
+
+       ret = light_aon_get_handle(&(aon_pm_ctrl->ipc_handle));
+       if (ret == -EPROBE_DEFER) {
+               pr_err("[%s, %d]failed to register ipc_handler.\n",__func__, __LINE__);
+               return ret;
+       }
 
        suspend_set_ops(&light_suspend_ops);
 
+       ret = cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN, "soc/thead:online",
+                       thead_cpuhp_online,
+                       thead_cpuhp_offline);
+       if(ret < 0) {
+               pr_err("[%s,%d]failed to register hotplug callbacks with err %08x.\n", __func__, __LINE__, ret);
+               return ret;
+       }
+
+       aon_pm_ctrl->dev = &pdev->dev;
+       aon_pm_ctrl->suspend_flag = false;
+       platform_set_drvdata(pdev, aon_pm_ctrl);
+
+       dev_info(&pdev->dev, "Light power management control sys successfully registered\n");
        return 0;
 }
 
-late_initcall(pm_light_init);
+static struct platform_driver light_pm_driver = {
+       .driver = {
+               .name   = "light-pm",
+               .of_match_table = aon_ctrl_ids,
+       },
+       .probe = light_pm_probe,
+};
+
+module_platform_driver(light_pm_driver);
index c33866f747dbedb590825329f760c63ca893f336..d15f65226ee7285cc45cf7e3fc263abf58cbcec1 100644 (file)
@@ -89,6 +89,52 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
 }
 #endif /* CONFIG_DEBUG_FS */
 
+#ifdef CONFIG_PM_SLEEP
+static void dw_spi_register_suspend(struct dw_spi *dws)
+{
+    struct dw_spi_context *ctx = &dws->ctx;
+
+    ctx->ctrlr0         = dw_readl(dws, DW_SPI_CTRLR0);
+    ctx->ctrlr1         = dw_readl(dws, DW_SPI_CTRLR1);
+    ctx->ssienr         = dw_readl(dws, DW_SPI_SSIENR);
+    ctx->mwcr           = dw_readl(dws, DW_SPI_MWCR);
+    ctx->ser            = dw_readl(dws, DW_SPI_SER);
+    ctx->baudr          = dw_readl(dws, DW_SPI_BAUDR);
+    ctx->txftlr         = dw_readl(dws, DW_SPI_TXFTLR);
+    ctx->rxftlr         = dw_readl(dws, DW_SPI_RXFTLR);
+    ctx->txflr          = dw_readl(dws, DW_SPI_TXFLR);
+    ctx->rxflr          = dw_readl(dws, DW_SPI_RXFLR);
+    ctx->imr            = dw_readl(dws, DW_SPI_IMR);
+    ctx->dmacr          = dw_readl(dws, DW_SPI_DMACR);
+    ctx->dmatdlr        = dw_readl(dws, DW_SPI_DMATDLR);
+    ctx->dmardlr        = dw_readl(dws, DW_SPI_DMARDLR);
+    ctx->rx_sample_dly  = dw_readl(dws, DW_SPI_RX_SAMPLE_DLY);
+
+}
+
+static void dw_spi_register_resume(struct dw_spi *dws)
+{
+    struct dw_spi_context *ctx = &dws->ctx;
+
+    dw_writel(dws, DW_SPI_SSIENR, 0);
+    dw_writel(dws, DW_SPI_CTRLR0, ctx->ctrlr0);
+    dw_writel(dws, DW_SPI_CTRLR1, ctx->ctrlr1);
+    dw_writel(dws, DW_SPI_TXFTLR, ctx->txftlr);
+    dw_writel(dws, DW_SPI_RXFTLR, ctx->rxftlr);
+    dw_writel(dws, DW_SPI_TXFLR, ctx->txflr);
+    dw_writel(dws, DW_SPI_RXFLR, ctx->rxflr);
+    dw_writel(dws, DW_SPI_IMR, ctx->imr);
+    dw_writel(dws, DW_SPI_DMATDLR, ctx->dmatdlr);
+    dw_writel(dws, DW_SPI_DMARDLR, ctx->dmardlr);
+    dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, ctx->rx_sample_dly);
+    dw_writel(dws, DW_SPI_SER, ctx->ser);
+    dw_writel(dws, DW_SPI_BAUDR, ctx->baudr);
+    dw_writel(dws, DW_SPI_MWCR, ctx->mwcr);
+    dw_writel(dws, DW_SPI_DMACR, ctx->dmacr);
+    dw_writel(dws, DW_SPI_SSIENR, ctx->ssienr);
+}
+#endif
+
 void dw_spi_set_cs(struct spi_device *spi, bool enable)
 {
        struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
@@ -940,6 +986,9 @@ int dw_spi_suspend_host(struct dw_spi *dws)
        if (ret)
                return ret;
 
+#ifdef CONFIG_PM_SLEEP
+    dw_spi_register_suspend(dws);
+#endif
        spi_shutdown_chip(dws);
        return 0;
 }
@@ -948,6 +997,9 @@ EXPORT_SYMBOL_GPL(dw_spi_suspend_host);
 int dw_spi_resume_host(struct dw_spi *dws)
 {
        spi_hw_init(&dws->master->dev, dws);
+#ifdef CONFIG_PM_SLEEP
+    dw_spi_register_resume(dws);
+#endif
        return spi_controller_resume(dws->master);
 }
 EXPORT_SYMBOL_GPL(dw_spi_resume_host);
index ed14c3d084c39545bbf26463cb2b598f4c02868f..cebdd16413781e28eabd99228f05001988c63c45 100644 (file)
@@ -27,9 +27,59 @@ struct dw_qspi_mmio {
        struct dw_spi  dws;
        struct clk     *clk;
        struct clk     *pclk;
+       struct clk     *sclk;
        void           *priv;
 };
 
+static int qspi_clk_prepare_enable(struct dw_qspi_mmio *dwsmmio)
+{
+   int ret;
+
+   ret = clk_prepare_enable(dwsmmio->pclk);
+   if (ret)
+       return ret;
+
+   ret = clk_prepare_enable(dwsmmio->sclk);
+   if (ret) {
+       clk_disable_unprepare(dwsmmio->pclk);
+       return ret;
+   }
+
+   return 0;
+}
+
+static void qspi_clk_disable_unprepare(struct dw_qspi_mmio *dwsmmio)
+{
+    clk_disable_unprepare(dwsmmio->pclk);
+    clk_disable_unprepare(dwsmmio->sclk);
+}
+
+static int __maybe_unused dw_qspi_mmio_suspend(struct device *dev)
+{
+   int ret;
+   struct dw_qspi_mmio *dwsmmio = dev_get_drvdata(dev);
+
+   ret = dw_qspi_suspend_host(&dwsmmio->dws);
+
+   qspi_clk_disable_unprepare(dwsmmio);
+
+   return ret;
+}
+
+static int __maybe_unused dw_qspi_mmio_resume(struct device *dev)
+{
+   struct dw_qspi_mmio *dwsmmio = dev_get_drvdata(dev);
+   int ret;
+
+   ret = qspi_clk_prepare_enable(dwsmmio);
+   if (ret) {
+       dev_err(dev, "failed to enable spi clock(%d)\n", ret);
+       return ret;
+   }
+
+   return dw_qspi_resume_host(&dwsmmio->dws);
+}
+
 static int dw_qspi_mmio_probe(struct platform_device *pdev)
 {
        int (*init_func)(struct platform_device *pdev,
@@ -74,6 +124,15 @@ static int dw_qspi_mmio_probe(struct platform_device *pdev)
        if (ret)
                goto out_clk;
 
+       dwsmmio->sclk = devm_clk_get_optional(&pdev->dev, "sclk");
+       if (IS_ERR(dwsmmio->sclk)) {
+               ret = PTR_ERR(dwsmmio->sclk);
+               goto out_clk;
+       }
+       ret = clk_prepare_enable(dwsmmio->sclk);
+       if (ret)
+               goto out_clk;
+
        /* set bus number */
        dws->bus_num = pdev->id;
 
@@ -113,6 +172,7 @@ static int dw_qspi_mmio_probe(struct platform_device *pdev)
 
 out:
        clk_disable_unprepare(dwsmmio->pclk);
+       clk_disable_unprepare(dwsmmio->sclk);
 out_clk:
        clk_disable_unprepare(dwsmmio->clk);
        return ret;
@@ -124,6 +184,7 @@ static int dw_qspi_mmio_remove(struct platform_device *pdev)
 
        dw_qspi_remove_host(&dwsmmio->dws);
        clk_disable_unprepare(dwsmmio->pclk);
+       clk_disable_unprepare(dwsmmio->sclk);
        clk_disable_unprepare(dwsmmio->clk);
 
        return 0;
@@ -135,12 +196,17 @@ static const struct of_device_id dw_qspi_mmio_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, dw_qspi_mmio_of_match);
 
+static const struct dev_pm_ops qspi_mmio_pm_ops = {
+    SET_SYSTEM_SLEEP_PM_OPS(dw_qspi_mmio_suspend, dw_qspi_mmio_resume)
+};
+
 static struct platform_driver dw_qspi_mmio_driver = {
        .probe          = dw_qspi_mmio_probe,
        .remove         = dw_qspi_mmio_remove,
        .driver         = {
                .name   = DRIVER_NAME,
                .of_match_table = dw_qspi_mmio_of_match,
+        .pm = &qspi_mmio_pm_ops,
        },
 };
 module_platform_driver(dw_qspi_mmio_driver);
index d0cc5bf4fa4e48d1c71b8735d9d0c2ffc0fc6d9c..2500d90ad3df129f98fb845a244573924cf4ff36 100644 (file)
@@ -29,6 +29,7 @@ struct dw_spi_mmio {
        struct dw_spi  dws;
        struct clk     *clk;
        struct clk     *pclk;
+       struct clk     *sclk;
        void           *priv;
        struct reset_control *rstc;
 };
@@ -222,6 +223,54 @@ static int dw_spi_keembay_init(struct platform_device *pdev,
        return 0;
 }
 
+static int spi_clk_prepare_enable(struct dw_spi_mmio *dwsmmio)
+{
+       int ret;
+
+       ret = clk_prepare_enable(dwsmmio->pclk);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(dwsmmio->sclk);
+       if (ret) {
+               clk_disable_unprepare(dwsmmio->pclk);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void spi_clk_disable_unprepare(struct dw_spi_mmio *dwsmmio)
+{
+       clk_disable_unprepare(dwsmmio->pclk);
+       clk_disable_unprepare(dwsmmio->sclk);
+}
+
+static int __maybe_unused dw_spi_mmio_suspend(struct device *dev)
+{
+    int ret;
+       struct dw_spi_mmio *dwsmmio = dev_get_drvdata(dev);
+
+       ret = dw_spi_suspend_host(&dwsmmio->dws);
+       spi_clk_disable_unprepare(dwsmmio);
+
+       return ret;
+}
+
+static int __maybe_unused dw_spi_mmio_resume(struct device *dev)
+{
+       struct dw_spi_mmio *dwsmmio = dev_get_drvdata(dev);
+       int ret;
+
+       ret = spi_clk_prepare_enable(dwsmmio);
+       if (ret) {
+               dev_err(dev, "failed to enable spi clock(%d)\n", ret);
+               return ret;
+       }
+
+       return dw_spi_resume_host(&dwsmmio->dws);
+}
+
 static int dw_spi_mmio_probe(struct platform_device *pdev)
 {
        int (*init_func)(struct platform_device *pdev,
@@ -267,6 +316,15 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
        if (ret)
                goto out_clk;
 
+       dwsmmio->sclk = devm_clk_get_optional(&pdev->dev, "sclk");
+       if (IS_ERR(dwsmmio->sclk)) {
+               ret = PTR_ERR(dwsmmio->sclk);
+               goto out_clk;
+       }
+       ret = clk_prepare_enable(dwsmmio->sclk);
+       if (ret)
+               goto out_clk;
+
        /* find an optional reset controller */
        dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi");
        if (IS_ERR(dwsmmio->rstc)) {
@@ -306,6 +364,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
 out:
        pm_runtime_disable(&pdev->dev);
        clk_disable_unprepare(dwsmmio->pclk);
+       clk_disable_unprepare(dwsmmio->sclk);
 out_clk:
        clk_disable_unprepare(dwsmmio->clk);
        reset_control_assert(dwsmmio->rstc);
@@ -320,6 +379,7 @@ static int dw_spi_mmio_remove(struct platform_device *pdev)
        dw_spi_remove_host(&dwsmmio->dws);
        pm_runtime_disable(&pdev->dev);
        clk_disable_unprepare(dwsmmio->pclk);
+       clk_disable_unprepare(dwsmmio->sclk);
        clk_disable_unprepare(dwsmmio->clk);
        reset_control_assert(dwsmmio->rstc);
 
@@ -347,6 +407,10 @@ static const struct acpi_device_id dw_spi_mmio_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match);
 #endif
 
+static const struct dev_pm_ops spi_mmio_pm_ops = {
+    SET_SYSTEM_SLEEP_PM_OPS(dw_spi_mmio_suspend, dw_spi_mmio_resume)
+};
+
 static struct platform_driver dw_spi_mmio_driver = {
        .probe          = dw_spi_mmio_probe,
        .remove         = dw_spi_mmio_remove,
@@ -354,6 +418,7 @@ static struct platform_driver dw_spi_mmio_driver = {
                .name   = DRIVER_NAME,
                .of_match_table = dw_spi_mmio_of_match,
                .acpi_match_table = ACPI_PTR(dw_spi_mmio_acpi_match),
+        .pm = &spi_mmio_pm_ops,
        },
 };
 module_platform_driver(dw_spi_mmio_driver);
index 803a5ea725c19027367db907a03d313acb06674e..a95f119b97d519add3830e6f8dbfc69a8c36fb0a 100644 (file)
@@ -138,6 +138,57 @@ static inline void dw_qspi_debugfs_remove(struct dw_spi *dws)
 }
 #endif /* CONFIG_DEBUG_FS */
 
+#ifdef CONFIG_PM_SLEEP
+static void dw_qspi_register_suspend(struct dw_spi *dws)
+{
+    struct dw_qspi_context *ctx = &dws->ctx;
+
+    ctx->ctrlr0         = dw_readl(dws, DW_SPI_CTRL0);
+    ctx->ctrlr1         = dw_readl(dws, DW_SPI_CTRL1);
+    ctx->ssienr         = dw_readl(dws, DW_SPI_SSIENR);
+    ctx->mwcr           = dw_readl(dws, DW_SPI_MWCR);
+    ctx->ser            = dw_readl(dws, DW_SPI_SER);
+    ctx->baudr          = dw_readl(dws, DW_SPI_BAUDR);
+    ctx->txftlr         = dw_readl(dws, DW_SPI_TXFLTR);
+    ctx->rxftlr         = dw_readl(dws, DW_SPI_RXFLTR);
+    ctx->txflr          = dw_readl(dws, DW_SPI_TXFLR);
+    ctx->rxflr          = dw_readl(dws, DW_SPI_RXFLR);
+    ctx->imr            = dw_readl(dws, DW_SPI_IMR);
+    ctx->dmacr          = dw_readl(dws, DW_SPI_DMACR);
+    ctx->dmatdlr        = dw_readl(dws, DW_SPI_DMATDLR);
+    ctx->dmardlr        = dw_readl(dws, DW_SPI_DMARDLR);
+    ctx->rx_sample_dly  = dw_readl(dws, DW_SPI_RX_SMP_DLY);
+    ctx->spi_ctrlr0     = dw_readl(dws, DW_SPI_SPI_CTRLR0);
+    ctx->txd_drv_edge   = dw_readl(dws, DW_SPI_TXD_DRV_EDGE);
+
+}
+
+static void dw_qspi_register_resume(struct dw_spi *dws)
+{
+    struct dw_qspi_context *ctx = &dws->ctx;
+
+    dw_writel(dws, DW_SPI_SSIENR, 0);
+    dw_writel(dws, DW_SPI_CTRL0, ctx->ctrlr0);
+    dw_writel(dws, DW_SPI_CTRL1, ctx->ctrlr1);
+    dw_writel(dws, DW_SPI_TXFLTR, ctx->txftlr);
+    dw_writel(dws, DW_SPI_RXFLTR, ctx->rxftlr);
+    dw_writel(dws, DW_SPI_TXFLR, ctx->txflr);
+    dw_writel(dws, DW_SPI_RXFLR, ctx->rxflr);
+    dw_writel(dws, DW_SPI_IMR, ctx->imr);
+    dw_writel(dws,DW_SPI_DMATDLR, ctx->dmatdlr);
+    dw_writel(dws, DW_SPI_DMARDLR, ctx->dmardlr);
+    dw_writel(dws, DW_SPI_RX_SMP_DLY, ctx->rx_sample_dly);
+    dw_writel(dws, DW_SPI_SPI_CTRLR0, ctx->spi_ctrlr0);
+    dw_writel(dws, DW_SPI_TXD_DRV_EDGE, ctx->txd_drv_edge);
+    dw_writel(dws, DW_SPI_SER, ctx->ser);
+    dw_writel(dws, DW_SPI_BAUDR, ctx->baudr);
+    dw_writel(dws, DW_SPI_MWCR, ctx->mwcr);
+    dw_writel(dws, DW_SPI_DMACR, ctx->dmacr);
+    dw_writel(dws, DW_SPI_SSIENR, ctx->ssienr);
+}
+#endif
+
+
 void dw_qspi_set_cs(struct spi_device *spi,struct dw_spi *dws, bool enable)
 {
        bool enable1 = !enable;
@@ -779,6 +830,10 @@ int dw_qspi_suspend_host(struct dw_spi *dws)
        if (ret)
                return ret;
 
+#ifdef CONFIG_PM_SLEEP
+    dw_qspi_register_suspend(dws);
+#endif
+
        spi_shutdown_chip(dws);
        return 0;
 }
@@ -787,6 +842,9 @@ EXPORT_SYMBOL_GPL(dw_qspi_suspend_host);
 int dw_qspi_resume_host(struct dw_spi *dws)
 {
        qspi_hw_init(&dws->master->dev, dws);
+#ifdef CONFIG_PM_SLEEP
+    dw_qspi_register_resume(dws);
+#endif
        return spi_controller_resume(dws->master);
 }
 EXPORT_SYMBOL_GPL(dw_qspi_resume_host);
index cfc2b0dd33756d814726f987a952cb8ca53112f3..17254c1ffcf93370a7b6badd6802e8674001f5b2 100644 (file)
@@ -148,6 +148,28 @@ enum dw_ssi_type {
        SSI_NS_MICROWIRE,
 };
 
+#ifdef CONFIG_PM_SLEEP
+struct dw_qspi_context {
+    u32 ctrlr0;
+    u32 ctrlr1;
+    u32 ssienr;
+    u32 mwcr;
+    u32 ser;
+    u32 baudr;
+    u32 txftlr;
+    u32 rxftlr;
+    u32 txflr;
+    u32 rxflr;
+    u32 imr;
+    u32 dmacr;
+    u32 dmatdlr;
+    u32 dmardlr;
+    u32 rx_sample_dly;
+    u32 spi_ctrlr0;
+    u32 txd_drv_edge;
+};
+#endif
+
 struct dw_spi;
 struct dw_spi_dma_ops {
        int (*dma_init)(struct dw_spi *dws);
@@ -212,6 +234,10 @@ struct dw_spi {
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs;
 #endif
+
+#ifdef CONFIG_PM_SLEEP
+    struct dw_qspi_context ctx;
+#endif
 };
 
 static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
index faf40cb66498dd11fb149f66c03412b7f942d610..ec50f8af19ba141681a3a8b5c3ab25e35ab5b9f9 100644 (file)
@@ -130,6 +130,26 @@ struct dw_spi_cfg {
        u32 freq;
 };
 
+#ifdef CONFIG_PM_SLEEP
+struct dw_spi_context {
+    u32 ctrlr0;
+    u32 ctrlr1;
+    u32 ssienr;
+    u32 mwcr;
+    u32 ser;
+    u32 baudr;
+    u32 txftlr;
+    u32 rxftlr;
+    u32 txflr;
+    u32 rxflr;
+    u32 imr;
+    u32 dmacr;
+    u32 dmatdlr;
+    u32 dmardlr;
+    u32 rx_sample_dly;
+};
+#endif
+
 struct dw_spi;
 struct dw_spi_dma_ops {
        int (*dma_init)(struct device *dev, struct dw_spi *dws);
@@ -189,6 +209,10 @@ struct dw_spi {
        struct dentry *debugfs;
        struct debugfs_regset32 regset;
 #endif
+
+#ifdef CONFIG_PM_SLEEP
+    struct dw_spi_context ctx;
+#endif
 };
 
 static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
index 035671e293559e3102a26346db607d7377cb18d4..0959b9f57cb6faa90ea7fdae350ee91f93f4cc56 100644 (file)
@@ -28,7 +28,7 @@
 #define USB_CLK_GATE_STS               0x0
 #define USB_LOGIC_ANALYZER_TRACE_STS0  0x4
 #define USB_LOGIC_ANALYZER_TRACE_STS1  0x8
-#define USB_GPIO                       0xc
+#define USB_GPIO                               0xc
 #define USB_DEBUG_STS0                 0x10
 #define USB_DEBUG_STS1                 0x14
 #define USB_DEBUG_STS2                 0x18
 #define USBPHY_TEST_CTRL1              0x28
 #define USBPHY_TEST_CTRL2              0x2c
 #define USBPHY_TEST_CTRL3              0x30
-#define USB_SSP_EN                     0x34
+#define USB_SSP_EN                             0x34
 #define USB_HADDR_SEL                  0x38
-#define USB_SYS                        0x3c
-#define USB_HOST_STATUS                0x40
+#define USB_SYS                                        0x3c
+#define USB_HOST_STATUS                        0x40
 #define USB_HOST_CTRL                  0x44
 #define USBPHY_HOST_CTRL               0x48
 #define USBPHY_HOST_STATUS             0x4c
 /* USB_SYS */
 #define TEST_POWERDOWN_SSP     BIT(2)
 #define TEST_POWERDOWN_HSP     BIT(1)
-#define COMMONONN              BIT(0)
+#define COMMONONN                      BIT(0)
 
 /* USB_SSP_EN */
-#define REF_SSP_EN             BIT(0)
+#define REF_SSP_EN                     BIT(0)
 
 /* USBPHY_HOST_CTRL */
 #define HOST_U2_PORT_DISABLE   BIT(6)
@@ -83,170 +83,64 @@ MODULE_PARM_DESC(usb_role, "USB role");
 
 struct dwc3_thead {
        struct device           *dev;
-       struct platform_device  *dwc3;
+       struct clk_bulk_data    *clks;
+       int                     num_clocks;
 
-       struct regmap           *usb0_apb;
+       struct regmap           *usb3_drd;
        struct regmap           *misc_sysreg;
 
-       struct extcon_dev       *edev;
-       struct extcon_dev       *host_edev;
-       struct notifier_block   vbus_nb;
-       struct notifier_block   host_nb;
-
        struct gpio_desc        *hubswitch;
        struct regulator        *hub1v2;
        struct regulator        *hub5v;
        struct regulator        *vbus;
-
-       enum usb_dr_mode        mode;
-       bool                    is_suspended;
-       bool                    pm_suspended;
 };
 
-static int dwc3_thead_vbus_notifier(struct notifier_block *nb,
-                                  unsigned long event, void *ptr)
-{
-       struct dwc3_thead *thead = container_of(nb, struct dwc3_thead, vbus_nb);
-
-       /* enable vbus override for device mode */
-       thead->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST;
-
-       return NOTIFY_DONE;
-}
-
-static int dwc3_thead_host_notifier(struct notifier_block *nb,
-                                  unsigned long event, void *ptr)
-{
-       struct dwc3_thead *thead = container_of(nb, struct dwc3_thead, host_nb);
-
-       /* disable vbus override in host mode */
-       thead->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL;
-
-       return NOTIFY_DONE;
-}
-
-static int dwc3_thead_register_extcon(struct dwc3_thead *thead)
-{
-       struct device           *dev = thead->dev;
-       struct extcon_dev       *host_edev;
-       int                     ret;
-
-       if (!of_property_read_bool(dev->of_node, "extcon"))
-               return 0;
-
-       thead->edev = extcon_get_edev_by_phandle(dev, 0);
-       if (IS_ERR(thead->edev))
-               return PTR_ERR(thead->edev);
-
-       thead->vbus_nb.notifier_call = dwc3_thead_vbus_notifier;
-
-       thead->host_edev = extcon_get_edev_by_phandle(dev, 1);
-       if (IS_ERR(thead->host_edev))
-               thead->host_edev = NULL;
-
-       ret = devm_extcon_register_notifier(dev, thead->edev, EXTCON_USB,
-                                           &thead->vbus_nb);
-       if (ret < 0) {
-               dev_err(dev, "VBUS notifier register failed\n");
-               return ret;
-       }
-
-       if (thead->host_edev)
-               host_edev = thead->host_edev;
-       else
-               host_edev = thead->edev;
-
-       thead->host_nb.notifier_call = dwc3_thead_host_notifier;
-       ret = devm_extcon_register_notifier(dev, host_edev, EXTCON_USB_HOST,
-                                           &thead->host_nb);
-       if (ret < 0) {
-               dev_err(dev, "Host notifier register failed\n");
-               return ret;
-       }
-
-       /* Update initial VBUS override based on extcon state */
-       if (extcon_get_state(thead->edev, EXTCON_USB) ||
-           !extcon_get_state(host_edev, EXTCON_USB_HOST))
-               dwc3_thead_vbus_notifier(&thead->vbus_nb, true, thead->edev);
-       else
-               dwc3_thead_vbus_notifier(&thead->vbus_nb, false, thead->edev);
-
-       return 0;
-}
-
-static int dwc3_thead_suspend(struct dwc3_thead *thead)
+static void dwc3_thead_deassert(struct dwc3_thead *thead)
 {
-       return 0;
-}
-
-static int dwc3_thead_resume(struct dwc3_thead *thead)
-{
-       return 0;
-}
-
-static int dwc3_thead_of_register_core(struct platform_device *pdev)
-{
-       struct dwc3_thead       *thead = platform_get_drvdata(pdev);
-       struct device_node      *np = pdev->dev.of_node, *dwc3_np;
-       struct device           *dev = &pdev->dev;
-       int                     ret;
-
-       dwc3_np = of_get_child_by_name(np, "dwc3");
-       if (!dwc3_np) {
-               dev_err(dev, "failed to find dwc3 core child\n");
-               return -ENODEV;
-       }
-
-       ret = of_platform_populate(np, NULL, NULL, dev);
-       if (ret) {
-               dev_err(dev, "failed to register dwc3 core - %d\n", ret);
-               goto node_put;
-       }
-
-       thead->dwc3 = of_find_device_by_node(dwc3_np);
-       if (!thead->dwc3) {
-               ret = -ENODEV;
-               dev_err(dev, "failed to get dwc3 platform device\n");
-       }
-
-node_put:
-       of_node_put(dwc3_np);
-
-       return ret;
-}
-
-static void dwc3_thead_savepower_u3phy(struct dwc3_thead *thead)
-{
-       struct device *dev = thead->dev;
-       int reg;
-
-       /* config usb top within USB ctrl & PHY reset */
+       /* 1. reset assert */
        regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
                                USB3_DRD_MASK, USB3_DRD_PRST);
 
-       /*
-        * dwc reg also need to be configed to save power
-        * 1 set USB_SYS[COMMONONN] while GCTL[SOFITPSYNC]=1
-        *   notice GCTL[SOFITPSYNC] should be mutex with GFLADJ[LPM_SEL].
-        * 2 enable GUSB3PIPECLT[SUSPENDEN]
+       /* 
+        *      2. Common Block Power-Down Control.
+        *      Controls the power-down signals in the PLL block
+        *      when the USB 3.0 femtoPHY is in Suspend or Sleep mode.
         */
-       regmap_update_bits(thead->usb0_apb, USB_SYS,
+       regmap_update_bits(thead->usb3_drd, USB_SYS,
                                COMMONONN, COMMONONN);
-       regmap_update_bits(thead->usb0_apb, USB_SSP_EN,
+
+       /*
+        *      3. Reference Clock Enable for SS function.
+        *      Enables the reference clock to the prescaler. 
+        *      The ref_ssp_en signal must remain de-asserted until
+        *      the reference clock is running at the appropriate frequency,
+        *      at which point ref_ssp_en can be asserted.
+        *      For lower power states, ref_ssp_en can also be de-asserted.
+        */
+       regmap_update_bits(thead->usb3_drd, USB_SSP_EN,
                                REF_SSP_EN, REF_SSP_EN);
-       regmap_write(thead->usb0_apb, USB_HOST_CTRL, 0x1101);
 
+       /* 4. set host ctrl */
+       regmap_write(thead->usb3_drd, USB_HOST_CTRL, 0x1101);
+
+       /* 5. reset deassert */
        regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
                                USB3_DRD_MASK, USB3_DRD_MASK);
 
-       regmap_read(thead->usb0_apb, USB_SYS, &reg);
-       dev_dbg(dev, "usb_sys:0x%x\n", reg);
+       /* 6. wait deassert complete */
+       udelay(10);
+}
+
+static void dwc3_thead_assert(struct dwc3_thead *thead)
+{
+       /* close ssp */
+       regmap_update_bits(thead->usb3_drd, USB_SSP_EN,
+                               REF_SSP_EN, 0);
 
-       regmap_read(thead->usb0_apb, USB_HOST_CTRL, &reg);
-       dev_dbg(dev, "host_ctrl:0x%x\n", reg);
+       /* reset assert usb */
+       regmap_update_bits(thead->misc_sysreg, USB3_DRD_SWRST,
+                               USB3_DRD_MASK, 0);
 
-       regmap_read(thead->misc_sysreg, USB3_DRD_SWRST, &reg);
-       dev_dbg(dev, "drd_swrst:0x%x\n", reg);
 }
 
 static int dwc3_thead_probe(struct platform_device *pdev)
@@ -271,111 +165,130 @@ static int dwc3_thead_probe(struct platform_device *pdev)
        thead->vbus = devm_regulator_get(dev, "vbus");
        if (IS_ERR(thead->vbus))
                dev_dbg(dev, "no need to get vbus\n");
-       else
-               regulator_enable(thead->vbus);
+       else {
+               ret = regulator_enable(thead->vbus);
+
+               if (ret) {
+                       dev_err(dev, "failed to enable regulator vbus %d\n", ret);
+               }
+       }
 
        thead->hub1v2 = devm_regulator_get(dev, "hub1v2");
        if (IS_ERR(thead->hub1v2))
                dev_dbg(dev, "no need to set hub1v2\n");
-       else
-               regulator_enable(thead->hub1v2);
+       else {
+               ret = regulator_enable(thead->hub1v2);
+
+               if (ret) {
+                       dev_err(dev, "failed to enable regulator hub1v2 %d\n", ret);
+               }
+       }
 
        thead->hub5v = devm_regulator_get(dev, "hub5v");
        if (IS_ERR(thead->hub5v))
                dev_dbg(dev, "no need to set hub5v\n");
-       else
-               regulator_enable(thead->hub5v);
+       else {
+               ret = regulator_enable(thead->hub5v);
+
+               if (ret) {
+                       dev_err(dev, "failed to enable regulator hub1v2 %d\n", ret);
+               }
+       }
 
        thead->misc_sysreg = syscon_regmap_lookup_by_phandle(np, "usb3-misc-regmap");
        if (IS_ERR(thead->misc_sysreg))
                return PTR_ERR(thead->misc_sysreg);
 
-       thead->usb0_apb = syscon_regmap_lookup_by_phandle(np, "usb3-drd-regmap");
-       if (IS_ERR(thead->usb0_apb))
-               return PTR_ERR(thead->usb0_apb);
+       thead->usb3_drd = syscon_regmap_lookup_by_phandle(np, "usb3-drd-regmap");
+       if (IS_ERR(thead->usb3_drd))
+               return PTR_ERR(thead->usb3_drd);
 
-       dwc3_thead_savepower_u3phy(thead);
+       ret = clk_bulk_get_all(thead->dev, &thead->clks);
+       if (ret < 0)
+               goto err;
 
-       ret = dwc3_thead_of_register_core(pdev);
-       if (ret) {
-               dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret);
-               goto depopulate;
-       }
+       thead->num_clocks = ret;
 
-       /* fixme: need to verify because hw can't support detect event */
-       ret = dwc3_thead_register_extcon(thead);
+       ret = clk_bulk_prepare_enable(thead->num_clocks, thead->clks);
        if (ret)
                goto err;
 
+       dwc3_thead_deassert(thead);
+
+       ret = of_platform_populate(np, NULL, NULL, dev);
+       if (ret) {
+               dev_err(dev, "failed to register dwc3 core - %d\n", ret);
+               goto err_clk_put;
+       }
 
-       device_init_wakeup(&pdev->dev, 1);
-       thead->is_suspended = false;
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
-       pm_runtime_forbid(dev);
+       pm_runtime_get_sync(dev);
+
        dev_info(dev, "light dwc3 probe ok!\n");
 
        return 0;
 
-depopulate:
-       of_platform_depopulate(&pdev->dev);
+err_clk_put:
+       clk_bulk_disable_unprepare(thead->num_clocks, thead->clks);
+       clk_bulk_put_all(thead->num_clocks, thead->clks);
 err:
        return ret;
 }
 
 static int dwc3_thead_remove(struct platform_device *pdev)
 {
-       struct device *dev = &pdev->dev;
+       struct dwc3_thead       *thead = platform_get_drvdata(pdev);
 
-       pm_runtime_allow(dev);
-       pm_runtime_disable(dev);
+       dwc3_thead_assert(thead);
 
-       return 0;
-}
+       of_platform_depopulate(thead->dev);
 
-static int __maybe_unused dwc3_thead_pm_suspend(struct device *dev)
-{
-       struct dwc3_thead *thead = dev_get_drvdata(dev);
-       int ret = 0;
+       clk_bulk_disable_unprepare(thead->num_clocks, thead->clks);
+       clk_bulk_put_all(thead->num_clocks, thead->clks);
 
-       ret = dwc3_thead_suspend(thead);
-       if (!ret)
-               thead->pm_suspended = true;
+       pm_runtime_disable(thead->dev);
+       pm_runtime_set_suspended(thead->dev);
 
-       return ret;
+       return 0;
 }
 
-static int __maybe_unused dwc3_thead_pm_resume(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_thead_pm_suspend(struct device *dev)
 {
        struct dwc3_thead *thead = dev_get_drvdata(dev);
-       int ret;
 
-       ret = dwc3_thead_resume(thead);
-       if (!ret)
-               thead->pm_suspended = false;
+       dwc3_thead_assert(thead);
 
-       return ret;
+       clk_bulk_disable(thead->num_clocks, thead->clks);
+
+       return 0;
 }
 
-static int __maybe_unused dwc3_thead_runtime_suspend(struct device *dev)
+
+static int dwc3_thead_pm_resume(struct device *dev)
 {
        struct dwc3_thead *thead = dev_get_drvdata(dev);
+       int ret;
 
-       return dwc3_thead_suspend(thead);
-}
+       ret = clk_bulk_prepare_enable(thead->num_clocks, thead->clks);
+       if (ret) {
+               dev_err(dev, "failed to enable clk ret=%d\n", ret);
+               return ret;
+       }
 
-static int __maybe_unused dwc3_thead_runtime_resume(struct device *dev)
-{
-       struct dwc3_thead *thead = dev_get_drvdata(dev);
+       dwc3_thead_deassert(thead);
 
-       return dwc3_thead_resume(thead);
+       return ret;
 }
 
 static const struct dev_pm_ops dwc3_thead_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(dwc3_thead_pm_suspend, dwc3_thead_pm_resume)
-       SET_RUNTIME_PM_OPS(dwc3_thead_runtime_suspend, dwc3_thead_runtime_resume,
-                          NULL)
 };
+#define DEV_PM_OPS     (&dwc3_thead_dev_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif /* CONFIG_PM_SLEEP */
 
 static const struct of_device_id dwc3_thead_of_match[] = {
        { .compatible = "thead,dwc3" },
@@ -388,10 +301,7 @@ static struct platform_driver dwc3_thead_driver = {
        .remove         = dwc3_thead_remove,
        .driver         = {
                .name   = "dwc3-thead",
-       /*
-        * fixme: need to verify because hw can't support plug event
-        */
-//             .pm     = &dwc3_thead_dev_pm_ops,
+               .pm     = DEV_PM_OPS,
                .of_match_table = dwc3_thead_of_match,
        },
 };
index f4901ad2bc0d0638591f693f509dd1e08fdd397f..1bfc2580c465cd7d9fbbbe1e8c2827f6ba84bbf7 100644 (file)
@@ -102,8 +102,7 @@ static int carveout_buf_setup(void)
        node = of_find_node_by_name(NULL, "vdmabuf_reserved_memory");
        if (!node) {
                ret = -EINVAL;
-               dev_err(drv_info->dev,
-                       "failed to find vdmabuf_reserved_memory node\n");
+               dev_info(drv_info->dev, "no vdmabuf_reserved_memory node\n");
        }
 
        for (i = 0; i <= VIRTIO_VDMABUF_CARVEOUTS_NUM; i++) {
@@ -1745,9 +1744,7 @@ static int __init vhost_vdmabuf_init(void)
 
        ret = carveout_buf_setup();
        if (ret < 0)
-               dev_warn(drv_info->dev,
-                        "vhost-vdmabuf: carveout bufs setup failed %d\n",
-                        ret);
+               dev_info(drv_info->dev, "vhost-vdmabuf: carveout buf not setup %d\n", ret);
 
        mutex_init(&drv_info->g_mutex);
 
index 503f087943a94ec25f2c8b1d2602cfd0929d464d..71eff21badc7e9955fb2086c8debdcc9b6469a86 100644 (file)
@@ -56,8 +56,7 @@ static int carveout_buf_setup(void)
        node = of_find_node_by_name(NULL, "vdmabuf_reserved_memory");
        if (!node) {
                ret = -EINVAL;
-               dev_err(drv_info->dev,
-                       "failed to find vdmabuf_reserved_memory node\n");
+               pr_info("no vdmabuf_reserved_memory node\n");
        }
 
        for (i = 0; i <= VIRTIO_VDMABUF_CARVEOUTS_NUM; i++) {
@@ -1201,6 +1200,7 @@ static int virtio_vdmabuf_create_dmabuf(struct virtio_vdmabuf *vdmabuf,
        struct dma_buf *dmabuf;
        unsigned long irqflags;
        struct page *page = NULL;
+       unsigned int gfp = GFP_KERNEL;
        int ret, i = 0, npages, bp_num;
 
        /* For carveout, buf size is fixed, user don't need specify it */
@@ -1226,7 +1226,6 @@ static int virtio_vdmabuf_create_dmabuf(struct virtio_vdmabuf *vdmabuf,
        exp_info.flags = O_RDWR;
        exp_info.priv = exp_buf;
 
-       unsigned int gfp = GFP_KERNEL;
        if(attr->flags & VIRTIO_VDAMBUF_DMA32)
        {
                gfp |= __GFP_DMA32;
@@ -1652,8 +1651,7 @@ static int __init virtio_vdmabuf_init(void)
 
        ret = carveout_buf_setup();
        if (ret < 0)
-               pr_warn("virtio-vdmabuf: carveout bufs setup failed %d\n",
-                        ret);
+               pr_info("virtio-vdmabuf: carveout buf not setup %d\n", ret);
 
        mutex_init(&drv_info->g_mutex);
 
index 6473e12623c65447709b277ed7cbbecd83b79f0f..5e6cc3c6b61155b9c7211be2774492e48f86640e 100644 (file)
@@ -5,7 +5,7 @@
 
 #ifndef _LIGHT_DSPSYS_H
 #define _LIGHT_DSPSYS_H
-
+// gate
 #define CLKGEN_DSP0_PCLK                       0
 #define CLKGEN_DSP0_CCLK                        1
 #define CLKGEN_DSP1_PCLK                        2
 #define CLKGEN_AXI4_DSPSYS_PCLK                 11
 #define CLKGEN_X2X_DSP0_ACLK_S                 12
 #define CLKGEN_X2X_DSP2_ACLK_S                 13
-#define LIGHT_CLKGEN_DSPSYS_CLK_END            14
+// MUX
+#define DSPSYS_DSP0_CLK_SWITCH          14
+#define DSPSYS_DSP1_CLK_SWITCH          15
+// DIV
+#define DSPSYS_DSP_CLK              16
+#define DSPSYS_DSP0_CLK_CDE         17
+#define DSPSYS_DSP1_CLK_CDE         18
+
+#define LIGHT_CLKGEN_DSPSYS_CLK_END            19
 
 #endif
diff --git a/include/dt-bindings/clock/light-miscsys.h b/include/dt-bindings/clock/light-miscsys.h
new file mode 100644 (file)
index 0000000..8b00cb3
--- /dev/null
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Alibaba Group Holding Limited.
+ */
+#ifndef _LIGHT_MISCSYS_H
+#define _LIGHT_MISCSYS_H
+#define CLKGEN_MISCSYS_MISCSYS_ACLK                    0
+#define CLKGEN_MISCSYS_USB3_DRD_CLK                    1
+#define CLKGEN_MISCSYS_USB3_DRD_CTRL_REF_CLK           2
+#define CLKGEN_MISCSYS_USB3_DRD_PHY_REF_CLK            3
+#define CLKGEN_MISCSYS_USB3_DRD_SUSPEND_CLK            4
+#define CLKGEN_MISCSYS_EMMC_CLK                                5
+#define CLKGEN_MISCSYS_SDIO0_CLK                       6
+#define CLKGEN_MISCSYS_SDIO1_CLK                       7
+#define CLKGEN_MISCSYS_AHB2_TEESYS_HCLK                        8
+#define CLKGEN_MISCSYS_APB3_TEESYS_HCLK                        9
+#define CLKGEN_MISCSYS_AXI4_TEESYS_ACLK                        10
+#define CLKGEN_MISCSYS_EIP120SI_CLK                    11
+#define CLKGEN_MISCSYS_EIP120SII_CLK                   12
+#define CLKGEN_MISCSYS_EIP120SIII_CLK                  13
+#define CLKGEN_MISCSYS_TEEDMAC_CLK                     14
+#define CLKGEN_MISCSYS_EIP150B_HCLK                    15
+#define CLKGEN_MISCSYS_OCRAM_HCLK                      16
+#define CLKGEN_MISCSYS_EFUSE_PCLK                      17
+#define CLKGEN_MISCSYS_TEE_SYSREG_PCLK                 18
+#define CLKGEN_MISCSYS_CLK_END                         19
+#endif
+
index 188aaa6cc435010c5a5bd319afedcdc858db676d..7ab41376e080ee93007207825fbf9cb99f76b33f 100644 (file)
@@ -18,7 +18,8 @@
 #define LIGHT_VPSYS_VENC_CCLK          9
 #define LIGHT_VPSYS_VENC_PCLK          10
 #define LIGHT_VPSYS_VENC_ACLK          11
-#define LIGHT_VPSYS_CLK_END            12
+#define LIGHT_VPSYS_G2D_CCLK_DIV    12
+#define LIGHT_VPSYS_CLK_END            13
 
 #endif
 
index 53739acb9162e1b72d9876d69d02a3f512680673..8b964fcf350ad0def56fa0d7a231de0f49ce4b86 100644 (file)
@@ -35,5 +35,7 @@
 #define IOPMP_TEE_DMAC 25
 #define IOPMP_DSP0     26
 #define IOPMP_DSP1     27
+#define IOPMP_AUDIO0   28
+#define IOPMP_AUDIO1   29
 
 #endif /* __DT_THEAD_LIGHT_IOPMP_H__ */
index dd23f32017393531d08b196d5bc478a40ca09eea..231e943262a8ee6452d62bc48d30e9597749466e 100644 (file)
@@ -37,6 +37,9 @@ enum light_aon_misc_func {
        LIGHT_AON_MISC_FUNC_AON_WDT_ON  = 10,
        LIGHT_AON_MISC_FUNC_AON_WDT_OFF = 11,
        LIGHT_AON_MISC_FUNC_AON_RESERVE_MEM = 12,
+       LIGHT_AON_MISC_FUNC_REQUIRE_STR = 13,
+       LIGHT_AON_MISC_FUNC_REQUIRE_STD = 14,
+       LIGHT_AON_MISC_FUNC_CPUHP = 15,
 };
 
 enum light_aon_pm_func {
index fbed5dd273db5c1e116eb81722e0a8705723e49e..41330024a9f87acc443369d4b5a5085f7f0274ac 100644 (file)
@@ -41,21 +41,11 @@ struct vm_area_struct;
 #define ___GFP_ACCOUNT         0x400000u
 #define ___GFP_ZEROTAGS                0x800000u
 #define ___GFP_SKIP_KASAN_POISON       0x1000000u
-#ifdef CONFIG_CMA
-#define ___GFP_CMA             0x2000000u
-#else
-#define ___GFP_CMA             0
-#endif
 #ifdef CONFIG_LOCKDEP
-#ifdef CONFIG_CMA
-#define ___GFP_NOLOCKDEP       0x4000000u
-#else
 #define ___GFP_NOLOCKDEP       0x2000000u
-#endif
 #else
 #define ___GFP_NOLOCKDEP       0
 #endif
-
 /* If the above are modified, __GFP_BITS_SHIFT may need updating */
 
 /*
@@ -69,7 +59,6 @@ struct vm_area_struct;
 #define __GFP_HIGHMEM  ((__force gfp_t)___GFP_HIGHMEM)
 #define __GFP_DMA32    ((__force gfp_t)___GFP_DMA32)
 #define __GFP_MOVABLE  ((__force gfp_t)___GFP_MOVABLE)  /* ZONE_MOVABLE allowed */
-#define __GFP_CMA      ((__force gfp_t)___GFP_CMA)
 #define GFP_ZONEMASK   (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)
 
 /**
@@ -246,11 +235,7 @@ struct vm_area_struct;
 #define __GFP_NOLOCKDEP ((__force gfp_t)___GFP_NOLOCKDEP)
 
 /* Room for N __GFP_FOO bits */
-#ifdef CONFIG_CMA
-#define __GFP_BITS_SHIFT (26 + IS_ENABLED(CONFIG_LOCKDEP))
-#else
 #define __GFP_BITS_SHIFT (25 + IS_ENABLED(CONFIG_LOCKDEP))
-#endif
 #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
 
 /**
index 220e92c80095db710db350bc0759ffa653c917bb..86eace6e7bb813c80bb2476b5a092bf8a9e1abea 100644 (file)
@@ -249,7 +249,7 @@ static inline struct page *
 alloc_zeroed_user_highpage_movable(struct vm_area_struct *vma,
                                   unsigned long vaddr)
 {
-       struct page *page = alloc_page_vma(GFP_HIGHUSER_MOVABLE | __GFP_CMA, vma, vaddr);
+       struct page *page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
 
        if (page)
                clear_user_highpage(page, vaddr);
index 9c70c50b9a1cce1f45c6bf04dcff2c707e4b3844..8930845604a03c641269ccbc3ce2d8c0fef7f2e0 100644 (file)
@@ -73,6 +73,10 @@ struct light_rpmsg_vproc {
         u32 out_idx;
         u32 core_id;
         spinlock_t mu_lock;
+#ifdef CONFIG_PM_SLEEP
+        struct semaphore pm_sem;
+        int sleep_flag;
+#endif
 };
 
 struct light_rpmsg_head {
index 028274000c0b095bf719b7db82f8bb956a1dab7d..e919d7afcaacedf5f318ed04f21a952250dc2433 100644 (file)
@@ -45,6 +45,8 @@ enum migratetype {
        MIGRATE_UNMOVABLE,
        MIGRATE_MOVABLE,
        MIGRATE_RECLAIMABLE,
+       MIGRATE_PCPTYPES,       /* the number of types on the pcp lists */
+       MIGRATE_HIGHATOMIC = MIGRATE_PCPTYPES,
 #ifdef CONFIG_CMA
        /*
         * MIGRATE_CMA migration type is designed to mimic the way
@@ -61,8 +63,6 @@ enum migratetype {
         */
        MIGRATE_CMA,
 #endif
-       MIGRATE_PCPTYPES, /* the number of types on the pcp lists */
-       MIGRATE_HIGHATOMIC = MIGRATE_PCPTYPES,
 #ifdef CONFIG_MEMORY_ISOLATION
        MIGRATE_ISOLATE,        /* can't allocate from here */
 #endif
@@ -75,11 +75,9 @@ extern const char * const migratetype_names[MIGRATE_TYPES];
 #ifdef CONFIG_CMA
 #  define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)
 #  define is_migrate_cma_page(_page) (get_pageblock_migratetype(_page) == MIGRATE_CMA)
-#  define get_cma_migrate_type() MIGRATE_CMA
 #else
 #  define is_migrate_cma(migratetype) false
 #  define is_migrate_cma_page(_page) false
-#  define get_cma_migrate_type() MIGRATE_MOVABLE
 #endif
 
 static inline bool is_migrate_movable(int mt)
index a7638d7487c43e852f08b8708471d173f5f3d0c2..44ca2b65b42744ae1125c84e179267b36dd838b7 100644 (file)
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -47,6 +47,7 @@ extern void lru_cache_enable(void);
 
 struct cma cma_areas[MAX_CMA_AREAS];
 unsigned cma_area_count;
+static DEFINE_MUTEX(cma_mutex);
 
 phys_addr_t cma_get_base(const struct cma *cma)
 {
@@ -511,7 +512,9 @@ struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align,
                mutex_unlock(&cma->lock);
 
                pfn = cma->base_pfn + (bitmap_no << cma->order_per_bit);
+               mutex_lock(&cma_mutex);
                ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA, gfp_mask, &info);
+               mutex_unlock(&cma_mutex);
                cma_info.nr_migrated += info.nr_migrated;
                cma_info.nr_reclaimed += info.nr_reclaimed;
                cma_info.nr_mapped += info.nr_mapped;
index 9e97de1c52d83edc82fee538db9017cc17ad4617..34b5b049997c498db3ae4058ee666901f11cd01f 100644 (file)
@@ -310,10 +310,10 @@ const char * const migratetype_names[MIGRATE_TYPES] = {
        "Unmovable",
        "Movable",
        "Reclaimable",
+       "HighAtomic",
 #ifdef CONFIG_CMA
        "CMA",
 #endif
-       "HighAtomic",
 #ifdef CONFIG_MEMORY_ISOLATION
        "Isolate",
 #endif
@@ -2452,17 +2452,6 @@ static int fallbacks[MIGRATE_TYPES][3] = {
 #endif
 };
 
-#ifdef CONFIG_CMA
-static __always_inline struct page *__rmqueue_cma_fallback(struct zone *zone,
-                                       unsigned int order)
-{
-       return __rmqueue_smallest(zone, order, MIGRATE_CMA);
-}
-#else
-static inline struct page *__rmqueue_cma_fallback(struct zone *zone,
-                                       unsigned int order) { return NULL; }
-#endif
-
 /*
  * Move the free pages in a range to the freelist tail of the requested type.
  * Note that start_page and end_pages are not aligned on a pageblock
@@ -2954,33 +2943,16 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype,
 
 retry:
        page = __rmqueue_smallest(zone, order, migratetype);
-
-       if (unlikely(!page) && __rmqueue_fallback(zone, order, migratetype,
-                                                 alloc_flags))
-               goto retry;
+       if (unlikely(!page)) {
+               if (!page && __rmqueue_fallback(zone, order, migratetype,
+                                                               alloc_flags))
+                       goto retry;
+       }
 
        trace_mm_page_alloc_zone_locked(page, order, migratetype);
        return page;
 }
 
-#ifdef CONFIG_CMA
-static struct page *__rmqueue_cma(struct zone *zone, unsigned int order,
-                                 int migratetype,
-                                 unsigned int alloc_flags)
-{
-       struct page *page = __rmqueue_cma_fallback(zone, order);
-       trace_mm_page_alloc_zone_locked(page, order, MIGRATE_CMA);
-       return page;
-}
-#else
-static inline struct page *__rmqueue_cma(struct zone *zone, unsigned int order,
-                                        int migratetype,
-                                        unsigned int alloc_flags)
-{
-       return NULL;
-}
-#endif
-
 /*
  * Obtain a specified number of elements from the buddy allocator, all under
  * a single hold of the lock, for efficiency.  Add them to the supplied list.
@@ -2994,14 +2966,8 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
 
        spin_lock(&zone->lock);
        for (i = 0; i < count; ++i) {
-               struct page *page;
-
-               if (is_migrate_cma(migratetype))
-                       page = __rmqueue_cma(zone, order, migratetype,
-                                            alloc_flags);
-               else
-                       page = __rmqueue(zone, order, migratetype, alloc_flags);
-
+               struct page *page = __rmqueue(zone, order, migratetype,
+                                                               alloc_flags);
                if (unlikely(page == NULL))
                        break;
 
@@ -3036,28 +3002,6 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
        return alloced;
 }
 
-/*
- * Return the pcp list that corresponds to the migrate type if that list isn't
- * empty.
- * If the list is empty return NULL.
- */
-static struct list_head *get_populated_pcp_list(struct zone *zone,
-                       unsigned int order, struct per_cpu_pages *pcp,
-                       int migratetype, unsigned int alloc_flags)
-{
-       struct list_head *list = &pcp->lists[migratetype];
-
-       if (list_empty(list)) {
-               pcp->count += rmqueue_bulk(zone, order,
-                               pcp->batch, list,
-                               migratetype, alloc_flags);
-
-               if (list_empty(list))
-                       list = NULL;
-       }
-       return list;
-}
-
 #ifdef CONFIG_NUMA
 /*
  * Called from the vmstat counter updater to drain pagesets of this
@@ -3320,6 +3264,7 @@ static void free_unref_page_commit(struct page *page, unsigned long pfn)
                trace_android_vh_pcplist_add_cma_pages_bypass(migratetype,
                        &pcp_skip_cma_pages);
                if (unlikely(is_migrate_isolate(migratetype)) ||
+                               is_migrate_cma(migratetype) ||
                                pcp_skip_cma_pages) {
                        free_one_page(zone, page, pfn, 0, migratetype,
                                      FPI_NONE);
@@ -3513,28 +3458,16 @@ static inline void zone_statistics(struct zone *preferred_zone, struct zone *z)
 static struct page *__rmqueue_pcplist(struct zone *zone, int migratetype,
                        unsigned int alloc_flags,
                        struct per_cpu_pages *pcp,
-                       gfp_t gfp_flags)
+                       struct list_head *list)
 {
-       struct page *page = NULL;
-       struct list_head *list = NULL;
+       struct page *page;
 
        do {
-               /* First try to get CMA pages */
-               if (migratetype == MIGRATE_MOVABLE &&
-                               alloc_flags & ALLOC_CMA) {
-                       list = get_populated_pcp_list(zone, 0, pcp,
-                                       get_cma_migrate_type(), alloc_flags);
-               }
-
-               if (list == NULL) {
-                       /*
-                        * Either CMA is not suitable or there are no
-                        * free CMA pages.
-                        */
-                       list = get_populated_pcp_list(zone, 0, pcp,
+               if (list_empty(list)) {
+                       pcp->count += rmqueue_bulk(zone, 0,
+                                       pcp->batch, list,
                                        migratetype, alloc_flags);
-                       if (unlikely(list == NULL) ||
-                                       unlikely(list_empty(list)))
+                       if (unlikely(list_empty(list)))
                                return NULL;
                }
 
@@ -3552,13 +3485,14 @@ static struct page *rmqueue_pcplist(struct zone *preferred_zone,
                        int migratetype, unsigned int alloc_flags)
 {
        struct per_cpu_pages *pcp;
+       struct list_head *list;
        struct page *page;
        unsigned long flags;
 
        local_irq_save(flags);
        pcp = &this_cpu_ptr(zone->pageset)->pcp;
-       page = __rmqueue_pcplist(zone,  migratetype, alloc_flags, pcp,
-                                gfp_flags);
+       list = &pcp->lists[migratetype];
+       page = __rmqueue_pcplist(zone,  migratetype, alloc_flags, pcp, list);
        if (page) {
                __count_zid_vm_events(PGALLOC, page_zonenum(page), 1);
                zone_statistics(preferred_zone, zone);
@@ -3580,9 +3514,16 @@ struct page *rmqueue(struct zone *preferred_zone,
        struct page *page;
 
        if (likely(order == 0)) {
-               page = rmqueue_pcplist(preferred_zone, zone, gfp_flags,
-                                      migratetype, alloc_flags);
-               goto out;
+               /*
+                * MIGRATE_MOVABLE pcplist could have the pages on CMA area and
+                * we need to skip it when CMA area isn't allowed.
+                */
+               if (!IS_ENABLED(CONFIG_CMA) || alloc_flags & ALLOC_CMA ||
+                               migratetype != MIGRATE_MOVABLE) {
+                       page = rmqueue_pcplist(preferred_zone, zone, gfp_flags,
+                                       migratetype, alloc_flags);
+                       goto out;
+               }
        }
 
        /*
@@ -3605,15 +3546,8 @@ struct page *rmqueue(struct zone *preferred_zone,
                        if (page)
                                trace_mm_page_alloc_zone_locked(page, order, migratetype);
                }
-               if (!page) {
-                       if (migratetype == MIGRATE_MOVABLE &&
-                                       alloc_flags & ALLOC_CMA)
-                               page = __rmqueue_cma(zone, order, migratetype,
-                                                    alloc_flags);
-                       if (!page)
-                               page = __rmqueue(zone, order, migratetype,
-                                                alloc_flags);
-               }
+               if (!page)
+                       page = __rmqueue(zone, order, migratetype, alloc_flags);
        } while (page && check_new_pages(page, order));
        spin_unlock(&zone->lock);
        if (!page)
@@ -3793,14 +3727,6 @@ bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark,
                        continue;
 
                for (mt = 0; mt < MIGRATE_PCPTYPES; mt++) {
-#ifdef CONFIG_CMA
-                       /*
-                        * Note that this check is needed only
-                        * when MIGRATE_CMA < MIGRATE_PCPTYPES.
-                        */
-                       if (mt == MIGRATE_CMA)
-                               continue;
-#endif
                        if (!free_area_empty(area, mt))
                                return true;
                }
@@ -3938,8 +3864,7 @@ static inline unsigned int current_alloc_flags(gfp_t gfp_mask,
        unsigned int pflags = current->flags;
 
        if (!(pflags & PF_MEMALLOC_NOCMA) &&
-                       gfp_migratetype(gfp_mask) == MIGRATE_MOVABLE &&
-                       gfp_mask & __GFP_CMA)
+                       gfp_migratetype(gfp_mask) == MIGRATE_MOVABLE)
                alloc_flags |= ALLOC_CMA;
 
 #endif
@@ -8782,7 +8707,7 @@ int alloc_contig_range(unsigned long start, unsigned long end,
 
        trace_android_vh_cma_drain_all_pages_bypass(migratetype,
                                                &skip_drain_all_pages);
-       if (skip_drain_all_pages)
+       if (!skip_drain_all_pages)
                drain_all_pages(cc.zone);
 
        /*
index 6fce68d224b2fed42ce9f8b0694f500244ff4373..73cd50735df29a341bb62316ec3bc9d6f8af064b 100644 (file)
@@ -347,7 +347,7 @@ static void destroy_cache(struct zs_pool *pool)
 static unsigned long cache_alloc_handle(struct zs_pool *pool, gfp_t gfp)
 {
        return (unsigned long)kmem_cache_alloc(pool->handle_cachep,
-                       gfp & ~(__GFP_HIGHMEM|__GFP_MOVABLE|__GFP_CMA));
+                       gfp & ~(__GFP_HIGHMEM|__GFP_MOVABLE));
 }
 
 static void cache_free_handle(struct zs_pool *pool, unsigned long handle)
@@ -358,7 +358,7 @@ static void cache_free_handle(struct zs_pool *pool, unsigned long handle)
 static struct zspage *cache_alloc_zspage(struct zs_pool *pool, gfp_t flags)
 {
        return kmem_cache_alloc(pool->zspage_cachep,
-                       flags & ~(__GFP_HIGHMEM|__GFP_MOVABLE|__GFP_CMA));
+                       flags & ~(__GFP_HIGHMEM|__GFP_MOVABLE));
 }
 
 static void cache_free_zspage(struct zs_pool *pool, struct zspage *zspage)
index 55c4b03e6d9813b4e52bcc50a3d11fc5b86a1a2b..b926229af38f55f94701c0b6a173d70da5c6b690 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/uaccess.h>
 #include <linux/gpio.h>
 #include <linux/fs.h>
+#include <linux/errno.h>
 #include <dt-bindings/gpio/gpio.h>
 #include <uapi/linux/rfkill.h>
 #ifdef CONFIG_OF
@@ -505,6 +506,12 @@ static int bluetooth_platdata_parse_dt(struct device *dev,
     }
 
     gpio = of_get_named_gpio_flags(node, "BT,power_gpio", 0, &flags);
+    LOG("%s: BT,power_gpio = %d\n", __func__, gpio);
+
+    if (gpio == -EPROBE_DEFER) {
+        return gpio;
+    }
+
     if (gpio_is_valid(gpio)){
         data->poweron_gpio.io = gpio;
         data->poweron_gpio.enable = (flags == GPIO_ACTIVE_HIGH)? 1:0;
index 0d99f64d6eb74d0341f466a55d21fe797fd85879..3fac7e680beb99f14268b97a47ba7e87bb45ad10 100644 (file)
@@ -651,6 +651,9 @@ static int wlan_platdata_parse_dt(struct device *dev,
                data->mregulator.power_ctrl_by_pmu = false;
                LOG("%s: wifi power controled by gpio.\n", __func__);
         gpio = of_get_named_gpio_flags(node, "WIFI,poweren_gpio", 0, &flags);
+        if (gpio == -EPROBE_DEFER) {
+            return gpio;
+        }
         if (gpio_is_valid(gpio)){
                        data->power_n.io = gpio;
                        data->power_n.enable = (flags == GPIO_ACTIVE_HIGH)? 1:0;
index 42762f59dd0c9762c7738fd64c8ced5a79f4f6a8..546e35d70fe90e90eb39a3175bef00348b22de2a 100644 (file)
@@ -190,6 +190,7 @@ unsigned char aw87519_audio_speaker(void)
                                        aw87519_spk_cnt->data[i],
                                        aw87519_spk_cnt->data[i+1]);
        }
+       mdelay(40); // aw87519 chip need 40ms setup time.
 
        return 0;
 }
@@ -537,9 +538,7 @@ int aw87519_hw_reset(struct aw87519 *aw87519)
                usleep_range(2500, 3000);
                gpio_set_value_cansleep(aw87519->reset_gpio, 1);
                usleep_range(5000, 5500);
-               aw87519->hwen_flag = 1;
        } else {
-               aw87519->hwen_flag = 0;
                dev_err(&aw87519->i2c_client->dev, "%s: failed\n", __func__);
        }
 
@@ -599,6 +598,7 @@ static int  aw87519_power_event(struct snd_soc_dapm_widget *w,
        struct aw87519 *pa = snd_soc_component_get_drvdata(c);
 
        if (SND_SOC_DAPM_EVENT_ON(event)) {
+               aw87519_hw_reset(pa);
                /* Before widget power up: turn chip on, sync registers */
                aw87519_audio_speaker();
        } else {
index 0b4422f0f90cce341462cdccdc7ef227b7e86116..467e0d2e31af146d4f701fcff4ef8a0fe6e2254d 100644 (file)
@@ -4,7 +4,7 @@
 #define CPR_PERI_DIV_SEL_REG  0x004 /*audio sys i2s clock div Register*/
 #define CPR_PERI_CLK_SEL_REG  0x008 /*audio sys i2s clock selection Register*/
 #define CPR_PERI_CTRL_REG  0x00C /*Peripheral control signal configuration register*/
-
+#define CPR_IP_RST_REG  0x014
 
 /* AUDIO SYS DIV SEL REG, offset: 0x4 */
 #define CPR_AUDIO_DIV0_SEL_POS                     (4U)
 #define CPR_I2S2_SRC_SEL_24M                   (0x1U << AUDIOSYS_I2S2_SRC_SEL_POS)
 #define CPR_I2S2_SRC_SEL_AUDIO_DIVCLK1         (0x2U << AUDIOSYS_I2S2_SRC_SEL_POS)
 
+#define CPR_TDM_SRC_SEL_POS                     (16U)
+#define CPR_TDM_SRC_SEL_MSK                     (0x3U << CPR_TDM_SRC_SEL_POS)
+#define CPR_TDM_SRC_SEL(X)                          (X << CPR_TDM_SRC_SEL_POS)
+
 /* PERI_CTRL_REG, Offset: 0xC */
 #define CPR_VAD_I2SIN_SYNC_POS            (12U)
 #define CPR_VAD_I2SIN_SYNC_MSK            (0x1U << CPR_VAD_I2SIN_SYNC_POS)
 #define CPR_SPDIF_SYNC_MSK                (0x1U << CPR_SPDIF_SYNC_POS)
 #define CPR_SPDIF_SYNC_EN                 (CPR_SPDIF_SYNC_MSK)
 
+/* CPR_IP_RST_REG, Offset: 0x014 */
+#define CPR_I2S0_SRST_N_SEL_POS                      (17U)
+#define CPR_I2S0_SRST_N_SEL_MSK                       (0x1U << CPR_I2S0_SRST_N_SEL_POS)
+#define CPR_I2S0_SRST_N_SEL(X)                            (X << CPR_I2S0_SRST_N_SEL_POS)
+#define CPR_I2S1_SRST_N_SEL_POS                      (18U)
+#define CPR_I2S1_SRST_N_SEL_MSK                       (0x1U << CPR_I2S1_SRST_N_SEL_POS)
+#define CPR_I2S1_SRST_N_SEL(X)                            (X << CPR_I2S1_SRST_N_SEL_POS)
+#define CPR_I2S2_SRST_N_SEL_POS                      (19U)
+#define CPR_I2S2_SRST_N_SEL_MSK                       (0x1U << CPR_I2S2_SRST_N_SEL_POS)
+#define CPR_I2S2_SRST_N_SEL(X)                            (X << CPR_I2S2_SRST_N_SEL_POS)
+#define CPR_I2S8CH_SRST_N_SEL_POS                      (20U)
+#define CPR_I2S8CH_SRST_N_SEL_MSK                       (0x1U << CPR_I2S8CH_SRST_N_SEL_POS)
+#define CPR_I2S8CH_SRST_N_SEL(X)                            (X << CPR_I2S8CH_SRST_N_SEL_POS)
+#define CPR_TDM_SRST_N_SEL_POS                      (21U)
+#define CPR_TDM_SRST_N_SEL_MSK                       (0x1U << CPR_TDM_SRST_N_SEL_POS)
+#define CPR_TDM_SRST_N_SEL(X)                            (X << CPR_TDM_SRST_N_SEL_POS)
 
 #endif /* _LIGHT_AUDIO_CPR_H */
 
index 32843f09d49ae87340e106d089a3bc84125615a9..1849881139be947df7d4ba23c4984521bf91366a 100644 (file)
@@ -56,7 +56,7 @@
 #define LIGHT_AUDIO_PAD_CONFIG(idx)   (priv->cfg_off + ((idx-25) >> 1) * 4)
 
 static int i2s_8ch_probe_flag = 0;
-static u32 light_special_sample_rates[] = { 11025, 22050, 44100, 88200 };
+//static u32 light_special_sample_rates[] = { 11025, 22050, 44100, 88200 };
 
 static int light_audio_cpr_set(struct light_i2s_priv *chip, unsigned int cpr_off,
                                unsigned int mask, unsigned int val)
@@ -165,21 +165,41 @@ static int light_i2s_8ch_dai_trigger(struct snd_pcm_substream *substream, int cm
        struct light_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 
+       if(!priv->regmap) {
+               return 0;
+       }
+
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (tx)
+               if (tx) {
                        light_snd_txctrl(priv, 1);
-               else
+                       priv->state |= I2S_STATE_TX_RUNNING;
+               }
+               else {
                        light_snd_rxctrl(priv, 1);
+                       priv->state |= I2S_STATE_RX_RUNNING;
+               }
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                if (tx) {
                        dmaengine_terminate_async(snd_dmaengine_pcm_get_chan(substream));  // work around for DMAC stop issue
                        light_snd_txctrl(priv, 0);
+                       priv->state &= ~I2S_STATE_TX_RUNNING;
+               } else {
+                       light_snd_rxctrl(priv, 0);
+                       priv->state &= ~I2S_STATE_RX_RUNNING;
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       regmap_read(priv->regmap, I2S_FUNCMODE, &priv->suspend_funcmode);
+               if (tx) {
+                       dmaengine_pause(snd_dmaengine_pcm_get_chan(substream));  // work around for DMAC stop issue
+                       light_snd_txctrl(priv, 0);
+               } else {
+                       light_snd_rxctrl(priv, 0);
                }
                break;
     default:
@@ -200,6 +220,8 @@ static int light_i2s_8ch_set_fmt_dai(struct snd_soc_dai *cpu_dai, unsigned int f
                return 0;
        }
 
+       pm_runtime_resume_and_get(priv->dev);
+
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                cnfout |= IISCNFOUT_TSAFS_I2S;
@@ -224,6 +246,8 @@ static int light_i2s_8ch_set_fmt_dai(struct snd_soc_dai *cpu_dai, unsigned int f
                        CNFIN_I2S_RXMODE_Msk,
                        cnfin);
 
+       pm_runtime_put_sync(priv->dev);
+
        return 0;
 }
 
@@ -330,6 +354,7 @@ static int light_i2s_8ch_dai_hw_params(struct snd_pcm_substream *substream, stru
                iiscnf_in &= ~CNFIN_RX_CH_SEL_LEFT;
                iiscnf_in &= ~CNFIN_RVOICEEN_MONO;
        }
+       iiscnf_in |= CNFIN_I2S_RXMODE_MASTER_MODE;
 
        if (tx)
                writel(iiscnf_out, priv->regs + I2S_IISCNF_OUT);
@@ -526,32 +551,12 @@ static int light_pcm_probe(struct platform_device *pdev,struct light_i2s_priv *i
 
 static bool light_i2s_8ch_wr_reg(struct device *dev, unsigned int reg)
 {
-        switch (reg) {
-        case I2S_IISEN:
-        case I2S_FUNCMODE:
-        case I2S_IISCNF_IN:
-        case I2S_FSSTA:
-        case I2S_IISCNF_OUT:
-        case I2S_DMACR:
-                return true;
-        default:
-                return false;
-        }
+    return true;
 }
 
 static bool light_i2s_8ch_rd_reg(struct device *dev, unsigned int reg)
 {
-        switch (reg) {
-        case I2S_IISEN:
-        case I2S_FUNCMODE:
-        case I2S_IISCNF_IN:
-        case I2S_FSSTA:
-        case I2S_IISCNF_OUT:
-        case I2S_DMACR:
-                return true;
-        default:
-                return false;
-        }
+    return true;
 }
 
 static const struct regmap_config light_i2s_8ch_regmap_config = {
@@ -561,9 +566,10 @@ static const struct regmap_config light_i2s_8ch_regmap_config = {
         .max_register = I2S_DR4,
         .writeable_reg = light_i2s_8ch_wr_reg,
         .readable_reg = light_i2s_8ch_rd_reg,
-        .cache_type = REGCACHE_FLAT,
+        .cache_type = REGCACHE_NONE,
 };
 
+#if 0
 static int light_audio_pinconf_set(struct device *dev, unsigned int pin_id, unsigned int val)
 {
        struct light_i2s_priv *priv = dev_get_drvdata(dev);
@@ -588,6 +594,7 @@ static int light_audio_pinctrl(struct device *dev)
 {
        return 0;
 }
+#endif
 
 static int light_i2s_8ch_runtime_suspend(struct device *dev)
 {
@@ -623,6 +630,75 @@ static int light_i2s_8ch_runtime_resume(struct device *dev)
        return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int light_i2s_8ch_suspend(struct device *dev)
+{
+    struct light_i2s_priv *priv = dev_get_drvdata(dev);
+
+       if(!priv->regmap || priv->state == I2S_STATE_IDLE) {
+               return 0;
+       }
+
+       regmap_read(priv->regmap, I2S_DIV0_LEVEL, &priv->suspend_div0_level);
+       regmap_read(priv->regmap, I2S_DIV3_LEVEL, &priv->suspend_div3_level);
+       regmap_read(priv->regmap, I2S_IISCNF_IN, &priv->suspend_iiscnf_in);
+       regmap_read(priv->regmap, I2S_FSSTA, &priv->suspend_fssta);
+       regmap_read(priv->regmap, I2S_IISCNF_OUT, &priv->suspend_ii2cnf_out);
+       regmap_read(priv->regmap, I2S_FADTLR, &priv->suspend_fadtlr);
+       regmap_read(priv->regmap, I2S_SCCR, &priv->suspend_sccr);
+       regmap_read(priv->regmap, I2S_TXFTLR, &priv->suspend_txftlr);
+       regmap_read(priv->regmap, I2S_RXFTLR, &priv->suspend_rxftlr);
+       regmap_read(priv->regmap, I2S_IMR, &priv->suspend_imr);
+       regmap_read(priv->regmap, I2S_DMATDLR, &priv->suspend_dmatdlr);
+       regmap_read(priv->regmap, I2S_DMARDLR, &priv->suspend_dmardlr);
+
+    regmap_read(priv->audio_cpr_regmap, CPR_PERI_DIV_SEL_REG, &priv->cpr_peri_div_sel);
+    regmap_read(priv->audio_cpr_regmap, CPR_PERI_CTRL_REG, &priv->cpr_peri_ctrl);
+    regmap_read(priv->audio_cpr_regmap, CPR_PERI_CLK_SEL_REG, &priv->cpr_peri_clk_sel);
+       
+       regmap_update_bits(priv->audio_cpr_regmap,
+                                                       CPR_IP_RST_REG, CPR_I2S8CH_SRST_N_SEL_MSK, CPR_I2S8CH_SRST_N_SEL(0));
+
+    clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static int light_i2s_8ch_resume(struct device *dev)
+{
+    struct light_i2s_priv *priv = dev_get_drvdata(dev);
+    int ret;
+
+       if(!priv->regmap || priv->state == I2S_STATE_IDLE) {
+               return 0;
+       }
+
+    ret = clk_prepare_enable(priv->clk);
+    if (ret) {
+            dev_err(priv->dev, "clock enable failed %d\n", ret);
+            return ret;
+    }
+
+    regmap_update_bits(priv->audio_cpr_regmap,
+                        CPR_IP_RST_REG, CPR_I2S8CH_SRST_N_SEL_MSK, CPR_I2S8CH_SRST_N_SEL(1));
+
+    regmap_write(priv->audio_cpr_regmap, CPR_PERI_CTRL_REG, priv->cpr_peri_ctrl);
+
+
+       regmap_write(priv->regmap, I2S_IISEN, 0);
+       regmap_write(priv->regmap, I2S_FSSTA, priv->suspend_fssta);
+       regmap_write(priv->regmap, I2S_FUNCMODE, priv->suspend_funcmode | FUNCMODE_TMODE_WEN | FUNCMODE_RMODE_WEN);
+       regmap_write(priv->regmap, I2S_IISCNF_IN, priv->suspend_iiscnf_in);
+       regmap_write(priv->regmap, I2S_IISCNF_OUT, priv->suspend_ii2cnf_out);
+    regmap_write(priv->audio_cpr_regmap, CPR_PERI_CLK_SEL_REG, priv->cpr_peri_clk_sel);
+       regmap_write(priv->regmap, I2S_DIV0_LEVEL, priv->suspend_div0_level);
+       regmap_write(priv->regmap, I2S_DIV3_LEVEL, priv->suspend_div3_level);
+    regmap_write(priv->audio_cpr_regmap, CPR_PERI_DIV_SEL_REG, priv->cpr_peri_div_sel);
+
+    return ret;
+}
+#endif
+
 static const struct of_device_id light_i2s_8ch_of_match[] = {
        { .compatible = "light,light-i2s-8ch"},
        {},
@@ -710,11 +786,8 @@ static int light_audio_i2s_8ch_probe(struct platform_device *pdev)
                        return PTR_ERR(priv->clk);
 
                pm_runtime_enable(&pdev->dev);
-               if (!pm_runtime_enabled(&pdev->dev)) {
-                       ret = light_i2s_8ch_runtime_resume(&pdev->dev);
-                       if (ret)
-                               goto err_pm_disable;
-               }
+               pm_runtime_resume_and_get(&pdev->dev); // clk gate is enabled by hardware as default register value
+               pm_runtime_put_sync(&pdev->dev);
 
                irq = platform_get_irq(pdev, 0);
 
@@ -778,6 +851,8 @@ static int light_i2s_8ch_remove(struct platform_device *pdev)
 static const struct dev_pm_ops light_i2s_8ch_pm_ops = {
         SET_RUNTIME_PM_OPS(light_i2s_8ch_runtime_suspend, light_i2s_8ch_runtime_resume,
                            NULL)
+               SET_SYSTEM_SLEEP_PM_OPS(light_i2s_8ch_suspend,
+                                    light_i2s_8ch_resume)
 };
 
 static struct platform_driver light_i2s_8ch_driver = {
index 250980fccca4634edbe6091003241029ba2990b4..c7e9d2b055af2f65c7cf6febfe07f1cb33451783 100644 (file)
@@ -118,9 +118,10 @@ static inline void light_snd_txctrl(struct light_i2s_priv *chip, bool on)
 {
        u32 dma_en = 0;
        u32 i2s_en = 0;
-       u32 i2s_status = 0;
        u32 i2s_imr = 0;
 
+       /* get current dma status, save rx configuration */
+       dma_en = readl(chip->regs + I2S_DMACR);
        if (on) {
                dma_en |= DMACR_TDMAE_EN;
                i2s_en |= IISEN_I2SEN;
@@ -128,36 +129,57 @@ static inline void light_snd_txctrl(struct light_i2s_priv *chip, bool on)
                writel(i2s_en, chip->regs + I2S_IISEN);
        } else {
                dma_en &= ~DMACR_TDMAE_EN;
-               i2s_en &= ~IISEN_I2SEN;
                i2s_imr  = readl(chip->regs + I2S_IMR);
                i2s_imr &= ~(IMR_TXUIRM_INTR_MSK);
                i2s_imr &= ~(IMR_TXEIM_INTR_MSK);
                writel(i2s_imr, chip->regs + I2S_IMR);
                writel(dma_en, chip->regs + I2S_DMACR);
-               writel(i2s_en, chip->regs + I2S_IISEN);
+
+               if (!chip->mclk_keepon) {
+                       /*
+                       * The enablement of I2S can onlybe truned off when
+                       * the DMA configuration for RX and TX is completely disabled.
+                       */
+                       if ( ((DMACR_TDMAE_MSK | DMACR_RDMAE_MSK) & dma_en) == 0) {
+                               i2s_en &= ~IISEN_I2SEN;
+                               writel(i2s_en, chip->regs + I2S_IISEN);
+                       }
+               }
        }
 }
 
 static inline void light_snd_rxctrl(struct light_i2s_priv *chip, bool on)
 {
-        u32 dma_en;
+    u32 dma_en;
        u32 i2s_en;
 
+       /* get current dma status, save tx configuration */
+       dma_en = readl(chip->regs + I2S_DMACR);
        if (on) {
                dma_en |= DMACR_RDMAE_EN;
                i2s_en |= IISEN_I2SEN;
+        writel(dma_en, chip->regs + I2S_DMACR);
+        writel(i2s_en, chip->regs + I2S_IISEN);
        } else {
                dma_en &= ~DMACR_RDMAE_EN;
-               i2s_en &= ~IISEN_I2SEN;
-       }
-
         writel(dma_en, chip->regs + I2S_DMACR);
-        writel(i2s_en, chip->regs + I2S_IISEN);
+               if (!chip->mclk_keepon) {
+                       /*
+                       * The enablement of I2S can onlybe truned off when
+                       * the DMA confgiguratio for RX and TX is completely disabled.
+                       */
+                       if ( ((DMACR_TDMAE_MSK | DMACR_RDMAE_MSK) & dma_en) == 0) {
+                               i2s_en &= ~IISEN_I2SEN;
+                               writel(i2s_en, chip->regs + I2S_IISEN);
+                       }
+               }
+       }
 }
 
 static int light_i2s_dai_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
+       //printk("%s: %s %s\n", __func__, substream->pcm->name, dai->name);
        return 0;
 }
 
@@ -166,11 +188,10 @@ static void light_i2s_dai_shutdown(struct snd_pcm_substream *substream,
 {
        struct light_i2s_priv *i2s_private = snd_soc_dai_get_drvdata(dai);
 
+       //printk("%s: %s %s\n", __func__, substream->pcm->name, dai->name);
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                light_snd_rxctrl(i2s_private, 0);
-
-       clk_disable_unprepare(i2s_private->clk);
 }
 
 /**
@@ -184,29 +205,46 @@ static int light_i2s_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 {
        int ret = 0;
 
-       struct light_i2s_priv *i2s_private = snd_soc_dai_get_drvdata(dai);
+       struct light_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
         bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 
+       //printk("%s: %s %s cmd=%d tx=%d\n", __func__, substream->pcm->name, dai->name, cmd, tx);
+
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (tx)
-                       light_snd_txctrl(i2s_private, 1);
-               else
-                       light_snd_rxctrl(i2s_private, 1);
+               if (tx) {
+                       light_snd_txctrl(priv, 1);
+                       priv->state |= I2S_STATE_TX_RUNNING;
+               }
+               else {
+                       light_snd_rxctrl(priv, 1);
+                       priv->state |= I2S_STATE_RX_RUNNING;
+               }
                break;
        case SNDRV_PCM_TRIGGER_STOP:
+               if (tx) {
+                       dmaengine_terminate_async(snd_dmaengine_pcm_get_chan(substream));  // work around for DMAC stop issue
+                       light_snd_txctrl(priv, 0);
+                       priv->state &= ~I2S_STATE_TX_RUNNING;
+               } else {
+                       light_snd_rxctrl(priv, 0);
+                       priv->state &= ~I2S_STATE_RX_RUNNING;
+               }
+               break;
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       regmap_read(priv->regmap, I2S_FUNCMODE, &priv->suspend_funcmode);
                if (tx) {
-                       dmaengine_terminate_async(snd_dmaengine_pcm_get_chan(substream));  // work around for DMAC stop issue
-                       light_snd_txctrl(i2s_private, 0);
+                       dmaengine_pause(snd_dmaengine_pcm_get_chan(substream));  // work around for DMAC stop issue
+                       light_snd_txctrl(priv, 0);
+               } else {
+                       light_snd_rxctrl(priv, 0);
                }
                break;
     default:
         return -EINVAL;
-
        }
 
        return ret;
@@ -218,6 +256,8 @@ static int light_i2s_set_fmt_dai(struct snd_soc_dai *cpu_dai, unsigned int fmt)
        u32 cnfout = 0;
        u32 cnfin = 0;
 
+    pm_runtime_resume_and_get(i2s_private->dev);
+
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
                cnfout |= IISCNFOUT_TSAFS_I2S;
@@ -238,10 +278,13 @@ static int light_i2s_set_fmt_dai(struct snd_soc_dai *cpu_dai, unsigned int fmt)
                        cnfout);
 
        cnfin |= CNFIN_I2S_RXMODE_MASTER_MODE;
+
        regmap_update_bits(i2s_private->regmap, I2S_IISCNF_IN,
                        CNFIN_I2S_RXMODE_Msk,
                        cnfin);
 
+       pm_runtime_put_sync(i2s_private->dev);
+
        return 0;
 }
 
@@ -256,7 +299,7 @@ static int light_i2s_dai_hw_params(struct snd_pcm_substream *substream, struct s
        u32 funcmode;
        u32 iiscnf_out;
        u32 iiscnf_in;
-       u32 i2s_en;
+       u32 i2s_en = 0;
 
        u32 channels = params_channels(params);
 
@@ -307,6 +350,10 @@ static int light_i2s_dai_hw_params(struct snd_pcm_substream *substream, struct s
                return -EINVAL;
        }
 
+       /* 
+        * FUNCMODE,I2S_IISCNF_OUT,I2S_IISCNF_IN registers,
+        * it is impossible to write to this register when I2S is enabled
+        */
        i2s_en &= ~IISEN_I2SEN;
        writel(i2s_en, i2s_private->regs + I2S_IISEN);
 
@@ -316,24 +363,19 @@ static int light_i2s_dai_hw_params(struct snd_pcm_substream *substream, struct s
        funcmode = readl(i2s_private->regs + I2S_FUNCMODE);
        if (tx) {
                funcmode |= FUNCMODE_TMODE_WEN;
-               funcmode &= ~FUNCMODE_CH1_ENABLE;
-               funcmode |= FUNCMODE_RMODE_WEN;
-               funcmode &= ~FUNCMODE_RMODE;
                funcmode &= ~FUNCMODE_TMODE;
                funcmode |= FUNCMODE_TMODE;
        } else {
                funcmode |= FUNCMODE_RMODE_WEN;
-               funcmode |= FUNCMODE_CH0_ENABLE;
-               funcmode |= FUNCMODE_CH1_ENABLE;
-               funcmode |= FUNCMODE_CH2_ENABLE;
-               funcmode |= FUNCMODE_TMODE_WEN;
-               funcmode &= ~FUNCMODE_TMODE;
                funcmode &= ~FUNCMODE_RMODE;
                funcmode |= FUNCMODE_RMODE;
        }
+       funcmode |= FUNCMODE_CH0_ENABLE;
 
        writel(funcmode, i2s_private->regs + I2S_FUNCMODE);
 
+       //printk("%s: %s %s channels=%d rate=%d fm=%x\n", __func__, substream->pcm->name, dai->name, channels, rate, funcmode);
+
        if (channels == MONO_SOURCE) {
                iiscnf_out |= IISCNFOUT_TX_VOICE_EN_MONO;
                iiscnf_in |= CNFIN_RX_CH_SEL_LEFT;
@@ -351,6 +393,13 @@ static int light_i2s_dai_hw_params(struct snd_pcm_substream *substream, struct s
 
        light_i2s_set_div_sclk(i2s_private, rate, DIV_DEFAULT);
 
+    /* Turn on the I2S enable switch ahead of time, 
+     * and start the MCLK before the PA is turned on
+        * to solve the pop noise caused by the sudden change in I2S startup.
+        */
+       i2s_en |= IISEN_I2SEN;
+       writel(i2s_en, i2s_private->regs + I2S_IISEN);
+
        return 0;
 }
 
@@ -421,6 +470,8 @@ static int light_i2s_dai_probe(struct snd_soc_dai *dai)
 {
        struct light_i2s_priv *i2s = snd_soc_dai_get_drvdata(dai);
 
+       //printk("%s: %p %p\n", __func__, i2s->base, i2s->regs);
+
        if(i2s)
                snd_soc_dai_init_dma_data(dai, &i2s->dma_params_tx,
                                &i2s->dma_params_rx);
@@ -461,6 +512,7 @@ static struct snd_soc_dai_driver light_i2s_soc_dai[] = {
                        .channels_max   = 2,
                },
                .ops = &light_i2s_dai_ops,
+               .symmetric_rates = 1,
        },
        {
                .probe = light_i2s_dai_probe,
@@ -495,32 +547,12 @@ static int light_pcm_probe(struct platform_device *pdev,struct light_i2s_priv *i
 
 static bool light_i2s_wr_reg(struct device *dev, unsigned int reg)
 {
-        switch (reg) {
-        case I2S_IISEN:
-        case I2S_FUNCMODE:
-        case I2S_IISCNF_IN:
-        case I2S_FSSTA:
-        case I2S_IISCNF_OUT:
-        case I2S_DMACR:
-                return true;
-        default:
-                return false;
-        }
+    return true;
 }
 
 static bool light_i2s_rd_reg(struct device *dev, unsigned int reg)
 {
-        switch (reg) {
-        case I2S_IISEN:
-        case I2S_FUNCMODE:
-        case I2S_IISCNF_IN:
-        case I2S_FSSTA:
-        case I2S_IISCNF_OUT:
-        case I2S_DMACR:
-                return true;
-        default:
-                return false;
-        }
+       return true;
 }
 
 static const struct regmap_config light_i2s_regmap_config = {
@@ -530,7 +562,7 @@ static const struct regmap_config light_i2s_regmap_config = {
         .max_register = I2S_DR4,
         .writeable_reg = light_i2s_wr_reg,
         .readable_reg = light_i2s_rd_reg,
-        .cache_type = REGCACHE_FLAT,
+        .cache_type = REGCACHE_NONE,
 };
 
 static int light_audio_pinconf_set(struct device *dev, unsigned int pin_id, unsigned int val)
@@ -581,29 +613,114 @@ static int light_audio_pinctrl(struct device *dev)
 
 static int light_i2s_runtime_suspend(struct device *dev)
 {
-        struct light_i2s_priv *i2s_priv = dev_get_drvdata(dev);
+       struct light_i2s_priv *i2s_priv = dev_get_drvdata(dev);
 
-        regcache_cache_only(i2s_priv->regmap, true);
-        clk_disable_unprepare(i2s_priv->clk);
+       //printk("%s: %s\n", __func__, i2s_priv->name);
+
+       if (!i2s_priv->mclk_keepon) {
+               regcache_cache_only(i2s_priv->regmap, true);
+               clk_disable_unprepare(i2s_priv->clk);
+       }
 
        return 0;
 }
 
 static int light_i2s_runtime_resume(struct device *dev)
 {
-        struct light_i2s_priv *i2s_priv = dev_get_drvdata(dev);
-        int ret;
+       struct light_i2s_priv *i2s_priv = dev_get_drvdata(dev);
+       int ret;
+
+       //printk("%s: %s\n", __func__, i2s_priv->name);
+
+       if (!i2s_priv->mclk_keepon) {
+               ret = clk_prepare_enable(i2s_priv->clk);
+               if (ret) {
+                       dev_err(i2s_priv->dev, "clock enable failed %d\n", ret);
+                       return ret;
+               }
+
+               regcache_cache_only(i2s_priv->regmap, false);
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int light_i2s_suspend(struct device *dev)
+{
+    struct light_i2s_priv *priv = dev_get_drvdata(dev);
+
+       if (priv->state == I2S_STATE_IDLE)
+               return 0;
+
+       regmap_read(priv->regmap, I2S_DIV0_LEVEL, &priv->suspend_div0_level);
+       regmap_read(priv->regmap, I2S_DIV3_LEVEL, &priv->suspend_div3_level);
+       regmap_read(priv->regmap, I2S_IISCNF_IN, &priv->suspend_iiscnf_in);
+       regmap_read(priv->regmap, I2S_FSSTA, &priv->suspend_fssta);
+       regmap_read(priv->regmap, I2S_IISCNF_OUT, &priv->suspend_ii2cnf_out);
+       regmap_read(priv->regmap, I2S_FADTLR, &priv->suspend_fadtlr);
+       regmap_read(priv->regmap, I2S_SCCR, &priv->suspend_sccr);
+       regmap_read(priv->regmap, I2S_TXFTLR, &priv->suspend_txftlr);
+       regmap_read(priv->regmap, I2S_RXFTLR, &priv->suspend_rxftlr);
+       regmap_read(priv->regmap, I2S_IMR, &priv->suspend_imr);
+       regmap_read(priv->regmap, I2S_DMATDLR, &priv->suspend_dmatdlr);
+       regmap_read(priv->regmap, I2S_DMARDLR, &priv->suspend_dmardlr);
+
+       if (strcmp(priv->name, AP_I2S)) {
+               regmap_read(priv->audio_cpr_regmap, CPR_PERI_DIV_SEL_REG, &priv->cpr_peri_div_sel);
+               regmap_read(priv->audio_cpr_regmap, CPR_PERI_CTRL_REG, &priv->cpr_peri_ctrl);
+               regmap_read(priv->audio_cpr_regmap, CPR_PERI_CLK_SEL_REG, &priv->cpr_peri_clk_sel);
+
+               if (!strcmp(priv->name, AUDIO_I2S0))
+                       regmap_update_bits(priv->audio_cpr_regmap, CPR_IP_RST_REG, CPR_I2S0_SRST_N_SEL_MSK, CPR_I2S0_SRST_N_SEL(0));
+               else if (!strcmp(priv->name, AUDIO_I2S1))
+                       regmap_update_bits(priv->audio_cpr_regmap, CPR_IP_RST_REG, CPR_I2S1_SRST_N_SEL_MSK, CPR_I2S1_SRST_N_SEL(0));
+               else if (!strcmp(priv->name, AUDIO_I2S2))
+                       regmap_update_bits(priv->audio_cpr_regmap, CPR_IP_RST_REG, CPR_I2S2_SRST_N_SEL_MSK, CPR_I2S2_SRST_N_SEL(0));
+       }
+
+    clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static int light_i2s_resume(struct device *dev)
+{
+    struct light_i2s_priv *priv = dev_get_drvdata(dev);
+    int ret;
 
-        ret = clk_prepare_enable(i2s_priv->clk);
-        if (ret) {
-                dev_err(i2s_priv->dev, "clock enable failed %d\n", ret);
-                return ret;
-        }
+       if (priv->state == I2S_STATE_IDLE)
+               return 0;
 
-        regcache_cache_only(i2s_priv->regmap, false);
+    ret = clk_prepare_enable(priv->clk);
+    if (ret) {
+            dev_err(priv->dev, "clock enable failed %d\n", ret);
+            return ret;
+    }
+
+       if (strcmp(priv->name, AP_I2S)) {
+               if (!strcmp(priv->name, AUDIO_I2S0))
+                       regmap_update_bits(priv->audio_cpr_regmap, CPR_IP_RST_REG, CPR_I2S0_SRST_N_SEL_MSK, CPR_I2S0_SRST_N_SEL(1));
+               else if (!strcmp(priv->name, AUDIO_I2S1))
+                       regmap_update_bits(priv->audio_cpr_regmap, CPR_IP_RST_REG, CPR_I2S1_SRST_N_SEL_MSK, CPR_I2S1_SRST_N_SEL(1));
+               else if (!strcmp(priv->name, AUDIO_I2S2))
+                       regmap_update_bits(priv->audio_cpr_regmap, CPR_IP_RST_REG, CPR_I2S2_SRST_N_SEL_MSK, CPR_I2S2_SRST_N_SEL(1));
+               regmap_write(priv->audio_cpr_regmap, CPR_PERI_CTRL_REG, priv->cpr_peri_ctrl);
+               regmap_write(priv->audio_cpr_regmap, CPR_PERI_CLK_SEL_REG, priv->cpr_peri_clk_sel);
+               regmap_write(priv->audio_cpr_regmap, CPR_PERI_DIV_SEL_REG, priv->cpr_peri_div_sel);
+       }
 
-        return ret;
+       regmap_write(priv->regmap, I2S_IISEN, 0);
+       regmap_write(priv->regmap, I2S_FSSTA, priv->suspend_fssta);
+       regmap_write(priv->regmap, I2S_FUNCMODE, priv->suspend_funcmode | FUNCMODE_TMODE_WEN | FUNCMODE_RMODE_WEN);
+       regmap_write(priv->regmap, I2S_IISCNF_IN, priv->suspend_iiscnf_in);
+       regmap_write(priv->regmap, I2S_IISCNF_OUT, priv->suspend_ii2cnf_out);
+       regmap_write(priv->regmap, I2S_DIV0_LEVEL, priv->suspend_div0_level);
+       regmap_write(priv->regmap, I2S_DIV3_LEVEL, priv->suspend_div3_level);
+
+    return ret;
 }
+#endif
 
 static const struct of_device_id light_i2s_of_match[] = {
        { .compatible = "light,light-i2s"},
@@ -636,7 +753,7 @@ static int light_audio_i2s_probe(struct platform_device *pdev)
                if (!strcmp(sprop, "i2s-master"))
                        i2s_priv->dai_fmt = SND_SOC_DAIFMT_I2S;
                else
-                       printk("mode is not i2s-master");
+                       printk("mode is not i2s-master\n");
        }
 
        sprop = of_get_property(np, "light,sel", NULL);
@@ -650,20 +767,30 @@ static int light_audio_i2s_probe(struct platform_device *pdev)
        else
                i2s_priv->dma_maxburst = 8;
 
+
+       iprop = of_get_property(np, "light,mclk_keepon", NULL);
+       if (iprop)
+               i2s_priv->mclk_keepon = be32_to_cpup(iprop);
+       else
+               i2s_priv->mclk_keepon = 0;
+
        dev_set_drvdata(&pdev->dev, i2s_priv);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        i2s_priv->regs = devm_ioremap_resource(dev, res);
+
+       //printk("%s: %p %p\n", __func__, i2s_priv->base, i2s_priv->regs);
+
        if (IS_ERR(i2s_priv->regs))
                return PTR_ERR(i2s_priv->regs);
 
-        i2s_priv->regmap = devm_regmap_init_mmio(&pdev->dev, i2s_priv->regs,
-                                            &light_i2s_regmap_config);
-        if (IS_ERR(i2s_priv->regmap)) {
-                dev_err(&pdev->dev,
-                        "Failed to initialise managed register map\n");
-                return PTR_ERR(i2s_priv->regmap);
-        }
+       i2s_priv->regmap = devm_regmap_init_mmio(&pdev->dev, i2s_priv->regs,
+                                                                               &light_i2s_regmap_config);
+       if (IS_ERR(i2s_priv->regmap)) {
+                       dev_err(&pdev->dev,
+                                       "Failed to initialise managed register map\n");
+                       return PTR_ERR(i2s_priv->regmap);
+       }
 
        if (!strcmp(i2s_priv->name, AUDIO_I2S0) || !strcmp(i2s_priv->name, AUDIO_I2S1) || !strcmp(i2s_priv->name, AUDIO_I2S2)) {
                i2s_priv->audio_pin_regmap = syscon_regmap_lookup_by_phandle(np, "audio-pin-regmap");
@@ -682,13 +809,6 @@ static int light_audio_i2s_probe(struct platform_device *pdev)
                light_audio_cpr_set(i2s_priv, CPR_PERI_CTRL_REG, CPR_I2S_SYNC_MSK, CPR_I2S_SYNC_EN);
        }
 
-       pm_runtime_enable(&pdev->dev);
-        if (!pm_runtime_enabled(&pdev->dev)) {
-                ret = light_i2s_runtime_resume(&pdev->dev);
-                if (ret)
-                        goto err_pm_disable;
-        }
-
        irq = platform_get_irq(pdev, 0);
 
        if (!res || (int)irq <= 0) {
@@ -700,9 +820,9 @@ static int light_audio_i2s_probe(struct platform_device *pdev)
        if (IS_ERR(i2s_priv->clk))
                 return PTR_ERR(i2s_priv->clk);
 
-       ret = clk_prepare_enable(i2s_priv->clk);
-        if (ret < 0)
-                return ret;
+    pm_runtime_enable(&pdev->dev);
+    pm_runtime_resume_and_get(&pdev->dev); // clk gate is enabled by hardware as default register value
+    pm_runtime_put_sync(&pdev->dev);
 
        i2s_priv->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
        i2s_priv->dma_params_rx.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -722,6 +842,10 @@ static int light_audio_i2s_probe(struct platform_device *pdev)
 
        light_pcm_probe(pdev, i2s_priv);
 
+       if (i2s_priv->mclk_keepon) {
+               clk_prepare_enable(i2s_priv->clk);
+       }
+
        ret = devm_snd_soc_register_component(&pdev->dev, &light_i2s_soc_component,
                                    light_i2s_soc_dai, ARRAY_SIZE(light_i2s_soc_dai));
        if (ret < 0) {
@@ -753,6 +877,8 @@ static int light_i2s_remove(struct platform_device *pdev)
 static const struct dev_pm_ops light_i2s_pm_ops = {
         SET_RUNTIME_PM_OPS(light_i2s_runtime_suspend, light_i2s_runtime_resume,
                            NULL)
+               SET_SYSTEM_SLEEP_PM_OPS(light_i2s_suspend,
+                                    light_i2s_resume)
 };
 
 static struct platform_driver light_i2s_driver = {
index a8efe74f5009eac6a9b112eac33efa2e352b8bb9..ada0b827671c23ccfe900e239c3af34b9f8e03ff 100644 (file)
 #define RXFIFO_IRQ_TH                               (0x20U)
 #define I2S_MAX_FIFO                                (0x20U)
 
+#define I2S_STATE_IDLE                         0
+#define I2S_STATE_TX_RUNNING   1
+#define I2S_STATE_RX_RUNNING   2
+
 struct light_i2s_priv {
        void __iomem *base;
        phys_addr_t phys;
@@ -471,11 +475,33 @@ struct light_i2s_priv {
        unsigned int dai_fmt;
        u32 dma_maxburst;
        unsigned int cfg_off;
+       u32 mclk_keepon;
 
        struct device *dev;
        char name[16];
        int chan_num:16;
        unsigned int clk_master:1;
+
+       u32 suspend_iisen;
+       u32 suspend_funcmode;
+       u32 suspend_iiscnf_in;
+       u32 suspend_fssta;
+       u32 suspend_ii2cnf_out;
+       u32 suspend_fadtlr;
+       u32 suspend_sccr;
+       u32 suspend_txftlr;
+       u32 suspend_rxftlr;
+       u32 suspend_imr;
+       u32 suspend_dmacr;
+       u32 suspend_dmatdlr;
+       u32 suspend_dmardlr;
+       u32 suspend_div0_level;
+       u32 suspend_div3_level;
+       u32 cpr_peri_div_sel;
+       u32 cpr_peri_ctrl;
+       u32 cpr_peri_clk_sel;
+
+       u32 state;
 };
 
 #endif /* _LIGHT_I2S_H */
index 76b6955ada3985d650767ffe953ce89973e48d9f..2e839c6ff8bc7dddc5af23925ee44d38ea475de1 100644 (file)
@@ -45,24 +45,20 @@ static int light_spdif_dai_probe(struct snd_soc_dai *dai)
 static int light_spdif_dai_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
-    struct light_spdif_priv *priv = snd_soc_dai_get_drvdata(dai);
+    //struct light_spdif_priv *priv = snd_soc_dai_get_drvdata(dai);
 
     pm_runtime_get_sync(dai->dev);
-    regmap_update_bits(priv->regmap, SPDIF_EN,
-                SPDIF_SPDIFEN_MSK, SPDIF_SPDIFEN_SEL(1));
-    atomic_inc(&priv->spdif_ref_cnt);
 
        return 0;
 }
 
 static void light_spdif_snd_txctrl(struct light_spdif_priv *priv, char on)
 {
-    unsigned int reg[24];
-    int i;
     regmap_update_bits(priv->regmap, SPDIF_TX_DMA_EN,
             SPDIF_TDMA_EN_MSK, SPDIF_TDMA_EN_SEL(on));
     regmap_update_bits(priv->regmap, SPDIF_TX_EN,
             SPDIF_TXEN_MSK, SPDIF_TXEN_SEL(on));
+    regmap_read(priv->regmap, SPDIF_TX_FIFO_TH, &priv->suspend_tx_fifo_th);
 
     return;
 }
@@ -83,16 +79,12 @@ static void light_spdif_dai_shutdown(struct snd_pcm_substream *substream,
 {
        struct light_spdif_priv *priv = snd_soc_dai_get_drvdata(dai);
 
-    if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+    if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
            light_spdif_snd_rxctrl(priv, 0);
-    else
+    } else {
         light_spdif_snd_txctrl(priv, 0);
-    
-    atomic_dec(&priv->spdif_ref_cnt);
-    if (atomic_read(&priv->spdif_ref_cnt) == 0) {
-        regmap_update_bits(priv->regmap, SPDIF_EN,
-                    SPDIF_SPDIFEN_MSK, SPDIF_SPDIFEN_SEL(0));
     }
+
        pm_runtime_put(dai->dev);
 }
 
@@ -106,24 +98,38 @@ static int light_spdif_dai_trigger(struct snd_pcm_substream *substream, int cmd,
         case SNDRV_PCM_TRIGGER_START:
         case SNDRV_PCM_TRIGGER_RESUME:
         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-            if (tx)
+            if (tx) {
                 light_spdif_snd_txctrl(priv, 1);
-            else
+                priv->state |= SPDIF_STATE_TX_RUNNING;
+            }
+            else {
                 light_spdif_snd_rxctrl(priv, 1);
+                priv->state |= SPDIF_STATE_RX_RUNNING;
+            }
             break;
         case SNDRV_PCM_TRIGGER_STOP:
-        case SNDRV_PCM_TRIGGER_SUSPEND:
         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
             if (tx) {
                 dmaengine_terminate_async(snd_dmaengine_pcm_get_chan(substream));  // work around for DMAC stop issue
                 light_spdif_snd_txctrl(priv, 0);
+                priv->state &= ~SPDIF_STATE_TX_RUNNING;
+            }
+            else {
+                light_spdif_snd_rxctrl(priv, 0);
+                priv->state &= ~SPDIF_STATE_RX_RUNNING;
+            }
+            break;
+        case SNDRV_PCM_TRIGGER_SUSPEND:
+            if (tx) {
+                dmaengine_pause(snd_dmaengine_pcm_get_chan(substream));  // work around for DMAC stop issue
+                light_spdif_snd_txctrl(priv, 0);
             }
             else
                 light_spdif_snd_rxctrl(priv, 0);
             break;
         default:
             return -EINVAL;
-    }    
+    }
 
     return ret;
 }
@@ -138,7 +144,7 @@ static int light_spdif_dai_hw_params(struct snd_pcm_substream *substream, struct
     u32 datawidth, chn_num, i;
     u32 sample_rate = params_rate(params);
     bool is_divclk1 = false; //audio_divclk1 for 44.1k...etc. audio_divclk0 for 48k....etc
-    u32 src_clk, tx_div, rx_div;
+    u32 src_clk, tx_div;
 
     switch (params_channels(params)) {
         case 1:
@@ -183,9 +189,6 @@ static int light_spdif_dai_hw_params(struct snd_pcm_substream *substream, struct
                         CPR_PERI_CLK_SEL_REG, CPR_SPDIF_SRC_SEL_MSK, CPR_SPDIF_SRC_SEL(1));
         src_clk = AUDIO_DIVCLK1;
     }
-    //printk("selected src_clk=%d substream->stream=%d sample_rate=%d\n", src_clk, substream->stream, sample_rate);
-    regmap_update_bits(priv->regmap, SPDIF_EN,
-            SPDIF_SPDIFEN_MSK, SPDIF_SPDIFEN_SEL(1));
 
     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
         tx_div = src_clk / (sample_rate * (32 * 2) * 2);
@@ -253,6 +256,7 @@ static const struct snd_soc_component_driver light_spdif_soc_component = {
        .name           = "light_spdif",
 };
 
+#if 0
 static void light_spdif_pinctrl(struct light_spdif_priv *priv)
 {
     //GPIO_SEL      AUDIO_PA21~24
@@ -266,41 +270,135 @@ static void light_spdif_pinctrl(struct light_spdif_priv *priv)
                                0x38,0xF000F, 0x70007); // AUDIO_PA22   SPDIF0_DOUT / AUDIO_PA23        SPDIF1_DOUT
     return;
 }
+#endif
 
 static const struct regmap_config light_spdif_regmap_config = {
         .reg_bits = 32,
         .reg_stride = 4,
         .val_bits = 32,
         .max_register = SPDIF_RX_USER_B5,
-        .cache_type = REGCACHE_FLAT,
+        .cache_type = REGCACHE_NONE,
 };
 
 static int light_spdif_runtime_suspend(struct device *dev)
 {
-        struct light_spdif_priv *priv = dev_get_drvdata(dev);
+    struct light_spdif_priv *priv = dev_get_drvdata(dev);
 
-        regcache_cache_only(priv->regmap, true);
-        clk_disable_unprepare(priv->clk);
+    regmap_update_bits(priv->regmap, SPDIF_EN,
+                SPDIF_SPDIFEN_MSK, SPDIF_SPDIFEN_SEL(0));
+
+    if (priv->id == 0) {
+        regmap_update_bits(priv->audio_cpr_regmap,
+                                CPR_IP_RST_REG, CPR_SPDIF0_SRST_N_SEL_MSK, CPR_SPDIF0_SRST_N_SEL(0));
+    } else if (priv->id == 1) {
+        regmap_update_bits(priv->audio_cpr_regmap,
+                                CPR_IP_RST_REG, CPR_SPDIF1_SRST_N_SEL_MSK, CPR_SPDIF1_SRST_N_SEL(0));
+    }
+
+    clk_disable_unprepare(priv->clk);
 
        return 0;
 }
 
 static int light_spdif_runtime_resume(struct device *dev)
 {
-        struct light_spdif_priv *priv = dev_get_drvdata(dev);
-        int ret;
+    struct light_spdif_priv *priv = dev_get_drvdata(dev);
+    int ret;
 
-        ret = clk_prepare_enable(priv->clk);
-        if (ret) {
-                dev_err(priv->dev, "clock enable failed %d\n", ret);
-                return ret;
-        }
+    ret = clk_prepare_enable(priv->clk);
+    if (ret) {
+            dev_err(priv->dev, "clock enable failed %d\n", ret);
+            return ret;
+    }
+    
+    if (priv->id == 0) {
+        regmap_update_bits(priv->audio_cpr_regmap,
+                                CPR_IP_RST_REG, CPR_SPDIF0_SRST_N_SEL_MSK, CPR_SPDIF0_SRST_N_SEL(1));
+    } else if (priv->id == 1) {
+        regmap_update_bits(priv->audio_cpr_regmap,
+                                CPR_IP_RST_REG, CPR_SPDIF1_SRST_N_SEL_MSK, CPR_SPDIF1_SRST_N_SEL(1));
+    }
 
-        regcache_cache_only(priv->regmap, false);
+    regmap_update_bits(priv->regmap, SPDIF_EN,
+        SPDIF_SPDIFEN_MSK, SPDIF_SPDIFEN_SEL(1));
 
-        return ret;
+    return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int light_spdif_suspend(struct device *dev)
+{
+    struct light_spdif_priv *priv = dev_get_drvdata(dev);
+
+       if (priv->state == SPDIF_STATE_IDLE)
+               return 0;
+
+    regmap_read(priv->regmap, SPDIF_TX_EN, &priv->suspend_tx_en);
+    regmap_read(priv->regmap, SPDIF_TX_CTL, &priv->suspend_tx_ctl);
+    regmap_read(priv->regmap, SPDIF_TX_DMA_EN, &priv->suspend_tx_dma_en);
+    regmap_read(priv->regmap, SPDIF_RX_EN, &priv->suspend_rx_en);
+    regmap_read(priv->regmap, SPDIF_RX_CTL, &priv->suspend_rx_ctl);
+    regmap_read(priv->regmap, SPDIF_RX_DMA_EN, &priv->suspend_rx_dma_en);
+    regmap_read(priv->audio_cpr_regmap, CPR_PERI_DIV_SEL_REG, &priv->cpr_peri_div_sel);
+    regmap_read(priv->audio_cpr_regmap, CPR_PERI_CTRL_REG, &priv->cpr_peri_ctrl);
+    regmap_read(priv->audio_cpr_regmap, CPR_PERI_CLK_SEL_REG, &priv->cpr_peri_clk_sel);
+
+    regmap_update_bits(priv->regmap, SPDIF_EN,
+                SPDIF_SPDIFEN_MSK, SPDIF_SPDIFEN_SEL(0));
+
+    if (priv->id == 0) {
+        regmap_update_bits(priv->audio_cpr_regmap,
+                                CPR_IP_RST_REG, CPR_SPDIF0_SRST_N_SEL_MSK, CPR_SPDIF0_SRST_N_SEL(0));
+    } else if (priv->id == 1) {
+        regmap_update_bits(priv->audio_cpr_regmap,
+                                CPR_IP_RST_REG, CPR_SPDIF1_SRST_N_SEL_MSK, CPR_SPDIF1_SRST_N_SEL(0));
+    }
+
+    clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static int light_spdif_resume(struct device *dev)
+{
+    struct light_spdif_priv *priv = dev_get_drvdata(dev);
+    int ret;
+
+       if (priv->state == SPDIF_STATE_IDLE)
+               return 0;
+
+    ret = clk_prepare_enable(priv->clk);
+    if (ret) {
+            dev_err(priv->dev, "clock enable failed %d\n", ret);
+            return ret;
+    }
+
+    regmap_write(priv->audio_cpr_regmap, CPR_PERI_DIV_SEL_REG, priv->cpr_peri_div_sel);
+    regmap_write(priv->audio_cpr_regmap, CPR_PERI_CTRL_REG, priv->cpr_peri_ctrl);
+    regmap_write(priv->audio_cpr_regmap, CPR_PERI_CLK_SEL_REG, priv->cpr_peri_clk_sel);
+
+    if (priv->id == 0) {
+        regmap_update_bits(priv->audio_cpr_regmap,
+                                CPR_IP_RST_REG, CPR_SPDIF0_SRST_N_SEL_MSK, CPR_SPDIF0_SRST_N_SEL(1));
+    } else if (priv->id == 1) {
+        regmap_update_bits(priv->audio_cpr_regmap,
+                                CPR_IP_RST_REG, CPR_SPDIF1_SRST_N_SEL_MSK, CPR_SPDIF1_SRST_N_SEL(1));
+    }
+
+    regmap_update_bits(priv->regmap, SPDIF_EN,
+        SPDIF_SPDIFEN_MSK, SPDIF_SPDIFEN_SEL(1));
+
+    regmap_write(priv->regmap, SPDIF_TX_EN, priv->suspend_tx_en);
+    regmap_write(priv->regmap, SPDIF_TX_CTL, priv->suspend_tx_ctl);
+    regmap_write(priv->regmap, SPDIF_TX_DMA_EN, priv->suspend_tx_dma_en);
+    regmap_write(priv->regmap, SPDIF_RX_EN, priv->suspend_rx_en);
+    regmap_write(priv->regmap, SPDIF_RX_CTL, priv->suspend_rx_ctl);
+    regmap_write(priv->regmap, SPDIF_RX_DMA_EN, priv->suspend_rx_dma_en);
+
+    return ret;
+}
+#endif
+
 static const struct of_device_id light_spdif_of_match[] = {
        { .compatible = "light,light-spdif"},
        {},
@@ -309,20 +407,18 @@ MODULE_DEVICE_TABLE(of, light_spdif_of_match);
 
 irqreturn_t spdif_interrupt(int irq, void* dev_id)
 {
-    struct light_spdif_priv* priv = (struct light_spdif_priv*)dev_id;
+    //struct light_spdif_priv* priv = (struct light_spdif_priv*)dev_id;
     return IRQ_HANDLED;
 }
 
 static int light_spdif_probe(struct platform_device *pdev)
 {
     struct device_node *np = pdev->dev.of_node;
-    const char *sprop;
     const uint32_t *iprop;
     struct light_spdif_priv *priv;
     struct resource *res;
     struct device *dev = &pdev->dev;
-    unsigned int irq;
-    int data_register, ret = 0;
+    int ret = 0;
 
     priv = devm_kzalloc(&pdev->dev, sizeof(struct light_spdif_priv), GFP_KERNEL);
     if (!priv) {
@@ -337,6 +433,10 @@ static int light_spdif_probe(struct platform_device *pdev)
        else
                priv->dma_maxburst = 8;
 
+    iprop = of_get_property(np, "id", NULL);
+       if (iprop)
+               priv->id = be32_to_cpup(iprop);
+
     dev_set_drvdata(dev, priv);
 
     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -370,6 +470,11 @@ static int light_spdif_probe(struct platform_device *pdev)
         dev_err(dev, "cannot find regmap for audio cpr register\n");
         return -EINVAL;
     }
+
+    pm_runtime_enable(&pdev->dev);
+    pm_runtime_resume_and_get(&pdev->dev); // clk gate is enabled by hardware as default register value
+    pm_runtime_put_sync(&pdev->dev);
+
     //AUDIO_DIV1 set to 1/6. 812.8512MHz / 6 = 135.4752MHz
     regmap_update_bits(priv->audio_cpr_regmap,
                             CPR_PERI_DIV_SEL_REG, CPR_AUDIO_DIV1_SEL_MSK, CPR_AUDIO_DIV1_SEL(5));
@@ -384,10 +489,6 @@ static int light_spdif_probe(struct platform_device *pdev)
     regmap_update_bits(priv->audio_cpr_regmap,
                             CPR_PERI_CTRL_REG, CPR_SPDIF_SYNC_MSK, CPR_SPDIF_SYNC_EN);
 
-    pm_runtime_enable(&pdev->dev);
-    pm_runtime_resume_and_get(&pdev->dev); // clk gate is enabled by hardware as default register value
-    pm_runtime_put_sync(&pdev->dev);
-
     priv->irq = platform_get_irq(pdev, 0);
     if (priv->irq== 0) {
         dev_err(dev, "could not map IRQ.\n");
@@ -435,6 +536,8 @@ static int light_spdif_remove(struct platform_device *pdev)
 
 static const struct dev_pm_ops light_spdif_pm_ops = {
     SET_RUNTIME_PM_OPS(light_spdif_runtime_suspend, light_spdif_runtime_resume, NULL)
+    SET_SYSTEM_SLEEP_PM_OPS(light_spdif_suspend,
+                                    light_spdif_resume)
 };
 
 static struct platform_driver light_spdif_driver = {
index c390ad85f0acaf5883ab1c48f6dc837bb9592c56..35982ce869d9a8c891ecd683c6bb98eb98d2f42d 100644 (file)
 #define CPR_PERI_CLK_SEL_REG  0x008 /*audio sys clock selection Register*/
 #define CPR_PERI_CTRL_REG  0x00C /*Peripheral control signal configuration register*/
 #define CPR_IP_CG_REG   0x010 /* ip clock gate register */
+#define CPR_IP_RST_REG  0x014
 
 /* AUDIO SYS DIV SEL REG, offset: 0x4 */
 #define CPR_AUDIO_DIV1_CG_POS                     (17U)
 #define CPR_SPDIF1_CG_SEL_POS                      (24U)
 #define CPR_SPDIF1_CG_SEL_MSK                       (0x1U << CPR_SPDIF1_CG_SEL_POS)
 #define CPR_SPDIF1_CG_SEL(X)                            (X << CPR_SPDIF1_CG_SEL_POS)
-
+/* CPR_IP_RST_REG */
+#define CPR_SPDIF0_SRST_N_SEL_POS                      (23U)
+#define CPR_SPDIF0_SRST_N_SEL_MSK                       (0x1U << CPR_SPDIF0_SRST_N_SEL_POS)
+#define CPR_SPDIF0_SRST_N_SEL(X)                            (X << CPR_SPDIF0_SRST_N_SEL_POS)
+#define CPR_SPDIF1_SRST_N_SEL_POS                      (24U)
+#define CPR_SPDIF1_SRST_N_SEL_MSK                       (0x1U << CPR_SPDIF1_SRST_N_SEL_POS)
+#define CPR_SPDIF1_SRST_N_SEL(X)                            (X << CPR_SPDIF1_SRST_N_SEL_POS)
 
 #define LIGHT_TDM_DMABUF_SIZE     (64 * 1024)
+#define SPDIF_STATE_IDLE                               0
+#define SPDIF_STATE_TX_RUNNING     1
+#define SPDIF_STATE_RX_RUNNING     2
 
 struct light_spdif_priv {
     void __iomem *regs;
@@ -202,7 +212,24 @@ struct light_spdif_priv {
     u32 dma_maxburst;
     struct device *dev; 
     unsigned int irq;
-    atomic_t spdif_ref_cnt;
+    u32 suspend_tx_en;
+    u32 suspend_tx_ctl;
+    u32 suspend_tx_fifo_th;
+    u32 suspend_tx_fifo_dl;
+    u32 suspend_tx_dma_en;
+    u32 suspend_tx_dma_th;
+    u32 suspend_spdif_imr;
+    u32 suspend_rx_en;
+    u32 suspend_rx_ctl;
+    u32 suspend_rx_fifo_th;
+    u32 suspend_rx_fifo_dl;
+    u32 suspend_rx_dma_en;
+    u32 suspend_rx_dma_th;
+    u32 cpr_peri_div_sel;
+    u32 cpr_peri_ctrl;
+    u32 cpr_peri_clk_sel;
+    u32 id;
+    u32 state;
 };
 
 
index 959307d3e7235e1d9ec69844e2962797aefc6e14..50efdaf620f9dd2fbe5b6b7fd8d917ba563dc2b0 100644 (file)
@@ -17,6 +17,7 @@
 #include <sound/pcm_params.h>
 #include <sound/sh_fsi.h>
 #include "light-pcm.h"
+#include "light-audio-cpr.h"
 #include "light-tdm.h"
 #include <linux/dmaengine.h>
 #include <linux/regmap.h>
@@ -57,6 +58,9 @@ static int light_tdm_dai_startup(struct snd_pcm_substream *substream,
 
 static void light_tdm_snd_rxctrl(struct light_tdm_priv *priv, char on)
 {
+    u32 dmactl;
+    u32 tdmen;
+
     if (priv->slot_num != 1) {
         return;
     }
@@ -65,8 +69,6 @@ static void light_tdm_snd_rxctrl(struct light_tdm_priv *priv, char on)
             DMACTL_DMAEN_MSK, DMACTL_DMAEN_SEL(on));
     regmap_update_bits(priv->regmap, TDM_TDMEN,
             TDMCTL_TDMEN_MSK, TDMCTL_TDMEN_SEL(on));
-    u32 dmactl;
-    u32 tdmen;
     regmap_read(priv->regmap, TDM_DMACTL, &dmactl);
     regmap_read(priv->regmap, TDM_TDMEN, &tdmen);
     //printk("%s TDM_DMACTL=0x%x TDM_TDMEN=0x%x\n", __func__, dmactl, tdmen);
@@ -99,10 +101,12 @@ static int light_tdm_dai_trigger(struct snd_pcm_substream *substream, int cmd, s
         case SNDRV_PCM_TRIGGER_RESUME:
         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
             light_tdm_snd_rxctrl(priv, 1);
+            priv->state = TDM_STATE_RUNNING;
             break;
         case SNDRV_PCM_TRIGGER_STOP:
-        case SNDRV_PCM_TRIGGER_SUSPEND:
         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+            priv->state = TDM_STATE_IDLE;
+        case SNDRV_PCM_TRIGGER_SUSPEND:
             light_tdm_snd_rxctrl(priv, 0);
             break;
         default:
@@ -115,11 +119,12 @@ static int light_tdm_dai_trigger(struct snd_pcm_substream *substream, int cmd, s
 static int light_tdm_set_fmt_dai(struct snd_soc_dai *dai, unsigned int fmt)
 {
     struct light_tdm_priv *priv = snd_soc_dai_get_drvdata(dai);
-    u32 cnfin = 0;
 
     if (priv->slot_num != 1) {
         return 0;
     }
+
+    pm_runtime_resume_and_get(priv->dev);
     
     switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
         case SND_SOC_DAIFMT_DSP_B:
@@ -137,10 +142,8 @@ static int light_tdm_set_fmt_dai(struct snd_soc_dai *dai, unsigned int fmt)
                        TDMCTL_SPEDGE_SEL(1));
     regmap_update_bits(priv->regmap, TDM_DMADL,
             DMACTL_DMADL_MSK, DMACTL_DMADL_SEL(0));
-    u32 tdmctl;
-    u32 dmadl;
-    regmap_read(priv->regmap, TDM_TDMCTL, &tdmctl);
-    regmap_read(priv->regmap, TDM_DMADL, &dmadl);
+
+    pm_runtime_put_sync(priv->dev);
 
     return 0;
 }
@@ -198,6 +201,7 @@ static int light_tdm_dai_hw_params(struct snd_pcm_substream *substream, struct s
 {
     struct light_tdm_priv *priv =  snd_soc_dai_get_drvdata(dai);
     u32 datawth, chn_num;
+    u32 tdmctl;
 
     if ( params_channels(params) != 1) {
         pr_err("Not support channel num\n");
@@ -244,8 +248,7 @@ static int light_tdm_dai_hw_params(struct snd_pcm_substream *substream, struct s
        regmap_update_bits(priv->regmap, TDM_TDMCTL,
                        TDMCTL_DATAWTH_MSK, TDMCTL_DATAWTH_SEL(datawth));
        regmap_update_bits(priv->regmap, TDM_TDMCTL,
-                       TDMCTL_CHNUM_MSK, TDMCTL_CHNUM_SEL(chn_num));   
-    u32 tdmctl;
+                       TDMCTL_CHNUM_MSK, TDMCTL_CHNUM_SEL(chn_num));
     regmap_read(priv->regmap, TDM_TDMCTL, &tdmctl);
     //printk("%s TDM_TDMCTL=0x%x\n", __func__, tdmctl); 
     light_tdm_set_div(priv, params);
@@ -277,6 +280,7 @@ static const struct snd_soc_component_driver light_tdm_soc_component = {
        .name           = "light_tdm",
 };
 
+#if 0
 static void light_tdm_pinctrl(struct light_tdm_priv *tdm_priv)
 {
     //GPIO_SEL
@@ -292,49 +296,130 @@ static void light_tdm_pinctrl(struct light_tdm_priv *tdm_priv)
                                0x40,0xF0000, 0x70000); // AUDIO_PA27
     return;
 }
+#endif
+
+static bool light_tdm_wr_reg(struct device *dev, unsigned int reg)
+{
+    return true;
+}
+
+static bool light_tdm_rd_reg(struct device *dev, unsigned int reg)
+{
+       return true;
+}
 
 static const struct regmap_config light_tdm_regmap_config = {
         .reg_bits = 32,
         .reg_stride = 4,
         .val_bits = 32,
         .max_register = TDM_DIV0_LEVEL,
-        .cache_type = REGCACHE_FLAT,
+        .writeable_reg = light_tdm_wr_reg,
+        .readable_reg = light_tdm_rd_reg,
+        .cache_type = REGCACHE_NONE,
 };
 
 static int light_tdm_runtime_suspend(struct device *dev)
 {
-        struct light_tdm_priv *priv = dev_get_drvdata(dev);
+    struct light_tdm_priv *priv = dev_get_drvdata(dev);
 
-        if (priv->slot_num != 1) {
-            return 0;
-        }
+    if (priv->slot_num != 1) {
+        return 0;
+    }
 
-        regcache_cache_only(priv->regmap, true);
-        clk_disable_unprepare(priv->clk);
+    clk_disable_unprepare(priv->clk);
 
        return 0;
 }
 
 static int light_tdm_runtime_resume(struct device *dev)
 {
-        struct light_tdm_priv *priv = dev_get_drvdata(dev);
-        int ret;
+    struct light_tdm_priv *priv = dev_get_drvdata(dev);
+    int ret;
 
-        if (priv->slot_num != 1) {
-            return 0;
-        }
+    if (priv->slot_num != 1) {
+        return 0;
+    }
 
-        ret = clk_prepare_enable(priv->clk);
-        if (ret) {
-                dev_err(priv->dev, "clock enable failed %d\n", ret);
-                return ret;
-        }
+    ret = clk_prepare_enable(priv->clk);
+    if (ret) {
+            dev_err(priv->dev, "clock enable failed %d\n", ret);
+            return ret;
+    }
 
-        regcache_cache_only(priv->regmap, false);
+    return ret;
+}
 
-        return ret;
+#ifdef CONFIG_PM_SLEEP
+static int light_tdm_suspend(struct device *dev)
+{
+    struct light_tdm_priv *priv = dev_get_drvdata(dev);
+
+    if (priv->slot_num != 1  || priv->state == TDM_STATE_IDLE) {
+        return 0;
+    }
+
+       regmap_read(priv->regmap, TDM_TDMCTL, &priv->suspend_tdmctl);
+       regmap_read(priv->regmap, TDM_CHOFFSET1, &priv->suspend_choffset1);
+       regmap_read(priv->regmap, TDM_CHOFFSET2, &priv->suspend_choffset2);
+       regmap_read(priv->regmap, TDM_CHOFFSET3, &priv->suspend_choffset3);
+       regmap_read(priv->regmap, TDM_CHOFFSET4, &priv->suspend_choffset4);
+       regmap_read(priv->regmap, TDM_FIFOTL1, &priv->suspend_fifotl1);
+       regmap_read(priv->regmap, TDM_FIFOTL2, &priv->suspend_fifotl2);
+       regmap_read(priv->regmap, TDM_FIFOTL3, &priv->suspend_fifotl3);
+       regmap_read(priv->regmap, TDM_FIFOTL4, &priv->suspend_fifotl4);
+       regmap_read(priv->regmap, TDM_IMR, &priv->suspend_imr);
+       regmap_read(priv->regmap, TDM_DMADL, &priv->suspend_dmadl);
+       regmap_read(priv->regmap, TDM_DIV0_LEVEL, &priv->suspend_div0level);
+
+    regmap_read(priv->audio_cpr_regmap, CPR_PERI_DIV_SEL_REG, &priv->cpr_peri_div_sel);
+    regmap_read(priv->audio_cpr_regmap, CPR_PERI_CLK_SEL_REG, &priv->cpr_peri_clk_sel);
+       
+       regmap_update_bits(priv->audio_cpr_regmap,
+                                                       CPR_IP_RST_REG, CPR_TDM_SRST_N_SEL_MSK, CPR_TDM_SRST_N_SEL(0));
+
+    clk_disable_unprepare(priv->clk);
+
+       return 0;
 }
 
+static int light_tdm_resume(struct device *dev)
+{
+    struct light_tdm_priv *priv = dev_get_drvdata(dev);
+    int ret;
+
+    if (priv->slot_num != 1  || priv->state == TDM_STATE_IDLE) {
+        return 0;
+    }
+
+    ret = clk_prepare_enable(priv->clk);
+    if (ret) {
+            dev_err(priv->dev, "clock enable failed %d\n", ret);
+            return ret;
+    }
+
+    regmap_write(priv->audio_cpr_regmap, CPR_PERI_DIV_SEL_REG, priv->cpr_peri_div_sel);
+    regmap_write(priv->audio_cpr_regmap, CPR_PERI_CLK_SEL_REG, priv->cpr_peri_clk_sel);
+
+    regmap_update_bits(priv->audio_cpr_regmap,
+                        CPR_IP_RST_REG, CPR_TDM_SRST_N_SEL_MSK, CPR_TDM_SRST_N_SEL(1));
+
+       regmap_write(priv->regmap, TDM_TDMCTL, priv->suspend_tdmctl);
+       regmap_write(priv->regmap, TDM_CHOFFSET1, priv->suspend_choffset1);
+       regmap_write(priv->regmap, TDM_CHOFFSET2, priv->suspend_choffset2);
+       regmap_write(priv->regmap, TDM_CHOFFSET3, priv->suspend_choffset3);
+       regmap_write(priv->regmap, TDM_CHOFFSET4, priv->suspend_choffset4);
+       regmap_write(priv->regmap, TDM_FIFOTL1, priv->suspend_fifotl1);
+       regmap_write(priv->regmap, TDM_FIFOTL2, priv->suspend_fifotl2);
+       regmap_write(priv->regmap, TDM_FIFOTL3, priv->suspend_fifotl3);
+       regmap_write(priv->regmap, TDM_FIFOTL4, priv->suspend_fifotl4);
+       regmap_write(priv->regmap, TDM_IMR, priv->suspend_imr);
+       regmap_write(priv->regmap, TDM_DMADL, priv->suspend_dmadl);
+       regmap_write(priv->regmap, TDM_DIV0_LEVEL, priv->suspend_div0level);
+
+    return ret;
+}
+#endif
+
 static const struct of_device_id light_tdm_of_match[] = {
        { .compatible = "light,light-tdm"},
        {},
@@ -343,7 +428,7 @@ MODULE_DEVICE_TABLE(of, light_tdm_of_match);
 
 irqreturn_t tdm_interrupt(int irq, void* dev_id)
 {
-    struct light_tdm_priv* priv = (struct light_tdm_priv*)dev_id;
+    //struct light_tdm_priv* priv = (struct light_tdm_priv*)dev_id;
     return IRQ_HANDLED;
 }
 
@@ -355,7 +440,7 @@ static int light_tdm_probe(struct platform_device *pdev)
     struct light_tdm_priv *tdm_priv;
     struct resource *res;
     struct device *dev = &pdev->dev;
-    unsigned int irq;
+
     int data_register, ret = 0;
 
     tdm_priv = devm_kzalloc(&pdev->dev, sizeof(struct light_tdm_priv), GFP_KERNEL);
@@ -529,6 +614,8 @@ static int light_tdm_remove(struct platform_device *pdev)
 
 static const struct dev_pm_ops light_tdm_pm_ops = {
     SET_RUNTIME_PM_OPS(light_tdm_runtime_suspend, light_tdm_runtime_resume, NULL)
+    SET_SYSTEM_SLEEP_PM_OPS(light_tdm_suspend,
+                    light_tdm_resume)
 };
 
 static struct platform_driver light_tdm_driver = {
index 565cf87aeefc6f017dee3fa724c56d5ab77d50ea..5c221f61ab06bf341e8e573fedd0908f557eea70 100644 (file)
 #define TDM_RDR4       0x60
 #define TDM_DIV0_LEVEL 0x64
 
-#define CPR_PERI_DIV_SEL_REG  0x004 /*audio sys i2s clock div Register*/
-#define CPR_PERI_CLK_SEL_REG  0x008 /*audio sys i2s clock selection Register*/
-#define CPR_IP_CG_REG   0x010 /* ip clock gate register */
-
-/* AUDIO SYS DIV SEL REG, offset: 0x4 */
-#define CPR_AUDIO_DIV1_SEL_POS                     (12U)
-#define CPR_AUDIO_DIV1_SEL_MSK                     (0x1FU << CPR_AUDIO_DIV1_SEL_POS)
-#define CPR_AUDIO_DIV1_SEL(X)                      (X << CPR_AUDIO_DIV1_SEL_POS)
-#define CPR_AUDIO_DIV0_SEL_POS                  (4U)
-#define CPR_AUDIO_DIV0_SEL_MSK               (0x1FU << CPR_AUDIO_DIV0_SEL_POS)
-#define CPR_AUDIO_DIV0_SEL(X)                      (X << CPR_AUDIO_DIV0_SEL_POS)
-#define CPR_TDM_CG_SEL_POS                      (21U)
-#define CPR_TDM_CG_SEL_MSK                       (0x1U << CPR_TDM_CG_SEL_POS)
-#define CPR_TDM_CG_SEL(X)                            (X << CPR_TDM_CG_SEL_POS)
-#define CPR_TDM_SRC_SEL_POS                     (16U)
-#define CPR_TDM_SRC_SEL_MSK                     (0x3U << CPR_TDM_SRC_SEL_POS)
-#define CPR_TDM_SRC_SEL(X)                          (X << CPR_TDM_SRC_SEL_POS)
-
 #define TDM_MODE_MASTER 0x1
 #define TDM_MODE_SLAVE 0x0
 #define TDMCTL_MODE_POS                             (0U) 
 #define TDMCTL_TDMEN_SEL(X)                            (X << TDMCTL_TDMEN_POS)
 
 #define LIGHT_TDM_DMABUF_SIZE     (64 * 1024)
+#define TDM_STATE_IDLE                         0
+#define TDM_STATE_RUNNING      1
 
 struct light_tdm_priv {
     void __iomem *regs;
@@ -119,6 +103,21 @@ struct light_tdm_priv {
     char slots;
     char slot_num;
     unsigned int irq;
+    u32 suspend_tdmctl;
+    u32 suspend_choffset1;
+    u32 suspend_choffset2;
+    u32 suspend_choffset3;
+    u32 suspend_choffset4;
+    u32 suspend_fifotl1;
+    u32 suspend_fifotl2;
+    u32 suspend_fifotl3;
+    u32 suspend_fifotl4;
+    u32 suspend_imr;
+    u32 suspend_dmadl;
+    u32 suspend_div0level;
+       u32 cpr_peri_div_sel;
+       u32 cpr_peri_clk_sel;
+    u32 state;
 };
 
 
index 67d3f5aad016735f2cc3cafe727720be39ab74ea..ca55a7455d8e462dd92c0c7a3a33d4c2b5551035 100644 (file)
@@ -259,7 +259,7 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus
                                                if (!found_cmd_mmap)
                                                        found_cmd_mmap = !strcmp(bname + 1, cmd);
                                                if (!found_coreutils_mmap)
-                                                       found_coreutils_mmap = !strcmp(bname + 1, "coreutils");
+                                                       found_coreutils_mmap = !strcmp(bname + 1, "coreutils") + !strcmp(bname + 1, "sleep.coreutils");
                                                if (!found_libc_mmap)
                                                        found_libc_mmap = !strncmp(bname + 1, "libc", 4);
                                                if (!found_ld_mmap)