@ Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt:84 @ Example: }; }; -[1]. Documentation/devicetree/bindings/arm/idle-states.yaml +[1]. Documentation/devicetree/bindings/cpu/idle-states.yaml @ Documentation/devicetree/bindings/arm/psci.yaml:104 @ 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-": @ Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml:37 @ properties: - allwinner,sun8i-v3-ccu - allwinner,sun8i-v3s-ccu - allwinner,sun9i-a80-ccu + - allwinner,sun20i-d1-ccu + - allwinner,sun20i-d1-r-ccu - allwinner,sun50i-a64-ccu - allwinner,sun50i-a64-r-ccu - allwinner,sun50i-a100-ccu @ Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml:84 @ if: enum: - allwinner,sun8i-a83t-r-ccu - allwinner,sun8i-h3-r-ccu + - allwinner,sun20i-d1-r-ccu - allwinner,sun50i-a64-r-ccu - allwinner,sun50i-a100-r-ccu - allwinner,sun50i-h6-r-ccu @ Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-ccu.yaml:105 @ else: properties: compatible: enum: + - allwinner,sun20i-d1-ccu - allwinner,sun50i-a100-ccu - allwinner,sun50i-h6-ccu - allwinner,sun50i-h616-ccu @ Documentation/devicetree/bindings/cpu/idle-states.yaml:4 @ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/arm/idle-states.yaml# +$id: http://devicetree.org/schemas/cpu/idle-states.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: ARM idle states binding description +title: Idle states binding description maintainers: - Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> + - Anup Patel <anup.patel@wdc.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. + 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: @ Documentation/devicetree/bindings/cpu/idle-states.yaml:51 @ description: |+ 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. + =========================================== - 2 - idle-states definitions + 4 - idle-states definitions =========================================== Idle states are characterized for a specific system through a set of @ Documentation/devicetree/bindings/cpu/idle-states.yaml:234 @ description: |+ properties specification that is the subject of the following sections. =========================================== - 3 - idle-states node + 5 - idle-states node =========================================== - ARM processor idle states are defined within the idle-states node, which is + 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. @ Documentation/devicetree/bindings/cpu/idle-states.yaml:246 @ description: |+ just supports idle_standby, an idle-states node is not required. =========================================== - 4 - References + 6 - References =========================================== [1] ARM Linux Kernel documentation - CPUs bindings @ Documentation/devicetree/bindings/cpu/idle-states.yaml:261 @ description: |+ [4] ARM Architecture Reference Manuals http://infocenter.arm.com/help/index.jsp - [6] ARM Linux Kernel documentation - Booting AArch64 Linux + [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 @ Documentation/devicetree/bindings/cpu/idle-states.yaml:282 @ properties: 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 + node[5] that is responsible for setting up CPU idle management in the OS implementation. const: psci @ Documentation/devicetree/bindings/cpu/idle-states.yaml:294 @ patternProperties: 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. + 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 @ Documentation/devicetree/bindings/cpu/idle-states.yaml:304 @ patternProperties: properties: compatible: - const: arm,idle-state + 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: @ Documentation/devicetree/bindings/cpu/idle-states.yaml:366 @ patternProperties: description: A string used as a descriptive name for the idle state. + additionalProperties: false + required: - compatible - entry-latency-us @ Documentation/devicetree/bindings/cpu/idle-states.yaml:709 @ examples: }; }; + - | + // 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>; + }; + }; + }; + ... @ Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml:17 @ properties: enum: - allwinner,sun8i-h3-crypto - allwinner,sun8i-r40-crypto + - allwinner,sun20i-d1-crypto - allwinner,sun50i-a64-crypto - allwinner,sun50i-h5-crypto - allwinner,sun50i-h6-crypto @ Documentation/devicetree/bindings/crypto/allwinner,sun8i-ce.yaml:48 @ properties: if: properties: compatible: - const: allwinner,sun50i-h6-crypto + contains: + enum: + - allwinner,sun20i-d1-crypto + - allwinner,sun50i-h6-crypto then: properties: clocks: @ Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml:23 @ properties: compatible: oneOf: - - const: allwinner,sun50i-a64-dma - - const: allwinner,sun50i-a100-dma - - const: allwinner,sun50i-h6-dma + - enum: + - allwinner,sun20i-d1-dma + - allwinner,sun50i-a64-dma + - allwinner,sun50i-a100-dma + - allwinner,sun50i-h6-dma - items: - const: allwinner,sun8i-r40-dma - const: allwinner,sun50i-a64-dma @ Documentation/devicetree/bindings/dma/allwinner,sun50i-a64-dma.yaml:63 @ if: properties: compatible: enum: + - allwinner,sun20i-d1-dma - allwinner,sun50i-a100-dma - allwinner,sun50i-h6-dma @ Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml:18 @ description: properties: compatible: - const: allwinner,sun6i-a31-hwspinlock + oneOf: + - items: + - enum: + - allwinner,sun8i-a23-hwspinlock + - allwinner,sun8i-a33-hwspinlock + - allwinner,sun8i-a50-hwspinlock + - allwinner,sun8i-a83t-hwspinlock + - allwinner,sun8i-h3-hwspinlock + - allwinner,sun9i-a80-hwspinlock + - allwinner,sun20i-d1-hwspinlock + - allwinner,sun50i-a64-hwspinlock + - allwinner,sun50i-h6-hwspinlock + - allwinner,sun50i-r329-hwspinlock + - const: allwinner,sun6i-a31-hwspinlock + - const: allwinner,sun6i-a31-hwspinlock reg: maxItems: 1 @ Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml:43 @ properties: resets: maxItems: 1 + interrupts: + maxItems: 1 + required: - compatible - reg - clocks - resets + - interrupts additionalProperties: false examples: - | #include <dt-bindings/clock/sun8i-a23-a33-ccu.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/reset/sun8i-a23-a33-ccu.h> hwlock@1c18000 { @ Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml:66 @ examples: reg = <0x01c18000 0x1000>; clocks = <&ccu CLK_BUS_SPINLOCK>; resets = <&ccu RST_BUS_SPINLOCK>; + interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>; }; ... @ Documentation/devicetree/bindings/i2c/marvell,mv64xxx-i2c.yaml:24 @ properties: - enum: - allwinner,sun8i-a23-i2c - allwinner,sun8i-a83t-i2c + - allwinner,sun20i-d1-i2c - allwinner,sun50i-a64-i2c - allwinner,sun50i-a100-i2c - allwinner,sun50i-h6-i2c @ Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml:21 @ properties: - items: - const: allwinner,sun50i-a64-lradc - const: allwinner,sun8i-a83t-r-lradc + - const: allwinner,sun50i-r329-lradc + - items: + - const: allwinner,sun20i-d1-lradc + - const: allwinner,sun50i-r329-lradc reg: maxItems: 1 + clocks: + maxItems: 1 + + resets: + maxItems: 1 + interrupts: maxItems: 1 @ Documentation/devicetree/bindings/input/allwinner,sun4i-a10-lradc-keys.yaml:81 @ required: - interrupts - vref-supply +if: + properties: + compatible: + contains: + enum: + - allwinner,sun50i-r329-lradc + +then: + required: + - clocks + - resets + additionalProperties: false examples: @ Documentation/devicetree/bindings/interrupt-controller/allwinner,sun20i-d1-intc.yaml:4 @ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/allwinner,sun20i-d1-intc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner D1 Interrupt Controller Device Tree Binding + +maintainers: + - Samuel Holland <samuel@sholland.org> + - Chen-Yu Tsai <wens@csie.org> + - Maxime Ripard <mripard@kernel.org> + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + const: allwinner,sun20i-d1-intc + + reg: + maxItems: 1 + + '#address-cells': + const: 0 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + description: + The first cell is the IRQ number, and the second cell is the trigger type + as defined in interrupt.txt in this directory. + +required: + - compatible + - reg + - clocks + - resets + - interrupt-controller + - "#interrupt-cells" + +additionalProperties: false + +examples: + - | + intc: interrupt-controller@6010000 { + compatible = "allwinner,sun20i-d1-intc"; + reg = <0x6010000 0x100>; + #address-cells = <0>; + clocks = <&ccu 137>; + resets = <&ccu 65>; + interrupt-parent = <&plic>; + interrupt-controller; + #interrupt-cells = <2>; + }; + +... @ Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml:47 @ properties: compatible: items: - enum: + - allwinner,sun20i-d1-plic - sifive,fu540-c000-plic - canaan,k210-plic - const: sifive,plic-1.0.0 @ Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml:20 @ properties: The content of the cell is the master ID. compatible: - const: allwinner,sun50i-h6-iommu + enum: + - allwinner,sun20i-d1-iommu + - allwinner,sun50i-h6-iommu reg: maxItems: 1 @ Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml:42 @ required: - reg - interrupts - clocks - - resets + +if: + properties: + compatible: + contains: + enum: + - allwinner,sun50i-h6-iommu + +then: + required: + - resets additionalProperties: false @ Documentation/devicetree/bindings/leds/allwinner,sun50i-r329-ledc.yaml:4 @ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/allwinner,sun50i-r329-ledc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner R329 LED Controller Bindings + +maintainers: + - Samuel Holland <samuel@sholland.org> + +description: + The LED controller found in Allwinner sunxi SoCs uses a one-wire serial + interface to drive up to 1024 RGB LEDs. + +properties: + compatible: + oneOf: + - const: allwinner,sun50i-r329-ledc + - items: + - enum: + - allwinner,sun20i-d1-ledc + - const: allwinner,sun50i-r329-ledc + + reg: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + clocks: + items: + - description: Bus clock + - description: Module clock + + clock-names: + items: + - const: bus + - const: mod + + resets: + maxItems: 1 + + dmas: + maxItems: 1 + description: TX DMA channel + + dma-names: + const: tx + + interrupts: + maxItems: 1 + + allwinner,pixel-format: + description: Pixel format (subpixel transmission order), default is "grb" + enum: + - bgr + - brg + - gbr + - grb + - rbg + - rgb + + allwinner,t0h-ns: + maxItems: 1 + description: Length of high pulse when transmitting a "0" bit + + allwinner,t0l-ns: + maxItems: 1 + description: Length of low pulse when transmitting a "0" bit + + allwinner,t1h-ns: + maxItems: 1 + description: Length of high pulse when transmitting a "1" bit + + allwinner,t1l-ns: + maxItems: 1 + description: Length of low pulse when transmitting a "1" bit + + allwinner,treset-ns: + maxItems: 1 + description: Minimum delay between transmission frames + +patternProperties: + "^multi-led@[0-9a-f]+$": + type: object + $ref: leds-class-multicolor.yaml# + properties: + reg: + minimum: 0 + maximum: 1023 + description: Index of the LED in the series (must be contiguous) + + required: + - reg + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - dmas + - dma-names + - interrupts + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/leds/common.h> + + ledc: led-controller@2008000 { + compatible = "allwinner,sun20i-d1-ledc", + "allwinner,sun50i-r329-ledc"; + reg = <0x2008000 0x400>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu 12>, <&ccu 34>; + clock-names = "bus", "mod"; + resets = <&ccu 12>; + dmas = <&dma 42>; + dma-names = "tx"; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH>; + + multi-led@0 { + reg = <0x0>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_INDICATOR; + }; + }; + +... @ Documentation/devicetree/bindings/mailbox/allwinner,sun20i-d1-msgbox.yaml:4 @ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/allwinner,sun20i-d1-msgbox.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner sunxi Message Box v2 + +maintainers: + - Samuel Holland <samuel@sholland.org> + +properties: + compatible: + oneOf: + - const: allwinner,sun20i-d1-msgbox + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: bus clock + + resets: + maxItems: 1 + description: bus reset + + interrupts: + items: + - description: receive interrupt + - description: transmit interrupt + + interrupt-names: + items: + - const: "rx" + - const: "tx" + + '#mbox-cells': + const: 2 + description: first cell is the user/channel number, second is direction + +required: + - compatible + - reg + - clocks + - resets + - interrupts + - interrupt-names + - '#mbox-cells' + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/sun20i-d1-ccu.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/reset/sun20i-d1-ccu.h> + + msgbox: mailbox@3003000 { + compatible = "allwinner,sun20i-d1-msgbox"; + reg = <0x3003000 0x1000>; + clocks = <&ccu CLK_BUS_MSGBOX0>; + resets = <&ccu RST_BUS_MSGBOX0>; + interrupts = <101 IRQ_TYPE_LEVEL_HIGH>, + <102 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "rx", "tx"; + #mbox-cells = <2>; + }; + +... @ Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml:23 @ properties: - allwinner,sun8i-h3-video-engine - allwinner,sun8i-v3s-video-engine - allwinner,sun8i-r40-video-engine + - allwinner,sun20i-d1-video-engine - allwinner,sun50i-a64-video-engine - allwinner,sun50i-h5-video-engine - allwinner,sun50i-h6-video-engine @ Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml:27 @ properties: - const: allwinner,sun7i-a20-mmc - const: allwinner,sun8i-a83t-emmc - const: allwinner,sun9i-a80-mmc + - const: allwinner,sun20i-d1-mmc - const: allwinner,sun50i-a64-emmc - const: allwinner,sun50i-a64-mmc - const: allwinner,sun50i-a100-emmc @ Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml:53 @ properties: - items: - const: allwinner,sun50i-h6-mmc - const: allwinner,sun50i-a64-mmc + - items: + - const: allwinner,sun20i-d1-emmc + - const: allwinner,sun50i-a100-emmc - items: - const: allwinner,sun50i-h616-emmc - const: allwinner,sun50i-a100-emmc @ Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml:23 @ properties: - const: allwinner,sun7i-a20-sid - const: allwinner,sun8i-a83t-sid - const: allwinner,sun8i-h3-sid + - const: allwinner,sun20i-d1-sid - const: allwinner,sun50i-a64-sid - items: - - const: allwinner,sun50i-a100-sid + - enum: + - allwinner,sun20i-d1-sid + - allwinner,sun50i-a100-sid + - allwinner,sun50i-h5-sid - const: allwinner,sun50i-a64-sid - const: allwinner,sun50i-h5-sid - const: allwinner,sun50i-h6-sid @ Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml:49 @ properties: - allwinner,sun8i-v3s-pinctrl - allwinner,sun9i-a80-pinctrl - allwinner,sun9i-a80-r-pinctrl + - allwinner,sun20i-d1-pinctrl - allwinner,sun50i-a64-pinctrl - allwinner,sun50i-a64-r-pinctrl - allwinner,sun50i-a100-pinctrl @ Documentation/devicetree/bindings/pinctrl/allwinner,sun4i-a10-pinctrl.yaml:174 @ allOf: minItems: 7 maxItems: 7 + - if: + properties: + compatible: + enum: + - allwinner,sun20i-d1-pinctrl + + then: + properties: + interrupts: + minItems: 6 + maxItems: 6 + - if: properties: compatible: @ Documentation/devicetree/bindings/pwm/pwm-sun8i-v536.txt:4 @ +Allwinner sun8i-v536 SoC PWM controller + +Required properties: + - compatible: should be "allwinner,<name>-pwm" + "allwinner,sun8i-v833-pwm" + "allwinner,sun8i-v536-pwm" + "allwinner,sun50i-r818-pwm" + "allwinner,sun50i-a133-pwm" + "allwinner,sun50i-r329-pwm" + - reg: physical base address and length of the controller's registers + - #pwm-cells: should be 3. See pwm.txt in this directory for a description of + the cells format. + - clocks: From common clock binding, handle to the parent clock. + - resets: From reset clock binding, handle to the parent clock. + +Example: + + pwm: pwm@300a0000 { + compatible = "allwinner,sun50i-r818-pwm"; + reg = <0x0300a000 0x3ff>; + clocks = <&ccu CLK_BUS_PWM>; + resets = <&ccu RST_BUS_PWM>; + #pwm-cells = <3>; + }; @ Documentation/devicetree/bindings/riscv/cpus.yaml:41 @ properties: - sifive,u5 - sifive,u7 - canaan,k210 + - thead,c906 - const: riscv - items: - enum: @ Documentation/devicetree/bindings/riscv/cpus.yaml:103 @ 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 @ Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml:19 @ properties: compatible: oneOf: - - const: allwinner,sun6i-a31-rtc - - const: allwinner,sun8i-a23-rtc - - const: allwinner,sun8i-h3-rtc - - const: allwinner,sun8i-r40-rtc - - const: allwinner,sun8i-v3-rtc - - const: allwinner,sun50i-h5-rtc + - enum: + - allwinner,sun6i-a31-rtc + - allwinner,sun8i-a23-rtc + - allwinner,sun8i-h3-rtc + - allwinner,sun8i-r40-rtc + - allwinner,sun8i-v3-rtc + - allwinner,sun50i-h5-rtc + - allwinner,sun50i-h6-rtc + - allwinner,sun50i-h616-rtc + - allwinner,sun50i-r329-rtc - items: - const: allwinner,sun50i-a64-rtc - const: allwinner,sun8i-h3-rtc - - const: allwinner,sun50i-h6-rtc + - items: + - const: allwinner,sun20i-d1-rtc + - const: allwinner,sun50i-r329-rtc reg: maxItems: 1 @ Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml:46 @ properties: - description: RTC Alarm 1 clocks: - maxItems: 1 + minItems: 1 + maxItems: 4 + + clock-names: + minItems: 1 + maxItems: 4 clock-output-names: minItems: 1 @ Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml:99 @ allOf: enum: - allwinner,sun8i-h3-rtc - allwinner,sun50i-h5-rtc + - allwinner,sun50i-h6-rtc then: properties: @ Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml:111 @ allOf: properties: compatible: contains: - const: allwinner,sun50i-h6-rtc + const: allwinner,sun50i-h616-rtc then: properties: - clock-output-names: + clocks: minItems: 3 maxItems: 3 + items: + - description: Bus clock for register access + - description: 24 MHz oscillator + - description: 32 kHz clock from the CCU + + clock-names: + minItems: 3 + maxItems: 3 + items: + - const: bus + - const: hosc + - const: pll-32k + + required: + - clocks + - clock-names - if: properties: compatible: contains: - const: allwinner,sun8i-r40-rtc + const: allwinner,sun50i-r329-rtc + + then: + properties: + clocks: + minItems: 3 + maxItems: 4 + items: + - description: Bus clock for register access + - description: 24 MHz oscillator + - description: AHB parent for internal SPI clock + - description: External 32768 Hz oscillator + + clock-names: + minItems: 3 + maxItems: 4 + items: + - const: bus + - const: hosc + - const: ahb + - const: ext-osc32k + + required: + - clocks + - clock-names + + - if: + properties: + compatible: + contains: + enum: + - allwinner,sun8i-r40-rtc + - allwinner,sun50i-h616-rtc + - allwinner,sun50i-r329-rtc then: properties: @ Documentation/devicetree/bindings/rtc/allwinner,sun6i-a31-rtc.yaml:191 @ required: - compatible - reg - interrupts - - clock-output-names additionalProperties: false @ Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml:29 @ properties: - items: - const: allwinner,sun8i-v3-i2s - const: allwinner,sun8i-h3-i2s + - const: allwinner,sun20i-d1-i2s - const: allwinner,sun50i-a64-codec-i2s - items: - const: allwinner,sun50i-a64-i2s - const: allwinner,sun8i-h3-i2s - const: allwinner,sun50i-h6-i2s + - const: allwinner,sun50i-r329-i2s + - items: + - const: allwinner,sun20i-d1-i2s + - const: allwinner,sun50i-r329-i2s reg: maxItems: 1 @ Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml:73 @ allOf: - allwinner,sun6i-a31-i2s - allwinner,sun8i-a83t-i2s - allwinner,sun8i-h3-i2s + - allwinner,sun20i-d1-i2s - allwinner,sun50i-a64-codec-i2s - allwinner,sun50i-h6-i2s + - allwinner,sun50i-r329-i2s then: required: @ Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml:21 @ properties: compatible: oneOf: - - const: allwinner,sun4i-a10-spdif - - const: allwinner,sun6i-a31-spdif - - const: allwinner,sun8i-h3-spdif - - const: allwinner,sun50i-h6-spdif + - enum: + - allwinner,sun4i-a10-spdif + - allwinner,sun6i-a31-spdif + - allwinner,sun8i-h3-spdif + - allwinner,sun20i-d1-spdif + - allwinner,sun50i-h6-spdif - items: - const: allwinner,sun8i-a83t-spdif - const: allwinner,sun8i-h3-spdif @ Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml:41 @ properties: maxItems: 1 clocks: - items: - - description: Bus Clock - - description: Module Clock + minItems: 2 + maxItems: 3 clock-names: - items: - - const: apb - - const: spdif + minItems: 2 + maxItems: 3 # Even though it only applies to subschemas under the conditionals, # not listing them here will trigger a warning because of the @ Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml:57 @ properties: maxItems: 1 allOf: + - if: + properties: + compatible: + contains: + enum: + - allwinner,sun20i-d1-spdif + + then: + properties: + clocks: + items: + - description: Bus Clock + - description: RX Module Clock + - description: TX Module Clock + + clock-names: + items: + - const: apb + - const: rx + - const: tx + + else: + properties: + clocks: + items: + - description: Bus Clock + - description: Module Clock + + clock-names: + items: + - const: apb + - const: spdif + - if: properties: compatible: @ Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml:97 @ allOf: enum: - allwinner,sun6i-a31-spdif - allwinner,sun8i-h3-spdif + - allwinner,sun20i-d1-spdif + - allwinner,sun50i-h6-spdif then: required: @ Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml:27 @ properties: - items: - enum: - allwinner,sun8i-r40-spi + - allwinner,sun20i-d1-spi - allwinner,sun50i-h6-spi - allwinner,sun50i-h616-spi - const: allwinner,sun8i-h3-spi @ Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml:42 @ properties: - items: - const: allwinner,sun8i-r40-system-control - const: allwinner,sun4i-a10-system-control + - const: allwinner,sun20i-d1-system-control - const: allwinner,sun50i-a64-sram-controller deprecated: true - const: allwinner,sun50i-a64-system-control @ Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml:78 @ patternProperties: - const: allwinner,sun4i-a10-sram-a3-a4 - const: allwinner,sun4i-a10-sram-c1 - const: allwinner,sun4i-a10-sram-d + - const: allwinner,sun20i-d1-dsp-sram - const: allwinner,sun50i-a64-sram-c - items: - const: allwinner,sun5i-a13-sram-a3-a4 @ Documentation/devicetree/bindings/sram/allwinner,sun4i-a10-system-control.yaml:101 @ patternProperties: - items: - const: allwinner,sun8i-r40-sram-c1 - const: allwinner,sun4i-a10-sram-c1 + - items: + - const: allwinner,sun20i-d1-sram-c1 + - const: allwinner,sun4i-a10-sram-c1 - items: - const: allwinner,sun50i-a64-sram-c1 - const: allwinner,sun4i-a10-sram-c1 @ Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml:19 @ properties: - allwinner,sun8i-a83t-ths - allwinner,sun8i-h3-ths - allwinner,sun8i-r40-ths + - allwinner,sun20i-d1-ths - allwinner,sun50i-a64-ths - allwinner,sun50i-a100-ths - allwinner,sun50i-h5-ths @ Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml:59 @ properties: - 0 - 1 + vref-supply: + description: + Regulator for the analog reference voltage + allOf: - if: properties: @ Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml:92 @ allOf: properties: compatible: contains: - const: allwinner,sun8i-h3-ths + enum: + - allwinner,sun8i-h3-ths + - allwinner,sun20i-d1-ths then: properties: @ Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml:113 @ allOf: enum: - allwinner,sun8i-h3-ths - allwinner,sun8i-r40-ths + - allwinner,sun20i-d1-ths - allwinner,sun50i-a64-ths - allwinner,sun50i-a100-ths - allwinner,sun50i-h5-ths @ Documentation/devicetree/bindings/thermal/allwinner,sun8i-a83t-ths.yaml:125 @ allOf: - clock-names - resets + - if: + properties: + compatible: + contains: + enum: + - allwinner,sun20i-d1-ths + + then: + required: + - vref-supply + required: - compatible - reg @ Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml:21 @ properties: - items: - const: allwinner,sun6i-a31-hstimer - const: allwinner,sun7i-a20-hstimer + - const: allwinner,sun50i-h6-hstimer + description: one interrupt, different register layout + - items: + - enum: + - allwinner,sun20i-d1-hstimer + - allwinner,sun50i-h616-hstimer + - const: allwinner,sun50i-h6-hstimer reg: maxItems: 1 @ Documentation/devicetree/bindings/timer/allwinner,sun5i-a13-hstimer.yaml:55 @ required: if: properties: compatible: - const: allwinner,sun5i-a13-hstimer + contains: + enum: + - allwinner,sun5i-a13-hstimer + - allwinner,sun20i-d1-hstimer + - allwinner,sun50i-h616-hstimer then: properties: @ Documentation/devicetree/bindings/timer/riscv,aclint-mtimer.yaml:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/riscv,aclint-mtimer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RISC-V ACLINT M-level Timer + +maintainers: + - Anup Patel <anup.patel@wdc.com> + +description: + RISC-V SOCs include an implementation of the M-level timer (MTIMER) defined + in the RISC-V Advanced Core Local Interruptor (ACLINT) specification. The + ACLINT MTIMER device is documented in the RISC-V ACLINT specification found + at https://github.com/riscv/riscv-aclint/blob/main/riscv-aclint.adoc. + + The ACLINT MTIMER device directly connects to the M-level timer interrupt + lines of various HARTs (or CPUs) so the RISC-V per-HART (or per-CPU) local + interrupt controller is the parent interrupt controller for the ACLINT + MTIMER device. + + The clock frequency of ACLINT is specified via "timebase-frequency" DT + property of "/cpus" DT node. The "timebase-frequency" DT property is + described in Documentation/devicetree/bindings/riscv/cpus.yaml + +properties: + compatible: + items: + - enum: + - sifive,fu540-c000-aclint-mtimer + - const: riscv,aclint-mtimer + + description: + Should be "<vendor>,<chip>-aclint-mtimer" and "riscv,aclint-mtimer". + + reg: + description: | + Specifies base physical address(s) of the MTIME register and MTIMECMPx + registers. The 1st region is the MTIME register base and size. The 2nd + region is the MTIMECMPx registers base and size. + minItems: 2 + maxItems: 2 + + interrupts-extended: + minItems: 1 + maxItems: 4095 + +additionalProperties: false + +required: + - compatible + - reg + - interrupts-extended + +examples: + - | + timer@2004000 { + compatible = "sifive,fu540-c000-aclint-mtimer", "riscv,aclint-mtimer"; + reg = <0x200bff8 0x8>, + <0x2004000 0x7ff8>; + interrupts-extended = <&cpu1intc 7>, + <&cpu2intc 7>, + <&cpu3intc 7>, + <&cpu4intc 7>; + }; +... @ Documentation/devicetree/bindings/usb/allwinner,sun4i-a10-musb.yaml:23 @ properties: - items: - enum: - allwinner,sun8i-a83t-musb + - allwinner,sun20i-d1-musb - allwinner,sun50i-h6-musb - const: allwinner,sun8i-a33-musb - items: @ MAINTAINERS:788 @ L: linux-media@vger.kernel.org S: Maintained F: drivers/staging/media/sunxi/cedrus/ +ALLWINNER PWM DRIVER +M: Ban Tao <fengzheng923@gmail.com> +L: linux-pwm@vger.kernel.org +S: Maintained +F: drivers/pwm/pwm-sun8i-v536.c + ALPHA PORT M: Richard Henderson <rth@twiddle.net> M: Ivan Kokshaysky <ink@jurassic.park.msu.ru> @ MAINTAINERS:5007 @ 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.patel@wdc.com> +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 @ MAINTAINERS:16377 @ S: Maintained F: drivers/mtd/nand/raw/r852.c F: drivers/mtd/nand/raw/r852.h +RISC-V ACLINT DRIVERS +M: Anup Patel <anup.patel@wdc.com> +L: linux-riscv@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/interrupt-controller/riscv,aclint-swi.yaml +F: Documentation/devicetree/bindings/timer/riscv,aclint-mtimer.yaml +F: drivers/clocksource/timer-clint.c +F: drivers/irqchip/irq-riscv-aclint-swi.c +F: include/linux/irqchip/irq-riscv-aclint-swi.h + RISC-V ARCHITECTURE M: Paul Walmsley <paul.walmsley@sifive.com> M: Palmer Dabbelt <palmer@dabbelt.com> @ arch/arm64/configs/defconfig:90 @ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_CPUFREQ_DT=y CONFIG_ACPI_CPPC_CPUFREQ=m -CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM=m CONFIG_ARM_ARMADA_37XX_CPUFREQ=y CONFIG_ARM_SCPI_CPUFREQ=y CONFIG_ARM_IMX_CPUFREQ_DT=m @ arch/arm64/configs/defconfig:99 @ CONFIG_ARM_RASPBERRYPI_CPUFREQ=m CONFIG_ARM_SCMI_CPUFREQ=y CONFIG_ARM_TEGRA186_CPUFREQ=y CONFIG_QORIQ_CPUFREQ=y +CONFIG_SUN50I_CPUFREQ_NVMEM=m CONFIG_ARM_SCMI_PROTOCOL=y CONFIG_ARM_SCPI_PROTOCOL=y CONFIG_RASPBERRYPI_FIRMWARE=y @ arch/riscv/Kconfig:21 @ config RISCV select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEBUG_WX + select ARCH_HAS_DMA_PREP_COHERENT + select ARCH_HAS_SYNC_DMA_FOR_CPU + select ARCH_HAS_SYNC_DMA_FOR_DEVICE + select ARCH_HAS_DMA_WRITE_COMBINE select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_GIGANTIC_PAGE select ARCH_HAS_KCOV select ARCH_HAS_MMIOWB + select ARCH_HAS_PROTECTION_MAP_INIT select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_DIRECT_MAP if MMU select ARCH_HAS_SET_MEMORY if MMU @ arch/riscv/Kconfig:53 @ config RISCV select CLONE_BACKWARDS select CLINT_TIMER if !MMU select COMMON_CLK + select CPU_PM if CPU_IDLE + select DMA_DIRECT_REMAP select EDAC_SUPPORT select GENERIC_ARCH_TOPOLOGY if SMP select GENERIC_ATOMIC64 if !64BIT @ arch/riscv/Kconfig:63 @ config RISCV select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO select GENERIC_IDLE_POLL_SETUP select GENERIC_IOREMAP if MMU + select GENERIC_IRQ_IPI select GENERIC_IRQ_MULTI_HANDLER select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW_LEVEL @ arch/riscv/Kconfig:198 @ config ARCH_WANT_GENERAL_HUGETLB config ARCH_SUPPORTS_UPROBES def_bool y +config ARCH_SUSPEND_POSSIBLE + def_bool y + config STACKTRACE_SUPPORT def_bool y @ arch/riscv/Kconfig:224 @ config GENERIC_HWEIGHT config FIX_EARLYCON_MEM def_bool MMU +config ARCH_HAS_PROTECTION_MAP_INIT + def_bool y + config PGTABLE_LEVELS int default 3 if 64BIT @ arch/riscv/Kconfig:265 @ config ARCH_RV64I select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER if !XIP_KERNEL - select SWIOTLB if MMU + # select SWIOTLB if MMU endchoice @ arch/riscv/Kconfig:585 @ source "kernel/power/Kconfig" endmenu +menu "CPU Power Management" + +source "drivers/cpufreq/Kconfig" + +source "drivers/cpuidle/Kconfig" + +endmenu + source "arch/riscv/kvm/Kconfig" @ arch/riscv/Kconfig.socs:4 @ menu "SoC selection" +config ARCH_SUNXI + bool "Allwinner sunXi SoCs" + select SIFIVE_PLIC + select SUN4I_TIMER + select SUN5I_HSTIMER + select SUN20I_INTC + help + This enables support for Allwinner sunXi SoC platforms. + config SOC_MICROCHIP_POLARFIRE bool "Microchip PolarFire SoCs" select MCHP_CLK_MPFS @ arch/riscv/Kconfig.socs:40 @ 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. @ arch/riscv/boot/dts/Makefile:2 @ # SPDX-License-Identifier: GPL-2.0 +subdir-y += allwinner subdir-y += sifive subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan subdir-y += microchip @ arch/riscv/boot/dts/allwinner/Makefile:2 @ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-nezha.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-nezha-1g.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-nezha-2g.dtb +dtb-$(CONFIG_ARCH_SUNXI) += sun20i-d1-nezha-512m.dtb @ arch/riscv/boot/dts/allwinner/sun20i-d1-nezha-1g.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2021 Wei Fu <wefu@redhat.com> + +/dts-v1/; + +#include "sun20i-d1-nezha.dts" + +/ { + // FIXME: this is temporary. + memory@40000000 { + device_type = "memory"; + reg = <0x40000000 0x40000000>; /* 1 GB */ + }; +}; @ arch/riscv/boot/dts/allwinner/sun20i-d1-nezha-2g.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2021 Wei Fu <wefu@redhat.com> + +/dts-v1/; + +#include "sun20i-d1-nezha.dts" + +/ { + // FIXME: this is temporary. + memory@40000000 { + device_type = "memory"; + reg = <0x40000000 0x80000000>; /* 2 GB */ + }; +}; @ arch/riscv/boot/dts/allwinner/sun20i-d1-nezha-512m.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2021 Wei Fu <wefu@redhat.com> + +/dts-v1/; + +#include "sun20i-d1-nezha.dts" + +/ { + memory@40000000 { + device_type = "memory"; + reg = <0x40000000 0x20000000>; /* 512 MB */ + }; +}; @ arch/riscv/boot/dts/allwinner/sun20i-d1-nezha.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + +/dts-v1/; + +#include "sun20i-d1.dtsi" + +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/input/input.h> +#include <dt-bindings/leds/common.h> +#include <dt-bindings/pwm/pwm.h> + +/ { + model = "Allwinner D1 NeZha"; + compatible = "allwinner,d1-nezha", "allwinner,sun20i-d1"; + + aliases { + ethernet0 = &emac; + mmc0 = &mmc0; + mmc1 = &mmc1; + mmc2 = &mmc2; + serial0 = &uart0; + spi0 = &spi0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + hdmi_connector: connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con_in: endpoint { + // FIXME: remote-endpoint = <&hdmi_out_con>; + }; + }; + }; + + reg_usbvbus: usbvbus { + compatible = "regulator-fixed"; + regulator-name = "usbvbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio 3 19 GPIO_ACTIVE_HIGH>; /* PD19 */ + enable-active-high; + vin-supply = <®_vcc>; + }; + + reg_vcc: vcc { + compatible = "regulator-fixed"; + regulator-name = "vcc"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_vcc_3v3: vcc-3v3 { + compatible = "regulator-fixed"; + regulator-name = "vcc-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + vin-supply = <®_vcc>; + }; + + reg_vdd_cpu: vdd-cpu { + compatible = "pwm-regulator"; + pwms = <&pwm 0 50000 0>; + pwm-supply = <®_vcc>; + regulator-name = "vdd-cpu"; + regulator-min-microvolt = <810000>; + regulator-max-microvolt = <1160000>; + }; + + wifi_pwrseq: wifi-pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio 6 12 GPIO_ACTIVE_LOW>; /* PG12 */ + }; +}; + +&codec { + allwinner,routing = "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "LINEINL", "HPOUTL", + "LINEINR", "HPOUTR", + "MICIN3", "Headset Microphone", + "Headset Microphone", "HBIAS"; + allwinner,widgets = "Microphone", "Headset Microphone", + "Headphone", "Headphone Jack"; + avcc-supply = <®_aldo>; + hpvcc-supply = <®_hpldo>; + vdd33-supply = <®_vcc_3v3>; + status = "okay"; +}; + +&cpu0 { + cpu-supply = <®_vdd_cpu>; +}; + +&ehci0 { + status = "okay"; +}; + +&ehci1 { + status = "okay"; +}; + +&emac { + pinctrl-0 = <&rgmii_pe_pins>; + pinctrl-names = "default"; + phy-handle = <&ext_rgmii_phy>; + phy-mode = "rgmii-id"; + phy-supply = <®_vcc_3v3>; + status = "okay"; +}; + +&gpio { + vcc-pa-supply = <®_vcc_3v3>; + vcc-pb-supply = <®_vcc_3v3>; + vcc-pc-supply = <®_vcc_3v3>; + vcc-pd-supply = <®_vcc_3v3>; + vcc-pe-supply = <®_vcc_3v3>; + vcc-pf-supply = <®_vcc_3v3>; + vcc-pg-supply = <®_vcc_3v3>; + + i2s2_pb_pins: i2s2-pb-pins { + pins = "PB5", "PB6", "PB7"; + function = "i2s2"; + }; + + i2s2_pb3_din_pin: i2s2-pb3-din-pin { + pins = "PB3"; + function = "i2s2_din"; + }; + + i2s2_pb4_dout_pin: i2s2-pb4-dout-pin { + pins = "PB4"; + function = "i2s2_dout"; + }; + + ledc_pc0_pin: ledc-pc0-pin { + pins = "PC0"; + function = "ledc"; + }; + + pwm0_pd16_pin: pwm0-pd16-pin { + pins = "PD16"; + function = "pwm"; + }; + + pwm2_pd18_pin: pwm2-pd18-pin { + pins = "PD18"; + function = "pwm"; + }; + + pwm7_pd22_pin: pwm7-pd22-pin { + pins = "PD22"; + function = "pwm"; + }; + + spdif_pd22_pin: spdif-pd22-pin { + pins = "PD22"; + function = "spdif"; + }; +}; + +&i2c0 { + pinctrl-0 = <&i2c0_pb10_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&i2c2 { + pinctrl-0 = <&i2c2_pb0_pins>; + pinctrl-names = "default"; + status = "okay"; + + pcf8574a: gpio@38 { + compatible = "nxp,pcf8574a"; + #address-cells = <0>; + reg = <0x38>; + gpio-controller; + #gpio-cells = <2>; + interrupts-extended = <&gpio 1 2 IRQ_TYPE_LEVEL_LOW>; /* PB2 */ + interrupt-controller; + #interrupt-cells = <2>; + }; +}; + +&i2s2 { + pinctrl-0 = <&i2s2_pb_pins>, <&i2s2_pb3_din_pin>, <&i2s2_pb4_dout_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + +&ledc { + pinctrl-0 = <&ledc_pc0_pin>; + pinctrl-names = "default"; + status = "okay"; + + led@0 { + reg = <0x0>; + color = <LED_COLOR_ID_RGB>; + function = LED_FUNCTION_INDICATOR; + }; +}; + +&lradc { + vref-supply = <®_aldo>; + wakeup-source; + status = "okay"; + + button-160 { + label = "OK"; + linux,code = <KEY_OK>; + channel = <0>; + voltage = <160000>; + }; +}; + +&mdio { + ext_rgmii_phy: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; + }; +}; + +&mmc0 { + bus-width = <4>; + cd-gpios = <&gpio 5 6 GPIO_ACTIVE_HIGH>; /* PF6 */ + disable-wp; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc0_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&mmc1 { + bus-width = <4>; + mmc-pwrseq = <&wifi_pwrseq>; + non-removable; + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + pinctrl-0 = <&mmc1_pins>; + pinctrl-names = "default"; + status = "okay"; + + xr829: wifi@1 { + reg = <1>; + host-wake-gpios = <&gpio 6 10 GPIO_ACTIVE_LOW>; /* PG10 */ + }; +}; + +&ohci0 { + status = "okay"; +}; + +&ohci1 { + status = "okay"; +}; + +&pwm { + pinctrl-0 = <&pwm0_pd16_pin>, <&pwm2_pd18_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + +®_aldo { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vdd33-supply = <®_vcc_3v3>; +}; + +®_hpldo { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + hpldoin-supply = <®_vcc_3v3>; +}; + +®_ldoa { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + ldo-in-supply = <®_vcc_3v3>; +}; + +&spdif { + pinctrl-0 = <&spdif_pd22_pin>; + pinctrl-names = "default"; + status = "okay"; +}; + +&spi0 { + pinctrl-0 = <&spi0_pins>; + pinctrl-names = "default"; + status = "okay"; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "boot0"; + reg = <0x00000000 0x00100000>; + }; + + partition@100000 { + label = "uboot"; + reg = <0x00100000 0x00300000>; + }; + + partition@400000 { + label = "secure_storage"; + reg = <0x00400000 0x00100000>; + }; + + partition@500000 { + label = "sys"; + reg = <0x00500000 0x0fb00000>; + }; + }; + }; +}; + +&spi1 { + pinctrl-0 = <&spi1_pd_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&ths { + vref-supply = <®_aldo>; +}; + +&uart0 { + pinctrl-0 = <&uart0_pb8_pins>; + pinctrl-names = "default"; + status = "okay"; +}; + +&uart1 { + pinctrl-0 = <&uart1_pg6_pins>, <&uart1_pg8_rts_cts_pins>; + pinctrl-names = "default"; + status = "okay"; + + bluetooth { + compatible = "xradio,xr829-bt"; + device-wakeup-gpios = <&gpio 6 16 GPIO_ACTIVE_LOW>; /* PG16 */ + interrupts-extended = <&gpio 6 17 IRQ_TYPE_LEVEL_LOW>; /* PG17 */ + interrupt-names = "wakeup"; + reset-gpios = <&gpio 6 18 GPIO_ACTIVE_LOW>; /* PG18 */ + }; +}; + +&usb_otg { + dr_mode = "otg"; + status = "okay"; +}; + +&usbphy { + usb0_id_det-gpios = <&gpio 3 21 GPIO_ACTIVE_LOW>; /* PD21 */ + usb0_vbus_det-gpios = <&gpio 3 20 GPIO_ACTIVE_HIGH>; /* PD20 */ + usb0_vbus-supply = <®_usbvbus>; + usb1_vbus-supply = <®_vcc>; + status = "okay"; +}; @ arch/riscv/boot/dts/allwinner/sun20i-d1.dtsi:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +// Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + +#include <dt-bindings/clock/sun20i-d1-ccu.h> +#include <dt-bindings/clock/sun20i-d1-r-ccu.h> +#include <dt-bindings/clock/sun6i-rtc.h> +#include <dt-bindings/clock/sun8i-de2.h> +#include <dt-bindings/clock/sun8i-tcon-top.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/mailbox/sun20i-d1-msgbox.h> +#include <dt-bindings/reset/sun20i-d1-ccu.h> +#include <dt-bindings/reset/sun20i-d1-r-ccu.h> +#include <dt-bindings/reset/sun8i-de2.h> +#include <dt-bindings/thermal/thermal.h> + +/ { + #address-cells = <1>; + #size-cells = <1>; + + // FIXME: no riscv architecture support for cpufreq + cpu_opp_table: cpu-opp-table { + compatible = "allwinner,sun20i-d1-operating-points", + "allwinner,sun50i-h6-operating-points"; + nvmem-cells = <&cpu_speed_grade>; + + opp-1080000000 { + // FIXME: this is probably wrong now. + clock-latency-ns = <244144>; /* 8 32k periods */ + opp-hz = /bits/ 64 <1008000000>; + + // FIXME: derive a real voltage range. + opp-microvolt-speed0 = <1100000>; + }; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + timebase-frequency = <24000000>; + + cpu0: cpu@0 { + // FIXME: is this the right compatible? + compatible = "thead,c906", "riscv"; + device_type = "cpu"; + reg = <0>; + clocks = <&ccu CLK_RISCV>; + clock-frequency = <24000000>; + #cooling-cells = <2>; + d-cache-block-size = <64>; + d-cache-sets = <256>; + d-cache-size = <32768>; + i-cache-block-size = <64>; + i-cache-sets = <128>; + i-cache-size = <32768>; + mmu-type = "riscv,sv39"; + operating-points-v2 = <&cpu_opp_table>; + riscv,isa = "rv64imafdc"; + + cpu0_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + #address-cells = <0>; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + }; + + osc24M: osc24M_clk { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "osc24M"; + }; + + // FIXME: depends on what T-HEAD tries to upstream. + pmu { + compatible = "thead,c900-pmu"; + }; + + thermal-zones { + cpu-thermal { + polling-delay = <0>; + polling-delay-passive = <0>; + thermal-sensors = <&ths 0>; + + trips { + cpu_target: cpu-target { + hysteresis = <3000>; + temperature = <85000>; + type = "passive"; + }; + + cpu-crit { + hysteresis = <0>; + temperature = <110000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_target>; + cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + }; + + soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + interrupt-parent = <&intc>; + + // TODO: write a binding and driver. + dsp: dsp@1700000 { + compatible = "allwinner,sun20i-d1-dsp"; + reg = <0x1700000 0x400>; + reg-names = "cfg"; + clocks = <&ccu CLK_BUS_DSP_CFG>, + <&ccu CLK_DSP>; + clock-names = "cfg", "dsp"; + resets = <&ccu RST_BUS_DSP_CFG>, + <&ccu RST_BUS_DSP_DBG>, + <&ccu RST_DSP>; + allwinner,sram = <&dsp_sram 1>; + interrupts = <136 IRQ_TYPE_LEVEL_HIGH>, + <137 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "dee", "pfe"; + // FIXME: this will be different for R528 (CPUX). + mboxes = <&riscv_msgbox MBOX_USER_DSP MBOX_RX>, + <&dsp_msgbox MBOX_USER_RISCV MBOX_TX>; + mbox-names = "rx", "tx"; + }; + + dsp_wdt: watchdog@1700400 { + compatible = "allwinner,sun20i-d1-wdt"; + reg = <0x1700400 0x20>; + clocks = <&osc24M>; + interrupts = <138 IRQ_TYPE_LEVEL_HIGH>; + status = "reserved"; + }; + + // TODO: write a binding and driver. + dsp_msgbox: mailbox@1701000 { + compatible = "allwinner,sun20i-d1-msgbox"; + reg = <0x1701000 0x1000>; + clocks = <&ccu CLK_BUS_MSGBOX1>; + resets = <&ccu RST_BUS_MSGBOX1>; + interrupts = <139 IRQ_TYPE_LEVEL_HIGH>, + <140 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "rx", "tx"; + #mbox-cells = <2>; + }; + + ve: video-codec@1c0e000 { + compatible = "allwinner,sun20i-d1-video-engine"; + reg = <0x1c0e000 0x2000>; + clocks = <&ccu CLK_BUS_VE>, + <&ccu CLK_VE>, + <&ccu CLK_MBUS_VE>; + clock-names = "ahb", "mod", "ram"; + resets = <&ccu RST_BUS_VE>; + allwinner,sram = <&ve_sram 1>; + interconnects = <&mbus 4>; + interconnect-names = "dma-mem"; + interrupts = <82 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&iommu 0>; + }; + + gpio: pinctrl@2000000 { + compatible = "allwinner,sun20i-d1-pinctrl"; + #address-cells = <0>; + reg = <0x2000000 0x800>; + clocks = <&ccu CLK_APB0>, + <&osc24M>, + <&rtc CLK_OSC32K>; + clock-names = "apb", "hosc", "losc"; + gpio-controller; + #gpio-cells = <3>; + interrupts = <85 IRQ_TYPE_LEVEL_HIGH>, + <87 IRQ_TYPE_LEVEL_HIGH>, + <89 IRQ_TYPE_LEVEL_HIGH>, + <91 IRQ_TYPE_LEVEL_HIGH>, + <93 IRQ_TYPE_LEVEL_HIGH>, + <95 IRQ_TYPE_LEVEL_HIGH>; + // FIXME: not in binding, should we add these? + interrupt-names = "pb", "pc", "pd", "pe", "pf", "pg"; + interrupt-controller; + #interrupt-cells = <3>; + + /omit-if-no-ref/ + i2c0_pb10_pins: i2c0-pb10-pins { + pins = "PB10", "PB11"; + function = "i2c0"; + }; + + /omit-if-no-ref/ + i2c2_pb0_pins: i2c2-pb0-pins { + pins = "PB0", "PB1"; + function = "i2c2"; + }; + + /omit-if-no-ref/ + i2c3_pb6_pins: i2c3-pb6-pins { + pins = "PB6", "PB7"; + function = "i2c3"; + }; + + /omit-if-no-ref/ + mmc0_pins: mmc0-pins { + pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; + function = "mmc0"; + }; + + /omit-if-no-ref/ + mmc1_pins: mmc1-pins { + pins = "PG0", "PG1", "PG2", "PG3", "PG4", "PG5"; + function = "mmc1"; + }; + + /omit-if-no-ref/ + mmc2_pins: mmc2-pins { + pins = "PC2", "PC3", "PC4", "PC5", "PC6", "PC7"; + function = "mmc2"; + }; + + /omit-if-no-ref/ + rgmii_pe_pins: rgmii-pe-pins { + pins = "PE0", "PE1", "PE2", "PE3", "PE4", + "PE5", "PE6", "PE7", "PE8", "PE9", + "PE11", "PE12", "PE13", "PE14", "PE15"; + function = "emac"; + }; + + /omit-if-no-ref/ + spi0_pins: spi0-pins { + pins = "PC2", "PC3", "PC4", "PC5", "PC6", "PC7"; + function = "spi0"; + }; + + /omit-if-no-ref/ + spi1_pb_pins: spi1-pb-pins { + pins = "PB0", "PB8", "PB9", "PB10", "PB11", "PB12"; + function = "spi1"; + }; + + /omit-if-no-ref/ + spi1_pd_pins: spi1-pd-pins { + pins = "PD10", "PD11", "PD12", "PD13", "PD14", "PD15"; + function = "spi1"; + }; + + /omit-if-no-ref/ + uart0_pb8_pins: uart0-pb8-pins { + pins = "PB8", "PB9"; + function = "uart0"; + }; + + /omit-if-no-ref/ + uart1_pg6_pins: uart1-pg6-pins { + pins = "PG6", "PG7"; + function = "uart1"; + }; + + /omit-if-no-ref/ + uart1_pg8_rts_cts_pins: uart1-pg8-rts-cts-pins { + pins = "PG8", "PG9"; + function = "uart1"; + }; + }; + + pwm: pwm@2000c00 { + compatible = "allwinner,sun20i-d1-pwm"; + reg = <0x2000c00 0x400>; + clocks = <&ccu CLK_BUS_PWM>, <&osc24M>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_PWM>; + interrupts = <34 IRQ_TYPE_LEVEL_HIGH>; + #pwm-cells = <3>; + status = "disabled"; + }; + + ccu: clock-controller@2001000 { + compatible = "allwinner,sun20i-d1-ccu"; + reg = <0x2001000 0x1000>; + clocks = <&osc24M>, + <&rtc CLK_OSC32K>, + <&rtc CLK_IOSC>; + clock-names = "hosc", "losc", "iosc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + // TODO: write a binding and driver. + ir_tx: irled@2003000 { + compatible = "allwinner,sun20i-d1-ir-tx"; + reg = <0x2003000 0x400>; + clocks = <&ccu CLK_BUS_IR_TX>, + <&osc24M>, + <&ccu CLK_IR_TX>; + clock-names = "bus", "pclk", "mclk"; + resets = <&ccu RST_BUS_IR_TX>; + dmas = <&dma 13>; + dma-names = "tx"; + interrupts = <35 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + ledc: led-controller@2008000 { + compatible = "allwinner,sun20i-d1-ledc", + "allwinner,sun50i-r329-ledc"; + reg = <0x2008000 0x400>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_LEDC>, <&ccu CLK_LEDC>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_LEDC>; + dmas = <&dma 42>; + dma-names = "tx"; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + // TODO: write a binding and driver. + gpadc: adc@2009000 { + compatible = "allwinner,sun20i-d1-gpadc"; + reg = <0x2009000 0x400>; + clocks = <&ccu CLK_BUS_GPADC>; + resets = <&ccu RST_BUS_GPADC>; + dmas = <&dma 12>; + dma-names = "rx"; + interrupts = <73 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + ths: temperature-sensor@2009400 { + compatible = "allwinner,sun20i-d1-ths"; + reg = <0x2009400 0x400>; + clocks = <&ccu CLK_BUS_THS>, <&osc24M>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_THS>; + interrupts = <74 IRQ_TYPE_LEVEL_HIGH>; + nvmem-cells = <&ths_calib>; + nvmem-cell-names = "calibration"; + #thermal-sensor-cells = <0>; + }; + + lradc: keys@2009800 { + compatible = "allwinner,sun20i-d1-lradc", + "allwinner,sun50i-r329-lradc"; + reg = <0x2009800 0x400>; + clocks = <&ccu CLK_BUS_LRADC>; + resets = <&ccu RST_BUS_LRADC>; + interrupts = <77 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + // TODO: write a binding and driver. + tpadc: touchscreen@2009c00 { + compatible = "allwinner,sun20i-d1-ts"; + reg = <0x2009c00 0x400>; + clocks = <&ccu CLK_BUS_TPADC>, <&ccu CLK_TPADC>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_TPADC>; + dmas = <&dma 13>; + dma-names = "rx"; + interrupts = <78 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + // FIXME: this driver probably needs updates. + iommu: iommu@2010000 { + compatible = "allwinner,sun20i-d1-iommu"; + reg = <0x2010000 0x10000>; + clocks = <&ccu CLK_BUS_IOMMU>; + interrupts = <80 IRQ_TYPE_LEVEL_HIGH>; + #iommu-cells = <1>; + }; + + codec: audio-codec@2030000 { + compatible = "allwinner,sun20i-d1-audio-codec"; + reg = <0x2030000 0x1000>; + clocks = <&ccu CLK_BUS_AUDIO>, + <&ccu CLK_AUDIO_ADC>, + <&ccu CLK_AUDIO_DAC>, + <&osc24M>, + <&rtc CLK_OSC32K>; + clock-names = "bus", "adc", "dac", "hosc", "losc"; + resets = <&ccu RST_BUS_AUDIO>; + dmas = <&dma 7>, <&dma 7>; + dma-names = "rx", "tx"; + interrupts = <41 IRQ_TYPE_LEVEL_HIGH>; + #sound-dai-cells = <0>; + status = "disabled"; + + regulators { + reg_aldo: aldo { + regulator-name = "aldo"; + }; + + reg_hpldo: hpldo { + regulator-name = "hpldo"; + }; + }; + + }; + + // TODO: try the posted driver. + dmic: dmic@2031000 { + compatible = "allwinner,sun20i-d1-dmic"; + reg = <0x2031000 0x400>; + clocks = <&ccu CLK_BUS_DMIC>, + <&ccu CLK_DMIC>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_DMIC>; + dmas = <&dma 8>; + dma-names = "rx"; + interrupts = <40 IRQ_TYPE_LEVEL_HIGH>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + i2s0: i2s@2032000 { + compatible = "allwinner,sun20i-d1-i2s"; + reg = <0x2032000 0x1000>; + clocks = <&ccu CLK_BUS_I2S0>, + <&ccu CLK_I2S0>; + clock-names = "apb", "mod"; + resets = <&ccu RST_BUS_I2S0>; + dmas = <&dma 3>, <&dma 3>; + dma-names = "rx", "tx"; + interrupts = <42 IRQ_TYPE_LEVEL_HIGH>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + i2s1: i2s@2033000 { + compatible = "allwinner,sun20i-d1-i2s"; + reg = <0x2033000 0x1000>; + clocks = <&ccu CLK_BUS_I2S1>, + <&ccu CLK_I2S1>; + clock-names = "apb", "mod"; + resets = <&ccu RST_BUS_I2S1>; + dmas = <&dma 4>, <&dma 4>; + dma-names = "rx", "tx"; + interrupts = <43 IRQ_TYPE_LEVEL_HIGH>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + // TODO: how to integrate ASRC? same or separate node? + i2s2: i2s@2034000 { + compatible = "allwinner,sun20i-d1-i2s"; + reg = <0x2034000 0x1000>; + clocks = <&ccu CLK_BUS_I2S2>, + <&ccu CLK_I2S2>; + clock-names = "apb", "mod"; + resets = <&ccu RST_BUS_I2S2>; + dmas = <&dma 5>, <&dma 5>; + dma-names = "rx", "tx"; + interrupts = <44 IRQ_TYPE_LEVEL_HIGH>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + // TODO: add receive functionality + spdif: spdif@2036000 { + compatible = "allwinner,sun20i-d1-spdif"; + reg = <0x2036000 0x400>; + clocks = <&ccu CLK_BUS_SPDIF>, + <&ccu CLK_SPDIF_RX>, + <&ccu CLK_SPDIF_TX>; + clock-names = "apb", "rx", "tx"; + resets = <&ccu RST_BUS_SPDIF>; + dmas = <&dma 2>, <&dma 2>; + dma-names = "rx", "tx"; + interrupts = <39 IRQ_TYPE_LEVEL_HIGH>; + #sound-dai-cells = <0>; + status = "disabled"; + }; + + timer: timer@2050000 { + compatible = "allwinner,sun20i-d1-timer", + "allwinner,sun8i-a23-timer"; + reg = <0x2050000 0xa0>; + clocks = <&osc24M>; + interrupts = <75 IRQ_TYPE_LEVEL_HIGH>, + <76 IRQ_TYPE_LEVEL_HIGH>; + }; + + wdt: watchdog@20500a0 { + compatible = "allwinner,sun20i-d1-wdt-reset", + "allwinner,sun20i-d1-wdt"; + reg = <0x20500a0 0x20>; + clocks = <&osc24M>; + interrupts = <79 IRQ_TYPE_LEVEL_HIGH>; + status = "reserved"; + }; + + // TODO: write a driver. + uart0: serial@2500000 { + compatible = "allwinner,sun20i-d1-uart", + "snps,dw-apb-uart"; + reg = <0x2500000 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + clocks = <&ccu CLK_BUS_UART0>; + resets = <&ccu RST_BUS_UART0>; + dmas = <&dma 14>, <&dma 14>; + dma-names = "rx", "tx"; + fifo-size = <64>; + interrupts = <18 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + // TODO: write a driver, add IDMA? + uart1: serial@2500400 { + compatible = "allwinner,sun20i-d1-uart1", + "allwinner,sun20i-d1-uart", + "snps,dw-apb-uart"; + reg = <0x2500400 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + clocks = <&ccu CLK_BUS_UART1>; + resets = <&ccu RST_BUS_UART1>; + dmas = <&dma 15>, <&dma 15>; + dma-names = "rx", "tx"; + fifo-size = <256>; + interrupts = <19 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + // TODO: write a driver. + uart2: serial@2500800 { + compatible = "allwinner,sun20i-d1-uart", + "snps,dw-apb-uart"; + reg = <0x2500800 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + clocks = <&ccu CLK_BUS_UART2>; + resets = <&ccu RST_BUS_UART2>; + dmas = <&dma 16>, <&dma 16>; + dma-names = "rx", "tx"; + fifo-size = <256>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + // TODO: write a driver. + uart3: serial@2500c00 { + compatible = "allwinner,sun20i-d1-uart", + "snps,dw-apb-uart"; + reg = <0x2500c00 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + clocks = <&ccu CLK_BUS_UART3>; + resets = <&ccu RST_BUS_UART3>; + dmas = <&dma 17>, <&dma 17>; + dma-names = "rx", "tx"; + fifo-size = <256>; + interrupts = <21 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + // TODO: write a driver. + uart4: serial@2501000 { + compatible = "allwinner,sun20i-d1-uart", + "snps,dw-apb-uart"; + reg = <0x2501000 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + clocks = <&ccu CLK_BUS_UART4>; + resets = <&ccu RST_BUS_UART4>; + dmas = <&dma 18>, <&dma 18>; + dma-names = "rx", "tx"; + fifo-size = <256>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + // TODO: write a driver. + uart5: serial@2501400 { + compatible = "allwinner,sun20i-d1-uart", + "snps,dw-apb-uart"; + reg = <0x2501400 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + clocks = <&ccu CLK_BUS_UART5>; + resets = <&ccu RST_BUS_UART5>; + dmas = <&dma 19>, <&dma 19>; + dma-names = "rx", "tx"; + fifo-size = <256>; + interrupts = <23 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + i2c0: i2c@2502000 { + compatible = "allwinner,sun20i-d1-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x2502000 0x400>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_I2C0>; + resets = <&ccu RST_BUS_I2C0>; + dmas = <&dma 43>, <&dma 43>; + dma-names = "rx", "tx"; + interrupts = <25 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + i2c1: i2c@2502400 { + compatible = "allwinner,sun20i-d1-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x2502400 0x400>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_I2C1>; + resets = <&ccu RST_BUS_I2C1>; + dmas = <&dma 44>, <&dma 44>; + dma-names = "rx", "tx"; + interrupts = <26 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + i2c2: i2c@2502800 { + compatible = "allwinner,sun20i-d1-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x2502800 0x400>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_I2C2>; + resets = <&ccu RST_BUS_I2C2>; + dmas = <&dma 45>, <&dma 45>; + dma-names = "rx", "tx"; + interrupts = <27 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + i2c3: i2c@2502c00 { + compatible = "allwinner,sun20i-d1-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x2502c00 0x400>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_I2C3>; + resets = <&ccu RST_BUS_I2C3>; + dmas = <&dma 46>, <&dma 46>; + dma-names = "rx", "tx"; + interrupts = <28 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + + syscon: syscon@3000000 { + compatible = "allwinner,sun20i-d1-system-control"; + reg = <0x3000000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + regulators { + reg_ldoa: ldoa { + regulator-name = "ldoa"; + }; + + reg_ldob: ldob { + regulator-name = "ldob"; + }; + }; + + sram@400000 { + compatible = "mmio-sram"; + reg = <0x400000 0x20000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x400000 0x20000>; + + /* + * This can be further divided into DSP IRAM, + * DSP DRAM0, and DSP DRAM1, but the mapping + * of all three is controlled by a single bit. + */ + dsp_sram: sram-section@0 { + compatible = "allwinner,sun20i-d1-dsp-sram"; + reg = <0 0x20000>; + }; + }; + + // FIXME: Address is not verified. It is copied from A64/H6. + sram@1d00000 { + compatible = "mmio-sram"; + reg = <0x1d00000 0x40000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x1d00000 0x40000>; + + ve_sram: sram-section@0 { + compatible = "allwinner,sun20i-d1-sram-c1", + "allwinner,sun4i-a10-sram-c1"; + reg = <0 0x40000>; + }; + }; + }; + + dma: dma-controller@3002000 { + compatible = "allwinner,sun20i-d1-dma"; + reg = <0x3002000 0x1000>; + clocks = <&ccu CLK_BUS_DMA>, <&ccu CLK_MBUS_DMA>; + clock-names = "bus", "mbus"; + resets = <&ccu RST_BUS_DMA>; + #dma-cells = <1>; + dma-channels = <16>; + dma-requests = <48>; + interrupts = <66 IRQ_TYPE_LEVEL_HIGH>, + <142 IRQ_TYPE_LEVEL_HIGH>; + }; + + msgbox: mailbox@3003000 { + compatible = "allwinner,sun20i-d1-msgbox"; + reg = <0x3003000 0x1000>; + clocks = <&ccu CLK_BUS_MSGBOX0>; + resets = <&ccu RST_BUS_MSGBOX0>; + interrupts = <101 IRQ_TYPE_LEVEL_HIGH>, + <102 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "rx", "tx"; + #mbox-cells = <2>; + }; + + hwspinlock: hwlock@3005000 { + compatible = "allwinner,sun20i-d1-hwspinlock", + "allwinner,sun6i-a31-hwspinlock"; + reg = <0x3005000 0x1000>; + clocks = <&ccu CLK_BUS_SPINLOCK>; + resets = <&ccu RST_BUS_SPINLOCK>; + interrupts = <70 IRQ_TYPE_LEVEL_HIGH>; + }; + + sid: efuse@3006000 { + compatible = "allwinner,sun20i-d1-sid", + "allwinner,sun50i-a64-sid"; + reg = <0x3006000 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + + cpu_speed_grade: cpu-speed-grade@0 { + reg = <0x0 0x2>; + }; + + ths_calib: ths-calib@14 { + reg = <0x14 0x4>; + }; + }; + + // TODO: write a binding and driver. + hstimer: timer@3008000 { + compatible = "allwinner,sun20i-d1-hstimer", + "allwinner,sun50i-h6-hstimer"; + reg = <0x3008000 0x1000>; + clocks = <&ccu CLK_BUS_HSTIMER>; + resets = <&ccu RST_BUS_HSTIMER>; + interrupts = <71 IRQ_TYPE_LEVEL_HIGH>, + <72 IRQ_TYPE_LEVEL_HIGH>; + }; + + crypto: crypto@3040000 { + compatible = "allwinner,sun20i-d1-crypto"; + reg = <0x3040000 0x800>; + clocks = <&ccu CLK_BUS_CE>, <&ccu CLK_CE>, <&ccu CLK_MBUS_CE>; + clock-names = "bus", "mod", "ram"; + resets = <&ccu RST_BUS_CE>; + interrupts = <68 IRQ_TYPE_LEVEL_HIGH>; + }; + + // TODO: write a binding and driver. + mbus: dram-controller@3102000 { + compatible = "allwinner,sun20i-d1-mbus"; + reg = <0x3102000 0x200000>; + #address-cells = <1>; + #size-cells = <1>; + clocks = <&ccu CLK_BUS_DRAM>, + <&ccu CLK_DRAM>, + <&ccu CLK_MBUS>; + clock-names = "bus", "dram", "mbus"; + dma-ranges = <0 0x40000000 0x80000000>; + #interconnect-cells = <1>; + interrupts = <59 IRQ_TYPE_LEVEL_HIGH>; + }; + + mmc0: mmc@4020000 { + compatible = "allwinner,sun20i-d1-mmc"; + reg = <0x4020000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC0>; + reset-names = "ahb"; + cap-sd-highspeed; + interrupts = <56 IRQ_TYPE_LEVEL_HIGH>; + max-frequency = <150000000>; + no-mmc; + status = "disabled"; + }; + + mmc1: mmc@4021000 { + compatible = "allwinner,sun20i-d1-mmc"; + reg = <0x4021000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC1>; + reset-names = "ahb"; + cap-sd-highspeed; + interrupts = <57 IRQ_TYPE_LEVEL_HIGH>; + max-frequency = <150000000>; + no-mmc; + status = "disabled"; + }; + + mmc2: mmc@4022000 { + compatible = "allwinner,sun20i-d1-emmc"; + reg = <0x4022000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>; + clock-names = "ahb", "mmc"; + resets = <&ccu RST_BUS_MMC2>; + reset-names = "ahb"; + cap-mmc-highspeed; + interrupts = <58 IRQ_TYPE_LEVEL_HIGH>; + max-frequency = <150000000>; + mmc-ddr-1_8v; + mmc-ddr-3_3v; + no-sd; + no-sdio; + status = "disabled"; + }; + + spi0: spi@4025000 { + compatible = "allwinner,sun20i-d1-spi", + "allwinner,sun50i-r329-spi"; + reg = <0x4025000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; + clock-names = "ahb", "mod"; + resets = <&ccu RST_BUS_SPI0>; + dmas = <&dma 22>, <&dma 22>; + dma-names = "rx", "tx"; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH>; + num-cs = <1>; + status = "disabled"; + }; + + spi1: spi@4026000 { + compatible = "allwinner,sun20i-d1-spi-dbi", + "allwinner,sun50i-r329-spi-dbi", + "allwinner,sun50i-r329-spi"; + reg = <0x4026000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>; + clock-names = "ahb", "mod"; + resets = <&ccu RST_BUS_SPI1>; + dmas = <&dma 23>, <&dma 23>; + dma-names = "rx", "tx"; + interrupts = <32 IRQ_TYPE_LEVEL_HIGH>; + num-cs = <1>; + status = "disabled"; + }; + + usb_otg: usb@4100000 { + compatible = "allwinner,sun20i-d1-musb", + "allwinner,sun8i-a33-musb"; + reg = <0x4100000 0x400>; + clocks = <&ccu CLK_BUS_OTG>; + resets = <&ccu RST_BUS_OTG>; + dmas = <&dma 30>, <&dma 30>, + <&dma 31>, <&dma 31>, + <&dma 32>, <&dma 32>, + <&dma 33>, <&dma 33>, + <&dma 34>, <&dma 34>; + dma-names = "ep1_rx", "ep1_tx", + "ep2_rx", "ep2_tx", + "ep3_rx", "ep3_tx", + "ep4_rx", "ep4_tx", + "ep5_rx", "ep5_tx"; + extcon = <&usbphy 0>; + interrupts = <45 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "mc"; + phys = <&usbphy 0>; + phy-names = "usb"; + status = "disabled"; + }; + + usbphy: phy@4100400 { + compatible = "allwinner,sun20i-d1-usb-phy"; + reg = <0x4100400 0x100>, + <0x4101800 0x100>, + <0x4200800 0x100>; + reg-names = "phy_ctrl", + "pmu0", + "pmu1"; + clocks = <&osc24M>, + <&osc24M>; + clock-names = "usb0_phy", + "usb1_phy"; + resets = <&ccu RST_USB_PHY0>, + <&ccu RST_USB_PHY1>; + reset-names = "usb0_reset", + "usb1_reset"; + #phy-cells = <1>; + status = "disabled"; + }; + + ehci0: usb@4101000 { + compatible = "allwinner,sun20i-d1-ehci", + "generic-ehci"; + reg = <0x4101000 0x100>; + clocks = <&ccu CLK_BUS_OHCI0>, + <&ccu CLK_BUS_EHCI0>, + <&ccu CLK_USB_OHCI0>; + resets = <&ccu RST_BUS_OHCI0>, + <&ccu RST_BUS_EHCI0>; + interrupts = <46 IRQ_TYPE_LEVEL_HIGH>; + phys = <&usbphy 0>; + phy-names = "usb"; + status = "disabled"; + }; + + ohci0: usb@4101400 { + compatible = "allwinner,sun20i-d1-ohci", + "generic-ohci"; + reg = <0x4101400 0x100>; + clocks = <&ccu CLK_BUS_OHCI0>, + <&ccu CLK_USB_OHCI0>; + resets = <&ccu RST_BUS_OHCI0>; + interrupts = <47 IRQ_TYPE_LEVEL_HIGH>; + phys = <&usbphy 0>; + phy-names = "usb"; + status = "disabled"; + }; + + ehci1: usb@4200000 { + compatible = "allwinner,sun20i-d1-ehci", + "generic-ehci"; + reg = <0x4200000 0x100>; + clocks = <&ccu CLK_BUS_OHCI1>, + <&ccu CLK_BUS_EHCI1>, + <&ccu CLK_USB_OHCI1>; + resets = <&ccu RST_BUS_OHCI1>, + <&ccu RST_BUS_EHCI1>; + interrupts = <49 IRQ_TYPE_LEVEL_HIGH>; + phys = <&usbphy 1>; + phy-names = "usb"; + status = "disabled"; + }; + + ohci1: usb@4200400 { + compatible = "allwinner,sun20i-d1-ohci", + "generic-ohci"; + reg = <0x4200400 0x100>; + clocks = <&ccu CLK_BUS_OHCI1>, + <&ccu CLK_USB_OHCI1>; + resets = <&ccu RST_BUS_OHCI1>; + interrupts = <50 IRQ_TYPE_LEVEL_HIGH>; + phys = <&usbphy 1>; + phy-names = "usb"; + status = "disabled"; + }; + + emac: ethernet@4500000 { + compatible = "allwinner,sun20i-d1-emac", + "allwinner,sun50i-a64-emac"; + reg = <0x4500000 0x10000>; + clocks = <&ccu CLK_BUS_EMAC>; + clock-names = "stmmaceth"; + resets = <&ccu RST_BUS_EMAC>; + reset-names = "stmmaceth"; + interrupts = <62 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "macirq"; + syscon = <&syscon>; + status = "disabled"; + + mdio: mdio { + compatible = "snps,dwmac-mdio"; + #address-cells = <1>; + #size-cells = <0>; + }; + }; + + de: display-engine@5000000 { + reg = <0x5000000 0x400000>; + interrupts = <103 IRQ_TYPE_LEVEL_HIGH>; + interconnects = <&mbus 11>; + interconnect-names = "dma-mem"; + iommus = <&iommu 2>; + }; + + deinterlace: deinterlace@5400000 { + reg = <0x5400000 0x10000>; + interconnects = <&mbus 10>; + interconnect-names = "dma-mem"; + interrupts = <104 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&iommu 4>; + }; + + g2d: g2d@5410000 { + reg = <0x5410000 0x40000>; + interconnects = <&mbus 9>; + interconnect-names = "dma-mem"; + interrupts = <105 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&iommu 3>; + }; + + dsi: dsi@5450000 { + reg = <0x5450000 0x2000>; + interrupts = <108 IRQ_TYPE_LEVEL_HIGH>; + }; + + tcon_top: tcon-top@5460000 { + reg = <0x5460000 0x1000>; + }; + + tcon_lcd: lcd-controller@5461000 { + reg = <0x5461000 0x1000>; + interrupts = <106 IRQ_TYPE_LEVEL_HIGH>; + }; + + tcon_tv: lcd-controller@5470000 { + reg = <0x5470000 0x1000>; + interrupts = <107 IRQ_TYPE_LEVEL_HIGH>; + }; + + hdmi: hdmi@5500000 { + reg = <0x5500000 0x100000>; + interrupts = <109 IRQ_TYPE_LEVEL_HIGH>; + }; + + tve_top: video-codec@5600000 { + reg = <0x5600000 0x4000>; + }; + + tve0: video-codec@5604000 { + reg = <0x5604000 0x4000>; + interrupts = <110 IRQ_TYPE_LEVEL_HIGH>; + }; + + csi: csi@5800000 { + reg = <0x5800000 0x400000>; + interrupts = <111 IRQ_TYPE_LEVEL_HIGH>, + <112 IRQ_TYPE_LEVEL_HIGH>, + <116 IRQ_TYPE_LEVEL_HIGH>, + <122 IRQ_TYPE_LEVEL_HIGH>; + interconnects = <&mbus 7>; + interconnect-names = "dma-mem"; + iommus = <&iommu 1>; + }; + + tvd_top: video-codec@5c00000 { + reg = <0x5c00000 0x1000>; + interconnects = <&mbus 6>; + interconnect-names = "dma-mem"; + }; + + tvd0: video-codec@5c01000 { + reg = <0x5c01000 0x1000>; + interrupts = <123 IRQ_TYPE_LEVEL_HIGH>; + }; + + intc: interrupt-controller@6010000 { + compatible = "allwinner,sun20i-d1-intc"; + reg = <0x6010000 0x100>; + #address-cells = <0>; + clocks = <&ccu CLK_BUS_RISCV_CFG>; + resets = <&ccu RST_BUS_RISCV_CFG>; + interrupt-parent = <&plic>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + riscv_wdt: watchdog@6011000 { + compatible = "allwinner,sun20i-d1-wdt"; + reg = <0x6011000 0x20>; + clocks = <&osc24M>; + interrupts = <147 IRQ_TYPE_LEVEL_HIGH>; + }; + + riscv_msgbox: mailbox@601f000 { + compatible = "allwinner,sun20i-d1-msgbox"; + reg = <0x601f000 0x1000>; + clocks = <&ccu CLK_BUS_MSGBOX2>; + resets = <&ccu RST_BUS_MSGBOX2>; + interrupts = <144 IRQ_TYPE_LEVEL_HIGH>, + <145 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "rx", "tx"; + #mbox-cells = <2>; + }; + + r_ccu: clock-controller@7010000 { + compatible = "allwinner,sun20i-d1-r-ccu"; + reg = <0x7010000 0x400>; + clocks = <&osc24M>, + <&rtc CLK_OSC32K>, + <&rtc CLK_IOSC>, + <&ccu CLK_PLL_PERIPH0_DIV3>; + clock-names = "hosc", "losc", "iosc", "pll-periph"; + #clock-cells = <1>; + #reset-cells = <1>; + interrupts = <64 IRQ_TYPE_LEVEL_HIGH>; + }; + + r_ir_rx: ir@7040000 { + compatible = "allwinner,sun20i-d1-ir", + "allwinner,sun6i-a31-ir"; + reg = <0x7040000 0x400>; + clocks = <&r_ccu CLK_BUS_R_IR_RX>, <&r_ccu CLK_R_IR_RX>; + clock-names = "apb", "ir"; + resets = <&r_ccu RST_BUS_R_IR_RX>; + interrupts = <167 IRQ_TYPE_LEVEL_HIGH>; + }; + + // TODO: audit all blocks for hidden use of CLK_DCXO24M + rtc: rtc@7090000 { + compatible = "allwinner,sun20i-d1-rtc", + "allwinner,sun50i-r329-rtc"; + reg = <0x7090000 0x400>; + clocks = <&r_ccu CLK_BUS_R_RTC>, + <&osc24M>, + <&r_ccu CLK_R_AHB>; + clock-names = "bus", "hosc", "ahb"; + #clock-cells = <1>; + interrupts = <160 IRQ_TYPE_LEVEL_HIGH>; + }; + + plic: interrupt-controller@10000000 { + compatible = "allwinner,sun20i-d1-plic", + "thead,c900-plic"; + reg = <0x10000000 0x4000000>; + #address-cells = <0>; + interrupts-extended = <&cpu0_intc 11>, + <&cpu0_intc 9>; + interrupt-controller; + #interrupt-cells = <1>; + riscv,ndev = <176>; + }; + + clint: clint@14000000 { + compatible = "allwinner,sun20i-d1-clint", + "sifive,clint0"; + reg = <0x14000000 0xc000>; + reg-io-width = <4>; + interrupts-extended = <&cpu0_intc 3>, + <&cpu0_intc 7>; + }; + }; +}; @ arch/riscv/configs/defconfig:24 @ CONFIG_SMP=y CONFIG_HOTPLUG_CPU=y CONFIG_VIRTUALIZATION=y CONFIG_KVM=m +CONFIG_PM=y +CONFIG_CPU_IDLE=y CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y @ arch/riscv/configs/nezha_defconfig:4 @ +CONFIG_DEFAULT_HOSTNAME="nezha" +# CONFIG_SWAP is not set +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_EXPERT=y +# CONFIG_SYSFS_SYSCALL is not set +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB_MERGE_DEFAULT is not set +CONFIG_ARCH_SUNXI=y +# CONFIG_RISCV_ERRATA_ALTERNATIVE is not set +# CONFIG_RISCV_ISA_C is not set +# CONFIG_EFI is not set +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_JUMP_LABEL=y +# CONFIG_SECCOMP is not set +# CONFIG_STACKPROTECTOR is not set +# CONFIG_GCC_PLUGINS is not set +# CONFIG_MQ_IOSCHED_DEADLINE is not set +# CONFIG_MQ_IOSCHED_KYBER is not set +# CONFIG_COREDUMP is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6_SIT is not set +# CONFIG_WIRELESS is not set +# CONFIG_ETHTOOL_NETLINK is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_ALLOW_DEV_COREDUMP is not set +CONFIG_MTD=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_PARTITIONED_MASTER=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_FASTMAP=y +CONFIG_MTD_UBI_BLOCK=y +# CONFIG_BLK_DEV is not set +CONFIG_SCSI=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_NETDEVICES=y +# CONFIG_NET_CORE is not set +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MICROSOFT is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MICROSEMI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +# CONFIG_NET_VENDOR_SOCIONEXT is not set +CONFIG_STMMAC_ETH=y +# CONFIG_DWMAC_GENERIC is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set +CONFIG_REALTEK_PHY=y +# CONFIG_USB_NET_DRIVERS is not set +# CONFIG_WLAN is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_SUN4I_LRADC=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_LDISC_AUTOLOAD is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=6 +CONFIG_SERIAL_8250_RUNTIME_UARTS=6 +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_EARLYCON_RISCV_SBI=y +CONFIG_HVC_RISCV_SBI=y +CONFIG_I2C=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_MV64XXX=y +CONFIG_SPI=y +CONFIG_SPI_SUN6I=y +CONFIG_SPI_SPIDEV=y +# CONFIG_PTP_1588_CLOCK is not set +CONFIG_PINCTRL=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_PCF857X=y +CONFIG_POWER_SUPPLY=y +CONFIG_THERMAL=y +CONFIG_THERMAL_STATISTICS=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_SUN8I_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_SYSFS=y +CONFIG_SUNXI_WATCHDOG=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_PWM=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_HRTIMER=y +CONFIG_SND_DYNAMIC_MINORS=y +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_DRIVERS is not set +# CONFIG_SND_SPI is not set +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_SND_SUN20I_CODEC=y +CONFIG_SND_SUN4I_I2S=y +CONFIG_SND_SUN4I_SPDIF=y +# CONFIG_USB_HID is not set +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_OTG=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_UAS=y +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SUNXI=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CH341=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_GADGET=y +CONFIG_USB_ETH=y +# CONFIG_USB_ETH_RNDIS is not set +CONFIG_USB_ETH_EEM=y +CONFIG_MMC=y +CONFIG_MMC_SUNXI=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_CLASS_MULTICOLOR=y +CONFIG_LEDS_SUN50I_R329=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_MTD=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_ACTIVITY=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEDS_TRIGGER_NETDEV=y +CONFIG_RTC_CLASS=y +CONFIG_DMADEVICES=y +CONFIG_DMA_SUN6I=y +# CONFIG_VIRTIO_MENU is not set +# CONFIG_VHOST_MENU is not set +CONFIG_SUN8I_DE2_CCU=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_SUN6I=y +CONFIG_MAILBOX=y +CONFIG_SUN50I_IOMMU=y +CONFIG_REMOTEPROC=y +CONFIG_REMOTEPROC_CDEV=y +CONFIG_SUN8I_DSP_REMOTEPROC=y +CONFIG_RPMSG_CHAR=y +CONFIG_RPMSG_VIRTIO=y +CONFIG_PWM=y +CONFIG_PWM_SUN8I_V536=y +CONFIG_PHY_SUN4I_USB=y +CONFIG_NVMEM_SUNXI_SID=y +CONFIG_EXT4_FS=y +# CONFIG_DNOTIFY is not set +CONFIG_FANOTIFY=y +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_TMPFS_INODE64=y +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_USER_API_RNG=y +CONFIG_CRYPTO_USER_API_AEAD=y +# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set +CONFIG_CRYPTO_DEV_SUN8I_CE=y +CONFIG_CRYPTO_DEV_SUN8I_CE_HASH=y +CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG=y +CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG=y +CONFIG_PRINTK_TIME=y +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +# CONFIG_FRAME_POINTER is not set +CONFIG_VMLINUX_MAP=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_MAGIC_SYSRQ_SERIAL is not set +CONFIG_DEBUG_FS=y +# CONFIG_DEBUG_MISC is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_STACKTRACE=y +# CONFIG_RCU_TRACE is not set +# CONFIG_FTRACE is not set +# CONFIG_RUNTIME_TESTING_MENU is not set @ arch/riscv/configs/nezha_fedora_defconfig:4 @ +CONFIG_DEFAULT_HOSTNAME="nezha" +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_WATCH_QUEUE=y +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_PSI=y +CONFIG_IKCONFIG=m +CONFIG_IKCONFIG_PROC=y +CONFIG_IKHEADERS=m +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=12 +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +CONFIG_CGROUP_MISC=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_USERFAULTFD=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_SHUFFLE_PAGE_ALLOCATOR=y +CONFIG_PROFILING=y +CONFIG_ARCH_SUNXI=y +# CONFIG_SUSPEND is not set +CONFIG_PM=y +CONFIG_PM_DEBUG=y +CONFIG_JUMP_LABEL=y +# CONFIG_SECCOMP is not set +# CONFIG_STACKPROTECTOR is not set +# CONFIG_GCC_PLUGINS is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_COMPRESS_XZ=y +CONFIG_BLK_DEV_ZONED=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_BLK_WBT=y +CONFIG_BLK_CGROUP_IOLATENCY=y +CONFIG_BLK_CGROUP_IOCOST=y +CONFIG_BLK_SED_OPAL=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_AIX_PARTITION=y +CONFIG_OSF_PARTITION=y +CONFIG_AMIGA_PARTITION=y +CONFIG_MAC_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_LDM_PARTITION=y +CONFIG_SGI_PARTITION=y +CONFIG_SUN_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_IOSCHED_BFQ=y +CONFIG_BFQ_GROUP_IOSCHED=y +CONFIG_BINFMT_MISC=m +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_KSM=y +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +CONFIG_ZSWAP=y +CONFIG_Z3FOLD=y +CONFIG_ZSMALLOC=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=m +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=m +CONFIG_TLS=m +CONFIG_XFRM_USER=y +CONFIG_XFRM_INTERFACE=m +CONFIG_XFRM_SUB_POLICY=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=m +CONFIG_NET_KEY_MIGRATE=y +CONFIG_XDP_SOCKETS=y +CONFIG_XDP_SOCKETS_DIAG=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_FIB_TRIE_STATS=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_NET_IPVTI=m +CONFIG_NET_FOU_IP_TUNNELS=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_ESP_OFFLOAD=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_DIAG=m +CONFIG_INET_UDP_DIAG=m +CONFIG_INET_RAW_DIAG=m +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_NV=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_YEAH=m +CONFIG_TCP_CONG_ILLINOIS=m +CONFIG_TCP_CONG_DCTCP=m +CONFIG_TCP_CONG_CDG=m +CONFIG_TCP_CONG_BBR=m +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_ESP_OFFLOAD=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_ILA=m +CONFIG_IPV6_VTI=m +CONFIG_IPV6_SIT=m +CONFIG_IPV6_SIT_6RD=y +CONFIG_IPV6_GRE=m +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y +CONFIG_IPV6_PIMSM_V2=y +CONFIG_IPV6_SEG6_LWTUNNEL=y +CONFIG_IPV6_SEG6_HMAC=y +CONFIG_NETFILTER=y +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_ZONES=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_NETDEV=y +CONFIG_NFT_NUMGEN=m +CONFIG_NFT_CT=m +CONFIG_NFT_FLOW_OFFLOAD=m +CONFIG_NFT_COUNTER=m +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_REDIR=m +CONFIG_NFT_NAT=m +CONFIG_NFT_TUNNEL=m +CONFIG_NFT_OBJREF=m +CONFIG_NFT_QUEUE=m +CONFIG_NFT_QUOTA=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_COMPAT=m +CONFIG_NFT_HASH=m +CONFIG_NFT_FIB_INET=m +CONFIG_NFT_XFRM=m +CONFIG_NFT_SOCKET=m +CONFIG_NFT_TPROXY=m +CONFIG_NFT_SYNPROXY=m +CONFIG_NFT_DUP_NETDEV=m +CONFIG_NFT_FWD_NETDEV=m +CONFIG_NFT_FIB_NETDEV=m +CONFIG_NF_FLOW_TABLE_INET=m +CONFIG_NF_FLOW_TABLE=m +CONFIG_NETFILTER_XT_SET=m +CONFIG_NETFILTER_XT_TARGET_AUDIT=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CGROUP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPCOMP=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_SET=m +CONFIG_IP_SET_BITMAP_IP=m +CONFIG_IP_SET_BITMAP_IPMAC=m +CONFIG_IP_SET_BITMAP_PORT=m +CONFIG_IP_SET_HASH_IP=m +CONFIG_IP_SET_HASH_IPMARK=m +CONFIG_IP_SET_HASH_IPPORT=m +CONFIG_IP_SET_HASH_IPPORTIP=m +CONFIG_IP_SET_HASH_IPPORTNET=m +CONFIG_IP_SET_HASH_IPMAC=m +CONFIG_IP_SET_HASH_MAC=m +CONFIG_IP_SET_HASH_NETPORTNET=m +CONFIG_IP_SET_HASH_NET=m +CONFIG_IP_SET_HASH_NETNET=m +CONFIG_IP_SET_HASH_NETPORT=m +CONFIG_IP_SET_HASH_NETIFACE=m +CONFIG_IP_SET_LIST_SET=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_FO=m +CONFIG_IP_VS_OVF=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_MH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m +CONFIG_IP_VS_FTP=m +CONFIG_IP_VS_PE_SIP=m +CONFIG_NF_SOCKET_IPV4=y +CONFIG_NFT_DUP_IPV4=m +CONFIG_NFT_FIB_IPV4=m +CONFIG_NF_TABLES_ARP=y +CONFIG_NF_FLOW_TABLE_IPV4=m +CONFIG_NF_LOG_ARP=m +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_SYNPROXY=m +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_NF_SOCKET_IPV6=y +CONFIG_NFT_DUP_IPV6=m +CONFIG_NFT_FIB_IPV6=m +CONFIG_NF_FLOW_TABLE_IPV6=m +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_SRH=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_SYNPROXY=m +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_IP6_NF_NAT=y +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_IP6_NF_TARGET_NPT=m +CONFIG_NF_TABLES_BRIDGE=m +CONFIG_NFT_BRIDGE_META=m +CONFIG_NFT_BRIDGE_REJECT=m +CONFIG_NF_CONNTRACK_BRIDGE=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1=y +CONFIG_SCTP_COOKIE_HMAC_MD5=y +CONFIG_RDS=m +CONFIG_RDS_TCP=m +CONFIG_TIPC=m +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +CONFIG_ATM_LANE=m +CONFIG_ATM_BR2684=m +CONFIG_L2TP=m +CONFIG_L2TP_DEBUGFS=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m +CONFIG_BRIDGE=y +CONFIG_NET_DSA=m +CONFIG_NET_DSA_TAG_GSWIP=m +CONFIG_NET_DSA_TAG_SJA1105=m +CONFIG_NET_DSA_TAG_TRAILER=m +CONFIG_VLAN_8021Q=m +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=m +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_6LOWPAN=m +CONFIG_6LOWPAN_DEBUGFS=y +CONFIG_6LOWPAN_GHC_EXT_HDR_HOP=m +CONFIG_6LOWPAN_GHC_UDP=m +CONFIG_6LOWPAN_GHC_ICMPV6=m +CONFIG_6LOWPAN_GHC_EXT_HDR_DEST=m +CONFIG_6LOWPAN_GHC_EXT_HDR_FRAG=m +CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m +CONFIG_IEEE802154=m +CONFIG_IEEE802154_6LOWPAN=m +CONFIG_MAC802154=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_CBS=m +CONFIG_NET_SCH_ETF=m +CONFIG_NET_SCH_TAPRIO=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=y +CONFIG_NET_SCH_CAKE=m +CONFIG_NET_SCH_FQ=m +CONFIG_NET_SCH_HHF=m +CONFIG_NET_SCH_PIE=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_SCH_PLUG=m +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_CLS_CGROUP=y +CONFIG_NET_CLS_BPF=m +CONFIG_NET_CLS_FLOWER=m +CONFIG_NET_CLS_MATCHALL=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_CANID=m +CONFIG_NET_EMATCH_IPSET=m +CONFIG_NET_EMATCH_IPT=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_SAMPLE=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_ACT_MPLS=m +CONFIG_NET_ACT_VLAN=m +CONFIG_NET_ACT_BPF=m +CONFIG_NET_ACT_CONNMARK=m +CONFIG_NET_ACT_CTINFO=m +CONFIG_NET_ACT_SKBMOD=m +CONFIG_NET_ACT_IFE=m +CONFIG_NET_ACT_TUNNEL_KEY=m +CONFIG_NET_ACT_CT=m +CONFIG_NET_IFE_SKBMARK=m +CONFIG_NET_IFE_SKBPRIO=m +CONFIG_NET_IFE_SKBTCINDEX=m +CONFIG_NET_TC_SKB_EXT=y +CONFIG_DCB=y +CONFIG_BATMAN_ADV=m +CONFIG_BATMAN_ADV_NC=y +CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m +CONFIG_NETLINK_DIAG=m +CONFIG_MPLS_ROUTING=m +CONFIG_MPLS_IPTUNNEL=m +CONFIG_NET_NCSI=y +CONFIG_NCSI_OEM_CMD_GET_MAC=y +CONFIG_CGROUP_NET_PRIO=y +CONFIG_NET_PKTGEN=m +CONFIG_HAMRADIO=y +CONFIG_AX25=m +CONFIG_NETROM=m +CONFIG_ROSE=m +CONFIG_MKISS=m +CONFIG_6PACK=m +CONFIG_BPQETHER=m +CONFIG_BAYCOM_SER_FDX=m +CONFIG_BAYCOM_SER_HDX=m +CONFIG_YAM=m +CONFIG_CAN=m +CONFIG_CAN_VCAN=m +CONFIG_CAN_VXCAN=m +CONFIG_CAN_SLCAN=m +CONFIG_CAN_C_CAN=m +CONFIG_CAN_C_CAN_PLATFORM=m +CONFIG_CAN_C_CAN_PCI=m +CONFIG_CAN_CC770=m +CONFIG_CAN_CC770_PLATFORM=m +CONFIG_CAN_IFI_CANFD=m +CONFIG_CAN_M_CAN=m +CONFIG_CAN_PEAK_PCIEFD=m +CONFIG_CAN_SJA1000=m +CONFIG_CAN_EMS_PCI=m +CONFIG_CAN_KVASER_PCI=m +CONFIG_CAN_PEAK_PCI=m +CONFIG_CAN_PLX_PCI=m +CONFIG_CAN_SJA1000_PLATFORM=m +CONFIG_CAN_SOFTING=m +CONFIG_CAN_HI311X=m +CONFIG_CAN_8DEV_USB=m +CONFIG_CAN_EMS_USB=m +CONFIG_CAN_ESD_USB2=m +CONFIG_CAN_GS_USB=m +CONFIG_CAN_KVASER_USB=m +CONFIG_CAN_MCBA_USB=m +CONFIG_CAN_PEAK_USB=m +CONFIG_BT=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m +CONFIG_BT_6LOWPAN=m +CONFIG_BT_LEDS=y +# CONFIG_BT_DEBUGFS is not set +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_INTEL=y +CONFIG_BT_HCIUART_AG6XX=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIVHCI=m +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_BT_ATH3K=m +CONFIG_BT_MTKSDIO=m +CONFIG_AF_RXRPC_IPV6=y +CONFIG_AF_RXRPC_DEBUG=y +CONFIG_RXKAD=y +CONFIG_AF_KCM=m +CONFIG_CFG80211=m +CONFIG_CFG80211_DEBUGFS=y +CONFIG_MAC80211=m +CONFIG_MAC80211_MESH=y +CONFIG_MAC80211_DEBUGFS=y +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_RFKILL_GPIO=m +CONFIG_NET_9P=m +CONFIG_NET_9P_VIRTIO=m +CONFIG_NFC=m +CONFIG_NFC_DIGITAL=m +CONFIG_NFC_NCI=m +CONFIG_NFC_NCI_SPI=m +CONFIG_NFC_HCI=m +CONFIG_NFC_SHDLC=y +CONFIG_NFC_TRF7970A=m +CONFIG_NFC_SIM=m +CONFIG_NFC_PORT100=m +CONFIG_NFC_PN544_I2C=m +CONFIG_NFC_PN533_USB=m +CONFIG_NFC_PN533_I2C=m +CONFIG_NFC_MICROREAD_I2C=m +CONFIG_NFC_MRVL_USB=m +CONFIG_NFC_ST21NFCA_I2C=m +CONFIG_NFC_NXP_NCI=m +CONFIG_NFC_NXP_NCI_I2C=m +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_HOTPLUG_PCI_PCIE=y +CONFIG_PCIE_PTM=y +CONFIG_PCI_DEBUG=y +CONFIG_PCI_STUB=y +CONFIG_PCI_PF_STUB=m +CONFIG_PCI_IOV=y +CONFIG_PCI_PRI=y +CONFIG_PCI_PASID=y +CONFIG_HOTPLUG_PCI=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCIE_XILINX=y +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_SW_SWITCHTEC=y +CONFIG_PCCARD=y +CONFIG_YENTA=m +CONFIG_PD6729=m +CONFIG_I82092=m +CONFIG_RAPIDIO=m +CONFIG_RAPIDIO_TSI721=m +CONFIG_RAPIDIO_ENUM_BASIC=m +CONFIG_RAPIDIO_CHMAN=m +CONFIG_RAPIDIO_MPORT_CDEV=m +CONFIG_RAPIDIO_TSI57X=m +CONFIG_RAPIDIO_CPS_XX=m +CONFIG_RAPIDIO_TSI568=m +CONFIG_RAPIDIO_CPS_GEN2=m +CONFIG_RAPIDIO_RXS_GEN3=m +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_COMPRESS=y +CONFIG_DEBUG_DEVRES=y +CONFIG_SUN50I_DE2_BUS=y +CONFIG_MTD=y +CONFIG_MTD_OF_PARTS=m +CONFIG_MTD_BLOCK=y +CONFIG_MTD_PARTITIONED_MASTER=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_BLOCK2MTD=m +CONFIG_MTD_RAW_NAND=m +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_FASTMAP=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_BLK_DEV_NULL_BLK=m +CONFIG_ZRAM=m +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=0 +CONFIG_BLK_DEV_DRBD=m +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_SX8=m +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_CDROM_PKTCDVD=m +CONFIG_ATA_OVER_ETH=m +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_RBD=m +CONFIG_BLK_DEV_NVME=m +CONFIG_NVME_MULTIPATH=y +CONFIG_NVME_FC=m +CONFIG_NVME_TCP=m +CONFIG_NVME_TARGET=m +CONFIG_NVME_TARGET_LOOP=m +CONFIG_NVME_TARGET_FC=m +CONFIG_NVME_TARGET_FCLOOP=m +CONFIG_NVME_TARGET_TCP=m +CONFIG_ENCLOSURE_SERVICES=m +CONFIG_APDS9802ALS=m +CONFIG_ISL29003=m +CONFIG_ISL29020=m +CONFIG_SENSORS_TSL2550=m +CONFIG_SENSORS_BH1770=m +CONFIG_SENSORS_APDS990X=m +CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_LEGACY=m +CONFIG_EEPROM_MAX6875=m +CONFIG_EEPROM_IDT_89HPESX=m +CONFIG_EEPROM_EE1004=m +CONFIG_SENSORS_LIS3_I2C=m +CONFIG_ALTERA_STAPL=m +CONFIG_ECHO=m +CONFIG_MISC_ALCOR_PCI=m +CONFIG_MISC_RTSX_PCI=m +CONFIG_MISC_RTSX_USB=m +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=m +CONFIG_BLK_DEV_SR=y +CONFIG_CHR_DEV_SG=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_CHR_DEV_SCH=m +CONFIG_SCSI_ENCLOSURE=m +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_FC_ATTRS=m +CONFIG_SCSI_SAS_ATA=y +CONFIG_ISCSI_TCP=m +CONFIG_SCSI_CXGB3_ISCSI=m +CONFIG_SCSI_CXGB4_ISCSI=m +CONFIG_SCSI_BNX2_ISCSI=m +CONFIG_SCSI_BNX2X_FCOE=m +CONFIG_BE2ISCSI=m +CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_SCSI_HPSA=m +CONFIG_SCSI_3W_9XXX=m +CONFIG_SCSI_3W_SAS=m +CONFIG_SCSI_ACARD=m +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 +CONFIG_AIC7XXX_RESET_DELAY_MS=15000 +# CONFIG_AIC7XXX_DEBUG_ENABLE is not set +# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set +CONFIG_SCSI_AIC79XX=m +CONFIG_AIC79XX_CMDS_PER_DEVICE=4 +CONFIG_AIC79XX_RESET_DELAY_MS=15000 +# CONFIG_AIC79XX_DEBUG_ENABLE is not set +# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set +CONFIG_SCSI_MVSAS=m +# CONFIG_SCSI_MVSAS_DEBUG is not set +CONFIG_SCSI_MVSAS_TASKLET=y +CONFIG_SCSI_MVUMI=m +CONFIG_SCSI_ARCMSR=m +CONFIG_SCSI_ESAS2R=m +CONFIG_MEGARAID_NEWGEN=y +CONFIG_MEGARAID_MM=m +CONFIG_MEGARAID_MAILBOX=m +CONFIG_MEGARAID_LEGACY=m +CONFIG_MEGARAID_SAS=m +CONFIG_SCSI_MPT2SAS=m +CONFIG_SCSI_SMARTPQI=m +CONFIG_SCSI_HPTIOP=m +CONFIG_SCSI_MYRB=m +CONFIG_SCSI_MYRS=m +CONFIG_LIBFC=m +CONFIG_LIBFCOE=m +CONFIG_FCOE=m +CONFIG_SCSI_SNIC=m +CONFIG_SCSI_DMX3191D=m +CONFIG_SCSI_FDOMAIN_PCI=m +CONFIG_SCSI_IPS=m +CONFIG_SCSI_INITIO=m +CONFIG_SCSI_INIA100=m +CONFIG_SCSI_STEX=m +CONFIG_SCSI_SYM53C8XX_2=m +CONFIG_SCSI_IPR=m +CONFIG_SCSI_QLOGIC_1280=m +CONFIG_SCSI_QLA_FC=m +CONFIG_TCM_QLA2XXX=m +CONFIG_SCSI_QLA_ISCSI=m +CONFIG_QEDI=m +CONFIG_QEDF=m +CONFIG_SCSI_DC395x=m +CONFIG_SCSI_AM53C974=m +CONFIG_SCSI_WD719X=m +CONFIG_SCSI_DEBUG=m +CONFIG_SCSI_PMCRAID=m +CONFIG_SCSI_PM8001=m +CONFIG_SCSI_BFA_FC=m +CONFIG_SCSI_VIRTIO=y +CONFIG_SCSI_CHELSIO_FCOE=m +CONFIG_SCSI_DH=y +CONFIG_SCSI_DH_RDAC=m +CONFIG_SCSI_DH_HP_SW=m +CONFIG_SCSI_DH_EMC=m +CONFIG_SCSI_DH_ALUA=m +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_MOBILE_LPM_POLICY=3 +CONFIG_SATA_AHCI_PLATFORM=m +CONFIG_SATA_INIC162X=m +CONFIG_SATA_ACARD_AHCI=m +CONFIG_SATA_SIL24=m +CONFIG_PDC_ADMA=m +CONFIG_SATA_QSTOR=m +CONFIG_SATA_SX4=m +# CONFIG_ATA_BMDMA is not set +CONFIG_PATA_CMD640_PCI=m +CONFIG_PATA_NS87410=m +CONFIG_PATA_OPTI=m +CONFIG_PATA_PCMCIA=m +CONFIG_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_MULTIPATH=m +CONFIG_MD_FAULTY=m +CONFIG_BCACHE=m +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING=y +CONFIG_DM_UNSTRIPED=y +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=y +CONFIG_DM_THIN_PROVISIONING=y +CONFIG_DM_CACHE=y +CONFIG_DM_WRITECACHE=m +CONFIG_DM_MIRROR=y +CONFIG_DM_LOG_USERSPACE=y +CONFIG_DM_RAID=y +CONFIG_DM_ZERO=y +CONFIG_DM_MULTIPATH=y +CONFIG_DM_MULTIPATH_QL=y +CONFIG_DM_MULTIPATH_ST=y +CONFIG_DM_DELAY=y +CONFIG_DM_DUST=m +CONFIG_DM_INIT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_FLAKEY=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_DM_SWITCH=y +CONFIG_DM_LOG_WRITES=y +CONFIG_DM_INTEGRITY=y +CONFIG_DM_ZONED=y +CONFIG_TARGET_CORE=y +CONFIG_TCM_IBLOCK=y +CONFIG_TCM_FILEIO=y +CONFIG_TCM_PSCSI=y +CONFIG_TCM_USER2=m +CONFIG_LOOPBACK_TARGET=m +CONFIG_TCM_FC=m +CONFIG_ISCSI_TARGET=m +CONFIG_ISCSI_TARGET_CXGB4=m +CONFIG_SBP_TARGET=m +CONFIG_FUSION=y +CONFIG_FUSION_SPI=m +CONFIG_FUSION_FC=m +CONFIG_FUSION_SAS=m +CONFIG_FUSION_MAX_SGE=40 +CONFIG_FUSION_CTL=m +CONFIG_FUSION_LAN=m +CONFIG_FUSION_LOGGING=y +CONFIG_FIREWIRE=m +CONFIG_FIREWIRE_OHCI=m +CONFIG_FIREWIRE_SBP2=m +CONFIG_FIREWIRE_NET=m +CONFIG_FIREWIRE_NOSY=m +CONFIG_BONDING=m +CONFIG_DUMMY=m +CONFIG_EQUALIZER=m +CONFIG_NET_FC=y +CONFIG_IFB=m +CONFIG_NET_TEAM=m +CONFIG_NET_TEAM_MODE_BROADCAST=m +CONFIG_NET_TEAM_MODE_ROUNDROBIN=m +CONFIG_NET_TEAM_MODE_RANDOM=m +CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m +CONFIG_NET_TEAM_MODE_LOADBALANCE=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_IPVLAN=m +CONFIG_IPVTAP=m +CONFIG_GENEVE=m +CONFIG_GTP=m +CONFIG_MACSEC=m +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_RIONET=m +CONFIG_TUN=m +CONFIG_VETH=m +CONFIG_VIRTIO_NET=y +CONFIG_NLMON=m +CONFIG_NET_VRF=m +CONFIG_ATM_TCP=m +CONFIG_ATM_ENI=m +CONFIG_ATM_NICSTAR=m +CONFIG_ATM_HE=m +CONFIG_ATM_SOLOS=m +CONFIG_B53_SPI_DRIVER=m +CONFIG_B53_MDIO_DRIVER=m +CONFIG_B53_MMAP_DRIVER=m +CONFIG_B53_SRAB_DRIVER=m +CONFIG_B53_SERDES=m +CONFIG_NET_DSA_BCM_SF2=m +CONFIG_NET_DSA_LOOP=m +CONFIG_NET_DSA_MT7530=m +CONFIG_NET_DSA_MICROCHIP_KSZ9477=m +CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI=m +CONFIG_NET_DSA_MV88E6XXX=m +CONFIG_NET_DSA_MV88E6XXX_PTP=y +CONFIG_NET_DSA_QCA8K=m +CONFIG_NET_DSA_SMSC_LAN9303_I2C=m +CONFIG_NET_DSA_SMSC_LAN9303_MDIO=m +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_3C589=m +CONFIG_VORTEX=m +CONFIG_TYPHOON=m +CONFIG_ADAPTEC_STARFIRE=m +CONFIG_ET131X=m +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_ALLWINNER is not set +CONFIG_ACENIC=m +CONFIG_ALTERA_TSE=m +CONFIG_AMD8111_ETH=m +CONFIG_PCNET32=m +CONFIG_PCMCIA_NMCLAN=m +# CONFIG_NET_VENDOR_ASIX is not set +CONFIG_ATL2=m +CONFIG_ATL1=m +CONFIG_ATL1E=m +CONFIG_ATL1C=m +CONFIG_ALX=m +CONFIG_B44=m +CONFIG_BCMGENET=m +CONFIG_TIGON3=m +CONFIG_BNX2X=m +CONFIG_BNXT=m +CONFIG_BNXT_DCB=y +CONFIG_BNA=m +CONFIG_MACB=m +CONFIG_MACB_PCI=m +# CONFIG_NET_VENDOR_CAVIUM is not set +CONFIG_CHELSIO_T1=m +CONFIG_CHELSIO_T1_1G=y +CONFIG_CHELSIO_T4_DCB=y +CONFIG_CHELSIO_T4VF=m +CONFIG_CHELSIO_IPSEC_INLINE=m +CONFIG_ENIC=m +# CONFIG_NET_VENDOR_CORTINA is not set +CONFIG_DNET=m +CONFIG_NET_TULIP=y +CONFIG_DE2104X=m +CONFIG_TULIP=m +CONFIG_TULIP_MMIO=y +CONFIG_WINBOND_840=m +CONFIG_DM9102=m +CONFIG_ULI526X=m +CONFIG_PCMCIA_XIRCOM=m +CONFIG_DL2K=m +CONFIG_SUNDANCE=m +# CONFIG_BE2NET_HWMON is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_FUJITSU is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_I825XX is not set +CONFIG_E100=m +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_IGB=m +CONFIG_IGBVF=m +CONFIG_IXGB=m +CONFIG_IXGBE=m +CONFIG_IXGBE_DCB=y +CONFIG_IXGBEVF=m +CONFIG_I40E=m +CONFIG_I40EVF=m +CONFIG_ICE=m +# CONFIG_ICE_SWITCHDEV is not set +CONFIG_FM10K=m +CONFIG_IGC=m +CONFIG_JME=m +# CONFIG_NET_VENDOR_LITEX is not set +CONFIG_MVMDIO=m +CONFIG_SKGE=m +CONFIG_SKGE_GENESIS=y +CONFIG_SKY2=m +CONFIG_MLX4_EN=m +CONFIG_MLX5_CORE=m +CONFIG_MLX5_CORE_EN=y +CONFIG_MLX5_CORE_IPOIB=y +CONFIG_MLXSW_CORE=m +CONFIG_KSZ884X_PCI=m +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MICROSEMI is not set +CONFIG_MYRI10GE=m +CONFIG_FEALNX=m +CONFIG_NATSEMI=m +CONFIG_NS83820=m +CONFIG_S2IO=m +CONFIG_VXGE=m +CONFIG_NFP=m +# CONFIG_NFP_APP_ABM_NIC is not set +# CONFIG_NET_VENDOR_NI is not set +CONFIG_PCMCIA_AXNET=m +CONFIG_NE2K_PCI=m +CONFIG_PCMCIA_PCNET=m +CONFIG_FORCEDETH=m +CONFIG_ETHOC=m +CONFIG_HAMACHI=m +CONFIG_YELLOWFIN=m +CONFIG_QLA3XXX=m +CONFIG_QLCNIC=m +CONFIG_NETXEN_NIC=m +CONFIG_QED=m +CONFIG_QEDE=m +# CONFIG_NET_VENDOR_QUALCOMM is not set +CONFIG_R6040=m +CONFIG_8139CP=m +CONFIG_8139TOO=m +# CONFIG_8139TOO_PIO is not set +CONFIG_8139TOO_8129=y +CONFIG_R8169=m +# CONFIG_NET_VENDOR_RENESAS is not set +CONFIG_ROCKER=m +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +CONFIG_SC92031=m +CONFIG_SIS900=m +CONFIG_SIS190=m +CONFIG_PCMCIA_SMC91C92=m +CONFIG_EPIC100=m +CONFIG_SMSC911X=m +CONFIG_SMSC9420=m +# CONFIG_NET_VENDOR_SOCIONEXT is not set +CONFIG_STMMAC_ETH=m +CONFIG_DWMAC_DWC_QOS_ETH=m +# CONFIG_DWMAC_SUNXI is not set +CONFIG_DWMAC_INTEL_PLAT=m +CONFIG_DWMAC_LOONGSON=m +CONFIG_STMMAC_PCI=m +CONFIG_HAPPYMEAL=m +CONFIG_SUNGEM=m +CONFIG_CASSINI=m +CONFIG_NIU=m +# CONFIG_NET_VENDOR_SYNOPSYS is not set +CONFIG_TEHUTI=m +CONFIG_TLAN=m +CONFIG_VIA_RHINE=m +CONFIG_VIA_RHINE_MMIO=y +CONFIG_VIA_VELOCITY=m +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5300=m +CONFIG_WIZNET_W5100_SPI=m +CONFIG_PCMCIA_XIRC2PS=m +CONFIG_LED_TRIGGER_PHY=y +CONFIG_SFP=m +CONFIG_AMD_PHY=m +CONFIG_ADIN_PHY=m +CONFIG_AQUANTIA_PHY=m +CONFIG_BROADCOM_PHY=m +CONFIG_BCM87XX_PHY=m +CONFIG_CICADA_PHY=m +CONFIG_CORTINA_PHY=m +CONFIG_DAVICOM_PHY=m +CONFIG_ICPLUS_PHY=m +CONFIG_LXT_PHY=m +CONFIG_INTEL_XWAY_PHY=m +CONFIG_LSI_ET1011C_PHY=m +CONFIG_MARVELL_PHY=m +CONFIG_MARVELL_10G_PHY=m +CONFIG_MICREL_PHY=m +CONFIG_MICROSEMI_PHY=m +CONFIG_NATIONAL_PHY=m +CONFIG_QSEMI_PHY=m +CONFIG_STE10XP=m +CONFIG_TERANETICS_PHY=m +CONFIG_DP83822_PHY=m +CONFIG_DP83848_PHY=m +CONFIG_VITESSE_PHY=m +CONFIG_XILINX_GMII2RGMII=m +CONFIG_MDIO_BITBANG=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOATM=m +CONFIG_PPPOE=m +CONFIG_PPTP=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_HUAWEI_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9700=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_NET_CX82310_ETH=m +CONFIG_USB_NET_KALMIA=m +CONFIG_USB_NET_QMI_WWAN=m +CONFIG_USB_HSO=m +CONFIG_USB_NET_INT51X1=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +CONFIG_USB_NET_CH9200=m +CONFIG_USB_NET_AQC111=m +# CONFIG_WLAN_VENDOR_ADMTEK is not set +CONFIG_ATH5K=m +CONFIG_ATH5K_DEBUG=y +CONFIG_ATH9K_HTC=m +CONFIG_CARL9170=m +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_ATH6KL_USB=m +CONFIG_ATH6KL_DEBUG=y +CONFIG_AR5523=m +CONFIG_WIL6210=m +CONFIG_ATH10K=m +CONFIG_ATH10K_PCI=m +CONFIG_ATH10K_SDIO=m +CONFIG_ATH10K_USB=m +CONFIG_ATH10K_DEBUGFS=y +CONFIG_WCN36XX=m +# CONFIG_WLAN_VENDOR_ATMEL is not set +CONFIG_B43=m +CONFIG_B43_SDIO=y +CONFIG_B43LEGACY=m +# CONFIG_B43LEGACY_DEBUG is not set +CONFIG_BRCMSMAC=m +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_USB=y +CONFIG_BRCMFMAC_PCIE=y +# CONFIG_WLAN_VENDOR_CISCO is not set +CONFIG_IWL4965=m +CONFIG_IWL3945=m +CONFIG_IWLEGACY_DEBUG=y +CONFIG_IWLEGACY_DEBUGFS=y +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_IWLWIFI_DEBUG=y +CONFIG_IWLWIFI_DEBUGFS=y +CONFIG_HERMES=m +CONFIG_HERMES_PRISM=y +CONFIG_PLX_HERMES=m +CONFIG_NORTEL_HERMES=m +CONFIG_PCI_HERMES=m +CONFIG_PCMCIA_HERMES=m +CONFIG_ORINOCO_USB=m +CONFIG_P54_COMMON=m +CONFIG_P54_USB=m +CONFIG_P54_PCI=m +CONFIG_LIBERTAS=m +CONFIG_LIBERTAS_USB=m +CONFIG_LIBERTAS_CS=m +CONFIG_LIBERTAS_SDIO=m +CONFIG_LIBERTAS_MESH=y +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +CONFIG_MWIFIEX_PCIE=m +CONFIG_MWIFIEX_USB=m +CONFIG_MWL8K=m +CONFIG_MT7601U=m +CONFIG_MT76x0U=m +CONFIG_MT76x0E=m +CONFIG_MT76x2E=m +CONFIG_MT76x2U=m +CONFIG_MT7603E=m +CONFIG_MT7615E=m +CONFIG_RT2X00=m +CONFIG_RT2400PCI=m +CONFIG_RT2500PCI=m +CONFIG_RT61PCI=m +CONFIG_RT2800PCI=m +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT3573=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_RT2X00_LIB_DEBUGFS=y +CONFIG_RTL8180=m +CONFIG_RTL8187=m +CONFIG_RTL8192CE=m +CONFIG_RTL8192SE=m +CONFIG_RTL8192DE=m +CONFIG_RTL8723AE=m +CONFIG_RTL8723BE=m +CONFIG_RTL8188EE=m +CONFIG_RTL8192EE=m +CONFIG_RTL8821AE=m +CONFIG_RTL8192CU=m +# CONFIG_RTLWIFI_DEBUG is not set +CONFIG_RTL8XXXU=m +CONFIG_RTL8XXXU_UNTESTED=y +CONFIG_RTW88=m +CONFIG_RTW88_8822BE=m +CONFIG_RTW88_8822CE=m +CONFIG_RSI_91X=m +CONFIG_CW1200=m +CONFIG_CW1200_WLAN_SDIO=m +CONFIG_CW1200_WLAN_SPI=m +CONFIG_WL1251=m +CONFIG_WL1251_SPI=m +CONFIG_WL1251_SDIO=m +CONFIG_WL12XX=m +CONFIG_WL18XX=m +CONFIG_WLCORE_SPI=m +CONFIG_WLCORE_SDIO=m +CONFIG_ZD1211RW=m +CONFIG_QTNFMAC_PCIE=m +CONFIG_MAC80211_HWSIM=m +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_VIRT_WIFI=m +CONFIG_IEEE802154_FAKELB=m +CONFIG_IEEE802154_AT86RF230=m +CONFIG_IEEE802154_MRF24J40=m +CONFIG_IEEE802154_CC2520=m +CONFIG_IEEE802154_ATUSB=m +CONFIG_IEEE802154_ADF7242=m +CONFIG_IEEE802154_CA8210=m +CONFIG_IEEE802154_MCR20A=m +CONFIG_INPUT_SPARSEKMAP=m +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_JOYDEV=m +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_QT1050=m +CONFIG_KEYBOARD_QT1070=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_SUN4I_LRADC=y +CONFIG_KEYBOARD_TM2_TOUCHKEY=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_MOUSE_PS2_SENTELIC=y +CONFIG_MOUSE_SERIAL=m +CONFIG_MOUSE_APPLETOUCH=m +CONFIG_MOUSE_BCM5974=m +CONFIG_MOUSE_CYAPA=m +CONFIG_MOUSE_ELAN_I2C=m +CONFIG_MOUSE_ELAN_I2C_SMBUS=y +CONFIG_MOUSE_VSXXXAA=m +CONFIG_MOUSE_SYNAPTICS_I2C=m +CONFIG_MOUSE_SYNAPTICS_USB=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=m +CONFIG_JOYSTICK_IFORCE_232=m +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDJOY=m +CONFIG_JOYSTICK_ZHENHUA=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_JOYSTICK_XPAD=m +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_JOYSTICK_PSXPAD_SPI=m +CONFIG_JOYSTICK_PSXPAD_SPI_FF=y +CONFIG_JOYSTICK_PXRC=m +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=m +CONFIG_TABLET_USB_AIPTEK=m +CONFIG_TABLET_USB_HANWANG=m +CONFIG_TABLET_USB_KBTAB=m +CONFIG_TABLET_USB_PEGASUS=m +CONFIG_TABLET_SERIAL_WACOM4=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_TOUCHSCREEN_AUO_PIXCIR=m +CONFIG_TOUCHSCREEN_DYNAPRO=m +CONFIG_TOUCHSCREEN_EETI=m +CONFIG_TOUCHSCREEN_EGALAX=m +CONFIG_TOUCHSCREEN_EGALAX_SERIAL=m +CONFIG_TOUCHSCREEN_FUJITSU=m +CONFIG_TOUCHSCREEN_ILI210X=m +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_TOUCHSCREEN_ELAN=m +CONFIG_TOUCHSCREEN_ELO=m +CONFIG_TOUCHSCREEN_WACOM_W8001=m +CONFIG_TOUCHSCREEN_WACOM_I2C=m +CONFIG_TOUCHSCREEN_MCS5000=m +CONFIG_TOUCHSCREEN_MMS114=m +CONFIG_TOUCHSCREEN_MTOUCH=m +CONFIG_TOUCHSCREEN_INEXIO=m +CONFIG_TOUCHSCREEN_MK712=m +CONFIG_TOUCHSCREEN_PENMOUNT=m +CONFIG_TOUCHSCREEN_EDT_FT5X06=m +CONFIG_TOUCHSCREEN_TOUCHRIGHT=m +CONFIG_TOUCHSCREEN_TOUCHWIN=m +CONFIG_TOUCHSCREEN_PIXCIR=m +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_TOUCHSCREEN_TOUCHIT213=m +CONFIG_TOUCHSCREEN_TSC_SERIO=m +CONFIG_TOUCHSCREEN_TSC2007=m +CONFIG_TOUCHSCREEN_RM_TS=m +CONFIG_TOUCHSCREEN_SILEAD=m +CONFIG_TOUCHSCREEN_SIS_I2C=m +CONFIG_TOUCHSCREEN_ST1232=m +CONFIG_TOUCHSCREEN_ZET6223=m +CONFIG_TOUCHSCREEN_ZFORCE=m +CONFIG_TOUCHSCREEN_IQS5XX=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_E3X0_BUTTON=m +CONFIG_INPUT_GPIO_VIBRA=m +CONFIG_INPUT_ATI_REMOTE2=m +CONFIG_INPUT_KEYSPAN_REMOTE=m +CONFIG_INPUT_KXTJ9=m +CONFIG_INPUT_POWERMATE=m +CONFIG_INPUT_YEALINK=m +CONFIG_INPUT_CM109=m +CONFIG_INPUT_UINPUT=m +CONFIG_INPUT_PWM_BEEPER=y +CONFIG_INPUT_GPIO_ROTARY_ENCODER=m +CONFIG_INPUT_CMA3000=m +CONFIG_INPUT_CMA3000_I2C=m +CONFIG_RMI4_I2C=m +CONFIG_RMI4_SPI=m +CONFIG_RMI4_SMB=m +CONFIG_RMI4_F34=y +CONFIG_RMI4_F55=y +CONFIG_LEGACY_PTY_COUNT=16 +# CONFIG_LDISC_AUTOLOAD is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=6 +CONFIG_SERIAL_8250_RUNTIME_UARTS=6 +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_EARLYCON_RISCV_SBI=y +CONFIG_HVC_RISCV_SBI=y +CONFIG_VIRTIO_CONSOLE=m +CONFIG_CARDMAN_4000=m +CONFIG_CARDMAN_4040=m +CONFIG_IPWIRELESS=m +CONFIG_TCG_TIS=y +CONFIG_TCG_TIS_SPI=m +CONFIG_TCG_ATMEL=m +CONFIG_TCG_VTPM_PROXY=m +CONFIG_XILLYBUS=m +CONFIG_XILLYBUS_PCIE=m +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_NFORCE2=m +CONFIG_I2C_NVIDIA_GPU=m +CONFIG_I2C_DESIGNWARE_PCI=m +CONFIG_I2C_MV64XXX=y +CONFIG_I2C_SIMTEC=m +CONFIG_I2C_DIOLAN_U2C=m +CONFIG_I2C_TINY_USB=m +CONFIG_I2C_STUB=m +CONFIG_I2C_SLAVE=y +CONFIG_I2C_SLAVE_EEPROM=m +CONFIG_SPI=y +CONFIG_SPI_SUN6I=y +CONFIG_SPI_SPIDEV=y +CONFIG_PPS_CLIENT_LDISC=m +CONFIG_PPS_CLIENT_GPIO=m +CONFIG_PTP_1588_CLOCK=m +CONFIG_PINCTRL=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_CADENCE=m +CONFIG_GPIO_SYSCON=y +CONFIG_GPIO_PCA953X=m +CONFIG_GPIO_PCF857X=y +CONFIG_GPIO_PCI_IDIO_16=m +CONFIG_W1=m +CONFIG_W1_MASTER_DS2490=m +CONFIG_W1_MASTER_DS2482=m +CONFIG_W1_SLAVE_THERM=m +CONFIG_W1_SLAVE_SMEM=m +CONFIG_W1_SLAVE_DS2405=m +CONFIG_W1_SLAVE_DS2408=m +# CONFIG_W1_SLAVE_DS2408_READBACK is not set +CONFIG_W1_SLAVE_DS2413=m +CONFIG_W1_SLAVE_DS2406=m +CONFIG_W1_SLAVE_DS2423=m +CONFIG_W1_SLAVE_DS2805=m +CONFIG_W1_SLAVE_DS2431=m +CONFIG_W1_SLAVE_DS2433=m +CONFIG_W1_SLAVE_DS2433_CRC=y +CONFIG_W1_SLAVE_DS2438=m +CONFIG_W1_SLAVE_DS2780=m +CONFIG_W1_SLAVE_DS2781=m +CONFIG_W1_SLAVE_DS28E04=m +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_SYSCON_REBOOT_MODE=y +CONFIG_CHARGER_LT3651=m +CONFIG_CHARGER_SMB347=m +CONFIG_SENSORS_AD7314=m +CONFIG_SENSORS_AD7414=m +CONFIG_SENSORS_AD7418=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1026=m +CONFIG_SENSORS_ADM1029=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ADM9240=m +CONFIG_SENSORS_ADT7310=m +CONFIG_SENSORS_ADT7410=m +CONFIG_SENSORS_ADT7411=m +CONFIG_SENSORS_ADT7462=m +CONFIG_SENSORS_ADT7470=m +CONFIG_SENSORS_ADT7475=m +CONFIG_SENSORS_ASC7621=m +CONFIG_SENSORS_ASPEED=m +CONFIG_SENSORS_ATXP1=m +CONFIG_SENSORS_DS620=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_F71805F=m +CONFIG_SENSORS_F71882FG=m +CONFIG_SENSORS_F75375S=m +CONFIG_SENSORS_FTSTEUTATES=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_GL520SM=m +CONFIG_SENSORS_G760A=m +CONFIG_SENSORS_G762=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_JC42=m +CONFIG_SENSORS_POWR1220=m +CONFIG_SENSORS_LINEAGE=m +CONFIG_SENSORS_LTC2945=m +CONFIG_SENSORS_LTC2990=m +CONFIG_SENSORS_LTC4151=m +CONFIG_SENSORS_LTC4215=m +CONFIG_SENSORS_LTC4222=m +CONFIG_SENSORS_LTC4245=m +CONFIG_SENSORS_LTC4260=m +CONFIG_SENSORS_LTC4261=m +CONFIG_SENSORS_MAX1111=m +CONFIG_SENSORS_MAX16065=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_MAX1668=m +CONFIG_SENSORS_MAX197=m +CONFIG_SENSORS_MAX31722=m +CONFIG_SENSORS_MAX6639=m +CONFIG_SENSORS_MAX6642=m +CONFIG_SENSORS_MAX6650=m +CONFIG_SENSORS_MAX6697=m +CONFIG_SENSORS_MAX31790=m +CONFIG_SENSORS_MCP3021=m +CONFIG_SENSORS_TC654=m +CONFIG_SENSORS_ADCXX=m +CONFIG_SENSORS_LM63=m +CONFIG_SENSORS_LM70=m +CONFIG_SENSORS_LM73=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM87=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_LM92=m +CONFIG_SENSORS_LM93=m +CONFIG_SENSORS_LM95234=m +CONFIG_SENSORS_LM95241=m +CONFIG_SENSORS_LM95245=m +CONFIG_SENSORS_PC87360=m +CONFIG_SENSORS_PC87427=m +CONFIG_SENSORS_NTC_THERMISTOR=m +CONFIG_SENSORS_NCT6683=m +CONFIG_SENSORS_NCT6775=m +CONFIG_SENSORS_NCT7802=m +CONFIG_SENSORS_NCT7904=m +CONFIG_SENSORS_NPCM7XX=m +CONFIG_SENSORS_PCF8591=m +CONFIG_PMBUS=m +CONFIG_SENSORS_ADM1275=m +CONFIG_SENSORS_LM25066=m +CONFIG_SENSORS_LTC2978=m +CONFIG_SENSORS_LTC3815=m +CONFIG_SENSORS_MAX16064=m +CONFIG_SENSORS_MAX20751=m +CONFIG_SENSORS_MAX34440=m +CONFIG_SENSORS_MAX8688=m +CONFIG_SENSORS_TPS40422=m +CONFIG_SENSORS_TPS53679=m +CONFIG_SENSORS_UCD9000=m +CONFIG_SENSORS_UCD9200=m +CONFIG_SENSORS_ZL6100=m +CONFIG_SENSORS_PWM_FAN=m +CONFIG_SENSORS_SHT15=m +CONFIG_SENSORS_SHT21=m +CONFIG_SENSORS_SHT3x=m +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_SIS5595=m +CONFIG_SENSORS_DME1737=m +CONFIG_SENSORS_EMC1403=m +CONFIG_SENSORS_EMC6W201=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_SMSC47M192=m +CONFIG_SENSORS_SMSC47B397=m +CONFIG_SENSORS_SCH5627=m +CONFIG_SENSORS_SCH5636=m +CONFIG_SENSORS_ADC128D818=m +CONFIG_SENSORS_ADS7828=m +CONFIG_SENSORS_ADS7871=m +CONFIG_SENSORS_AMC6821=m +CONFIG_SENSORS_INA209=m +CONFIG_SENSORS_INA2XX=m +CONFIG_SENSORS_INA3221=m +CONFIG_SENSORS_TC74=m +CONFIG_SENSORS_THMC50=m +CONFIG_SENSORS_TMP102=m +CONFIG_SENSORS_TMP103=m +CONFIG_SENSORS_TMP108=m +CONFIG_SENSORS_TMP401=m +CONFIG_SENSORS_TMP421=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_VT1211=m +CONFIG_SENSORS_VT8231=m +CONFIG_SENSORS_W83773G=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83791D=m +CONFIG_SENSORS_W83792D=m +CONFIG_SENSORS_W83793=m +CONFIG_SENSORS_W83795=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83L786NG=m +CONFIG_SENSORS_W83627HF=m +CONFIG_SENSORS_W83627EHF=m +CONFIG_THERMAL_STATISTICS=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_SUN8I_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_SYSFS=y +CONFIG_SUNXI_WATCHDOG=y +CONFIG_MFD_BD9571MWV=m +CONFIG_MFD_MAX77650=m +CONFIG_MFD_VIPERBOARD=m +CONFIG_MFD_SM501=m +CONFIG_MFD_SM501_GPIO=y +CONFIG_MFD_WL1273_CORE=m +CONFIG_MFD_VX855=m +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_PWM=y +CONFIG_RC_CORE=y +CONFIG_RC_MAP=m +CONFIG_LIRC=y +CONFIG_BPF_LIRC_MODE2=y +CONFIG_RC_DECODERS=y +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_SHARP_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_XMP_DECODER=m +CONFIG_IR_IMON_DECODER=m +CONFIG_IR_RCMM_DECODER=m +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_HIX5HD2=m +CONFIG_IR_IMON=m +CONFIG_IR_IMON_RAW=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_SPI=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_IGORPLUGUSB=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_RC_LOOPBACK=m +CONFIG_IR_GPIO_CIR=m +CONFIG_IR_GPIO_TX=m +CONFIG_IR_PWM_TX=m +CONFIG_MEDIA_CEC_SUPPORT=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_SUPPORT_FILTER=y +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_MEDIA_PLATFORM_SUPPORT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_FB=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_HRTIMER=m +CONFIG_SND_DYNAMIC_MINORS=y +# CONFIG_SND_SUPPORT_OLD_API is not set +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_DUMMY=m +CONFIG_SND_ALOOP=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m +CONFIG_SND_AC97_POWER_SAVE=y +CONFIG_SND_AD1889=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m +CONFIG_SND_BT87X=m +CONFIG_SND_CA0106=m +CONFIG_SND_CMIPCI=m +CONFIG_SND_OXYGEN=m +CONFIG_SND_CS4281=m +CONFIG_SND_CS46XX=m +CONFIG_SND_CTXFI=m +CONFIG_SND_DARLA20=m +CONFIG_SND_GINA20=m +CONFIG_SND_LAYLA20=m +CONFIG_SND_DARLA24=m +CONFIG_SND_GINA24=m +CONFIG_SND_LAYLA24=m +CONFIG_SND_MONA=m +CONFIG_SND_MIA=m +CONFIG_SND_ECHO3G=m +CONFIG_SND_INDIGO=m +CONFIG_SND_INDIGOIO=m +CONFIG_SND_INDIGODJ=m +CONFIG_SND_INDIGOIOX=m +CONFIG_SND_INDIGODJX=m +CONFIG_SND_ENS1370=m +CONFIG_SND_ENS1371=m +CONFIG_SND_FM801=m +CONFIG_SND_HDSP=m +CONFIG_SND_HDSPM=m +CONFIG_SND_ICE1724=m +CONFIG_SND_KORG1212=m +CONFIG_SND_LOLA=m +CONFIG_SND_LX6464ES=m +CONFIG_SND_MIXART=m +CONFIG_SND_NM256=m +CONFIG_SND_PCXHR=m +CONFIG_SND_RIPTIDE=m +CONFIG_SND_RME32=m +CONFIG_SND_RME96=m +CONFIG_SND_RME9652=m +CONFIG_SND_VIRTUOSO=m +CONFIG_SND_VX222=m +CONFIG_SND_YMFPCI=m +CONFIG_SND_HDA_PREALLOC_SIZE=4096 +# CONFIG_SND_SPI is not set +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m +CONFIG_SND_USB_CAIAQ_INPUT=y +CONFIG_SND_USB_6FIRE=m +CONFIG_SND_USB_HIFACE=m +CONFIG_SND_BCD2000=m +CONFIG_SND_USB_POD=m +CONFIG_SND_USB_PODHD=m +CONFIG_SND_USB_TONEPORT=m +CONFIG_SND_USB_VARIAX=m +CONFIG_SND_DICE=m +CONFIG_SND_OXFW=m +CONFIG_SND_ISIGHT=m +CONFIG_SND_FIREWORKS=m +CONFIG_SND_BEBOB=m +CONFIG_SND_FIREWIRE_DIGI00X=m +CONFIG_SND_FIREWIRE_TASCAM=m +CONFIG_SND_FIREWIRE_MOTU=m +CONFIG_SND_FIREFACE=m +# CONFIG_SND_PCMCIA is not set +CONFIG_SND_SOC=y +CONFIG_SND_SOC_AMD_ACP=m +CONFIG_SND_SOC_AMD_CZ_RT5645_MACH=m +CONFIG_SND_DESIGNWARE_I2S=m +CONFIG_SND_DESIGNWARE_PCM=y +CONFIG_SND_I2S_HI6210_I2S=m +CONFIG_SND_SOC_SOF_TOPLEVEL=y +CONFIG_SND_SOC_SOF_PCI=m +CONFIG_SND_SUN20I_CODEC=y +CONFIG_SND_SUN4I_I2S=y +CONFIG_SND_SUN4I_SPDIF=y +CONFIG_SND_SOC_ADAU1761_I2C=m +CONFIG_SND_SOC_ADAU1761_SPI=m +CONFIG_SND_SOC_AK5558=m +CONFIG_SND_SOC_BD28623=m +CONFIG_SND_SOC_CS35L34=m +CONFIG_SND_SOC_CS35L35=m +CONFIG_SND_SOC_CS35L36=m +CONFIG_SND_SOC_CS42L42=m +CONFIG_SND_SOC_CS43130=m +CONFIG_SND_SOC_CX2072X=m +CONFIG_SND_SOC_ES7134=m +CONFIG_SND_SOC_MAX98088=m +CONFIG_SND_SOC_MAX9867=m +CONFIG_SND_SOC_MAX98927=m +CONFIG_SND_SOC_PCM1789_I2C=m +CONFIG_SND_SOC_PCM186X_I2C=m +CONFIG_SND_SOC_PCM186X_SPI=m +CONFIG_SND_SOC_PCM3060_I2C=m +CONFIG_SND_SOC_PCM3060_SPI=m +CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TAS6424=m +CONFIG_SND_SOC_TDA7419=m +CONFIG_SND_SOC_TLV320AIC32X4_I2C=m +CONFIG_SND_SOC_TLV320AIC32X4_SPI=m +CONFIG_SND_SOC_TSCS42XX=m +CONFIG_SND_SOC_WM8524=m +CONFIG_SND_SOC_MAX9759=m +CONFIG_SND_SOC_NAU8824=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y +CONFIG_HIDRAW=y +CONFIG_UHID=m +CONFIG_HID_A4TECH=m +CONFIG_HID_ACCUTOUCH=m +CONFIG_HID_ACRUX=m +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=m +CONFIG_HID_APPLEIR=m +CONFIG_HID_AUREAL=m +CONFIG_HID_BELKIN=m +CONFIG_HID_BETOP_FF=m +CONFIG_HID_BIGBEN_FF=m +CONFIG_HID_CHERRY=m +CONFIG_HID_CHICONY=m +CONFIG_HID_CORSAIR=m +CONFIG_HID_COUGAR=m +CONFIG_HID_MACALLY=m +CONFIG_HID_PRODIKEYS=m +CONFIG_HID_CMEDIA=m +CONFIG_HID_CP2112=m +CONFIG_HID_CYPRESS=m +CONFIG_HID_DRAGONRISE=m +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=m +CONFIG_HID_ELAN=m +CONFIG_HID_ELECOM=m +CONFIG_HID_ELO=m +CONFIG_HID_EZKEY=m +CONFIG_HID_GEMBIRD=m +CONFIG_HID_GFRM=m +CONFIG_HID_HOLTEK=m +CONFIG_HOLTEK_FF=y +CONFIG_HID_GT683R=m +CONFIG_HID_KEYTOUCH=m +CONFIG_HID_KYE=m +CONFIG_HID_UCLOGIC=m +CONFIG_HID_WALTOP=m +CONFIG_HID_VIEWSONIC=m +CONFIG_HID_GYRATION=m +CONFIG_HID_ICADE=m +CONFIG_HID_ITE=m +CONFIG_HID_JABRA=m +CONFIG_HID_TWINHAN=m +CONFIG_HID_KENSINGTON=m +CONFIG_HID_LCPOWER=m +CONFIG_HID_LENOVO=m +CONFIG_HID_LOGITECH=m +CONFIG_HID_LOGITECH_DJ=m +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MALTRON=m +CONFIG_HID_MAYFLASH=m +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MONTEREY=m +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_NTI=m +CONFIG_HID_NTRIG=m +CONFIG_HID_ORTEK=m +CONFIG_HID_PANTHERLORD=m +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PENMOUNT=m +CONFIG_HID_PETALYNX=m +CONFIG_HID_PICOLCD=m +CONFIG_HID_PICOLCD_FB=y +CONFIG_HID_PICOLCD_BACKLIGHT=y +CONFIG_HID_PICOLCD_LEDS=y +CONFIG_HID_PICOLCD_CIR=y +CONFIG_HID_PLANTRONICS=m +CONFIG_HID_PRIMAX=m +CONFIG_HID_RETRODE=m +CONFIG_HID_ROCCAT=m +CONFIG_HID_SAITEK=m +CONFIG_HID_SAMSUNG=m +CONFIG_HID_SONY=m +CONFIG_SONY_FF=y +CONFIG_HID_SPEEDLINK=m +CONFIG_HID_STEAM=m +CONFIG_HID_STEELSERIES=m +CONFIG_HID_SUNPLUS=m +CONFIG_HID_RMI=m +CONFIG_HID_GREENASIA=m +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=m +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=m +CONFIG_HID_TOPSEED=m +CONFIG_HID_THINGM=m +CONFIG_HID_THRUSTMASTER=m +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_UDRAW_PS3=m +CONFIG_HID_WACOM=m +CONFIG_HID_WIIMOTE=m +CONFIG_HID_XINMO=m +CONFIG_HID_ZEROPLUS=m +CONFIG_ZEROPLUS_FF=y +CONFIG_HID_ZYDACRON=m +CONFIG_HID_SENSOR_HUB=m +CONFIG_HID_ALPS=m +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_LED_TRIG=y +CONFIG_USB_ULPI_BUS=m +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_OTG=y +CONFIG_USB_LEDS_TRIGGER_USBPORT=m +CONFIG_USB_MON=m +CONFIG_USB_XHCI_HCD=m +CONFIG_USB_XHCI_DBGCAP=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_UHCI_HCD=m +CONFIG_USB_SL811_HCD=m +CONFIG_USB_SL811_HCD_ISO=y +CONFIG_USB_PRINTER=m +CONFIG_USB_TMC=m +CONFIG_USB_STORAGE=m +CONFIG_USB_STORAGE_REALTEK=m +CONFIG_USB_STORAGE_DATAFAB=m +CONFIG_USB_STORAGE_FREECOM=m +CONFIG_USB_STORAGE_ISD200=m +CONFIG_USB_STORAGE_USBAT=m +CONFIG_USB_STORAGE_SDDR09=m +CONFIG_USB_STORAGE_SDDR55=m +CONFIG_USB_STORAGE_JUMPSHOT=m +CONFIG_USB_STORAGE_ALAUDA=m +CONFIG_USB_STORAGE_ONETOUCH=m +CONFIG_USB_STORAGE_KARMA=m +CONFIG_USB_STORAGE_CYPRESS_ATACB=m +CONFIG_USB_STORAGE_ENE_UB6250=m +CONFIG_USB_UAS=m +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USBIP_CORE=m +CONFIG_USBIP_VHCI_HCD=m +CONFIG_USBIP_HOST=m +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SUNXI=m +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_SIMPLE=m +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_F8153X=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_XSENS_MT=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m +CONFIG_USB_SERIAL_UPD78F0730=m +CONFIG_USB_SERIAL_DEBUG=m +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +CONFIG_USB_ADUTUX=m +CONFIG_USB_SEVSEG=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_IDMOUSE=m +CONFIG_USB_FTDI_ELAN=m +CONFIG_USB_APPLEDISPLAY=m +CONFIG_USB_SISUSBVGA=m +CONFIG_USB_LD=m +CONFIG_USB_TRANCEVIBRATOR=m +CONFIG_USB_IOWARRIOR=m +CONFIG_USB_ISIGHTFW=m +CONFIG_USB_YUREX=m +CONFIG_USB_HUB_USB251XB=m +CONFIG_USB_HSIC_USB3503=m +CONFIG_USB_HSIC_USB4604=m +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m +CONFIG_USB_CXACRU=m +CONFIG_USB_UEAGLEATM=m +CONFIG_USB_XUSBATM=m +CONFIG_NOP_USB_XCEIV=m +CONFIG_USB_GADGET=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_ETH=y +# CONFIG_USB_ETH_RNDIS is not set +CONFIG_USB_ETH_EEM=y +CONFIG_USB_ROLE_SWITCH=y +CONFIG_MMC=y +CONFIG_PWRSEQ_SD8787=m +CONFIG_PWRSEQ_SIMPLE=m +CONFIG_SDIO_UART=m +CONFIG_MMC_SDHCI=m +CONFIG_MMC_SDHCI_PCI=m +CONFIG_MMC_SDHCI_PLTFM=m +CONFIG_MMC_SDHCI_CADENCE=m +CONFIG_MMC_ALCOR=m +CONFIG_MMC_TIFM_SD=m +CONFIG_MMC_SPI=y +CONFIG_MMC_SDRICOH_CS=m +CONFIG_MMC_CB710=m +CONFIG_MMC_VIA_SDMMC=m +CONFIG_MMC_VUB300=m +CONFIG_MMC_USHC=m +CONFIG_MMC_REALTEK_PCI=m +CONFIG_MMC_REALTEK_USB=m +CONFIG_MMC_SUNXI=y +CONFIG_MMC_SDHCI_XENON=m +CONFIG_MEMSTICK=m +CONFIG_MSPRO_BLOCK=m +CONFIG_MEMSTICK_TIFM_MS=m +CONFIG_MEMSTICK_JMICRON_38X=m +CONFIG_MEMSTICK_R592=m +CONFIG_MEMSTICK_REALTEK_PCI=m +CONFIG_MEMSTICK_REALTEK_USB=m +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_CLASS_FLASH=m +CONFIG_LEDS_CLASS_MULTICOLOR=y +CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y +CONFIG_LEDS_AN30259A=m +CONFIG_LEDS_CR0014114=m +CONFIG_LEDS_LM3530=m +CONFIG_LEDS_LM3532=m +CONFIG_LEDS_LM3692X=m +CONFIG_LEDS_SUN50I_R329=m +CONFIG_LEDS_PCA9532=m +CONFIG_LEDS_PCA9532_GPIO=y +CONFIG_LEDS_GPIO=m +CONFIG_LEDS_LP3944=m +CONFIG_LEDS_LP3952=m +CONFIG_LEDS_PWM=y +CONFIG_LEDS_LT3593=m +CONFIG_LEDS_MAX77650=m +CONFIG_LEDS_IS31FL32XX=m +CONFIG_LEDS_BLINKM=m +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_MLXREG=m +CONFIG_LEDS_USER=m +CONFIG_LEDS_AS3645A=m +CONFIG_LEDS_LM3601X=m +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_MTD=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_ACTIVITY=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=y +CONFIG_LEDS_TRIGGER_CAMERA=y +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEDS_TRIGGER_NETDEV=y +CONFIG_LEDS_TRIGGER_PATTERN=y +CONFIG_LEDS_TRIGGER_AUDIO=y +CONFIG_ACCESSIBILITY=y +CONFIG_A11Y_BRAILLE_CONSOLE=y +CONFIG_INFINIBAND=m +CONFIG_INFINIBAND_USER_MAD=m +CONFIG_INFINIBAND_USER_ACCESS=m +CONFIG_INFINIBAND_MTHCA=m +CONFIG_INFINIBAND_CXGB4=m +CONFIG_INFINIBAND_EFA=m +CONFIG_MLX4_INFINIBAND=m +CONFIG_MLX5_INFINIBAND=m +CONFIG_INFINIBAND_OCRDMA=m +CONFIG_INFINIBAND_QEDR=m +CONFIG_RDMA_RXE=m +CONFIG_INFINIBAND_IPOIB=m +CONFIG_INFINIBAND_IPOIB_CM=y +CONFIG_INFINIBAND_IPOIB_DEBUG_DATA=y +CONFIG_INFINIBAND_SRP=m +CONFIG_INFINIBAND_SRPT=m +CONFIG_INFINIBAND_ISER=m +CONFIG_INFINIBAND_ISERT=m +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_ABEOZ9=m +CONFIG_RTC_DRV_ABX80X=m +CONFIG_RTC_DRV_DS1307=m +CONFIG_RTC_DRV_DS1374=m +CONFIG_RTC_DRV_DS1374_WDT=y +CONFIG_RTC_DRV_DS1672=m +CONFIG_RTC_DRV_MAX6900=m +CONFIG_RTC_DRV_RS5C372=m +CONFIG_RTC_DRV_ISL1208=m +CONFIG_RTC_DRV_ISL12022=m +CONFIG_RTC_DRV_ISL12026=m +CONFIG_RTC_DRV_X1205=m +CONFIG_RTC_DRV_PCF8523=m +CONFIG_RTC_DRV_PCF85063=m +CONFIG_RTC_DRV_PCF8563=m +CONFIG_RTC_DRV_PCF8583=m +CONFIG_RTC_DRV_M41T80=m +CONFIG_RTC_DRV_M41T80_WDT=y +CONFIG_RTC_DRV_BQ32K=m +CONFIG_RTC_DRV_FM3130=m +CONFIG_RTC_DRV_RX8010=m +CONFIG_RTC_DRV_RX8581=m +CONFIG_RTC_DRV_RX8025=m +CONFIG_RTC_DRV_EM3027=m +CONFIG_RTC_DRV_RV3028=m +CONFIG_RTC_DRV_SD3078=m +CONFIG_RTC_DRV_M41T93=m +CONFIG_RTC_DRV_M41T94=m +CONFIG_RTC_DRV_DS1305=m +CONFIG_RTC_DRV_DS1343=m +CONFIG_RTC_DRV_DS1347=m +CONFIG_RTC_DRV_DS1390=m +CONFIG_RTC_DRV_MAX6916=m +CONFIG_RTC_DRV_R9701=m +CONFIG_RTC_DRV_RX4581=m +CONFIG_RTC_DRV_RS5C348=m +CONFIG_RTC_DRV_MAX6902=m +CONFIG_RTC_DRV_PCF2123=m +CONFIG_RTC_DRV_MCP795=m +CONFIG_RTC_DRV_DS3232=m +# CONFIG_RTC_DRV_DS3232_HWMON is not set +CONFIG_RTC_DRV_PCF2127=m +CONFIG_RTC_DRV_RV3029C2=m +CONFIG_RTC_DRV_DS1286=m +CONFIG_RTC_DRV_DS1511=m +CONFIG_RTC_DRV_DS1553=m +CONFIG_RTC_DRV_DS1685_FAMILY=m +CONFIG_RTC_DRV_DS1742=m +CONFIG_RTC_DRV_DS2404=m +CONFIG_RTC_DRV_STK17TA8=m +CONFIG_RTC_DRV_M48T35=m +CONFIG_RTC_DRV_M48T59=m +CONFIG_RTC_DRV_MSM6242=m +CONFIG_RTC_DRV_BQ4802=m +CONFIG_RTC_DRV_RP5C01=m +CONFIG_RTC_DRV_V3020=m +CONFIG_RTC_DRV_SUN6I=y +CONFIG_RTC_DRV_R7301=m +CONFIG_DMADEVICES=y +CONFIG_DMA_SUN6I=y +CONFIG_SYNC_FILE=y +CONFIG_UDMABUF=y +CONFIG_AUXDISPLAY=y +CONFIG_HD44780=m +CONFIG_HT16K33=m +CONFIG_UIO_CIF=m +CONFIG_UIO_AEC=m +CONFIG_UIO_SERCOS3=m +CONFIG_UIO_PCI_GENERIC=m +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=m +CONFIG_VIRTIO_INPUT=m +CONFIG_VIRTIO_MMIO=y +CONFIG_STAGING=y +CONFIG_RTLLIB=m +CONFIG_RTL8192E=m +CONFIG_RTL8723BS=m +CONFIG_R8712U=m +CONFIG_STAGING_MEDIA=y +CONFIG_VIDEO_SUNXI=y +CONFIG_VIDEO_SUNXI_CEDRUS=m +CONFIG_QLGE=m +CONFIG_COMMON_CLK_SI544=m +CONFIG_COMMON_CLK_PWM=y +# CONFIG_CLK_SUNXI is not set +CONFIG_SUN8I_DE2_CCU=y +CONFIG_XILINX_VCU=m +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_SUN6I=y +CONFIG_MAILBOX=y +# CONFIG_SUN6I_MSGBOX is not set +CONFIG_SUN50I_IOMMU=y +CONFIG_REMOTEPROC=y +CONFIG_REMOTEPROC_CDEV=y +CONFIG_SUN8I_DSP_REMOTEPROC=y +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_VIRTIO=m +CONFIG_SOUNDWIRE=y +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=m +CONFIG_PWM=y +CONFIG_PWM_SUN8I_V536=y +CONFIG_PHY_SUN4I_USB=y +CONFIG_POWERCAP=y +CONFIG_ANDROID=y +CONFIG_NVMEM_SUNXI_SID=y +CONFIG_FPGA=m +CONFIG_ALTERA_PR_IP_CORE=m +CONFIG_ALTERA_PR_IP_CORE_PLAT=m +CONFIG_FPGA_MGR_ALTERA_PS_SPI=m +CONFIG_FPGA_MGR_ALTERA_CVP=m +CONFIG_FPGA_MGR_XILINX_SPI=m +CONFIG_FPGA_MGR_ICE40_SPI=m +CONFIG_FPGA_MGR_MACHXO2_SPI=m +CONFIG_XILINX_PR_DECOUPLER=m +CONFIG_OF_FPGA_REGION=m +CONFIG_FPGA_DFL=m +CONFIG_FPGA_DFL_AFU=m +CONFIG_FPGA_DFL_PCI=m +CONFIG_FSI=m +CONFIG_FSI_MASTER_GPIO=m +CONFIG_FSI_MASTER_HUB=m +CONFIG_FSI_SCOM=m +CONFIG_VALIDATE_FS_PARSER=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_REISERFS_FS=m +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +CONFIG_JFS_SECURITY=y +CONFIG_XFS_FS=y +CONFIG_XFS_QUOTA=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_XFS_ONLINE_SCRUB=y +CONFIG_GFS2_FS=m +CONFIG_GFS2_FS_LOCKING_DLM=y +CONFIG_OCFS2_FS=m +# CONFIG_OCFS2_FS_STATS is not set +# CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_NILFS2_FS=m +CONFIG_F2FS_FS=m +CONFIG_F2FS_FS_SECURITY=y +# CONFIG_F2FS_IOSTAT is not set +CONFIG_FS_DAX=y +CONFIG_FS_ENCRYPTION=y +CONFIG_FS_VERITY=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_QFMT_V2=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=m +CONFIG_CUSE=m +CONFIG_VIRTIO_FS=m +CONFIG_OVERLAY_FS=m +CONFIG_FSCACHE=m +CONFIG_FSCACHE_STATS=y +CONFIG_CACHEFILES=m +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_ORANGEFS_FS=m +CONFIG_AFFS_FS=m +CONFIG_ECRYPT_FS=m +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +CONFIG_JFFS2_FS=m +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_CRAMFS=m +CONFIG_SQUASHFS=m +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZSTD=y +CONFIG_MINIX_FS=m +CONFIG_ROMFS_FS=m +CONFIG_PSTORE=y +CONFIG_PSTORE_LZO_COMPRESS=m +CONFIG_PSTORE_LZ4_COMPRESS=m +CONFIG_PSTORE_LZ4HC_COMPRESS=m +CONFIG_PSTORE_842_COMPRESS=y +CONFIG_PSTORE_RAM=m +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +CONFIG_NFS_FS=y +# CONFIG_NFS_V2 is not set +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_SWAP=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_NFSD=m +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_NFSD_SCSILAYOUT=y +CONFIG_NFSD_FLEXFILELAYOUT=y +CONFIG_NFSD_V4_SECURITY_LABEL=y +CONFIG_SUNRPC_DEBUG=y +CONFIG_CEPH_FS=m +CONFIG_CEPH_FSCACHE=y +CONFIG_CEPH_FS_POSIX_ACL=y +CONFIG_CEPH_FS_SECURITY_LABEL=y +CONFIG_CIFS=m +CONFIG_CIFS_UPCALL=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_DFS_UPCALL=y +CONFIG_CIFS_FSCACHE=y +CONFIG_CODA_FS=m +CONFIG_AFS_FS=m +CONFIG_AFS_DEBUG=y +CONFIG_AFS_FSCACHE=y +CONFIG_9P_FS=m +CONFIG_9P_FSCACHE=y +CONFIG_9P_FS_POSIX_ACL=y +CONFIG_9P_FS_SECURITY=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_MAC_ROMAN=m +CONFIG_NLS_MAC_CELTIC=m +CONFIG_NLS_MAC_CENTEURO=m +CONFIG_NLS_MAC_CROATIAN=m +CONFIG_NLS_MAC_CYRILLIC=m +CONFIG_NLS_MAC_GAELIC=m +CONFIG_NLS_MAC_GREEK=m +CONFIG_NLS_MAC_ICELAND=m +CONFIG_NLS_MAC_INUIT=m +CONFIG_NLS_MAC_ROMANIAN=m +CONFIG_NLS_MAC_TURKISH=m +CONFIG_DLM=m +CONFIG_DLM_DEBUG=y +CONFIG_UNICODE=y +CONFIG_KEYS_REQUEST_CACHE=y +CONFIG_PERSISTENT_KEYRINGS=y +CONFIG_TRUSTED_KEYS=m +CONFIG_ENCRYPTED_KEYS=y +CONFIG_KEY_DH_OPERATIONS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_INFINIBAND=y +CONFIG_SECURITY_NETWORK_XFRM=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +CONFIG_SECURITY_YAMA=y +CONFIG_SECURITY_LOCKDOWN_LSM=y +CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y +CONFIG_INTEGRITY_SIGNATURE=y +CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y +CONFIG_IMA=y +CONFIG_IMA_DEFAULT_HASH_SHA256=y +CONFIG_IMA_WRITE_POLICY=y +CONFIG_IMA_APPRAISE=y +CONFIG_IMA_APPRAISE_MODSIG=y +# CONFIG_IMA_TRUSTED_KEYRING is not set +CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY=y +CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" +CONFIG_CRYPTO_FIPS=y +CONFIG_CRYPTO_USER=y +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_ECRDSA=m +CONFIG_CRYPTO_CHACHA20POLY1305=m +CONFIG_CRYPTO_AEGIS128=m +CONFIG_CRYPTO_CFB=m +CONFIG_CRYPTO_LRW=m +CONFIG_CRYPTO_OFB=m +CONFIG_CRYPTO_KEYWRAP=m +CONFIG_CRYPTO_ADIANTUM=m +CONFIG_CRYPTO_XCBC=m +CONFIG_CRYPTO_VMAC=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_RMD160=m +CONFIG_CRYPTO_SHA3=m +CONFIG_CRYPTO_SM3=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_AES_TI=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_SM4=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_ANSI_CPRNG=m +CONFIG_CRYPTO_DRBG_HASH=y +CONFIG_CRYPTO_DRBG_CTR=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_USER_API_RNG=y +CONFIG_CRYPTO_USER_API_AEAD=y +CONFIG_CRYPTO_STATS=y +CONFIG_CRYPTO_DEV_SUN8I_CE=y +CONFIG_CRYPTO_DEV_SUN8I_CE_HASH=y +CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG=y +CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG=y +CONFIG_CRYPTO_DEV_VIRTIO=m +CONFIG_PKCS8_PRIVATE_KEY_PARSER=m +CONFIG_SIGNED_PE_FILE_VERIFICATION=y +CONFIG_SECONDARY_TRUSTED_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_KEYRING=y +# CONFIG_RAID6_PQ_BENCHMARK is not set +CONFIG_CRC_CCITT=y +CONFIG_XZ_DEC_MICROLZMA=y +CONFIG_PRINTK_TIME=y +CONFIG_CONSOLE_LOGLEVEL_QUIET=3 +CONFIG_BOOT_PRINTK_DELAY=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_STRIP_ASM_SYMS=y +# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set +# CONFIG_FRAME_POINTER is not set +CONFIG_VMLINUX_MAP=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x0 +# CONFIG_DEBUG_MISC is not set +CONFIG_DEBUG_VM=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_SHIRQ=y +CONFIG_SOFTLOCKUP_DETECTOR=y +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_DEBUG_ATOMIC_SLEEP=y +CONFIG_BUG_ON_DATA_CORRUPTION=y +CONFIG_RCU_TORTURE_TEST=m +CONFIG_FUNCTION_PROFILER=y +CONFIG_STACK_TRACER=y +CONFIG_SCHED_TRACER=y +CONFIG_HWLAT_TRACER=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_RING_BUFFER_BENCHMARK=m +CONFIG_TRACE_EVAL_MAP_FILE=y +CONFIG_ATOMIC64_SELFTEST=y +CONFIG_ASYNC_RAID6_TEST=m +CONFIG_TEST_KSTRTOX=y @ arch/riscv/configs/rv32_defconfig:24 @ CONFIG_SMP=y CONFIG_HOTPLUG_CPU=y CONFIG_VIRTUALIZATION=y CONFIG_KVM=m +CONFIG_PM=y +CONFIG_CPU_IDLE=y CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y @ arch/riscv/include/asm/asm.h:70 @ #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 + #endif /* _ASM_RISCV_ASM_H */ @ arch/riscv/include/asm/cacheflush.h:25 @ static inline void flush_dcache_page(struct page *page) } #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 +#define ICACHE_IPA_X5 ".long 0x0382800b" +#define ICACHE_IVA_X5 ".long 0x0302800b" +#define SYNC_IS ".long 0x01b0000b" + +static inline void flush_icache_range(unsigned long start, unsigned long end) +{ + register unsigned long tmp asm("x5") = start & (~(L1_CACHE_BYTES-1)); + + for (; tmp < ALIGN(end, L1_CACHE_BYTES); tmp += L1_CACHE_BYTES) { + __asm__ __volatile__ ( + ICACHE_IVA_X5 + : + : "r" (tmp) + : "memory"); + } + + __asm__ __volatile__(SYNC_IS); + + return; +} + +static inline void flush_icache_range_phy(unsigned long start, unsigned long end) +{ + register unsigned long tmp asm("x5") = start & (~(L1_CACHE_BYTES-1)); + + for (; tmp < ALIGN(end, L1_CACHE_BYTES); tmp += L1_CACHE_BYTES) { + __asm__ __volatile__ ( + ICACHE_IPA_X5 + : + : "r" (tmp) + : "memory"); + } + + __asm__ __volatile__(SYNC_IS); + + return; +} + +static inline void __flush_icache_page(struct page *page) { + unsigned long start = PFN_PHYS(page_to_pfn(page)); + + flush_icache_range_phy(start, start + PAGE_SIZE); + + return; +} + /* * RISC-V doesn't have an instruction to flush parts of the instruction cache, * so instead we just flush the whole thing. */ -#define flush_icache_range(start, end) flush_icache_all() +#define flush_icache_range(start, end) flush_icache_range(start, end) #define flush_icache_user_page(vma, pg, addr, len) \ flush_icache_mm(vma->vm_mm, 0) @ arch/riscv/include/asm/cpuidle.h:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 Allwinner Ltd + * Copyright (C) 2021 Western Digital Corporation or its affiliates. + */ + +#ifndef _ASM_RISCV_CPUIDLE_H +#define _ASM_RISCV_CPUIDLE_H + +#include <asm/barrier.h> +#include <asm/processor.h> + +static inline void cpu_do_idle(void) +{ + /* + * Add mb() here to ensure that all + * IO/MEM accesses are completed prior + * to entering WFI. + */ + mb(); + wait_for_interrupt(); +} + +#endif @ arch/riscv/include/asm/pgtable-64.h:64 @ static inline void pud_clear(pud_t *pudp) static inline pmd_t *pud_pgtable(pud_t pud) { - return (pmd_t *)pfn_to_virt(pud_val(pud) >> _PAGE_PFN_SHIFT); + return (pmd_t *)pfn_to_virt((pud_val(pud) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT); } static inline struct page *pud_page(pud_t pud) { - return pfn_to_page(pud_val(pud) >> _PAGE_PFN_SHIFT); + return pfn_to_page((pud_val(pud) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT); } static inline pmd_t pfn_pmd(unsigned long pfn, pgprot_t prot) @ arch/riscv/include/asm/pgtable-64.h:79 @ static inline pmd_t pfn_pmd(unsigned long pfn, pgprot_t prot) static inline unsigned long _pmd_pfn(pmd_t pmd) { - return pmd_val(pmd) >> _PAGE_PFN_SHIFT; + return (pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT; } #define mk_pmd(page, prot) pfn_pmd(page_to_pfn(page), prot) @ arch/riscv/include/asm/pgtable-bits.h:27 @ #define _PAGE_DIRTY (1 << 7) /* Set by hardware on any write */ #define _PAGE_SOFT (1 << 8) /* Reserved for software */ +#define _PAGE_DMA_MASK __riscv_custom_pte.mask +#define _PAGE_DMA_CACHE __riscv_custom_pte.cache +#define _PAGE_DMA_IO __riscv_custom_pte.io +#define _PAGE_DMA_WC __riscv_custom_pte.wc + #define _PAGE_SPECIAL _PAGE_SOFT #define _PAGE_TABLE _PAGE_PRESENT @ arch/riscv/include/asm/pgtable-bits.h:43 @ #define _PAGE_PFN_SHIFT 10 +#ifndef __ASSEMBLY__ + +struct riscv_custom_pte { + unsigned long cache; + unsigned long mask; + unsigned long io; + unsigned long wc; +}; + +extern struct riscv_custom_pte __riscv_custom_pte; + /* Set of bits to preserve across pte_modify() */ #define _PAGE_CHG_MASK (~(unsigned long)(_PAGE_PRESENT | _PAGE_READ | \ _PAGE_WRITE | _PAGE_EXEC | \ - _PAGE_USER | _PAGE_GLOBAL)) + _PAGE_USER | _PAGE_GLOBAL | \ + _PAGE_DMA_MASK)) + +#endif + /* * when all of R/W/X are zero, the PTE is a pointer to the next level * of the page table; otherwise, it is a leaf PTE. @ arch/riscv/include/asm/pgtable.h:120 @ #define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) /* Page protection bits */ -#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_USER) +#define _PAGE_BASE (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_USER | _PAGE_DMA_CACHE) -#define PAGE_NONE __pgprot(_PAGE_PROT_NONE) +#define PAGE_NONE __pgprot(_PAGE_DMA_CACHE | _PAGE_PROT_NONE) #define PAGE_READ __pgprot(_PAGE_BASE | _PAGE_READ) #define PAGE_WRITE __pgprot(_PAGE_BASE | _PAGE_READ | _PAGE_WRITE) #define PAGE_EXEC __pgprot(_PAGE_BASE | _PAGE_EXEC) @ arch/riscv/include/asm/pgtable.h:141 @ | _PAGE_PRESENT \ | _PAGE_ACCESSED \ | _PAGE_DIRTY \ - | _PAGE_GLOBAL) + | _PAGE_GLOBAL \ + | _PAGE_DMA_CACHE) #define PAGE_KERNEL __pgprot(_PAGE_KERNEL) #define PAGE_KERNEL_READ __pgprot(_PAGE_KERNEL & ~_PAGE_WRITE) @ arch/riscv/include/asm/pgtable.h:152 @ #define PAGE_TABLE __pgprot(_PAGE_TABLE) -/* - * The RISC-V ISA doesn't yet specify how to query or modify PMAs, so we can't - * change the properties of memory regions. - */ -#define _PAGE_IOREMAP _PAGE_KERNEL +#define _PAGE_IOREMAP ((_PAGE_KERNEL & ~_PAGE_DMA_MASK) | _PAGE_DMA_IO) extern pgd_t swapper_pg_dir[]; @ arch/riscv/include/asm/pgtable.h:232 @ static inline unsigned long _pgd_pfn(pgd_t pgd) static inline struct page *pmd_page(pmd_t pmd) { - return pfn_to_page(pmd_val(pmd) >> _PAGE_PFN_SHIFT); + return pfn_to_page((pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT); } static inline unsigned long pmd_page_vaddr(pmd_t pmd) { - return (unsigned long)pfn_to_virt(pmd_val(pmd) >> _PAGE_PFN_SHIFT); + return (unsigned long)pfn_to_virt((pmd_val(pmd) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT); } static inline pte_t pmd_pte(pmd_t pmd) @ arch/riscv/include/asm/pgtable.h:253 @ static inline pte_t pud_pte(pud_t pud) /* Yields the page frame number (PFN) of a page table entry */ static inline unsigned long pte_pfn(pte_t pte) { - return (pte_val(pte) >> _PAGE_PFN_SHIFT); + return (pte_val(pte) & _PAGE_CHG_MASK) >> _PAGE_PFN_SHIFT; } #define pte_page(x) pfn_to_page(pte_pfn(x)) @ arch/riscv/include/asm/pgtable.h:492 @ static inline int ptep_clear_flush_young(struct vm_area_struct *vma, return ptep_test_and_clear_young(vma, address, ptep); } +#define pgprot_noncached pgprot_noncached +static inline pgprot_t pgprot_noncached(pgprot_t _prot) +{ + unsigned long prot = pgprot_val(_prot); + + prot &= ~_PAGE_DMA_MASK; + prot |= _PAGE_DMA_IO; + + return __pgprot(prot); +} + +#define pgprot_writecombine pgprot_writecombine +static inline pgprot_t pgprot_writecombine(pgprot_t _prot) +{ + unsigned long prot = pgprot_val(_prot); + + prot &= ~_PAGE_DMA_MASK; + prot |= _PAGE_DMA_WC; + + return __pgprot(prot); +} + +#define __HAVE_PHYS_MEM_ACCESS_PROT +extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, + unsigned long size, pgprot_t vma_prot); + /* * THP functions */ @ arch/riscv/include/asm/sbi.h:30 @ enum sbi_ext_id { SBI_EXT_IPI = 0x735049, SBI_EXT_RFENCE = 0x52464E43, SBI_EXT_HSM = 0x48534D, + SBI_EXT_SRST = 0x53525354, + SBI_EXT_DMA = 0xAB150401, }; enum sbi_ext_base_fid { @ arch/riscv/include/asm/sbi.h:66 @ 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_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, +}; + +enum sbi_ext_dma_fid { + SBI_DMA_SYNC = 0, +}; + +enum sbi_dma_sync_data_direction { + SBI_DMA_BIDIRECTIONAL = 0, + SBI_DMA_TO_DEVICE = 1, + SBI_DMA_FROM_DEVICE = 2, + SBI_DMA_NONE = 3, }; enum sbi_hsm_hart_status { @ arch/riscv/include/asm/sbi.h:145 @ struct sbiret { }; void sbi_init(void); +void sbi_ipi_init(void); struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, @ arch/riscv/include/asm/sbi.h:184 @ int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask, unsigned long size, unsigned long asid); int sbi_probe_extension(int ext); +void sbi_dma_sync(unsigned long start, + unsigned long size, + enum sbi_dma_sync_data_direction dir); /* Check if current SBI specification version is 0.1 or not */ static inline int sbi_spec_is_0_1(void) @ arch/riscv/include/asm/sbi.h:207 @ static inline unsigned long sbi_minor_version(void) return sbi_spec_version & SBI_SPEC_VERSION_MINOR_MASK; } +/* Make SBI version */ +static inline unsigned long sbi_mk_version(unsigned long major, + unsigned long minor) +{ + return ((major & SBI_SPEC_VERSION_MAJOR_MASK) << + SBI_SPEC_VERSION_MAJOR_SHIFT) | minor; +} + int sbi_err_map_linux_errno(int err); #else /* CONFIG_RISCV_SBI */ static inline int sbi_remote_fence_i(const unsigned long *hart_mask) { return -1; } +static inline void sbi_ipi_init(void) { } static inline void sbi_init(void) {} #endif /* CONFIG_RISCV_SBI */ #endif /* _ASM_RISCV_SBI_H */ @ arch/riscv/include/asm/smp.h:18 @ struct seq_file; extern unsigned long boot_cpu_hartid; -struct riscv_ipi_ops { - void (*ipi_inject)(const struct cpumask *target); - void (*ipi_clear)(void); -}; - #ifdef CONFIG_SMP /* * Mapping between linux logical cpu index and hartid. @ arch/riscv/include/asm/smp.h:31 @ void show_ipi_stats(struct seq_file *p, int prec); /* SMP initialization hook for setup_arch */ void __init setup_smp(void); -/* Called from C code, this handles an IPI. */ -void handle_IPI(struct pt_regs *regs); - /* Hook for the generic smp_call_function_many() routine. */ void arch_send_call_function_ipi_mask(struct cpumask *mask); @ arch/riscv/include/asm/smp.h:40 @ void arch_send_call_function_single_ipi(int cpu); int riscv_hartid_to_cpuid(int hartid); void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out); -/* Set custom IPI operations */ -void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops); +/* Enable IPI for CPU hotplug */ +void riscv_ipi_enable(void); + +/* Disable IPI for CPU hotplug */ +void riscv_ipi_disable(void); -/* Clear IPI for current CPU */ -void riscv_clear_ipi(void); +/* Check if IPI interrupt numbers are available */ +bool riscv_ipi_have_virq_range(void); + +/* Set the IPI interrupt numbers for arch (called by irqchip drivers) */ +void riscv_ipi_set_virq_range(int virq, int nr_irqs); /* Secondary hart entry */ asmlinkage void smp_callin(void); @ arch/riscv/include/asm/smp.h:93 @ static inline void riscv_cpuid_to_hartid_mask(const struct cpumask *in, cpumask_set_cpu(boot_cpu_hartid, out); } -static inline void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops) +static inline void riscv_ipi_enable(void) { } -static inline void riscv_clear_ipi(void) +static inline void riscv_ipi_disable(void) +{ +} + +static inline bool riscv_ipi_have_virq_range(void) +{ + return false; +} + +static inline void riscv_ipi_set_virq_range(int virq, int nr) { } @ arch/riscv/include/asm/soc.h:20 @ = { .compatible = compat, .data = fn } void soc_early_init(void); +void soc_setup_vm(void); extern unsigned long __soc_early_init_table_start; extern unsigned long __soc_early_init_table_end; @ arch/riscv/include/asm/suspend.h:4 @ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + */ + +#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 @ arch/riscv/include/asm/vendorid_list.h:9 @ #define ASM_VENDOR_LIST_H #define SIFIVE_VENDOR_ID 0x489 +#define THEAD_VENDOR_ID 0x401 #endif @ arch/riscv/kernel/Makefile:50 @ 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 @ arch/riscv/kernel/Makefile:59 @ obj-$(CONFIG_RISCV_BASE_PMU) += perf_event.o obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o obj-$(CONFIG_RISCV_SBI) += sbi.o +obj-$(CONFIG_RISCV_SBI) += sbi-ipi.o ifeq ($(CONFIG_RISCV_SBI), y) obj-$(CONFIG_SMP) += cpu_ops_sbi.o endif @ arch/riscv/kernel/asm-offsets.c:15 @ #include <asm/kvm_host.h> #include <asm/thread_info.h> #include <asm/ptrace.h> +#include <asm/suspend.h> void asm_offsets(void); @ arch/riscv/kernel/asm-offsets.c:270 @ void asm_offsets(void) 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); + OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs); + /* * 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 @ arch/riscv/kernel/cacheinfo.c:97 @ static void fill_cacheinfo(struct cacheinfo **this_leaf, { unsigned int size, sets, line_size; + /* Buggy: may not init leaves, but num_leaves was set below. */ if (!of_property_read_u32(node, "cache-size", &size) && !of_property_read_u32(node, "cache-block-size", &line_size) && !of_property_read_u32(node, "cache-sets", &sets)) { @ arch/riscv/kernel/cpu-hotplug.c:15 @ #include <linux/sched/hotplug.h> #include <asm/irq.h> #include <asm/cpu_ops.h> -#include <asm/sbi.h> +#include <asm/smp.h> void cpu_stop(void); void arch_cpu_idle_dead(void) @ arch/riscv/kernel/cpu-hotplug.c:50 @ int __cpu_disable(void) remove_cpu_topology(cpu); set_cpu_online(cpu, false); + riscv_ipi_disable(); irq_migrate_all_off_this_cpu(); return ret; @ arch/riscv/kernel/cpu_ops_sbi.c:100 @ 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; } @ arch/riscv/kernel/head.S:18 @ #include <asm/image.h> #include "efi-header.S" -#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 */ - __HEAD ENTRY(_start) /* @ arch/riscv/kernel/head.S:71 @ pe_head_start: .align 2 #ifdef CONFIG_MMU -relocate: + .global relocate_enable_mmu +relocate_enable_mmu: /* Relocate return address */ la a1, kernel_map XIP_FIXUP_OFFSET a1 @ arch/riscv/kernel/head.S:167 @ secondary_start_common: /* Enable virtual memory and relocate to virtual address */ la a0, swapper_pg_dir XIP_FIXUP_OFFSET a0 - call relocate + call relocate_enable_mmu #endif call setup_trap_vector tail smp_callin @ arch/riscv/kernel/head.S:307 @ clear_bss_done: #ifdef CONFIG_MMU la a0, early_pg_dir XIP_FIXUP_OFFSET a0 - call relocate + call relocate_enable_mmu #endif /* CONFIG_MMU */ call setup_trap_vector @ arch/riscv/kernel/irq.c:11 @ #include <linux/interrupt.h> #include <linux/irqchip.h> #include <linux/seq_file.h> -#include <asm/smp.h> +#include <asm/sbi.h> int arch_show_interrupts(struct seq_file *p, int prec) { @ arch/riscv/kernel/irq.c:24 @ void __init init_IRQ(void) irqchip_init(); if (!handle_arch_irq) panic("No interrupt controller found."); + sbi_ipi_init(); } @ arch/riscv/kernel/process.c:26 @ #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"); @ arch/riscv/kernel/process.c:41 @ extern asmlinkage void ret_from_kernel_thread(void); void arch_cpu_idle(void) { - wait_for_interrupt(); + cpu_do_idle(); raw_local_irq_enable(); } @ arch/riscv/kernel/sbi-ipi.c:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SBI based IPI support. + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + */ + +#define pr_fmt(fmt) "riscv-sbi-ipi: " fmt +#include <linux/cpu.h> +#include <linux/cpumask.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/smp.h> +#include <asm/sbi.h> + +static int intc_parent_irq __ro_after_init; +static struct irq_domain *sbi_ipi_domain __ro_after_init; +static DEFINE_PER_CPU(unsigned long, sbi_ipi_bits); + +static void sbi_ipi_dummy(struct irq_data *d) +{ +} + +static void sbi_ipi_send_mask(struct irq_data *d, const struct cpumask *mask) +{ + int cpu; + struct cpumask hartid_mask; + + /* Barrier before doing atomic bit update to IPI bits */ + smp_mb__before_atomic(); + for_each_cpu(cpu, mask) + set_bit(d->hwirq, per_cpu_ptr(&sbi_ipi_bits, cpu)); + /* Barrier after doing atomic bit update to IPI bits */ + smp_mb__after_atomic(); + + riscv_cpuid_to_hartid_mask(mask, &hartid_mask); + + sbi_send_ipi(cpumask_bits(&hartid_mask)); +} + +static struct irq_chip sbi_ipi_chip = { + .name = "RISC-V SBI IPI", + .irq_mask = sbi_ipi_dummy, + .irq_unmask = sbi_ipi_dummy, + .ipi_send_mask = sbi_ipi_send_mask, +}; + +static int sbi_ipi_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_percpu_devid(irq); + irq_domain_set_info(d, irq, hwirq, &sbi_ipi_chip, d->host_data, + handle_percpu_devid_irq, NULL, NULL); + + return 0; +} + +static int sbi_ipi_domain_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + int i, ret; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + struct irq_fwspec *fwspec = arg; + + ret = irq_domain_translate_onecell(d, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) { + ret = sbi_ipi_domain_map(d, virq + i, hwirq + i); + if (ret) + return ret; + } + + return 0; +} + +static const struct irq_domain_ops sbi_ipi_domain_ops = { + .translate = irq_domain_translate_onecell, + .alloc = sbi_ipi_domain_alloc, + .free = irq_domain_free_irqs_top, +}; + +static void sbi_ipi_handle_irq(struct irq_desc *desc) +{ + int err; + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long irqs, *bits = this_cpu_ptr(&sbi_ipi_bits); + irq_hw_number_t hwirq; + + chained_irq_enter(chip, desc); + + while (true) { + csr_clear(CSR_IP, IE_SIE); + + /* Order bit clearing and data access. */ + mb(); + + irqs = xchg(bits, 0); + if (!irqs) + goto done; + + for_each_set_bit(hwirq, &irqs, BITS_PER_LONG) { + err = generic_handle_domain_irq(sbi_ipi_domain, + hwirq); + if (unlikely(err)) + pr_warn_ratelimited( + "can't find mapping for hwirq %lu\n", + hwirq); + } + } + +done: + chained_irq_exit(chip, desc); +} + +static int sbi_ipi_dying_cpu(unsigned int cpu) +{ + disable_percpu_irq(intc_parent_irq); + return 0; +} + +static int sbi_ipi_starting_cpu(unsigned int cpu) +{ + enable_percpu_irq(intc_parent_irq, + irq_get_trigger_type(intc_parent_irq)); + return 0; +} + +static int __init sbi_ipi_set_virq(void) +{ + int virq; + struct irq_fwspec ipi = { + .fwnode = sbi_ipi_domain->fwnode, + .param_count = 1, + .param[0] = 0, + }; + + virq = __irq_domain_alloc_irqs(sbi_ipi_domain, -1, BITS_PER_LONG, + NUMA_NO_NODE, &ipi, + false, NULL); + if (virq <= 0) { + pr_err("unable to alloc IRQs from SBI IPI IRQ domain\n"); + return -ENOMEM; + } + + riscv_ipi_set_virq_range(virq, BITS_PER_LONG); + + return 0; +} + +static int __init sbi_ipi_domain_init(struct irq_domain *domain) +{ + struct irq_fwspec swi = { + .fwnode = domain->fwnode, + .param_count = 1, + .param[0] = RV_IRQ_SOFT, + }; + + intc_parent_irq = __irq_domain_alloc_irqs(domain, -1, 1, + NUMA_NO_NODE, &swi, + false, NULL); + if (intc_parent_irq <= 0) { + pr_err("unable to alloc IRQ from INTC IRQ domain\n"); + return -ENOMEM; + } + + irq_set_chained_handler(intc_parent_irq, sbi_ipi_handle_irq); + + sbi_ipi_domain = irq_domain_add_linear(NULL, BITS_PER_LONG, + &sbi_ipi_domain_ops, NULL); + if (!sbi_ipi_domain) { + pr_err("unable to add SBI IPI IRQ domain\n"); + return -ENOMEM; + } + + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "irqchip/riscv/sbi-ipi:starting", + sbi_ipi_starting_cpu, sbi_ipi_dying_cpu); + + return sbi_ipi_set_virq(); +} + +void __init sbi_ipi_init(void) +{ + struct irq_domain *domain = NULL; + struct device_node *cpu, *child; + + if (riscv_ipi_have_virq_range()) + return; + + for_each_of_cpu_node(cpu) { + child = of_get_compatible_child(cpu, "riscv,cpu-intc"); + if (!child) { + pr_err("failed to find INTC node [%pOF]\n", cpu); + return; + } + + domain = irq_find_host(child); + of_node_put(child); + if (domain) + break; + } + if (!domain) { + pr_err("can't find INTC IRQ domain\n"); + return; + } + + if (sbi_ipi_domain_init(domain)) + pr_err("failed to register IPI domain\n"); + else + pr_info("registered IPI domain\n"); +} @ arch/riscv/kernel/sbi.c:10 @ #include <linux/init.h> #include <linux/pm.h> +#include <linux/reboot.h> #include <asm/sbi.h> #include <asm/smp.h> @ arch/riscv/kernel/sbi.c:505 @ int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask, } EXPORT_SYMBOL(sbi_remote_hfence_vvma_asid); +static void sbi_srst_reset(unsigned long type, unsigned long reason) +{ + sbi_ecall(SBI_EXT_SRST, SBI_EXT_SRST_RESET, type, reason, + 0, 0, 0, 0); + pr_warn("%s: type=0x%lx reason=0x%lx failed\n", + __func__, type, reason); +} + +static int sbi_srst_reboot(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + sbi_srst_reset((mode == REBOOT_WARM || mode == REBOOT_SOFT) ? + SBI_SRST_RESET_TYPE_WARM_REBOOT : + SBI_SRST_RESET_TYPE_COLD_REBOOT, + SBI_SRST_RESET_REASON_NONE); + return NOTIFY_DONE; +} + +static struct notifier_block sbi_srst_reboot_nb; + +static void sbi_srst_power_off(void) +{ + sbi_srst_reset(SBI_SRST_RESET_TYPE_SHUTDOWN, + SBI_SRST_RESET_REASON_NONE); +} + /** * sbi_probe_extension() - Check if an SBI extension ID is supported or not. * @extid: The extension ID to be probed. @ arch/riscv/kernel/sbi.c:551 @ int sbi_probe_extension(int extid) } EXPORT_SYMBOL(sbi_probe_extension); +void sbi_dma_sync(unsigned long start, + unsigned long size, + enum sbi_dma_sync_data_direction dir) +{ +#if 0 + sbi_ecall(SBI_EXT_DMA, SBI_DMA_SYNC, start, size, dir, + 0, 0, 0); +#else + /* Just for try, it should be in sbi ecall and will be removed before merged */ + register unsigned long i asm("a0") = start & ~(L1_CACHE_BYTES - 1); + + for (; i < ALIGN(start + size, L1_CACHE_BYTES); i += L1_CACHE_BYTES) + __asm__ __volatile__(".long 0x02b5000b" : : "r"(i)); + + __asm__ __volatile__(".long 0x01b0000b"); +#endif +} +EXPORT_SYMBOL(sbi_dma_sync); + static long __sbi_base_ecall(int fid) { struct sbiret ret; @ arch/riscv/kernel/sbi.c:611 @ long sbi_get_mimpid(void) return __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID); } -static void sbi_send_cpumask_ipi(const struct cpumask *target) -{ - struct cpumask hartid_mask; - - riscv_cpuid_to_hartid_mask(target, &hartid_mask); - - sbi_send_ipi(cpumask_bits(&hartid_mask)); -} - -static const struct riscv_ipi_ops sbi_ipi_ops = { - .ipi_inject = sbi_send_cpumask_ipi -}; - void __init sbi_init(void) { int ret; @ arch/riscv/kernel/sbi.c:644 @ void __init sbi_init(void) } else { __sbi_rfence = __sbi_rfence_v01; } + if ((sbi_spec_version >= sbi_mk_version(0, 3)) && + (sbi_probe_extension(SBI_EXT_SRST) > 0)) { + pr_info("SBI SRST extension detected\n"); + pm_power_off = sbi_srst_power_off; + sbi_srst_reboot_nb.notifier_call = sbi_srst_reboot; + sbi_srst_reboot_nb.priority = 192; + register_restart_handler(&sbi_srst_reboot_nb); + } } else { __sbi_set_timer = __sbi_set_timer_v01; __sbi_send_ipi = __sbi_send_ipi_v01; __sbi_rfence = __sbi_rfence_v01; } - - riscv_set_ipi_ops(&sbi_ipi_ops); } @ arch/riscv/kernel/smp.c:20 @ #include <linux/sched.h> #include <linux/seq_file.h> #include <linux/delay.h> +#include <linux/irq.h> #include <linux/irq_work.h> -#include <asm/sbi.h> #include <asm/tlbflush.h> #include <asm/cacheflush.h> @ arch/riscv/kernel/smp.c:44 @ void __init smp_setup_processor_id(void) cpuid_to_hartid_map(0) = boot_cpu_hartid; } -/* A collection of single bit ipi messages. */ -static struct { - unsigned long stats[IPI_MAX] ____cacheline_aligned; - unsigned long bits ____cacheline_aligned; -} ipi_data[NR_CPUS] __cacheline_aligned; +static int ipi_virq_base __ro_after_init; +static int nr_ipi __ro_after_init = IPI_MAX; +static struct irq_desc *ipi_desc[IPI_MAX] __read_mostly; int riscv_hartid_to_cpuid(int hartid) { @ arch/riscv/kernel/smp.c:88 @ static void ipi_stop(void) wait_for_interrupt(); } -static const struct riscv_ipi_ops *ipi_ops __ro_after_init; - -void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops) -{ - ipi_ops = ops; -} -EXPORT_SYMBOL_GPL(riscv_set_ipi_ops); - -void riscv_clear_ipi(void) -{ - if (ipi_ops && ipi_ops->ipi_clear) - ipi_ops->ipi_clear(); - - csr_clear(CSR_IP, IE_SIE); -} -EXPORT_SYMBOL_GPL(riscv_clear_ipi); - static void send_ipi_mask(const struct cpumask *mask, enum ipi_message_type op) { - int cpu; - - smp_mb__before_atomic(); - for_each_cpu(cpu, mask) - set_bit(op, &ipi_data[cpu].bits); - smp_mb__after_atomic(); - - if (ipi_ops && ipi_ops->ipi_inject) - ipi_ops->ipi_inject(mask); - else - pr_warn("SMP: IPI inject method not available\n"); + __ipi_send_mask(ipi_desc[op], mask); } static void send_ipi_single(int cpu, enum ipi_message_type op) { - smp_mb__before_atomic(); - set_bit(op, &ipi_data[cpu].bits); - smp_mb__after_atomic(); - - if (ipi_ops && ipi_ops->ipi_inject) - ipi_ops->ipi_inject(cpumask_of(cpu)); - else - pr_warn("SMP: IPI inject method not available\n"); + __ipi_send_mask(ipi_desc[op], cpumask_of(cpu)); } #ifdef CONFIG_IRQ_WORK @ arch/riscv/kernel/smp.c:105 @ void arch_irq_work_raise(void) } #endif -void handle_IPI(struct pt_regs *regs) +static irqreturn_t handle_IPI(int irq, void *data) +{ + int ipi = irq - ipi_virq_base; + + switch (ipi) { + case IPI_RESCHEDULE: + scheduler_ipi(); + break; + case IPI_CALL_FUNC: + generic_smp_call_function_interrupt(); + break; + case IPI_CPU_STOP: + ipi_stop(); + break; + case IPI_IRQ_WORK: + irq_work_run(); + break; +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST + case IPI_TIMER: + tick_receive_broadcast(); + break; +#endif + default: + pr_warn("CPU%d: unhandled IPI%d\n", smp_processor_id(), ipi); + break; + }; + + return IRQ_HANDLED; +} + +void riscv_ipi_enable(void) { - unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits; - unsigned long *stats = ipi_data[smp_processor_id()].stats; + int i; - riscv_clear_ipi(); + if (WARN_ON_ONCE(!ipi_virq_base)) + return; - while (true) { - unsigned long ops; + for (i = 0; i < nr_ipi; i++) + enable_percpu_irq(ipi_virq_base + i, 0); +} + +void riscv_ipi_disable(void) +{ + int i; - /* Order bit clearing and data access. */ - mb(); + if (WARN_ON_ONCE(!ipi_virq_base)) + return; - ops = xchg(pending_ipis, 0); - if (ops == 0) - return; + for (i = 0; i < nr_ipi; i++) + disable_percpu_irq(ipi_virq_base + i); +} - if (ops & (1 << IPI_RESCHEDULE)) { - stats[IPI_RESCHEDULE]++; - scheduler_ipi(); - } +bool riscv_ipi_have_virq_range(void) +{ + return (ipi_virq_base) ? true : false; +} - if (ops & (1 << IPI_CALL_FUNC)) { - stats[IPI_CALL_FUNC]++; - generic_smp_call_function_interrupt(); - } +void riscv_ipi_set_virq_range(int virq, int nr) +{ + int i, err; - if (ops & (1 << IPI_CPU_STOP)) { - stats[IPI_CPU_STOP]++; - ipi_stop(); - } + if (WARN_ON(ipi_virq_base)) + return; - if (ops & (1 << IPI_IRQ_WORK)) { - stats[IPI_IRQ_WORK]++; - irq_work_run(); - } + WARN_ON(nr < IPI_MAX); + nr_ipi = min(nr, IPI_MAX); + ipi_virq_base = virq; -#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST - if (ops & (1 << IPI_TIMER)) { - stats[IPI_TIMER]++; - tick_receive_broadcast(); - } -#endif - BUG_ON((ops >> IPI_MAX) != 0); + /* Request IPIs */ + for (i = 0; i < nr_ipi; i++) { + err = request_percpu_irq(ipi_virq_base + i, handle_IPI, + "IPI", &ipi_virq_base); + WARN_ON(err); - /* Order data access and bit testing. */ - mb(); + ipi_desc[i] = irq_to_desc(ipi_virq_base + i); + irq_set_status_flags(ipi_virq_base + i, IRQ_HIDDEN); } + + /* Enabled IPIs for boot CPU immediately */ + riscv_ipi_enable(); } +EXPORT_SYMBOL_GPL(riscv_ipi_set_virq_range); static const char * const ipi_names[] = { [IPI_RESCHEDULE] = "Rescheduling interrupts", @ arch/riscv/kernel/smp.c:204 @ void show_ipi_stats(struct seq_file *p, int prec) seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : ""); for_each_online_cpu(cpu) - seq_printf(p, "%10lu ", ipi_data[cpu].stats[i]); + seq_printf(p, "%10u ", irq_desc_kstat_cpu(ipi_desc[i], cpu)); seq_printf(p, " %s\n", ipi_names[i]); } } @ arch/riscv/kernel/smpboot.c:33 @ #include <asm/numa.h> #include <asm/tlbflush.h> #include <asm/sections.h> -#include <asm/sbi.h> #include <asm/smp.h> #include <asm/alternative.h> @ arch/riscv/kernel/smpboot.c:161 @ asmlinkage __visible void smp_callin(void) struct mm_struct *mm = &init_mm; unsigned int curr_cpuid = smp_processor_id(); - riscv_clear_ipi(); - /* All kernel threads share the same mm context. */ mmgrab(mm); current->active_mm = mm; + riscv_ipi_enable(); + notify_cpu_starting(curr_cpuid); numa_add_cpu(curr_cpuid); update_siblings_masks(curr_cpuid); @ arch/riscv/kernel/soc.c:6 @ * Copyright (C) 2020 Western Digital Corporation or its affiliates. */ #include <linux/init.h> +#include <linux/mm.h> #include <linux/libfdt.h> #include <linux/pgtable.h> +#include <asm/image.h> #include <asm/soc.h> /* @ arch/riscv/kernel/soc.c:31 @ void __init soc_early_init(void) } } } + +static void __init thead_init(void) +{ + __riscv_custom_pte.cache = 0x7000000000000000; + __riscv_custom_pte.mask = 0xf800000000000000; + __riscv_custom_pte.io = BIT(63); + __riscv_custom_pte.wc = 0; +} + +void __init soc_setup_vm(void) +{ + unsigned long vendor_id = + ((struct riscv_image_header *)(&_start))->res1; + + switch (vendor_id) { + case THEAD_VENDOR_ID: + // Do not rely on the bootloader... + default: + thead_init(); + break; + } +}; @ arch/riscv/kernel/suspend.c:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + */ + +#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; +} @ arch/riscv/kernel/suspend_entry.S:4 @ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + */ + +#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) @ arch/riscv/kernel/vdso/flush_icache.S:8 @ #include <linux/linkage.h> #include <asm/unistd.h> +#include <asm/cache.h> + +/* + * icache.ipa rs1 + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000001 11000 rs1 000 00000 0001011 + * + * icache.iva rs1 + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000001 10000 rs1 000 00000 0001011 + * + * sync.is + * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | + * 0000000 11011 00000 000 00000 0001011 + */ +#define ICACHE_IPA_X5 .long 0x0382800b +#define ICACHE_IVA_X5 .long 0x0302800b +#define SYNC_IS .long 0x01b0000b .text /* int __vdso_flush_icache(void *start, void *end, unsigned long flags); */ ENTRY(__vdso_flush_icache) .cfi_startproc -#ifdef CONFIG_SMP - li a7, __NR_riscv_flush_icache - ecall -#else - fence.i + srli t0, a0, L1_CACHE_SHIFT + slli t0, t0, L1_CACHE_SHIFT + addi a1, a1, (L1_CACHE_BYTES - 1) + srli a1, a1, L1_CACHE_SHIFT + slli a1, a1, L1_CACHE_SHIFT +1: ICACHE_IVA_X5 + addi t0, t0, L1_CACHE_BYTES + bne t0, a1, 1b + SYNC_IS li a0, 0 -#endif ret .cfi_endproc ENDPROC(__vdso_flush_icache) @ arch/riscv/mm/Makefile:16 @ obj-y += extable.o obj-$(CONFIG_MMU) += fault.o pageattr.o obj-y += cacheflush.o obj-y += context.o +obj-y += dma-mapping.o ifeq ($(CONFIG_MMU),y) obj-$(CONFIG_SMP) += tlbflush.o @ arch/riscv/mm/cacheflush.c:6 @ * Copyright (C) 2017 SiFive */ +#include <linux/pfn.h> #include <asm/cacheflush.h> #ifdef CONFIG_SMP @ arch/riscv/mm/cacheflush.c:90 @ void flush_icache_pte(pte_t pte) struct page *page = pte_page(pte); if (!test_and_set_bit(PG_dcache_clean, &page->flags)) - flush_icache_all(); + __flush_icache_page(page); } #endif /* CONFIG_MMU */ @ arch/riscv/mm/dma-mapping.c:4 @ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/dma-map-ops.h> +#include <asm/sbi.h> + +void arch_dma_prep_coherent(struct page *page, size_t size) +{ + void *ptr = page_address(page); + + memset(ptr, 0, size); + sbi_dma_sync(page_to_phys(page), size, SBI_DMA_BIDIRECTIONAL); +} + +void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ + switch (dir) { + case DMA_TO_DEVICE: + case DMA_FROM_DEVICE: + case DMA_BIDIRECTIONAL: + sbi_dma_sync(paddr, size, dir); + break; + default: + BUG(); + } +} + +void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ + switch (dir) { + case DMA_TO_DEVICE: + return; + case DMA_FROM_DEVICE: + case DMA_BIDIRECTIONAL: + sbi_dma_sync(paddr, size, dir); + break; + default: + BUG(); + } +} + +pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, + unsigned long size, pgprot_t vma_prot) +{ + if (!pfn_valid(pfn)) + return pgprot_noncached(vma_prot); + else if (file->f_flags & O_SYNC) + return pgprot_writecombine(vma_prot); + + return vma_prot; +} +EXPORT_SYMBOL(phys_mem_access_prot); @ arch/riscv/mm/init.c:591 @ static void __init create_fdt_early_page_table(pgd_t *pgdir, uintptr_t dtb_pa) dtb_early_pa = dtb_pa; } +static void __init setup_protection_map(void) +{ + protection_map[0] = __P000; + protection_map[1] = __P001; + protection_map[2] = __P010; + protection_map[3] = __P011; + protection_map[4] = __P100; + protection_map[5] = __P101; + protection_map[6] = __P110; + protection_map[7] = __P111; + protection_map[8] = __S000; + protection_map[9] = __S001; + protection_map[10] = __S010; + protection_map[11] = __S011; + protection_map[12] = __S100; + protection_map[13] = __S101; + protection_map[14] = __S110; + protection_map[15] = __S111; +} + asmlinkage void __init setup_vm(uintptr_t dtb_pa) { pmd_t __maybe_unused fix_bmap_spmd, fix_bmap_epmd; + soc_setup_vm(); + setup_protection_map(); + kernel_map.virt_addr = KERNEL_LINK_ADDR; #ifdef CONFIG_XIP_KERNEL @ arch/riscv/mm/init.c:880 @ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, return vmemmap_populate_basepages(start, end, node, NULL); } #endif + +struct riscv_custom_pte __riscv_custom_pte __ro_after_init; +EXPORT_SYMBOL(__riscv_custom_pte); @ arch/riscv/mm/physaddr.c:17 @ phys_addr_t __virt_to_phys(unsigned long x) * Boundary checking aginst the kernel linear mapping space. */ WARN(y >= KERN_VIRT_SIZE, - "virt_to_phys used for non-linear address: %pK (%pS)\n", + "virt_to_phys used for non-linear address: %p (%pS)\n", (void *)x, (void *)x); return __va_to_pa_nodebug(x); @ drivers/bus/sun50i-de2.c:27 @ static int sun50i_de2_bus_probe(struct platform_device *pdev) return 0; } -static int sun50i_de2_bus_remove(struct platform_device *pdev) -{ - sunxi_sram_release(&pdev->dev); - return 0; -} - static const struct of_device_id sun50i_de2_bus_of_match[] = { { .compatible = "allwinner,sun50i-a64-de2", }, { /* sentinel */ } @ drivers/bus/sun50i-de2.c:34 @ static const struct of_device_id sun50i_de2_bus_of_match[] = { static struct platform_driver sun50i_de2_bus_driver = { .probe = sun50i_de2_bus_probe, - .remove = sun50i_de2_bus_remove, .driver = { .name = "sun50i-de2-bus", + .suppress_bind_attrs = true, .of_match_table = sun50i_de2_bus_of_match, }, }; @ drivers/clk/Makefile:113 @ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_ARCH_STI) += st/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ -obj-$(CONFIG_SUNXI_CCU) += sunxi-ng/ +obj-y += sunxi-ng/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-y += ti/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ @ drivers/clk/sunxi-ng/Kconfig:3 @ # SPDX-License-Identifier: GPL-2.0-only config SUNXI_CCU - bool "Clock support for Allwinner SoCs" + tristate "Clock support for Allwinner SoCs" depends on ARCH_SUNXI || COMPILE_TEST select RESET_CONTROLLER default ARCH_SUNXI @ drivers/clk/sunxi-ng/Kconfig:11 @ config SUNXI_CCU if SUNXI_CCU config SUNIV_F1C100S_CCU - bool "Support for the Allwinner newer F1C100s CCU" + tristate "Support for the Allwinner newer F1C100s CCU" default MACH_SUNIV depends on MACH_SUNIV || COMPILE_TEST +config SUN20I_D1_CCU + tristate "Support for the Allwinner D1 CCU" + default RISCV && ARCH_SUNXI + depends on (RISCV && ARCH_SUNXI) || COMPILE_TEST + +config SUN20I_D1_R_CCU + tristate "Support for the Allwinner D1 PRCM CCU" + default RISCV && ARCH_SUNXI + depends on (RISCV && ARCH_SUNXI) || COMPILE_TEST + config SUN50I_A64_CCU - bool "Support for the Allwinner A64 CCU" + tristate "Support for the Allwinner A64 CCU" default ARM64 && ARCH_SUNXI depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN50I_A100_CCU - bool "Support for the Allwinner A100 CCU" + tristate "Support for the Allwinner A100 CCU" default ARM64 && ARCH_SUNXI depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN50I_A100_R_CCU - bool "Support for the Allwinner A100 PRCM CCU" + tristate "Support for the Allwinner A100 PRCM CCU" default ARM64 && ARCH_SUNXI depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN50I_H6_CCU - bool "Support for the Allwinner H6 CCU" + tristate "Support for the Allwinner H6 CCU" default ARM64 && ARCH_SUNXI depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN50I_H616_CCU - bool "Support for the Allwinner H616 CCU" + tristate "Support for the Allwinner H616 CCU" default ARM64 && ARCH_SUNXI depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN50I_H6_R_CCU - bool "Support for the Allwinner H6 and H616 PRCM CCU" + tristate "Support for the Allwinner H6 and H616 PRCM CCU" default ARM64 && ARCH_SUNXI depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN4I_A10_CCU - bool "Support for the Allwinner A10/A20 CCU" + tristate "Support for the Allwinner A10/A20 CCU" default MACH_SUN4I default MACH_SUN7I depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST @ drivers/clk/sunxi-ng/Kconfig:65 @ config SUN5I_CCU bool "Support for the Allwinner sun5i family CCM" default MACH_SUN5I depends on MACH_SUN5I || COMPILE_TEST + depends on SUNXI_CCU=y config SUN6I_A31_CCU - bool "Support for the Allwinner A31/A31s CCU" + tristate "Support for the Allwinner A31/A31s CCU" default MACH_SUN6I depends on MACH_SUN6I || COMPILE_TEST +config SUN6I_RTC_CCU + tristate "Support for the Allwinner H616/R329 RTC CCU" + default ARCH_SUNXI + depends on ARCH_SUNXI || COMPILE_TEST + config SUN8I_A23_CCU - bool "Support for the Allwinner A23 CCU" + tristate "Support for the Allwinner A23 CCU" default MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST config SUN8I_A33_CCU - bool "Support for the Allwinner A33 CCU" + tristate "Support for the Allwinner A33 CCU" default MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST config SUN8I_A83T_CCU - bool "Support for the Allwinner A83T CCU" + tristate "Support for the Allwinner A83T CCU" default MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST config SUN8I_H3_CCU - bool "Support for the Allwinner H3 CCU" + tristate "Support for the Allwinner H3 CCU" default MACH_SUN8I || (ARM64 && ARCH_SUNXI) depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST config SUN8I_V3S_CCU - bool "Support for the Allwinner V3s CCU" + tristate "Support for the Allwinner V3s CCU" default MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST config SUN8I_DE2_CCU - bool "Support for the Allwinner SoCs DE2 CCU" + tristate "Support for the Allwinner SoCs DE2 CCU" default MACH_SUN8I || (ARM64 && ARCH_SUNXI) config SUN8I_R40_CCU - bool "Support for the Allwinner R40 CCU" + tristate "Support for the Allwinner R40 CCU" default MACH_SUN8I depends on MACH_SUN8I || COMPILE_TEST config SUN9I_A80_CCU - bool "Support for the Allwinner A80 CCU" + tristate "Support for the Allwinner A80 CCU" default MACH_SUN9I depends on MACH_SUN9I || COMPILE_TEST config SUN8I_R_CCU - bool "Support for Allwinner SoCs' PRCM CCUs" + tristate "Support for Allwinner SoCs' PRCM CCUs" default MACH_SUN8I || (ARCH_SUNXI && ARM64) endif @ drivers/clk/sunxi-ng/Makefile:4 @ # SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SUNXI_CCU) += sunxi-ccu.o + # Common objects -obj-y += ccu_common.o -obj-y += ccu_mmc_timing.o -obj-y += ccu_reset.o +sunxi-ccu-y += ccu_common.o +sunxi-ccu-y += ccu_mmc_timing.o +sunxi-ccu-y += ccu_reset.o # Base clock types -obj-y += ccu_div.o -obj-y += ccu_frac.o -obj-y += ccu_gate.o -obj-y += ccu_mux.o -obj-y += ccu_mult.o -obj-y += ccu_phase.o -obj-y += ccu_sdm.o +sunxi-ccu-y += ccu_div.o +sunxi-ccu-y += ccu_frac.o +sunxi-ccu-y += ccu_gate.o +sunxi-ccu-y += ccu_mux.o +sunxi-ccu-y += ccu_mult.o +sunxi-ccu-y += ccu_phase.o +sunxi-ccu-y += ccu_sdm.o # Multi-factor clocks -obj-y += ccu_nk.o -obj-y += ccu_nkm.o -obj-y += ccu_nkmp.o -obj-y += ccu_nm.o -obj-y += ccu_mp.o +sunxi-ccu-y += ccu_nk.o +sunxi-ccu-y += ccu_nkm.o +sunxi-ccu-y += ccu_nkmp.o +sunxi-ccu-y += ccu_nm.o +sunxi-ccu-y += ccu_mp.o # SoC support -obj-$(CONFIG_SUNIV_F1C100S_CCU) += ccu-suniv-f1c100s.o -obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o -obj-$(CONFIG_SUN50I_A100_CCU) += ccu-sun50i-a100.o -obj-$(CONFIG_SUN50I_A100_R_CCU) += ccu-sun50i-a100-r.o -obj-$(CONFIG_SUN50I_H6_CCU) += ccu-sun50i-h6.o -obj-$(CONFIG_SUN50I_H616_CCU) += ccu-sun50i-h616.o -obj-$(CONFIG_SUN50I_H6_R_CCU) += ccu-sun50i-h6-r.o -obj-$(CONFIG_SUN4I_A10_CCU) += ccu-sun4i-a10.o -obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o -obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o -obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o -obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o -obj-$(CONFIG_SUN8I_A83T_CCU) += ccu-sun8i-a83t.o -obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o -obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o -obj-$(CONFIG_SUN8I_DE2_CCU) += ccu-sun8i-de2.o -obj-$(CONFIG_SUN8I_R_CCU) += ccu-sun8i-r.o -obj-$(CONFIG_SUN8I_R40_CCU) += ccu-sun8i-r40.o -obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o -obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o -obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o +obj-$(CONFIG_SUNIV_F1C100S_CCU) += suniv-f1c100s-ccu.o +obj-$(CONFIG_SUN20I_D1_CCU) += sun20i-d1-ccu.o +obj-$(CONFIG_SUN20I_D1_R_CCU) += sun20i-d1-r-ccu.o +obj-$(CONFIG_SUN50I_A64_CCU) += sun50i-a64-ccu.o +obj-$(CONFIG_SUN50I_A100_CCU) += sun50i-a100-ccu.o +obj-$(CONFIG_SUN50I_A100_R_CCU) += sun50i-a100-r-ccu.o +obj-$(CONFIG_SUN50I_H6_CCU) += sun50i-h6-ccu.o +obj-$(CONFIG_SUN50I_H6_R_CCU) += sun50i-h6-r-ccu.o +obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o +obj-$(CONFIG_SUN4I_A10_CCU) += sun4i-a10-ccu.o +obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o +obj-$(CONFIG_SUN6I_A31_CCU) += sun6i-a31-ccu.o +obj-$(CONFIG_SUN6I_RTC_CCU) += sun6i-rtc-ccu.o +obj-$(CONFIG_SUN8I_A23_CCU) += sun8i-a23-ccu.o +obj-$(CONFIG_SUN8I_A33_CCU) += sun8i-a33-ccu.o +obj-$(CONFIG_SUN8I_A83T_CCU) += sun8i-a83t-ccu.o +obj-$(CONFIG_SUN8I_H3_CCU) += sun8i-h3-ccu.o +obj-$(CONFIG_SUN8I_R40_CCU) += sun8i-r40-ccu.o +obj-$(CONFIG_SUN8I_V3S_CCU) += sun8i-v3s-ccu.o +obj-$(CONFIG_SUN8I_DE2_CCU) += sun8i-de2-ccu.o +obj-$(CONFIG_SUN8I_R_CCU) += sun8i-r-ccu.o +obj-$(CONFIG_SUN9I_A80_CCU) += sun9i-a80-ccu.o +obj-$(CONFIG_SUN9I_A80_CCU) += sun9i-a80-de-ccu.o +obj-$(CONFIG_SUN9I_A80_CCU) += sun9i-a80-usb-ccu.o + +suniv-f1c100s-ccu-y += ccu-suniv-f1c100s.o +sun20i-d1-ccu-y += ccu-sun20i-d1.o +sun20i-d1-r-ccu-y += ccu-sun20i-d1-r.o +sun50i-a64-ccu-y += ccu-sun50i-a64.o +sun50i-a100-ccu-y += ccu-sun50i-a100.o +sun50i-a100-r-ccu-y += ccu-sun50i-a100-r.o +sun50i-h6-ccu-y += ccu-sun50i-h6.o +sun50i-h6-r-ccu-y += ccu-sun50i-h6-r.o +sun50i-h616-ccu-y += ccu-sun50i-h616.o +sun4i-a10-ccu-y += ccu-sun4i-a10.o +sun5i-ccu-y += ccu-sun5i.o +sun6i-a31-ccu-y += ccu-sun6i-a31.o +sun6i-rtc-ccu-y += ccu-sun6i-rtc.o +sun8i-a23-ccu-y += ccu-sun8i-a23.o +sun8i-a33-ccu-y += ccu-sun8i-a33.o +sun8i-a83t-ccu-y += ccu-sun8i-a83t.o +sun8i-h3-ccu-y += ccu-sun8i-h3.o +sun8i-r40-ccu-y += ccu-sun8i-r40.o +sun8i-v3s-ccu-y += ccu-sun8i-v3s.o +sun8i-de2-ccu-y += ccu-sun8i-de2.o +sun8i-r-ccu-y += ccu-sun8i-r.o +sun9i-a80-ccu-y += ccu-sun9i-a80.o +sun9i-a80-de-ccu-y += ccu-sun9i-a80-de.o +sun9i-a80-usb-ccu-y += ccu-sun9i-a80-usb.o @ drivers/clk/sunxi-ng/ccu-sun20i-d1-r.c:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 huangzhenwei@allwinnertech.com + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_gate.h" +#include "ccu_mp.h" + +#include "ccu-sun20i-d1-r.h" + +static const struct clk_parent_data r_ahb_apb0_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .fw_name = "iosc" }, + { .fw_name = "pll-periph" }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX(r_ahb_clk, "r-ahb", + r_ahb_apb0_parents, 0x000, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + 0); +static const struct clk_hw *r_ahb_hw = &r_ahb_clk.common.hw; + +static SUNXI_CCU_MP_DATA_WITH_MUX(r_apb0_clk, "r-apb0", + r_ahb_apb0_parents, 0x00c, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + 0); +static const struct clk_hw *r_apb0_hw = &r_apb0_clk.common.hw; + +static SUNXI_CCU_GATE_HWS(bus_r_timer_clk, "bus-r-timer", &r_apb0_hw, + 0x11c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_r_twd_clk, "bus-r-twd", &r_apb0_hw, + 0x12c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_r_ppu_clk, "bus-r-ppu", &r_apb0_hw, + 0x1ac, BIT(0), 0); + +static const struct clk_parent_data r_ir_rx_parents[] = { + { .fw_name = "losc" }, + { .fw_name = "hosc" }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(r_ir_rx_clk, "r-ir-rx", + r_ir_rx_parents, 0x1c0, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_r_ir_rx_clk, "bus-r-ir-rx", &r_apb0_hw, + 0x1cc, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_r_rtc_clk, "bus-r-rtc", &r_ahb_hw, + 0x20c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_r_cpucfg_clk, "bus-r-cpucfg", &r_apb0_hw, + 0x22c, BIT(0), 0); + +static struct ccu_common *sun20i_d1_r_ccu_clks[] = { + &r_ahb_clk.common, + &r_apb0_clk.common, + &bus_r_timer_clk.common, + &bus_r_twd_clk.common, + &bus_r_ppu_clk.common, + &r_ir_rx_clk.common, + &bus_r_ir_rx_clk.common, + &bus_r_rtc_clk.common, + &bus_r_cpucfg_clk.common, +}; + +static struct clk_hw_onecell_data sun20i_d1_r_hw_clks = { + .num = CLK_NUMBER, + .hws = { + [CLK_R_AHB] = &r_ahb_clk.common.hw, + [CLK_R_APB0] = &r_apb0_clk.common.hw, + [CLK_BUS_R_TIMER] = &bus_r_timer_clk.common.hw, + [CLK_BUS_R_TWD] = &bus_r_twd_clk.common.hw, + [CLK_BUS_R_PPU] = &bus_r_ppu_clk.common.hw, + [CLK_R_IR_RX] = &r_ir_rx_clk.common.hw, + [CLK_BUS_R_IR_RX] = &bus_r_ir_rx_clk.common.hw, + [CLK_BUS_R_RTC] = &bus_r_rtc_clk.common.hw, + [CLK_BUS_R_CPUCFG] = &bus_r_cpucfg_clk.common.hw, + }, +}; + +static struct ccu_reset_map sun20i_d1_r_ccu_resets[] = { + [RST_BUS_R_TIMER] = { 0x11c, BIT(16) }, + [RST_BUS_R_TWD] = { 0x12c, BIT(16) }, + [RST_BUS_R_PPU] = { 0x1ac, BIT(16) }, + [RST_BUS_R_IR_RX] = { 0x1cc, BIT(16) }, + [RST_BUS_R_RTC] = { 0x20c, BIT(16) }, + [RST_BUS_R_CPUCFG] = { 0x22c, BIT(16) }, +}; + +static const struct sunxi_ccu_desc sun20i_d1_r_ccu_desc = { + .ccu_clks = sun20i_d1_r_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun20i_d1_r_ccu_clks), + + .hw_clks = &sun20i_d1_r_hw_clks, + + .resets = sun20i_d1_r_ccu_resets, + .num_resets = ARRAY_SIZE(sun20i_d1_r_ccu_resets), +}; + +static int sun20i_d1_r_ccu_probe(struct platform_device *pdev) +{ + void __iomem *reg; + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun20i_d1_r_ccu_desc); +} + +static const struct of_device_id sun20i_d1_r_ccu_ids[] = { + { .compatible = "allwinner,sun20i-d1-r-ccu" }, + { } +}; + +static struct platform_driver sun20i_d1_r_ccu_driver = { + .probe = sun20i_d1_r_ccu_probe, + .driver = { + .name = "sun20i-d1-r-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun20i_d1_r_ccu_ids, + }, +}; +module_platform_driver(sun20i_d1_r_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun20i-d1-r.h:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 frank@allwinnertech.com + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#ifndef _CCU_SUN20I_D1_R_H +#define _CCU_SUN20I_D1_R_H + +#include <dt-bindings/clock/sun20i-d1-r-ccu.h> +#include <dt-bindings/reset/sun20i-d1-r-ccu.h> + +#define CLK_R_APB0 1 + +#define CLK_NUMBER (CLK_BUS_R_CPUCFG + 1) + +#endif /* _CCU_SUN20I_D1_R_H */ @ drivers/clk/sunxi-ng/ccu-sun20i-d1.c:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 huangzhenwei@allwinnertech.com + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "../clk.h" + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" + +#include "ccu-sun20i-d1.h" + +static const struct clk_parent_data osc24M[] = { + { .fw_name = "hosc" } +}; + +/* + * For the CPU PLL, the output divider is described as "only for testing" + * in the user manual. So it's not modelled and forced to 0. + */ +#define SUN20I_D1_PLL_CPUX_REG 0x000 +static struct ccu_mult pll_cpux_clk = { + .enable = BIT(27), + .lock = BIT(28), + .mult = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-cpux", osc24M, + &ccu_mult_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +/* Some PLLs are input * N / div1 / P. Model them as NKMP with no K */ +#define SUN20I_D1_PLL_DDR0_REG 0x010 +static struct ccu_nkmp pll_ddr0_clk = { + .enable = BIT(27), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(0, 1), /* output divider */ + .common = { + .reg = 0x010, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-ddr0", osc24M, + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +#define SUN20I_D1_PLL_PERIPH0_REG 0x020 +static struct ccu_nm pll_periph0_4x_clk = { + .enable = BIT(27), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ + .common = { + .reg = 0x020, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-periph0-4x", osc24M, + &ccu_nm_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const struct clk_hw *pll_periph0_4x_hws[] = { + &pll_periph0_4x_clk.common.hw +}; +static SUNXI_CCU_M_HWS(pll_periph0_2x_clk, "pll-periph0-2x", + pll_periph0_4x_hws, 0x020, 16, 3, 0); +static SUNXI_CCU_M_HWS(pll_periph0_800M_clk, "pll-periph0-800M", + pll_periph0_4x_hws, 0x020, 20, 3, 0); + +static const struct clk_hw *pll_periph0_2x_hws[] = { + &pll_periph0_2x_clk.common.hw +}; +static CLK_FIXED_FACTOR_HWS(pll_periph0_clk, "pll-periph0", + pll_periph0_2x_hws, 2, 1, 0); + +static const struct clk_hw *pll_periph0_hws[] = { &pll_periph0_clk.hw }; +static CLK_FIXED_FACTOR_HWS(pll_periph0_div3_clk, "pll-periph0-div3", + pll_periph0_2x_hws, 6, 1, 0); + +/* + * For Video PLLs, the output divider is described as "only for testing" + * in the user manual. So it's not modelled and forced to 0. + */ +#define SUN20I_D1_PLL_VIDEO0_REG 0x040 +static struct ccu_nm pll_video0_4x_clk = { + .enable = BIT(27), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ + .common = { + .reg = 0x040, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-video0-4x", osc24M, + &ccu_nm_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const struct clk_hw *pll_video0_4x_hws[] = { + &pll_video0_4x_clk.common.hw +}; +static CLK_FIXED_FACTOR_HWS(pll_video0_2x_clk, "pll-video0-2x", + pll_video0_4x_hws, 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR_HWS(pll_video0_clk, "pll-video0", + pll_video0_4x_hws, 4, 1, CLK_SET_RATE_PARENT); + +#define SUN20I_D1_PLL_VIDEO1_REG 0x048 +static struct ccu_nm pll_video1_4x_clk = { + .enable = BIT(27), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ + .common = { + .reg = 0x048, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-video1-4x", osc24M, + &ccu_nm_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const struct clk_hw *pll_video1_4x_hws[] = { + &pll_video1_4x_clk.common.hw +}; +static CLK_FIXED_FACTOR_HWS(pll_video1_2x_clk, "pll-video1-2x", + pll_video1_4x_hws, 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR_HWS(pll_video1_clk, "pll-video1", + pll_video1_4x_hws, 4, 1, CLK_SET_RATE_PARENT); + +#define SUN20I_D1_PLL_VE_REG 0x058 +static struct ccu_nkmp pll_ve_clk = { + .enable = BIT(27), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ + .p = _SUNXI_CCU_DIV(0, 1), /* output divider */ + .common = { + .reg = 0x058, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-ve", osc24M, + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +/* + * PLL_AUDIO0 has m0, m1 dividers in addition to the usual N, M factors. + * Since we only need one frequency from this PLL (22.5792 x 4 == 90.3168 MHz), + * ignore them for now. Enforce the default for them, which is m1 = 0, m0 = 0. + * The M factor must be an even number to produce a 50% duty cycle output. + */ +#define SUN20I_D1_PLL_AUDIO0_REG 0x078 +static struct ccu_sdm_setting pll_audio0_sdm_table[] = { + { .rate = 90316800, .pattern = 0xc001288d, .m = 6, .n = 22 }, +}; + +static struct ccu_nm pll_audio0_4x_clk = { + .enable = BIT(27), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .m = _SUNXI_CCU_DIV(16, 6), + .sdm = _SUNXI_CCU_SDM(pll_audio0_sdm_table, BIT(24), + 0x178, BIT(31)), + .common = { + .reg = 0x078, + .features = CCU_FEATURE_SIGMA_DELTA_MOD, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-audio0-4x", osc24M, + &ccu_nm_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const struct clk_hw *pll_audio0_4x_hws[] = { + &pll_audio0_4x_clk.common.hw +}; +static CLK_FIXED_FACTOR_HWS(pll_audio0_2x_clk, "pll-audio0-2x", + pll_audio0_4x_hws, 2, 1, 0); +static CLK_FIXED_FACTOR_HWS(pll_audio0_clk, "pll-audio0", + pll_audio0_4x_hws, 4, 1, 0); + +/* + * PLL_AUDIO1 doesn't need Fractional-N. The output is usually 614.4 MHz for + * audio. The ADC or DAC should divide the PLL output further to 24.576 MHz. + */ +#define SUN20I_D1_PLL_AUDIO1_REG 0x080 +static struct ccu_nm pll_audio1_clk = { + .enable = BIT(27), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), + .m = _SUNXI_CCU_DIV(1, 1), + .common = { + .reg = 0x080, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-audio1", osc24M, + &ccu_nm_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const struct clk_hw *pll_audio1_hws[] = { + &pll_audio1_clk.common.hw +}; +static SUNXI_CCU_M_HWS(pll_audio1_div2_clk, "pll-audio1-div2", + pll_audio1_hws, 0x080, 16, 3, 0); +static SUNXI_CCU_M_HWS(pll_audio1_div5_clk, "pll-audio1-div5", + pll_audio1_hws, 0x080, 20, 3, 0); + +/* + * The CPUX gate is not modelled - it is in a separate register (0x504) + * and has a special key field. The clock does not need to be ungated anyway. + */ +static const struct clk_parent_data cpux_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .fw_name = "iosc" }, + { .hw = &pll_cpux_clk.common.hw }, + { .hw = &pll_periph0_clk.hw }, + { .hw = &pll_periph0_2x_clk.common.hw }, + { .hw = &pll_periph0_800M_clk.common.hw }, +}; +static SUNXI_CCU_MUX_DATA(cpux_clk, "cpux", cpux_parents, + 0x500, 24, 3, CLK_SET_RATE_PARENT); + +static const struct clk_hw *cpux_hws[] = { &cpux_clk.common.hw }; +static SUNXI_CCU_M_HWS(cpux_axi_clk, "cpux-axi", + cpux_hws, 0x500, 0, 2, 0); +static SUNXI_CCU_M_HWS(cpux_apb_clk, "cpux-apb", + cpux_hws, 0x500, 8, 2, 0); + +static const struct clk_parent_data psi_ahb_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .fw_name = "iosc" }, + { .hw = &pll_periph0_clk.hw }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX(psi_ahb_clk, "psi-ahb", psi_ahb_parents, 0x510, + 0, 2, /* M */ + 8, 2, /* P */ + 24, 2, /* mux */ + 0); + +static const struct clk_parent_data apb0_apb1_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .hw = &psi_ahb_clk.common.hw }, + { .hw = &pll_periph0_clk.hw }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX(apb0_clk, "apb0", apb0_apb1_parents, 0x520, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 2, /* mux */ + 0); + +static SUNXI_CCU_MP_DATA_WITH_MUX(apb1_clk, "apb1", apb0_apb1_parents, 0x524, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 2, /* mux */ + 0); + +static const struct clk_hw *psi_ahb_hws[] = { &psi_ahb_clk.common.hw }; +static const struct clk_hw *apb0_hws[] = { &apb0_clk.common.hw }; +static const struct clk_hw *apb1_hws[] = { &apb1_clk.common.hw }; + +static const struct clk_hw *de_di_g2d_parents[] = { + &pll_periph0_2x_clk.common.hw, + &pll_video0_4x_clk.common.hw, + &pll_video1_4x_clk.common.hw, + &pll_audio1_div2_clk.common.hw, +}; +static SUNXI_CCU_M_HW_WITH_MUX_GATE(de_clk, "de", de_di_g2d_parents, 0x600, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_HWS(bus_de_clk, "bus-de", psi_ahb_hws, + 0x60c, BIT(0), 0); + +static SUNXI_CCU_M_HW_WITH_MUX_GATE(di_clk, "di", de_di_g2d_parents, 0x620, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_HWS(bus_di_clk, "bus-di", psi_ahb_hws, + 0x62c, BIT(0), 0); + +static SUNXI_CCU_M_HW_WITH_MUX_GATE(g2d_clk, "g2d", de_di_g2d_parents, 0x630, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_g2d_clk, "bus-g2d", psi_ahb_hws, + 0x63c, BIT(0), 0); + +static const struct clk_parent_data ce_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_periph0_2x_clk.common.hw }, + { .hw = &pll_periph0_clk.hw }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0x680, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_ce_clk, "bus-ce", psi_ahb_hws, + 0x68c, BIT(0), 0); + +static const struct clk_hw *ve_parents[] = { + &pll_ve_clk.common.hw, + &pll_periph0_2x_clk.common.hw, +}; +static SUNXI_CCU_M_HW_WITH_MUX_GATE(ve_clk, "ve", ve_parents, 0x690, + 0, 5, /* M */ + 24, 1, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_HWS(bus_ve_clk, "bus-ve", psi_ahb_hws, + 0x69c, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_dma_clk, "bus-dma", psi_ahb_hws, + 0x70c, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_msgbox0_clk, "bus-msgbox0", psi_ahb_hws, + 0x71c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_msgbox1_clk, "bus-msgbox1", psi_ahb_hws, + 0x71c, BIT(1), 0); +static SUNXI_CCU_GATE_HWS(bus_msgbox2_clk, "bus-msgbox2", psi_ahb_hws, + 0x71c, BIT(2), 0); + +static SUNXI_CCU_GATE_HWS(bus_spinlock_clk, "bus-spinlock", psi_ahb_hws, + 0x72c, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_hstimer_clk, "bus-hstimer", psi_ahb_hws, + 0x73c, BIT(0), 0); + +static SUNXI_CCU_GATE_DATA(avs_clk, "avs", osc24M, + 0x740, BIT(31), 0); + +static SUNXI_CCU_GATE_HWS(bus_dbg_clk, "bus-dbg", psi_ahb_hws, + 0x78c, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_pwm_clk, "bus-pwm", apb0_hws, + 0x7ac, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_iommu_clk, "bus-iommu", apb0_hws, + 0x7bc, BIT(0), 0); + +static const struct clk_hw *dram_parents[] = { + &pll_ddr0_clk.common.hw, + &pll_audio1_div2_clk.common.hw, + &pll_periph0_2x_clk.common.hw, + &pll_periph0_800M_clk.common.hw, +}; +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(dram_clk, "dram", dram_parents, 0x800, + 0, 2, /* M */ + 8, 2, /* P */ + 24, 2, /* mux */ + BIT(31), CLK_IS_CRITICAL); + +static CLK_FIXED_FACTOR_HW(mbus_clk, "mbus", + &dram_clk.common.hw, 4, 1, 0); + +static const struct clk_hw *mbus_hws[] = { &mbus_clk.hw }; + +static SUNXI_CCU_GATE_HWS(mbus_dma_clk, "mbus-dma", mbus_hws, + 0x804, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(mbus_ve_clk, "mbus-ve", mbus_hws, + 0x804, BIT(1), 0); +static SUNXI_CCU_GATE_HWS(mbus_ce_clk, "mbus-ce", mbus_hws, + 0x804, BIT(2), 0); +static SUNXI_CCU_GATE_HWS(mbus_tvin_clk, "mbus-tvin", mbus_hws, + 0x804, BIT(7), 0); +static SUNXI_CCU_GATE_HWS(mbus_csi_clk, "mbus-csi", mbus_hws, + 0x804, BIT(8), 0); +static SUNXI_CCU_GATE_HWS(mbus_g2d_clk, "mbus-g2d", mbus_hws, + 0x804, BIT(10), 0); +static SUNXI_CCU_GATE_HWS(mbus_riscv_clk, "mbus-riscv", mbus_hws, + 0x804, BIT(11), 0); + +static SUNXI_CCU_GATE_HWS(bus_dram_clk, "bus-dram", psi_ahb_hws, + 0x80c, BIT(0), CLK_IS_CRITICAL); + +static const struct clk_parent_data mmc0_mmc1_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_periph0_clk.hw }, + { .hw = &pll_periph0_2x_clk.common.hw }, + { .hw = &pll_audio1_div2_clk.common.hw }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(mmc0_clk, "mmc0", mmc0_mmc1_parents, 0x830, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(mmc1_clk, "mmc1", mmc0_mmc1_parents, 0x834, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static const struct clk_parent_data mmc2_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_periph0_clk.hw }, + { .hw = &pll_periph0_2x_clk.common.hw }, + { .hw = &pll_periph0_800M_clk.common.hw }, + { .hw = &pll_audio1_div2_clk.common.hw }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(mmc2_clk, "mmc2", mmc2_parents, 0x838, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_mmc0_clk, "bus-mmc0", psi_ahb_hws, + 0x84c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_mmc1_clk, "bus-mmc1", psi_ahb_hws, + 0x84c, BIT(1), 0); +static SUNXI_CCU_GATE_HWS(bus_mmc2_clk, "bus-mmc2", psi_ahb_hws, + 0x84c, BIT(2), 0); + +static SUNXI_CCU_GATE_HWS(bus_uart0_clk, "bus-uart0", apb1_hws, + 0x90c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_uart1_clk, "bus-uart1", apb1_hws, + 0x90c, BIT(1), 0); +static SUNXI_CCU_GATE_HWS(bus_uart2_clk, "bus-uart2", apb1_hws, + 0x90c, BIT(2), 0); +static SUNXI_CCU_GATE_HWS(bus_uart3_clk, "bus-uart3", apb1_hws, + 0x90c, BIT(3), 0); +static SUNXI_CCU_GATE_HWS(bus_uart4_clk, "bus-uart4", apb1_hws, + 0x90c, BIT(4), 0); +static SUNXI_CCU_GATE_HWS(bus_uart5_clk, "bus-uart5", apb1_hws, + 0x90c, BIT(5), 0); + +static SUNXI_CCU_GATE_HWS(bus_i2c0_clk, "bus-i2c0", apb1_hws, + 0x91c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_i2c1_clk, "bus-i2c1", apb1_hws, + 0x91c, BIT(1), 0); +static SUNXI_CCU_GATE_HWS(bus_i2c2_clk, "bus-i2c2", apb1_hws, + 0x91c, BIT(2), 0); +static SUNXI_CCU_GATE_HWS(bus_i2c3_clk, "bus-i2c3", apb1_hws, + 0x91c, BIT(3), 0); + +static const struct clk_parent_data spi_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_periph0_clk.hw }, + { .hw = &pll_periph0_2x_clk.common.hw }, + { .hw = &pll_audio1_div2_clk.common.hw }, + { .hw = &pll_audio1_div5_clk.common.hw }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(spi0_clk, "spi0", spi_parents, 0x940, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(spi1_clk, "spi1", spi_parents, 0x944, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_spi0_clk, "bus-spi0", psi_ahb_hws, + 0x96c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_spi1_clk, "bus-spi1", psi_ahb_hws, + 0x96c, BIT(1), 0); + +static SUNXI_CCU_GATE_HWS_WITH_PREDIV(emac_25M_clk, "emac-25M", pll_periph0_hws, + 0x970, BIT(31) | BIT(30), 24, 0); + +static SUNXI_CCU_GATE_HWS(bus_emac_clk, "bus-emac", psi_ahb_hws, + 0x97c, BIT(0), 0); + +static const struct clk_parent_data ir_tx_ledc_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_periph0_clk.hw }, +}; +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(ir_tx_clk, "ir-tx", ir_tx_ledc_parents, 0x9c0, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_ir_tx_clk, "bus-ir-tx", apb0_hws, + 0x9cc, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_gpadc_clk, "bus-gpadc", apb0_hws, + 0x9ec, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_ths_clk, "bus-ths", apb0_hws, + 0x9fc, BIT(0), 0); + +static const struct clk_hw *i2s_spdif_tx_parents[] = { + &pll_audio0_clk.hw, + &pll_audio0_4x_clk.common.hw, + &pll_audio1_div2_clk.common.hw, + &pll_audio1_div5_clk.common.hw, +}; +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(i2s0_clk, "i2s0", i2s_spdif_tx_parents, 0xa10, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(i2s1_clk, "i2s1", i2s_spdif_tx_parents, 0xa14, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(i2s2_clk, "i2s2", i2s_spdif_tx_parents, 0xa18, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static const struct clk_hw *i2s2_asrc_parents[] = { + &pll_audio0_4x_clk.common.hw, + &pll_periph0_clk.hw, + &pll_audio1_div2_clk.common.hw, + &pll_audio1_div5_clk.common.hw, +}; +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(i2s2_asrc_clk, "i2s2-asrc", i2s2_asrc_parents, 0xa1c, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_i2s0_clk, "bus-i2s0", apb0_hws, + 0xa20, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_i2s1_clk, "bus-i2s1", apb0_hws, + 0xa20, BIT(1), 0); +static SUNXI_CCU_GATE_HWS(bus_i2s2_clk, "bus-i2s2", apb0_hws, + 0xa20, BIT(2), 0); + +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(spdif_tx_clk, "spdif-tx", i2s_spdif_tx_parents, 0xa24, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static const struct clk_hw *spdif_rx_parents[] = { + &pll_periph0_clk.hw, + &pll_audio1_div2_clk.common.hw, + &pll_audio1_div5_clk.common.hw, +}; +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(spdif_rx_clk, "spdif-rx", spdif_rx_parents, 0xa28, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_spdif_clk, "bus-spdif", apb0_hws, + 0xa2c, BIT(0), 0); + +static const struct clk_hw *dmic_codec_parents[] = { + &pll_audio0_clk.hw, + &pll_audio1_div2_clk.common.hw, + &pll_audio1_div5_clk.common.hw, +}; +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(dmic_clk, "dmic", dmic_codec_parents, 0xa40, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_dmic_clk, "bus-dmic", apb0_hws, + 0xa4c, BIT(0), 0); + +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(audio_dac_clk, "audio-dac", dmic_codec_parents, 0xa50, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(audio_adc_clk, "audio-adc", dmic_codec_parents, 0xa54, + 0, 5, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_audio_clk, "bus-audio", apb0_hws, + 0xa5c, BIT(0), 0); + + +/* + * The first parent is a 48 MHz input clock divided by 4. That 48 MHz clock is + * a 2x multiplier from osc24M synchronized by pll-periph0, and is also used by + * the OHCI module. + */ +static const struct clk_parent_data usb_ohci_parents[] = { + { .hw = &pll_periph0_clk.hw }, + { .fw_name = "hosc" }, + { .fw_name = "losc" }, +}; +static const struct ccu_mux_fixed_prediv usb_ohci_predivs[] = { + { .index = 0, .div = 50 }, + { .index = 1, .div = 2 }, +}; + +static struct ccu_mux usb_ohci0_clk = { + .enable = BIT(31), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = usb_ohci_predivs, + .n_predivs = ARRAY_SIZE(usb_ohci_predivs), + }, + .common = { + .reg = 0xa70, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS_DATA("usb-ohci0", + usb_ohci_parents, + &ccu_mux_ops, + 0), + }, +}; + +static struct ccu_mux usb_ohci1_clk = { + .enable = BIT(31), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = usb_ohci_predivs, + .n_predivs = ARRAY_SIZE(usb_ohci_predivs), + }, + .common = { + .reg = 0xa74, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS_DATA("usb-ohci1", + usb_ohci_parents, + &ccu_mux_ops, + 0), + }, +}; + +static SUNXI_CCU_GATE_HWS(bus_ohci0_clk, "bus-ohci0", psi_ahb_hws, + 0xa8c, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_ohci1_clk, "bus-ohci1", psi_ahb_hws, + 0xa8c, BIT(1), 0); +static SUNXI_CCU_GATE_HWS(bus_ehci0_clk, "bus-ehci0", psi_ahb_hws, + 0xa8c, BIT(4), 0); +static SUNXI_CCU_GATE_HWS(bus_ehci1_clk, "bus-ehci1", psi_ahb_hws, + 0xa8c, BIT(5), 0); +static SUNXI_CCU_GATE_HWS(bus_otg_clk, "bus-otg", psi_ahb_hws, + 0xa8c, BIT(8), 0); + +static SUNXI_CCU_GATE_HWS(bus_lradc_clk, "bus-lradc", apb0_hws, + 0xa9c, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_dpss_top_clk, "bus-dpss-top", psi_ahb_hws, + 0xabc, BIT(0), 0); + +static SUNXI_CCU_GATE_DATA(hdmi_24M_clk, "hdmi-24M", osc24M, + 0xb04, BIT(31), 0); + +static SUNXI_CCU_GATE_HWS_WITH_PREDIV(hdmi_cec_32k_clk, "hdmi-cec-32k", + pll_periph0_2x_hws, + 0xb10, BIT(30), 36621, 0); + +static const struct clk_parent_data hdmi_cec_parents[] = { + { .fw_name = "losc" }, + { .hw = &hdmi_cec_32k_clk.common.hw }, +}; +static SUNXI_CCU_MUX_DATA_WITH_GATE(hdmi_cec_clk, "hdmi-cec", hdmi_cec_parents, 0xb10, + 24, 1, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_hdmi_clk, "bus-hdmi", psi_ahb_hws, + 0xb1c, BIT(0), 0); + +static const struct clk_parent_data mipi_dsi_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_periph0_clk.hw }, + { .hw = &pll_video0_2x_clk.hw }, + { .hw = &pll_video1_2x_clk.hw }, + { .hw = &pll_audio1_div2_clk.common.hw }, +}; +static SUNXI_CCU_M_DATA_WITH_MUX_GATE(mipi_dsi_clk, "mipi-dsi", mipi_dsi_parents, 0xb24, + 0, 4, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_HWS(bus_mipi_dsi_clk, "bus-mipi-dsi", psi_ahb_hws, + 0xb4c, BIT(0), 0); + +static const struct clk_hw *tcon_tve_parents[] = { + &pll_video0_clk.hw, + &pll_video0_4x_clk.common.hw, + &pll_video1_clk.hw, + &pll_video1_4x_clk.common.hw, + &pll_periph0_2x_clk.common.hw, + &pll_audio1_div2_clk.common.hw, +}; +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(tcon_lcd0_clk, "tcon-lcd0", tcon_tve_parents, 0xb60, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_HWS(bus_tcon_lcd0_clk, "bus-tcon-lcd0", psi_ahb_hws, + 0xb7c, BIT(0), 0); + +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(tcon_tv_clk, "tcon-tv", tcon_tve_parents, 0xb80, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_HWS(bus_tcon_tv_clk, "bus-tcon-tv", psi_ahb_hws, + 0xb9c, BIT(0), 0); + +static SUNXI_CCU_MP_HW_WITH_MUX_GATE(tve_clk, "tve", tcon_tve_parents, 0xbb0, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_tve_top_clk, "bus-tve-top", psi_ahb_hws, + 0xbbc, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_tve_clk, "bus-tve", psi_ahb_hws, + 0xbbc, BIT(1), 0); + +static const struct clk_parent_data tvd_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_video0_clk.hw }, + { .hw = &pll_video1_clk.hw }, + { .hw = &pll_periph0_clk.hw }, +}; +static SUNXI_CCU_M_DATA_WITH_MUX_GATE(tvd_clk, "tvd", tvd_parents, 0xbc0, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_tvd_top_clk, "bus-tvd-top", psi_ahb_hws, + 0xbdc, BIT(0), 0); +static SUNXI_CCU_GATE_HWS(bus_tvd_clk, "bus-tvd", psi_ahb_hws, + 0xbdc, BIT(1), 0); + +static SUNXI_CCU_MP_DATA_WITH_MUX_GATE(ledc_clk, "ledc", ir_tx_ledc_parents, 0xbf0, + 0, 4, /* M */ + 8, 2, /* P */ + 24, 1, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_ledc_clk, "bus-ledc", psi_ahb_hws, + 0xbfc, BIT(0), 0); + +static const struct clk_hw *csi_top_parents[] = { + &pll_periph0_2x_clk.common.hw, + &pll_video0_2x_clk.hw, + &pll_video1_2x_clk.hw, +}; +static SUNXI_CCU_M_HW_WITH_MUX_GATE(csi_top_clk, "csi-top", csi_top_parents, 0xc04, + 0, 4, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static const struct clk_parent_data csi_mclk_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_periph0_clk.hw }, + { .hw = &pll_video0_clk.hw }, + { .hw = &pll_video1_clk.hw }, + { .hw = &pll_audio1_div2_clk.common.hw }, + { .hw = &pll_audio1_div5_clk.common.hw }, +}; +static SUNXI_CCU_M_DATA_WITH_MUX_GATE(csi_mclk_clk, "csi-mclk", csi_mclk_parents, 0xc08, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_csi_clk, "bus-csi", psi_ahb_hws, + 0xc1c, BIT(0), 0); + +static const struct clk_parent_data tpadc_parents[] = { + { .fw_name = "hosc" }, + { .hw = &pll_audio0_clk.hw }, +}; +static SUNXI_CCU_MUX_DATA_WITH_GATE(tpadc_clk, "tpadc", tpadc_parents, 0xc50, + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_tpadc_clk, "bus-tpadc", apb0_hws, + 0xc5c, BIT(0), 0); + +static SUNXI_CCU_GATE_HWS(bus_tzma_clk, "bus-tzma", apb0_hws, + 0xc6c, BIT(0), 0); + +static const struct clk_parent_data dsp_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .fw_name = "iosc" }, + { .hw = &pll_periph0_2x_clk.common.hw }, + { .hw = &pll_audio1_div2_clk.common.hw }, +}; +static SUNXI_CCU_M_DATA_WITH_MUX_GATE(dsp_clk, "dsp", dsp_parents, 0xc70, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_GATE_HWS(bus_dsp_cfg_clk, "bus-dsp-cfg", psi_ahb_hws, + 0xc7c, BIT(1), 0); + +/* + * The RISC-V gate is not modelled - it is in a separate register (0xd04) + * and has a special key field. The clock is critical anyway. + */ +static const struct clk_parent_data riscv_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .fw_name = "iosc" }, + { .hw = &pll_periph0_800M_clk.common.hw }, + { .hw = &pll_periph0_clk.hw }, + { .hw = &pll_cpux_clk.common.hw }, + { .hw = &pll_audio1_div2_clk.common.hw }, +}; +static SUNXI_CCU_M_DATA_WITH_MUX(riscv_clk, "riscv", riscv_parents, 0xd00, + 0, 5, /* M */ + 24, 3, /* mux */ + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + +/* The riscv-axi clk must be divided by at least 2. */ +static struct clk_div_table riscv_axi_table[] = { + { .val = 1, .div = 2 }, + { .val = 2, .div = 3 }, + { .val = 3, .div = 4 }, + { /* Sentinel */ } +}; +static SUNXI_CCU_DIV_TABLE_HW(riscv_axi_clk, "riscv-axi", &riscv_clk.common.hw, + 0xd00, 8, 2, riscv_axi_table, 0); + +static SUNXI_CCU_GATE_HWS(bus_riscv_cfg_clk, "bus-riscv-cfg", psi_ahb_hws, + 0xd0c, BIT(0), CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE_DATA(fanout_24M_clk, "fanout-24M", osc24M, + 0xf30, BIT(0), 0); +static SUNXI_CCU_GATE_DATA_WITH_PREDIV(fanout_12M_clk, "fanout-12M", osc24M, + 0xf30, BIT(1), 2, 0); +static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_16M_clk, "fanout-16M", pll_periph0_2x_hws, + 0xf30, BIT(2), 75, 0); +static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_25M_clk, "fanout-25M", pll_periph0_hws, + 0xf30, BIT(3), 24, 0); +static SUNXI_CCU_GATE_HWS_WITH_PREDIV(fanout_32k_clk, "fanout-32k", pll_periph0_2x_hws, + 0xf30, BIT(4), 36621, 0); + +/* This clock has a second divider that is not modelled and forced to 0. */ +#define SUN20I_D1_FANOUT_27M_REG 0xf34 +static const struct clk_hw *fanout_27M_parents[] = { + &pll_video0_clk.hw, + &pll_video1_clk.hw, +}; +static SUNXI_CCU_M_HW_WITH_MUX_GATE(fanout_27M_clk, "fanout-27M", fanout_27M_parents, 0xf34, + 0, 5, /* M */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_M_HWS_WITH_GATE(fanout_pclk_clk, "fanout-pclk", apb0_hws, 0xf38, + 0, 5, /* M */ + BIT(31), /* gate */ + 0); + +static const struct clk_hw *fanout_parents[] = { + &fanout_32k_clk.common.hw, + &fanout_12M_clk.common.hw, + &fanout_16M_clk.common.hw, + &fanout_24M_clk.common.hw, + &fanout_25M_clk.common.hw, + &fanout_27M_clk.common.hw, + &fanout_pclk_clk.common.hw, +}; +static SUNXI_CCU_MUX_HW_WITH_GATE(fanout0_clk, "fanout0", fanout_parents, 0xf3c, + 0, 3, /* mux */ + BIT(21), /* gate */ + 0); +static SUNXI_CCU_MUX_HW_WITH_GATE(fanout1_clk, "fanout1", fanout_parents, 0xf3c, + 3, 3, /* mux */ + BIT(22), /* gate */ + 0); +static SUNXI_CCU_MUX_HW_WITH_GATE(fanout2_clk, "fanout2", fanout_parents, 0xf3c, + 6, 3, /* mux */ + BIT(23), /* gate */ + 0); + +static struct ccu_common *sun20i_d1_ccu_clks[] = { + &pll_cpux_clk.common, + &pll_ddr0_clk.common, + &pll_periph0_4x_clk.common, + &pll_periph0_2x_clk.common, + &pll_periph0_800M_clk.common, + &pll_video0_4x_clk.common, + &pll_video1_4x_clk.common, + &pll_ve_clk.common, + &pll_audio0_4x_clk.common, + &pll_audio1_clk.common, + &pll_audio1_div2_clk.common, + &pll_audio1_div5_clk.common, + &cpux_clk.common, + &cpux_axi_clk.common, + &cpux_apb_clk.common, + &psi_ahb_clk.common, + &apb0_clk.common, + &apb1_clk.common, + &de_clk.common, + &bus_de_clk.common, + &di_clk.common, + &bus_di_clk.common, + &g2d_clk.common, + &bus_g2d_clk.common, + &ce_clk.common, + &bus_ce_clk.common, + &ve_clk.common, + &bus_ve_clk.common, + &bus_dma_clk.common, + &bus_msgbox0_clk.common, + &bus_msgbox1_clk.common, + &bus_msgbox2_clk.common, + &bus_spinlock_clk.common, + &bus_hstimer_clk.common, + &avs_clk.common, + &bus_dbg_clk.common, + &bus_pwm_clk.common, + &bus_iommu_clk.common, + &dram_clk.common, + &mbus_dma_clk.common, + &mbus_ve_clk.common, + &mbus_ce_clk.common, + &mbus_tvin_clk.common, + &mbus_csi_clk.common, + &mbus_g2d_clk.common, + &mbus_riscv_clk.common, + &bus_dram_clk.common, + &mmc0_clk.common, + &mmc1_clk.common, + &mmc2_clk.common, + &bus_mmc0_clk.common, + &bus_mmc1_clk.common, + &bus_mmc2_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_uart3_clk.common, + &bus_uart4_clk.common, + &bus_uart5_clk.common, + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_i2c2_clk.common, + &bus_i2c3_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &bus_spi0_clk.common, + &bus_spi1_clk.common, + &emac_25M_clk.common, + &bus_emac_clk.common, + &ir_tx_clk.common, + &bus_ir_tx_clk.common, + &bus_gpadc_clk.common, + &bus_ths_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &i2s2_clk.common, + &i2s2_asrc_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_i2s2_clk.common, + &spdif_tx_clk.common, + &spdif_rx_clk.common, + &bus_spdif_clk.common, + &dmic_clk.common, + &bus_dmic_clk.common, + &audio_dac_clk.common, + &audio_adc_clk.common, + &bus_audio_clk.common, + &usb_ohci0_clk.common, + &usb_ohci1_clk.common, + &bus_ohci0_clk.common, + &bus_ohci1_clk.common, + &bus_ehci0_clk.common, + &bus_ehci1_clk.common, + &bus_otg_clk.common, + &bus_lradc_clk.common, + &bus_dpss_top_clk.common, + &hdmi_24M_clk.common, + &hdmi_cec_32k_clk.common, + &hdmi_cec_clk.common, + &bus_hdmi_clk.common, + &mipi_dsi_clk.common, + &bus_mipi_dsi_clk.common, + &tcon_lcd0_clk.common, + &bus_tcon_lcd0_clk.common, + &tcon_tv_clk.common, + &bus_tcon_tv_clk.common, + &tve_clk.common, + &bus_tve_top_clk.common, + &bus_tve_clk.common, + &tvd_clk.common, + &bus_tvd_top_clk.common, + &bus_tvd_clk.common, + &ledc_clk.common, + &bus_ledc_clk.common, + &csi_top_clk.common, + &csi_mclk_clk.common, + &bus_csi_clk.common, + &tpadc_clk.common, + &bus_tpadc_clk.common, + &bus_tzma_clk.common, + &dsp_clk.common, + &bus_dsp_cfg_clk.common, + &riscv_clk.common, + &riscv_axi_clk.common, + &bus_riscv_cfg_clk.common, + &fanout_24M_clk.common, + &fanout_12M_clk.common, + &fanout_16M_clk.common, + &fanout_25M_clk.common, + &fanout_32k_clk.common, + &fanout_27M_clk.common, + &fanout_pclk_clk.common, + &fanout0_clk.common, + &fanout1_clk.common, + &fanout2_clk.common, +}; + +static struct clk_hw_onecell_data sun20i_d1_hw_clks = { + .num = CLK_NUMBER, + .hws = { + [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw, + [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw, + [CLK_PLL_PERIPH0_4X] = &pll_periph0_4x_clk.common.hw, + [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.common.hw, + [CLK_PLL_PERIPH0_800M] = &pll_periph0_800M_clk.common.hw, + [CLK_PLL_PERIPH0] = &pll_periph0_clk.hw, + [CLK_PLL_PERIPH0_DIV3] = &pll_periph0_div3_clk.hw, + [CLK_PLL_VIDEO0_4X] = &pll_video0_4x_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.hw, + [CLK_PLL_VIDEO1_4X] = &pll_video1_4x_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_AUDIO0_4X] = &pll_audio0_4x_clk.common.hw, + [CLK_PLL_AUDIO0_2X] = &pll_audio0_2x_clk.hw, + [CLK_PLL_AUDIO0] = &pll_audio0_clk.hw, + [CLK_PLL_AUDIO1] = &pll_audio1_clk.common.hw, + [CLK_PLL_AUDIO1_DIV2] = &pll_audio1_div2_clk.common.hw, + [CLK_PLL_AUDIO1_DIV5] = &pll_audio1_div5_clk.common.hw, + [CLK_CPUX] = &cpux_clk.common.hw, + [CLK_CPUX_AXI] = &cpux_axi_clk.common.hw, + [CLK_CPUX_APB] = &cpux_apb_clk.common.hw, + [CLK_PSI_AHB] = &psi_ahb_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_MBUS] = &mbus_clk.hw, + [CLK_DE] = &de_clk.common.hw, + [CLK_BUS_DE] = &bus_de_clk.common.hw, + [CLK_DI] = &di_clk.common.hw, + [CLK_BUS_DI] = &bus_di_clk.common.hw, + [CLK_G2D] = &g2d_clk.common.hw, + [CLK_BUS_G2D] = &bus_g2d_clk.common.hw, + [CLK_CE] = &ce_clk.common.hw, + [CLK_BUS_CE] = &bus_ce_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_BUS_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_DMA] = &bus_dma_clk.common.hw, + [CLK_BUS_MSGBOX0] = &bus_msgbox0_clk.common.hw, + [CLK_BUS_MSGBOX1] = &bus_msgbox1_clk.common.hw, + [CLK_BUS_MSGBOX2] = &bus_msgbox2_clk.common.hw, + [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw, + [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_BUS_DBG] = &bus_dbg_clk.common.hw, + [CLK_BUS_PWM] = &bus_pwm_clk.common.hw, + [CLK_BUS_IOMMU] = &bus_iommu_clk.common.hw, + [CLK_DRAM] = &dram_clk.common.hw, + [CLK_MBUS_DMA] = &mbus_dma_clk.common.hw, + [CLK_MBUS_VE] = &mbus_ve_clk.common.hw, + [CLK_MBUS_CE] = &mbus_ce_clk.common.hw, + [CLK_MBUS_TVIN] = &mbus_tvin_clk.common.hw, + [CLK_MBUS_CSI] = &mbus_csi_clk.common.hw, + [CLK_MBUS_G2D] = &mbus_g2d_clk.common.hw, + [CLK_MBUS_RISCV] = &mbus_riscv_clk.common.hw, + [CLK_BUS_DRAM] = &bus_dram_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw, + [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw, + [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw, + [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, + [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, + [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, + [CLK_BUS_UART3] = &bus_uart3_clk.common.hw, + [CLK_BUS_UART4] = &bus_uart4_clk.common.hw, + [CLK_BUS_UART5] = &bus_uart5_clk.common.hw, + [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, + [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, + [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw, + [CLK_BUS_I2C3] = &bus_i2c3_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, + [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw, + [CLK_EMAC_25M] = &emac_25M_clk.common.hw, + [CLK_BUS_EMAC] = &bus_emac_clk.common.hw, + [CLK_IR_TX] = &ir_tx_clk.common.hw, + [CLK_BUS_IR_TX] = &bus_ir_tx_clk.common.hw, + [CLK_BUS_GPADC] = &bus_gpadc_clk.common.hw, + [CLK_BUS_THS] = &bus_ths_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_I2S2] = &i2s2_clk.common.hw, + [CLK_I2S2_ASRC] = &i2s2_asrc_clk.common.hw, + [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw, + [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw, + [CLK_BUS_I2S2] = &bus_i2s2_clk.common.hw, + [CLK_SPDIF_TX] = &spdif_tx_clk.common.hw, + [CLK_SPDIF_RX] = &spdif_rx_clk.common.hw, + [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw, + [CLK_DMIC] = &dmic_clk.common.hw, + [CLK_BUS_DMIC] = &bus_dmic_clk.common.hw, + [CLK_AUDIO_DAC] = &audio_dac_clk.common.hw, + [CLK_AUDIO_ADC] = &audio_adc_clk.common.hw, + [CLK_BUS_AUDIO] = &bus_audio_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw, + [CLK_BUS_OHCI1] = &bus_ohci1_clk.common.hw, + [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw, + [CLK_BUS_EHCI1] = &bus_ehci1_clk.common.hw, + [CLK_BUS_OTG] = &bus_otg_clk.common.hw, + [CLK_BUS_LRADC] = &bus_lradc_clk.common.hw, + [CLK_BUS_DPSS_TOP] = &bus_dpss_top_clk.common.hw, + [CLK_HDMI_24M] = &hdmi_24M_clk.common.hw, + [CLK_HDMI_CEC_32K] = &hdmi_cec_32k_clk.common.hw, + [CLK_HDMI_CEC] = &hdmi_cec_clk.common.hw, + [CLK_BUS_HDMI] = &bus_hdmi_clk.common.hw, + [CLK_MIPI_DSI] = &mipi_dsi_clk.common.hw, + [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw, + [CLK_TCON_LCD0] = &tcon_lcd0_clk.common.hw, + [CLK_BUS_TCON_LCD0] = &bus_tcon_lcd0_clk.common.hw, + [CLK_TCON_TV] = &tcon_tv_clk.common.hw, + [CLK_BUS_TCON_TV] = &bus_tcon_tv_clk.common.hw, + [CLK_TVE] = &tve_clk.common.hw, + [CLK_BUS_TVE_TOP] = &bus_tve_top_clk.common.hw, + [CLK_BUS_TVE] = &bus_tve_clk.common.hw, + [CLK_TVD] = &tvd_clk.common.hw, + [CLK_BUS_TVD_TOP] = &bus_tvd_top_clk.common.hw, + [CLK_BUS_TVD] = &bus_tvd_clk.common.hw, + [CLK_LEDC] = &ledc_clk.common.hw, + [CLK_BUS_LEDC] = &bus_ledc_clk.common.hw, + [CLK_CSI_TOP] = &csi_top_clk.common.hw, + [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw, + [CLK_BUS_CSI] = &bus_csi_clk.common.hw, + [CLK_TPADC] = &tpadc_clk.common.hw, + [CLK_BUS_TPADC] = &bus_tpadc_clk.common.hw, + [CLK_BUS_TZMA] = &bus_tzma_clk.common.hw, + [CLK_DSP] = &dsp_clk.common.hw, + [CLK_BUS_DSP_CFG] = &bus_dsp_cfg_clk.common.hw, + [CLK_RISCV] = &riscv_clk.common.hw, + [CLK_RISCV_AXI] = &riscv_axi_clk.common.hw, + [CLK_BUS_RISCV_CFG] = &bus_riscv_cfg_clk.common.hw, + [CLK_FANOUT_24M] = &fanout_24M_clk.common.hw, + [CLK_FANOUT_12M] = &fanout_12M_clk.common.hw, + [CLK_FANOUT_16M] = &fanout_16M_clk.common.hw, + [CLK_FANOUT_25M] = &fanout_25M_clk.common.hw, + [CLK_FANOUT_32K] = &fanout_32k_clk.common.hw, + [CLK_FANOUT_27M] = &fanout_27M_clk.common.hw, + [CLK_FANOUT_PCLK] = &fanout_pclk_clk.common.hw, + [CLK_FANOUT0] = &fanout0_clk.common.hw, + [CLK_FANOUT1] = &fanout1_clk.common.hw, + [CLK_FANOUT2] = &fanout2_clk.common.hw, + }, +}; + +static struct ccu_reset_map sun20i_d1_ccu_resets[] = { + [RST_MBUS] = { 0x540, BIT(30) }, + [RST_BUS_DE] = { 0x60c, BIT(16) }, + [RST_BUS_DI] = { 0x62c, BIT(16) }, + [RST_BUS_G2D] = { 0x63c, BIT(16) }, + [RST_BUS_CE] = { 0x68c, BIT(16) }, + [RST_BUS_VE] = { 0x69c, BIT(16) }, + [RST_BUS_DMA] = { 0x70c, BIT(16) }, + [RST_BUS_MSGBOX0] = { 0x71c, BIT(16) }, + [RST_BUS_MSGBOX1] = { 0x71c, BIT(17) }, + [RST_BUS_MSGBOX2] = { 0x71c, BIT(18) }, + [RST_BUS_SPINLOCK] = { 0x72c, BIT(16) }, + [RST_BUS_HSTIMER] = { 0x73c, BIT(16) }, + [RST_BUS_DBG] = { 0x78c, BIT(16) }, + [RST_BUS_PWM] = { 0x7ac, BIT(16) }, + [RST_BUS_DRAM] = { 0x80c, BIT(16) }, + [RST_BUS_MMC0] = { 0x84c, BIT(16) }, + [RST_BUS_MMC1] = { 0x84c, BIT(17) }, + [RST_BUS_MMC2] = { 0x84c, BIT(18) }, + [RST_BUS_UART0] = { 0x90c, BIT(16) }, + [RST_BUS_UART1] = { 0x90c, BIT(17) }, + [RST_BUS_UART2] = { 0x90c, BIT(18) }, + [RST_BUS_UART3] = { 0x90c, BIT(19) }, + [RST_BUS_UART4] = { 0x90c, BIT(20) }, + [RST_BUS_UART5] = { 0x90c, BIT(21) }, + [RST_BUS_I2C0] = { 0x91c, BIT(16) }, + [RST_BUS_I2C1] = { 0x91c, BIT(17) }, + [RST_BUS_I2C2] = { 0x91c, BIT(18) }, + [RST_BUS_I2C3] = { 0x91c, BIT(19) }, + [RST_BUS_SPI0] = { 0x96c, BIT(16) }, + [RST_BUS_SPI1] = { 0x96c, BIT(17) }, + [RST_BUS_EMAC] = { 0x97c, BIT(16) }, + [RST_BUS_IR_TX] = { 0x9cc, BIT(16) }, + [RST_BUS_GPADC] = { 0x9ec, BIT(16) }, + [RST_BUS_THS] = { 0x9fc, BIT(16) }, + [RST_BUS_I2S0] = { 0xa20, BIT(16) }, + [RST_BUS_I2S1] = { 0xa20, BIT(17) }, + [RST_BUS_I2S2] = { 0xa20, BIT(18) }, + [RST_BUS_SPDIF] = { 0xa2c, BIT(16) }, + [RST_BUS_DMIC] = { 0xa4c, BIT(16) }, + [RST_BUS_AUDIO] = { 0xa5c, BIT(16) }, + [RST_USB_PHY0] = { 0xa70, BIT(30) }, + [RST_USB_PHY1] = { 0xa74, BIT(30) }, + [RST_BUS_OHCI0] = { 0xa8c, BIT(16) }, + [RST_BUS_OHCI1] = { 0xa8c, BIT(17) }, + [RST_BUS_EHCI0] = { 0xa8c, BIT(20) }, + [RST_BUS_EHCI1] = { 0xa8c, BIT(21) }, + [RST_BUS_OTG] = { 0xa8c, BIT(24) }, + [RST_BUS_LRADC] = { 0xa9c, BIT(16) }, + [RST_BUS_DPSS_TOP] = { 0xabc, BIT(16) }, + [RST_BUS_HDMI_MAIN] = { 0xb1c, BIT(16) }, + [RST_BUS_HDMI_SUB] = { 0xb1c, BIT(17) }, + [RST_BUS_MIPI_DSI] = { 0xb4c, BIT(16) }, + [RST_BUS_TCON_LCD0] = { 0xb7c, BIT(16) }, + [RST_BUS_TCON_TV] = { 0xb9c, BIT(16) }, + [RST_BUS_LVDS0] = { 0xbac, BIT(16) }, + [RST_BUS_TVE_TOP] = { 0xbbc, BIT(16) }, + [RST_BUS_TVE] = { 0xbbc, BIT(17) }, + [RST_BUS_TVD_TOP] = { 0xbdc, BIT(16) }, + [RST_BUS_TVD] = { 0xbdc, BIT(17) }, + [RST_BUS_LEDC] = { 0xbfc, BIT(16) }, + [RST_BUS_CSI] = { 0xc1c, BIT(16) }, + [RST_BUS_TPADC] = { 0xc5c, BIT(16) }, + [RST_DSP] = { 0xc7c, BIT(16) }, + [RST_BUS_DSP_CFG] = { 0xc7c, BIT(17) }, + [RST_BUS_DSP_DBG] = { 0xc7c, BIT(18) }, + [RST_BUS_RISCV_CFG] = { 0xd0c, BIT(16) }, +}; + +static const struct sunxi_ccu_desc sun20i_d1_ccu_desc = { + .ccu_clks = sun20i_d1_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun20i_d1_ccu_clks), + + .hw_clks = &sun20i_d1_hw_clks, + + .resets = sun20i_d1_ccu_resets, + .num_resets = ARRAY_SIZE(sun20i_d1_ccu_resets), +}; + +static const u32 pll_regs[] = { + SUN20I_D1_PLL_CPUX_REG, + SUN20I_D1_PLL_DDR0_REG, + SUN20I_D1_PLL_PERIPH0_REG, + SUN20I_D1_PLL_VIDEO0_REG, + SUN20I_D1_PLL_VIDEO1_REG, + SUN20I_D1_PLL_VE_REG, + SUN20I_D1_PLL_AUDIO0_REG, + SUN20I_D1_PLL_AUDIO1_REG, +}; + +static const u32 pll_video_regs[] = { + SUN20I_D1_PLL_VIDEO0_REG, + SUN20I_D1_PLL_VIDEO1_REG, +}; + +static struct ccu_mux_nb sun20i_d1_riscv_nb = { + .common = &riscv_clk.common, + .cm = &riscv_clk.mux, + .delay_us = 1, + .bypass_index = 4, /* index of pll-periph0 */ +}; + +static int sun20i_d1_ccu_probe(struct platform_device *pdev) +{ + void __iomem *reg; + u32 val; + int i, ret; + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + /* Enable the enable, LDO, and lock bits on all PLLs. */ + for (i = 0; i < ARRAY_SIZE(pll_regs); i++) { + val = readl(reg + pll_regs[i]); + val |= BIT(31) | BIT(30) | BIT(29); + writel(val, reg + pll_regs[i]); + } + + /* Force PLL_CPUX factor M to 0. */ + val = readl(reg + SUN20I_D1_PLL_CPUX_REG); + val &= ~GENMASK(1, 0); + writel(val, reg + SUN20I_D1_PLL_CPUX_REG); + + /* + * Force the output divider of video PLLs to 0. + * + * See the comment before pll-video0 definition for the reason. + */ + for (i = 0; i < ARRAY_SIZE(pll_video_regs); i++) { + val = readl(reg + pll_video_regs[i]); + val &= ~BIT(0); + writel(val, reg + pll_video_regs[i]); + } + + /* Enforce m1 = 0, m0 = 0 for PLL_AUDIO0 */ + val = readl(reg + SUN20I_D1_PLL_AUDIO0_REG); + val &= ~BIT(1) | BIT(0); + writel(val, reg + SUN20I_D1_PLL_AUDIO0_REG); + + /* Force fanout-27M factor N to 0. */ + val = readl(reg + SUN20I_D1_FANOUT_27M_REG); + val &= ~GENMASK(9, 8); + writel(val, reg + SUN20I_D1_FANOUT_27M_REG); + + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun20i_d1_ccu_desc); + if (ret) + return ret; + + /* Reparent CPU during PLL CPUX rate changes */ + ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, + &sun20i_d1_riscv_nb); + + return 0; +} + +static const struct of_device_id sun20i_d1_ccu_ids[] = { + { .compatible = "allwinner,sun20i-d1-ccu" }, + { } +}; + +static struct platform_driver sun20i_d1_ccu_driver = { + .probe = sun20i_d1_ccu_probe, + .driver = { + .name = "sun20i-d1-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun20i_d1_ccu_ids, + }, +}; +module_platform_driver(sun20i_d1_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun20i-d1.h:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 frank@allwinnertech.com + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#ifndef _CCU_SUN20I_D1_H_ +#define _CCU_SUN20I_D1_H_ + +#include <dt-bindings/clock/sun20i-d1-ccu.h> +#include <dt-bindings/reset/sun20i-d1-ccu.h> + +#define CLK_NUMBER (CLK_FANOUT2 + 1) + +#endif /* _CCU_SUN20I_D1_H_ */ @ drivers/clk/sunxi-ng/ccu-sun4i-a10.c:10 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> #include "ccu_common.h" #include "ccu_reset.h" @ drivers/clk/sunxi-ng/ccu-sun4i-a10.c:1430 @ static const struct sunxi_ccu_desc sun7i_a20_ccu_desc = { .num_resets = ARRAY_SIZE(sunxi_a10_a20_ccu_resets), }; -static void __init sun4i_ccu_init(struct device_node *node, - const struct sunxi_ccu_desc *desc) +static int sun4i_a10_ccu_probe(struct platform_device *pdev) { + const struct sunxi_ccu_desc *desc; void __iomem *reg; u32 val; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); - return; - } + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); val = readl(reg + SUN4I_PLL_AUDIO_REG); @ drivers/clk/sunxi-ng/ccu-sun4i-a10.c:1470 @ static void __init sun4i_ccu_init(struct device_node *node, val &= ~GENMASK(7, 6); writel(val | (2 << 6), reg + SUN4I_AHB_REG); - of_sunxi_ccu_probe(node, reg, desc); + return devm_sunxi_ccu_probe(&pdev->dev, reg, desc); } -static void __init sun4i_a10_ccu_setup(struct device_node *node) -{ - sun4i_ccu_init(node, &sun4i_a10_ccu_desc); -} -CLK_OF_DECLARE(sun4i_a10_ccu, "allwinner,sun4i-a10-ccu", - sun4i_a10_ccu_setup); +static const struct of_device_id sun4i_a10_ccu_ids[] = { + { + .compatible = "allwinner,sun4i-a10-ccu", + .data = &sun4i_a10_ccu_desc, + }, + { + .compatible = "allwinner,sun7i-a20-ccu", + .data = &sun7i_a20_ccu_desc, + }, + { } +}; -static void __init sun7i_a20_ccu_setup(struct device_node *node) -{ - sun4i_ccu_init(node, &sun7i_a20_ccu_desc); -} -CLK_OF_DECLARE(sun7i_a20_ccu, "allwinner,sun7i-a20-ccu", - sun7i_a20_ccu_setup); +static struct platform_driver sun4i_a10_ccu_driver = { + .probe = sun4i_a10_ccu_probe, + .driver = { + .name = "sun4i-a10-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun4i_a10_ccu_ids, + }, +}; +module_platform_driver(sun4i_a10_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c:8 @ #include <linux/clk-provider.h> #include <linux/module.h> -#include <linux/of_address.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun50i-a100-r.c:215 @ static struct platform_driver sun50i_a100_r_ccu_driver = { }, }; module_platform_driver(sun50i_a100_r_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun50i-a100.c:9 @ #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_address.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun50i-a100.c:1277 @ static struct platform_driver sun50i_a100_ccu_driver = { }, }; module_platform_driver(sun50i_a100_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun50i-a64.c:8 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun50i-a64.c:983 @ static struct platform_driver sun50i_a64_ccu_driver = { .of_match_table = sun50i_a64_ccu_ids, }, }; -builtin_platform_driver(sun50i_a64_ccu_driver); +module_platform_driver(sun50i_a64_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c:7 @ */ #include <linux/clk-provider.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c:225 @ static const struct sunxi_ccu_desc sun50i_h616_r_ccu_desc = { .num_resets = ARRAY_SIZE(sun50i_h616_r_ccu_resets), }; -static void __init sunxi_r_ccu_init(struct device_node *node, - const struct sunxi_ccu_desc *desc) +static int sun50i_h6_r_ccu_probe(struct platform_device *pdev) { + const struct sunxi_ccu_desc *desc; void __iomem *reg; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map the clock registers\n", node); - return; - } + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; - of_sunxi_ccu_probe(node, reg, desc); -} + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); -static void __init sun50i_h6_r_ccu_setup(struct device_node *node) -{ - sunxi_r_ccu_init(node, &sun50i_h6_r_ccu_desc); + return devm_sunxi_ccu_probe(&pdev->dev, reg, desc); } -CLK_OF_DECLARE(sun50i_h6_r_ccu, "allwinner,sun50i-h6-r-ccu", - sun50i_h6_r_ccu_setup); -static void __init sun50i_h616_r_ccu_setup(struct device_node *node) -{ - sunxi_r_ccu_init(node, &sun50i_h616_r_ccu_desc); -} -CLK_OF_DECLARE(sun50i_h616_r_ccu, "allwinner,sun50i-h616-r-ccu", - sun50i_h616_r_ccu_setup); +static const struct of_device_id sun50i_h6_r_ccu_ids[] = { + { + .compatible = "allwinner,sun50i-h6-r-ccu", + .data = &sun50i_h6_r_ccu_desc, + }, + { + .compatible = "allwinner,sun50i-h616-r-ccu", + .data = &sun50i_h616_r_ccu_desc, + }, + { } +}; + +static struct platform_driver sun50i_h6_r_ccu_driver = { + .probe = sun50i_h6_r_ccu_probe, + .driver = { + .name = "sun50i-h6-r-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun50i_h6_r_ccu_ids, + }, +}; +module_platform_driver(sun50i_h6_r_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun50i-h6.c:8 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun50i-h6.c:1257 @ static struct platform_driver sun50i_h6_ccu_driver = { .of_match_table = sun50i_h6_ccu_ids, }, }; -builtin_platform_driver(sun50i_h6_ccu_driver); +module_platform_driver(sun50i_h6_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun50i-h616.c:10 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun50i-h616.c:1085 @ static const u32 usb2_clk_regs[] = { SUN50I_H616_USB3_CLK_REG, }; -static void __init sun50i_h616_ccu_setup(struct device_node *node) +static int sun50i_h616_ccu_probe(struct platform_device *pdev) { void __iomem *reg; u32 val; int i; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map clock registers\n", node); - return; - } + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); /* Enable the lock bits and the output enable bits on all PLLs */ for (i = 0; i < ARRAY_SIZE(pll_regs); i++) { @ drivers/clk/sunxi-ng/ccu-sun50i-h616.c:1142 @ static void __init sun50i_h616_ccu_setup(struct device_node *node) val |= BIT(24); writel(val, reg + SUN50I_H616_HDMI_CEC_CLK_REG); - of_sunxi_ccu_probe(node, reg, &sun50i_h616_ccu_desc); + return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_h616_ccu_desc); } -CLK_OF_DECLARE(sun50i_h616_ccu, "allwinner,sun50i-h616-ccu", - sun50i_h616_ccu_setup); +static const struct of_device_id sun50i_h616_ccu_ids[] = { + { .compatible = "allwinner,sun50i-h616-ccu" }, + { } +}; + +static struct platform_driver sun50i_h616_ccu_driver = { + .probe = sun50i_h616_ccu_probe, + .driver = { + .name = "sun50i-h616-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun50i_h616_ccu_ids, + }, +}; +module_platform_driver(sun50i_h616_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun6i-a31.c:12 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include "ccu_common.h" #include "ccu_reset.h" @ drivers/clk/sunxi-ng/ccu-sun6i-a31.c:1230 @ static struct ccu_mux_nb sun6i_a31_cpu_nb = { .bypass_index = 1, /* index of 24 MHz oscillator */ }; -static void __init sun6i_a31_ccu_setup(struct device_node *node) +static int sun6i_a31_ccu_probe(struct platform_device *pdev) { void __iomem *reg; + int ret; u32 val; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map the clock registers\n", node); - return; - } + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN6I_A31_PLL_AUDIO_REG); @ drivers/clk/sunxi-ng/ccu-sun6i-a31.c:1260 @ static void __init sun6i_a31_ccu_setup(struct device_node *node) val |= 0x3 << 12; writel(val, reg + SUN6I_A31_AHB1_REG); - of_sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc); + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun6i_a31_ccu_desc); + if (ret) + return ret; ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk, &sun6i_a31_cpu_nb); + + return 0; } -CLK_OF_DECLARE(sun6i_a31_ccu, "allwinner,sun6i-a31-ccu", - sun6i_a31_ccu_setup); + +static const struct of_device_id sun6i_a31_ccu_ids[] = { + { .compatible = "allwinner,sun6i-a31-ccu" }, + { } +}; + +static struct platform_driver sun6i_a31_ccu_driver = { + .probe = sun6i_a31_ccu_probe, + .driver = { + .name = "sun6i-a31-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun6i_a31_ccu_ids, + }, +}; +module_platform_driver(sun6i_a31_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun6i-rtc.c:4 @ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (c) 2021 Samuel Holland <samuel@sholland.org> +// + +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> + +#include "ccu_common.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mux.h" + +#include "ccu-sun6i-rtc.h" + +#define IOSC_ACCURACY 300000000 /* 30% */ +#define IOSC_RATE 16000000 + +#define LOSC_RATE 32768 +#define LOSC_RATE_SHIFT 15 + +#define LOSC_CTRL_REG 0x0 +#define LOSC_CTRL_KEY 0x16aa0000 + +#define IOSC_32K_CLK_DIV_REG 0x8 +#define IOSC_32K_CLK_DIV GENMASK(4, 0) +#define IOSC_32K_PRE_DIV 32 + +#define IOSC_CLK_CALI_REG 0xc +#define IOSC_CLK_CALI_DIV_ONES 22 +#define IOSC_CLK_CALI_EN BIT(1) +#define IOSC_CLK_CALI_SRC_SEL BIT(0) + +#define LOSC_OUT_GATING_REG 0x60 + +#define DCXO_CTRL_REG 0x160 +#define DCXO_CTRL_CLK16M_RC_EN BIT(0) + +struct sun6i_rtc_match_data { + bool have_ext_osc32k : 1; + bool have_iosc_calibration : 1; + bool rtc_32k_single_parent : 1; + const struct clk_parent_data *osc32k_fanout_parents; + u8 osc32k_fanout_nparents; +}; + +static bool have_iosc_calibration; + +static int ccu_iosc_enable(struct clk_hw *hw) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + + return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN); +} + +static void ccu_iosc_disable(struct clk_hw *hw) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + + return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN); +} + +static int ccu_iosc_is_enabled(struct clk_hw *hw) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + + return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN); +} + +static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + + if (have_iosc_calibration) { + u32 reg = readl(cm->base + IOSC_CLK_CALI_REG); + + /* + * Recover the IOSC frequency by shifting the ones place of + * (fixed-point divider * 32768) into bit zero. + */ + if (reg & IOSC_CLK_CALI_EN) + return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT); + } + + return IOSC_RATE; +} + +static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw, + unsigned long parent_accuracy) +{ + return IOSC_ACCURACY; +} + +static const struct clk_ops ccu_iosc_ops = { + .enable = ccu_iosc_enable, + .disable = ccu_iosc_disable, + .is_enabled = ccu_iosc_is_enabled, + .recalc_rate = ccu_iosc_recalc_rate, + .recalc_accuracy = ccu_iosc_recalc_accuracy, +}; + +static struct ccu_common iosc_clk = { + .reg = DCXO_CTRL_REG, + .hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops, + CLK_GET_RATE_NOCACHE), +}; + +static int ccu_iosc_32k_prepare(struct clk_hw *hw) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + u32 val; + + if (!have_iosc_calibration) + return 0; + + val = readl(cm->base + IOSC_CLK_CALI_REG); + writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL, + cm->base + IOSC_CLK_CALI_REG); + + return 0; +} + +static void ccu_iosc_32k_unprepare(struct clk_hw *hw) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + u32 val; + + if (!have_iosc_calibration) + return; + + val = readl(cm->base + IOSC_CLK_CALI_REG); + writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL), + cm->base + IOSC_CLK_CALI_REG); +} + +static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + u32 val; + + if (have_iosc_calibration) { + val = readl(cm->base + IOSC_CLK_CALI_REG); + + /* Assume the calibrated 32k clock is accurate. */ + if (val & IOSC_CLK_CALI_SRC_SEL) + return LOSC_RATE; + } + + val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV; + + return parent_rate / IOSC_32K_PRE_DIV / (val + 1); +} + +static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw, + unsigned long parent_accuracy) +{ + struct ccu_common *cm = hw_to_ccu_common(hw); + u32 val; + + if (have_iosc_calibration) { + val = readl(cm->base + IOSC_CLK_CALI_REG); + + /* Assume the calibrated 32k clock is accurate. */ + if (val & IOSC_CLK_CALI_SRC_SEL) + return 0; + } + + return parent_accuracy; +} + +static const struct clk_ops ccu_iosc_32k_ops = { + .prepare = ccu_iosc_32k_prepare, + .unprepare = ccu_iosc_32k_unprepare, + .recalc_rate = ccu_iosc_32k_recalc_rate, + .recalc_accuracy = ccu_iosc_32k_recalc_accuracy, +}; + +static struct ccu_common iosc_32k_clk = { + .hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw, + &ccu_iosc_32k_ops, + CLK_GET_RATE_NOCACHE), +}; + +/* .fw_name will be cleared if the clock-names property is missing. */ +static struct clk_parent_data ext_osc32k[] = { + { .fw_name = "ext-osc32k", .index = 0 } +}; + +static SUNXI_CCU_GATE_DATA(ext_osc32k_gate_clk, "ext-osc32k-gate", + ext_osc32k, 0x0, BIT(4), 0); + +static const struct clk_hw *osc32k_parents[] = { + &iosc_32k_clk.hw, + &ext_osc32k_gate_clk.common.hw +}; + +static struct clk_init_data osc32k_init_data = { + .name = "osc32k", + .ops = &ccu_mux_ops, + .parent_hws = osc32k_parents, + .num_parents = ARRAY_SIZE(osc32k_parents), /* updated during probe */ +}; + +static struct ccu_mux osc32k_clk = { + .mux = _SUNXI_CCU_MUX(0, 1), + .common = { + .reg = LOSC_CTRL_REG, + .features = CCU_FEATURE_KEY_FIELD, + .hw.init = &osc32k_init_data, + }, +}; + +/* This falls back to the global name for fwnodes without a named reference. */ +static const struct clk_parent_data osc24M[] = { + { .fw_name = "hosc", .name = "osc24M" } +}; + +static struct ccu_gate osc24M_32k_clk = { + .enable = BIT(16), + .common = { + .reg = LOSC_OUT_GATING_REG, + .prediv = 750, + .features = CCU_FEATURE_ALL_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS_DATA("osc24M-32k", osc24M, + &ccu_gate_ops, 0), + }, +}; + +static const struct clk_hw *rtc_32k_parents[] = { + &osc32k_clk.common.hw, + &osc24M_32k_clk.common.hw +}; + +static struct clk_init_data rtc_32k_init_data = { + .name = "rtc-32k", + .ops = &ccu_mux_ops, + .parent_hws = rtc_32k_parents, + .num_parents = ARRAY_SIZE(rtc_32k_parents), /* updated during probe */ +}; + +static struct ccu_mux rtc_32k_clk = { + .mux = _SUNXI_CCU_MUX(1, 1), + .common = { + .reg = LOSC_CTRL_REG, + .features = CCU_FEATURE_KEY_FIELD, + .hw.init = &rtc_32k_init_data, + }, +}; + +static struct clk_init_data osc32k_fanout_init_data = { + .name = "osc32k-fanout", + .ops = &ccu_mux_ops, + /* parents are set during probe */ +}; + +static struct ccu_mux osc32k_fanout_clk = { + .enable = BIT(0), + .mux = _SUNXI_CCU_MUX(1, 2), + .common = { + .reg = LOSC_OUT_GATING_REG, + .hw.init = &osc32k_fanout_init_data, + }, +}; + +static struct ccu_common *sun6i_rtc_ccu_clks[] = { + &iosc_clk, + &iosc_32k_clk, + &ext_osc32k_gate_clk.common, /* updated during probe */ + &osc32k_clk.common, + &osc24M_32k_clk.common, + &rtc_32k_clk.common, + &osc32k_fanout_clk.common, +}; + +static struct clk_hw_onecell_data sun6i_rtc_ccu_hw_clks = { + .num = CLK_NUMBER, + .hws = { + [CLK_OSC32K] = &osc32k_clk.common.hw, + [CLK_OSC32K_FANOUT] = &osc32k_fanout_clk.common.hw, + [CLK_IOSC] = &iosc_clk.hw, + }, +}; + +static const struct sunxi_ccu_desc sun6i_rtc_ccu_desc = { + .ccu_clks = sun6i_rtc_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun6i_rtc_ccu_clks), + + .hw_clks = &sun6i_rtc_ccu_hw_clks, +}; + +static const struct clk_parent_data sun50i_h6_osc32k_fanout_parents[] = { + { .hw = &osc32k_clk.common.hw }, +}; + +static const struct clk_parent_data sun50i_h616_osc32k_fanout_parents[] = { + { .hw = &osc32k_clk.common.hw }, + { .fw_name = "pll-32k" }, + { .hw = &osc24M_32k_clk.common.hw } +}; + +static const struct clk_parent_data sun50i_r329_osc32k_fanout_parents[] = { + { .hw = &osc32k_clk.common.hw }, + { .hw = &ext_osc32k_gate_clk.common.hw }, + { .hw = &osc24M_32k_clk.common.hw } +}; + +static const struct sun6i_rtc_match_data sun50i_h6_rtc_ccu_data = { + .have_ext_osc32k = true, + .have_iosc_calibration = true, + .osc32k_fanout_parents = sun50i_h6_osc32k_fanout_parents, + .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h6_osc32k_fanout_parents), +}; + +static const struct sun6i_rtc_match_data sun50i_h616_rtc_ccu_data = { + .have_iosc_calibration = true, + .rtc_32k_single_parent = true, + .osc32k_fanout_parents = sun50i_h616_osc32k_fanout_parents, + .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_h616_osc32k_fanout_parents), +}; + +static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = { + .have_ext_osc32k = true, + .osc32k_fanout_parents = sun50i_r329_osc32k_fanout_parents, + .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents), +}; + +static const struct of_device_id sun6i_rtc_ccu_match[] = { + { + .compatible = "allwinner,sun50i-h6-rtc", + .data = &sun50i_h6_rtc_ccu_data, + }, + { + .compatible = "allwinner,sun50i-h616-rtc", + .data = &sun50i_h616_rtc_ccu_data, + }, + { + .compatible = "allwinner,sun50i-r329-rtc", + .data = &sun50i_r329_rtc_ccu_data, + }, +}; + +int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg) +{ + struct device_node *node = dev->of_node; + const struct sun6i_rtc_match_data *data; + const struct of_device_id *match; + + match = of_match_device(sun6i_rtc_ccu_match, dev); + if (!match) + return 0; + + data = match->data; + have_iosc_calibration = data->have_iosc_calibration; + + if (data->have_ext_osc32k) { + /* ext-osc32k was the only input clock in the old binding. */ + if (!of_property_read_bool(node, "clock-names")) + ext_osc32k->fw_name = NULL; + } else { + /* Do not register the orphan ext-osc32k-gate clock. */ + sun6i_rtc_ccu_clks[2] = NULL; + + osc32k_init_data.num_parents = 1; + } + + if (data->rtc_32k_single_parent) + rtc_32k_init_data.num_parents = 1; + + osc32k_fanout_init_data.parent_data = data->osc32k_fanout_parents; + osc32k_fanout_init_data.num_parents = data->osc32k_fanout_nparents; + + return devm_sunxi_ccu_probe(dev, reg, &sun6i_rtc_ccu_desc); +} + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun6i-rtc.h:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _CCU_SUN6I_RTC_H +#define _CCU_SUN6I_RTC_H + +#include <dt-bindings/clock/sun6i-rtc.h> + +#define CLK_NUMBER (CLK_IOSC + 1) + +#endif /* _CCU_SUN6I_RTC_H */ @ drivers/clk/sunxi-ng/ccu-sun8i-a23.c:8 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include "ccu_common.h" #include "ccu_reset.h" @ drivers/clk/sunxi-ng/ccu-sun8i-a23.c:728 @ static const struct sunxi_ccu_desc sun8i_a23_ccu_desc = { .num_resets = ARRAY_SIZE(sun8i_a23_ccu_resets), }; -static void __init sun8i_a23_ccu_setup(struct device_node *node) +static int sun8i_a23_ccu_probe(struct platform_device *pdev) { void __iomem *reg; u32 val; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map the clock registers\n", node); - return; - } + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN8I_A23_PLL_AUDIO_REG); @ drivers/clk/sunxi-ng/ccu-sun8i-a23.c:747 @ static void __init sun8i_a23_ccu_setup(struct device_node *node) val &= ~BIT(16); writel(val, reg + SUN8I_A23_PLL_MIPI_REG); - of_sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc); + return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_a23_ccu_desc); } -CLK_OF_DECLARE(sun8i_a23_ccu, "allwinner,sun8i-a23-ccu", - sun8i_a23_ccu_setup); + +static const struct of_device_id sun8i_a23_ccu_ids[] = { + { .compatible = "allwinner,sun8i-a23-ccu" }, + { } +}; + +static struct platform_driver sun8i_a23_ccu_driver = { + .probe = sun8i_a23_ccu_probe, + .driver = { + .name = "sun8i-a23-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun8i_a23_ccu_ids, + }, +}; +module_platform_driver(sun8i_a23_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun8i-a33.c:8 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include "ccu_common.h" #include "ccu_reset.h" @ drivers/clk/sunxi-ng/ccu-sun8i-a33.c:788 @ static struct ccu_mux_nb sun8i_a33_cpu_nb = { .bypass_index = 1, /* index of 24 MHz oscillator */ }; -static void __init sun8i_a33_ccu_setup(struct device_node *node) +static int sun8i_a33_ccu_probe(struct platform_device *pdev) { void __iomem *reg; + int ret; u32 val; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map the clock registers\n", node); - return; - } + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN8I_A33_PLL_AUDIO_REG); @ drivers/clk/sunxi-ng/ccu-sun8i-a33.c:808 @ static void __init sun8i_a33_ccu_setup(struct device_node *node) val &= ~BIT(16); writel(val, reg + SUN8I_A33_PLL_MIPI_REG); - of_sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc); + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_a33_ccu_desc); + if (ret) + return ret; /* Gate then ungate PLL CPU after any rate changes */ ccu_pll_notifier_register(&sun8i_a33_pll_cpu_nb); @ drivers/clk/sunxi-ng/ccu-sun8i-a33.c:818 @ static void __init sun8i_a33_ccu_setup(struct device_node *node) /* Reparent CPU during PLL CPU rate changes */ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, &sun8i_a33_cpu_nb); + + return 0; } -CLK_OF_DECLARE(sun8i_a33_ccu, "allwinner,sun8i-a33-ccu", - sun8i_a33_ccu_setup); + +static const struct of_device_id sun8i_a33_ccu_ids[] = { + { .compatible = "allwinner,sun8i-a33-ccu" }, + { } +}; + +static struct platform_driver sun8i_a33_ccu_driver = { + .probe = sun8i_a33_ccu_probe, + .driver = { + .name = "sun8i-a33-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun8i_a33_ccu_ids, + }, +}; +module_platform_driver(sun8i_a33_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun8i-a83t.c:8 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun8i-a83t.c:923 @ static struct platform_driver sun8i_a83t_ccu_driver = { .of_match_table = sun8i_a83t_ccu_ids, }, }; -builtin_platform_driver(sun8i_a83t_ccu_driver); +module_platform_driver(sun8i_a83t_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun8i-de2.c:8 @ #include <linux/clk.h> #include <linux/clk-provider.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> +#include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/reset.h> @ drivers/clk/sunxi-ng/ccu-sun8i-de2.c:397 @ static struct platform_driver sunxi_de2_clk_driver = { .of_match_table = sunxi_de2_clk_ids, }, }; -builtin_platform_driver(sunxi_de2_clk_driver); +module_platform_driver(sunxi_de2_clk_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun8i-h3.c:8 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> #include "ccu_common.h" #include "ccu_reset.h" @ drivers/clk/sunxi-ng/ccu-sun8i-h3.c:1142 @ static struct ccu_mux_nb sun8i_h3_cpu_nb = { .bypass_index = 1, /* index of 24 MHz oscillator */ }; -static void __init sunxi_h3_h5_ccu_init(struct device_node *node, - const struct sunxi_ccu_desc *desc) +static int sun8i_h3_ccu_probe(struct platform_device *pdev) { + const struct sunxi_ccu_desc *desc; void __iomem *reg; + int ret; u32 val; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map the clock registers\n", node); - return; - } + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN8I_H3_PLL_AUDIO_REG); val &= ~GENMASK(19, 16); writel(val | (0 << 16), reg + SUN8I_H3_PLL_AUDIO_REG); - of_sunxi_ccu_probe(node, reg, desc); + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, desc); + if (ret) + return ret; /* Gate then ungate PLL CPU after any rate changes */ ccu_pll_notifier_register(&sun8i_h3_pll_cpu_nb); @ drivers/clk/sunxi-ng/ccu-sun8i-h3.c:1172 @ static void __init sunxi_h3_h5_ccu_init(struct device_node *node, /* Reparent CPU during PLL CPU rate changes */ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, &sun8i_h3_cpu_nb); -} -static void __init sun8i_h3_ccu_setup(struct device_node *node) -{ - sunxi_h3_h5_ccu_init(node, &sun8i_h3_ccu_desc); + return 0; } -CLK_OF_DECLARE(sun8i_h3_ccu, "allwinner,sun8i-h3-ccu", - sun8i_h3_ccu_setup); -static void __init sun50i_h5_ccu_setup(struct device_node *node) -{ - sunxi_h3_h5_ccu_init(node, &sun50i_h5_ccu_desc); -} -CLK_OF_DECLARE(sun50i_h5_ccu, "allwinner,sun50i-h5-ccu", - sun50i_h5_ccu_setup); +static const struct of_device_id sun8i_h3_ccu_ids[] = { + { + .compatible = "allwinner,sun8i-h3-ccu", + .data = &sun8i_h3_ccu_desc, + }, + { + .compatible = "allwinner,sun50i-h5-ccu", + .data = &sun50i_h5_ccu_desc, + }, + { } +}; + +static struct platform_driver sun8i_h3_ccu_driver = { + .probe = sun8i_h3_ccu_probe, + .driver = { + .name = "sun8i-h3-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun8i_h3_ccu_ids, + }, +}; +module_platform_driver(sun8i_h3_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun8i-r.c:7 @ */ #include <linux/clk-provider.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun8i-r.c:258 @ static const struct sunxi_ccu_desc sun50i_a64_r_ccu_desc = { .num_resets = ARRAY_SIZE(sun50i_a64_r_ccu_resets), }; -static void __init sunxi_r_ccu_init(struct device_node *node, - const struct sunxi_ccu_desc *desc) +static int sun8i_r_ccu_probe(struct platform_device *pdev) { + const struct sunxi_ccu_desc *desc; void __iomem *reg; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map the clock registers\n", node); - return; - } + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; - of_sunxi_ccu_probe(node, reg, desc); -} + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); -static void __init sun8i_a83t_r_ccu_setup(struct device_node *node) -{ - sunxi_r_ccu_init(node, &sun8i_a83t_r_ccu_desc); + return devm_sunxi_ccu_probe(&pdev->dev, reg, desc); } -CLK_OF_DECLARE(sun8i_a83t_r_ccu, "allwinner,sun8i-a83t-r-ccu", - sun8i_a83t_r_ccu_setup); -static void __init sun8i_h3_r_ccu_setup(struct device_node *node) -{ - sunxi_r_ccu_init(node, &sun8i_h3_r_ccu_desc); -} -CLK_OF_DECLARE(sun8i_h3_r_ccu, "allwinner,sun8i-h3-r-ccu", - sun8i_h3_r_ccu_setup); +static const struct of_device_id sun8i_r_ccu_ids[] = { + { + .compatible = "allwinner,sun8i-a83t-r-ccu", + .data = &sun8i_a83t_r_ccu_desc, + }, + { + .compatible = "allwinner,sun8i-h3-r-ccu", + .data = &sun8i_h3_r_ccu_desc, + }, + { + .compatible = "allwinner,sun50i-a64-r-ccu", + .data = &sun50i_a64_r_ccu_desc, + }, + { } +}; -static void __init sun50i_a64_r_ccu_setup(struct device_node *node) -{ - sunxi_r_ccu_init(node, &sun50i_a64_r_ccu_desc); -} -CLK_OF_DECLARE(sun50i_a64_r_ccu, "allwinner,sun50i-a64-r-ccu", - sun50i_a64_r_ccu_setup); +static struct platform_driver sun8i_r_ccu_driver = { + .probe = sun8i_r_ccu_probe, + .driver = { + .name = "sun8i-r-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun8i_r_ccu_ids, + }, +}; +module_platform_driver(sun8i_r_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun8i-r40.c:8 @ #include <linux/clk-provider.h> #include <linux/io.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> @ drivers/clk/sunxi-ng/ccu-sun8i-r40.c:1375 @ static struct platform_driver sun8i_r40_ccu_driver = { .of_match_table = sun8i_r40_ccu_ids, }, }; -builtin_platform_driver(sun8i_r40_ccu_driver); +module_platform_driver(sun8i_r40_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun8i-v3s.c:11 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> #include "ccu_common.h" #include "ccu_reset.h" @ drivers/clk/sunxi-ng/ccu-sun8i-v3s.c:810 @ static const struct sunxi_ccu_desc sun8i_v3_ccu_desc = { .num_resets = ARRAY_SIZE(sun8i_v3_ccu_resets), }; -static void __init sun8i_v3_v3s_ccu_init(struct device_node *node, - const struct sunxi_ccu_desc *ccu_desc) +static int sun8i_v3s_ccu_probe(struct platform_device *pdev) { + const struct sunxi_ccu_desc *desc; void __iomem *reg; u32 val; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map the clock registers\n", node); - return; - } + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); /* Force the PLL-Audio-1x divider to 1 */ val = readl(reg + SUN8I_V3S_PLL_AUDIO_REG); val &= ~GENMASK(19, 16); writel(val, reg + SUN8I_V3S_PLL_AUDIO_REG); - of_sunxi_ccu_probe(node, reg, ccu_desc); -} - -static void __init sun8i_v3s_ccu_setup(struct device_node *node) -{ - sun8i_v3_v3s_ccu_init(node, &sun8i_v3s_ccu_desc); + return devm_sunxi_ccu_probe(&pdev->dev, reg, desc); } -static void __init sun8i_v3_ccu_setup(struct device_node *node) -{ - sun8i_v3_v3s_ccu_init(node, &sun8i_v3_ccu_desc); -} +static const struct of_device_id sun8i_v3s_ccu_ids[] = { + { + .compatible = "allwinner,sun8i-v3-ccu", + .data = &sun8i_v3_ccu_desc, + }, + { + .compatible = "allwinner,sun8i-v3s-ccu", + .data = &sun8i_v3s_ccu_desc, + }, + { } +}; -CLK_OF_DECLARE(sun8i_v3s_ccu, "allwinner,sun8i-v3s-ccu", - sun8i_v3s_ccu_setup); +static struct platform_driver sun8i_v3s_ccu_driver = { + .probe = sun8i_v3s_ccu_probe, + .driver = { + .name = "sun8i-v3s-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun8i_v3s_ccu_ids, + }, +}; +module_platform_driver(sun8i_v3s_ccu_driver); -CLK_OF_DECLARE(sun8i_v3_ccu, "allwinner,sun8i-v3-ccu", - sun8i_v3_ccu_setup); +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c:8 @ #include <linux/clk.h> #include <linux/clk-provider.h> -#include <linux/of_address.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/reset.h> @ drivers/clk/sunxi-ng/ccu-sun9i-a80-de.c:273 @ static struct platform_driver sun9i_a80_de_clk_driver = { .of_match_table = sun9i_a80_de_clk_ids, }, }; -builtin_platform_driver(sun9i_a80_de_clk_driver); +module_platform_driver(sun9i_a80_de_clk_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c:8 @ #include <linux/clk.h> #include <linux/clk-provider.h> -#include <linux/of_address.h> +#include <linux/module.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun9i-a80-usb.c:141 @ static struct platform_driver sun9i_a80_usb_clk_driver = { .of_match_table = sun9i_a80_usb_clk_ids, }, }; -builtin_platform_driver(sun9i_a80_usb_clk_driver); +module_platform_driver(sun9i_a80_usb_clk_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-sun9i-a80.c:8 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> #include <linux/platform_device.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu-sun9i-a80.c:1248 @ static struct platform_driver sun9i_a80_ccu_driver = { .of_match_table = sun9i_a80_ccu_ids, }, }; -builtin_platform_driver(sun9i_a80_ccu_driver); +module_platform_driver(sun9i_a80_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c:9 @ #include <linux/clk-provider.h> #include <linux/io.h> -#include <linux/of_address.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include "ccu_common.h" #include "ccu_reset.h" @ drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c:529 @ static struct ccu_mux_nb suniv_cpu_nb = { static void __init suniv_f1c100s_ccu_setup(struct device_node *node) { void __iomem *reg; + int ret; u32 val; - reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (IS_ERR(reg)) { - pr_err("%pOF: Could not map the clock registers\n", node); - return; - } + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); /* Force the PLL-Audio-1x divider to 4 */ val = readl(reg + SUNIV_PLL_AUDIO_REG); val &= ~GENMASK(19, 16); writel(val | (3 << 16), reg + SUNIV_PLL_AUDIO_REG); - of_sunxi_ccu_probe(node, reg, &suniv_ccu_desc); + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &suniv_ccu_desc); + if (ret) + return ret; /* Gate then ungate PLL CPU after any rate changes */ ccu_pll_notifier_register(&suniv_pll_cpu_nb); @ drivers/clk/sunxi-ng/ccu-suniv-f1c100s.c:551 @ static void __init suniv_f1c100s_ccu_setup(struct device_node *node) /* Reparent CPU during PLL CPU rate changes */ ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk, &suniv_cpu_nb); + + return 0; } -CLK_OF_DECLARE(suniv_f1c100s_ccu, "allwinner,suniv-f1c100s-ccu", - suniv_f1c100s_ccu_setup); + +static const struct of_device_id suniv_f1c100s_ccu_ids[] = { + { .compatible = "allwinner,suniv-f1c100s-ccu" }, + { } +}; + +static struct platform_driver suniv_f1c100s_ccu_driver = { + .probe = suniv_f1c100s_ccu_probe, + .driver = { + .name = "suniv-f1c100s-ccu", + .suppress_bind_attrs = true, + .of_match_table = suniv_f1c100s_ccu_ids, + }, +}; +module_platform_driver(suniv_f1c100s_ccu_driver); + +MODULE_IMPORT_NS(SUNXI_CCU); +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu_common.c:12 @ #include <linux/clk-provider.h> #include <linux/device.h> #include <linux/iopoll.h> +#include <linux/module.h> #include <linux/slab.h> #include "ccu_common.h" @ drivers/clk/sunxi-ng/ccu_common.c:40 @ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); } +EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, SUNXI_CCU); /* * This clock notifier is called when the frequency of a PLL clock is @ drivers/clk/sunxi-ng/ccu_common.c:88 @ int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb) return clk_notifier_register(pll_nb->common->hw.clk, &pll_nb->clk_nb); } +EXPORT_SYMBOL_NS_GPL(ccu_pll_notifier_register, SUNXI_CCU); static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev, struct device_node *node, void __iomem *reg, @ drivers/clk/sunxi-ng/ccu_common.c:200 @ int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg, return 0; } +EXPORT_SYMBOL_NS_GPL(devm_sunxi_ccu_probe, SUNXI_CCU); void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg, const struct sunxi_ccu_desc *desc) @ drivers/clk/sunxi-ng/ccu_common.c:218 @ void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg, kfree(ccu); } } + +MODULE_LICENSE("GPL"); @ drivers/clk/sunxi-ng/ccu_common.h:20 @ #define CCU_FEATURE_LOCK_REG BIT(5) #define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6) #define CCU_FEATURE_SIGMA_DELTA_MOD BIT(7) +#define CCU_FEATURE_KEY_FIELD BIT(8) /* MMC timing mode switch bit */ #define CCU_MMC_NEW_TIMING_MODE BIT(30) @ drivers/clk/sunxi-ng/ccu_div.c:144 @ const struct clk_ops ccu_div_ops = { .recalc_rate = ccu_div_recalc_rate, .set_rate = ccu_div_set_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_div_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_div.h:111 @ struct ccu_div { _shift, _width, _table, 0, \ _flags) +#define SUNXI_CCU_DIV_TABLE_HW(_struct, _name, _parent, _reg, \ + _shift, _width, \ + _table, _flags) \ + struct ccu_div _struct = { \ + .div = _SUNXI_CCU_DIV_TABLE(_shift, _width, \ + _table), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_HW(_name, \ + _parent, \ + &ccu_div_ops, \ + _flags), \ + } \ + } + + #define SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name, \ _parents, _table, \ _reg, \ @ drivers/clk/sunxi-ng/ccu_div.h:185 @ struct ccu_div { SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg, \ _mshift, _mwidth, 0, _flags) +#define SUNXI_CCU_M_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) \ + struct ccu_div _struct = { \ + .enable = _gate, \ + .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \ + .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \ + _parents, \ + &ccu_div_ops, \ + _flags), \ + }, \ + } + +#define SUNXI_CCU_M_DATA_WITH_MUX(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _flags) \ + SUNXI_CCU_M_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + 0, _flags) + +#define SUNXI_CCU_M_HW_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, _muxshift, _muxwidth, \ + _gate, _flags) \ + struct ccu_div _struct = { \ + .enable = _gate, \ + .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \ + .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS_HW(_name, \ + _parents, \ + &ccu_div_ops, \ + _flags), \ + }, \ + } + +#define SUNXI_CCU_M_HWS_WITH_GATE(_struct, _name, _parent, _reg, \ + _mshift, _mwidth, _gate, \ + _flags) \ + struct ccu_div _struct = { \ + .enable = _gate, \ + .div = _SUNXI_CCU_DIV(_mshift, _mwidth), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_HWS(_name, \ + _parent, \ + &ccu_div_ops, \ + _flags), \ + }, \ + } + +#define SUNXI_CCU_M_HWS(_struct, _name, _parent, _reg, _mshift, \ + _mwidth, _flags) \ + SUNXI_CCU_M_HWS_WITH_GATE(_struct, _name, _parent, _reg, \ + _mshift, _mwidth, 0, _flags) + static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw) { struct ccu_common *common = hw_to_ccu_common(hw); @ drivers/clk/sunxi-ng/ccu_frac.c:21 @ bool ccu_frac_helper_is_enabled(struct ccu_common *common, return !(readl(common->base + common->reg) & cf->enable); } +EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_is_enabled, SUNXI_CCU); void ccu_frac_helper_enable(struct ccu_common *common, struct ccu_frac_internal *cf) @ drivers/clk/sunxi-ng/ccu_frac.c:37 @ void ccu_frac_helper_enable(struct ccu_common *common, writel(reg & ~cf->enable, common->base + common->reg); spin_unlock_irqrestore(common->lock, flags); } +EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_enable, SUNXI_CCU); void ccu_frac_helper_disable(struct ccu_common *common, struct ccu_frac_internal *cf) @ drivers/clk/sunxi-ng/ccu_frac.c:53 @ void ccu_frac_helper_disable(struct ccu_common *common, writel(reg | cf->enable, common->base + common->reg); spin_unlock_irqrestore(common->lock, flags); } +EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_disable, SUNXI_CCU); bool ccu_frac_helper_has_rate(struct ccu_common *common, struct ccu_frac_internal *cf, @ drivers/clk/sunxi-ng/ccu_frac.c:64 @ bool ccu_frac_helper_has_rate(struct ccu_common *common, return (cf->rates[0] == rate) || (cf->rates[1] == rate); } +EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_has_rate, SUNXI_CCU); unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, struct ccu_frac_internal *cf) @ drivers/clk/sunxi-ng/ccu_frac.c:86 @ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, return (reg & cf->select) ? cf->rates[1] : cf->rates[0]; } +EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_read_rate, SUNXI_CCU); int ccu_frac_helper_set_rate(struct ccu_common *common, struct ccu_frac_internal *cf, @ drivers/clk/sunxi-ng/ccu_frac.c:115 @ int ccu_frac_helper_set_rate(struct ccu_common *common, return 0; } +EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_set_rate, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_gate.c:27 @ void ccu_gate_helper_disable(struct ccu_common *common, u32 gate) spin_unlock_irqrestore(common->lock, flags); } +EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_disable, SUNXI_CCU); static void ccu_gate_disable(struct clk_hw *hw) { @ drivers/clk/sunxi-ng/ccu_gate.c:53 @ int ccu_gate_helper_enable(struct ccu_common *common, u32 gate) return 0; } +EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_enable, SUNXI_CCU); static int ccu_gate_enable(struct clk_hw *hw) { @ drivers/clk/sunxi-ng/ccu_gate.c:69 @ int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate) return readl(common->base + common->reg) & gate; } +EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_is_enabled, SUNXI_CCU); static int ccu_gate_is_enabled(struct clk_hw *hw) { @ drivers/clk/sunxi-ng/ccu_gate.c:130 @ const struct clk_ops ccu_gate_ops = { .set_rate = ccu_gate_set_rate, .recalc_rate = ccu_gate_recalc_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_gate_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_gate.h:56 @ struct ccu_gate { } /* - * The following two macros allow the re-use of the data structure + * The following macros allow the re-use of the data structure * holding the parent info. */ #define SUNXI_CCU_GATE_HWS(_struct, _name, _parent, _reg, _gate, _flags) \ @ drivers/clk/sunxi-ng/ccu_gate.h:71 @ struct ccu_gate { } \ } +#define SUNXI_CCU_GATE_HWS_WITH_PREDIV(_struct, _name, _parent, _reg, \ + _gate, _prediv, _flags) \ + struct ccu_gate _struct = { \ + .enable = _gate, \ + .common = { \ + .reg = _reg, \ + .prediv = _prediv, \ + .features = CCU_FEATURE_ALL_PREDIV, \ + .hw.init = CLK_HW_INIT_HWS(_name, \ + _parent, \ + &ccu_gate_ops, \ + _flags), \ + } \ + } + #define SUNXI_CCU_GATE_DATA(_struct, _name, _data, _reg, _gate, _flags) \ struct ccu_gate _struct = { \ .enable = _gate, \ @ drivers/clk/sunxi-ng/ccu_gate.h:99 @ struct ccu_gate { } \ } +#define SUNXI_CCU_GATE_DATA_WITH_PREDIV(_struct, _name, _parent, _reg, \ + _gate, _prediv, _flags) \ + struct ccu_gate _struct = { \ + .enable = _gate, \ + .common = { \ + .reg = _reg, \ + .prediv = _prediv, \ + .features = CCU_FEATURE_ALL_PREDIV, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \ + _parent, \ + &ccu_gate_ops, \ + _flags), \ + } \ + } + static inline struct ccu_gate *hw_to_ccu_gate(struct clk_hw *hw) { struct ccu_common *common = hw_to_ccu_common(hw); @ drivers/clk/sunxi-ng/ccu_mp.c:248 @ const struct clk_ops ccu_mp_ops = { .recalc_rate = ccu_mp_recalc_rate, .set_rate = ccu_mp_set_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_mp_ops, SUNXI_CCU); /* * Support for MMC timing mode switching @ drivers/clk/sunxi-ng/ccu_mp.c:329 @ const struct clk_ops ccu_mp_mmc_ops = { .recalc_rate = ccu_mp_mmc_recalc_rate, .set_rate = ccu_mp_mmc_set_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_mp_mmc_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_mp.h:85 @ struct ccu_mp { _muxshift, _muxwidth, \ 0, _flags) +#define SUNXI_CCU_MP_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _pshift, _pwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) \ + struct ccu_mp _struct = { \ + .enable = _gate, \ + .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ + .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \ + .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \ + _parents, \ + &ccu_mp_ops, \ + _flags), \ + } \ + } + +#define SUNXI_CCU_MP_DATA_WITH_MUX(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _pshift, _pwidth, \ + _muxshift, _muxwidth, \ + _flags) \ + SUNXI_CCU_MP_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _pshift, _pwidth, \ + _muxshift, _muxwidth, \ + 0, _flags) + +#define SUNXI_CCU_MP_HW_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _pshift, _pwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) \ + struct ccu_mp _struct = { \ + .enable = _gate, \ + .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \ + .p = _SUNXI_CCU_DIV(_pshift, _pwidth), \ + .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS_HW(_name, \ + _parents, \ + &ccu_mp_ops, \ + _flags), \ + } \ + } + static inline struct ccu_mp *hw_to_ccu_mp(struct clk_hw *hw) { struct ccu_common *common = hw_to_ccu_common(hw); @ drivers/clk/sunxi-ng/ccu_mult.c:173 @ const struct clk_ops ccu_mult_ops = { .recalc_rate = ccu_mult_recalc_rate, .set_rate = ccu_mult_set_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_mult_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_mux.c:15 @ #include "ccu_gate.h" #include "ccu_mux.h" +#define CCU_MUX_KEY_VALUE 0x16aa0000 + static u16 ccu_mux_get_prediv(struct ccu_common *common, struct ccu_mux_internal *cm, int parent_index) @ drivers/clk/sunxi-ng/ccu_mux.c:69 @ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common, { return parent_rate / ccu_mux_get_prediv(common, cm, parent_index); } +EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_apply_prediv, SUNXI_CCU); static unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common, struct ccu_mux_internal *cm, @ drivers/clk/sunxi-ng/ccu_mux.c:158 @ int ccu_mux_helper_determine_rate(struct ccu_common *common, req->rate = best_rate; return 0; } +EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_determine_rate, SUNXI_CCU); u8 ccu_mux_helper_get_parent(struct ccu_common *common, struct ccu_mux_internal *cm) @ drivers/clk/sunxi-ng/ccu_mux.c:181 @ u8 ccu_mux_helper_get_parent(struct ccu_common *common, return parent; } +EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_get_parent, SUNXI_CCU); int ccu_mux_helper_set_parent(struct ccu_common *common, struct ccu_mux_internal *cm, @ drivers/clk/sunxi-ng/ccu_mux.c:196 @ int ccu_mux_helper_set_parent(struct ccu_common *common, spin_lock_irqsave(common->lock, flags); reg = readl(common->base + common->reg); + + /* The key field always reads as zero. */ + if (common->features & CCU_FEATURE_KEY_FIELD) + reg |= CCU_MUX_KEY_VALUE; + reg &= ~GENMASK(cm->width + cm->shift - 1, cm->shift); writel(reg | (index << cm->shift), common->base + common->reg); @ drivers/clk/sunxi-ng/ccu_mux.c:208 @ int ccu_mux_helper_set_parent(struct ccu_common *common, return 0; } +EXPORT_SYMBOL_NS_GPL(ccu_mux_helper_set_parent, SUNXI_CCU); static void ccu_mux_disable(struct clk_hw *hw) { @ drivers/clk/sunxi-ng/ccu_mux.c:265 @ const struct clk_ops ccu_mux_ops = { .determine_rate = __clk_mux_determine_rate, .recalc_rate = ccu_mux_recalc_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_mux_ops, SUNXI_CCU); /* * This clock notifier is called when the frequency of the of the parent @ drivers/clk/sunxi-ng/ccu_mux.c:300 @ int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb) return clk_notifier_register(clk, &mux_nb->clk_nb); } +EXPORT_SYMBOL_NS_GPL(ccu_mux_notifier_register, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_mux.h:75 @ struct ccu_mux { SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, NULL, \ _reg, _shift, _width, 0, _flags) +#define SUNXI_CCU_MUX_DATA_WITH_GATE(_struct, _name, _parents, _reg, \ + _shift, _width, _gate, _flags) \ + struct ccu_mux _struct = { \ + .enable = _gate, \ + .mux = _SUNXI_CCU_MUX(_shift, _width), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \ + _parents, \ + &ccu_mux_ops, \ + _flags), \ + } \ + } + +#define SUNXI_CCU_MUX_DATA(_struct, _name, _parents, _reg, \ + _shift, _width, _flags) \ + SUNXI_CCU_MUX_DATA_WITH_GATE(_struct, _name, _parents, _reg, \ + _shift, _width, 0, _flags) + +#define SUNXI_CCU_MUX_HW_WITH_GATE(_struct, _name, _parents, _reg, \ + _shift, _width, _gate, _flags) \ + struct ccu_mux _struct = { \ + .enable = _gate, \ + .mux = _SUNXI_CCU_MUX(_shift, _width), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS_HW(_name, \ + _parents, \ + &ccu_mux_ops, \ + _flags), \ + } \ + } + static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw) { struct ccu_common *common = hw_to_ccu_common(hw); @ drivers/clk/sunxi-ng/ccu_nk.c:160 @ const struct clk_ops ccu_nk_ops = { .round_rate = ccu_nk_round_rate, .set_rate = ccu_nk_set_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_nk_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_nkm.c:209 @ const struct clk_ops ccu_nkm_ops = { .recalc_rate = ccu_nkm_recalc_rate, .set_rate = ccu_nkm_set_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_nkm_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_nkmp.c:233 @ const struct clk_ops ccu_nkmp_ops = { .round_rate = ccu_nkmp_round_rate, .set_rate = ccu_nkmp_set_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_nkmp_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_nm.c:241 @ const struct clk_ops ccu_nm_ops = { .round_rate = ccu_nm_round_rate, .set_rate = ccu_nm_set_rate, }; +EXPORT_SYMBOL_NS_GPL(ccu_nm_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_phase.c:124 @ const struct clk_ops ccu_phase_ops = { .get_phase = ccu_phase_get_phase, .set_phase = ccu_phase_set_phase, }; +EXPORT_SYMBOL_NS_GPL(ccu_phase_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_reset.c:78 @ const struct reset_control_ops ccu_reset_ops = { .reset = ccu_reset_reset, .status = ccu_reset_status, }; +EXPORT_SYMBOL_NS_GPL(ccu_reset_ops, SUNXI_CCU); @ drivers/clk/sunxi-ng/ccu_sdm.c:23 @ bool ccu_sdm_helper_is_enabled(struct ccu_common *common, return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable); } +EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled, SUNXI_CCU); void ccu_sdm_helper_enable(struct ccu_common *common, struct ccu_sdm_internal *sdm, @ drivers/clk/sunxi-ng/ccu_sdm.c:53 @ void ccu_sdm_helper_enable(struct ccu_common *common, writel(reg | sdm->enable, common->base + common->reg); spin_unlock_irqrestore(common->lock, flags); } +EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable, SUNXI_CCU); void ccu_sdm_helper_disable(struct ccu_common *common, struct ccu_sdm_internal *sdm) @ drivers/clk/sunxi-ng/ccu_sdm.c:74 @ void ccu_sdm_helper_disable(struct ccu_common *common, writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg); spin_unlock_irqrestore(common->lock, flags); } +EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_disable, SUNXI_CCU); /* * Sigma delta modulation provides a way to do fractional-N frequency @ drivers/clk/sunxi-ng/ccu_sdm.c:108 @ bool ccu_sdm_helper_has_rate(struct ccu_common *common, return false; } +EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_has_rate, SUNXI_CCU); unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common, struct ccu_sdm_internal *sdm, @ drivers/clk/sunxi-ng/ccu_sdm.c:139 @ unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common, /* We can't calculate the effective clock rate, so just fail. */ return 0; } +EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_read_rate, SUNXI_CCU); int ccu_sdm_helper_get_factors(struct ccu_common *common, struct ccu_sdm_internal *sdm, @ drivers/clk/sunxi-ng/ccu_sdm.c:161 @ int ccu_sdm_helper_get_factors(struct ccu_common *common, /* nothing found */ return -EINVAL; } +EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_get_factors, SUNXI_CCU); @ drivers/clocksource/timer-clint.c:5 @ /* * Copyright (C) 2020 Western Digital Corporation or its affiliates. * - * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a - * CLINT MMIO timer device. + * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a CLINT + * MMIO device which is a composite device capable of injecting M-mode + * software interrupts and M-mode timer interrupts. + * + * The RISC-V ACLINT specification is modular in nature and defines + * separate devices for M-mode software interrupt (MSWI), M-mode timer + * (MTIMER) and S-mode software interrupt (SSWI). + * + * This is a common timer driver for the CLINT device and the ACLINT + * MTIMER device. */ #define pr_fmt(fmt) "clint: " fmt @ drivers/clocksource/timer-clint.c:28 @ #include <linux/sched_clock.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/interrupt.h> +#include <linux/irqchip/irq-riscv-aclint-swi.h> #include <linux/of_irq.h> #include <linux/smp.h> #include <linux/timex.h> -#ifndef CONFIG_RISCV_M_MODE +#ifdef CONFIG_RISCV_M_MODE #include <asm/clint.h> + +u64 __iomem *clint_time_val; +EXPORT_SYMBOL(clint_time_val); #endif #define CLINT_IPI_OFF 0 @ drivers/clocksource/timer-clint.c:45 @ #define CLINT_TIMER_VAL_OFF 0xbff8 /* CLINT manages IPI and Timer for RISC-V M-mode */ -static u32 __iomem *clint_ipi_base; static u64 __iomem *clint_timer_cmp; static u64 __iomem *clint_timer_val; static unsigned long clint_timer_freq; static unsigned int clint_timer_irq; -#ifdef CONFIG_RISCV_M_MODE -u64 __iomem *clint_time_val; -EXPORT_SYMBOL(clint_time_val); -#endif - -static void clint_send_ipi(const struct cpumask *target) -{ - unsigned int cpu; - - for_each_cpu(cpu, target) - writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu)); -} - -static void clint_clear_ipi(void) -{ - writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id())); -} - -static struct riscv_ipi_ops clint_ipi_ops = { - .ipi_inject = clint_send_ipi, - .ipi_clear = clint_clear_ipi, -}; - #ifdef CONFIG_64BIT #define clint_get_cycles() readq_relaxed(clint_timer_val) #else @ drivers/clocksource/timer-clint.c:139 @ static int __init clint_timer_init_dt(struct device_node *np) { int rc; u32 i, nr_irqs; - void __iomem *base; + void __iomem *base = NULL; + void __iomem *base1 = NULL; struct of_phandle_args oirq; + bool is_aclint = of_device_is_compatible(np, "riscv,aclint-mtimer"); /* * Ensure that CLINT device interrupts are either RV_IRQ_TIMER or @ drivers/clocksource/timer-clint.c:182 @ static int __init clint_timer_init_dt(struct device_node *np) return -ENODEV; } - clint_ipi_base = base + CLINT_IPI_OFF; - clint_timer_cmp = base + CLINT_TIMER_CMP_OFF; - clint_timer_val = base + CLINT_TIMER_VAL_OFF; + if (is_aclint) { + clint_timer_val = base; + base1 = of_iomap(np, 1); + if (!base1) { + rc = -ENODEV; + pr_err("%pOFP: could not map registers\n", np); + goto fail_iounmap; + } + clint_timer_cmp = base1; + } else { + clint_timer_cmp = base + CLINT_TIMER_CMP_OFF; + clint_timer_val = base + CLINT_TIMER_VAL_OFF; + } clint_timer_freq = riscv_timebase; #ifdef CONFIG_RISCV_M_MODE @ drivers/clocksource/timer-clint.c:231 @ static int __init clint_timer_init_dt(struct device_node *np) goto fail_free_irq; } - riscv_set_ipi_ops(&clint_ipi_ops); - clint_clear_ipi(); + if (!is_aclint) { + rc = aclint_swi_init(np, base + CLINT_IPI_OFF); + if (rc) { + pr_err("%pOFP: aclint swi init failed [%d]\n", + np, rc); + goto fail_remove_cpuhp; + } + } return 0; +fail_remove_cpuhp: + cpuhp_remove_state(CPUHP_AP_CLINT_TIMER_STARTING); fail_free_irq: free_irq(clint_timer_irq, &clint_clock_event); fail_iounmap: - iounmap(base); + if (base1) + iounmap(base1); + if (base) + iounmap(base); return rc; } TIMER_OF_DECLARE(clint_timer, "riscv,clint0", clint_timer_init_dt); TIMER_OF_DECLARE(clint_timer1, "sifive,clint0", clint_timer_init_dt); +TIMER_OF_DECLARE(clint_timer2, "riscv,aclint-mtimer", clint_timer_init_dt); @ drivers/clocksource/timer-riscv.c:59 @ static u64 notrace riscv_sched_clock(void) static struct clocksource riscv_clocksource = { .name = "riscv_clocksource", - .rating = 300, + .rating = 400, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, .read = riscv_clocksource_rdtime, @ drivers/cpufreq/Kconfig:324 @ config QORIQ_CPUFREQ This adds the CPUFreq driver support for Freescale QorIQ SoCs which are capable of changing the CPU's frequency dynamically. +config SUN50I_CPUFREQ_NVMEM + tristate "Allwinner nvmem based SUN50I CPUFreq driver" + depends on ARCH_SUNXI || COMPILE_TEST + depends on NVMEM_SUNXI_SID + select PM_OPP + help + This adds the nvmem based CPUFreq driver for Allwinner + sun20i/sun50i SoCs. + + To compile this driver as a module, choose M here: the + module will be called sun50i-cpufreq-nvmem. + endif endmenu @ drivers/cpufreq/Kconfig.arm:32 @ config ACPI_CPPC_CPUFREQ_FIE If in doubt, say N. -config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM - tristate "Allwinner nvmem based SUN50I CPUFreq driver" - depends on ARCH_SUNXI - depends on NVMEM_SUNXI_SID - select PM_OPP - help - This adds the nvmem based CPUFreq driver for Allwinner - h6 SoC. - - To compile this driver as a module, choose M here: the - module will be called sun50i-cpufreq-nvmem. - config ARM_ARMADA_37XX_CPUFREQ tristate "Armada 37xx CPUFreq support" depends on ARCH_MVEBU && CPUFREQ_DT @ drivers/cpufreq/Makefile:81 @ obj-$(CONFIG_ARM_SCMI_CPUFREQ) += scmi-cpufreq.o obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o -obj-$(CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM) += sun50i-cpufreq-nvmem.o obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o @ drivers/cpufreq/Makefile:110 @ obj-$(CONFIG_LOONGSON1_CPUFREQ) += loongson1-cpufreq.o obj-$(CONFIG_SH_CPU_FREQ) += sh-cpufreq.o obj-$(CONFIG_SPARC_US2E_CPUFREQ) += sparc-us2e-cpufreq.o obj-$(CONFIG_SPARC_US3_CPUFREQ) += sparc-us3-cpufreq.o +obj-$(CONFIG_SUN50I_CPUFREQ_NVMEM) += sun50i-cpufreq-nvmem.o @ drivers/cpufreq/cpufreq-dt-platdev.c:104 @ static const struct of_device_id allowlist[] __initconst = { * platforms using "operating-points-v2" property. */ static const struct of_device_id blocklist[] __initconst = { + { .compatible = "allwinner,sun20i-d1", }, + { .compatible = "allwinner,sun50i-a100", }, { .compatible = "allwinner,sun50i-h6", }, { .compatible = "arm,vexpress", }, @ drivers/cpufreq/sun50i-cpufreq-nvmem.c:22 @ #define MAX_NAME_LEN 7 -#define NVMEM_MASK 0x7 -#define NVMEM_SHIFT 5 +#define SUN50I_A100_NVMEM_MASK 0xf +#define SUN50I_A100_NVMEM_SHIFT 12 + +#define SUN50I_H6_NVMEM_MASK 0x7 +#define SUN50I_H6_NVMEM_SHIFT 5 + +struct sunxi_cpufreq_soc_data { + int (*efuse_xlate)(struct nvmem_cell *speedbin_nvmem); +}; static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev; +static int sun20i_d1_efuse_xlate(struct nvmem_cell *speedbin_nvmem) +{ + return 0; +} + +static int sun50i_a100_efuse_xlate(struct nvmem_cell *speedbin_nvmem) +{ + size_t len; + u16 *speedbin; + u16 efuse_value; + + speedbin = nvmem_cell_read(speedbin_nvmem, &len); + if (IS_ERR(speedbin)) + return PTR_ERR(speedbin); + + efuse_value = (*speedbin >> SUN50I_A100_NVMEM_SHIFT) & SUN50I_A100_NVMEM_MASK; + kfree(speedbin); + + switch (efuse_value) { + case 0b100: + return 2; + case 0b010: + return 1; + default: + return 0; + } +} + +static int sun50i_h6_efuse_xlate(struct nvmem_cell *speedbin_nvmem) +{ + size_t len; + u32 *speedbin; + u32 efuse_value; + + speedbin = nvmem_cell_read(speedbin_nvmem, &len); + if (IS_ERR(speedbin)) + return PTR_ERR(speedbin); + + efuse_value = (*speedbin >> SUN50I_H6_NVMEM_SHIFT) & SUN50I_H6_NVMEM_MASK; + kfree(speedbin); + + /* + * We treat unexpected efuse values as if the SoC was from + * the slowest bin. Expected efuse values are 1-3, slowest + * to fastest. + */ + if (efuse_value >= 1 && efuse_value <= 3) + return efuse_value - 1; + else + return 0; +} + /** * sun50i_cpufreq_get_efuse() - Determine speed grade from efuse value - * @versions: Set to the value parsed from efuse + * @soc_data: pointer to sunxi_cpufreq_soc_data context * - * Returns 0 if success. + * Returns speed grade (OPP voltage index) if successful. */ -static int sun50i_cpufreq_get_efuse(u32 *versions) +static int sun50i_cpufreq_get_efuse(const struct sunxi_cpufreq_soc_data *soc_data) { struct nvmem_cell *speedbin_nvmem; struct device_node *np; struct device *cpu_dev; - u32 *speedbin, efuse_value; - size_t len; + int speed; int ret; cpu_dev = get_cpu_device(0); @ drivers/cpufreq/sun50i-cpufreq-nvmem.c:124 @ static int sun50i_cpufreq_get_efuse(u32 *versions) return PTR_ERR(speedbin_nvmem); } - speedbin = nvmem_cell_read(speedbin_nvmem, &len); + speed = soc_data->efuse_xlate(speedbin_nvmem); nvmem_cell_put(speedbin_nvmem); - if (IS_ERR(speedbin)) - return PTR_ERR(speedbin); - - efuse_value = (*speedbin >> NVMEM_SHIFT) & NVMEM_MASK; - - /* - * We treat unexpected efuse values as if the SoC was from - * the slowest bin. Expected efuse values are 1-3, slowest - * to fastest. - */ - if (efuse_value >= 1 && efuse_value <= 3) - *versions = efuse_value - 1; - else - *versions = 0; - kfree(speedbin); - return 0; + return speed; }; static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev) { + const struct of_device_id *match; struct opp_table **opp_tables; char name[MAX_NAME_LEN]; unsigned int cpu; - u32 speed = 0; + int speed = 0; int ret; + match = dev_get_platdata(&pdev->dev); + if (!match) + return -EINVAL; + opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL); if (!opp_tables) return -ENOMEM; - ret = sun50i_cpufreq_get_efuse(&speed); - if (ret) - return ret; + speed = sun50i_cpufreq_get_efuse(match->data); + if (speed < 0) + return speed; snprintf(name, MAX_NAME_LEN, "speed%d", speed); @ drivers/cpufreq/sun50i-cpufreq-nvmem.c:214 @ static struct platform_driver sun50i_cpufreq_driver = { }, }; +static const struct sunxi_cpufreq_soc_data sun20i_d1_data = { + .efuse_xlate = sun20i_d1_efuse_xlate, +}; + +static const struct sunxi_cpufreq_soc_data sun50i_a100_data = { + .efuse_xlate = sun50i_a100_efuse_xlate, +}; + +static const struct sunxi_cpufreq_soc_data sun50i_h6_data = { + .efuse_xlate = sun50i_h6_efuse_xlate, +}; + static const struct of_device_id sun50i_cpufreq_match_list[] = { - { .compatible = "allwinner,sun50i-h6" }, + { .compatible = "allwinner,sun20i-d1", .data = &sun20i_d1_data }, + { .compatible = "allwinner,sun50i-a100", .data = &sun50i_a100_data }, + { .compatible = "allwinner,sun50i-h6", .data = &sun50i_h6_data }, {} }; MODULE_DEVICE_TABLE(of, sun50i_cpufreq_match_list); @ drivers/cpufreq/sun50i-cpufreq-nvmem.c:264 @ static int __init sun50i_cpufreq_init(void) if (unlikely(ret < 0)) return ret; - sun50i_cpufreq_pdev = - platform_device_register_simple("sun50i-cpufreq-nvmem", - -1, NULL, 0); + sun50i_cpufreq_pdev = platform_device_register_data(NULL, + "sun50i-cpufreq-nvmem", -1, match, sizeof(*match)); ret = PTR_ERR_OR_ZERO(sun50i_cpufreq_pdev); if (ret == 0) return 0; @ drivers/cpuidle/Kconfig:50 @ 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" @ drivers/cpuidle/Kconfig:69 @ depends on PPC source "drivers/cpuidle/Kconfig.powerpc" endmenu +menu "RISC-V CPU Idle Drivers" +depends on RISCV +source "drivers/cpuidle/Kconfig.riscv" +endmenu + config HALTPOLL_CPUIDLE tristate "Halt poll cpuidle driver" depends on X86 && KVM_GUEST @ drivers/cpuidle/Kconfig.arm:30 @ 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, @ drivers/cpuidle/Kconfig.riscv:4 @ +# SPDX-License-Identifier: GPL-2.0-only +# +# RISC-V CPU Idle drivers +# + +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. @ drivers/cpuidle/Makefile:9 @ 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 @ drivers/cpuidle/Makefile:38 @ 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_RISCV_SBI_CPUIDLE) += cpuidle-riscv-sbi.o @ drivers/cpuidle/cpuidle-psci-domain.c:50 @ 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; @ drivers/cpuidle/cpuidle-psci-domain.c:65 @ 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. */ @ drivers/cpuidle/cpuidle-psci-domain.c:77 @ 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) @ drivers/cpuidle/cpuidle-psci-domain.c:92 @ 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; @ drivers/cpuidle/cpuidle-psci-domain.c:119 @ 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; @ drivers/cpuidle/cpuidle-psci-domain.c:182 @ 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; @ drivers/cpuidle/cpuidle-psci-domain.c:214 @ 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); -} @ drivers/cpuidle/cpuidle-psci.h:13 @ 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) { } @ drivers/cpuidle/cpuidle-riscv-sbi.c:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RISC-V SBI CPU idle driver. + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + */ + +#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; + +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; +} + +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; + + /* + * 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; + } + + 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); @ drivers/cpuidle/dt_idle_genpd.c:4 @ +// 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. + */ + +#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); +} @ drivers/cpuidle/dt_idle_genpd.h:4 @ +/* 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 @ drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c:109 @ static const struct ce_variant ce_a64_variant = { .trng = CE_ID_NOTSUPP, }; +static const struct ce_variant ce_d1_variant = { + .alg_cipher = { CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES, + }, + .alg_hash = { CE_ALG_MD5, CE_ALG_SHA1, CE_ALG_SHA224, CE_ALG_SHA256, + CE_ALG_SHA384, CE_ALG_SHA512 + }, + .op_mode = { CE_OP_ECB, CE_OP_CBC + }, + .ce_clks = { + { "bus", 0, 200000000 }, + { "mod", 300000000, 0 }, + { "ram", 0, 400000000 }, + }, + .esr = ESR_D1, + .prng = CE_ALG_PRNG, + .trng = CE_ALG_TRNG, +}; + static const struct ce_variant ce_r40_variant = { .alg_cipher = { CE_ALG_AES, CE_ALG_DES, CE_ALG_3DES, }, @ drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c:213 @ int sun8i_ce_run_task(struct sun8i_ce_dev *ce, int flow, const char *name) dev_err(ce->dev, "CE ERROR: keysram access error for AES\n"); break; case ESR_A64: + case ESR_D1: case ESR_H5: case ESR_R40: v >>= (flow * 4); @ drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c:1012 @ static const struct of_device_id sun8i_ce_crypto_of_match_table[] = { .data = &ce_h3_variant }, { .compatible = "allwinner,sun8i-r40-crypto", .data = &ce_r40_variant }, + { .compatible = "allwinner,sun20i-d1-crypto", + .data = &ce_d1_variant }, { .compatible = "allwinner,sun50i-a64-crypto", .data = &ce_a64_variant }, { .compatible = "allwinner,sun50i-h5-crypto", @ drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h:97 @ #define ESR_R40 2 #define ESR_H5 3 #define ESR_H6 4 +#define ESR_D1 5 #define PRNG_DATA_SIZE (160 / 8) #define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8) @ drivers/dma/Kconfig:166 @ config DMA_SUN4I config DMA_SUN6I tristate "Allwinner A31 SoCs DMA support" - depends on MACH_SUN6I || MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST + depends on ARCH_SUNXI || COMPILE_TEST depends on RESET_CONTROLLER select DMA_ENGINE select DMA_VIRTUAL_CHANNELS @ drivers/dma/sun6i-dma.c:93 @ #define DMA_CHAN_CUR_PARA 0x1c +/* + * LLI address mangling + * + * The LLI link physical address is also mangled, but we avoid dealing + * with that by allocating LLIs from the DMA32 zone. + */ +#define SET_SRC_HIGH_ADDR(x) ((((x) >> 32) & 0x3U) << 16) +#define SET_DST_HIGH_ADDR(x) ((((x) >> 32) & 0x3U) << 18) /* * Various hardware related defines @ drivers/dma/sun6i-dma.c:143 @ struct sun6i_dma_config { u32 dst_burst_lengths; u32 src_addr_widths; u32 dst_addr_widths; + bool has_high_addr; bool has_mbus_clk; }; @ drivers/dma/sun6i-dma.c:235 @ to_sun6i_desc(struct dma_async_tx_descriptor *tx) return container_of(tx, struct sun6i_desc, vd.tx); } +static inline bool sun6i_dma_has_high_addr(struct sun6i_dma_dev *sdev) +{ + return IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) && + sdev->cfg->has_high_addr; +} + static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev) { dev_dbg(sdev->slave.dev, "Common register:\n" @ drivers/dma/sun6i-dma.c:259 @ static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev) static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev, struct sun6i_pchan *pchan) { - phys_addr_t reg = virt_to_phys(pchan->base); - - dev_dbg(sdev->slave.dev, "Chan %d reg: %pa\n" + dev_dbg(sdev->slave.dev, "Chan %d reg: 0x%lx\n" "\t___en(%04x): \t0x%08x\n" "\tpause(%04x): \t0x%08x\n" "\tstart(%04x): \t0x%08x\n" @ drivers/dma/sun6i-dma.c:268 @ static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev, "\t__dst(%04x): \t0x%08x\n" "\tcount(%04x): \t0x%08x\n" "\t_para(%04x): \t0x%08x\n\n", - pchan->idx, ®, + pchan->idx, pchan->base - sdev->base, DMA_CHAN_ENABLE, readl(pchan->base + DMA_CHAN_ENABLE), DMA_CHAN_PAUSE, @ drivers/dma/sun6i-dma.c:401 @ static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev, } static inline void sun6i_dma_dump_lli(struct sun6i_vchan *vchan, - struct sun6i_dma_lli *lli) + struct sun6i_dma_lli *v_lli, + dma_addr_t p_lli) { - phys_addr_t p_lli = virt_to_phys(lli); - dev_dbg(chan2dev(&vchan->vc.chan), - "\n\tdesc: p - %pa v - 0x%p\n" + "\n\tdesc:\tp - %pad v - 0x%p\n" "\t\tc - 0x%08x s - 0x%08x d - 0x%08x\n" "\t\tl - 0x%08x p - 0x%08x n - 0x%08x\n", - &p_lli, lli, - lli->cfg, lli->src, lli->dst, - lli->len, lli->para, lli->p_lli_next); + &p_lli, v_lli, + v_lli->cfg, v_lli->src, v_lli->dst, + v_lli->len, v_lli->para, v_lli->p_lli_next); } static void sun6i_dma_free_desc(struct virt_dma_desc *vd) @ drivers/dma/sun6i-dma.c:460 @ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan) pchan->desc = to_sun6i_desc(&desc->tx); pchan->done = NULL; - sun6i_dma_dump_lli(vchan, pchan->desc->v_lli); + sun6i_dma_dump_lli(vchan, pchan->desc->v_lli, pchan->desc->p_lli); irq_reg = pchan->idx / DMA_IRQ_CHAN_NR; irq_offset = pchan->idx % DMA_IRQ_CHAN_NR; @ drivers/dma/sun6i-dma.c:663 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( if (!txd) return NULL; - v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli); + v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32|GFP_NOWAIT, &p_lli); if (!v_lli) { dev_err(sdev->slave.dev, "Failed to alloc lli memory\n"); goto err_txd_free; @ drivers/dma/sun6i-dma.c:673 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( v_lli->dst = dest; v_lli->len = len; v_lli->para = NORMAL_WAIT; + if (sun6i_dma_has_high_addr(sdev)) + v_lli->para |= SET_SRC_HIGH_ADDR(src) | + SET_DST_HIGH_ADDR(dest); burst = convert_burst(8); width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES); @ drivers/dma/sun6i-dma.c:688 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( sun6i_dma_lli_add(NULL, v_lli, p_lli, txd); - sun6i_dma_dump_lli(vchan, v_lli); + sun6i_dma_dump_lli(vchan, v_lli, p_lli); return vchan_tx_prep(&vchan->vc, &txd->vd, flags); @ drivers/dma/sun6i-dma.c:726 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg( return NULL; for_each_sg(sgl, sg, sg_len, i) { - v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli); + v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32|GFP_NOWAIT, &p_lli); if (!v_lli) goto err_lli_free; @ drivers/dma/sun6i-dma.c:736 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg( if (dir == DMA_MEM_TO_DEV) { v_lli->src = sg_dma_address(sg); v_lli->dst = sconfig->dst_addr; + if (sun6i_dma_has_high_addr(sdev)) + v_lli->para |= SET_SRC_HIGH_ADDR(sg_dma_address(sg)) | + SET_DST_HIGH_ADDR(sconfig->dst_addr); v_lli->cfg = lli_cfg; sdev->cfg->set_drq(&v_lli->cfg, DRQ_SDRAM, vchan->port); sdev->cfg->set_mode(&v_lli->cfg, LINEAR_MODE, IO_MODE); @ drivers/dma/sun6i-dma.c:752 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg( } else { v_lli->src = sconfig->src_addr; v_lli->dst = sg_dma_address(sg); + if (sun6i_dma_has_high_addr(sdev)) + v_lli->para |= SET_SRC_HIGH_ADDR(sconfig->src_addr) | + SET_DST_HIGH_ADDR(sg_dma_address(sg)); v_lli->cfg = lli_cfg; sdev->cfg->set_drq(&v_lli->cfg, vchan->port, DRQ_SDRAM); sdev->cfg->set_mode(&v_lli->cfg, IO_MODE, LINEAR_MODE); @ drivers/dma/sun6i-dma.c:770 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg( } dev_dbg(chan2dev(chan), "First: %pad\n", &txd->p_lli); - for (prev = txd->v_lli; prev; prev = prev->v_lli_next) - sun6i_dma_dump_lli(vchan, prev); + for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli; + p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next) + sun6i_dma_dump_lli(vchan, v_lli, p_lli); return vchan_tx_prep(&vchan->vc, &txd->vd, flags); err_lli_free: - for (prev = txd->v_lli; prev; prev = prev->v_lli_next) - dma_pool_free(sdev->pool, prev, virt_to_phys(prev)); + for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli; + p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next) + dma_pool_free(sdev->pool, v_lli, p_lli); kfree(txd); return NULL; } @ drivers/dma/sun6i-dma.c:813 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic( return NULL; for (i = 0; i < periods; i++) { - v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli); + v_lli = dma_pool_alloc(sdev->pool, GFP_DMA32|GFP_NOWAIT, &p_lli); if (!v_lli) { dev_err(sdev->slave.dev, "Failed to alloc lli memory\n"); goto err_lli_free; @ drivers/dma/sun6i-dma.c:825 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic( if (dir == DMA_MEM_TO_DEV) { v_lli->src = buf_addr + period_len * i; v_lli->dst = sconfig->dst_addr; + if (sun6i_dma_has_high_addr(sdev)) + v_lli->para |= SET_SRC_HIGH_ADDR(buf_addr + period_len * i) | + SET_DST_HIGH_ADDR(sconfig->dst_addr); v_lli->cfg = lli_cfg; sdev->cfg->set_drq(&v_lli->cfg, DRQ_SDRAM, vchan->port); sdev->cfg->set_mode(&v_lli->cfg, LINEAR_MODE, IO_MODE); } else { v_lli->src = sconfig->src_addr; v_lli->dst = buf_addr + period_len * i; + if (sun6i_dma_has_high_addr(sdev)) + v_lli->para |= SET_SRC_HIGH_ADDR(sconfig->src_addr) | + SET_DST_HIGH_ADDR(buf_addr + period_len * i); v_lli->cfg = lli_cfg; sdev->cfg->set_drq(&v_lli->cfg, vchan->port, DRQ_SDRAM); sdev->cfg->set_mode(&v_lli->cfg, IO_MODE, LINEAR_MODE); @ drivers/dma/sun6i-dma.c:852 @ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic( return vchan_tx_prep(&vchan->vc, &txd->vd, flags); err_lli_free: - for (prev = txd->v_lli; prev; prev = prev->v_lli_next) - dma_pool_free(sdev->pool, prev, virt_to_phys(prev)); + for (p_lli = txd->p_lli, v_lli = txd->v_lli; v_lli; + p_lli = v_lli->p_lli_next, v_lli = v_lli->v_lli_next) + dma_pool_free(sdev->pool, v_lli, p_lli); kfree(txd); return NULL; } @ drivers/dma/sun6i-dma.c:941 @ static int sun6i_dma_terminate_all(struct dma_chan *chan) vchan_get_all_descriptors(&vchan->vc, &head); if (pchan) { + writel(DMA_CHAN_PAUSE_PAUSE, pchan->base + DMA_CHAN_PAUSE); writel(DMA_CHAN_ENABLE_STOP, pchan->base + DMA_CHAN_ENABLE); writel(DMA_CHAN_PAUSE_RESUME, pchan->base + DMA_CHAN_PAUSE); @ drivers/dma/sun6i-dma.c:958 @ static int sun6i_dma_terminate_all(struct dma_chan *chan) return 0; } +static void sun6i_dma_synchronize(struct dma_chan *chan) +{ + struct sun6i_vchan *vchan = to_sun6i_vchan(chan); + + vchan_synchronize(&vchan->vc); +} + static enum dma_status sun6i_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *state) @ drivers/dma/sun6i-dma.c:1215 @ static struct sun6i_dma_config sun50i_a64_dma_cfg = { }; /* - * TODO: Add support for more than 4g physical addressing. - * * The A100 binding uses the number of dma channels from the * device tree node. */ @ drivers/dma/sun6i-dma.c:1233 @ static struct sun6i_dma_config sun50i_a100_dma_cfg = { BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_8_BYTES), + .has_high_addr = true, .has_mbus_clk = true, }; @ drivers/dma/sun6i-dma.c:1288 @ static const struct of_device_id sun6i_dma_match[] = { { .compatible = "allwinner,sun8i-a83t-dma", .data = &sun8i_a83t_dma_cfg }, { .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg }, { .compatible = "allwinner,sun8i-v3s-dma", .data = &sun8i_v3s_dma_cfg }, + { .compatible = "allwinner,sun20i-d1-dma", .data = &sun50i_a100_dma_cfg }, { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg }, { .compatible = "allwinner,sun50i-a100-dma", .data = &sun50i_a100_dma_cfg }, { .compatible = "allwinner,sun50i-h6-dma", .data = &sun50i_h6_dma_cfg }, @ drivers/dma/sun6i-dma.c:1368 @ static int sun6i_dma_probe(struct platform_device *pdev) sdc->slave.device_pause = sun6i_dma_pause; sdc->slave.device_resume = sun6i_dma_resume; sdc->slave.device_terminate_all = sun6i_dma_terminate_all; + sdc->slave.device_synchronize = sun6i_dma_synchronize; sdc->slave.src_addr_widths = sdc->cfg->src_addr_widths; sdc->slave.dst_addr_widths = sdc->cfg->dst_addr_widths; sdc->slave.directions = BIT(DMA_DEV_TO_MEM) | @ drivers/dma/sun6i-dma.c:1427 @ static int sun6i_dma_probe(struct platform_device *pdev) vchan_init(&vchan->vc, &sdc->slave); } - ret = reset_control_deassert(sdc->rstc); + ret = reset_control_reset(sdc->rstc); if (ret) { - dev_err(&pdev->dev, "Couldn't deassert the device from reset\n"); + dev_err(&pdev->dev, "Couldn't reset the device\n"); goto err_chan_free; } @ drivers/hwspinlock/sun6i_hwspinlock.c:107 @ static int sun6i_hwspinlock_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(priv->ahb_clk)) { - err = PTR_ERR(priv->ahb_clk); - dev_err(&pdev->dev, "unable to get AHB clock (%d)\n", err); - return err; - } + priv->ahb_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->ahb_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->ahb_clk), + "unable to get AHB clock\n"); - priv->reset = devm_reset_control_get(&pdev->dev, "ahb"); + priv->reset = devm_reset_control_get(&pdev->dev, NULL); if (IS_ERR(priv->reset)) return dev_err_probe(&pdev->dev, PTR_ERR(priv->reset), "unable to get reset control\n"); @ drivers/hwspinlock/sun6i_hwspinlock.c:130 @ static int sun6i_hwspinlock_probe(struct platform_device *pdev) } /* - * bit 28 and 29 represents the hwspinlock setup + * Bits 28 and 29 represent the number of available locks. + * + * The datasheets have two conflicting interpretations for these bits: + * | 00 | 01 | 10 | 11 | + * +-----+----+-----+-----+ + * | 256 | 32 | 64 | 128 | A80, A83T, H3, A64, A50, D1 + * | 32 | 64 | 128 | 256 | H5, H6, R329 + * where some datasheets use "4" instead of "0" for the first column. * - * every datasheet (A64, A80, A83T, H3, H5, H6 ...) says the default value is 0x1 and 0x1 - * to 0x4 represent 32, 64, 128 and 256 locks - * but later datasheets (H5, H6) say 00, 01, 10, 11 represent 32, 64, 128 and 256 locks, - * but that would mean H5 and H6 have 64 locks, while their datasheets talk about 32 locks - * all the time, not a single mentioning of 64 locks - * the 0x4 value is also not representable by 2 bits alone, so some datasheets are not - * correct - * one thing have all in common, default value of the sysstatus register is 0x10000000, - * which results in bit 28 being set - * this is the reason 0x1 is considered being 32 locks and bit 30 is taken into account - * verified on H2+ (datasheet 0x1 = 32 locks) and H5 (datasheet 01 = 64 locks) + * Experiments shows that the first interpretation is correct, as all + * known implementations report the value "1" and have 32 spinlocks. */ - num_banks = readl(io_base + SPINLOCK_SYSSTATUS_REG) >> 28; - switch (num_banks) { - case 1 ... 4: - priv->nlocks = 1 << (4 + num_banks); - break; - default: - err = -EINVAL; - dev_err(&pdev->dev, "unsupported hwspinlock setup (%d)\n", num_banks); - goto bank_fail; - } + num_banks = readl(io_base + SPINLOCK_SYSSTATUS_REG) >> 28 & 0x3; + if (!num_banks) + num_banks = 4; + priv->nlocks = 1 << (4 + num_banks); priv->bank = devm_kzalloc(&pdev->dev, struct_size(priv->bank, lock, priv->nlocks), GFP_KERNEL); @ drivers/input/keyboard/sun4i-lradc-keys.c:17 @ * there are no boards known to use channel 1. */ +#include <linux/clk.h> #include <linux/err.h> #include <linux/init.h> #include <linux/input.h> @ drivers/input/keyboard/sun4i-lradc-keys.c:26 @ #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/pm_wakeup.h> #include <linux/regulator/consumer.h> +#include <linux/reset.h> #include <linux/slab.h> #define LRADC_CTRL 0x00 @ drivers/input/keyboard/sun4i-lradc-keys.c:90 @ struct sun4i_lradc_data { struct device *dev; struct input_dev *input; void __iomem *base; + struct clk *clk; + struct reset_control *reset; struct regulator *vref_supply; struct sun4i_lradc_keymap *chan0_map; const struct lradc_variant *variant; @ drivers/input/keyboard/sun4i-lradc-keys.c:149 @ static int sun4i_lradc_open(struct input_dev *dev) if (error) return error; + error = reset_control_deassert(lradc->reset); + if (error) + goto err_disable_reg; + + error = clk_prepare_enable(lradc->clk); + if (error) + goto err_assert_reset; + lradc->vref = regulator_get_voltage(lradc->vref_supply) * lradc->variant->divisor_numerator / lradc->variant->divisor_denominator; @ drivers/input/keyboard/sun4i-lradc-keys.c:170 @ static int sun4i_lradc_open(struct input_dev *dev) writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC); return 0; + +err_assert_reset: + reset_control_assert(lradc->reset); +err_disable_reg: + regulator_disable(lradc->vref_supply); + + return error; } static void sun4i_lradc_close(struct input_dev *dev) @ drivers/input/keyboard/sun4i-lradc-keys.c:188 @ static void sun4i_lradc_close(struct input_dev *dev) SAMPLE_RATE(2), lradc->base + LRADC_CTRL); writel(0, lradc->base + LRADC_INTC); + clk_disable_unprepare(lradc->clk); + reset_control_assert(lradc->reset); regulator_disable(lradc->vref_supply); } @ drivers/input/keyboard/sun4i-lradc-keys.c:252 @ static int sun4i_lradc_probe(struct platform_device *pdev) { struct sun4i_lradc_data *lradc; struct device *dev = &pdev->dev; - int i; - int error; + int error, i, irq; lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL); if (!lradc) @ drivers/input/keyboard/sun4i-lradc-keys.c:268 @ static int sun4i_lradc_probe(struct platform_device *pdev) return -EINVAL; } + lradc->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(lradc->clk)) + return PTR_ERR(lradc->clk); + + lradc->reset = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(lradc->reset)) + return PTR_ERR(lradc->reset); + lradc->vref_supply = devm_regulator_get(dev, "vref"); if (IS_ERR(lradc->vref_supply)) return PTR_ERR(lradc->vref_supply); @ drivers/input/keyboard/sun4i-lradc-keys.c:305 @ static int sun4i_lradc_probe(struct platform_device *pdev) if (IS_ERR(lradc->base)) return PTR_ERR(lradc->base); - error = devm_request_irq(dev, platform_get_irq(pdev, 0), - sun4i_lradc_irq, 0, + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + error = devm_request_irq(dev, irq, sun4i_lradc_irq, 0, "sun4i-a10-lradc-keys", lradc); if (error) return error; @ drivers/input/keyboard/sun4i-lradc-keys.c:318 @ static int sun4i_lradc_probe(struct platform_device *pdev) if (error) return error; + if (device_property_read_bool(dev, "wakeup-source")) { + device_set_wakeup_capable(dev, true); + + error = dev_pm_set_wake_irq(dev, irq); + if (error) + dev_warn(dev, "Failed to set wake IRQ\n"); + } + return 0; } @ drivers/input/keyboard/sun4i-lradc-keys.c:334 @ static const struct of_device_id sun4i_lradc_of_match[] = { .data = &lradc_variant_a10 }, { .compatible = "allwinner,sun8i-a83t-r-lradc", .data = &r_lradc_variant_a83t }, + { .compatible = "allwinner,sun50i-r329-lradc", + .data = &r_lradc_variant_a83t }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match); @ drivers/iommu/sun50i-iommu.c:50 @ #define IOMMU_TLB_FLUSH_MACRO_TLB BIT(16) #define IOMMU_TLB_FLUSH_MICRO_TLB(i) (BIT(i) & GENMASK(5, 0)) +#define IOMMU_TLB_IVLD_MODE_SEL_REG 0x084 +#define IOMMU_TLB_IVLD_START_ADDR_REG 0x088 +#define IOMMU_TLB_IVLD_END_ADDR_REG 0x08c #define IOMMU_TLB_IVLD_ADDR_REG 0x090 #define IOMMU_TLB_IVLD_ADDR_MASK_REG 0x094 #define IOMMU_TLB_IVLD_ENABLE_REG 0x098 #define IOMMU_TLB_IVLD_ENABLE_ENABLE BIT(0) #define IOMMU_PC_IVLD_ADDR_REG 0x0a0 +#define IOMMU_PC_IVLD_START_ADDR_REG 0x0a4 #define IOMMU_PC_IVLD_ENABLE_REG 0x0a8 #define IOMMU_PC_IVLD_ENABLE_ENABLE BIT(0) +#define IOMMU_PC_IVLD_END_ADDR_REG 0x0ac #define IOMMU_DM_AUT_CTRL_REG(d) (0x0b0 + ((d) / 2) * 4) #define IOMMU_DM_AUT_CTRL_RD_UNAVAIL(d, m) (1 << (((d & 1) * 16) + ((m) * 2))) #define IOMMU_DM_AUT_CTRL_WR_UNAVAIL(d, m) (1 << (((d & 1) * 16) + ((m) * 2) + 1)) @ drivers/iommu/sun50i-iommu.c:79 @ #define IOMMU_L1PG_INT_REG 0x0180 #define IOMMU_L2PG_INT_REG 0x0184 +#define IOMMU_VA_REG 0x0190 +#define IOMMU_VA_DATA_REG 0x0194 +#define IOMMU_VA_CONFIG_REG 0x0198 +#define IOMMU_PMU_ENABLE_REG 0x0200 +#define IOMMU_PMU_CLR_REG 0x0210 +#define IOMMU_PMU_ACCESS_LOW_REG(i) (0x230 + (i) * 16) +#define IOMMU_PMU_ACCESS_HIGH_REG(i) (0x234 + (i) * 16) +#define IOMMU_PMU_HIT_LOW_REG(i) (0x238 + (i) * 16) +#define IOMMU_PMU_HIT_HIGH_REG(i) (0x23c + (i) * 16) +#define IOMMU_PMU_TL_LOW_REG(i) (0x300 + (i) * 16) +#define IOMMU_PMU_TL_HIGH_REG(i) (0x304 + (i) * 16) +#define IOMMU_PMU_ML_REG(i) (0x308 + (i) * 16) + #define IOMMU_INT_INVALID_L2PG BIT(17) #define IOMMU_INT_INVALID_L1PG BIT(16) #define IOMMU_INT_MASTER_PERMISSION(m) BIT(m) @ drivers/iommu/sun50i-iommu.c:966 @ static int sun50i_iommu_probe(struct platform_device *pdev) goto err_free_group; } - iommu->reset = devm_reset_control_get(&pdev->dev, NULL); + iommu->reset = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); if (IS_ERR(iommu->reset)) { dev_err(&pdev->dev, "Couldn't get our reset line.\n"); ret = PTR_ERR(iommu->reset); @ drivers/iommu/sun50i-iommu.c:1007 @ static int sun50i_iommu_probe(struct platform_device *pdev) } static const struct of_device_id sun50i_iommu_dt[] = { + { .compatible = "allwinner,sun20i-d1-iommu", }, { .compatible = "allwinner,sun50i-h6-iommu", }, { /* sentinel */ }, }; @ drivers/irqchip/Kconfig:532 @ config SIFIVE_PLIC If you don't know what to do here, say Y. +config SUN20I_INTC + bool + config EXYNOS_IRQ_COMBINER bool "Samsung Exynos IRQ combiner support" if COMPILE_TEST depends on (ARCH_EXYNOS && ARM) || COMPILE_TEST @ drivers/irqchip/Makefile:26 @ obj-$(CONFIG_OMPIC) += irq-ompic.o obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_OMAP_IRQCHIP) += irq-omap-intc.o -obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o -obj-$(CONFIG_ARCH_SUNXI) += irq-sun6i-r.o -obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o +#obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o +#obj-$(CONFIG_ARCH_SUNXI) += irq-sun6i-r.o +#obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o @ drivers/irqchip/Makefile:101 @ obj-$(CONFIG_CSKY_MPINTC) += irq-csky-mpintc.o obj-$(CONFIG_CSKY_APB_INTC) += irq-csky-apb-intc.o obj-$(CONFIG_RISCV_INTC) += irq-riscv-intc.o obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o +obj-$(CONFIG_SUN20I_INTC) += irq-sun20i.o obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o obj-$(CONFIG_IMX_INTMUX) += irq-imx-intmux.o obj-$(CONFIG_MADERA_IRQ) += irq-madera.o @ drivers/irqchip/irq-riscv-intc.c:29 @ static asmlinkage void riscv_intc_irq(struct pt_regs *regs) if (unlikely(cause >= BITS_PER_LONG)) panic("unexpected interrupt cause"); - switch (cause) { -#ifdef CONFIG_SMP - case RV_IRQ_SOFT: - /* - * We only use software interrupts to pass IPIs, so if a - * non-SMP system gets one, then we don't know what to do. - */ - handle_IPI(regs); - break; -#endif - default: - generic_handle_domain_irq(intc_domain, cause); - break; - } + generic_handle_domain_irq(intc_domain, cause); } /* @ drivers/irqchip/irq-riscv-intc.c:49 @ static void riscv_intc_irq_unmask(struct irq_data *d) csr_set(CSR_IE, BIT(d->hwirq)); } -static int riscv_intc_cpu_starting(unsigned int cpu) -{ - csr_set(CSR_IE, BIT(RV_IRQ_SOFT)); - return 0; -} - -static int riscv_intc_cpu_dying(unsigned int cpu) -{ - csr_clear(CSR_IE, BIT(RV_IRQ_SOFT)); - return 0; -} - static struct irq_chip riscv_intc_chip = { .name = "RISC-V INTC", .irq_mask = riscv_intc_irq_mask, @ drivers/irqchip/irq-riscv-intc.c:65 @ static int riscv_intc_domain_map(struct irq_domain *d, unsigned int irq, return 0; } +static int riscv_intc_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *arg) +{ + int i, ret; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + struct irq_fwspec *fwspec = arg; + + ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) { + ret = riscv_intc_domain_map(domain, virq + i, hwirq + i); + if (ret) + return ret; + } + + return 0; +} + static const struct irq_domain_ops riscv_intc_domain_ops = { .map = riscv_intc_domain_map, .xlate = irq_domain_xlate_onecell, + .alloc = riscv_intc_domain_alloc }; static int __init riscv_intc_init(struct device_node *node, @ drivers/irqchip/irq-riscv-intc.c:126 @ static int __init riscv_intc_init(struct device_node *node, return rc; } - cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_STARTING, - "irqchip/riscv/intc:starting", - riscv_intc_cpu_starting, - riscv_intc_cpu_dying); - pr_info("%d local interrupts mapped\n", BITS_PER_LONG); return 0; @ drivers/irqchip/irq-sifive-plic.c:401 @ static int __init plic_init(struct device_node *node, IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init); IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */ +IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_init); @ drivers/irqchip/irq-sun20i.c:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Allwinner sun20i (D1) wakeup irqchip driver. + */ + +#include <linux/bitmap.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/syscore_ops.h> + +#define SUN20I_HWIRQ_OFFSET 16 +#define SUN20I_NR_HWIRQS 160 + +static struct irq_chip sun20i_intc_chip = { + .name = "sun20i-intc", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, +}; + +static int sun20i_intc_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + if (fwspec->param_count < 2) + return -EINVAL; + if (fwspec->param[0] < SUN20I_HWIRQ_OFFSET) + return -EINVAL; + + *hwirq = fwspec->param[0]; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + + return 0; +} + +static int sun20i_intc_domain_alloc(struct irq_domain *domain, + unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct irq_fwspec *fwspec = arg; + unsigned long hwirq; + unsigned int type; + int i, ret; + + ret = sun20i_intc_domain_translate(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + if (hwirq + nr_irqs > SUN20I_HWIRQ_OFFSET + SUN20I_NR_HWIRQS) + return -EINVAL; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, fwspec); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; ++i, ++hwirq, ++virq) + irq_domain_set_hwirq_and_chip(domain, virq, hwirq, + &sun20i_intc_chip, 0); + + return 0; +} + +static const struct irq_domain_ops sun20i_intc_domain_ops = { + .translate = sun20i_intc_domain_translate, + .alloc = sun20i_intc_domain_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int __init sun20i_intc_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *domain, *parent_domain; + + parent_domain = irq_find_host(parent); + if (!parent_domain) { + pr_err("%pOF: Failed to obtain parent domain\n", node); + return -ENXIO; + } + + domain = irq_domain_add_hierarchy(parent_domain, 0, 0, node, + &sun20i_intc_domain_ops, NULL); + if (!domain) { + pr_err("%pOF: Failed to allocate domain\n", node); + return -ENOMEM; + } + + return 0; +} +IRQCHIP_DECLARE(sun20i_intc, "allwinner,sun20i-d1-intc", sun20i_intc_init); @ drivers/leds/Kconfig:300 @ config LEDS_SUNFIRE This option enables support for the Left, Middle, and Right LEDs on the I/O and CPU boards of SunFire UltraSPARC servers. +config LEDS_SUN50I_R329 + tristate "LED support for Allwinner R329 LED controller" + depends on LEDS_CLASS + depends on ARCH_SUNXI || COMPILE_TEST + help + This option enables support for the RGB LED controller + provided in some Allwinner sunxi SoCs, like the R329. + config LEDS_IPAQ_MICRO tristate "LED Support for the Compaq iPAQ h3xxx" depends on LEDS_CLASS @ drivers/leds/Makefile:80 @ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o +obj-$(CONFIG_LEDS_SUN50I_R329) += leds-sun50i-r329.o obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o @ drivers/leds/leds-sun50i-r329.c:4 @ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2021 Samuel Holland <samuel@sholland.org> +// +// Partly based on drivers/leds/leds-turris-omnia.c, which is: +// Copyright (c) 2020 by Marek Behún <kabel@kernel.org> +// + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/led-class-multicolor.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/reset.h> +#include <linux/spinlock.h> + +#define LEDC_CTRL_REG 0x0000 +#define LEDC_CTRL_REG_DATA_LENGTH (0x1fff << 16) +#define LEDC_CTRL_REG_RGB_MODE (0x7 << 6) +#define LEDC_CTRL_REG_LEDC_EN BIT(0) +#define LEDC_T01_TIMING_CTRL_REG 0x0004 +#define LEDC_T01_TIMING_CTRL_REG_T1H (0x3f << 21) +#define LEDC_T01_TIMING_CTRL_REG_T1L (0x1f << 16) +#define LEDC_T01_TIMING_CTRL_REG_T0H (0x1f << 6) +#define LEDC_T01_TIMING_CTRL_REG_T0L (0x3f << 0) +#define LEDC_RESET_TIMING_CTRL_REG 0x000c +#define LEDC_RESET_TIMING_CTRL_REG_LED_NUM (0x3ff << 0) +#define LEDC_DATA_REG 0x0014 +#define LEDC_DMA_CTRL_REG 0x0018 +#define LEDC_DMA_CTRL_REG_FIFO_TRIG_LEVEL (0x1f << 0) +#define LEDC_INT_CTRL_REG 0x001c +#define LEDC_INT_CTRL_REG_GLOBAL_INT_EN BIT(5) +#define LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN BIT(1) +#define LEDC_INT_CTRL_REG_TRANS_FINISH_INT_EN BIT(0) +#define LEDC_INT_STS_REG 0x0020 +#define LEDC_INT_STS_REG_FIFO_CPUREQ_INT BIT(1) +#define LEDC_INT_STS_REG_TRANS_FINISH_INT BIT(0) + +#define LEDC_FIFO_DEPTH 32 +#define LEDC_MAX_LEDS 1024 + +#define LEDS_TO_BYTES(n) ((n) * sizeof(u32)) + +struct sun50i_r329_ledc_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled_info[3]; +}; +#define to_ledc_led(mc) container_of(mc, struct sun50i_r329_ledc_led, mc_cdev) + +struct sun50i_r329_ledc_timing { + u32 t0h_ns; + u32 t0l_ns; + u32 t1h_ns; + u32 t1l_ns; + u32 treset_ns; +}; + +struct sun50i_r329_ledc { + struct device *dev; + void __iomem *base; + struct clk *bus_clk; + struct clk *mod_clk; + struct reset_control *reset; + + u32 *buffer; + struct dma_chan *dma_chan; + dma_addr_t dma_handle; + int pio_length; + int pio_offset; + + spinlock_t lock; + int next_length; + bool xfer_active; + + u32 format; + struct sun50i_r329_ledc_timing timing; + + int num_leds; + struct sun50i_r329_ledc_led leds[]; +}; + +static int sun50i_r329_ledc_dma_xfer(struct sun50i_r329_ledc *priv, int length) +{ + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + + desc = dmaengine_prep_slave_single(priv->dma_chan, priv->dma_handle, + LEDS_TO_BYTES(length), + DMA_MEM_TO_DEV, 0); + if (!desc) + return -ENOMEM; + + cookie = dmaengine_submit(desc); + if (dma_submit_error(cookie)) + return -EIO; + + dma_async_issue_pending(priv->dma_chan); + + return 0; +} + +static void sun50i_r329_ledc_pio_xfer(struct sun50i_r329_ledc *priv, int length) +{ + u32 burst, offset, val; + + if (length) { + /* New transfer (FIFO is empty). */ + offset = 0; + burst = min(length, LEDC_FIFO_DEPTH); + } else { + /* Existing transfer (FIFO is half-full). */ + length = priv->pio_length; + offset = priv->pio_offset; + burst = min(length, LEDC_FIFO_DEPTH / 2); + } + + iowrite32_rep(priv->base + LEDC_DATA_REG, priv->buffer + offset, burst); + + if (burst < length) { + priv->pio_length = length - burst; + priv->pio_offset = offset + burst; + + if (!offset) { + val = readl(priv->base + LEDC_INT_CTRL_REG); + val |= LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN; + writel(val, priv->base + LEDC_INT_CTRL_REG); + } + } else { + /* Disable the request IRQ once all data is written. */ + val = readl(priv->base + LEDC_INT_CTRL_REG); + val &= ~LEDC_INT_CTRL_REG_FIFO_CPUREQ_INT_EN; + writel(val, priv->base + LEDC_INT_CTRL_REG); + } +} + +static void sun50i_r329_ledc_start_xfer(struct sun50i_r329_ledc *priv, + int length) +{ + u32 val; + + dev_dbg(priv->dev, "Updating %d LEDs\n", length); + + val = readl(priv->base + LEDC_CTRL_REG); + val &= ~LEDC_CTRL_REG_DATA_LENGTH; + val |= length << 16 | LEDC_CTRL_REG_LEDC_EN; + writel(val, priv->base + LEDC_CTRL_REG); + + if (length > LEDC_FIFO_DEPTH) { + int ret = sun50i_r329_ledc_dma_xfer(priv, length); + + if (!ret) + return; + + dev_warn(priv->dev, "Failed to set up DMA: %d\n", ret); + } + + sun50i_r329_ledc_pio_xfer(priv, length); +} + +static irqreturn_t sun50i_r329_ledc_irq(int irq, void *dev_id) +{ + struct sun50i_r329_ledc *priv = dev_id; + u32 val; + + val = readl(priv->base + LEDC_INT_STS_REG); + + if (val & LEDC_INT_STS_REG_TRANS_FINISH_INT) { + int next_length; + + /* Start the next transfer if needed. */ + spin_lock(&priv->lock); + next_length = priv->next_length; + if (next_length) + priv->next_length = 0; + else + priv->xfer_active = false; + spin_unlock(&priv->lock); + + if (next_length) + sun50i_r329_ledc_start_xfer(priv, next_length); + } else if (val & LEDC_INT_STS_REG_FIFO_CPUREQ_INT) { + /* Continue the current transfer. */ + sun50i_r329_ledc_pio_xfer(priv, 0); + } + + writel(val, priv->base + LEDC_INT_STS_REG); + + return IRQ_HANDLED; +} + +static void sun50i_r329_ledc_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct sun50i_r329_ledc *priv = dev_get_drvdata(cdev->dev->parent); + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct sun50i_r329_ledc_led *led = to_ledc_led(mc_cdev); + int addr = led - priv->leds; + unsigned long flags; + bool xfer_active; + int next_length; + + led_mc_calc_color_components(mc_cdev, brightness); + + priv->buffer[addr] = led->subled_info[0].brightness << 16 | + led->subled_info[1].brightness << 8 | + led->subled_info[2].brightness; + + dev_dbg(priv->dev, "LED %d -> #%06x\n", addr, priv->buffer[addr]); + + spin_lock_irqsave(&priv->lock, flags); + next_length = max(priv->next_length, addr + 1); + xfer_active = priv->xfer_active; + if (xfer_active) + priv->next_length = next_length; + else + priv->xfer_active = true; + spin_unlock_irqrestore(&priv->lock, flags); + + if (!xfer_active) + sun50i_r329_ledc_start_xfer(priv, next_length); +} + +static const char *const sun50i_r329_ledc_formats[] = { + "rgb", + "rbg", + "grb", + "gbr", + "brg", + "bgr", +}; + +static int sun50i_r329_ledc_parse_format(const struct device_node *np, + struct sun50i_r329_ledc *priv) +{ + const char *format = "grb"; + u32 i; + + of_property_read_string(np, "allwinner,pixel-format", &format); + + for (i = 0; i < ARRAY_SIZE(sun50i_r329_ledc_formats); ++i) { + if (!strcmp(format, sun50i_r329_ledc_formats[i])) { + priv->format = i; + return 0; + } + } + + dev_err(priv->dev, "Bad pixel format '%s'\n", format); + + return -EINVAL; +} + +static void sun50i_r329_ledc_set_format(struct sun50i_r329_ledc *priv) +{ + u32 val; + + val = readl(priv->base + LEDC_CTRL_REG); + val &= ~LEDC_CTRL_REG_RGB_MODE; + val |= priv->format << 6; + writel(val, priv->base + LEDC_CTRL_REG); +} + +static const struct sun50i_r329_ledc_timing sun50i_r329_ledc_default_timing = { + .t0h_ns = 336, + .t0l_ns = 840, + .t1h_ns = 882, + .t1l_ns = 294, + .treset_ns = 300000, +}; + +static int sun50i_r329_ledc_parse_timing(const struct device_node *np, + struct sun50i_r329_ledc *priv) +{ + struct sun50i_r329_ledc_timing *timing = &priv->timing; + + *timing = sun50i_r329_ledc_default_timing; + + of_property_read_u32(np, "allwinner,t0h-ns", &timing->t0h_ns); + of_property_read_u32(np, "allwinner,t0l-ns", &timing->t0l_ns); + of_property_read_u32(np, "allwinner,t1h-ns", &timing->t1h_ns); + of_property_read_u32(np, "allwinner,t1l-ns", &timing->t1l_ns); + of_property_read_u32(np, "allwinner,treset-ns", &timing->treset_ns); + + return 0; +} + +static void sun50i_r329_ledc_set_timing(struct sun50i_r329_ledc *priv) +{ + const struct sun50i_r329_ledc_timing *timing = &priv->timing; + unsigned long mod_freq = clk_get_rate(priv->mod_clk); + u32 cycle_ns = NSEC_PER_SEC / mod_freq; + u32 val; + + val = (timing->t1h_ns / cycle_ns) << 21 | + (timing->t1l_ns / cycle_ns) << 16 | + (timing->t0h_ns / cycle_ns) << 6 | + (timing->t0l_ns / cycle_ns); + writel(val, priv->base + LEDC_T01_TIMING_CTRL_REG); + + val = (timing->treset_ns / cycle_ns) << 16 | + (priv->num_leds - 1); + writel(val, priv->base + LEDC_RESET_TIMING_CTRL_REG); +} + +static int sun50i_r329_ledc_resume(struct device *dev) +{ + struct sun50i_r329_ledc *priv = dev_get_drvdata(dev); + u32 val; + int ret; + + ret = reset_control_deassert(priv->reset); + if (ret) + return ret; + + ret = clk_prepare_enable(priv->bus_clk); + if (ret) + goto err_assert_reset; + + ret = clk_prepare_enable(priv->mod_clk); + if (ret) + goto err_disable_bus_clk; + + sun50i_r329_ledc_set_format(priv); + sun50i_r329_ledc_set_timing(priv); + + /* The trigger level must be at least the burst length. */ + val = readl(priv->base + LEDC_DMA_CTRL_REG); + val &= ~LEDC_DMA_CTRL_REG_FIFO_TRIG_LEVEL; + val |= LEDC_FIFO_DEPTH / 2; + writel(val, priv->base + LEDC_DMA_CTRL_REG); + + val = LEDC_INT_CTRL_REG_GLOBAL_INT_EN | + LEDC_INT_CTRL_REG_TRANS_FINISH_INT_EN; + writel(val, priv->base + LEDC_INT_CTRL_REG); + + return 0; + +err_disable_bus_clk: + clk_disable_unprepare(priv->bus_clk); +err_assert_reset: + reset_control_assert(priv->reset); + + return ret; +} + +static int sun50i_r329_ledc_suspend(struct device *dev) +{ + struct sun50i_r329_ledc *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->mod_clk); + clk_disable_unprepare(priv->bus_clk); + reset_control_assert(priv->reset); + + return 0; +} + +static void sun50i_r329_ledc_dma_cleanup(void *data) +{ + struct sun50i_r329_ledc *priv = data; + struct device *dma_dev = dmaengine_get_dma_device(priv->dma_chan); + + if (priv->buffer) + dma_free_wc(dma_dev, LEDS_TO_BYTES(priv->num_leds), + priv->buffer, priv->dma_handle); + dma_release_channel(priv->dma_chan); +} + +static int sun50i_r329_ledc_probe(struct platform_device *pdev) +{ + const struct device_node *np = pdev->dev.of_node; + struct dma_slave_config dma_cfg = {}; + struct led_init_data init_data = {}; + struct device *dev = &pdev->dev; + struct device_node *child; + struct sun50i_r329_ledc *priv; + struct resource *mem; + int count, irq, ret; + + count = of_get_available_child_count(np); + if (!count) + return -ENODEV; + if (count > LEDC_MAX_LEDS) { + dev_err(dev, "Too many LEDs! (max is %d)\n", LEDC_MAX_LEDS); + return -EINVAL; + } + + priv = devm_kzalloc(dev, struct_size(priv, leds, count), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->num_leds = count; + spin_lock_init(&priv->lock); + dev_set_drvdata(dev, priv); + + ret = sun50i_r329_ledc_parse_format(np, priv); + if (ret) + return ret; + + ret = sun50i_r329_ledc_parse_timing(np, priv); + if (ret) + return ret; + + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(priv->bus_clk)) + return PTR_ERR(priv->bus_clk); + + priv->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(priv->mod_clk)) + return PTR_ERR(priv->mod_clk); + + priv->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(priv->reset)) + return PTR_ERR(priv->reset); + + priv->dma_chan = dma_request_chan(dev, "tx"); + if (IS_ERR(priv->dma_chan)) + return PTR_ERR(priv->dma_chan); + + ret = devm_add_action_or_reset(dev, sun50i_r329_ledc_dma_cleanup, priv); + if (ret) + return ret; + + dma_cfg.dst_addr = mem->start + LEDC_DATA_REG; + dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_cfg.dst_maxburst = LEDC_FIFO_DEPTH / 2; + ret = dmaengine_slave_config(priv->dma_chan, &dma_cfg); + if (ret) + return ret; + + priv->buffer = dma_alloc_wc(dmaengine_get_dma_device(priv->dma_chan), + LEDS_TO_BYTES(priv->num_leds), + &priv->dma_handle, GFP_KERNEL); + if (!priv->buffer) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, sun50i_r329_ledc_irq, + 0, dev_name(dev), priv); + if (ret) + return ret; + + ret = sun50i_r329_ledc_resume(dev); + if (ret) + return ret; + + for_each_available_child_of_node(np, child) { + struct sun50i_r329_ledc_led *led; + struct led_classdev *cdev; + u32 addr, color; + + ret = of_property_read_u32(child, "reg", &addr); + if (ret || addr >= count) { + dev_err(dev, "LED 'reg' values must be from 0 to %d\n", + priv->num_leds - 1); + ret = -EINVAL; + goto err_put_child; + } + + ret = of_property_read_u32(child, "color", &color); + if (ret || color != LED_COLOR_ID_RGB) { + dev_err(dev, "LED 'color' must be LED_COLOR_ID_RGB\n"); + ret = -EINVAL; + goto err_put_child; + } + + led = &priv->leds[addr]; + + led->subled_info[0].color_index = LED_COLOR_ID_RED; + led->subled_info[0].channel = 0; + led->subled_info[1].color_index = LED_COLOR_ID_GREEN; + led->subled_info[1].channel = 1; + led->subled_info[2].color_index = LED_COLOR_ID_BLUE; + led->subled_info[2].channel = 2; + + led->mc_cdev.num_colors = ARRAY_SIZE(led->subled_info); + led->mc_cdev.subled_info = led->subled_info; + + cdev = &led->mc_cdev.led_cdev; + cdev->max_brightness = U8_MAX; + cdev->brightness_set = sun50i_r329_ledc_brightness_set; + + init_data.fwnode = of_fwnode_handle(child); + + ret = devm_led_classdev_multicolor_register_ext(dev, + &led->mc_cdev, + &init_data); + if (ret) { + dev_err(dev, "Failed to register LED %u: %d\n", + addr, ret); + goto err_put_child; + } + } + + dev_info(dev, "Registered %d LEDs\n", priv->num_leds); + + return 0; + +err_put_child: + of_node_put(child); + sun50i_r329_ledc_suspend(&pdev->dev); + + return ret; +} + +static int sun50i_r329_ledc_remove(struct platform_device *pdev) +{ + sun50i_r329_ledc_suspend(&pdev->dev); + + return 0; +} + +static void sun50i_r329_ledc_shutdown(struct platform_device *pdev) +{ + sun50i_r329_ledc_suspend(&pdev->dev); +} + +static const struct of_device_id sun50i_r329_ledc_of_match[] = { + { .compatible = "allwinner,sun50i-r329-ledc" }, + {} +}; +MODULE_DEVICE_TABLE(of, sun50i_r329_ledc_of_match); + +static SIMPLE_DEV_PM_OPS(sun50i_r329_ledc_pm, + sun50i_r329_ledc_suspend, sun50i_r329_ledc_resume); + +static struct platform_driver sun50i_r329_ledc_driver = { + .probe = sun50i_r329_ledc_probe, + .remove = sun50i_r329_ledc_remove, + .shutdown = sun50i_r329_ledc_shutdown, + .driver = { + .name = "sun50i-r329-ledc", + .of_match_table = sun50i_r329_ledc_of_match, + .pm = pm_ptr(&sun50i_r329_ledc_pm), + }, +}; +module_platform_driver(sun50i_r329_ledc_driver); + +MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); +MODULE_DESCRIPTION("Allwinner R329 LED controller driver"); +MODULE_LICENSE("GPL"); @ drivers/mailbox/Kconfig:262 @ config ZYNQMP_IPI_MBOX registers to kick the other processor or enquire status. config SUN6I_MSGBOX - tristate "Allwinner sun6i/sun8i/sun9i/sun50i Message Box" + tristate "Allwinner sun6i/sun8i/sun9i/sun50i ARISC Message Box" depends on ARCH_SUNXI || COMPILE_TEST default ARCH_SUNXI help Mailbox implementation for the hardware message box present in various Allwinner SoCs. This mailbox is used for communication - between the application CPUs and the power management coprocessor. + between the ARM CPUs and the ARISC power management coprocessor. + +config SUN8I_MSGBOX + tristate "Allwinner sun8i/sun20i/sun50i DSP/RISC-V Message Box" + depends on ARCH_SUNXI || COMPILE_TEST + default ARCH_SUNXI + help + Mailbox implementation for the hardware message box present in + various Allwinner SoCs. This mailbox is used for communication + between the ARM/RISC-V CPUs and the integrated DSP. config SPRD_MBOX tristate "Spreadtrum Mailbox" @ drivers/mmc/host/Kconfig:969 @ config MMC_REALTEK_USB config MMC_SUNXI tristate "Allwinner sunxi SD/MMC Host Controller support" depends on ARCH_SUNXI || COMPILE_TEST + depends on SUNXI_CCU help This selects support for the SD/MMC Host Controller on Allwinner sunxi SoCs. @ drivers/mmc/host/sunxi-mmc.c:57 @ #define SDXC_REG_MISTA (0x34) /* SMC Masked Interrupt Status Register */ #define SDXC_REG_RINTR (0x38) /* SMC Raw Interrupt Status Register */ #define SDXC_REG_STAS (0x3C) /* SMC Status Register */ -#define SDXC_REG_FTRGL (0x40) /* SMC FIFO Threshold Watermark Registe */ +#define SDXC_REG_FTRGL (0x40) /* SMC FIFO Threshold Watermark Register */ #define SDXC_REG_FUNS (0x44) /* SMC Function Select Register */ #define SDXC_REG_CBCR (0x48) /* SMC CIU Byte Count Register */ #define SDXC_REG_BBCR (0x4C) /* SMC BIU Byte Count Register */ #define SDXC_REG_DBGC (0x50) /* SMC Debug Enable Register */ -#define SDXC_REG_HWRST (0x78) /* SMC Card Hardware Reset for Register */ +#define SDXC_REG_HWRST (0x78) /* SMC Card Hardware Reset Register */ #define SDXC_REG_DMAC (0x80) /* SMC IDMAC Control Register */ -#define SDXC_REG_DLBA (0x84) /* SMC IDMAC Descriptor List Base Addre */ +#define SDXC_REG_DLBA (0x84) /* SMC IDMAC Descriptor List Base Address */ #define SDXC_REG_IDST (0x88) /* SMC IDMAC Status Register */ #define SDXC_REG_IDIE (0x8C) /* SMC IDMAC Interrupt Enable Register */ -#define SDXC_REG_CHDA (0x90) -#define SDXC_REG_CBDA (0x94) +#define SDXC_REG_CHDA (0x90) /* Current Host Descriptor Address */ +#define SDXC_REG_CBDA (0x94) /* Current Buffer Descriptor Address */ + +/* New registers introduced in A80 */ +#define SDXC_REG_A12A 0x058 /* Auto Command 12 Register */ +#define SDXC_REG_THLD 0x100 /* Card Threshold Control Register */ +#define SDXC_REG_DSBD 0x10C /* eMMC 4.5 DDR Start Bit Detection */ + +/* New registers introduced in A83T */ +#define SDXC_REG_NTSR 0x05C /* New Timing Set Register */ +#define SDXC_REG_SDBG 0x060 /* New Timing Set Debug Register */ + +/* New registers introduced in H3 */ +#define SDXC_REG_RES_CRC 0x110 /* CRC Response from Card/eMMC */ +#define SDXC_REG_D7_CRC 0x114 /* CRC Data 7 from Card/eMMC */ +#define SDXC_REG_D6_CRC 0x118 /* CRC Data 6 from Card/eMMC */ +#define SDXC_REG_D5_CRC 0x11C /* CRC Data 5 from Card/eMMC */ +#define SDXC_REG_D4_CRC 0x120 /* CRC Data 4 from Card/eMMC */ +#define SDXC_REG_D3_CRC 0x124 /* CRC Data 3 from Card/eMMC */ +#define SDXC_REG_D2_CRC 0x128 /* CRC Data 2 from Card/eMMC */ +#define SDXC_REG_D1_CRC 0x12C /* CRC Data 1 from Card/eMMC */ +#define SDXC_REG_D0_CRC 0x130 /* CRC Data 0 from Card/eMMC */ +#define SDXC_REG_CRC_STA 0x134 /* CRC Status from Write Operation */ /* New registers introduced in A64 */ -#define SDXC_REG_A12A 0x058 /* SMC Auto Command 12 Register */ -#define SDXC_REG_SD_NTSR 0x05C /* SMC New Timing Set Register */ +#define SDXC_REG_CSDC 0x054 /* CRC Status Detect Register */ #define SDXC_REG_DRV_DL 0x140 /* Drive Delay Control Register */ -#define SDXC_REG_SAMP_DL_REG 0x144 /* SMC sample delay control */ -#define SDXC_REG_DS_DL_REG 0x148 /* SMC data strobe delay control */ +#define SDXC_REG_SAMP_DL 0x144 /* Sample Delay Control Register */ +#define SDXC_REG_DS_DL 0x148 /* Data Strobe Delay Control Register */ + +/* New registers introduced in H6 */ +#define SDXC_REG_EMCE 0x064 /* Embedded Encrypt/Decrypt Control */ +#define SDXC_REG_EMCE_DBG 0x068 /* Embedded Encrypt/Decrypt Debug */ + +/* New registers introduced in H616 */ +#define SDXC_REG_EXT_CMD 0x138 /* Extended Command Register */ +#define SDXC_REG_EXT_RESP 0x13C /* Extended Response Register */ + +/* New registers introduced in A100 */ +#define SDXC_REG_HS400_DL 0x14C /* HS400 Delay Control Register */ #define mmc_readl(host, reg) \ readl((host)->reg_base + SDXC_##reg) @ drivers/mmc/host/sunxi-mmc.c:248 @ #define SDXC_IDMAC_DES0_CES BIT(30) /* card error summary */ #define SDXC_IDMAC_DES0_OWN BIT(31) /* 1-idma owns it, 0-host owns it */ +/* Buffer size must be a multiple of 4 bytes. */ +#define SDXC_IDMAC_DES1_ALIGN 4 + #define SDXC_CLK_400K 0 #define SDXC_CLK_25M 1 #define SDXC_CLK_50M 2 @ drivers/mmc/host/sunxi-mmc.c:398 @ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, { struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu; dma_addr_t next_desc = host->sg_dma; - int i, max_len = (1 << host->cfg->idma_des_size_bits); + int i; for (i = 0; i < data->sg_len; i++) { pdes[i].config = cpu_to_le32(SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_DIC); - if (data->sg[i].length == max_len) - pdes[i].buf_size = 0; /* 0 == max_len */ - else - pdes[i].buf_size = cpu_to_le32(data->sg[i].length); + pdes[i].buf_size = + cpu_to_le32(ALIGN(data->sg[i].length, + SDXC_IDMAC_DES1_ALIGN)); next_desc += sizeof(struct sunxi_idma_des); pdes[i].buf_addr_ptr1 = cpu_to_le32(sg_dma_address(&data->sg[i]) >> host->cfg->idma_des_shift); - pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc >> - host->cfg->idma_des_shift); + pdes[i].buf_addr_ptr2 = + cpu_to_le32(next_desc >> + host->cfg->idma_des_shift); } pdes[0].config |= cpu_to_le32(SDXC_IDMAC_DES0_FD); @ drivers/mmc/host/sunxi-mmc.c:752 @ static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off) */ writel(SDXC_CAL_DL_SW_EN, host->reg_base + reg_off); +#if 0 + writel(SDXC_CAL_START, host->reg_base + reg_off); + + unsigned long expire = jiffies + msecs_to_jiffies(250); + u32 rval; + + do { + rval = readl(host->reg_base + reg_off); + } while (time_before(jiffies, expire) && !(rval & SDXC_CAL_DONE)); +#endif + return 0; } @ drivers/mmc/host/sunxi-mmc.c:881 @ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, */ if (host->use_new_timings) { /* Don't touch the delay bits */ - rval = mmc_readl(host, REG_SD_NTSR); + rval = mmc_readl(host, REG_NTSR); rval |= SDXC_2X_TIMING_MODE; - mmc_writel(host, REG_SD_NTSR, rval); + mmc_writel(host, REG_NTSR, rval); } /* sunxi_mmc_clk_set_phase expects the actual card clock rate */ @ drivers/mmc/host/sunxi-mmc.c:891 @ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, if (ret) return ret; - ret = sunxi_mmc_calibrate(host, SDXC_REG_SAMP_DL_REG); + ret = sunxi_mmc_calibrate(host, SDXC_REG_SAMP_DL); if (ret) return ret; @ drivers/mmc/host/sunxi-mmc.c:1215 @ static const struct sunxi_mmc_cfg sun9i_a80_cfg = { .can_calibrate = false, }; +static const struct sunxi_mmc_cfg sun20i_d1_cfg = { + .idma_des_size_bits = 13, + .idma_des_shift = 2, + .can_calibrate = true, + .mask_data0 = true, + .needs_new_timings = true, +}; + static const struct sunxi_mmc_cfg sun50i_a64_cfg = { .idma_des_size_bits = 16, - .clk_delays = NULL, .can_calibrate = true, .mask_data0 = true, .needs_new_timings = true, @ drivers/mmc/host/sunxi-mmc.c:1232 @ static const struct sunxi_mmc_cfg sun50i_a64_cfg = { static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = { .idma_des_size_bits = 13, - .clk_delays = NULL, .can_calibrate = true, .needs_new_timings = true, }; @ drivers/mmc/host/sunxi-mmc.c:1239 @ static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = { static const struct sunxi_mmc_cfg sun50i_a100_cfg = { .idma_des_size_bits = 16, .idma_des_shift = 2, - .clk_delays = NULL, .can_calibrate = true, .mask_data0 = true, .needs_new_timings = true, @ drivers/mmc/host/sunxi-mmc.c:1247 @ static const struct sunxi_mmc_cfg sun50i_a100_cfg = { static const struct sunxi_mmc_cfg sun50i_a100_emmc_cfg = { .idma_des_size_bits = 13, .idma_des_shift = 2, - .clk_delays = NULL, .can_calibrate = true, .needs_new_timings = true, }; @ drivers/mmc/host/sunxi-mmc.c:1257 @ static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun8i-a83t-emmc", .data = &sun8i_a83t_emmc_cfg }, { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg }, + { .compatible = "allwinner,sun20i-d1-mmc", .data = &sun20i_d1_cfg }, { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg }, { .compatible = "allwinner,sun50i-a100-mmc", .data = &sun50i_a100_cfg }, @ drivers/mmc/host/sunxi-mmc.c:1464 @ static int sunxi_mmc_probe(struct platform_device *pdev) mmc->max_blk_count = 8192; mmc->max_blk_size = 4096; mmc->max_segs = PAGE_SIZE / sizeof(struct sunxi_idma_des); - mmc->max_seg_size = (1 << host->cfg->idma_des_size_bits); + mmc->max_seg_size = (1 << host->cfg->idma_des_size_bits) - + SDXC_IDMAC_DES1_ALIGN; mmc->max_req_size = mmc->max_seg_size * mmc->max_segs; /* 400kHz ~ 52MHz */ mmc->f_min = 400000; @ drivers/nvmem/sunxi_sid.c:187 @ static const struct sunxi_sid_cfg sun8i_h3_cfg = { .need_register_readout = true, }; +static const struct sunxi_sid_cfg sun20i_d1_cfg = { + .value_offset = 0x200, + .size = 0x100, +}; + static const struct sunxi_sid_cfg sun50i_a64_cfg = { .value_offset = 0x200, .size = 0x100, @ drivers/nvmem/sunxi_sid.c:208 @ static const struct of_device_id sunxi_sid_of_match[] = { { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, { .compatible = "allwinner,sun8i-a83t-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, + { .compatible = "allwinner,sun20i-d1-sid", .data = &sun20i_d1_cfg }, { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-h5-sid", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-h6-sid", .data = &sun50i_h6_cfg }, @ drivers/of/irq.c:63 @ struct device_node *of_irq_find_parent(struct device_node *child) return NULL; do { - if (of_property_read_u32(child, "interrupt-parent", &parent)) { + if (of_property_read_u32(child, "interrupt-parent", &parent) && + of_property_read_u32(child, "interrupts-extended", &parent)) { p = of_get_parent(child); } else { if (of_irq_workarounds & OF_IMAP_NO_PHANDLE) @ drivers/of/irq.c:557 @ void __init of_irq_init(const struct of_device_id *matches) desc->interrupt_parent = of_irq_find_parent(np); if (desc->interrupt_parent == np) desc->interrupt_parent = NULL; + pr_notice("of_irq_init: found %pOF with parent %pOF\n", + desc->dev, desc->interrupt_parent); list_add_tail(&desc->list, &intc_desc_list); } @ drivers/of/irq.c:583 @ void __init of_irq_init(const struct of_device_id *matches) of_node_set_flag(desc->dev, OF_POPULATED); - pr_debug("of_irq_init: init %pOF (%p), parent %p\n", - desc->dev, - desc->dev, desc->interrupt_parent); + pr_notice("of_irq_init: init %pOF with parent %pOF\n", + desc->dev, desc->interrupt_parent); ret = desc->irq_init_cb(desc->dev, desc->interrupt_parent); if (ret) { @ drivers/phy/allwinner/phy-sun4i-usb.c:46 @ #define REG_PHYCTL_A33 0x10 #define REG_PHY_OTGCTL 0x20 -#define REG_PMU_UNK1 0x10 +#define REG_HCI_PHY_CTL 0x10 #define PHYCTL_DATA BIT(7) @ drivers/phy/allwinner/phy-sun4i-usb.c:85 @ /* A83T specific control bits for PHY0 */ #define PHY_CTL_VBUSVLDEXT BIT(5) #define PHY_CTL_SIDDQ BIT(3) +#define PHY_CTL_H3_SIDDQ BIT(1) /* A83T specific control bits for PHY2 HSIC */ #define SUNXI_EHCI_HS_FORCE BIT(20) @ drivers/phy/allwinner/phy-sun4i-usb.c:119 @ struct sun4i_usb_phy_cfg { int hsic_index; enum sun4i_usb_phy_type type; u32 disc_thresh; + u32 hci_phy_ctl_clear; u8 phyctl_offset; bool dedicated_clocks; - bool enable_pmu_unk1; bool phy0_dual_route; int missing_phys; }; @ drivers/phy/allwinner/phy-sun4i-usb.c:292 @ static int sun4i_usb_phy_init(struct phy *_phy) return ret; } + if (phy->pmu && data->cfg->hci_phy_ctl_clear) { + val = readl(phy->pmu + REG_HCI_PHY_CTL); + val &= ~data->cfg->hci_phy_ctl_clear; + writel(val, phy->pmu + REG_HCI_PHY_CTL); + } + if (data->cfg->type == sun8i_a83t_phy || data->cfg->type == sun50i_h6_phy) { if (phy->index == 0) { @ drivers/phy/allwinner/phy-sun4i-usb.c:307 @ static int sun4i_usb_phy_init(struct phy *_phy) writel(val, data->base + data->cfg->phyctl_offset); } } else { - if (phy->pmu && data->cfg->enable_pmu_unk1) { - val = readl(phy->pmu + REG_PMU_UNK1); - writel(val & ~2, phy->pmu + REG_PMU_UNK1); - } - /* Enable USB 45 Ohm resistor calibration */ if (phy->index == 0) sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); @ drivers/phy/allwinner/phy-sun4i-usb.c:868 @ static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { @ drivers/phy/allwinner/phy-sun4i-usb.c:876 @ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { .disc_thresh = 2, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { @ drivers/phy/allwinner/phy-sun4i-usb.c:884 @ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { @ drivers/phy/allwinner/phy-sun4i-usb.c:892 @ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { .disc_thresh = 2, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { @ drivers/phy/allwinner/phy-sun4i-usb.c:900 @ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { @ drivers/phy/allwinner/phy-sun4i-usb.c:908 @ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = false, }; static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { @ drivers/phy/allwinner/phy-sun4i-usb.c:924 @ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; @ drivers/phy/allwinner/phy-sun4i-usb.c:934 @ static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; @ drivers/phy/allwinner/phy-sun4i-usb.c:944 @ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, + .phy0_dual_route = true, +}; + +static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = { + .num_phys = 2, + .type = sun50i_h6_phy, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .hci_phy_ctl_clear = PHY_CTL_SIDDQ, .phy0_dual_route = true, }; @ drivers/phy/allwinner/phy-sun4i-usb.c:963 @ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, - .enable_pmu_unk1 = true, + .hci_phy_ctl_clear = PHY_CTL_H3_SIDDQ, .phy0_dual_route = true, }; static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = { .num_phys = 4, .type = sun50i_h6_phy, - .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, .phy0_dual_route = true, @ drivers/phy/allwinner/phy-sun4i-usb.c:987 @ static const struct of_device_id sun4i_usb_phy_of_match[] = { { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, { .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg }, { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, - { .compatible = "allwinner,sun50i-a64-usb-phy", - .data = &sun50i_a64_cfg}, + { .compatible = "allwinner,sun20i-d1-usb-phy", .data = &sun20i_d1_cfg }, + { .compatible = "allwinner,sun50i-a64-usb-phy", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-h6-usb-phy", .data = &sun50i_h6_cfg }, { }, }; @ drivers/pinctrl/sunxi/Kconfig:87 @ config PINCTRL_SUN9I_A80_R depends on RESET_CONTROLLER select PINCTRL_SUNXI +config PINCTRL_SUN20I_D1 + bool "Support for the Allwinner D1 PIO" + default RISCV && ARCH_SUNXI + select PINCTRL_SUNXI + config PINCTRL_SUN50I_A64 bool "Support for the Allwinner A64 PIO" default ARM64 && ARCH_SUNXI @ drivers/pinctrl/sunxi/Makefile:23 @ obj-$(CONFIG_PINCTRL_SUN8I_A83T_R) += pinctrl-sun8i-a83t-r.o obj-$(CONFIG_PINCTRL_SUN8I_H3) += pinctrl-sun8i-h3.o obj-$(CONFIG_PINCTRL_SUN8I_H3_R) += pinctrl-sun8i-h3-r.o obj-$(CONFIG_PINCTRL_SUN8I_V3S) += pinctrl-sun8i-v3s.o +obj-$(CONFIG_PINCTRL_SUN20I_D1) += pinctrl-sun20i-d1.o obj-$(CONFIG_PINCTRL_SUN50I_H5) += pinctrl-sun50i-h5.o obj-$(CONFIG_PINCTRL_SUN50I_H6) += pinctrl-sun50i-h6.o obj-$(CONFIG_PINCTRL_SUN50I_H6_R) += pinctrl-sun50i-h6-r.o @ drivers/pinctrl/sunxi/pinctrl-sun20i-d1.c:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner D1 SoC pinctrl driver. + * + * Copyright (c) 2020 wuyan@allwinnertech.com + * Copyright (c) 2021 Samuel Holland <samuel@sholland.org> + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-sunxi.h" + +/* PB:8pins,PC:8pins,PD:23pins,PE:18pins,PF:7pins, PG:16pins */ +static const struct sunxi_desc_pin d1_pins[] = { + /* PB */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm"), + SUNXI_FUNCTION(0x3, "ir"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x5, "spi1"), /* WP */ + SUNXI_FUNCTION(0x6, "uart0"), /* TX */ + SUNXI_FUNCTION(0x7, "uart2"), /* TX */ + SUNXI_FUNCTION(0x8, "spdif"), /* OUT */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm"), + SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT3 */ + SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN3 */ + SUNXI_FUNCTION(0x6, "uart0"), /* RX */ + SUNXI_FUNCTION(0x7, "uart2"), /* RX */ + SUNXI_FUNCTION(0x8, "ir"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D0 */ + SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT2 */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN2 */ + SUNXI_FUNCTION(0x6, "lcd0"), /* D18 */ + SUNXI_FUNCTION(0x7, "uart4"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D1 */ + SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT1 */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN0 */ + SUNXI_FUNCTION(0x6, "lcd0"), /* D19 */ + SUNXI_FUNCTION(0x7, "uart4"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D8 */ + SUNXI_FUNCTION(0x3, "i2s2_dout"), /* DOUT0 */ + SUNXI_FUNCTION(0x4, "i2c1"), /* SCK */ + SUNXI_FUNCTION(0x5, "i2s2_din"), /* DIN1 */ + SUNXI_FUNCTION(0x6, "lcd0"), /* D20 */ + SUNXI_FUNCTION(0x7, "uart5"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D9 */ + SUNXI_FUNCTION(0x3, "i2s2"), /* BCLK */ + SUNXI_FUNCTION(0x4, "i2c1"), /* SDA */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION(0x6, "lcd0"), /* D21 */ + SUNXI_FUNCTION(0x7, "uart5"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D16 */ + SUNXI_FUNCTION(0x3, "i2s2"), /* LRCK */ + SUNXI_FUNCTION(0x4, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION(0x6, "lcd0"), /* D22 */ + SUNXI_FUNCTION(0x7, "uart3"), /* TX */ + SUNXI_FUNCTION(0x8, "bist0"), /* BIST_RESULT0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D17 */ + SUNXI_FUNCTION(0x3, "i2s2"), /* MCLK */ + SUNXI_FUNCTION(0x4, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x5, "ir"), /* RX */ + SUNXI_FUNCTION(0x6, "lcd0"), /* D23 */ + SUNXI_FUNCTION(0x7, "uart3"), /* RX */ + SUNXI_FUNCTION(0x8, "bist1"), /* BIST_RESULT1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 7)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* DATA3 */ + SUNXI_FUNCTION(0x3, "pwm"), + SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x5, "spi1"), /* HOLD */ + SUNXI_FUNCTION(0x6, "uart0"), /* TX */ + SUNXI_FUNCTION(0x7, "uart1"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 8)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* DATA2 */ + SUNXI_FUNCTION(0x3, "pwm"), + SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x5, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x6, "uart0"), /* RX */ + SUNXI_FUNCTION(0x7, "uart1"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 9)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 10), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* DATA1 */ + SUNXI_FUNCTION(0x3, "pwm"), + SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x5, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x6, "clk"), /* FANOUT0 */ + SUNXI_FUNCTION(0x7, "uart1"), /* RTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 10)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 11), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* DATA0 */ + SUNXI_FUNCTION(0x3, "pwm"), + SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x5, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x6, "clk"), /* FANOUT1 */ + SUNXI_FUNCTION(0x7, "uart1"), /* CTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 11)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 12), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "dmic"), /* CLK */ + SUNXI_FUNCTION(0x3, "pwm"), + SUNXI_FUNCTION(0x4, "spdif"), /* IN */ + SUNXI_FUNCTION(0x5, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x6, "clk"), /* FANOUT2 */ + SUNXI_FUNCTION(0x7, "ir"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 0, 12)), + /* PC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart2"), /* TX */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x4, "ledc"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart2"), /* RX */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CLK */ + SUNXI_FUNCTION(0x3, "mmc2"), /* CLK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CS0 */ + SUNXI_FUNCTION(0x3, "mmc2"), /* CMD */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* MOSI */ + SUNXI_FUNCTION(0x3, "mmc2"), /* D2 */ + SUNXI_FUNCTION(0x4, "boot"), /* SEL0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* MISO */ + SUNXI_FUNCTION(0x3, "mmc2"), /* D1 */ + SUNXI_FUNCTION(0x4, "boot"), /* SEL1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* WP */ + SUNXI_FUNCTION(0x3, "mmc2"), /* D0 */ + SUNXI_FUNCTION(0x4, "uart3"), /* TX */ + SUNXI_FUNCTION(0x5, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x6, "pll"), /* DBG-CLK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* HOLD */ + SUNXI_FUNCTION(0x3, "mmc2"), /* D3 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RX */ + SUNXI_FUNCTION(0x5, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x6, "tcon"), /* TRIG0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 1, 7)), + /* PD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V0P */ + SUNXI_FUNCTION(0x4, "dsi"), /* D0P */ + SUNXI_FUNCTION(0x5, "i2c0"), /* SCK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V0N */ + SUNXI_FUNCTION(0x4, "dsi"), /* D0N */ + SUNXI_FUNCTION(0x5, "uart2"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V1P */ + SUNXI_FUNCTION(0x4, "dsi"), /* D1P */ + SUNXI_FUNCTION(0x5, "uart2"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V1N */ + SUNXI_FUNCTION(0x4, "dsi"), /* D1N */ + SUNXI_FUNCTION(0x5, "uart2"), /* RTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V2P */ + SUNXI_FUNCTION(0x4, "dsi"), /* CKP */ + SUNXI_FUNCTION(0x5, "uart2"), /* CTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V2N */ + SUNXI_FUNCTION(0x4, "dsi"), /* CKN */ + SUNXI_FUNCTION(0x5, "uart5"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* CKP */ + SUNXI_FUNCTION(0x4, "dsi"), /* D2P */ + SUNXI_FUNCTION(0x5, "uart5"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* CKN */ + SUNXI_FUNCTION(0x4, "dsi"), /* D2N */ + SUNXI_FUNCTION(0x5, "uart4"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 7)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V3P */ + SUNXI_FUNCTION(0x4, "dsi"), /* D3P */ + SUNXI_FUNCTION(0x5, "uart4"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 8)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */ + SUNXI_FUNCTION(0x3, "lvds0"), /* V3N */ + SUNXI_FUNCTION(0x4, "dsi"), /* D3N */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 9)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V0P */ + SUNXI_FUNCTION(0x4, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x5, "uart3"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 10)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V0N */ + SUNXI_FUNCTION(0x4, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x5, "uart3"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 11)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V1P */ + SUNXI_FUNCTION(0x4, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x5, "i2c0"), /* SDA */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 12)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V1N */ + SUNXI_FUNCTION(0x4, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x5, "uart3"), /* RTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 13)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V2P */ + SUNXI_FUNCTION(0x4, "spi1"), /* HOLD */ + SUNXI_FUNCTION(0x5, "uart3"), /* CTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 14)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V2N */ + SUNXI_FUNCTION(0x4, "spi1"), /* WP */ + SUNXI_FUNCTION(0x5, "ir"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 15)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* CKP */ + SUNXI_FUNCTION(0x4, "dmic"), /* DATA3 */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 16)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */ + SUNXI_FUNCTION(0x3, "lvds1"), /* CKN */ + SUNXI_FUNCTION(0x4, "dmic"), /* DATA2 */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 17)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V3P */ + SUNXI_FUNCTION(0x4, "dmic"), /* DATA1 */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 18)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* DE */ + SUNXI_FUNCTION(0x3, "lvds1"), /* V3N */ + SUNXI_FUNCTION(0x4, "dmic"), /* DATA0 */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 19)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x4, "dmic"), /* CLK */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 20)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x4, "uart1"), /* TX */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 21)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spdif"), /* OUT */ + SUNXI_FUNCTION(0x3, "ir"), /* RX */ + SUNXI_FUNCTION(0x4, "uart1"), /* RX */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 2, 22)), + /* PE */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "uart2"), /* RTS */ + SUNXI_FUNCTION(0x4, "i2c1"), /* SCK */ + SUNXI_FUNCTION(0x5, "lcd0"), /* HSYNC */ + SUNXI_FUNCTION(0x8, "emac"), /* RXCTL/CRS_DV */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "uart2"), /* CTS */ + SUNXI_FUNCTION(0x4, "i2c1"), /* SDA */ + SUNXI_FUNCTION(0x5, "lcd0"), /* VSYNC */ + SUNXI_FUNCTION(0x8, "emac"), /* RXD0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* PCLK */ + SUNXI_FUNCTION(0x3, "uart2"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */ + SUNXI_FUNCTION(0x6, "uart0"), /* TX */ + SUNXI_FUNCTION(0x8, "emac"), /* RXD1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "csi0"), /* MCLK */ + SUNXI_FUNCTION(0x3, "uart2"), /* RX */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */ + SUNXI_FUNCTION(0x6, "uart0"), /* RX */ + SUNXI_FUNCTION(0x8, "emac"), /* TXCK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D0 */ + SUNXI_FUNCTION(0x3, "uart4"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */ + SUNXI_FUNCTION(0x6, "d_jtag"), /* MS */ + SUNXI_FUNCTION(0x7, "r_jtag"), /* MS */ + SUNXI_FUNCTION(0x8, "emac"), /* TXD0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D1 */ + SUNXI_FUNCTION(0x3, "uart4"), /* RX */ + SUNXI_FUNCTION(0x4, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x5, "ledc"), + SUNXI_FUNCTION(0x6, "d_jtag"), /* D1 */ + SUNXI_FUNCTION(0x7, "r_jtag"), /* D1 */ + SUNXI_FUNCTION(0x8, "emac"), /* TXD1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D2 */ + SUNXI_FUNCTION(0x3, "uart5"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x5, "spdif"), /* IN */ + SUNXI_FUNCTION(0x6, "d_jtag"), /* D0 */ + SUNXI_FUNCTION(0x7, "r_jtag"), /* D0 */ + SUNXI_FUNCTION(0x8, "emac"), /* TXCTL/TXEN */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D3 */ + SUNXI_FUNCTION(0x3, "uart5"), /* RX */ + SUNXI_FUNCTION(0x4, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x5, "spdif"), /* OUT */ + SUNXI_FUNCTION(0x6, "d_jtag"), /* CK */ + SUNXI_FUNCTION(0x7, "r_jtag"), /* CK */ + SUNXI_FUNCTION(0x8, "emac"), /* CK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 7)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D4 */ + SUNXI_FUNCTION(0x3, "uart1"), /* RTS */ + SUNXI_FUNCTION(0x4, "pwm"), + SUNXI_FUNCTION(0x5, "uart3"), /* TX */ + SUNXI_FUNCTION(0x6, "jtag"), /* MS */ + SUNXI_FUNCTION(0x8, "emac"), /* MDC */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 8)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D5 */ + SUNXI_FUNCTION(0x3, "uart1"), /* CTS */ + SUNXI_FUNCTION(0x4, "pwm"), + SUNXI_FUNCTION(0x5, "uart3"), /* RX */ + SUNXI_FUNCTION(0x6, "jtag"), /* D1 */ + SUNXI_FUNCTION(0x8, "emac"), /* MDIO */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 9)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D6 */ + SUNXI_FUNCTION(0x3, "uart1"), /* TX */ + SUNXI_FUNCTION(0x4, "pwm"), + SUNXI_FUNCTION(0x5, "ir"), /* RX */ + SUNXI_FUNCTION(0x6, "jtag"), /* D0 */ + SUNXI_FUNCTION(0x8, "emac"), /* EPHY-25M */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 10)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ncsi0"), /* D7 */ + SUNXI_FUNCTION(0x3, "uart1"), /* RX */ + SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT3 */ + SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN3 */ + SUNXI_FUNCTION(0x6, "jtag"), /* CK */ + SUNXI_FUNCTION(0x8, "emac"), /* TXD2 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 11)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x3, "ncsi0"), /* FIELD */ + SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT2 */ + SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN2 */ + SUNXI_FUNCTION(0x8, "emac"), /* TXD3 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 12)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x3, "pwm"), + SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT0 */ + SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN1 */ + SUNXI_FUNCTION(0x6, "dmic"), /* DATA3 */ + SUNXI_FUNCTION(0x8, "emac"), /* RXD2 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 13)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1"), /* SCK */ + SUNXI_FUNCTION(0x3, "d_jtag"), /* MS */ + SUNXI_FUNCTION(0x4, "i2s0_dout"), /* DOUT1 */ + SUNXI_FUNCTION(0x5, "i2s0_din"), /* DIN0 */ + SUNXI_FUNCTION(0x6, "dmic"), /* DATA2 */ + SUNXI_FUNCTION(0x8, "emac"), /* RXD3 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 14)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1"), /* SDA */ + SUNXI_FUNCTION(0x3, "d_jtag"), /* D1 */ + SUNXI_FUNCTION(0x4, "pwm"), + SUNXI_FUNCTION(0x5, "i2s0"), /* LRCK */ + SUNXI_FUNCTION(0x6, "dmic"), /* DATA1 */ + SUNXI_FUNCTION(0x8, "emac"), /* RXCK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 15)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x3, "d_jtag"), /* D0 */ + SUNXI_FUNCTION(0x4, "pwm"), + SUNXI_FUNCTION(0x5, "i2s0"), /* BCLK */ + SUNXI_FUNCTION(0x6, "dmic"), /* DATA0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 16)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x3, "d_jtag"), /* CK */ + SUNXI_FUNCTION(0x4, "ir"), /* TX */ + SUNXI_FUNCTION(0x5, "i2s0"), /* MCLK */ + SUNXI_FUNCTION(0x6, "dmic"), /* CLK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 3, 17)), + /* PF */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */ + SUNXI_FUNCTION(0x3, "jtag"), /* MS */ + SUNXI_FUNCTION(0x4, "r_jtag"), /* MS */ + SUNXI_FUNCTION(0x5, "i2s2_dout"), /* DOUT1 */ + SUNXI_FUNCTION(0x6, "i2s2_din"), /* DIN0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */ + SUNXI_FUNCTION(0x3, "jtag"), /* DI */ + SUNXI_FUNCTION(0x4, "r_jtag"), /* DI */ + SUNXI_FUNCTION(0x5, "i2s2_dout"), /* DOUT0 */ + SUNXI_FUNCTION(0x6, "i2s2_din"), /* DIN1 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart0"), /* TX */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x5, "ledc"), + SUNXI_FUNCTION(0x6, "spdif"), /* IN */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */ + SUNXI_FUNCTION(0x3, "jtag"), /* DO */ + SUNXI_FUNCTION(0x4, "r_jtag"), /* DO */ + SUNXI_FUNCTION(0x5, "i2s2"), /* BCLK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */ + SUNXI_FUNCTION(0x3, "uart0"), /* RX */ + SUNXI_FUNCTION(0x4, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION(0x6, "ir"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */ + SUNXI_FUNCTION(0x3, "jtag"), /* CK */ + SUNXI_FUNCTION(0x4, "r_jtag"), /* CK */ + SUNXI_FUNCTION(0x5, "i2s2"), /* LRCK */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "spdif"), /* OUT */ + SUNXI_FUNCTION(0x4, "ir"), /* RX */ + SUNXI_FUNCTION(0x5, "i2s2"), /* MCLK */ + SUNXI_FUNCTION(0x6, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 4, 6)), + /* PG */ + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart3"), /* TX */ + SUNXI_FUNCTION(0x4, "emac"), /* RXCTRL/CRS_DV */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 0)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */ + SUNXI_FUNCTION(0x3, "uart3"), /* RX */ + SUNXI_FUNCTION(0x4, "emac"), /* RXD0 */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 1)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D0 */ + SUNXI_FUNCTION(0x3, "uart3"), /* RTS */ + SUNXI_FUNCTION(0x4, "emac"), /* RXD1 */ + SUNXI_FUNCTION(0x5, "uart4"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 2)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D1 */ + SUNXI_FUNCTION(0x3, "uart3"), /* CTS */ + SUNXI_FUNCTION(0x4, "emac"), /* TXCK */ + SUNXI_FUNCTION(0x5, "uart4"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 3)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D2 */ + SUNXI_FUNCTION(0x3, "uart5"), /* TX */ + SUNXI_FUNCTION(0x4, "emac"), /* TXD0 */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 4)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D3 */ + SUNXI_FUNCTION(0x3, "uart5"), /* RX */ + SUNXI_FUNCTION(0x4, "emac"), /* TXD1 */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 5)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart1"), /* TX */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* TXD2 */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 6)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart1"), /* RX */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* TXD3 */ + SUNXI_FUNCTION(0x5, "spdif"), /* IN */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 7)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart1"), /* RTS */ + SUNXI_FUNCTION(0x3, "i2c1"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* RXD2 */ + SUNXI_FUNCTION(0x5, "uart3"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 8)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart1"), /* CTS */ + SUNXI_FUNCTION(0x3, "i2c1"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* RXD3 */ + SUNXI_FUNCTION(0x5, "uart3"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 9)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm"), + SUNXI_FUNCTION(0x3, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* RXCK */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */ + SUNXI_FUNCTION(0x6, "ir"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 10)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1"), /* MCLK */ + SUNXI_FUNCTION(0x3, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* EPHY-25M */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */ + SUNXI_FUNCTION(0x6, "tcon"), /* TRIG0 */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 11)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1"), /* LRCK */ + SUNXI_FUNCTION(0x3, "i2c0"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* TXCTL/TXEN */ + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */ + SUNXI_FUNCTION(0x6, "pwm"), + SUNXI_FUNCTION(0x7, "uart1"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 12)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1"), /* BCLK */ + SUNXI_FUNCTION(0x3, "i2c0"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* CLKIN/RXER */ + SUNXI_FUNCTION(0x5, "pwm"), + SUNXI_FUNCTION(0x6, "ledc"), + SUNXI_FUNCTION(0x7, "uart1"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 13)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 14), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1_din"), /* DIN0 */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SCK */ + SUNXI_FUNCTION(0x4, "emac"), /* MDC */ + SUNXI_FUNCTION(0x5, "i2s1_dout"), /* DOUT1 */ + SUNXI_FUNCTION(0x6, "spi0"), /* WP */ + SUNXI_FUNCTION(0x7, "uart1"), /* RTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 14)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 15), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s1_dout"), /* DOUT0 */ + SUNXI_FUNCTION(0x3, "i2c2"), /* SDA */ + SUNXI_FUNCTION(0x4, "emac"), /* MDIO */ + SUNXI_FUNCTION(0x5, "i2s1_din"), /* DIN1 */ + SUNXI_FUNCTION(0x6, "spi0"), /* HOLD */ + SUNXI_FUNCTION(0x7, "uart1"), /* CTS */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 15)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 16), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir"), /* RX */ + SUNXI_FUNCTION(0x3, "tcon"), /* TRIG0 */ + SUNXI_FUNCTION(0x4, "pwm"), + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT2 */ + SUNXI_FUNCTION(0x6, "spdif"), /* IN */ + SUNXI_FUNCTION(0x7, "ledc"), + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 16)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 17), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart2"), /* TX */ + SUNXI_FUNCTION(0x3, "i2c3"), /* SCK */ + SUNXI_FUNCTION(0x4, "pwm"), + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT0 */ + SUNXI_FUNCTION(0x6, "ir"), /* TX */ + SUNXI_FUNCTION(0x7, "uart0"), /* TX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 17)), + SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 18), + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart2"), /* RX */ + SUNXI_FUNCTION(0x3, "i2c3"), /* SDA */ + SUNXI_FUNCTION(0x4, "pwm"), + SUNXI_FUNCTION(0x5, "clk"), /* FANOUT1 */ + SUNXI_FUNCTION(0x6, "spdif"), /* OUT */ + SUNXI_FUNCTION(0x7, "uart0"), /* RX */ + SUNXI_FUNCTION_IRQ_BANK(0xe, 5, 18)), +}; + +static const unsigned int d1_irq_bank_map[] = { 1, 2, 3, 4, 5, 6 }; + +static const struct sunxi_pinctrl_desc d1_pinctrl_data = { + .pins = d1_pins, + .npins = ARRAY_SIZE(d1_pins), + .irq_banks = ARRAY_SIZE(d1_irq_bank_map), + .irq_bank_map = d1_irq_bank_map, + .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_CTL, +}; + +static int d1_pinctrl_probe(struct platform_device *pdev) +{ + return sunxi_pinctrl_init(pdev, &d1_pinctrl_data); +} + +static const struct of_device_id d1_pinctrl_match[] = { + { .compatible = "allwinner,sun20i-d1-pinctrl" }, + {} +}; + +static struct platform_driver d1_pinctrl_driver = { + .probe = d1_pinctrl_probe, + .driver = { + .name = "sun20i-d1-pinctrl", + .of_match_table = d1_pinctrl_match, + }, +}; +builtin_platform_driver(d1_pinctrl_driver); @ drivers/pinctrl/sunxi/pinctrl-sunxi.c:13 @ * warranty of any kind, whether express or implied. */ +#define DEBUG #include <linux/io.h> #include <linux/clk.h> #include <linux/gpio/driver.h> @ drivers/pinctrl/sunxi/pinctrl-sunxi.c:658 @ static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl, reg &= ~IO_BIAS_MASK; writel(reg | val, pctl->membase + sunxi_grp_config_reg(pin)); return 0; + case BIAS_VOLTAGE_PIO_POW_MODE_CTL: + val = 1800000 < uV && uV <= 2500000 ? BIT(bank) : 0; + + raw_spin_lock_irqsave(&pctl->lock, flags); + reg = readl(pctl->membase + PIO_POW_MOD_CTL_REG); + reg &= ~BIT(bank); + writel(reg | val, pctl->membase + PIO_POW_MOD_CTL_REG); + raw_spin_unlock_irqrestore(&pctl->lock, flags); + + fallthrough; case BIAS_VOLTAGE_PIO_POW_MODE_SEL: val = uV <= 1800000 ? 1 : 0; @ drivers/pinctrl/sunxi/pinctrl-sunxi.c:676 @ static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl, reg &= ~(1 << bank); writel(reg | val << bank, pctl->membase + PIO_POW_MOD_SEL_REG); raw_spin_unlock_irqrestore(&pctl->lock, flags); + return 0; default: return -EINVAL; @ drivers/pinctrl/sunxi/pinctrl-sunxi.c:1029 @ static void sunxi_pinctrl_irq_ack(struct irq_data *d) struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); u32 status_reg = sunxi_irq_status_reg(pctl->desc, d->hwirq); u8 status_idx = sunxi_irq_status_offset(d->hwirq); + u32 new, old; + + old = readl(pctl->membase + status_reg); /* Clear the IRQ */ writel(1 << status_idx, pctl->membase + status_reg); + + new = readl(pctl->membase + status_reg); + + pr_err("acked %ld in 0x%08x, was 0x%08x, now 0x%08x\n", + d->hwirq, status_reg, old, new); } static void sunxi_pinctrl_irq_mask(struct irq_data *d) @ drivers/pinctrl/sunxi/pinctrl-sunxi.h:37 @ #define SUNXI_PIN_NAME_MAX_LEN 5 -#define BANK_MEM_SIZE 0x24 +#define BANK_MEM_SIZE 0x30 #define MUX_REGS_OFFSET 0x0 #define DATA_REGS_OFFSET 0x10 #define DLEVEL_REGS_OFFSET 0x14 -#define PULL_REGS_OFFSET 0x1c +#define PULL_REGS_OFFSET 0x24 #define PINS_PER_BANK 32 #define MUX_PINS_PER_REG 8 @ drivers/pinctrl/sunxi/pinctrl-sunxi.h:50 @ #define DATA_PINS_PER_REG 32 #define DATA_PINS_BITS 1 #define DATA_PINS_MASK 0x01 -#define DLEVEL_PINS_PER_REG 16 -#define DLEVEL_PINS_BITS 2 +#define DLEVEL_PINS_PER_REG 8 +#define DLEVEL_PINS_BITS 4 #define DLEVEL_PINS_MASK 0x03 #define PULL_PINS_PER_REG 16 #define PULL_PINS_BITS 2 @ drivers/pinctrl/sunxi/pinctrl-sunxi.h:87 @ #define IO_BIAS_MASK GENMASK(3, 0) #define SUN4I_FUNC_INPUT 0 -#define SUN4I_FUNC_IRQ 6 +#define SUN4I_FUNC_IRQ 0xe #define PINCTRL_SUN5I_A10S BIT(1) #define PINCTRL_SUN5I_A13 BIT(2) @ drivers/pinctrl/sunxi/pinctrl-sunxi.h:101 @ #define PINCTRL_SUN8I_V3S BIT(10) #define PIO_POW_MOD_SEL_REG 0x340 +#define PIO_POW_MOD_CTL_REG 0x344 enum sunxi_desc_bias_voltage { BIAS_VOLTAGE_NONE, @ drivers/pinctrl/sunxi/pinctrl-sunxi.h:115 @ enum sunxi_desc_bias_voltage { * register, as seen on H6 SoC, for example. */ BIAS_VOLTAGE_PIO_POW_MODE_SEL, + /* + * Bias voltage is set through PIO_POW_MOD_SEL_REG + * and PIO_POW_MOD_CTL_REG register, as seen on + * A100 and D1 SoC, for example. + */ + BIAS_VOLTAGE_PIO_POW_MODE_CTL, }; struct sunxi_desc_function { @ drivers/pwm/Kconfig:575 @ config PWM_SUN4I To compile this driver as a module, choose M here: the module will be called pwm-sun4i. +config PWM_SUN8I_V536 + tristate "Allwinner SUN8I V536 enhanced PWM support" + depends on ARCH_SUNXI || COMPILE_TEST + depends on HAS_IOMEM && COMMON_CLK + help + Enhanced PWM framework driver for Allwinner A133, D1, R329, R818, + V536 and V833 SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-sun8i-v536. + config PWM_TEGRA tristate "NVIDIA Tegra PWM support" depends on ARCH_TEGRA || COMPILE_TEST @ drivers/pwm/Makefile:56 @ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o +obj-$(CONFIG_PWM_SUN8I_V536) += pwm-sun8i-v536.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o @ drivers/pwm/pwm-sun8i-v536.c:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Allwinner sun8i-v536 Pulse Width Modulation Controller + * + * Copyright (C) 2021 Ban Tao <fengzheng923@gmail.com> + * + * + * Limitations: + * - When PWM is disabled, the output is driven to inactive. + * - If the register is reconfigured while PWM is running, + * it does not complete the currently running period. + * - If the user input duty is beyond acceptible limits, + * -EINVAL is returned. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pwm.h> +#include <linux/clk.h> +#include <linux/reset.h> + +#define PWM_GET_CLK_OFFSET(chan) (0x20 + ((chan >> 1) * 0x4)) +#define PWM_CLK_APB_SCR BIT(7) +#define PWM_DIV_M 0 +#define PWM_DIV_M_MASK GENMASK(3, PWM_DIV_M) + +#define PWM_CLK_REG 0x40 +#define PWM_CLK_GATING BIT(0) + +#define PWM_ENABLE_REG 0x80 +#define PWM_EN BIT(0) + +#define PWM_CTL_REG(chan) (0x100 + 0x20 * chan) +#define PWM_ACT_STA BIT(8) +#define PWM_PRESCAL_K 0 +#define PWM_PRESCAL_K_MASK GENMASK(7, PWM_PRESCAL_K) + +#define PWM_PERIOD_REG(chan) (0x104 + 0x20 * chan) +#define PWM_ENTIRE_CYCLE 16 +#define PWM_ENTIRE_CYCLE_MASK GENMASK(31, PWM_ENTIRE_CYCLE) +#define PWM_ACT_CYCLE 0 +#define PWM_ACT_CYCLE_MASK GENMASK(15, PWM_ACT_CYCLE) + +#define BIT_CH(bit, chan) ((bit) << (chan)) +#define SET_BITS(shift, mask, reg, val) \ + (((reg) & ~mask) | (val << (shift))) + +#define PWM_OSC_CLK 24000000 +#define PWM_PRESCALER_MAX 256 +#define PWM_CLK_DIV_M__MAX 9 +#define PWM_ENTIRE_CYCLE_MAX 65536 + +struct sun8i_pwm_data { + unsigned int npwm; +}; + +struct sun8i_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + struct reset_control *rst_clk; + void __iomem *base; + const struct sun8i_pwm_data *data; +}; + +static inline struct sun8i_pwm_chip *to_sun8i_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct sun8i_pwm_chip, chip); +} + +static inline u32 sun8i_pwm_readl(struct sun8i_pwm_chip *chip, + unsigned long offset) +{ + return readl(chip->base + offset); +} + +static inline void sun8i_pwm_writel(struct sun8i_pwm_chip *chip, + u32 val, unsigned long offset) +{ + writel(val, chip->base + offset); +} + +static void sun8i_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) +{ + struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip); + u64 clk_rate; + u32 tmp, entire_cycles, active_cycles; + unsigned int prescaler, div_m; + + tmp = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + if (tmp & PWM_CLK_APB_SCR) + clk_rate = clk_get_rate(pc->clk); + else + clk_rate = PWM_OSC_CLK; + + tmp = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + div_m = 0x1 << (tmp & PWM_DIV_M_MASK); + + tmp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm)); + prescaler = (tmp & PWM_PRESCAL_K_MASK) + 1; + + tmp = sun8i_pwm_readl(pc, PWM_PERIOD_REG(pwm->hwpwm)); + entire_cycles = (tmp >> PWM_ENTIRE_CYCLE) + 1; + active_cycles = (tmp & PWM_ACT_CYCLE_MASK); + + /* (clk / div_m / prescaler) / entire_cycles = NSEC_PER_SEC / period_ns. */ + state->period = DIV_ROUND_CLOSEST_ULL(entire_cycles * NSEC_PER_SEC, + clk_rate) * div_m * prescaler; + /* duty_ns / period_ns = active_cycles / entire_cycles. */ + state->duty_cycle = DIV_ROUND_CLOSEST_ULL(active_cycles * state->period, + entire_cycles); + + /* parsing polarity */ + tmp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm)); + if (tmp & PWM_ACT_STA) + state->polarity = PWM_POLARITY_NORMAL; + else + state->polarity = PWM_POLARITY_INVERSED; + + /* parsing enabled */ + tmp = sun8i_pwm_readl(pc, PWM_ENABLE_REG); + if (tmp & BIT_CH(PWM_EN, pwm->hwpwm)) + state->enabled = true; + else + state->enabled = false; + + dev_dbg(chip->dev, "duty_ns=%lld period_ns=%lld polarity=%s enabled=%s.\n", + state->duty_cycle, state->period, + state->polarity ? "inversed":"normal", + state->enabled ? "true":"false"); +} + +static void sun8i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip); + u32 temp; + + temp = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm)); + + if (polarity == PWM_POLARITY_NORMAL) + temp |= PWM_ACT_STA; + else + temp &= ~PWM_ACT_STA; + + sun8i_pwm_writel(pc, temp, PWM_CTL_REG(pwm->hwpwm)); +} + +static int sun8i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip); + unsigned long long c; + unsigned long entire_cycles, active_cycles; + unsigned int div_m, prescaler; + u64 duty_ns = state->duty_cycle, period_ns = state->period; + u32 config; + int ret = 0; + + if (period_ns > 334) { + /* if freq < 3M, then select 24M clock */ + c = PWM_OSC_CLK; + config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + config &= ~PWM_CLK_APB_SCR; + sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + } else { + /* if freq > 3M, then select APB as clock */ + c = clk_get_rate(pc->clk); + config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + config |= PWM_CLK_APB_SCR; + sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + } + + dev_dbg(chip->dev, "duty_ns=%lld period_ns=%lld c =%llu.\n", + duty_ns, period_ns, c); + + /* + * (clk / div_m / prescaler) / entire_cycles = NSEC_PER_SEC / period_ns. + * So, entire_cycles = clk * period_ns / NSEC_PER_SEC / div_m / prescaler. + */ + c = c * period_ns; + c = DIV_ROUND_CLOSEST_ULL(c, NSEC_PER_SEC); + for (div_m = 0; div_m < PWM_CLK_DIV_M__MAX; div_m++) { + for (prescaler = 0; prescaler < PWM_PRESCALER_MAX; prescaler++) { + /* + * actual prescaler = prescaler(reg value) + 1. + * actual div_m = 0x1 << div_m(reg value). + */ + entire_cycles = ((unsigned long)c >> div_m)/(prescaler + 1); + if (entire_cycles <= PWM_ENTIRE_CYCLE_MAX) + goto calc_end; + } + } + ret = -EINVAL; + goto exit; + +calc_end: + /* + * duty_ns / period_ns = active_cycles / entire_cycles. + * So, active_cycles = entire_cycles * duty_ns / period_ns. + */ + c = (unsigned long long)entire_cycles * duty_ns; + c = DIV_ROUND_CLOSEST_ULL(c, period_ns); + active_cycles = c; + if (entire_cycles == 0) + entire_cycles++; + + /* config clk div_m*/ + config = sun8i_pwm_readl(pc, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + config = SET_BITS(PWM_DIV_M, PWM_DIV_M_MASK, config, div_m); + sun8i_pwm_writel(pc, config, PWM_GET_CLK_OFFSET(pwm->hwpwm)); + + /* config prescaler */ + config = sun8i_pwm_readl(pc, PWM_CTL_REG(pwm->hwpwm)); + config = SET_BITS(PWM_PRESCAL_K, PWM_PRESCAL_K_MASK, config, prescaler); + sun8i_pwm_writel(pc, config, PWM_CTL_REG(pwm->hwpwm)); + + /* config active and period cycles */ + config = sun8i_pwm_readl(pc, PWM_PERIOD_REG(pwm->hwpwm)); + config = SET_BITS(PWM_ACT_CYCLE, PWM_ACT_CYCLE_MASK, config, active_cycles); + config = SET_BITS(PWM_ENTIRE_CYCLE, PWM_ENTIRE_CYCLE_MASK, + config, (entire_cycles - 1)); + sun8i_pwm_writel(pc, config, PWM_PERIOD_REG(pwm->hwpwm)); + + dev_dbg(chip->dev, "active_cycles=%lu entire_cycles=%lu prescaler=%u div_m=%u\n", + active_cycles, entire_cycles, prescaler, div_m); + +exit: + return ret; +} + +static void sun8i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, + bool enable) +{ + struct sun8i_pwm_chip *pc = to_sun8i_pwm_chip(chip); + u32 clk, pwm_en; + + clk = sun8i_pwm_readl(pc, PWM_CLK_REG); + pwm_en = sun8i_pwm_readl(pc, PWM_ENABLE_REG); + + if (enable) { + clk |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + sun8i_pwm_writel(pc, clk, PWM_CLK_REG); + + pwm_en |= BIT_CH(PWM_EN, pwm->hwpwm); + sun8i_pwm_writel(pc, pwm_en, PWM_ENABLE_REG); + } else { + pwm_en &= ~BIT_CH(PWM_EN, pwm->hwpwm); + sun8i_pwm_writel(pc, pwm_en, PWM_ENABLE_REG); + + clk &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + sun8i_pwm_writel(pc, clk, PWM_CLK_REG); + } +} + +static int sun8i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct pwm_state curstate; + int ret; + + pwm_get_state(pwm, &curstate); + + ret = sun8i_pwm_config(chip, pwm, state); + + if (state->polarity != curstate.polarity) + sun8i_pwm_set_polarity(chip, pwm, state->polarity); + + if (state->enabled != curstate.enabled) + sun8i_pwm_enable(chip, pwm, state->enabled); + + return ret; +} + +static const struct pwm_ops sun8i_pwm_ops = { + .get_state = sun8i_pwm_get_state, + .apply = sun8i_pwm_apply, + .owner = THIS_MODULE, +}; + +static const struct sun8i_pwm_data sun8i_pwm_data_c9 = { + .npwm = 9, +}; + +static const struct sun8i_pwm_data sun20i_pwm_data_c8 = { + .npwm = 8, +}; + +static const struct sun8i_pwm_data sun50i_pwm_data_c16 = { + .npwm = 16, +}; + +static const struct of_device_id sun8i_pwm_dt_ids[] = { + { + .compatible = "allwinner,sun8i-v536-pwm", + .data = &sun8i_pwm_data_c9, + }, { + .compatible = "allwinner,sun20i-d1-pwm", + .data = &sun20i_pwm_data_c8, + }, { + .compatible = "allwinner,sun50i-r818-pwm", + .data = &sun50i_pwm_data_c16, + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, sun8i_pwm_dt_ids); + +static int sun8i_pwm_probe(struct platform_device *pdev) +{ + struct sun8i_pwm_chip *pc; + int ret; + + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (!pc) + return dev_err_probe(&pdev->dev, -ENOMEM, + "memory allocation failed\n"); + + pc->data = of_device_get_match_data(&pdev->dev); + if (!pc->data) + return dev_err_probe(&pdev->dev, -ENODEV, + "can't get match data\n"); + + pc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pc->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(pc->base), + "can't remap pwm resource\n"); + + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk), + "get clock failed\n"); + + pc->rst_clk = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(pc->rst_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pc->rst_clk), + "get reset failed\n"); + + /* Deassert reset */ + ret = reset_control_deassert(pc->rst_clk); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "cannot deassert reset control\n"); + + ret = clk_prepare_enable(pc->clk); + if (ret) { + dev_err(&pdev->dev, "cannot prepare and enable clk %pe\n", + ERR_PTR(ret)); + goto err_clk; + } + + pc->chip.dev = &pdev->dev; + pc->chip.ops = &sun8i_pwm_ops; + pc->chip.npwm = pc->data->npwm; + pc->chip.of_xlate = of_pwm_xlate_with_flags; + pc->chip.base = -1; + pc->chip.of_pwm_n_cells = 3; + + ret = pwmchip_add(&pc->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + goto err_pwm_add; + } + + platform_set_drvdata(pdev, pc); + + return 0; + +err_pwm_add: + clk_disable_unprepare(pc->clk); +err_clk: + reset_control_assert(pc->rst_clk); + + return ret; +} + +static int sun8i_pwm_remove(struct platform_device *pdev) +{ + struct sun8i_pwm_chip *pc = platform_get_drvdata(pdev); + + pwmchip_remove(&pc->chip); + clk_disable_unprepare(pc->clk); + reset_control_assert(pc->rst_clk); + + return 0; +} + +static struct platform_driver sun8i_pwm_driver = { + .driver = { + .name = "sun8i-pwm-v536", + .of_match_table = sun8i_pwm_dt_ids, + }, + .probe = sun8i_pwm_probe, + .remove = sun8i_pwm_remove, +}; +module_platform_driver(sun8i_pwm_driver); + +MODULE_ALIAS("platform:sun8i-v536-pwm"); +MODULE_AUTHOR("Ban Tao <fengzheng923@gmail.com>"); +MODULE_DESCRIPTION("Allwinner sun8i-v536 PWM driver"); +MODULE_LICENSE("GPL v2"); @ drivers/remoteproc/Kconfig:314 @ config STM32_RPROC This can be either built-in or a loadable module. +config SUN8I_DSP_REMOTEPROC + tristate "Allwinner sun8i DSP remoteproc support" + depends on ARCH_SUNXI || COMPILE_TEST + help + Say y here to support the DSP in some sun8i/sun20i/sun50i SoCs + via the remote processor framework. + config TI_K3_DSP_REMOTEPROC tristate "TI K3 DSP remoteproc support" depends on ARCH_K3 @ drivers/remoteproc/Makefile:38 @ qcom_wcnss_pil-y += qcom_wcnss_iris.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_SUN8I_DSP_REMOTEPROC) += sun8i_dsp_rproc.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o @ drivers/remoteproc/sun8i_dsp_rproc.c:4 @ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2021 Samuel Holland <samuel@sholland.org> + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/remoteproc.h> +#include <linux/reset.h> +#include <linux/soc/sunxi/sunxi_sram.h> + +#include "remoteproc_internal.h" + +#define SUN8I_DSP_RESET_VEC_REG 0x0000 +#define SUN8I_DSP_CTRL_REG0 0x0004 +#define SUN8I_DSP_CTRL_REG0_RUN_STALL BIT(0) +#define SUN8I_DSP_CTRL_REG0_RESET_VEC_SEL BIT(1) +#define SUN8I_DSP_CTRL_REG0_DSP_CLKEN BIT(2) +#define SUN8I_DSP_CTRL_REG1 0x0008 +#define SUN8I_DSP_PRID_REG 0x000c +#define SUN8I_DSP_PRID_REG_PRID_MASK (0xff << 0) +#define SUN8I_DSP_STAT_REG 0x0010 +#define SUN8I_DSP_STAT_REG_PFAULT_INFO_VALID BIT(0) +#define SUN8I_DSP_STAT_REG_PFAULT_ERROR BIT(1) +#define SUN8I_DSP_STAT_REG_DOUBLE_EXCE_ERROR BIT(2) +#define SUN8I_DSP_STAT_REG_XOCD_MODE BIT(3) +#define SUN8I_DSP_STAT_REG_DEBUG_MODE BIT(4) +#define SUN8I_DSP_STAT_REG_PWAIT_MODE BIT(5) +#define SUN8I_DSP_STAT_REG_IRAM0_LOAD_STORE BIT(6) +#define SUN8I_DSP_BIST_CTRL_REG 0x0014 +#define SUN8I_DSP_BIST_CTRL_REG_EN BIT(0) +#define SUN8I_DSP_BIST_CTRL_REG_WDATA_PAT_MASK (0x7 << 1) +#define SUN8I_DSP_BIST_CTRL_REG_ADDR_MODE_SEL BIT(4) +#define SUN8I_DSP_BIST_CTRL_REG_REG_SEL_MASK (0x7 << 5) +#define SUN8I_DSP_BIST_CTRL_REG_BUSY BIT(8) +#define SUN8I_DSP_BIST_CTRL_REG_STOP BIT(9) +#define SUN8I_DSP_BIST_CTRL_REG_ERR_CYC_MASK (0x3 << 10) +#define SUN8I_DSP_BIST_CTRL_REG_ERR_PAT_MASK (0x7 << 12) +#define SUN8I_DSP_BIST_CTRL_REG_ERR_STA BIT(15) +#define SUN8I_DSP_BIST_CTRL_REG_SELECT_MASK (0xf << 16) +#define SUN8I_DSP_JTRST_REG 0x001c +#define SUN8I_DSP_VER_REG 0x0020 +#define SUN8I_DSP_VER_REG_MINOR_VER_MASK (0x1f << 0) +#define SUN8I_DSP_VER_REG_MAJOR_VER_MASK (0x1f << 16) + +#define SUN8I_DSP_CLK_FREQ 400000000 + +struct sun8i_dsp_rproc { + void __iomem *cfg_base; + struct clk *cfg_clk; + struct reset_control *cfg_reset; + struct reset_control *dbg_reset; + struct clk *dsp_clk; + struct reset_control *dsp_reset; + struct mbox_client client; + struct mbox_chan *rx_chan; + struct mbox_chan *tx_chan; +}; + +static int sun8i_dsp_rproc_start(struct rproc *rproc) +{ + struct sun8i_dsp_rproc *dsp = rproc->priv; + int ret; + u32 val; + + ret = sunxi_sram_claim(rproc->dev.parent); + if (ret) + return ret; + + ret = clk_prepare_enable(dsp->cfg_clk); + if (ret) + goto err_release_sram; + + ret = reset_control_deassert(dsp->cfg_reset); + if (ret) + goto err_gate_cfg; + + ret = reset_control_deassert(dsp->dbg_reset); + if (ret) + goto err_reset_cfg; + + writel(rproc->bootaddr, dsp->cfg_base + SUN8I_DSP_RESET_VEC_REG); + + val = readl(dsp->cfg_base + SUN8I_DSP_CTRL_REG0); + val |= SUN8I_DSP_CTRL_REG0_RESET_VEC_SEL | + SUN8I_DSP_CTRL_REG0_RUN_STALL; + writel(val, dsp->cfg_base + SUN8I_DSP_CTRL_REG0); + + ret = clk_prepare_enable(dsp->dsp_clk); + if (ret) + goto err_reset_dbg; + + ret = reset_control_deassert(dsp->dsp_reset); + if (ret) + goto err_gate_dsp; + + val &= ~SUN8I_DSP_CTRL_REG0_RUN_STALL; + writel(val, dsp->cfg_base + SUN8I_DSP_CTRL_REG0); + + return 0; + +err_gate_dsp: + clk_disable_unprepare(dsp->dsp_clk); +err_reset_dbg: + reset_control_assert(dsp->dbg_reset); +err_reset_cfg: + reset_control_assert(dsp->cfg_reset); +err_gate_cfg: + clk_disable_unprepare(dsp->cfg_clk); +err_release_sram: + sunxi_sram_release(rproc->dev.parent); + + return ret; +} + +static int sun8i_dsp_rproc_stop(struct rproc *rproc) +{ + struct sun8i_dsp_rproc *dsp = rproc->priv; + + reset_control_assert(dsp->dsp_reset); + clk_disable_unprepare(dsp->dsp_clk); + reset_control_assert(dsp->dbg_reset); + reset_control_assert(dsp->cfg_reset); + clk_disable_unprepare(dsp->cfg_clk); + sunxi_sram_release(rproc->dev.parent); + + return 0; +} + +static void sun8i_dsp_rproc_kick(struct rproc *rproc, int vqid) +{ + struct sun8i_dsp_rproc *dsp = rproc->priv; + long msg = vqid; + int ret; + + ret = mbox_send_message(dsp->tx_chan, (void *)msg); + if (ret) + dev_warn(&rproc->dev, "Failed to kick: %d\n", ret); +} + +static const struct rproc_ops sun8i_dsp_rproc_ops = { + .start = sun8i_dsp_rproc_start, + .stop = sun8i_dsp_rproc_stop, + .kick = sun8i_dsp_rproc_kick, +}; + +static void sun8i_dsp_rproc_mbox_rx_callback(struct mbox_client *client, void *msg) +{ + struct rproc *rproc = dev_get_drvdata(client->dev); + + rproc_vq_interrupt(rproc, (long)msg); +} + +static void sun8i_dsp_rproc_mbox_free(void *data) +{ + struct sun8i_dsp_rproc *dsp = data; + + if (!IS_ERR_OR_NULL(dsp->tx_chan)) + mbox_free_channel(dsp->tx_chan); + if (!IS_ERR_OR_NULL(dsp->rx_chan)) + mbox_free_channel(dsp->rx_chan); +} + +static int sun8i_dsp_rproc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct sun8i_dsp_rproc *dsp; + const char *firmware_name; + struct rproc *rproc; + int i, ret; + u32 freq; + + firmware_name = NULL; + of_property_read_string(np, "firmware-name", &firmware_name); + rproc = devm_rproc_alloc(dev, dev_name(dev), &sun8i_dsp_rproc_ops, + firmware_name, sizeof(struct sun8i_dsp_rproc)); + if (!rproc) + return -ENOMEM; + + dev_set_drvdata(dev, rproc); + dsp = rproc->priv; + + i = of_property_match_string(np, "reg-names", "cfg"); + if (i < 0) + return -EINVAL; + + dsp->cfg_base = devm_platform_ioremap_resource(pdev, i); + if (IS_ERR(dsp->cfg_base)) + return dev_err_probe(dev, PTR_ERR(dsp->cfg_base), + "Failed to map cfg\n"); + + dsp->cfg_clk = devm_clk_get(dev, "cfg"); + if (IS_ERR(dsp->cfg_clk)) + return dev_err_probe(dev, PTR_ERR(dsp->cfg_clk), + "Failed to get %s clock\n", "cfg"); + + dsp->cfg_reset = devm_reset_control_get_exclusive(dev, "cfg"); + if (IS_ERR(dsp->cfg_reset)) + return dev_err_probe(dev, PTR_ERR(dsp->cfg_reset), + "Failed to get %s reset\n", "cfg"); + + dsp->cfg_reset = devm_reset_control_get_exclusive(dev, "dbg"); + if (IS_ERR(dsp->cfg_reset)) + return dev_err_probe(dev, PTR_ERR(dsp->cfg_reset), + "Failed to get %s reset\n", "dbg"); + + dsp->dsp_clk = devm_clk_get(dev, "dsp"); + if (IS_ERR(dsp->dsp_clk)) + return dev_err_probe(dev, PTR_ERR(dsp->dsp_clk), + "Failed to get %s clock\n", "dsp"); + + freq = SUN8I_DSP_CLK_FREQ; + of_property_read_u32(np, "clock-frequency", &freq); + ret = clk_set_rate(dsp->dsp_clk, freq); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set clock frequency\n"); + + dsp->dsp_reset = devm_reset_control_get_exclusive(dev, "dsp"); + if (IS_ERR(dsp->dsp_reset)) + return dev_err_probe(dev, PTR_ERR(dsp->dsp_reset), + "Failed to get %s reset\n", "dsp"); + + dsp->client.dev = dev; + dsp->client.rx_callback = sun8i_dsp_rproc_mbox_rx_callback; + + ret = devm_add_action(dev, sun8i_dsp_rproc_mbox_free, dsp); + if (ret) + return ret; + + dsp->rx_chan = mbox_request_channel_byname(&dsp->client, "rx"); + if (IS_ERR(dsp->rx_chan)) + return dev_err_probe(dev, PTR_ERR(dsp->rx_chan), + "Failed to request RX channel\n"); + + dsp->tx_chan = mbox_request_channel_byname(&dsp->client, "tx"); + if (IS_ERR(dsp->tx_chan)) + return dev_err_probe(dev, PTR_ERR(dsp->tx_chan), + "Failed to request TX channel\n"); + + ret = devm_rproc_add(dev, rproc); + if (ret) + return dev_err_probe(dev, ret, "Failed to register rproc\n"); + + return 0; +} + +static const struct of_device_id sun8i_dsp_rproc_of_match[] = { + { .compatible = "allwinner,sun20i-d1-dsp" }, + {} +}; +MODULE_DEVICE_TABLE(of, sun8i_dsp_rproc_of_match); + +static struct platform_driver sun8i_dsp_rproc_driver = { + .probe = sun8i_dsp_rproc_probe, + .driver = { + .name = "sun8i-dsp-rproc", + .of_match_table = sun8i_dsp_rproc_of_match, + }, +}; +module_platform_driver(sun8i_dsp_rproc_driver); + +MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); +MODULE_DESCRIPTION("Allwinner sun8i DSP remoteproc driver"); +MODULE_LICENSE("GPL"); @ drivers/rtc/rtc-sun6i.c:16 @ #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/clk/sunxi-ng.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/fs.h> @ drivers/rtc/rtc-sun6i.c:137 @ struct sun6i_rtc_clk_data { unsigned int has_auto_swt : 1; }; +#define RTC_LINEAR_DAY BIT(0) + struct sun6i_rtc_dev { struct rtc_device *rtc; const struct sun6i_rtc_clk_data *data; void __iomem *base; int irq; unsigned long alarm; + unsigned long flags; struct clk_hw hw; struct clk_hw *int_osc; @ drivers/rtc/rtc-sun6i.c:370 @ CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc", CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc", sun8i_h3_rtc_clk_init); -static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = { - .rc_osc_rate = 16000000, - .fixed_prescaler = 32, - .has_prescaler = 1, - .has_out_clk = 1, - .export_iosc = 1, - .has_losc_en = 1, - .has_auto_swt = 1, -}; - -static void __init sun50i_h6_rtc_clk_init(struct device_node *node) -{ - sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data); -} -CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc", - sun50i_h6_rtc_clk_init); - /* * The R40 user manual is self-conflicting on whether the prescaler is * fixed or configurable. The clock diagram shows it as fixed, but there @ drivers/rtc/rtc-sun6i.c:461 @ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time); rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time); - rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); - rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date); - rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); - - rtc_tm->tm_mon -= 1; - - /* - * switch from (data_year->min)-relative offset to - * a (1900)-relative one - */ - rtc_tm->tm_year += SUN6I_YEAR_OFF; + if (chip->flags & RTC_LINEAR_DAY) { + struct tm tm; + + /* + * Newer chips store a linear day number, the manual + * does not mandate any epoch base. The BSP driver uses + * the UNIX epoch, let's just copy that, as it's the + * easiest anyway. + */ + time64_to_tm((date & 0xffff) * 3600ULL * 24, 0, &tm); + rtc_tm->tm_mday = tm.tm_mday; + rtc_tm->tm_mon = tm.tm_mon; + rtc_tm->tm_year = tm.tm_year; + } else { + rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); + rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date) - 1; + rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); + + /* + * switch from (data_year->min)-relative offset to + * a (1900)-relative one + */ + rtc_tm->tm_year += SUN6I_YEAR_OFF; + } return 0; } @ drivers/rtc/rtc-sun6i.c:574 @ static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) u32 date = 0; u32 time = 0; - rtc_tm->tm_year -= SUN6I_YEAR_OFF; rtc_tm->tm_mon += 1; - date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | - SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | - SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year); + if (chip->flags & RTC_LINEAR_DAY) { + date = mktime64(rtc_tm->tm_year + 1900, rtc_tm->tm_mon, + rtc_tm->tm_mday, 0, 0, 0) / (3600ULL * 24); + } else { + rtc_tm->tm_year -= SUN6I_YEAR_OFF; + + date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | + SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | + SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year); - if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN)) - date |= SUN6I_LEAP_SET_VALUE(1); + if (is_leap_year(rtc_tm->tm_year + SUN6I_YEAR_MIN)) + date |= SUN6I_LEAP_SET_VALUE(1); + } time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | @ drivers/rtc/rtc-sun6i.c:692 @ static int sun6i_rtc_probe(struct platform_device *pdev) chip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(chip->base)) return PTR_ERR(chip->base); + + if (IS_REACHABLE(CONFIG_SUN6I_RTC_CCU)) { + ret = sun6i_rtc_ccu_probe(&pdev->dev, chip->base); + if (ret) + return ret; + } } platform_set_drvdata(pdev, chip); + chip->flags = (unsigned long)of_device_get_match_data(&pdev->dev); + chip->irq = platform_get_irq(pdev, 0); if (chip->irq < 0) return chip->irq; @ drivers/rtc/rtc-sun6i.c:775 @ static const struct of_device_id sun6i_rtc_dt_ids[] = { { .compatible = "allwinner,sun8i-v3-rtc" }, { .compatible = "allwinner,sun50i-h5-rtc" }, { .compatible = "allwinner,sun50i-h6-rtc" }, + { .compatible = "allwinner,sun50i-h616-rtc", + .data = (void *)RTC_LINEAR_DAY }, + { .compatible = "allwinner,sun20i-d1-rtc", + .data = (void *)RTC_LINEAR_DAY }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); @ drivers/soc/sunxi/Kconfig:8 @ config SUNXI_MBUS bool - default ARCH_SUNXI + default ARCH_SUNXI && !RISCV help Say y to enable the fixups needed to support the Allwinner MBUS DMA quirks. @ drivers/soc/sunxi/sunxi_sram.c:21 @ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/regulator/driver.h> #include <linux/soc/sunxi/sunxi_sram.h> +#define SUNXI_SRAM_EMAC_CLOCK_REG 0x30 +#define SUNXI_SYS_LDO_CTRL_REG 0x150 + struct sunxi_sram_func { char *func; u8 val; @ drivers/soc/sunxi/sunxi_sram.c:83 @ static struct sunxi_sram_desc sun4i_a10_sram_d = { SUNXI_SRAM_MAP(1, 1, "usb-otg")), }; +static struct sunxi_sram_desc sun20i_d1_dsp_sram = { + .data = SUNXI_SRAM_DATA("DSP", 0x8, 0, 1, + SUNXI_SRAM_MAP(1, 0, "cpu"), + SUNXI_SRAM_MAP(0, 1, "dsp")), +}; + static struct sunxi_sram_desc sun50i_a64_sram_c = { .data = SUNXI_SRAM_DATA("C", 0x4, 24, 1, - SUNXI_SRAM_MAP(0, 1, "cpu"), - SUNXI_SRAM_MAP(1, 0, "de2")), + SUNXI_SRAM_MAP(1, 0, "cpu"), + SUNXI_SRAM_MAP(0, 1, "de2")), }; static const struct of_device_id sunxi_sram_dt_ids[] = { @ drivers/soc/sunxi/sunxi_sram.c:108 @ static const struct of_device_id sunxi_sram_dt_ids[] = { .compatible = "allwinner,sun4i-a10-sram-d", .data = &sun4i_a10_sram_d.data, }, + { + .compatible = "allwinner,sun20i-d1-dsp-sram", + .data = &sun20i_d1_dsp_sram.data, + }, { .compatible = "allwinner,sun50i-a64-sram-c", .data = &sun50i_a64_sram_c.data, @ drivers/soc/sunxi/sunxi_sram.c:123 @ static struct device *sram_dev; static LIST_HEAD(claimed_sram); static DEFINE_SPINLOCK(sram_lock); static void __iomem *base; +static struct dentry *debugfs_dir; static int sunxi_sram_show(struct seq_file *s, void *data) { @ drivers/soc/sunxi/sunxi_sram.c:272 @ int sunxi_sram_claim(struct device *dev) writel(val | ((device << sram_data->offset) & mask), base + sram_data->reg); + sram_desc->claimed = true; spin_unlock(&sram_lock); return 0; @ drivers/soc/sunxi/sunxi_sram.c:283 @ int sunxi_sram_release(struct device *dev) { const struct sunxi_sram_data *sram_data; struct sunxi_sram_desc *sram_desc; + unsigned int device; + u32 val, mask; if (!dev || !dev->of_node) return -EINVAL; - sram_data = sunxi_sram_of_parse(dev->of_node, NULL); + sram_data = sunxi_sram_of_parse(dev->of_node, &device); if (IS_ERR(sram_data)) return -EINVAL; sram_desc = to_sram_desc(sram_data); spin_lock(&sram_lock); + mask = GENMASK(sram_data->offset + sram_data->width - 1, + sram_data->offset); + val = readl(base + sram_data->reg); + val &= ~mask; + writel(val | ((~device << sram_data->offset) & mask), + base + sram_data->reg); + sram_desc->claimed = false; spin_unlock(&sram_lock); @ drivers/soc/sunxi/sunxi_sram.c:310 @ int sunxi_sram_release(struct device *dev) } EXPORT_SYMBOL(sunxi_sram_release); +static const struct regulator_ops sunxi_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_desc sun20i_d1_ldos[] = { + { + .name = "ldoa", + .supply_name = "ldo-in", + .of_match = "ldoa", + .regulators_node = "regulators", + .ops = &sunxi_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = BIT(5), + .min_uV = 1600005, /* nominally 1.8 V */ + .uV_step = 13333, + .vsel_reg = SUNXI_SYS_LDO_CTRL_REG, + .vsel_mask = GENMASK(7, 0), + }, + { + .name = "ldob", + .supply_name = "ldo-in", + .of_match = "ldob", + .regulators_node = "regulators", + .ops = &sunxi_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = BIT(6), + .min_uV = 1166675, /* nominally 1.5 V */ + .uV_step = 13333, + .vsel_reg = SUNXI_SYS_LDO_CTRL_REG, + .vsel_mask = GENMASK(15, 8), + }, +}; + struct sunxi_sramc_variant { + const struct regulator_desc *ldos; + int num_ldos; int num_emac_clocks; }; @ drivers/soc/sunxi/sunxi_sram.c:362 @ static const struct sunxi_sramc_variant sun8i_h3_sramc_variant = { .num_emac_clocks = 1, }; +static const struct sunxi_sramc_variant sun20i_d1_sramc_variant = { + .ldos = sun20i_d1_ldos, + .num_ldos = ARRAY_SIZE(sun20i_d1_ldos), + .num_emac_clocks = 1, +}; + static const struct sunxi_sramc_variant sun50i_a64_sramc_variant = { .num_emac_clocks = 1, }; @ drivers/soc/sunxi/sunxi_sram.c:376 @ static const struct sunxi_sramc_variant sun50i_h616_sramc_variant = { .num_emac_clocks = 2, }; -#define SUNXI_SRAM_EMAC_CLOCK_REG 0x30 static bool sunxi_sram_regmap_accessible_reg(struct device *dev, unsigned int reg) { @ drivers/soc/sunxi/sunxi_sram.c:383 @ static bool sunxi_sram_regmap_accessible_reg(struct device *dev, variant = of_device_get_match_data(dev); + if (reg == SUNXI_SYS_LDO_CTRL_REG) + return true; if (reg < SUNXI_SRAM_EMAC_CLOCK_REG) return false; if (reg > SUNXI_SRAM_EMAC_CLOCK_REG + variant->num_emac_clocks * 4) @ drivers/soc/sunxi/sunxi_sram.c:398 @ static struct regmap_config sunxi_sram_emac_clock_regmap = { .val_bits = 32, .reg_stride = 4, /* last defined register */ - .max_register = SUNXI_SRAM_EMAC_CLOCK_REG + 4, + .max_register = SUNXI_SYS_LDO_CTRL_REG, /* other devices have no business accessing other registers */ .readable_reg = sunxi_sram_regmap_accessible_reg, .writeable_reg = sunxi_sram_regmap_accessible_reg, @ drivers/soc/sunxi/sunxi_sram.c:406 @ static struct regmap_config sunxi_sram_emac_clock_regmap = { static int sunxi_sram_probe(struct platform_device *pdev) { - struct dentry *d; struct regmap *emac_clock; const struct sunxi_sramc_variant *variant; + int ret; sram_dev = &pdev->dev; @ drivers/soc/sunxi/sunxi_sram.c:420 @ static int sunxi_sram_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); - - d = debugfs_create_file("sram", S_IRUGO, NULL, NULL, - &sunxi_sram_fops); - if (!d) - return -ENOMEM; - - if (variant->num_emac_clocks > 0) { + if (variant->num_ldos || variant->num_emac_clocks) { emac_clock = devm_regmap_init_mmio(&pdev->dev, base, &sunxi_sram_emac_clock_regmap); @ drivers/soc/sunxi/sunxi_sram.c:428 @ static int sunxi_sram_probe(struct platform_device *pdev) return PTR_ERR(emac_clock); } + if (variant->num_ldos) { + struct regulator_config config = { .dev = &pdev->dev }; + struct regulator_dev *rdev; + int i; + + for (i = 0; i < variant->num_ldos; ++i) { + const struct regulator_desc *desc = &variant->ldos[i]; + + rdev = devm_regulator_register(&pdev->dev, desc, &config); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + } + } + + ret = devm_of_platform_populate(&pdev->dev); + if (ret) + return ret; + + debugfs_dir = debugfs_create_dir("sunxi-sram", NULL); + debugfs_create_file("sram", S_IRUGO, debugfs_dir, NULL, + &sunxi_sram_fops); + + return 0; +} + +static int sunxi_sram_remove(struct platform_device *pdev) +{ + debugfs_remove(debugfs_dir); + return 0; } @ drivers/soc/sunxi/sunxi_sram.c:481 @ static const struct of_device_id sunxi_sram_dt_match[] = { .compatible = "allwinner,sun8i-h3-system-control", .data = &sun8i_h3_sramc_variant, }, + { + .compatible = "allwinner,sun20i-d1-system-control", + .data = &sun20i_d1_sramc_variant, + }, { .compatible = "allwinner,sun50i-a64-sram-controller", .data = &sun50i_a64_sramc_variant, @ drivers/soc/sunxi/sunxi_sram.c:511 @ static struct platform_driver sunxi_sram_driver = { .of_match_table = sunxi_sram_dt_match, }, .probe = sunxi_sram_probe, + .remove = sunxi_sram_remove, }; module_platform_driver(sunxi_sram_driver); @ drivers/spi/spi-sun6i.c:33 @ #define SUN6I_GBL_CTL_REG 0x04 #define SUN6I_GBL_CTL_BUS_ENABLE BIT(0) #define SUN6I_GBL_CTL_MASTER BIT(1) +#define SUN6I_GBL_CTL_SAMPLE_MODE BIT(2) #define SUN6I_GBL_CTL_TP BIT(7) #define SUN6I_GBL_CTL_RST BIT(31) @ drivers/spi/spi-sun6i.c:85 @ #define SUN6I_XMIT_CNT_REG 0x34 #define SUN6I_BURST_CTL_CNT_REG 0x38 +#define SUN6I_BURST_CTL_CNT_QUAD_EN BIT(29) +#define SUN6I_BURST_CTL_CNT_DUAL_EN BIT(28) #define SUN6I_TXDATA_REG 0x200 #define SUN6I_RXDATA_REG 0x300 +struct sun6i_spi_quirks { + unsigned long fifo_depth; + bool has_divider : 1; + bool has_new_sample_mode : 1; +}; + struct sun6i_spi { + const struct sun6i_spi_quirks *quirks; struct spi_master *master; void __iomem *base_addr; dma_addr_t dma_addr_rx; @ drivers/spi/spi-sun6i.c:112 @ struct sun6i_spi { const u8 *tx_buf; u8 *rx_buf; int len; - unsigned long fifo_depth; }; static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg) @ drivers/spi/spi-sun6i.c:168 @ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi) u8 byte; /* See how much data we can fit */ - cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi); + cnt = sspi->quirks->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi); len = min((int)cnt, sspi->len); @ drivers/spi/spi-sun6i.c:301 @ static int sun6i_spi_transfer_one(struct spi_master *master, * the hardcoded value used in old generation of Allwinner * SPI controller. (See spi-sun4i.c) */ - trig_level = sspi->fifo_depth / 4 * 3; + trig_level = sspi->quirks->fifo_depth / 4 * 3; } else { /* * Setup FIFO DMA request trigger level * We choose 1/2 of the full fifo depth, that value will * be used as DMA burst length. */ - trig_level = sspi->fifo_depth / 2; + trig_level = sspi->quirks->fifo_depth / 2; if (tfr->tx_buf) reg |= SUN6I_FIFO_CTL_TF_DRQ_EN; @ drivers/spi/spi-sun6i.c:359 @ static int sun6i_spi_transfer_one(struct spi_master *master, sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); /* Ensure that we have a parent clock fast enough */ - mclk_rate = clk_get_rate(sspi->mclk); - if (mclk_rate < (2 * tfr->speed_hz)) { - clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); + if (sspi->quirks->has_divider) { mclk_rate = clk_get_rate(sspi->mclk); - } + if (mclk_rate < (2 * tfr->speed_hz)) { + clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + } - /* - * Setup clock divider. - * - * We have two choices there. Either we can use the clock - * divide rate 1, which is calculated thanks to this formula: - * SPI_CLK = MOD_CLK / (2 ^ cdr) - * Or we can use CDR2, which is calculated with the formula: - * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) - * Wether we use the former or the latter is set through the - * DRS bit. - * - * First try CDR2, and if we can't reach the expected - * frequency, fall back to CDR1. - */ - div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz); - div_cdr2 = DIV_ROUND_UP(div_cdr1, 2); - if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { - reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS; - tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2); + /* + * Setup clock divider. + * + * We have two choices there. Either we can use the clock + * divide rate 1, which is calculated thanks to this formula: + * SPI_CLK = MOD_CLK / (2 ^ cdr) + * Or we can use CDR2, which is calculated with the formula: + * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) + * Wether we use the former or the latter is set through the + * DRS bit. + * + * First try CDR2, and if we can't reach the expected + * frequency, fall back to CDR1. + */ + div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz); + div_cdr2 = DIV_ROUND_UP(div_cdr1, 2); + if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { + reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS; + tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2); + } else { + div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1)); + reg = SUN6I_CLK_CTL_CDR1(div); + tfr->effective_speed_hz = mclk_rate / (1 << div); + } + sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); } else { - div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1)); - reg = SUN6I_CLK_CTL_CDR1(div); - tfr->effective_speed_hz = mclk_rate / (1 << div); + clk_set_rate(sspi->mclk, tfr->speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + tfr->effective_speed_hz = mclk_rate; } - sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); /* Finally enable the bus - doing so before might raise SCK to HIGH */ reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG); reg |= SUN6I_GBL_CTL_BUS_ENABLE; @ drivers/spi/spi-sun6i.c:409 @ static int sun6i_spi_transfer_one(struct spi_master *master, /* Setup the counters */ sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, tfr->len); sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len); - sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len); + + reg = tx_len; + switch (tfr->rx_nbits) { + case SPI_NBITS_QUAD: + reg |= SUN6I_BURST_CTL_CNT_QUAD_EN; + break; + case SPI_NBITS_DUAL: + reg |= SUN6I_BURST_CTL_CNT_DUAL_EN; + break; + } + sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, reg); if (!use_dma) { /* Fill the TX FIFO */ @ drivers/spi/spi-sun6i.c:438 @ static int sun6i_spi_transfer_one(struct spi_master *master, reg = SUN6I_INT_CTL_TC; if (!use_dma) { - if (rx_len > sspi->fifo_depth) + if (rx_len > sspi->quirks->fifo_depth) reg |= SUN6I_INT_CTL_RF_RDY; - if (tx_len > sspi->fifo_depth) + if (tx_len > sspi->quirks->fifo_depth) reg |= SUN6I_INT_CTL_TF_ERQ; } @ drivers/spi/spi-sun6i.c:516 @ static int sun6i_spi_runtime_resume(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct sun6i_spi *sspi = spi_master_get_devdata(master); int ret; + u32 reg; ret = clk_prepare_enable(sspi->hclk); if (ret) { @ drivers/spi/spi-sun6i.c:536 @ static int sun6i_spi_runtime_resume(struct device *dev) goto err2; } - sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, - SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP); + reg = SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP; + if (sspi->quirks->has_new_sample_mode) + reg |= SUN6I_GBL_CTL_SAMPLE_MODE; + sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, reg); return 0; @ drivers/spi/spi-sun6i.c:574 @ static bool sun6i_spi_can_dma(struct spi_master *master, * the fifo length we can just fill the fifo and wait for a single * irq, so don't bother setting up dma */ - return xfer->len > sspi->fifo_depth; + return xfer->len > sspi->quirks->fifo_depth; } static int sun6i_spi_probe(struct platform_device *pdev) @ drivers/spi/spi-sun6i.c:613 @ static int sun6i_spi_probe(struct platform_device *pdev) } sspi->master = master; - sspi->fifo_depth = (unsigned long)of_device_get_match_data(&pdev->dev); + sspi->quirks = of_device_get_match_data(&pdev->dev); master->max_speed_hz = 100 * 1000 * 1000; master->min_speed_hz = 3 * 1000; @ drivers/spi/spi-sun6i.c:621 @ static int sun6i_spi_probe(struct platform_device *pdev) master->set_cs = sun6i_spi_set_cs; master->transfer_one = sun6i_spi_transfer_one; master->num_chipselect = 4; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST + | SPI_RX_DUAL | SPI_RX_QUAD; master->bits_per_word_mask = SPI_BPW_MASK(8); master->dev.of_node = pdev->dev.of_node; master->auto_runtime_pm = true; @ drivers/spi/spi-sun6i.c:728 @ static int sun6i_spi_remove(struct platform_device *pdev) return 0; } +static const struct sun6i_spi_quirks sun6i_a31_spi_quirks = { + .fifo_depth = SUN6I_FIFO_DEPTH, + .has_divider = true, +}; + +static const struct sun6i_spi_quirks sun8i_h3_spi_quirks = { + .fifo_depth = SUN8I_FIFO_DEPTH, + .has_divider = true, +}; + +static const struct sun6i_spi_quirks sun50i_r329_spi_quirks = { + .fifo_depth = SUN8I_FIFO_DEPTH, + .has_new_sample_mode = true, +}; + static const struct of_device_id sun6i_spi_match[] = { - { .compatible = "allwinner,sun6i-a31-spi", .data = (void *)SUN6I_FIFO_DEPTH }, - { .compatible = "allwinner,sun8i-h3-spi", .data = (void *)SUN8I_FIFO_DEPTH }, + { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_quirks }, + { .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_quirks }, + { .compatible = "allwinner,sun50i-r329-spi", .data = &sun50i_r329_spi_quirks }, {} }; MODULE_DEVICE_TABLE(of, sun6i_spi_match); @ drivers/staging/media/sunxi/cedrus/cedrus.c:583 @ static const struct cedrus_variant sun8i_r40_cedrus_variant = { .mod_rate = 297000000, }; +static const struct cedrus_variant sun20i_d1_cedrus_variant = { + .capabilities = CEDRUS_CAPABILITY_UNTILED | + CEDRUS_CAPABILITY_MPEG2_DEC | + CEDRUS_CAPABILITY_H264_DEC | + CEDRUS_CAPABILITY_H265_DEC, + .mod_rate = 432000000, +}; + static const struct cedrus_variant sun50i_a64_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED | CEDRUS_CAPABILITY_MPEG2_DEC | @ drivers/staging/media/sunxi/cedrus/cedrus.c:648 @ static const struct of_device_id cedrus_dt_match[] = { .compatible = "allwinner,sun8i-r40-video-engine", .data = &sun8i_r40_cedrus_variant, }, + { + .compatible = "allwinner,sun20i-d1-video-engine", + .data = &sun20i_d1_cedrus_variant, + }, { .compatible = "allwinner,sun50i-a64-video-engine", .data = &sun50i_a64_cedrus_variant, @ drivers/thermal/sun8i_thermal.c:20 @ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/thermal.h> @ drivers/thermal/sun8i_thermal.c:54 @ #define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16) #define SUN8I_THS_DATA_IRQ_STS(x) BIT(x + 8) -#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16) +#define SUN50I_THS_CTRL0_FS_DIV(x) ((GENMASK(15, 0) & (x)) << 16) +#define SUN50I_THS_CTRL0_T_ACQ(x) (GENMASK(15, 0) & (x)) #define SUN50I_THS_FILTER_EN BIT(2) #define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x)) #define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12) @ drivers/thermal/sun8i_thermal.c:70 @ struct tsensor { }; struct ths_thermal_chip { - bool has_mod_clk; - bool has_bus_clk_reset; int sensor_num; int offset; int scale; @ drivers/thermal/sun8i_thermal.c:88 @ struct ths_device { struct device *dev; struct regmap *regmap; struct reset_control *reset; + struct regulator *vref_supply; struct clk *bus_clk; struct clk *mod_clk; struct tsensor sensor[MAX_SENSOR_NUM]; @ drivers/thermal/sun8i_thermal.c:195 @ static irqreturn_t sun8i_irq_thread(int irq, void *data) for_each_set_bit(i, &irq_bitmap, tmdev->chip->sensor_num) { thermal_zone_device_update(tmdev->sensor[i].tzd, - THERMAL_EVENT_UNSPECIFIED); + THERMAL_EVENT_TEMP_SAMPLE); } return IRQ_HANDLED; @ drivers/thermal/sun8i_thermal.c:338 @ static int sun8i_ths_resource_init(struct ths_device *tmdev) if (IS_ERR(tmdev->regmap)) return PTR_ERR(tmdev->regmap); - if (tmdev->chip->has_bus_clk_reset) { - tmdev->reset = devm_reset_control_get(dev, NULL); - if (IS_ERR(tmdev->reset)) - return PTR_ERR(tmdev->reset); + tmdev->vref_supply = devm_regulator_get(dev, "vref"); + if (IS_ERR(tmdev->vref_supply)) + return PTR_ERR(tmdev->vref_supply); - tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus"); - if (IS_ERR(tmdev->bus_clk)) - return PTR_ERR(tmdev->bus_clk); - } + tmdev->reset = devm_reset_control_get_optional(dev, NULL); + if (IS_ERR(tmdev->reset)) + return PTR_ERR(tmdev->reset); - if (tmdev->chip->has_mod_clk) { - tmdev->mod_clk = devm_clk_get(&pdev->dev, "mod"); - if (IS_ERR(tmdev->mod_clk)) - return PTR_ERR(tmdev->mod_clk); - } + tmdev->bus_clk = devm_clk_get_optional(&pdev->dev, "bus"); + if (IS_ERR(tmdev->bus_clk)) + return PTR_ERR(tmdev->bus_clk); - ret = reset_control_deassert(tmdev->reset); + tmdev->mod_clk = devm_clk_get_optional(&pdev->dev, "mod"); + if (IS_ERR(tmdev->mod_clk)) + return PTR_ERR(tmdev->mod_clk); + + ret = regulator_enable(tmdev->vref_supply); if (ret) return ret; + ret = reset_control_deassert(tmdev->reset); + if (ret) + goto disable_vref_supply; + ret = clk_prepare_enable(tmdev->bus_clk); if (ret) goto assert_reset; @ drivers/thermal/sun8i_thermal.c:386 @ static int sun8i_ths_resource_init(struct ths_device *tmdev) clk_disable_unprepare(tmdev->bus_clk); assert_reset: reset_control_assert(tmdev->reset); +disable_vref_supply: + regulator_disable(tmdev->vref_supply); return ret; } @ drivers/thermal/sun8i_thermal.c:427 @ static int sun8i_h3_thermal_init(struct ths_device *tmdev) return 0; } -/* - * Without this undocumented value, the returned temperatures would - * be higher than real ones by about 20C. - */ -#define SUN50I_H6_CTRL0_UNK 0x0000002f - static int sun50i_h6_thermal_init(struct ths_device *tmdev) { int val; /* - * T_acq = 20us + * T_sample = 20us > T_acq + T_conv + * T_acq = 2us + * T_conv = 14 cycles * clkin = 24MHz * - * x = T_acq * clkin - 1 + * x = T_sample * clkin - 1 * = 479 */ regmap_write(tmdev->regmap, SUN50I_THS_CTRL0, - SUN50I_H6_CTRL0_UNK | SUN50I_THS_CTRL0_T_ACQ(479)); + SUN50I_THS_CTRL0_FS_DIV(479) | + SUN50I_THS_CTRL0_T_ACQ(47)); /* average over 4 samples */ regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC, SUN50I_THS_FILTER_EN | @ drivers/thermal/sun8i_thermal.c:544 @ static int sun8i_ths_remove(struct platform_device *pdev) clk_disable_unprepare(tmdev->mod_clk); clk_disable_unprepare(tmdev->bus_clk); reset_control_assert(tmdev->reset); + regulator_disable(tmdev->vref_supply); return 0; } @ drivers/thermal/sun8i_thermal.c:564 @ static const struct ths_thermal_chip sun8i_h3_ths = { .sensor_num = 1, .scale = 1211, .offset = 217000, - .has_mod_clk = true, - .has_bus_clk_reset = true, .temp_data_base = SUN8I_THS_TEMP_DATA, .calibrate = sun8i_h3_ths_calibrate, .init = sun8i_h3_thermal_init, @ drivers/thermal/sun8i_thermal.c:575 @ static const struct ths_thermal_chip sun8i_r40_ths = { .sensor_num = 2, .offset = 251086, .scale = 1130, - .has_mod_clk = true, - .has_bus_clk_reset = true, .temp_data_base = SUN8I_THS_TEMP_DATA, .calibrate = sun8i_h3_ths_calibrate, .init = sun8i_h3_thermal_init, @ drivers/thermal/sun8i_thermal.c:582 @ static const struct ths_thermal_chip sun8i_r40_ths = { .calc_temp = sun8i_ths_calc_temp, }; +static const struct ths_thermal_chip sun20i_d1_ths = { + .sensor_num = 1, + .offset = 188147, + .scale = 672, + .temp_data_base = SUN50I_H6_THS_TEMP_DATA, + .calibrate = sun50i_h6_ths_calibrate, + .init = sun50i_h6_thermal_init, + .irq_ack = sun50i_h6_irq_ack, + .calc_temp = sun8i_ths_calc_temp, +}; + static const struct ths_thermal_chip sun50i_a64_ths = { .sensor_num = 3, .offset = 260890, .scale = 1170, - .has_mod_clk = true, - .has_bus_clk_reset = true, .temp_data_base = SUN8I_THS_TEMP_DATA, .calibrate = sun8i_h3_ths_calibrate, .init = sun8i_h3_thermal_init, @ drivers/thermal/sun8i_thermal.c:606 @ static const struct ths_thermal_chip sun50i_a64_ths = { static const struct ths_thermal_chip sun50i_a100_ths = { .sensor_num = 3, - .has_bus_clk_reset = true, .ft_deviation = 8000, .offset = 187744, .scale = 672, @ drivers/thermal/sun8i_thermal.c:618 @ static const struct ths_thermal_chip sun50i_a100_ths = { static const struct ths_thermal_chip sun50i_h5_ths = { .sensor_num = 2, - .has_mod_clk = true, - .has_bus_clk_reset = true, .temp_data_base = SUN8I_THS_TEMP_DATA, .calibrate = sun8i_h3_ths_calibrate, .init = sun8i_h3_thermal_init, @ drivers/thermal/sun8i_thermal.c:627 @ static const struct ths_thermal_chip sun50i_h5_ths = { static const struct ths_thermal_chip sun50i_h6_ths = { .sensor_num = 2, - .has_bus_clk_reset = true, .ft_deviation = 7000, .offset = 187744, .scale = 672, @ drivers/thermal/sun8i_thermal.c:641 @ static const struct of_device_id of_ths_match[] = { { .compatible = "allwinner,sun8i-a83t-ths", .data = &sun8i_a83t_ths }, { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths }, { .compatible = "allwinner,sun8i-r40-ths", .data = &sun8i_r40_ths }, + { .compatible = "allwinner,sun20i-d1-ths", .data = &sun20i_d1_ths }, { .compatible = "allwinner,sun50i-a64-ths", .data = &sun50i_a64_ths }, { .compatible = "allwinner,sun50i-a100-ths", .data = &sun50i_a100_ths }, { .compatible = "allwinner,sun50i-h5-ths", .data = &sun50i_h5_ths }, @ drivers/thermal/thermal_of.c:561 @ void thermal_zone_of_sensor_unregister(struct device *dev, tz = tzd->devdata; - /* no __thermal_zone, nothing to be done */ - if (!tz) - return; - /* stop temperature polling */ thermal_zone_device_disable(tzd); @ include/dt-bindings/clock/sun20i-d1-ccu.h:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +/* + * Copyright (C) 2020 huangzhenwei@allwinnertech.com + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#ifndef _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_ +#define _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_ + +#define CLK_PLL_CPUX 0 +#define CLK_PLL_DDR0 1 +#define CLK_PLL_PERIPH0_4X 2 +#define CLK_PLL_PERIPH0_2X 3 +#define CLK_PLL_PERIPH0_800M 4 +#define CLK_PLL_PERIPH0 5 +#define CLK_PLL_PERIPH0_DIV3 6 +#define CLK_PLL_VIDEO0_4X 7 +#define CLK_PLL_VIDEO0_2X 8 +#define CLK_PLL_VIDEO0 9 +#define CLK_PLL_VIDEO1_4X 10 +#define CLK_PLL_VIDEO1_2X 11 +#define CLK_PLL_VIDEO1 12 +#define CLK_PLL_VE 13 +#define CLK_PLL_AUDIO0_4X 14 +#define CLK_PLL_AUDIO0_2X 15 +#define CLK_PLL_AUDIO0 16 +#define CLK_PLL_AUDIO1 17 +#define CLK_PLL_AUDIO1_DIV2 18 +#define CLK_PLL_AUDIO1_DIV5 19 +#define CLK_CPUX 20 +#define CLK_CPUX_AXI 21 +#define CLK_CPUX_APB 22 +#define CLK_PSI_AHB 23 +#define CLK_APB0 24 +#define CLK_APB1 25 +#define CLK_MBUS 26 +#define CLK_DE 27 +#define CLK_BUS_DE 28 +#define CLK_DI 29 +#define CLK_BUS_DI 30 +#define CLK_G2D 31 +#define CLK_BUS_G2D 32 +#define CLK_CE 33 +#define CLK_BUS_CE 34 +#define CLK_VE 35 +#define CLK_BUS_VE 36 +#define CLK_BUS_DMA 37 +#define CLK_BUS_MSGBOX0 38 +#define CLK_BUS_MSGBOX1 39 +#define CLK_BUS_MSGBOX2 40 +#define CLK_BUS_SPINLOCK 41 +#define CLK_BUS_HSTIMER 42 +#define CLK_AVS 43 +#define CLK_BUS_DBG 44 +#define CLK_BUS_PWM 45 +#define CLK_BUS_IOMMU 46 +#define CLK_DRAM 47 +#define CLK_MBUS_DMA 48 +#define CLK_MBUS_VE 49 +#define CLK_MBUS_CE 50 +#define CLK_MBUS_TVIN 51 +#define CLK_MBUS_CSI 52 +#define CLK_MBUS_G2D 53 +#define CLK_MBUS_RISCV 54 +#define CLK_BUS_DRAM 55 +#define CLK_MMC0 56 +#define CLK_MMC1 57 +#define CLK_MMC2 58 +#define CLK_BUS_MMC0 59 +#define CLK_BUS_MMC1 60 +#define CLK_BUS_MMC2 61 +#define CLK_BUS_UART0 62 +#define CLK_BUS_UART1 63 +#define CLK_BUS_UART2 64 +#define CLK_BUS_UART3 65 +#define CLK_BUS_UART4 66 +#define CLK_BUS_UART5 67 +#define CLK_BUS_I2C0 68 +#define CLK_BUS_I2C1 69 +#define CLK_BUS_I2C2 70 +#define CLK_BUS_I2C3 71 +#define CLK_SPI0 72 +#define CLK_SPI1 73 +#define CLK_BUS_SPI0 74 +#define CLK_BUS_SPI1 75 +#define CLK_EMAC_25M 76 +#define CLK_BUS_EMAC 77 +#define CLK_IR_TX 78 +#define CLK_BUS_IR_TX 79 +#define CLK_BUS_GPADC 80 +#define CLK_BUS_THS 81 +#define CLK_I2S0 82 +#define CLK_I2S1 83 +#define CLK_I2S2 84 +#define CLK_I2S2_ASRC 85 +#define CLK_BUS_I2S0 86 +#define CLK_BUS_I2S1 87 +#define CLK_BUS_I2S2 88 +#define CLK_SPDIF_TX 89 +#define CLK_SPDIF_RX 90 +#define CLK_BUS_SPDIF 91 +#define CLK_DMIC 92 +#define CLK_BUS_DMIC 93 +#define CLK_AUDIO_DAC 94 +#define CLK_AUDIO_ADC 95 +#define CLK_BUS_AUDIO 96 +#define CLK_USB_OHCI0 97 +#define CLK_USB_OHCI1 98 +#define CLK_BUS_OHCI0 99 +#define CLK_BUS_OHCI1 100 +#define CLK_BUS_EHCI0 101 +#define CLK_BUS_EHCI1 102 +#define CLK_BUS_OTG 103 +#define CLK_BUS_LRADC 104 +#define CLK_BUS_DPSS_TOP 105 +#define CLK_HDMI_24M 106 +#define CLK_HDMI_CEC_32K 107 +#define CLK_HDMI_CEC 108 +#define CLK_BUS_HDMI 109 +#define CLK_MIPI_DSI 110 +#define CLK_BUS_MIPI_DSI 111 +#define CLK_TCON_LCD0 112 +#define CLK_BUS_TCON_LCD0 113 +#define CLK_TCON_TV 114 +#define CLK_BUS_TCON_TV 115 +#define CLK_TVE 116 +#define CLK_BUS_TVE_TOP 117 +#define CLK_BUS_TVE 118 +#define CLK_TVD 119 +#define CLK_BUS_TVD_TOP 120 +#define CLK_BUS_TVD 121 +#define CLK_LEDC 122 +#define CLK_BUS_LEDC 123 +#define CLK_CSI_TOP 124 +#define CLK_CSI_MCLK 125 +#define CLK_BUS_CSI 126 +#define CLK_TPADC 127 +#define CLK_BUS_TPADC 128 +#define CLK_BUS_TZMA 129 +#define CLK_DSP 130 +#define CLK_BUS_DSP_CFG 131 +#define CLK_RISCV 132 +#define CLK_RISCV_AXI 133 +#define CLK_BUS_RISCV_CFG 134 +#define CLK_FANOUT_24M 135 +#define CLK_FANOUT_12M 136 +#define CLK_FANOUT_16M 137 +#define CLK_FANOUT_25M 138 +#define CLK_FANOUT_32K 139 +#define CLK_FANOUT_27M 140 +#define CLK_FANOUT_PCLK 141 +#define CLK_FANOUT0 142 +#define CLK_FANOUT1 143 +#define CLK_FANOUT2 144 + +#endif /* _DT_BINDINGS_CLK_SUN20I_D1_CCU_H_ */ @ include/dt-bindings/clock/sun20i-d1-r-ccu.h:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +/* + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#ifndef _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_ +#define _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_ + +#define CLK_R_AHB 0 + +#define CLK_BUS_R_TIMER 2 +#define CLK_BUS_R_TWD 3 +#define CLK_BUS_R_PPU 4 +#define CLK_R_IR_RX 5 +#define CLK_BUS_R_IR_RX 6 +#define CLK_BUS_R_RTC 7 +#define CLK_BUS_R_CPUCFG 8 + +#endif /* _DT_BINDINGS_CLK_SUN20I_D1_R_CCU_H_ */ @ include/dt-bindings/clock/sun6i-rtc.h:4 @ +/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */ + +#ifndef _DT_BINDINGS_CLK_SUN6I_RTC_H_ +#define _DT_BINDINGS_CLK_SUN6I_RTC_H_ + +#define CLK_OSC32K 0 +#define CLK_OSC32K_FANOUT 1 +#define CLK_IOSC 2 + +#endif /* _DT_BINDINGS_CLK_SUN6I_RTC_H_ */ @ include/dt-bindings/mailbox/sun20i-d1-msgbox.h:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This header provides constants for binding nvidia,tegra186-hsp. + */ + +#ifndef _DT_BINDINGS_MAILBOX_SUN20I_D1_MSGBOX_H_ +#define _DT_BINDINGS_MAILBOX_SUN20I_D1_MSGBOX_H_ + +/* First cell: channel (transmitting user) */ +#define MBOX_USER_CPUX 0 +#define MBOX_USER_DSP 1 +#define MBOX_USER_RISCV 2 + +/* Second cell: direction (RX if phandle references local mailbox, else TX) */ +#define MBOX_RX 0 +#define MBOX_TX 1 + +#endif /* _DT_BINDINGS_MAILBOX_SUN20I_D1_MSGBOX_H_ */ @ include/dt-bindings/reset/sun20i-d1-ccu.h:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +/* + * Copyright (c) 2020 huangzhenwei@allwinnertech.com + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#ifndef _DT_BINDINGS_RST_SUN20I_D1_CCU_H_ +#define _DT_BINDINGS_RST_SUN20I_D1_CCU_H_ + +#define RST_MBUS 0 +#define RST_BUS_DE 1 +#define RST_BUS_DI 2 +#define RST_BUS_G2D 3 +#define RST_BUS_CE 4 +#define RST_BUS_VE 5 +#define RST_BUS_DMA 6 +#define RST_BUS_MSGBOX0 7 +#define RST_BUS_MSGBOX1 8 +#define RST_BUS_MSGBOX2 9 +#define RST_BUS_SPINLOCK 10 +#define RST_BUS_HSTIMER 11 +#define RST_BUS_DBG 12 +#define RST_BUS_PWM 13 +#define RST_BUS_DRAM 14 +#define RST_BUS_MMC0 15 +#define RST_BUS_MMC1 16 +#define RST_BUS_MMC2 17 +#define RST_BUS_UART0 18 +#define RST_BUS_UART1 19 +#define RST_BUS_UART2 20 +#define RST_BUS_UART3 21 +#define RST_BUS_UART4 22 +#define RST_BUS_UART5 23 +#define RST_BUS_I2C0 24 +#define RST_BUS_I2C1 25 +#define RST_BUS_I2C2 26 +#define RST_BUS_I2C3 27 +#define RST_BUS_SPI0 28 +#define RST_BUS_SPI1 29 +#define RST_BUS_EMAC 30 +#define RST_BUS_IR_TX 31 +#define RST_BUS_GPADC 32 +#define RST_BUS_THS 33 +#define RST_BUS_I2S0 34 +#define RST_BUS_I2S1 35 +#define RST_BUS_I2S2 36 +#define RST_BUS_SPDIF 37 +#define RST_BUS_DMIC 38 +#define RST_BUS_AUDIO 39 +#define RST_USB_PHY0 40 +#define RST_USB_PHY1 41 +#define RST_BUS_OHCI0 42 +#define RST_BUS_OHCI1 43 +#define RST_BUS_EHCI0 44 +#define RST_BUS_EHCI1 45 +#define RST_BUS_OTG 46 +#define RST_BUS_LRADC 47 +#define RST_BUS_DPSS_TOP 48 +#define RST_BUS_HDMI_SUB 49 +#define RST_BUS_HDMI_MAIN 50 +#define RST_BUS_MIPI_DSI 51 +#define RST_BUS_TCON_LCD0 52 +#define RST_BUS_TCON_TV 53 +#define RST_BUS_LVDS0 54 +#define RST_BUS_TVE 55 +#define RST_BUS_TVE_TOP 56 +#define RST_BUS_TVD 57 +#define RST_BUS_TVD_TOP 58 +#define RST_BUS_LEDC 59 +#define RST_BUS_CSI 60 +#define RST_BUS_TPADC 61 +#define RST_DSP 62 +#define RST_BUS_DSP_CFG 63 +#define RST_BUS_DSP_DBG 64 +#define RST_BUS_RISCV_CFG 65 + +#endif /* _DT_BINDINGS_RST_SUN20I_D1_CCU_H_ */ @ include/dt-bindings/reset/sun20i-d1-r-ccu.h:4 @ +// SPDX-License-Identifier: (GPL-2.0+ or MIT) +/* + * Copyright (C) 2021 Samuel Holland <samuel@sholland.org> + */ + +#ifndef _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_ +#define _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_ + +#define RST_BUS_R_TIMER 0 +#define RST_BUS_R_TWD 1 +#define RST_BUS_R_PPU 2 +#define RST_BUS_R_IR_RX 3 +#define RST_BUS_R_RTC 4 +#define RST_BUS_R_CPUCFG 5 + +#endif /* _DT_BINDINGS_RST_SUN20I_D1_R_CCU_H_ */ @ include/linux/clk/sunxi-ng.h:9 @ #ifndef _LINUX_CLK_SUNXI_NG_H_ #define _LINUX_CLK_SUNXI_NG_H_ -#include <linux/errno.h> - -#ifdef CONFIG_SUNXI_CCU int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode); int sunxi_ccu_get_mmc_timing_mode(struct clk *clk); -#else -static inline int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, - bool new_mode) -{ - return -ENOTSUPP; -} -static inline int sunxi_ccu_get_mmc_timing_mode(struct clk *clk) -{ - return -ENOTSUPP; -} -#endif +int sun6i_rtc_ccu_probe(struct device *dev, void __iomem *reg); #endif @ include/linux/dmaengine.h:397 @ enum dma_slave_buswidth { * should be read (RX), if the source is memory this argument is * ignored. * @dst_addr: this is the physical address where DMA slave data - * should be written (TX), if the source is memory this argument + * should be written (TX), if the destination is memory this argument * is ignored. * @src_addr_width: this is the width in bytes of the source (RX) * register where DMA data shall be read. If the source @ mm/mmap.c:103 @ static void unmap_region(struct mm_struct *mm, * w: (no) no * x: (yes) yes */ +#ifdef CONFIG_ARCH_HAS_PROTECTION_MAP_INIT +pgprot_t protection_map[16] __ro_after_init; +#else pgprot_t protection_map[16] __ro_after_init = { __P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111, __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111 }; +#endif #ifndef CONFIG_ARCH_HAS_FILTER_PGPROT static inline pgprot_t arch_filter_pgprot(pgprot_t prot) @ sound/soc/soc-core.c:20 @ // o Add more codecs and platforms to ensure good API coverage. // o Support TDM on PCM and I2S +#define DEBUG #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @ sound/soc/soc-core.c:1025 @ int snd_soc_add_pcm_runtime(struct snd_soc_card *card, if (!snd_soc_is_matching_component(platform, component)) continue; + dev_warn(card->dev, "ASoC: Adding component %s for platform %pOF\n", + component->name, platform->of_node); snd_soc_rtd_add_component(rtd, component); } } @ sound/soc/soc-dapm.c:2344 @ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, * right channel. * * A stereo control is signified by a valid 'rconnect' - * value, either 0 for unconnected, or >= 0 for connected. + * value, either 0 for unconnected, or > 0 for connected. * This is chosen instead of using snd_soc_volsw_is_stereo, * so that the behavior of snd_soc_dapm_mixer_update_power * doesn't change even when the kcontrol passed in is @ sound/soc/sunxi/Kconfig:33 @ config SND_SUN8I_CODEC_ANALOG Say Y or M if you want to add support for the analog controls for the codec embedded in newer Allwinner SoCs. +config SND_SUN20I_CODEC + tristate "Allwinner D1 (sun20i) Audio Codec" + depends on ARCH_SUNXI || COMPILE_TEST + help + Say Y or M to add support for the audio codec in Allwinner D1 SoC. + config SND_SUN50I_CODEC_ANALOG tristate "Allwinner sun50i Codec Analog Controls Support" depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST @ sound/soc/sunxi/Makefile:5 @ obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o +obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o +obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o obj-$(CONFIG_SND_SUN8I_CODEC_ANALOG) += sun8i-codec-analog.o +obj-$(CONFIG_SND_SUN20I_CODEC) += sun20i-codec.o obj-$(CONFIG_SND_SUN50I_CODEC_ANALOG) += sun50i-codec-analog.o -obj-$(CONFIG_SND_SUN8I_CODEC) += sun8i-codec.o -obj-$(CONFIG_SND_SUN8I_ADDA_PR_REGMAP) += sun8i-adda-pr-regmap.o @ sound/soc/sunxi/sun20i-codec.c:4 @ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/reset.h> + +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> +#include <sound/simple_card_utils.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#define SUN20I_CODEC_DAC_DPC 0x0000 +#define SUN20I_CODEC_DAC_DPC_EN_DA 31 +#define SUN20I_CODEC_DAC_DPC_HPF_EN 18 +#define SUN20I_CODEC_DAC_DPC_DVOL 12 +#define SUN20I_CODEC_DAC_VOL_CTRL 0x0004 +#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL 16 +#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_L 8 +#define SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_R 0 +#define SUN20I_CODEC_DAC_FIFOC 0x0010 +#define SUN20I_CODEC_DAC_FIFOC_FS 29 +#define SUN20I_CODEC_DAC_FIFOC_FIFO_MODE 24 +#define SUN20I_CODEC_DAC_FIFOC_DRQ_CLR_CNT 21 +#define SUN20I_CODEC_DAC_FIFOC_TRIG_LEVEL 8 +#define SUN20I_CODEC_DAC_FIFOC_MONO_EN 6 +#define SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS 5 +#define SUN20I_CODEC_DAC_FIFOC_DRQ_EN 4 +#define SUN20I_CODEC_DAC_FIFOC_FIFO_FLUSH 0 +#define SUN20I_CODEC_DAC_TXDATA 0x0020 +#define SUN20I_CODEC_DAC_DEBUG 0x0028 +#define SUN20I_CODEC_DAC_DEBUG_DA_SWP 6 +#define SUN20I_CODEC_DAC_ADDA_LOOP_MODE 0 + +#define SUN20I_CODEC_ADC_FIFOC 0x0030 +#define SUN20I_CODEC_ADC_FIFOC_FS 29 +#define SUN20I_CODEC_ADC_FIFOC_EN_AD 28 +#define SUN20I_CODEC_ADC_FIFOC_FIFO_MODE 24 +#define SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS 16 +#define SUN20I_CODEC_ADC_FIFOC_TRIG_LEVEL 4 +#define SUN20I_CODEC_ADC_FIFOC_DRQ_EN 3 +#define SUN20I_CODEC_ADC_FIFOC_FIFO_FLUSH 0 +#define SUN20I_CODEC_ADC_VOL_CTRL 0x0034 +#define SUN20I_CODEC_ADC_VOL_CTRL_ADC3_VOL 16 +#define SUN20I_CODEC_ADC_VOL_CTRL_ADC2_VOL 8 +#define SUN20I_CODEC_ADC_VOL_CTRL_ADC1_VOL 0 +#define SUN20I_CODEC_ADC_RXDATA 0x0040 +#define SUN20I_CODEC_ADC_DEBUG 0x004c +#define SUN20I_CODEC_ADC_DEBUG_AD_SWP1 24 +#define SUN20I_CODEC_ADC_DIG_CTRL 0x0050 +#define SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN 16 +#define SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN 0 + +#define SUN20I_CODEC_DAC_DAP_CTRL 0x00f0 +#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_EN 31 +#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_DRC_EN 29 +#define SUN20I_CODEC_DAC_DAP_CTRL_DAP_HPF_EN 28 + +#define SUN20I_CODEC_ADC_DAP_CTRL 0x00f8 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_EN 31 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_DRC_EN 29 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP0_HPF_EN 28 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_EN 27 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_DRC_EN 25 +#define SUN20I_CODEC_ADC_DAP_CTRL_DAP1_HPF_EN 24 + +#define SUN20I_CODEC_ADC1 0x0300 +#define SUN20I_CODEC_ADC1_ADC1_EN 31 +#define SUN20I_CODEC_ADC1_MICIN1_PGA_EN 30 +#define SUN20I_CODEC_ADC1_ADC1_DITHER_EN 29 +#define SUN20I_CODEC_ADC1_MICIN1_SIN_EN 28 +#define SUN20I_CODEC_ADC1_FMINL_EN 27 +#define SUN20I_CODEC_ADC1_FMINL_GAIN 26 +#define SUN20I_CODEC_ADC1_DITHER_LEVEL 24 +#define SUN20I_CODEC_ADC1_LINEINL_EN 23 +#define SUN20I_CODEC_ADC1_LINEINL_GAIN 22 +#define SUN20I_CODEC_ADC1_ADC1_PGA_GAIN 8 +#define SUN20I_CODEC_ADC2 0x0304 +#define SUN20I_CODEC_ADC2_ADC2_EN 31 +#define SUN20I_CODEC_ADC2_MICIN2_PGA_EN 30 +#define SUN20I_CODEC_ADC2_ADC2_DITHER_EN 29 +#define SUN20I_CODEC_ADC2_MICIN2_SIN_EN 28 +#define SUN20I_CODEC_ADC2_FMINR_EN 27 +#define SUN20I_CODEC_ADC2_FMINR_GAIN 26 +#define SUN20I_CODEC_ADC2_DITHER_LEVEL 24 +#define SUN20I_CODEC_ADC2_LINEINR_EN 23 +#define SUN20I_CODEC_ADC2_LINEINR_GAIN 22 +#define SUN20I_CODEC_ADC2_ADC2_PGA_GAIN 8 +#define SUN20I_CODEC_ADC3 0x0308 +#define SUN20I_CODEC_ADC3_ADC3_EN 31 +#define SUN20I_CODEC_ADC3_MICIN3_PGA_EN 30 +#define SUN20I_CODEC_ADC3_ADC3_DITHER_EN 29 +#define SUN20I_CODEC_ADC3_MICIN3_SIN_EN 28 +#define SUN20I_CODEC_ADC3_DITHER_LEVEL 24 +#define SUN20I_CODEC_ADC3_ADC3_PGA_GAIN 8 + +#define SUN20I_CODEC_DAC 0x0310 +#define SUN20I_CODEC_DAC_DACL_EN 15 +#define SUN20I_CODEC_DAC_DACR_EN 14 +#define SUN20I_CODEC_DAC_LINEOUTL_EN 13 +#define SUN20I_CODEC_DAC_LMUTE 12 +#define SUN20I_CODEC_DAC_LINEOUTR_EN 11 +#define SUN20I_CODEC_DAC_RMUTE 10 +#define SUN20I_CODEC_DAC_LINEOUTL_DIFFEN 6 +#define SUN20I_CODEC_DAC_LINEOUTR_DIFFEN 5 +#define SUN20I_CODEC_DAC_LINEOUT_VOL_CTRL 0 + +#define SUN20I_CODEC_MICBIAS 0x0318 +#define SUN20I_CODEC_MICBIAS_SELDETADCFS 28 +#define SUN20I_CODEC_MICBIAS_SELDETADCDB 26 +#define SUN20I_CODEC_MICBIAS_SELDETADCBF 24 +#define SUN20I_CODEC_MICBIAS_JACKDETEN 23 +#define SUN20I_CODEC_MICBIAS_SELDETADCDY 21 +#define SUN20I_CODEC_MICBIAS_MICADCEN 20 +#define SUN20I_CODEC_MICBIAS_POPFREE 19 +#define SUN20I_CODEC_MICBIAS_DET_MODE 18 +#define SUN20I_CODEC_MICBIAS_AUTOPLEN 17 +#define SUN20I_CODEC_MICBIAS_MICDETPL 16 +#define SUN20I_CODEC_MICBIAS_HMICBIASEN 15 +#define SUN20I_CODEC_MICBIAS_HMICBIASSEL 13 +#define SUN20I_CODEC_MICBIAS_HMIC_CHOPPER_EN 12 +#define SUN20I_CODEC_MICBIAS_HMIC_CHOPPER_CLK 10 +#define SUN20I_CODEC_MICBIAS_MMICBIASEN 7 +#define SUN20I_CODEC_MICBIAS_MMICBIASSEL 5 +#define SUN20I_CODEC_MICBIAS_MMIC_CHOPPER_EN 4 +#define SUN20I_CODEC_MICBIAS_MMIC_CHOPPER_CLK 2 + +/* TODO */ +#define SUN20I_CODEC_RAMP 0x031c +#define SUN20I_CODEC_RAMP_HP_PULL_OUT_EN 15 + +#define SUN20I_CODEC_HMIC_CTRL 0x0328 +#define SUN20I_CODEC_HMIC_CTRL_SAMPLE_SELECT 21 +#define SUN20I_CODEC_HMIC_CTRL_MDATA_THRESHOLD 16 +#define SUN20I_CODEC_HMIC_CTRL_SF 14 +#define SUN20I_CODEC_HMIC_CTRL_M 10 +#define SUN20I_CODEC_HMIC_CTRL_N 6 +#define SUN20I_CODEC_HMIC_CTRL_THRESH_DEBOUNCE 3 +#define SUN20I_CODEC_HMIC_CTRL_JACK_OUT_IRQ_EN 2 +#define SUN20I_CODEC_HMIC_CTRL_JACK_IN_IRQ_EN 1 +#define SUN20I_CODEC_HMIC_CTRL_MIC_DET_IRQ_EN 0 +#define SUN20I_CODEC_HMIC_STS 0x032c +#define SUN20I_CODEC_HMIC_STS_MDATA_DISCARD 13 +#define SUN20I_CODEC_HMIC_STS_HMIC_DATA 8 +#define SUN20I_CODEC_HMIC_STS_JACK_OUT_IRQ 4 +#define SUN20I_CODEC_HMIC_STS_JACK_IN_IRQ 3 +#define SUN20I_CODEC_HMIC_STS_MIC_DET_IRQ 0 + +#define SUN20I_CODEC_HP2 0x0340 +#define SUN20I_CODEC_HP2_HPFB_BUF_EN 31 +#define SUN20I_CODEC_HP2_HEADPHONE_GAIN 28 +#define SUN20I_CODEC_HP2_HPFB_RES 26 +#define SUN20I_CODEC_HP2_HP_DRVEN 21 +#define SUN20I_CODEC_HP2_HP_DRVOUTEN 20 +#define SUN20I_CODEC_HP2_RSWITCH 19 +#define SUN20I_CODEC_HP2_RAMPEN 18 +#define SUN20I_CODEC_HP2_HPFB_IN_EN 17 +#define SUN20I_CODEC_HP2_RAMP_FINAL_CONTROL 16 +#define SUN20I_CODEC_HP2_RAMP_OUT_EN 15 +#define SUN20I_CODEC_HP2_RAMP_FINAL_STATE_RES 13 + +/* Not affected by codec bus clock/reset */ +#define SUN20I_CODEC_POWER 0x0348 +#define SUN20I_CODEC_POWER_ALDO_EN_MASK BIT(31) +#define SUN20I_CODEC_POWER_HPLDO_EN_MASK BIT(30) +#define SUN20I_CODEC_POWER_ALDO_VOLTAGE_MASK GENMASK(14, 12) +#define SUN20I_CODEC_POWER_HPLDO_VOLTAGE_MASK GENMASK(10, 8) + +#define SUN20I_CODEC_ADC_CUR 0x034c + +#define SUN20I_CODEC_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE|\ + SNDRV_PCM_FMTBIT_S20_LE|\ + SNDRV_PCM_FMTBIT_S32_LE) + +#define DRIVER_NAME "sun20i-codec" +#define PREFIX "allwinner," + +/* snd_soc_register_card() takes over drvdata, so the card must be first! */ +struct sun20i_codec { + struct snd_soc_card card; + struct snd_soc_dai_link dai_link; + struct snd_soc_dai_link_component dlcs[3]; + struct snd_dmaengine_dai_dma_data dma_data[2]; + + struct clk *bus_clk; + struct clk *adc_clk; + struct clk *dac_clk; + struct reset_control *reset; +}; + +static int sun20i_codec_dai_probe(struct snd_soc_dai *dai) +{ + struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, + &codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK], + &codec->dma_data[SNDRV_PCM_STREAM_CAPTURE]); + + return 0; +} + +static struct clk *sun20i_codec_get_clk(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai); + + return substream->stream == SNDRV_PCM_STREAM_CAPTURE ? + codec->adc_clk : codec->dac_clk; +} + +static const unsigned int sun20i_codec_rates[] = { + 7350, 8000, 11025, 12000, 14700, 16000, 22050, 24000, + 29400, 32000, 44100, 48000, 88200, 96000, 176400, 192000, +}; + +static const struct snd_pcm_hw_constraint_list sun20i_codec_rate_lists[] = { + [SNDRV_PCM_STREAM_PLAYBACK] = { + .list = sun20i_codec_rates, + .count = ARRAY_SIZE(sun20i_codec_rates), + }, + [SNDRV_PCM_STREAM_CAPTURE] = { + .list = sun20i_codec_rates, + .count = ARRAY_SIZE(sun20i_codec_rates) - 4, /* max 48 kHz */ + }, +}; + +static int sun20i_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + const struct snd_pcm_hw_constraint_list *list; + int ret; + + list = &sun20i_codec_rate_lists[substream->stream]; + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, list); + if (ret) + return ret; + + ret = clk_prepare_enable(sun20i_codec_get_clk(substream, dai)); + if (ret) + return ret; + + return 0; +} + +static void sun20i_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + clk_disable_unprepare(sun20i_codec_get_clk(substream, dai)); +} + +static unsigned int sun20i_codec_get_clk_rate(unsigned int sample_rate) +{ + return (sample_rate % 4000) ? 22579200 : 24576000; +} + +static const unsigned short sun20i_codec_divisors[] = { + 512, 1024, 2048, 128, + 768, 1536, 3072, 256, +}; + +static int sun20i_codec_get_fs(unsigned int clk_rate, unsigned int sample_rate) +{ + unsigned int divisor = clk_rate / sample_rate; + int i; + + for (i = 0; i < ARRAY_SIZE(sun20i_codec_divisors); ++i) + if (sun20i_codec_divisors[i] == divisor) + return i; + + return -EINVAL; +} + +static int sun20i_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sun20i_codec *codec = snd_soc_dai_get_drvdata(dai); + struct snd_soc_component *component = dai->component; + unsigned int channels = params_channels(params); + unsigned int sample_bits = params_width(params); + unsigned int sample_rate = params_rate(params); + unsigned int clk_rate = sun20i_codec_get_clk_rate(sample_rate); + enum dma_slave_buswidth dma_width; + unsigned int reg; + int ret, val; + + switch (params_physical_width(params)) { + case 16: + dma_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 32: + dma_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + dev_err(dai->dev, "Unsupported physical sample width: %d\n", + params_physical_width(params)); + return -EINVAL; + } + codec->dma_data[substream->stream].addr_width = dma_width; + + ret = clk_set_rate(sun20i_codec_get_clk(substream, dai), + sun20i_codec_get_clk_rate(sample_rate)); + if (ret) + return ret; + + reg = substream->stream == SNDRV_PCM_STREAM_CAPTURE ? + SUN20I_CODEC_ADC_FIFOC : SUN20I_CODEC_DAC_FIFOC; + + val = sun20i_codec_get_fs(clk_rate, sample_rate); + if (val < 0) + return val; + snd_soc_component_update_bits(component, reg, + 0x7 << SUN20I_CODEC_DAC_FIFOC_FS, + val << SUN20I_CODEC_DAC_FIFOC_FS); + + /* Data is at MSB for full 4-byte samples, otherwise at LSB. */ + val = sample_bits != 32; + snd_soc_component_update_bits(component, reg, + 0x1 << SUN20I_CODEC_DAC_FIFOC_FIFO_MODE, + val << SUN20I_CODEC_DAC_FIFOC_FIFO_MODE); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + val = sample_bits > 16; + snd_soc_component_update_bits(component, reg, + 0x1 << SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS, + val << SUN20I_CODEC_ADC_FIFOC_SAMPLE_BITS); + + val = BIT(channels) - 1; + snd_soc_component_update_bits(component, SUN20I_CODEC_ADC_DIG_CTRL, + 0xf << SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN, + val << SUN20I_CODEC_ADC_DIG_CTRL_ADC_EN); + } else { + val = sample_bits > 16; + snd_soc_component_update_bits(component, reg, + 0x1 << SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS, + val << SUN20I_CODEC_DAC_FIFOC_SAMPLE_BITS); + + val = channels == 1; + snd_soc_component_update_bits(component, reg, + 0x1 << SUN20I_CODEC_DAC_FIFOC_MONO_EN, + val << SUN20I_CODEC_DAC_FIFOC_MONO_EN); + } + + return 0; +} + +static int sun20i_codec_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg, mask; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + reg = SUN20I_CODEC_ADC_FIFOC; + mask = BIT(SUN20I_CODEC_ADC_FIFOC_DRQ_EN); + } else { + reg = SUN20I_CODEC_DAC_FIFOC; + mask = BIT(SUN20I_CODEC_DAC_FIFOC_DRQ_EN); + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + mask |= BIT(SUN20I_CODEC_DAC_FIFOC_FIFO_FLUSH); + snd_soc_component_update_bits(component, reg, mask, mask); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + snd_soc_component_update_bits(component, reg, mask, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops sun20i_codec_dai_ops = { + .startup = sun20i_codec_startup, + .shutdown = sun20i_codec_shutdown, + .hw_params = sun20i_codec_hw_params, + .trigger = sun20i_codec_trigger, +}; + +static struct snd_soc_dai_driver sun20i_codec_dai = { + .name = DRIVER_NAME, + .probe = sun20i_codec_dai_probe, + .ops = &sun20i_codec_dai_ops, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 3, /* ??? */ + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SUN20I_CODEC_PCM_FORMATS, + .sig_bits = 20, + }, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SUN20I_CODEC_PCM_FORMATS, + .sig_bits = 20, + }, +}; + +static const DECLARE_TLV_DB_SCALE(sun20i_codec_boost_vol_scale, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(sun20i_codec_digital_vol_scale, -12000, 75, 1); +static const DECLARE_TLV_DB_SCALE(sun20i_codec_headphone_vol_scale, -4200, 600, 0); +/* FIXME */ +static const DECLARE_TLV_DB_SCALE(sun20i_codec_line_out_vol_scale, -4650, 150, 1); +/* FIXME */ +static const DECLARE_TLV_DB_SCALE(sun20i_codec_pga_vol_scale, 500, 100, 0); + +static const char *const sun20i_codec_line_out_mode_enum_text[] = { + "Single-Ended", "Differential" +}; + +static const SOC_ENUM_DOUBLE_DECL(sun20i_codec_line_out_mode_enum, + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LINEOUTL_DIFFEN, + SUN20I_CODEC_DAC_LINEOUTR_DIFFEN, + sun20i_codec_line_out_mode_enum_text); + +static const struct snd_kcontrol_new sun20i_codec_controls[] = { + /* Digital Controls */ + SOC_DOUBLE_TLV("DAC Playback Volume", + SUN20I_CODEC_DAC_VOL_CTRL, + SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_L, + SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_R, + 0xc0, 0, sun20i_codec_digital_vol_scale), + SOC_SINGLE_TLV("ADC3 Capture Volume", + SUN20I_CODEC_ADC_VOL_CTRL, + SUN20I_CODEC_ADC_VOL_CTRL_ADC3_VOL, + 0xc0, 0, sun20i_codec_digital_vol_scale), + SOC_SINGLE_TLV("ADC2 Capture Volume", + SUN20I_CODEC_ADC_VOL_CTRL, + SUN20I_CODEC_ADC_VOL_CTRL_ADC2_VOL, + 0xc0, 0, sun20i_codec_digital_vol_scale), + SOC_SINGLE_TLV("ADC1 Capture Volume", + SUN20I_CODEC_ADC_VOL_CTRL, + SUN20I_CODEC_ADC_VOL_CTRL_ADC1_VOL, + 0xc0, 0, sun20i_codec_digital_vol_scale), + + /* Analog Controls */ + SOC_DOUBLE_R_TLV("FM Capture Volume", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC1_FMINL_GAIN, + 0x1, 0, sun20i_codec_boost_vol_scale), + SOC_DOUBLE_R_TLV("Line In Capture Volume", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC1_LINEINL_GAIN, + 0x1, 0, sun20i_codec_boost_vol_scale), + SOC_ENUM("Line Out Mode Playback Enum", + sun20i_codec_line_out_mode_enum), + SOC_SINGLE_TLV("Line Out Playback Volume", + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LINEOUT_VOL_CTRL, + 0x1f, 0, sun20i_codec_line_out_vol_scale), + SOC_SINGLE_TLV("Headphone Playback Volume", + SUN20I_CODEC_HP2, + SUN20I_CODEC_HP2_HEADPHONE_GAIN, + 0x7, 1, sun20i_codec_headphone_vol_scale), +}; + +static const struct snd_kcontrol_new sun20i_codec_line_out_switch = + SOC_DAPM_DOUBLE("Line Out Playback Switch", + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LMUTE, + SUN20I_CODEC_DAC_RMUTE, 1, 1); + +static const struct snd_kcontrol_new sun20i_codec_hp_switch = + SOC_DAPM_SINGLE("Headphone Playback Switch", + SUN20I_CODEC_HP2, + SUN20I_CODEC_HP2_HP_DRVOUTEN, 1, 0); + +static const struct snd_kcontrol_new sun20i_codec_adc12_mixer_controls[] = { + /* ADC1 Only */ + SOC_DAPM_SINGLE("Mic1 Capture Switch", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC1_MICIN1_SIN_EN, 1, 0), + /* Shared */ + SOC_DAPM_DOUBLE_R("FM Capture Switch", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC1_FMINL_EN, 1, 0), + /* Shared */ + SOC_DAPM_DOUBLE_R("Line In Capture Switch", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC1_LINEINL_EN, 1, 0), + /* ADC2 Only */ + SOC_DAPM_SINGLE("Mic2 Capture Switch", + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC2_MICIN2_SIN_EN, 1, 0), +}; + +static const struct snd_kcontrol_new sun20i_codec_adc3_mixer_controls[] = { + SOC_DAPM_SINGLE("Mic3 Capture Switch", + SUN20I_CODEC_ADC3, + SUN20I_CODEC_ADC3_MICIN3_SIN_EN, 1, 0), +}; + +static const struct snd_kcontrol_new sun20i_codec_mic1_volume = + SOC_DAPM_SINGLE_TLV("Capture Volume", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC1_ADC1_PGA_GAIN, + 0x1f, 0, sun20i_codec_pga_vol_scale); + +static const struct snd_kcontrol_new sun20i_codec_mic2_volume = + SOC_DAPM_SINGLE_TLV("Capture Volume", + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC2_ADC2_PGA_GAIN, + 0x1f, 0, sun20i_codec_pga_vol_scale); + +static const struct snd_kcontrol_new sun20i_codec_mic3_volume = + SOC_DAPM_SINGLE_TLV("Capture Volume", + SUN20I_CODEC_ADC3, + SUN20I_CODEC_ADC3_ADC3_PGA_GAIN, + 0x1f, 0, sun20i_codec_pga_vol_scale); + +static const struct snd_soc_dapm_widget sun20i_codec_widgets[] = { + /* Playback */ + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), + + SND_SOC_DAPM_SWITCH("LINEOUTL Switch", + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LINEOUTL_EN, 0, + &sun20i_codec_line_out_switch), + SND_SOC_DAPM_SWITCH("LINEOUTR Switch", + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_LINEOUTR_EN, 0, + &sun20i_codec_line_out_switch), + + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + + SND_SOC_DAPM_SWITCH("HPOUTL Switch", + SND_SOC_NOPM, 0, 0, &sun20i_codec_hp_switch), + SND_SOC_DAPM_SWITCH("HPOUTR Switch", + SND_SOC_NOPM, 0, 0, &sun20i_codec_hp_switch), + SND_SOC_DAPM_SUPPLY("Headphone Driver", + SUN20I_CODEC_HP2, + SUN20I_CODEC_HP2_HP_DRVEN, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DACL", NULL, + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_DACL_EN, 0), + SND_SOC_DAPM_DAC("DACR", NULL, + SUN20I_CODEC_DAC, + SUN20I_CODEC_DAC_DACR_EN, 0), + SND_SOC_DAPM_SUPPLY("DAC", + SUN20I_CODEC_DAC_DPC, + SUN20I_CODEC_DAC_DPC_EN_DA, 0, NULL, 0), + + SND_SOC_DAPM_AIF_IN("DACL FIFO", "Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DACR FIFO", "Playback", 1, + SND_SOC_NOPM, 0, 0), + + /* Capture */ + SND_SOC_DAPM_AIF_OUT("ADC1 FIFO", "Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ADC2 FIFO", "Capture", 1, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ADC3 FIFO", "Capture", 2, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_ADC("ADC1", NULL, + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC1_ADC1_EN, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC2_ADC2_EN, 0), + SND_SOC_DAPM_ADC("ADC3", NULL, + SUN20I_CODEC_ADC3, + SUN20I_CODEC_ADC3_ADC3_EN, 0), + SND_SOC_DAPM_SUPPLY("ADC", + SUN20I_CODEC_ADC_FIFOC, + SUN20I_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_NAMED_CTL("ADC1 Mixer", SND_SOC_NOPM, 0, 0, + sun20i_codec_adc12_mixer_controls, 3), + SND_SOC_DAPM_MIXER_NAMED_CTL("ADC2 Mixer", SND_SOC_NOPM, 0, 0, + sun20i_codec_adc12_mixer_controls + 1, 3), + SND_SOC_DAPM_MIXER_NAMED_CTL("ADC3 Mixer", SND_SOC_NOPM, 0, 0, + sun20i_codec_adc3_mixer_controls, + ARRAY_SIZE(sun20i_codec_adc3_mixer_controls)), + + SND_SOC_DAPM_PGA("Mic1", + SUN20I_CODEC_ADC1, + SUN20I_CODEC_ADC1_MICIN1_PGA_EN, 0, + &sun20i_codec_mic1_volume, 1), + SND_SOC_DAPM_PGA("Mic2", + SUN20I_CODEC_ADC2, + SUN20I_CODEC_ADC2_MICIN2_PGA_EN, 0, + &sun20i_codec_mic2_volume, 1), + SND_SOC_DAPM_PGA("Mic3", + SUN20I_CODEC_ADC3, + SUN20I_CODEC_ADC3_MICIN3_PGA_EN, 0, + &sun20i_codec_mic3_volume, 1), + + SND_SOC_DAPM_INPUT("MICIN1"), + SND_SOC_DAPM_INPUT("MICIN2"), + SND_SOC_DAPM_INPUT("MICIN3"), + + SND_SOC_DAPM_INPUT("FMINL"), + SND_SOC_DAPM_INPUT("FMINR"), + + SND_SOC_DAPM_INPUT("LINEINL"), + SND_SOC_DAPM_INPUT("LINEINR"), + + SND_SOC_DAPM_SUPPLY("HBIAS", + SUN20I_CODEC_MICBIAS, + SUN20I_CODEC_MICBIAS_HMICBIASEN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", + SUN20I_CODEC_MICBIAS, + SUN20I_CODEC_MICBIAS_MMICBIASEN, 0, NULL, 0), + + SND_SOC_DAPM_REGULATOR_SUPPLY("avcc", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("hpvcc", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd33", 0, 0), +}; + +static const struct snd_soc_dapm_route sun20i_codec_routes[] = { + /* Playback */ + { "LINEOUTL", NULL, "LINEOUTL Switch" }, + { "LINEOUTR", NULL, "LINEOUTR Switch" }, + + { "LINEOUTL Switch", "Line Out Playback Switch", "DACL" }, + { "LINEOUTR Switch", "Line Out Playback Switch", "DACR" }, + + { "HPOUTL", NULL, "HPOUTL Switch" }, + { "HPOUTR", NULL, "HPOUTR Switch" }, + + { "HPOUTL Switch", "Headphone Playback Switch", "DACL" }, + { "HPOUTR Switch", "Headphone Playback Switch", "DACR" }, + { "HPOUTL Switch", NULL, "Headphone Driver" }, + { "HPOUTR Switch", NULL, "Headphone Driver" }, + { "Headphone Driver", NULL, "hpvcc" }, + + { "DACL", NULL, "DACL FIFO" }, + { "DACR", NULL, "DACR FIFO" }, + { "DACL", NULL, "DAC" }, + { "DACR", NULL, "DAC" }, + { "DACL", NULL, "avcc" }, + { "DACR", NULL, "avcc" }, + + /* Capture */ + { "ADC1 FIFO", NULL, "ADC1" }, + { "ADC2 FIFO", NULL, "ADC2" }, + { "ADC3 FIFO", NULL, "ADC3" }, + + { "ADC1", NULL, "ADC1 Mixer" }, + { "ADC2", NULL, "ADC2 Mixer" }, + { "ADC3", NULL, "ADC3 Mixer" }, + { "ADC1", NULL, "ADC" }, + { "ADC2", NULL, "ADC" }, + { "ADC3", NULL, "ADC" }, + { "ADC1", NULL, "avcc" }, + { "ADC2", NULL, "avcc" }, + { "ADC3", NULL, "avcc" }, + + { "ADC1 Mixer", "Mic1 Capture Switch", "Mic1" }, + { "ADC2 Mixer", "Mic2 Capture Switch", "Mic2" }, + { "ADC3 Mixer", "Mic3 Capture Switch", "Mic3" }, + { "ADC1 Mixer", "FM Capture Switch", "FMINL" }, + { "ADC2 Mixer", "FM Capture Switch", "FMINR" }, + { "ADC1 Mixer", "Line In Capture Switch", "LINEINL" }, + { "ADC2 Mixer", "Line In Capture Switch", "LINEINR" }, + + { "Mic1", NULL, "MICIN1" }, + { "Mic2", NULL, "MICIN2" }, + { "Mic3", NULL, "MICIN3" }, + + { "HBIAS", NULL, "vdd33" }, + { "MBIAS", NULL, "vdd33" }, +}; + +static int sun20i_codec_component_probe(struct snd_soc_component *component) +{ + struct sun20i_codec *codec = snd_soc_component_get_drvdata(component); + int ret; + + ret = reset_control_deassert(codec->reset); + if (ret) + return ret; + + ret = clk_prepare_enable(codec->bus_clk); + if (ret) + goto err_assert_reset; + + /* Enable digital volume control. */ + snd_soc_component_update_bits(component, SUN20I_CODEC_DAC_VOL_CTRL, + 0x1 << SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL, + 0x1 << SUN20I_CODEC_DAC_VOL_CTRL_DAC_VOL_SEL); + snd_soc_component_update_bits(component, SUN20I_CODEC_ADC_DIG_CTRL, + 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN, + 0x3 << SUN20I_CODEC_ADC_DIG_CTRL_ADC_VOL_EN); + + /* Maaagic... */ + snd_soc_component_update_bits(component, SUN20I_CODEC_RAMP, + BIT(1) | BIT(0), BIT(0)); + + return 0; + +err_assert_reset: + reset_control_assert(codec->reset); + + return ret; +} + +static void sun20i_codec_component_remove(struct snd_soc_component *component) +{ + struct sun20i_codec *codec = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(codec->bus_clk); + reset_control_assert(codec->reset); +} + +static const struct snd_soc_component_driver sun20i_codec_component = { + .controls = sun20i_codec_controls, + .num_controls = ARRAY_SIZE(sun20i_codec_controls), + .dapm_widgets = sun20i_codec_widgets, + .num_dapm_widgets = ARRAY_SIZE(sun20i_codec_widgets), + .dapm_routes = sun20i_codec_routes, + .num_dapm_routes = ARRAY_SIZE(sun20i_codec_routes), + .probe = sun20i_codec_component_probe, + .remove = sun20i_codec_component_remove, +}; + +static int sun20i_codec_init_card(struct device *dev, + struct sun20i_codec *codec) +{ + struct snd_soc_dai_link *dai_link = &codec->dai_link; + struct snd_soc_card *card = &codec->card; + int ret; + + codec->dlcs[0].of_node = dev->of_node; + codec->dlcs[0].dai_name = DRIVER_NAME; + codec->dlcs[1].name = "snd-soc-dummy"; + codec->dlcs[1].dai_name = "snd-soc-dummy-dai"; + codec->dlcs[2].of_node = dev->of_node; + + dai_link->name = DRIVER_NAME; + dai_link->stream_name = DRIVER_NAME; + dai_link->cpus = &codec->dlcs[0]; + dai_link->num_cpus = 1; + dai_link->codecs = &codec->dlcs[1]; + dai_link->num_codecs = 1; + dai_link->platforms = &codec->dlcs[2]; + dai_link->num_platforms = 1; + + card->name = DRIVER_NAME; + card->dev = dev; + card->owner = THIS_MODULE; + card->dai_link = dai_link; + card->num_links = 1; + card->fully_routed = true; + + ret = snd_soc_of_parse_audio_simple_widgets(card, PREFIX "widgets"); + if (ret) + return ret; + + ret = snd_soc_of_parse_audio_routing(card, PREFIX "routing"); + if (ret) + return ret; + + ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); + if (ret) + return ret; + + return 0; +} + +static const struct regmap_config sun20i_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SUN20I_CODEC_ADC_CUR, +}; + +static const struct regulator_ops sun20i_codec_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_desc sun20i_codec_ldos[] = { + { + .name = "aldo", + .supply_name = "vdd33", + .of_match = "aldo", + .regulators_node = "regulators", + .ops = &sun20i_codec_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = BIT(3), + .min_uV = 1650000, + .uV_step = 50000, + .vsel_reg = SUN20I_CODEC_POWER, + .vsel_mask = SUN20I_CODEC_POWER_ALDO_VOLTAGE_MASK, + .enable_reg = SUN20I_CODEC_POWER, + .enable_mask = SUN20I_CODEC_POWER_ALDO_EN_MASK, + }, + { + .name = "hpldo", + .supply_name = "hpldoin", + .of_match = "hpldo", + .regulators_node = "regulators", + .ops = &sun20i_codec_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = BIT(3), + .min_uV = 1650000, + .uV_step = 50000, + .vsel_reg = SUN20I_CODEC_POWER, + .vsel_mask = SUN20I_CODEC_POWER_HPLDO_VOLTAGE_MASK, + .enable_reg = SUN20I_CODEC_POWER, + .enable_mask = SUN20I_CODEC_POWER_HPLDO_EN_MASK, + }, +}; + +static int sun20i_codec_probe(struct platform_device *pdev) +{ + struct regulator_config config = { .dev = &pdev->dev }; + struct device *dev = &pdev->dev; + struct sun20i_codec *codec; + struct regulator_dev *rdev; + struct regmap *regmap; + struct resource *res; + void __iomem *base; + int i, ret; + + codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + dev_set_drvdata(dev, codec); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), + "Failed to map registers\n"); + + regmap = devm_regmap_init_mmio(dev, base, + &sun20i_codec_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to create regmap\n"); + + codec->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(codec->bus_clk)) + return dev_err_probe(dev, PTR_ERR(codec->bus_clk), + "Failed to get bus clock\n"); + + codec->adc_clk = devm_clk_get(dev, "adc"); + if (IS_ERR(codec->adc_clk)) + return dev_err_probe(dev, PTR_ERR(codec->adc_clk), + "Failed to get ADC clock\n"); + + codec->dac_clk = devm_clk_get(dev, "dac"); + if (IS_ERR(codec->dac_clk)) + return dev_err_probe(dev, PTR_ERR(codec->dac_clk), + "Failed to get DAC clock\n"); + + codec->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(codec->reset)) + return dev_err_probe(dev, PTR_ERR(codec->reset), + "Failed to get reset\n"); + + for (i = 0; i < ARRAY_SIZE(sun20i_codec_ldos); ++i) { + const struct regulator_desc *desc = &sun20i_codec_ldos[i]; + + rdev = devm_regulator_register(dev, desc, &config); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + } + + ret = devm_snd_soc_register_component(dev, &sun20i_codec_component, + &sun20i_codec_dai, 1); + if (ret) + return dev_err_probe(dev, ret, "Failed to register component\n"); + + codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = + res->start + SUN20I_CODEC_DAC_TXDATA; + codec->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 8; + codec->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = + res->start + SUN20I_CODEC_ADC_RXDATA; + codec->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 8; + + ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to register PCM\n"); + + ret = sun20i_codec_init_card(dev, codec); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize card\n"); + + ret = devm_snd_soc_register_card(dev, &codec->card); + if (ret) + return dev_err_probe(dev, ret, "Failed to register card\n"); + + return 0; +} + +static const struct of_device_id sun20i_codec_of_match[] = { + { .compatible = "allwinner,sun20i-d1-audio-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, sun20i_codec_of_match); + +static struct platform_driver sun20i_codec_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = sun20i_codec_of_match, + }, + .probe = sun20i_codec_probe, +}; +module_platform_driver(sun20i_codec_driver); + +MODULE_DESCRIPTION("Allwinner D1 (sun20i) codec driver"); +MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sun20i-codec"); @ sound/soc/sunxi/sun4i-i2s.c:41 @ #define SUN4I_I2S_FMT0_BCLK_POLARITY_MASK BIT(6) #define SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED (1 << 6) #define SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL (0 << 6) -#define SUN4I_I2S_FMT0_SR_MASK GENMASK(5, 4) -#define SUN4I_I2S_FMT0_SR(sr) ((sr) << 4) -#define SUN4I_I2S_FMT0_WSS_MASK GENMASK(3, 2) -#define SUN4I_I2S_FMT0_WSS(wss) ((wss) << 2) #define SUN4I_I2S_FMT0_FMT_MASK GENMASK(1, 0) #define SUN4I_I2S_FMT0_FMT_RIGHT_J (2 << 0) #define SUN4I_I2S_FMT0_FMT_LEFT_J (1 << 0) @ sound/soc/sunxi/sun4i-i2s.c:70 @ #define SUN4I_I2S_INT_STA_REG 0x20 #define SUN4I_I2S_CLK_DIV_REG 0x24 -#define SUN4I_I2S_CLK_DIV_MCLK_EN BIT(7) -#define SUN4I_I2S_CLK_DIV_BCLK_MASK GENMASK(6, 4) #define SUN4I_I2S_CLK_DIV_BCLK(bclk) ((bclk) << 4) #define SUN4I_I2S_CLK_DIV_MCLK_MASK GENMASK(3, 0) #define SUN4I_I2S_CLK_DIV_MCLK(mclk) ((mclk) << 0) @ sound/soc/sunxi/sun4i-i2s.c:112 @ #define SUN8I_I2S_FIFO_TX_REG 0x20 #define SUN8I_I2S_CHAN_CFG_REG 0x30 -#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(6, 4) +#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(7, 4) #define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(chan) ((chan - 1) << 4) -#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(2, 0) +#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(3, 0) #define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(chan) (chan - 1) #define SUN8I_I2S_TX_CHAN_MAP_REG 0x44 @ sound/soc/sunxi/sun4i-i2s.c:135 @ #define SUN50I_H6_I2S_TX_CHAN_EN_MASK GENMASK(15, 0) #define SUN50I_H6_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1)) -#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG 0x44 -#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG 0x48 +#define SUN50I_H6_I2S_TX_CHAN_SEL_REG(pin) (0x34 + 4 * (pin)) +#define SUN50I_H6_I2S_TX_CHAN_MAP0_REG(pin) (0x44 + 8 * (pin)) +#define SUN50I_H6_I2S_TX_CHAN_MAP1_REG(pin) (0x48 + 8 * (pin)) #define SUN50I_H6_I2S_RX_CHAN_SEL_REG 0x64 #define SUN50I_H6_I2S_RX_CHAN_MAP0_REG 0x68 #define SUN50I_H6_I2S_RX_CHAN_MAP1_REG 0x6C +#define SUN50I_R329_I2S_RX_CHAN_MAP0_REG 0x68 +#define SUN50I_R329_I2S_RX_CHAN_MAP1_REG 0x6c +#define SUN50I_R329_I2S_RX_CHAN_MAP2_REG 0x70 +#define SUN50I_R329_I2S_RX_CHAN_MAP3_REG 0x74 + struct sun4i_i2s; /** @ sound/soc/sunxi/sun4i-i2s.c:178 @ struct sun4i_i2s_quirks { struct reg_field field_fmt_wss; struct reg_field field_fmt_sr; + unsigned int num_din_pins; + unsigned int num_dout_pins; + const struct sun4i_i2s_clk_div *bclk_dividers; unsigned int num_bclk_dividers; const struct sun4i_i2s_clk_div *mclk_dividers; @ sound/soc/sunxi/sun4i-i2s.c:529 @ static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, unsigned int lrck_period; /* Map the channels for playback and capture */ - regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0xFEDCBA98); - regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x76543210); - regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98); - regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210); + regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0xFEDCBA98); + regmap_write(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x76543210); + if (i2s->variant->num_din_pins > 1) { + regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP0_REG, 0x0F0E0D0C); + regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP1_REG, 0x0B0A0908); + regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP2_REG, 0x07060504); + regmap_write(i2s->regmap, SUN50I_R329_I2S_RX_CHAN_MAP3_REG, 0x03020100); + } else { + regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0xFEDCBA98); + regmap_write(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x76543210); + } /* Configure the channels */ - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, + regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), SUN50I_H6_I2S_TX_CHAN_SEL_MASK, SUN50I_H6_I2S_TX_CHAN_SEL(channels)); regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_RX_CHAN_SEL_REG, @ sound/soc/sunxi/sun4i-i2s.c:576 @ static int sun50i_h6_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, SUN8I_I2S_FMT0_LRCK_PERIOD_MASK, SUN8I_I2S_FMT0_LRCK_PERIOD(lrck_period)); - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, + regmap_update_bits(i2s->regmap, SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), SUN50I_H6_I2S_TX_CHAN_EN_MASK, SUN50I_H6_I2S_TX_CHAN_EN(channels)); @ sound/soc/sunxi/sun4i-i2s.c:628 @ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, params_physical_width(params)); return -EINVAL; } + i2s->capture_dma_data.addr_width = width; i2s->playback_dma_data.addr_width = width; sr = i2s->variant->get_sr(word_size); @ sound/soc/sunxi/sun4i-i2s.c:1224 @ static const struct reg_default sun50i_h6_i2s_reg_defaults[] = { { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 }, { SUN4I_I2S_CLK_DIV_REG, 0x00000000 }, { SUN8I_I2S_CHAN_CFG_REG, 0x00000000 }, - { SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 }, - { SUN50I_H6_I2S_TX_CHAN_MAP0_REG, 0x00000000 }, - { SUN50I_H6_I2S_TX_CHAN_MAP1_REG, 0x00000000 }, + { SUN50I_H6_I2S_TX_CHAN_SEL_REG(0), 0x00000000 }, + { SUN50I_H6_I2S_TX_CHAN_MAP0_REG(0), 0x00000000 }, + { SUN50I_H6_I2S_TX_CHAN_MAP1_REG(0), 0x00000000 }, { SUN50I_H6_I2S_RX_CHAN_SEL_REG, 0x00000000 }, { SUN50I_H6_I2S_RX_CHAN_MAP0_REG, 0x00000000 }, { SUN50I_H6_I2S_RX_CHAN_MAP1_REG, 0x00000000 }, @ sound/soc/sunxi/sun4i-i2s.c:1263 @ static const struct regmap_config sun50i_h6_i2s_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = SUN50I_H6_I2S_RX_CHAN_MAP1_REG, + .max_register = SUN50I_R329_I2S_RX_CHAN_MAP3_REG, .cache_type = REGCACHE_FLAT, .reg_defaults = sun50i_h6_i2s_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sun50i_h6_i2s_reg_defaults), @ sound/soc/sunxi/sun4i-i2s.c:1448 @ static const struct sun4i_i2s_quirks sun50i_h6_i2s_quirks = { .set_fmt = sun50i_h6_i2s_set_soc_fmt, }; +static const struct sun4i_i2s_quirks sun50i_r329_i2s_quirks = { + .has_reset = true, + .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, + .sun4i_i2s_regmap = &sun50i_h6_i2s_regmap_config, + .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8), + .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2), + .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6), + .num_din_pins = 4, + .num_dout_pins = 4, + .bclk_dividers = sun8i_i2s_clk_div, + .num_bclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div), + .mclk_dividers = sun8i_i2s_clk_div, + .num_mclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div), + .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate, + .get_sr = sun8i_i2s_get_sr_wss, + .get_wss = sun8i_i2s_get_sr_wss, + .set_chan_cfg = sun50i_h6_i2s_set_chan_cfg, + .set_fmt = sun50i_h6_i2s_set_soc_fmt, +}; + static int sun4i_i2s_init_regmap_fields(struct device *dev, struct sun4i_i2s *i2s) { @ sound/soc/sunxi/sun4i-i2s.c:1640 @ static const struct of_device_id sun4i_i2s_match[] = { .compatible = "allwinner,sun50i-h6-i2s", .data = &sun50i_h6_i2s_quirks, }, + { + .compatible = "allwinner,sun50i-r329-i2s", + .data = &sun50i_r329_i2s_quirks, + }, {} }; MODULE_DEVICE_TABLE(of, sun4i_i2s_match); @ sound/soc/sunxi/sun4i-spdif.c:29 @ #include <sound/soc.h> #define SUN4I_SPDIF_CTL (0x00) + #define SUN4I_SPDIF_CTL_RST_RX BIT(12) #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */ #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2) #define SUN4I_SPDIF_CTL_GEN BIT(1) - #define SUN4I_SPDIF_CTL_RESET BIT(0) + #define SUN4I_SPDIF_CTL_RST_TX BIT(0) #define SUN4I_SPDIF_TXCFG (0x04) #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31) @ sound/soc/sunxi/sun4i-spdif.c:56 @ #define SUN4I_SPDIF_TXFIFO (0x0C) +#define SUN8I_SPDIF_ISTA (0x0C) + #define SUN4I_SPDIF_RXFIFO (0x10) #define SUN4I_SPDIF_FCTL (0x14) @ sound/soc/sunxi/sun4i-spdif.c:86 @ #define SUN4I_SPDIF_FSTA (0x18) #define SUN4I_SPDIF_FSTA_TXE BIT(14) - #define SUN4I_SPDIF_FSTA_TXECNTSHT (8) + #define SUN4I_SPDIF_FSTA_TXE_CNT (8) #define SUN4I_SPDIF_FSTA_RXA BIT(6) - #define SUN4I_SPDIF_FSTA_RXACNTSHT (0) + #define SUN4I_SPDIF_FSTA_RXA_CNT (0) + + #define SUN8I_SPDIF_FSTA_TXE BIT(31) + #define SUN8I_SPDIF_FSTA_TXE_CNT (16) + #define SUN8I_SPDIF_FSTA_RXA BIT(15) + #define SUN8I_SPDIF_FSTA_RXA_CNT (0) #define SUN4I_SPDIF_INT (0x1C) #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18) @ sound/soc/sunxi/sun4i-spdif.c:178 @ /** * struct sun4i_spdif_quirks - Differences between SoC variants. * + * @tx_clk_name: firmware name for the TX clock reference. * @reg_dac_txdata: TX FIFO offset for DMA config. - * @has_reset: SoC needs reset deasserted. * @val_fctl_ftx: TX FIFO flush bitmask. */ struct sun4i_spdif_quirks { + const char *tx_clk_name; unsigned int reg_dac_txdata; - bool has_reset; unsigned int val_fctl_ftx; }; struct sun4i_spdif_dev { struct platform_device *pdev; - struct clk *spdif_clk; struct clk *apb_clk; + struct clk *tx_clk; struct reset_control *rst; struct snd_soc_dai_driver cpu_dai_drv; struct regmap *regmap; @ sound/soc/sunxi/sun4i-spdif.c:204 @ static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) const struct sun4i_spdif_quirks *quirks = host->quirks; /* soft reset SPDIF */ - regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET); + regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RST_TX); /* flush TX FIFO */ regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, @ sound/soc/sunxi/sun4i-spdif.c:323 @ static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - ret = clk_set_rate(host->spdif_clk, mclk); + ret = clk_set_rate(host->tx_clk, mclk); if (ret < 0) { dev_err(&pdev->dev, "Setting SPDIF clock rate for %d Hz failed!\n", mclk); @ sound/soc/sunxi/sun4i-spdif.c:436 @ static struct snd_soc_dai_driver sun4i_spdif_dai = { }; static const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { + .tx_clk_name = "spdif", .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, }; static const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { + .tx_clk_name = "spdif", .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, - .has_reset = true, }; static const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { + .tx_clk_name = "spdif", .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, - .has_reset = true, +}; + +static const struct sun4i_spdif_quirks sun20i_d1_spdif_quirks = { + .tx_clk_name = "tx", + .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, + .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, }; static const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { + .tx_clk_name = "spdif", .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, - .has_reset = true, }; static const struct of_device_id sun4i_spdif_of_match[] = { @ sound/soc/sunxi/sun4i-spdif.c:478 @ static const struct of_device_id sun4i_spdif_of_match[] = { .compatible = "allwinner,sun8i-h3-spdif", .data = &sun8i_h3_spdif_quirks, }, + { + .compatible = "allwinner,sun20i-d1-spdif", + .data = &sun20i_d1_spdif_quirks, + }, { .compatible = "allwinner,sun50i-h6-spdif", .data = &sun50i_h6_spdif_quirks, @ sound/soc/sunxi/sun4i-spdif.c:498 @ static int sun4i_spdif_runtime_suspend(struct device *dev) { struct sun4i_spdif_dev *host = dev_get_drvdata(dev); - clk_disable_unprepare(host->spdif_clk); + clk_disable_unprepare(host->tx_clk); clk_disable_unprepare(host->apb_clk); return 0; @ sound/soc/sunxi/sun4i-spdif.c:509 @ static int sun4i_spdif_runtime_resume(struct device *dev) struct sun4i_spdif_dev *host = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(host->spdif_clk); + ret = clk_prepare_enable(host->tx_clk); if (ret) return ret; ret = clk_prepare_enable(host->apb_clk); if (ret) - clk_disable_unprepare(host->spdif_clk); + clk_disable_unprepare(host->tx_clk); return ret; } @ sound/soc/sunxi/sun4i-spdif.c:561 @ static int sun4i_spdif_probe(struct platform_device *pdev) return PTR_ERR(host->apb_clk); } - host->spdif_clk = devm_clk_get(&pdev->dev, "spdif"); - if (IS_ERR(host->spdif_clk)) { - dev_err(&pdev->dev, "failed to get a spdif clock.\n"); - return PTR_ERR(host->spdif_clk); + host->tx_clk = devm_clk_get(&pdev->dev, quirks->tx_clk_name); + if (IS_ERR(host->tx_clk)) { + dev_err(&pdev->dev, "failed to get TX module clock.\n"); + return PTR_ERR(host->tx_clk); } host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; @ sound/soc/sunxi/sun4i-spdif.c:573 @ static int sun4i_spdif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); - if (quirks->has_reset) { - host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, - NULL); - if (PTR_ERR(host->rst) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); - return ret; - } - if (!IS_ERR(host->rst)) - reset_control_deassert(host->rst); - } + host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(host->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->rst), + "Failed to get reset\n"); + + reset_control_deassert(host->rst); ret = devm_snd_soc_register_component(&pdev->dev, &sun4i_spdif_component, &sun4i_spdif_dai, 1); @ sound/soc/sunxi/sun4i-spdif.c:606 @ static int sun4i_spdif_probe(struct platform_device *pdev) static int sun4i_spdif_remove(struct platform_device *pdev) { + struct sun4i_spdif_dev *host = dev_get_drvdata(&pdev->dev); + pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) sun4i_spdif_runtime_suspend(&pdev->dev); + reset_control_assert(host->rst); + return 0; }