mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
Merge 605ea5aafe ("Merge tag 'spi-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi") into android-mainline
Steps on the way to 5.11-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I6c69d551d93dbf3868b56dfb7a5299577bca536d
This commit is contained in:
@@ -62,6 +62,20 @@ Required properties:
|
||||
- #power-domain-cells : Should be 1. Contains the device or the power
|
||||
domain ID value used by SCMI commands.
|
||||
|
||||
Regulator bindings for the SCMI Regulator based on SCMI Message Protocol
|
||||
------------------------------------------------------------
|
||||
An SCMI Regulator is permanently bound to a well defined SCMI Voltage Domain,
|
||||
and should be always positioned as a root regulator.
|
||||
It does not support any current operation.
|
||||
|
||||
SCMI Regulators are grouped under a 'regulators' node which in turn is a child
|
||||
of the SCMI Voltage protocol node inside the desired SCMI instance node.
|
||||
|
||||
This binding uses the common regulator binding[6].
|
||||
|
||||
Required properties:
|
||||
- reg : shall identify an existent SCMI Voltage Domain.
|
||||
|
||||
Sensor bindings for the sensors based on SCMI Message Protocol
|
||||
--------------------------------------------------------------
|
||||
SCMI provides an API to access the various sensors on the SoC.
|
||||
@@ -105,6 +119,7 @@ Required sub-node properties:
|
||||
[3] Documentation/devicetree/bindings/thermal/thermal*.yaml
|
||||
[4] Documentation/devicetree/bindings/sram/sram.yaml
|
||||
[5] Documentation/devicetree/bindings/reset/reset.txt
|
||||
[6] Documentation/devicetree/bindings/regulator/regulator.yaml
|
||||
|
||||
Example:
|
||||
|
||||
@@ -169,6 +184,25 @@ firmware {
|
||||
reg = <0x16>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
||||
scmi_voltage: protocol@17 {
|
||||
reg = <0x17>;
|
||||
|
||||
regulators {
|
||||
regulator_devX: regulator@0 {
|
||||
reg = <0x0>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
|
||||
regulator_devY: regulator@9 {
|
||||
reg = <0x9>;
|
||||
regulator-min-microvolt = <500000>;
|
||||
regulator-max-microvolt = <4200000>;
|
||||
};
|
||||
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
189
Documentation/devicetree/bindings/regulator/dlg,da9121.yaml
Normal file
189
Documentation/devicetree/bindings/regulator/dlg,da9121.yaml
Normal file
@@ -0,0 +1,189 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/dlg,da9121.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Dialog Semiconductor DA9121 voltage regulator
|
||||
|
||||
maintainers:
|
||||
- Adam Ward <Adam.Ward.opensource@diasemi.com>
|
||||
|
||||
description: |
|
||||
Dialog Semiconductor DA9121 Single-channel 10A double-phase buck converter
|
||||
Dialog Semiconductor DA9122 Double-channel 5A single-phase buck converter
|
||||
Dialog Semiconductor DA9220 Double-channel 3A single-phase buck converter
|
||||
Dialog Semiconductor DA9217 Single-channel 6A double-phase buck converter
|
||||
Dialog Semiconductor DA9130 Single-channel 10A double-phase buck converter
|
||||
Dialog Semiconductor DA9131 Double-channel 5A single-phase buck converter
|
||||
Dialog Semiconductor DA9132 Double-channel 3A single-phase buck converter
|
||||
|
||||
Current limits
|
||||
|
||||
This is PER PHASE, and the current limit setting in the devices reflect
|
||||
that with a maximum 10A limit. Allowing for transients at/near double
|
||||
the rated current, this translates across the device range to per
|
||||
channel figures as so...
|
||||
|
||||
| DA9121 DA9122 DA9220 DA9217 DA9140
|
||||
| /DA9130 /DA9131 /DA9132
|
||||
-----------------------------------------------------------------------------
|
||||
Output current / channel | 10000000 5000000 3000000 6000000 40000000
|
||||
Output current / phase | 5000000 5000000 3000000 3000000 9500000
|
||||
-----------------------------------------------------------------------------
|
||||
Min regulator-min-microvolt| 300000 300000 300000 300000 500000
|
||||
Max regulator-max-microvolt| 1900000 1900000 1900000 1900000 1000000
|
||||
Device hardware default | 1000000 1000000 1000000 1000000 1000000
|
||||
-----------------------------------------------------------------------------
|
||||
Min regulator-min-microamp | 7000000 3500000 3500000 7000000 26000000
|
||||
Max regulator-max-microamp | 20000000 10000000 6000000 12000000 78000000
|
||||
Device hardware default | 15000000 7500000 5500000 11000000 58000000
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "pmic@[0-9a-f]{1,2}"
|
||||
compatible:
|
||||
enum:
|
||||
- dlg,da9121
|
||||
- dlg,da9122
|
||||
- dlg,da9220
|
||||
- dlg,da9217
|
||||
- dlg,da9130
|
||||
- dlg,da9131
|
||||
- dlg,da9132
|
||||
- dlg,da9140
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: Specifies the I2C slave address.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: IRQ line information.
|
||||
|
||||
dlg,irq-polling-delay-passive-ms:
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
minimum: 1000
|
||||
maximum: 10000
|
||||
description: |
|
||||
Specify the polling period, measured in milliseconds, between interrupt status
|
||||
update checks. Range 1000-10000 ms.
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
$ref: regulator.yaml#
|
||||
description: |
|
||||
This node defines the settings for the BUCK. The content of the
|
||||
sub-node is defined by the standard binding for regulators; see regulator.yaml.
|
||||
The DA9121 regulator is bound using their names listed below
|
||||
buck1 - BUCK1
|
||||
buck2 - BUCK2 //DA9122, DA9220, DA9131, DA9132 only
|
||||
|
||||
patternProperties:
|
||||
"^buck([1-2])$":
|
||||
type: object
|
||||
$ref: regulator.yaml#
|
||||
|
||||
properties:
|
||||
regulator-mode:
|
||||
maxItems: 1
|
||||
description: Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h
|
||||
|
||||
regulator-initial-mode:
|
||||
maxItems: 1
|
||||
description: Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h
|
||||
|
||||
enable-gpios:
|
||||
maxItems: 1
|
||||
description: Specify a valid GPIO for platform control of the regulator
|
||||
|
||||
dlg,ripple-cancel:
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
description: |
|
||||
Defined in include/dt-bindings/regulator/dlg,da9121-regulator.h
|
||||
Only present on multi-channel devices (DA9122, DA9220, DA9131, DA9132)
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/regulator/dlg,da9121-regulator.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pmic@68 {
|
||||
compatible = "dlg,da9121";
|
||||
reg = <0x68>;
|
||||
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
dlg,irq-polling-delay-passive-ms = <2000>;
|
||||
|
||||
regulators {
|
||||
DA9121_BUCK1: buck1 {
|
||||
regulator-name = "BUCK1";
|
||||
regulator-min-microvolt = <300000>;
|
||||
regulator-max-microvolt = <1900000>;
|
||||
regulator-min-microamp = <7000000>;
|
||||
regulator-max-microamp = <20000000>;
|
||||
regulator-boot-on;
|
||||
regulator-initial-mode = <DA9121_BUCK_MODE_AUTO>;
|
||||
enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/regulator/dlg,da9121-regulator.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pmic@68 {
|
||||
compatible = "dlg,da9122";
|
||||
reg = <0x68>;
|
||||
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
dlg,irq-polling-delay-passive-ms = <2000>;
|
||||
|
||||
regulators {
|
||||
DA9122_BUCK1: buck1 {
|
||||
regulator-name = "BUCK1";
|
||||
regulator-min-microvolt = <300000>;
|
||||
regulator-max-microvolt = <1900000>;
|
||||
regulator-min-microamp = <3500000>;
|
||||
regulator-max-microamp = <10000000>;
|
||||
regulator-boot-on;
|
||||
regulator-initial-mode = <DA9121_BUCK_MODE_AUTO>;
|
||||
enable-gpios = <&gpio6 1 GPIO_ACTIVE_HIGH>;
|
||||
dlg,ripple-cancel = <DA9121_BUCK_RIPPLE_CANCEL_NONE>;
|
||||
};
|
||||
DA9122_BUCK2: buck2 {
|
||||
regulator-name = "BUCK2";
|
||||
regulator-min-microvolt = <300000>;
|
||||
regulator-max-microvolt = <1900000>;
|
||||
regulator-min-microamp = <3500000>;
|
||||
regulator-max-microamp = <10000000>;
|
||||
regulator-boot-on;
|
||||
regulator-initial-mode = <DA9121_BUCK_MODE_AUTO>;
|
||||
enable-gpios = <&gpio6 2 GPIO_ACTIVE_HIGH>;
|
||||
dlg,ripple-cancel = <DA9121_BUCK_RIPPLE_CANCEL_NONE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -26,12 +26,22 @@ if:
|
||||
const: regulator-fixed-clock
|
||||
required:
|
||||
- clocks
|
||||
else:
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: regulator-fixed-domain
|
||||
required:
|
||||
- power-domains
|
||||
- required-opps
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- regulator-fixed
|
||||
- regulator-fixed-clock
|
||||
- regulator-fixed-domain
|
||||
|
||||
regulator-name: true
|
||||
|
||||
@@ -46,6 +56,20 @@ properties:
|
||||
is mandatory if compatible is chosen to regulator-fixed-clock.
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
description:
|
||||
Power domain to use for enable control. This binding is only
|
||||
available if the compatible is chosen to regulator-fixed-domain.
|
||||
maxItems: 1
|
||||
|
||||
required-opps:
|
||||
description:
|
||||
Performance state to use for enable control. This binding is only
|
||||
available if the compatible is chosen to regulator-fixed-domain. The
|
||||
power-domain binding is mandatory if compatible is chosen to
|
||||
regulator-fixed-domain.
|
||||
maxItems: 1
|
||||
|
||||
startup-delay-us:
|
||||
description: startup time in microseconds
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
@@ -89,4 +113,27 @@ examples:
|
||||
gpio-open-drain;
|
||||
vin-supply = <&parent_reg>;
|
||||
};
|
||||
reg_1v8_clk: regulator-1v8-clk {
|
||||
compatible = "regulator-fixed-clock";
|
||||
regulator-name = "1v8";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
clocks = <&clock1>;
|
||||
startup-delay-us = <70000>;
|
||||
enable-active-high;
|
||||
regulator-boot-on;
|
||||
vin-supply = <&parent_reg>;
|
||||
};
|
||||
reg_1v8_domain: regulator-1v8-domain {
|
||||
compatible = "regulator-fixed-domain";
|
||||
regulator-name = "1v8";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
power-domains = <&domain1>;
|
||||
required-opps = <&domain1_state1>;
|
||||
startup-delay-us = <70000>;
|
||||
enable-active-high;
|
||||
regulator-boot-on;
|
||||
vin-supply = <&parent_reg>;
|
||||
};
|
||||
...
|
||||
|
||||
@@ -10,7 +10,7 @@ Required properties:
|
||||
name. The content of each sub-node is defined by the
|
||||
standard binding for regulators; see regulator.txt.
|
||||
|
||||
Regualtors of MCP16502 PMIC:
|
||||
Regulators of MCP16502 PMIC:
|
||||
1) VDD_IO - Buck (1.2 - 3.7 V)
|
||||
2) VDD_DDR - Buck (0.6 - 1.85 V)
|
||||
3) VDD_CORE - Buck (0.6 - 1.85 V)
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/nxp,pf8x00-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NXP PF8100/PF8121A/PF8200 PMIC regulators
|
||||
|
||||
maintainers:
|
||||
- Jagan Teki <jagan@amarulasolutions.com>
|
||||
- Troy Kisky <troy.kisky@boundarydevices.com>
|
||||
|
||||
description: |
|
||||
PF8100/PF8121A/PF8200 is a PMIC designed for highperformance consumer
|
||||
applications. It features seven high efficiency buck converters, four
|
||||
linear and one vsnvs regulators. It has built-in one time programmable
|
||||
fuse bank for device configurations.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nxp,pf8x00
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
description: |
|
||||
list of regulators provided by this controller
|
||||
|
||||
patternProperties:
|
||||
"^ldo[1-4]$":
|
||||
type: object
|
||||
$ref: regulator.yaml#
|
||||
description:
|
||||
Properties for single LDO regulator.
|
||||
|
||||
properties:
|
||||
regulator-name:
|
||||
pattern: "^ldo[1-4]$"
|
||||
description:
|
||||
should be "ldo1", ..., "ldo4"
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^buck[1-7]$":
|
||||
type: object
|
||||
$ref: regulator.yaml#
|
||||
description:
|
||||
Properties for single BUCK regulator.
|
||||
|
||||
properties:
|
||||
regulator-name:
|
||||
pattern: "^buck[1-7]$"
|
||||
description:
|
||||
should be "buck1", ..., "buck7"
|
||||
|
||||
nxp,ilim-ma:
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
minimum: 2100
|
||||
maximum: 4500
|
||||
description:
|
||||
BUCK regulators current limit in mA.
|
||||
|
||||
Listed current limits in mA are,
|
||||
2100 (default)
|
||||
2600
|
||||
3000
|
||||
4500
|
||||
|
||||
nxp,phase-shift:
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
minimum: 45
|
||||
maximum: 0
|
||||
description:
|
||||
BUCK regulators phase shift control in degrees.
|
||||
|
||||
Listed phase shift control values in degrees are,
|
||||
45
|
||||
90
|
||||
135
|
||||
180
|
||||
225
|
||||
270
|
||||
315
|
||||
0 (default)
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
"^vsnvs$":
|
||||
type: object
|
||||
$ref: regulator.yaml#
|
||||
description:
|
||||
Properties for single VSNVS regulator.
|
||||
|
||||
properties:
|
||||
regulator-name:
|
||||
pattern: "^vsnvs$"
|
||||
description:
|
||||
should be "vsnvs"
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
pmic@8 {
|
||||
compatible = "nxp,pf8x00";
|
||||
reg = <0x08>;
|
||||
|
||||
regulators {
|
||||
reg_ldo1: ldo1 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-min-microvolt = <1500000>;
|
||||
};
|
||||
|
||||
reg_ldo2: ldo2 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-min-microvolt = <1500000>;
|
||||
};
|
||||
|
||||
reg_ldo3: ldo3 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-min-microvolt = <1500000>;
|
||||
};
|
||||
|
||||
reg_ldo4: ldo4 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-min-microvolt = <1500000>;
|
||||
};
|
||||
|
||||
reg_buck1: buck1 {
|
||||
nxp,ilim-ma = <4500>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-min-microvolt = <400000>;
|
||||
};
|
||||
|
||||
reg_buck2: buck2 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-min-microvolt = <400000>;
|
||||
};
|
||||
|
||||
reg_buck3: buck3 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-min-microvolt = <400000>;
|
||||
};
|
||||
|
||||
reg_buck4: buck4 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-min-microvolt = <400000>;
|
||||
};
|
||||
|
||||
reg_buck5: buck5 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-min-microvolt = <400000>;
|
||||
};
|
||||
|
||||
reg_buck6: buck6 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-min-microvolt = <400000>;
|
||||
};
|
||||
|
||||
reg_buck7: buck7 {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-min-microvolt = <3300000>;
|
||||
};
|
||||
|
||||
reg_vsnvs: vsnvs {
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-min-microvolt = <1800000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -26,10 +26,13 @@ Supported regulator node names:
|
||||
PM8009: smps1 - smps2, ldo1 - ldo7
|
||||
PM8150: smps1 - smps10, ldo1 - ldo18
|
||||
PM8150L: smps1 - smps8, ldo1 - ldo11, bob, flash, rgb
|
||||
PM8350: smps1 - smps12, ldo1 - ldo10,
|
||||
PM8350C: smps1 - smps10, ldo1 - ldo13, bob
|
||||
PM8998: smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
|
||||
PMI8998: bob
|
||||
PM6150: smps1 - smps5, ldo1 - ldo19
|
||||
PM6150L: smps1 - smps8, ldo1 - ldo11, bob
|
||||
PMX55: smps1 - smps7, ldo1 - ldo16
|
||||
|
||||
========================
|
||||
First Level Nodes - PMIC
|
||||
@@ -43,10 +46,13 @@ First Level Nodes - PMIC
|
||||
"qcom,pm8009-rpmh-regulators"
|
||||
"qcom,pm8150-rpmh-regulators"
|
||||
"qcom,pm8150l-rpmh-regulators"
|
||||
"qcom,pm8350-rpmh-regulators"
|
||||
"qcom,pm8350c-rpmh-regulators"
|
||||
"qcom,pm8998-rpmh-regulators"
|
||||
"qcom,pmi8998-rpmh-regulators"
|
||||
"qcom,pm6150-rpmh-regulators"
|
||||
"qcom,pm6150l-rpmh-regulators"
|
||||
"qcom,pmx55-rpmh-regulators"
|
||||
|
||||
- qcom,pmic-id
|
||||
Usage: required
|
||||
|
||||
@@ -105,6 +105,54 @@ patternProperties:
|
||||
PMIC hardware state machine.
|
||||
type: boolean
|
||||
|
||||
# Setups where regulator (especially the buck8) output voltage is scaled
|
||||
# by adding external connection where some other regulator output is
|
||||
# connected to feedback-pin (over suitable resistors) is getting popular
|
||||
# amongst users of BD71837. (This allows for example scaling down the
|
||||
# buck8 voltages to suit lover GPU voltages for projects where buck8 is
|
||||
# (ab)used to supply power for GPU.
|
||||
#
|
||||
# So we allow describing this external connection from DT and scale the
|
||||
# voltages accordingly. This is what the connection should look like:
|
||||
#
|
||||
# |---------------|
|
||||
# | buck 8 |-------+----->Vout
|
||||
# | | |
|
||||
# |---------------| |
|
||||
# | |
|
||||
# | |
|
||||
# +-------+--R2----+
|
||||
# |
|
||||
# R1
|
||||
# |
|
||||
# V FB-pull-up
|
||||
#
|
||||
# Here the buck output is sifted according to formula:
|
||||
#
|
||||
# Vout_o = Vo - (Vpu - Vo)*R2/R1
|
||||
# Linear_step = step_orig*(R1+R2)/R1
|
||||
#
|
||||
# where:
|
||||
# Vout_o is adjusted voltage output at vsel reg value 0
|
||||
# Vo is original voltage output at vsel reg value 0
|
||||
# Vpu is the pull-up voltage V FB-pull-up in the picture
|
||||
# R1 and R2 are resistor values.
|
||||
|
||||
rohm,fb-pull-up-microvolt:
|
||||
description:
|
||||
Feedback-pin has pull-up connection to adjust voltage range. This is
|
||||
the used pull-up voltage before R1.
|
||||
|
||||
rohm,feedback-pull-up-r1-ohms:
|
||||
description:
|
||||
Feedback-pin has pull-up connection to adjust voltage range. This is
|
||||
the used R1 resistor.
|
||||
|
||||
rohm,feedback-pull-up-r2-ohms:
|
||||
description:
|
||||
Feedback-pin has pull-up connection to adjust voltage range. This is
|
||||
the used R2 resistor.
|
||||
|
||||
required:
|
||||
- regulator-name
|
||||
|
||||
|
||||
@@ -99,6 +99,55 @@ patternProperties:
|
||||
Enable/Disable control of this regulator must be left to the
|
||||
PMIC hardware state machine.
|
||||
type: boolean
|
||||
|
||||
# Setups where regulator (especially the buck8) output voltage is scaled
|
||||
# by adding external connection where some other regulator output is
|
||||
# connected to feedback-pin (over suitable resistors) is getting popular
|
||||
# amongst users of BD71837. (This allows for example scaling down the
|
||||
# buck8 voltages to suit lover GPU voltages for projects where buck8 is
|
||||
# (ab)used to supply power for GPU.
|
||||
#
|
||||
# So we allow describing this external connection from DT and scale the
|
||||
# voltages accordingly. This is what the connection should look like:
|
||||
#
|
||||
# |---------------|
|
||||
# | buck 8 |-------+----->Vout
|
||||
# | | |
|
||||
# |---------------| |
|
||||
# | |
|
||||
# | |
|
||||
# +-------+--R2----+
|
||||
# |
|
||||
# R1
|
||||
# |
|
||||
# V FB-pull-up
|
||||
#
|
||||
# Here the buck output is sifted according to formula:
|
||||
#
|
||||
# Vout_o = Vo - (Vpu - Vo)*R2/R1
|
||||
# Linear_step = step_orig*(R1+R2)/R1
|
||||
#
|
||||
# where:
|
||||
# Vout_o is adjusted voltage output at vsel reg value 0
|
||||
# Vo is original voltage output at vsel reg value 0
|
||||
# Vpu is the pull-up voltage V FB-pull-up in the picture
|
||||
# R1 and R2 are resistor values.
|
||||
|
||||
rohm,fb-pull-up-microvolt:
|
||||
description:
|
||||
Feedback-pin has pull-up connection to adjust voltage range. This is
|
||||
the used pull-up voltage before R1.
|
||||
|
||||
rohm,feedback-pull-up-r1-ohms:
|
||||
description:
|
||||
Feedback-pin has pull-up connection to adjust voltage range. This is
|
||||
the used R1 resistor.
|
||||
|
||||
rohm,feedback-pull-up-r2-ohms:
|
||||
description:
|
||||
Feedback-pin has pull-up connection to adjust voltage range. This is
|
||||
the used R2 resistor.
|
||||
|
||||
required:
|
||||
- regulator-name
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ properties:
|
||||
const: baikal,bt1-ssi
|
||||
- description: Baikal-T1 System Boot SPI Controller
|
||||
const: baikal,bt1-sys-ssi
|
||||
- description: Canaan Kendryte K210 SoS SPI Controller
|
||||
const: canaan,k210-spi
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
|
||||
@@ -42,6 +42,33 @@ properties:
|
||||
cs2 : &gpio1 1 0
|
||||
cs3 : &gpio1 2 0
|
||||
|
||||
The second flag of a gpio descriptor can be GPIO_ACTIVE_HIGH (0)
|
||||
or GPIO_ACTIVE_LOW(1). Legacy device trees often use 0.
|
||||
|
||||
There is a special rule set for combining the second flag of an
|
||||
cs-gpio with the optional spi-cs-high flag for SPI slaves.
|
||||
|
||||
Each table entry defines how the CS pin is to be physically
|
||||
driven (not considering potential gpio inversions by pinmux):
|
||||
|
||||
device node | cs-gpio | CS pin state active | Note
|
||||
================+===============+=====================+=====
|
||||
spi-cs-high | - | H |
|
||||
- | - | L |
|
||||
spi-cs-high | ACTIVE_HIGH | H |
|
||||
- | ACTIVE_HIGH | L | 1
|
||||
spi-cs-high | ACTIVE_LOW | H | 2
|
||||
- | ACTIVE_LOW | L |
|
||||
|
||||
Notes:
|
||||
1) Should print a warning about polarity inversion.
|
||||
Here it would be wise to avoid and define the gpio as
|
||||
ACTIVE_LOW.
|
||||
2) Should print a warning about polarity inversion
|
||||
because ACTIVE_LOW is overridden by spi-cs-high.
|
||||
Should be generally avoided and be replaced by
|
||||
spi-cs-high + ACTIVE_HIGH.
|
||||
|
||||
num-cs:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
|
||||
@@ -17,15 +17,17 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: sifive,fu540-c000-spi
|
||||
- enum:
|
||||
- sifive,fu540-c000-spi
|
||||
- sifive,fu740-c000-spi
|
||||
- const: sifive,spi0
|
||||
|
||||
description:
|
||||
Should be "sifive,<chip>-spi" and "sifive,spi<version>".
|
||||
Supported compatible strings are -
|
||||
"sifive,fu540-c000-spi" for the SiFive SPI v0 as integrated
|
||||
onto the SiFive FU540 chip, and "sifive,spi0" for the SiFive
|
||||
SPI v0 IP block with no chip integration tweaks.
|
||||
"sifive,fu540-c000-spi" and "sifive,fu740-c000-spi" for the SiFive SPI v0
|
||||
as integrated onto the SiFive FU540 and FU740 chip resp, and "sifive,spi0"
|
||||
for the SiFive SPI v0 IP block with no chip integration tweaks.
|
||||
Please refer to sifive-blocks-ip-versioning.txt for details
|
||||
|
||||
SPI RTL that corresponds to the IP block version numbers can be found here -
|
||||
|
||||
@@ -5138,6 +5138,7 @@ S: Supported
|
||||
W: http://www.dialog-semiconductor.com/products
|
||||
F: Documentation/devicetree/bindings/input/da90??-onkey.txt
|
||||
F: Documentation/devicetree/bindings/mfd/da90*.txt
|
||||
F: Documentation/devicetree/bindings/regulator/dlg,da9*.yaml
|
||||
F: Documentation/devicetree/bindings/regulator/da92*.txt
|
||||
F: Documentation/devicetree/bindings/regulator/slg51000.txt
|
||||
F: Documentation/devicetree/bindings/sound/da[79]*.txt
|
||||
@@ -5162,6 +5163,7 @@ F: drivers/rtc/rtc-da90??.c
|
||||
F: drivers/thermal/da90??-thermal.c
|
||||
F: drivers/video/backlight/da90??_bl.c
|
||||
F: drivers/watchdog/da90??_wdt.c
|
||||
F: include/dt-bindings/regulator/dlg,da9*-regulator.h
|
||||
F: include/linux/mfd/da903x.h
|
||||
F: include/linux/mfd/da9052/
|
||||
F: include/linux/mfd/da9055/
|
||||
@@ -12679,6 +12681,12 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml
|
||||
F: drivers/gpu/drm/imx/dcss/
|
||||
|
||||
NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
|
||||
M: Jagan Teki <jagan@amarulasolutions.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml
|
||||
F: drivers/regulator/pf8x00-regulator.c
|
||||
|
||||
NXP PTN5150A CC LOGIC AND EXTCON DRIVER
|
||||
M: Krzysztof Kozlowski <krzk@kernel.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
struct regmap_mmio_context {
|
||||
void __iomem *regs;
|
||||
unsigned val_bytes;
|
||||
bool relaxed_mmio;
|
||||
|
||||
bool attached_clk;
|
||||
struct clk *clk;
|
||||
@@ -75,6 +76,13 @@ static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
|
||||
writeb(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void regmap_mmio_write8_relaxed(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
writeb_relaxed(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg,
|
||||
unsigned int val)
|
||||
@@ -82,6 +90,13 @@ static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
|
||||
writew(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void regmap_mmio_write16le_relaxed(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
writew_relaxed(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg,
|
||||
unsigned int val)
|
||||
@@ -96,6 +111,13 @@ static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
|
||||
writel(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void regmap_mmio_write32le_relaxed(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
writel_relaxed(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg,
|
||||
unsigned int val)
|
||||
@@ -110,6 +132,13 @@ static void regmap_mmio_write64le(struct regmap_mmio_context *ctx,
|
||||
{
|
||||
writeq(val, ctx->regs + reg);
|
||||
}
|
||||
|
||||
static void regmap_mmio_write64le_relaxed(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
writeq_relaxed(val, ctx->regs + reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
|
||||
@@ -137,12 +166,24 @@ static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
|
||||
return readb(ctx->regs + reg);
|
||||
}
|
||||
|
||||
static unsigned int regmap_mmio_read8_relaxed(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg)
|
||||
{
|
||||
return readb_relaxed(ctx->regs + reg);
|
||||
}
|
||||
|
||||
static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg)
|
||||
{
|
||||
return readw(ctx->regs + reg);
|
||||
}
|
||||
|
||||
static unsigned int regmap_mmio_read16le_relaxed(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg)
|
||||
{
|
||||
return readw_relaxed(ctx->regs + reg);
|
||||
}
|
||||
|
||||
static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg)
|
||||
{
|
||||
@@ -155,6 +196,12 @@ static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
|
||||
return readl(ctx->regs + reg);
|
||||
}
|
||||
|
||||
static unsigned int regmap_mmio_read32le_relaxed(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg)
|
||||
{
|
||||
return readl_relaxed(ctx->regs + reg);
|
||||
}
|
||||
|
||||
static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg)
|
||||
{
|
||||
@@ -167,6 +214,12 @@ static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx,
|
||||
{
|
||||
return readq(ctx->regs + reg);
|
||||
}
|
||||
|
||||
static unsigned int regmap_mmio_read64le_relaxed(struct regmap_mmio_context *ctx,
|
||||
unsigned int reg)
|
||||
{
|
||||
return readq_relaxed(ctx->regs + reg);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
|
||||
@@ -237,6 +290,7 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
|
||||
|
||||
ctx->regs = regs;
|
||||
ctx->val_bytes = config->val_bits / 8;
|
||||
ctx->relaxed_mmio = config->use_relaxed_mmio;
|
||||
ctx->clk = ERR_PTR(-ENODEV);
|
||||
|
||||
switch (regmap_get_val_endian(dev, ®map_mmio, config)) {
|
||||
@@ -247,21 +301,41 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
|
||||
#endif
|
||||
switch (config->val_bits) {
|
||||
case 8:
|
||||
ctx->reg_read = regmap_mmio_read8;
|
||||
ctx->reg_write = regmap_mmio_write8;
|
||||
if (ctx->relaxed_mmio) {
|
||||
ctx->reg_read = regmap_mmio_read8_relaxed;
|
||||
ctx->reg_write = regmap_mmio_write8_relaxed;
|
||||
} else {
|
||||
ctx->reg_read = regmap_mmio_read8;
|
||||
ctx->reg_write = regmap_mmio_write8;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
ctx->reg_read = regmap_mmio_read16le;
|
||||
ctx->reg_write = regmap_mmio_write16le;
|
||||
if (ctx->relaxed_mmio) {
|
||||
ctx->reg_read = regmap_mmio_read16le_relaxed;
|
||||
ctx->reg_write = regmap_mmio_write16le_relaxed;
|
||||
} else {
|
||||
ctx->reg_read = regmap_mmio_read16le;
|
||||
ctx->reg_write = regmap_mmio_write16le;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
ctx->reg_read = regmap_mmio_read32le;
|
||||
ctx->reg_write = regmap_mmio_write32le;
|
||||
if (ctx->relaxed_mmio) {
|
||||
ctx->reg_read = regmap_mmio_read32le_relaxed;
|
||||
ctx->reg_write = regmap_mmio_write32le_relaxed;
|
||||
} else {
|
||||
ctx->reg_read = regmap_mmio_read32le;
|
||||
ctx->reg_write = regmap_mmio_write32le;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 64:
|
||||
ctx->reg_read = regmap_mmio_read64le;
|
||||
ctx->reg_write = regmap_mmio_write64le;
|
||||
if (ctx->relaxed_mmio) {
|
||||
ctx->reg_read = regmap_mmio_read64le_relaxed;
|
||||
ctx->reg_write = regmap_mmio_write64le_relaxed;
|
||||
} else {
|
||||
ctx->reg_read = regmap_mmio_read64le;
|
||||
ctx->reg_write = regmap_mmio_write64le;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// Copyright(c) 2015-17 Intel Corporation.
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
@@ -1924,12 +1924,15 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
||||
}
|
||||
}
|
||||
|
||||
if (regmap_should_log(map))
|
||||
dev_info(map->dev, "%x <= %x\n", reg, val);
|
||||
ret = map->reg_write(context, reg, val);
|
||||
if (ret == 0) {
|
||||
if (regmap_should_log(map))
|
||||
dev_info(map->dev, "%x <= %x\n", reg, val);
|
||||
|
||||
trace_regmap_reg_write(map, reg, val);
|
||||
trace_regmap_reg_write(map, reg, val);
|
||||
}
|
||||
|
||||
return map->reg_write(context, reg, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -126,7 +126,6 @@ TRACE_EVENT(regcache_sync,
|
||||
__string( name, regmap_name(map) )
|
||||
__string( status, status )
|
||||
__string( type, type )
|
||||
__field( int, type )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
|
||||
@@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o
|
||||
scmi-transport-y = shmem.o
|
||||
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
|
||||
scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o
|
||||
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o
|
||||
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
|
||||
$(scmi-transport-y)
|
||||
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
|
||||
|
||||
@@ -169,6 +169,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(perf);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(power);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(reset);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(sensors);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(voltage);
|
||||
DECLARE_SCMI_REGISTER_UNREGISTER(system);
|
||||
|
||||
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \
|
||||
|
||||
@@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = {
|
||||
{ SCMI_PROTOCOL_CLOCK, { "clocks" },},
|
||||
{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
|
||||
{ SCMI_PROTOCOL_RESET, { "reset" },},
|
||||
{ SCMI_PROTOCOL_VOLTAGE, { "regulator" },},
|
||||
};
|
||||
|
||||
static inline void
|
||||
@@ -946,6 +947,7 @@ static int __init scmi_driver_init(void)
|
||||
scmi_power_register();
|
||||
scmi_reset_register();
|
||||
scmi_sensors_register();
|
||||
scmi_voltage_register();
|
||||
scmi_system_register();
|
||||
|
||||
return platform_driver_register(&scmi_driver);
|
||||
@@ -961,6 +963,7 @@ static void __exit scmi_driver_exit(void)
|
||||
scmi_power_unregister();
|
||||
scmi_reset_unregister();
|
||||
scmi_sensors_unregister();
|
||||
scmi_voltage_unregister();
|
||||
scmi_system_unregister();
|
||||
|
||||
platform_driver_unregister(&scmi_driver);
|
||||
|
||||
380
drivers/firmware/arm_scmi/voltage.c
Normal file
380
drivers/firmware/arm_scmi/voltage.c
Normal file
@@ -0,0 +1,380 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* System Control and Management Interface (SCMI) Voltage Protocol
|
||||
*
|
||||
* Copyright (C) 2020 ARM Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0)
|
||||
#define REMAINING_LEVELS_MASK GENMASK(31, 16)
|
||||
#define RETURNED_LEVELS_MASK GENMASK(11, 0)
|
||||
|
||||
enum scmi_voltage_protocol_cmd {
|
||||
VOLTAGE_DOMAIN_ATTRIBUTES = 0x3,
|
||||
VOLTAGE_DESCRIBE_LEVELS = 0x4,
|
||||
VOLTAGE_CONFIG_SET = 0x5,
|
||||
VOLTAGE_CONFIG_GET = 0x6,
|
||||
VOLTAGE_LEVEL_SET = 0x7,
|
||||
VOLTAGE_LEVEL_GET = 0x8,
|
||||
};
|
||||
|
||||
#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
|
||||
|
||||
struct scmi_msg_resp_domain_attributes {
|
||||
__le32 attr;
|
||||
u8 name[SCMI_MAX_STR_SIZE];
|
||||
};
|
||||
|
||||
struct scmi_msg_cmd_describe_levels {
|
||||
__le32 domain_id;
|
||||
__le32 level_index;
|
||||
};
|
||||
|
||||
struct scmi_msg_resp_describe_levels {
|
||||
__le32 flags;
|
||||
#define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f))))
|
||||
#define NUM_RETURNED_LEVELS(f) ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f))))
|
||||
#define SUPPORTS_SEGMENTED_LEVELS(f) ((f) & BIT(12))
|
||||
__le32 voltage[];
|
||||
};
|
||||
|
||||
struct scmi_msg_cmd_config_set {
|
||||
__le32 domain_id;
|
||||
__le32 config;
|
||||
};
|
||||
|
||||
struct scmi_msg_cmd_level_set {
|
||||
__le32 domain_id;
|
||||
__le32 flags;
|
||||
__le32 voltage_level;
|
||||
};
|
||||
|
||||
struct voltage_info {
|
||||
unsigned int version;
|
||||
unsigned int num_domains;
|
||||
struct scmi_voltage_info *domains;
|
||||
};
|
||||
|
||||
static int scmi_protocol_attributes_get(const struct scmi_handle *handle,
|
||||
struct voltage_info *vinfo)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
|
||||
SCMI_PROTOCOL_VOLTAGE, 0, sizeof(__le32), &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret)
|
||||
vinfo->num_domains =
|
||||
NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf));
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_init_voltage_levels(struct device *dev,
|
||||
struct scmi_voltage_info *v,
|
||||
u32 num_returned, u32 num_remaining,
|
||||
bool segmented)
|
||||
{
|
||||
u32 num_levels;
|
||||
|
||||
num_levels = num_returned + num_remaining;
|
||||
/*
|
||||
* segmented levels entries are represented by a single triplet
|
||||
* returned all in one go.
|
||||
*/
|
||||
if (!num_levels ||
|
||||
(segmented && (num_remaining || num_returned != 3))) {
|
||||
dev_err(dev,
|
||||
"Invalid level descriptor(%d/%d/%d) for voltage dom %d\n",
|
||||
num_levels, num_returned, num_remaining, v->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL);
|
||||
if (!v->levels_uv)
|
||||
return -ENOMEM;
|
||||
|
||||
v->num_levels = num_levels;
|
||||
v->segmented = segmented;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_voltage_descriptors_get(const struct scmi_handle *handle,
|
||||
struct voltage_info *vinfo)
|
||||
{
|
||||
int ret, dom;
|
||||
struct scmi_xfer *td, *tl;
|
||||
struct device *dev = handle->dev;
|
||||
struct scmi_msg_resp_domain_attributes *resp_dom;
|
||||
struct scmi_msg_resp_describe_levels *resp_levels;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, VOLTAGE_DOMAIN_ATTRIBUTES,
|
||||
SCMI_PROTOCOL_VOLTAGE, sizeof(__le32),
|
||||
sizeof(*resp_dom), &td);
|
||||
if (ret)
|
||||
return ret;
|
||||
resp_dom = td->rx.buf;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, VOLTAGE_DESCRIBE_LEVELS,
|
||||
SCMI_PROTOCOL_VOLTAGE, sizeof(__le64), 0, &tl);
|
||||
if (ret)
|
||||
goto outd;
|
||||
resp_levels = tl->rx.buf;
|
||||
|
||||
for (dom = 0; dom < vinfo->num_domains; dom++) {
|
||||
u32 desc_index = 0;
|
||||
u16 num_returned = 0, num_remaining = 0;
|
||||
struct scmi_msg_cmd_describe_levels *cmd;
|
||||
struct scmi_voltage_info *v;
|
||||
|
||||
/* Retrieve domain attributes at first ... */
|
||||
put_unaligned_le32(dom, td->tx.buf);
|
||||
ret = scmi_do_xfer(handle, td);
|
||||
/* Skip domain on comms error */
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
v = vinfo->domains + dom;
|
||||
v->id = dom;
|
||||
v->attributes = le32_to_cpu(resp_dom->attr);
|
||||
strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
|
||||
|
||||
cmd = tl->tx.buf;
|
||||
/* ...then retrieve domain levels descriptions */
|
||||
do {
|
||||
u32 flags;
|
||||
int cnt;
|
||||
|
||||
cmd->domain_id = cpu_to_le32(v->id);
|
||||
cmd->level_index = desc_index;
|
||||
ret = scmi_do_xfer(handle, tl);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
flags = le32_to_cpu(resp_levels->flags);
|
||||
num_returned = NUM_RETURNED_LEVELS(flags);
|
||||
num_remaining = NUM_REMAINING_LEVELS(flags);
|
||||
|
||||
/* Allocate space for num_levels if not already done */
|
||||
if (!v->num_levels) {
|
||||
ret = scmi_init_voltage_levels(dev, v,
|
||||
num_returned,
|
||||
num_remaining,
|
||||
SUPPORTS_SEGMENTED_LEVELS(flags));
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (desc_index + num_returned > v->num_levels) {
|
||||
dev_err(handle->dev,
|
||||
"No. of voltage levels can't exceed %d\n",
|
||||
v->num_levels);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < num_returned; cnt++) {
|
||||
s32 val;
|
||||
|
||||
val =
|
||||
(s32)le32_to_cpu(resp_levels->voltage[cnt]);
|
||||
v->levels_uv[desc_index + cnt] = val;
|
||||
if (val < 0)
|
||||
v->negative_volts_allowed = true;
|
||||
}
|
||||
|
||||
desc_index += num_returned;
|
||||
|
||||
scmi_reset_rx_to_maxsz(handle, tl);
|
||||
/* check both to avoid infinite loop due to buggy fw */
|
||||
} while (num_returned && num_remaining);
|
||||
|
||||
if (ret) {
|
||||
v->num_levels = 0;
|
||||
devm_kfree(dev, v->levels_uv);
|
||||
}
|
||||
|
||||
scmi_reset_rx_to_maxsz(handle, td);
|
||||
}
|
||||
|
||||
scmi_xfer_put(handle, tl);
|
||||
outd:
|
||||
scmi_xfer_put(handle, td);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __scmi_voltage_get_u32(const struct scmi_handle *handle,
|
||||
u8 cmd_id, u32 domain_id, u32 *value)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct voltage_info *vinfo = handle->voltage_priv;
|
||||
|
||||
if (domain_id >= vinfo->num_domains)
|
||||
return -EINVAL;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, cmd_id,
|
||||
SCMI_PROTOCOL_VOLTAGE,
|
||||
sizeof(__le32), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
put_unaligned_le32(domain_id, t->tx.buf);
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
if (!ret)
|
||||
*value = get_unaligned_le32(t->rx.buf);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_voltage_config_set(const struct scmi_handle *handle,
|
||||
u32 domain_id, u32 config)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct voltage_info *vinfo = handle->voltage_priv;
|
||||
struct scmi_msg_cmd_config_set *cmd;
|
||||
|
||||
if (domain_id >= vinfo->num_domains)
|
||||
return -EINVAL;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, VOLTAGE_CONFIG_SET,
|
||||
SCMI_PROTOCOL_VOLTAGE,
|
||||
sizeof(*cmd), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cmd = t->tx.buf;
|
||||
cmd->domain_id = cpu_to_le32(domain_id);
|
||||
cmd->config = cpu_to_le32(config & GENMASK(3, 0));
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_voltage_config_get(const struct scmi_handle *handle,
|
||||
u32 domain_id, u32 *config)
|
||||
{
|
||||
return __scmi_voltage_get_u32(handle, VOLTAGE_CONFIG_GET,
|
||||
domain_id, config);
|
||||
}
|
||||
|
||||
static int scmi_voltage_level_set(const struct scmi_handle *handle,
|
||||
u32 domain_id, u32 flags, s32 volt_uV)
|
||||
{
|
||||
int ret;
|
||||
struct scmi_xfer *t;
|
||||
struct voltage_info *vinfo = handle->voltage_priv;
|
||||
struct scmi_msg_cmd_level_set *cmd;
|
||||
|
||||
if (domain_id >= vinfo->num_domains)
|
||||
return -EINVAL;
|
||||
|
||||
ret = scmi_xfer_get_init(handle, VOLTAGE_LEVEL_SET,
|
||||
SCMI_PROTOCOL_VOLTAGE,
|
||||
sizeof(*cmd), 0, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cmd = t->tx.buf;
|
||||
cmd->domain_id = cpu_to_le32(domain_id);
|
||||
cmd->flags = cpu_to_le32(flags);
|
||||
cmd->voltage_level = cpu_to_le32(volt_uV);
|
||||
|
||||
ret = scmi_do_xfer(handle, t);
|
||||
|
||||
scmi_xfer_put(handle, t);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scmi_voltage_level_get(const struct scmi_handle *handle,
|
||||
u32 domain_id, s32 *volt_uV)
|
||||
{
|
||||
return __scmi_voltage_get_u32(handle, VOLTAGE_LEVEL_GET,
|
||||
domain_id, (u32 *)volt_uV);
|
||||
}
|
||||
|
||||
static const struct scmi_voltage_info * __must_check
|
||||
scmi_voltage_info_get(const struct scmi_handle *handle, u32 domain_id)
|
||||
{
|
||||
struct voltage_info *vinfo = handle->voltage_priv;
|
||||
|
||||
if (domain_id >= vinfo->num_domains ||
|
||||
!vinfo->domains[domain_id].num_levels)
|
||||
return NULL;
|
||||
|
||||
return vinfo->domains + domain_id;
|
||||
}
|
||||
|
||||
static int scmi_voltage_domains_num_get(const struct scmi_handle *handle)
|
||||
{
|
||||
struct voltage_info *vinfo = handle->voltage_priv;
|
||||
|
||||
return vinfo->num_domains;
|
||||
}
|
||||
|
||||
static struct scmi_voltage_ops voltage_ops = {
|
||||
.num_domains_get = scmi_voltage_domains_num_get,
|
||||
.info_get = scmi_voltage_info_get,
|
||||
.config_set = scmi_voltage_config_set,
|
||||
.config_get = scmi_voltage_config_get,
|
||||
.level_set = scmi_voltage_level_set,
|
||||
.level_get = scmi_voltage_level_get,
|
||||
};
|
||||
|
||||
static int scmi_voltage_protocol_init(struct scmi_handle *handle)
|
||||
{
|
||||
int ret;
|
||||
u32 version;
|
||||
struct voltage_info *vinfo;
|
||||
|
||||
ret = scmi_version_get(handle, SCMI_PROTOCOL_VOLTAGE, &version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(handle->dev, "Voltage Version %d.%d\n",
|
||||
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
|
||||
|
||||
vinfo = devm_kzalloc(handle->dev, sizeof(*vinfo), GFP_KERNEL);
|
||||
if (!vinfo)
|
||||
return -ENOMEM;
|
||||
vinfo->version = version;
|
||||
|
||||
ret = scmi_protocol_attributes_get(handle, vinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (vinfo->num_domains) {
|
||||
vinfo->domains = devm_kcalloc(handle->dev, vinfo->num_domains,
|
||||
sizeof(*vinfo->domains),
|
||||
GFP_KERNEL);
|
||||
if (!vinfo->domains)
|
||||
return -ENOMEM;
|
||||
ret = scmi_voltage_descriptors_get(handle, vinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
dev_warn(handle->dev, "No Voltage domains found.\n");
|
||||
}
|
||||
|
||||
handle->voltage_ops = &voltage_ops;
|
||||
handle->voltage_priv = vinfo;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_VOLTAGE, voltage)
|
||||
@@ -1288,7 +1288,8 @@ static int ads7846_probe(struct spi_device *spi)
|
||||
* may not. So we stick to very-portable 8 bit words, both RX and TX.
|
||||
*/
|
||||
spi->bits_per_word = 8;
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi->mode &= ~SPI_MODE_X_MASK;
|
||||
spi->mode |= SPI_MODE_0;
|
||||
err = spi_setup(spi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -175,7 +175,7 @@ int netup_spi_init(struct netup_unidvb_dev *ndev)
|
||||
struct spi_master *master;
|
||||
struct netup_spi *nspi;
|
||||
|
||||
master = spi_alloc_master(&ndev->pci_dev->dev,
|
||||
master = devm_spi_alloc_master(&ndev->pci_dev->dev,
|
||||
sizeof(struct netup_spi));
|
||||
if (!master) {
|
||||
dev_err(&ndev->pci_dev->dev,
|
||||
@@ -208,6 +208,7 @@ int netup_spi_init(struct netup_unidvb_dev *ndev)
|
||||
ndev->pci_slot,
|
||||
ndev->pci_func);
|
||||
if (!spi_new_device(master, &netup_spi_board)) {
|
||||
spi_unregister_master(master);
|
||||
ndev->spi = NULL;
|
||||
dev_err(&ndev->pci_dev->dev,
|
||||
"%s(): unable to create SPI device\n", __func__);
|
||||
@@ -226,13 +227,13 @@ void netup_spi_release(struct netup_unidvb_dev *ndev)
|
||||
if (!spi)
|
||||
return;
|
||||
|
||||
spi_unregister_master(spi->master);
|
||||
spin_lock_irqsave(&spi->lock, flags);
|
||||
reg = readw(&spi->regs->control_stat);
|
||||
writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
|
||||
reg = readw(&spi->regs->control_stat);
|
||||
writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
|
||||
spin_unlock_irqrestore(&spi->lock, flags);
|
||||
spi_unregister_master(spi->master);
|
||||
ndev->spi = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -741,8 +741,6 @@ static int cros_ec_spi_probe(struct spi_device *spi)
|
||||
struct cros_ec_spi *ec_spi;
|
||||
int err;
|
||||
|
||||
spi->bits_per_word = 8;
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi->rt = true;
|
||||
err = spi_setup(spi);
|
||||
if (err < 0)
|
||||
|
||||
@@ -155,6 +155,15 @@ config REGULATOR_ARIZONA_MICSUPP
|
||||
and Wolfson Microelectronic Arizona codecs
|
||||
devices.
|
||||
|
||||
config REGULATOR_ARM_SCMI
|
||||
tristate "SCMI based regulator driver"
|
||||
depends on ARM_SCMI_PROTOCOL && OF
|
||||
help
|
||||
This adds the regulator driver support for ARM platforms using SCMI
|
||||
protocol for device voltage management.
|
||||
This driver uses SCMI Message Protocol driver to interact with the
|
||||
firmware providing the device Voltage functionality.
|
||||
|
||||
config REGULATOR_AS3711
|
||||
tristate "AS3711 PMIC"
|
||||
depends on MFD_AS3711
|
||||
@@ -303,6 +312,26 @@ config REGULATOR_DA9063
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called da9063-regulator.
|
||||
|
||||
config REGULATOR_DA9121
|
||||
tristate "Dialog Semiconductor DA9121/DA9122/DA9220/DA9217/DA9130/DA9131/DA9132 regulator"
|
||||
depends on I2C && OF
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say y here to support for the Dialog Semiconductor DA9121. The
|
||||
DA9121 is a single channel dual-phase buck converter controlled
|
||||
through an I2C interface.
|
||||
|
||||
DA9121 Single-channel dual-phase 10A buck converter
|
||||
DA9130 Single-channel dual-phase 10A buck converter (Automotive)
|
||||
DA9217 Single-channel dual-phase 6A buck converter
|
||||
DA9122 Dual-channel single-phase 5A buck converter
|
||||
DA9131 Dual-channel single-phase 5A buck converter (Automotive)
|
||||
DA9220 Dual-channel single-phase 3A buck converter
|
||||
DA9132 Dual-channel single-phase 3A buck converter (Automotive)
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called da9121-regulator.
|
||||
|
||||
config REGULATOR_DA9210
|
||||
tristate "Dialog Semiconductor DA9210 regulator"
|
||||
depends on I2C
|
||||
@@ -791,9 +820,17 @@ config REGULATOR_PCF50633
|
||||
Say Y here to support the voltage regulators and converters
|
||||
on PCF50633
|
||||
|
||||
config REGULATOR_PF8X00
|
||||
tristate "NXP PF8100/PF8121A/PF8200 regulator driver"
|
||||
depends on I2C && OF
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say y here to support the regulators found on the NXP
|
||||
PF8100/PF8121A/PF8200 PMIC.
|
||||
|
||||
config REGULATOR_PFUZE100
|
||||
tristate "Freescale PFUZE100/200/3000/3001 regulator driver"
|
||||
depends on I2C
|
||||
depends on I2C && OF
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say y here to support the regulators found on the Freescale
|
||||
@@ -843,7 +880,7 @@ config REGULATOR_QCOM_RPM
|
||||
|
||||
config REGULATOR_QCOM_RPMH
|
||||
tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
|
||||
depends on QCOM_RPMH || COMPILE_TEST
|
||||
depends on QCOM_RPMH || (QCOM_RPMH=n && COMPILE_TEST)
|
||||
help
|
||||
This driver supports control of PMIC regulators via the RPMh hardware
|
||||
block found on Qualcomm Technologies Inc. SoCs. RPMh regulator
|
||||
|
||||
@@ -24,6 +24,7 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
|
||||
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o
|
||||
obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o
|
||||
obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
|
||||
@@ -38,6 +39,7 @@ obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DA9121) += da9121-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
|
||||
@@ -100,6 +102,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o
|
||||
|
||||
@@ -455,7 +455,8 @@ static int as3722_sd_set_mode(struct regulator_dev *rdev,
|
||||
switch (mode) {
|
||||
case REGULATOR_MODE_FAST:
|
||||
val = as3722_reg_lookup[id].mode_mask;
|
||||
case REGULATOR_MODE_NORMAL: /* fall down */
|
||||
fallthrough;
|
||||
case REGULATOR_MODE_NORMAL:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
||||
@@ -594,7 +594,7 @@ static const struct regulator_desc axp22x_regulators[] = {
|
||||
AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK,
|
||||
AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK),
|
||||
AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
|
||||
AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK,
|
||||
AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK,
|
||||
AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK),
|
||||
AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
|
||||
AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK,
|
||||
|
||||
@@ -1323,13 +1323,142 @@ static void mark_hw_controlled(struct device *dev, struct device_node *np,
|
||||
dev_warn(dev, "Bad regulator node\n");
|
||||
}
|
||||
|
||||
static int get_hw_controlled_regulators(struct device *dev,
|
||||
struct bd718xx_regulator_data *reg_data,
|
||||
unsigned int num_reg_data, int *info)
|
||||
/*
|
||||
* Setups where regulator (especially the buck8) output voltage is scaled
|
||||
* by adding external connection where some other regulator output is connected
|
||||
* to feedback-pin (over suitable resistors) is getting popular amongst users
|
||||
* of BD71837. (This allows for example scaling down the buck8 voltages to suit
|
||||
* lover GPU voltages for projects where buck8 is (ab)used to supply power
|
||||
* for GPU. Additionally some setups do allow DVS for buck8 but as this do
|
||||
* produce voltage spikes the HW must be evaluated to be able to survive this
|
||||
* - hence I keep the DVS disabled for non DVS bucks by default. I don't want
|
||||
* to help you burn your proto board)
|
||||
*
|
||||
* So we allow describing this external connection from DT and scale the
|
||||
* voltages accordingly. This is what the connection should look like:
|
||||
*
|
||||
* |------------|
|
||||
* | buck 8 |-------+----->Vout
|
||||
* | | |
|
||||
* |------------| |
|
||||
* | FB pin |
|
||||
* | |
|
||||
* +-------+--R2---+
|
||||
* |
|
||||
* R1
|
||||
* |
|
||||
* V FB-pull-up
|
||||
*
|
||||
* Here the buck output is sifted according to formula:
|
||||
*
|
||||
* Vout_o = Vo - (Vpu - Vo)*R2/R1
|
||||
* Linear_step = step_orig*(R1+R2)/R1
|
||||
*
|
||||
* where:
|
||||
* Vout_o is adjusted voltage output at vsel reg value 0
|
||||
* Vo is original voltage output at vsel reg value 0
|
||||
* Vpu is the pull-up voltage V FB-pull-up in the picture
|
||||
* R1 and R2 are resistor values.
|
||||
*
|
||||
* As a real world example for buck8 and a specific GPU:
|
||||
* VLDO = 1.6V (used as FB-pull-up)
|
||||
* R1 = 1000ohms
|
||||
* R2 = 150ohms
|
||||
* VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V
|
||||
* Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV
|
||||
*/
|
||||
static int setup_feedback_loop(struct device *dev, struct device_node *np,
|
||||
struct bd718xx_regulator_data *reg_data,
|
||||
unsigned int num_reg_data, int fb_uv)
|
||||
{
|
||||
int i, r1, r2, ret;
|
||||
|
||||
/*
|
||||
* We do adjust the values in the global desc based on DT settings.
|
||||
* This may not be best approach as it can cause problems if more than
|
||||
* one PMIC is controlled from same processor. I don't see such use-case
|
||||
* for BD718x7 now - so we spare some bits.
|
||||
*
|
||||
* If this will point out to be a problem - then we can allocate new
|
||||
* bd718xx_regulator_data array at probe and just use the global
|
||||
* array as a template where we copy initial values. Then we can
|
||||
* use allocated descs for regultor registration and do IC specific
|
||||
* modifications to this copy while leaving other PMICs untouched. But
|
||||
* that means allocating new array for each PMIC - and currently I see
|
||||
* no need for that.
|
||||
*/
|
||||
|
||||
for (i = 0; i < num_reg_data; i++) {
|
||||
struct regulator_desc *desc = ®_data[i].desc;
|
||||
int j;
|
||||
|
||||
if (!of_node_name_eq(np, desc->of_match))
|
||||
continue;
|
||||
|
||||
pr_info("Looking at node '%s'\n", desc->of_match);
|
||||
|
||||
/* The feedback loop connection does not make sense for LDOs */
|
||||
if (desc->id >= BD718XX_LDO1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = of_property_read_u32(np, "rohm,feedback-pull-up-r1-ohms",
|
||||
&r1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!r1)
|
||||
return -EINVAL;
|
||||
|
||||
ret = of_property_read_u32(np, "rohm,feedback-pull-up-r2-ohms",
|
||||
&r2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (desc->n_linear_ranges && desc->linear_ranges) {
|
||||
struct linear_range *new;
|
||||
|
||||
new = devm_kzalloc(dev, desc->n_linear_ranges *
|
||||
sizeof(struct linear_range),
|
||||
GFP_KERNEL);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
for (j = 0; j < desc->n_linear_ranges; j++) {
|
||||
int min = desc->linear_ranges[j].min;
|
||||
int step = desc->linear_ranges[j].step;
|
||||
|
||||
min -= (fb_uv - min)*r2/r1;
|
||||
step = step * (r1 + r2);
|
||||
step /= r1;
|
||||
|
||||
new[j].min = min;
|
||||
new[j].step = step;
|
||||
|
||||
dev_dbg(dev, "%s: old range min %d, step %d\n",
|
||||
desc->name, desc->linear_ranges[j].min,
|
||||
desc->linear_ranges[j].step);
|
||||
dev_dbg(dev, "new range min %d, step %d\n", min,
|
||||
step);
|
||||
}
|
||||
desc->linear_ranges = new;
|
||||
}
|
||||
dev_dbg(dev, "regulator '%s' has FB pull-up configured\n",
|
||||
desc->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int get_special_regulators(struct device *dev,
|
||||
struct bd718xx_regulator_data *reg_data,
|
||||
unsigned int num_reg_data, int *info)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *np;
|
||||
struct device_node *nproot = dev->of_node;
|
||||
const char *prop = "rohm,no-regulator-enable-control";
|
||||
int uv;
|
||||
|
||||
*info = 0;
|
||||
|
||||
@@ -1338,13 +1467,32 @@ static int get_hw_controlled_regulators(struct device *dev,
|
||||
dev_err(dev, "failed to find regulators node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
for_each_child_of_node(nproot, np)
|
||||
if (of_property_read_bool(np, prop))
|
||||
for_each_child_of_node(nproot, np) {
|
||||
if (of_property_read_bool(np, "rohm,no-regulator-enable-control"))
|
||||
mark_hw_controlled(dev, np, reg_data, num_reg_data,
|
||||
info);
|
||||
ret = of_property_read_u32(np, "rohm,fb-pull-up-microvolt",
|
||||
&uv);
|
||||
if (ret) {
|
||||
if (ret == -EINVAL)
|
||||
continue;
|
||||
else
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = setup_feedback_loop(dev, np, reg_data, num_reg_data, uv);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
of_node_put(nproot);
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
of_node_put(np);
|
||||
of_node_put(nproot);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bd718xx_probe(struct platform_device *pdev)
|
||||
@@ -1432,8 +1580,10 @@ static int bd718xx_probe(struct platform_device *pdev)
|
||||
* be affected by PMIC state machine - Eg. regulator is likely to stay
|
||||
* on even in SUSPEND
|
||||
*/
|
||||
get_hw_controlled_regulators(pdev->dev.parent, reg_data, num_reg_data,
|
||||
err = get_special_regulators(pdev->dev.parent, reg_data, num_reg_data,
|
||||
&omit_enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < num_reg_data; i++) {
|
||||
|
||||
|
||||
@@ -2958,6 +2958,8 @@ static int _regulator_list_voltage(struct regulator_dev *rdev,
|
||||
if (ops->list_voltage) {
|
||||
if (selector >= rdev->desc->n_voltages)
|
||||
return -EINVAL;
|
||||
if (selector < rdev->desc->linear_min_sel)
|
||||
return 0;
|
||||
if (lock)
|
||||
regulator_lock(rdev);
|
||||
ret = ops->list_voltage(rdev, selector);
|
||||
@@ -3109,6 +3111,8 @@ int regulator_list_hardware_vsel(struct regulator *regulator,
|
||||
|
||||
if (selector >= rdev->desc->n_voltages)
|
||||
return -EINVAL;
|
||||
if (selector < rdev->desc->linear_min_sel)
|
||||
return 0;
|
||||
if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -4030,6 +4034,12 @@ int regulator_set_voltage_time(struct regulator *regulator,
|
||||
|
||||
for (i = 0; i < rdev->desc->n_voltages; i++) {
|
||||
/* We only look for exact voltage matches here */
|
||||
if (i < rdev->desc->linear_min_sel)
|
||||
continue;
|
||||
|
||||
if (old_sel >= 0 && new_sel >= 0)
|
||||
break;
|
||||
|
||||
voltage = regulator_list_voltage(regulator, i);
|
||||
if (voltage < 0)
|
||||
return -EINVAL;
|
||||
@@ -5305,6 +5315,8 @@ regulator_register(const struct regulator_desc *regulator_desc,
|
||||
/* FIXME: this currently triggers a chicken-and-egg problem
|
||||
* when creating -SUPPLY symlink in sysfs to a regulator
|
||||
* that is just being created */
|
||||
rdev_dbg(rdev, "will resolve supply early: %s\n",
|
||||
rdev->supply_name);
|
||||
ret = regulator_resolve_supply(rdev);
|
||||
if (!ret)
|
||||
ret = set_machine_constraints(rdev);
|
||||
@@ -5537,7 +5549,7 @@ void regulator_set_drvdata(struct regulator *regulator, void *data)
|
||||
EXPORT_SYMBOL_GPL(regulator_set_drvdata);
|
||||
|
||||
/**
|
||||
* regulator_get_id - get regulator ID
|
||||
* rdev_get_id - get regulator ID
|
||||
* @rdev: regulator
|
||||
*/
|
||||
int rdev_get_id(struct regulator_dev *rdev)
|
||||
|
||||
1075
drivers/regulator/da9121-regulator.c
Normal file
1075
drivers/regulator/da9121-regulator.c
Normal file
File diff suppressed because it is too large
Load Diff
291
drivers/regulator/da9121-regulator.h
Normal file
291
drivers/regulator/da9121-regulator.h
Normal file
@@ -0,0 +1,291 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* DA9121 Single-channel dual-phase 10A buck converter
|
||||
* DA9130 Single-channel dual-phase 10A buck converter (Automotive)
|
||||
* DA9217 Single-channel dual-phase 6A buck converter
|
||||
* DA9122 Dual-channel single-phase 5A buck converter
|
||||
* DA9131 Dual-channel single-phase 5A buck converter (Automotive)
|
||||
* DA9220 Dual-channel single-phase 3A buck converter
|
||||
* DA9132 Dual-channel single-phase 3A buck converter (Automotive)
|
||||
*
|
||||
* Copyright (C) 2020 Dialog Semiconductor
|
||||
*
|
||||
* Authors: Steve Twiss, Dialog Semiconductor
|
||||
* Adam Ward, Dialog Semiconductor
|
||||
*/
|
||||
|
||||
#ifndef __DA9121_REGISTERS_H__
|
||||
#define __DA9121_REGISTERS_H__
|
||||
|
||||
/* Values for: DA9121_REG_BUCK_BUCKx_4 registers, fields CHx_y_MODE
|
||||
* DA9121_REG_BUCK_BUCKx_7 registers, fields CHx_RIPPLE_CANCEL
|
||||
*/
|
||||
#include <dt-bindings/regulator/dlg,da9121-regulator.h>
|
||||
|
||||
enum da9121_variant {
|
||||
DA9121_TYPE_DA9121_DA9130,
|
||||
DA9121_TYPE_DA9220_DA9132,
|
||||
DA9121_TYPE_DA9122_DA9131,
|
||||
DA9121_TYPE_DA9217
|
||||
};
|
||||
|
||||
/* Minimum, maximum and default polling millisecond periods are provided
|
||||
* here as an example. It is expected that any final implementation will
|
||||
* include a modification of these settings to match the required
|
||||
* application.
|
||||
*/
|
||||
#define DA9121_DEFAULT_POLLING_PERIOD_MS 3000
|
||||
#define DA9121_MAX_POLLING_PERIOD_MS 10000
|
||||
#define DA9121_MIN_POLLING_PERIOD_MS 1000
|
||||
|
||||
/* Registers */
|
||||
|
||||
#define DA9121_REG_SYS_STATUS_0 0x01
|
||||
#define DA9121_REG_SYS_STATUS_1 0x02
|
||||
#define DA9121_REG_SYS_STATUS_2 0x03
|
||||
#define DA9121_REG_SYS_EVENT_0 0x04
|
||||
#define DA9121_REG_SYS_EVENT_1 0x05
|
||||
#define DA9121_REG_SYS_EVENT_2 0x06
|
||||
#define DA9121_REG_SYS_MASK_0 0x07
|
||||
#define DA9121_REG_SYS_MASK_1 0x08
|
||||
#define DA9121_REG_SYS_MASK_2 0x09
|
||||
#define DA9121_REG_SYS_MASK_3 0x0A
|
||||
#define DA9121_REG_SYS_CONFIG_0 0x0B
|
||||
#define DA9121_REG_SYS_CONFIG_1 0x0C
|
||||
#define DA9121_REG_SYS_CONFIG_2 0x0D
|
||||
#define DA9121_REG_SYS_CONFIG_3 0x0E
|
||||
#define DA9121_REG_SYS_GPIO0_0 0x10
|
||||
#define DA9121_REG_SYS_GPIO0_1 0x11
|
||||
#define DA9121_REG_SYS_GPIO1_0 0x12
|
||||
#define DA9121_REG_SYS_GPIO1_1 0x13
|
||||
#define DA9121_REG_SYS_GPIO2_0 0x14
|
||||
#define DA9121_REG_SYS_GPIO2_1 0x15
|
||||
#define DA9121_REG_BUCK_BUCK1_0 0x20
|
||||
#define DA9121_REG_BUCK_BUCK1_1 0x21
|
||||
#define DA9121_REG_BUCK_BUCK1_2 0x22
|
||||
#define DA9121_REG_BUCK_BUCK1_3 0x23
|
||||
#define DA9121_REG_BUCK_BUCK1_4 0x24
|
||||
#define DA9121_REG_BUCK_BUCK1_5 0x25
|
||||
#define DA9121_REG_BUCK_BUCK1_6 0x26
|
||||
#define DA9121_REG_BUCK_BUCK1_7 0x27
|
||||
#define DA9xxx_REG_BUCK_BUCK2_0 0x28
|
||||
#define DA9xxx_REG_BUCK_BUCK2_1 0x29
|
||||
#define DA9xxx_REG_BUCK_BUCK2_2 0x2A
|
||||
#define DA9xxx_REG_BUCK_BUCK2_3 0x2B
|
||||
#define DA9xxx_REG_BUCK_BUCK2_4 0x2C
|
||||
#define DA9xxx_REG_BUCK_BUCK2_5 0x2D
|
||||
#define DA9xxx_REG_BUCK_BUCK2_6 0x2E
|
||||
#define DA9xxx_REG_BUCK_BUCK2_7 0x2F
|
||||
#define DA9121_REG_OTP_DEVICE_ID 0x48
|
||||
#define DA9121_REG_OTP_VARIANT_ID 0x49
|
||||
#define DA9121_REG_OTP_CUSTOMER_ID 0x4A
|
||||
#define DA9121_REG_OTP_CONFIG_ID 0x4B
|
||||
|
||||
/* Register bits */
|
||||
|
||||
/* DA9121_REG_SYS_STATUS_0 */
|
||||
|
||||
#define DA9xxx_MASK_SYS_STATUS_0_SG BIT(2)
|
||||
#define DA9121_MASK_SYS_STATUS_0_TEMP_CRIT BIT(1)
|
||||
#define DA9121_MASK_SYS_STATUS_0_TEMP_WARN BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_STATUS_1 */
|
||||
|
||||
#define DA9xxx_MASK_SYS_STATUS_1_PG2 BIT(7)
|
||||
#define DA9xxx_MASK_SYS_STATUS_1_OV2 BIT(6)
|
||||
#define DA9xxx_MASK_SYS_STATUS_1_UV2 BIT(5)
|
||||
#define DA9xxx_MASK_SYS_STATUS_1_OC2 BIT(4)
|
||||
#define DA9121_MASK_SYS_STATUS_1_PG1 BIT(3)
|
||||
#define DA9121_MASK_SYS_STATUS_1_OV1 BIT(2)
|
||||
#define DA9121_MASK_SYS_STATUS_1_UV1 BIT(1)
|
||||
#define DA9121_MASK_SYS_STATUS_1_OC1 BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_STATUS_2 */
|
||||
|
||||
#define DA9121_MASK_SYS_STATUS_2_GPIO2 BIT(2)
|
||||
#define DA9121_MASK_SYS_STATUS_2_GPIO1 BIT(1)
|
||||
#define DA9121_MASK_SYS_STATUS_2_GPIO0 BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_EVENT_0 */
|
||||
|
||||
#define DA9xxx_MASK_SYS_EVENT_0_E_SG BIT(2)
|
||||
#define DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT BIT(1)
|
||||
#define DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_EVENT_1 */
|
||||
|
||||
#define DA9xxx_MASK_SYS_EVENT_1_E_PG2 BIT(7)
|
||||
#define DA9xxx_MASK_SYS_EVENT_1_E_OV2 BIT(6)
|
||||
#define DA9xxx_MASK_SYS_EVENT_1_E_UV2 BIT(5)
|
||||
#define DA9xxx_MASK_SYS_EVENT_1_E_OC2 BIT(4)
|
||||
#define DA9121_MASK_SYS_EVENT_1_E_PG1 BIT(3)
|
||||
#define DA9121_MASK_SYS_EVENT_1_E_OV1 BIT(2)
|
||||
#define DA9121_MASK_SYS_EVENT_1_E_UV1 BIT(1)
|
||||
#define DA9121_MASK_SYS_EVENT_1_E_OC1 BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_EVENT_2 */
|
||||
|
||||
#define DA9121_MASK_SYS_EVENT_2_E_GPIO2 BIT(2)
|
||||
#define DA9121_MASK_SYS_EVENT_2_E_GPIO1 BIT(1)
|
||||
#define DA9121_MASK_SYS_EVENT_2_E_GPIO0 BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_MASK_0 */
|
||||
|
||||
#define DA9xxx_MASK_SYS_MASK_0_M_SG BIT(2)
|
||||
#define DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT BIT(1)
|
||||
#define DA9121_MASK_SYS_MASK_0_M_TEMP_WARN BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_MASK_1 */
|
||||
|
||||
#define DA9xxx_MASK_SYS_MASK_1_M_PG2 BIT(7)
|
||||
#define DA9xxx_MASK_SYS_MASK_1_M_OV2 BIT(6)
|
||||
#define DA9xxx_MASK_SYS_MASK_1_M_UV2 BIT(5)
|
||||
#define DA9xxx_MASK_SYS_MASK_1_M_OC2 BIT(4)
|
||||
#define DA9121_MASK_SYS_MASK_1_M_PG1 BIT(3)
|
||||
#define DA9121_MASK_SYS_MASK_1_M_OV1 BIT(2)
|
||||
#define DA9121_MASK_SYS_MASK_1_M_UV1 BIT(1)
|
||||
#define DA9121_MASK_SYS_MASK_1_M_OC1 BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_MASK_2 */
|
||||
|
||||
#define DA9121_MASK_SYS_MASK_2_M_GPIO2 BIT(2)
|
||||
#define DA9121_MASK_SYS_MASK_2_M_GPIO1 BIT(1)
|
||||
#define DA9121_MASK_SYS_MASK_2_M_GPIO0 BIT(0)
|
||||
|
||||
/* DA9122_REG_SYS_MASK_3 */
|
||||
|
||||
#define DA9121_MASK_SYS_MASK_3_M_VR_HOT BIT(3)
|
||||
#define DA9xxx_MASK_SYS_MASK_3_M_SG_STAT BIT(2)
|
||||
#define DA9xxx_MASK_SYS_MASK_3_M_PG2_STAT BIT(1)
|
||||
#define DA9121_MASK_SYS_MASK_3_M_PG1_STAT BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_CONFIG_0 */
|
||||
|
||||
#define DA9121_MASK_SYS_CONFIG_0_CH1_DIS_DLY 0xF0
|
||||
#define DA9121_MASK_SYS_CONFIG_0_CH1_EN_DLY 0x0F
|
||||
|
||||
/* DA9xxx_REG_SYS_CONFIG_1 */
|
||||
|
||||
#define DA9xxx_MASK_SYS_CONFIG_1_CH2_DIS_DLY 0xF0
|
||||
#define DA9xxx_MASK_SYS_CONFIG_1_CH2_EN_DLY 0x0F
|
||||
|
||||
/* DA9121_REG_SYS_CONFIG_2 */
|
||||
|
||||
#define DA9121_MASK_SYS_CONFIG_2_OC_LATCHOFF 0x60
|
||||
#define DA9121_MASK_SYS_CONFIG_2_OC_DVC_MASK BIT(4)
|
||||
#define DA9121_MASK_SYS_CONFIG_2_PG_DVC_MASK 0x0C
|
||||
|
||||
/* DA9121_REG_SYS_CONFIG_3 */
|
||||
|
||||
#define DA9121_MASK_SYS_CONFIG_3_OSC_TUNE 0X70
|
||||
#define DA9121_MASK_SYS_CONFIG_3_I2C_TIMEOUT BIT(1)
|
||||
|
||||
/* DA9121_REG_SYS_GPIO0_0 */
|
||||
|
||||
#define DA9121_MASK_SYS_GPIO0_0_GPIO0_MODE 0X1E
|
||||
#define DA9121_MASK_SYS_GPIO0_0_GPIO0_OBUF BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_GPIO0_1 */
|
||||
|
||||
#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_FALL BIT(7)
|
||||
#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_RISE BIT(6)
|
||||
#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB 0x30
|
||||
#define DA9121_MASK_SYS_GPIO0_1_GPIO0_PUPD BIT(3)
|
||||
#define DA9121_MASK_SYS_GPIO0_1_GPIO0_POL BIT(2)
|
||||
#define DA9121_MASK_SYS_GPIO0_1_GPIO0_TRIG 0x03
|
||||
|
||||
/* DA9121_REG_SYS_GPIO1_0 */
|
||||
|
||||
#define DA9121_MASK_SYS_GPIO1_0_GPIO1_MODE 0x1E
|
||||
#define DA9121_MASK_SYS_GPIO1_0_GPIO1_OBUF BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_GPIO1_1 */
|
||||
|
||||
#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_FALL BIT(7)
|
||||
#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_RISE BIT(6)
|
||||
#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB 0x30
|
||||
#define DA9121_MASK_SYS_GPIO1_1_GPIO1_PUPD BIT(3)
|
||||
#define DA9121_MASK_SYS_GPIO1_1_GPIO1_POL BIT(2)
|
||||
#define DA9121_MASK_SYS_GPIO1_1_GPIO1_TRIG 0x03
|
||||
|
||||
/* DA9121_REG_SYS_GPIO2_0 */
|
||||
|
||||
#define DA9121_MASK_SYS_GPIO2_0_GPIO2_MODE 0x1E
|
||||
#define DA9121_MASK_SYS_GPIO2_0_GPIO2_OBUF BIT(0)
|
||||
|
||||
/* DA9121_REG_SYS_GPIO2_1 */
|
||||
|
||||
#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_FALL BIT(7)
|
||||
#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_RISE BIT(6)
|
||||
#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB 0x30
|
||||
#define DA9121_MASK_SYS_GPIO2_1_GPIO2_PUPD BIT(3)
|
||||
#define DA9121_MASK_SYS_GPIO2_1_GPIO2_POL BIT(2)
|
||||
#define DA9121_MASK_SYS_GPIO2_1_GPIO2_TRIG 0x03
|
||||
|
||||
/* DA9121_REG_BUCK_BUCK1_0 / DA9xxx_REG_BUCK_BUCK2_0 */
|
||||
|
||||
#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_DWN 0x70
|
||||
#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_UP 0x0E
|
||||
#define DA9121_MASK_BUCK_BUCKx_0_CHx_EN BIT(0)
|
||||
|
||||
/* DA9121_REG_BUCK_BUCK1_1 / DA9xxx_REG_BUCK_BUCK2_1 */
|
||||
|
||||
#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_SHDN 0x70
|
||||
#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_STARTUP 0x0E
|
||||
#define DA9121_MASK_BUCK_BUCKx_1_CHx_PD_DIS BIT(0)
|
||||
|
||||
/* DA9121_REG_BUCK_BUCK1_2 / DA9xxx_REG_BUCK_BUCK2_2 */
|
||||
|
||||
#define DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM 0x0F
|
||||
|
||||
/* DA9121_REG_BUCK_BUCK1_3 / DA9xxx_REG_BUCK_BUCK2_3 */
|
||||
|
||||
#define DA9121_MASK_BUCK_BUCKx_3_CHx_VMAX 0xFF
|
||||
|
||||
/* DA9121_REG_BUCK_BUCK1_4 / DA9xxx_REG_BUCK_BUCK2_4 */
|
||||
|
||||
#define DA9121_MASK_BUCK_BUCKx_4_CHx_VSEL BIT(4)
|
||||
#define DA9121_MASK_BUCK_BUCKx_4_CHx_B_MODE 0x0C
|
||||
#define DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE 0x03
|
||||
|
||||
/* DA9121_REG_BUCK_BUCK1_5 / DA9xxx_REG_BUCK_BUCK2_5 */
|
||||
|
||||
#define DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT 0xFF
|
||||
|
||||
/* DA9121_REG_BUCK_BUCK1_6 / DA9xxx_REG_BUCK_BUCK2_6 */
|
||||
|
||||
#define DA9121_MASK_BUCK_BUCKx_6_CHx_B_VOUT 0xFF
|
||||
|
||||
/* DA9121_REG_BUCK_BUCK1_7 / DA9xxx_REG_BUCK_BUCK2_7 */
|
||||
|
||||
#define DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL 0x03
|
||||
|
||||
|
||||
/* DA9121_REG_OTP_DEVICE_ID */
|
||||
|
||||
#define DA9121_MASK_OTP_DEVICE_ID_DEV_ID 0xFF
|
||||
|
||||
#define DA9121_DEVICE_ID 0x05
|
||||
|
||||
/* DA9121_REG_OTP_VARIANT_ID */
|
||||
|
||||
#define DA9121_SHIFT_OTP_VARIANT_ID_MRC 4
|
||||
#define DA9121_MASK_OTP_VARIANT_ID_MRC 0xF0
|
||||
#define DA9121_SHIFT_OTP_VARIANT_ID_VRC 0
|
||||
#define DA9121_MASK_OTP_VARIANT_ID_VRC 0x0F
|
||||
|
||||
#define DA9121_VARIANT_MRC_BASE 0x2
|
||||
#define DA9121_VARIANT_VRC 0x1
|
||||
#define DA9220_VARIANT_VRC 0x0
|
||||
#define DA9122_VARIANT_VRC 0x2
|
||||
#define DA9217_VARIANT_VRC 0x7
|
||||
|
||||
/* DA9121_REG_OTP_CUSTOMER_ID */
|
||||
|
||||
#define DA9121_MASK_OTP_CUSTOMER_ID_CUST_ID 0xFF
|
||||
|
||||
/* DA9121_REG_OTP_CONFIG_ID */
|
||||
|
||||
#define DA9121_MASK_OTP_CONFIG_ID_CONFIG_REV 0xFF
|
||||
|
||||
#endif /* __DA9121_REGISTERS_H__ */
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/fixed.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
@@ -34,11 +36,13 @@ struct fixed_voltage_data {
|
||||
struct regulator_dev *dev;
|
||||
|
||||
struct clk *enable_clock;
|
||||
unsigned int clk_enable_counter;
|
||||
unsigned int enable_counter;
|
||||
int performance_state;
|
||||
};
|
||||
|
||||
struct fixed_dev_type {
|
||||
bool has_enable_clock;
|
||||
bool has_performance_state;
|
||||
};
|
||||
|
||||
static int reg_clock_enable(struct regulator_dev *rdev)
|
||||
@@ -50,7 +54,7 @@ static int reg_clock_enable(struct regulator_dev *rdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->clk_enable_counter++;
|
||||
priv->enable_counter++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -60,16 +64,41 @@ static int reg_clock_disable(struct regulator_dev *rdev)
|
||||
struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
|
||||
|
||||
clk_disable_unprepare(priv->enable_clock);
|
||||
priv->clk_enable_counter--;
|
||||
priv->enable_counter--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reg_clock_is_enabled(struct regulator_dev *rdev)
|
||||
static int reg_domain_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
|
||||
struct device *dev = rdev->dev.parent;
|
||||
int ret;
|
||||
|
||||
ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->enable_counter++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int reg_domain_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
|
||||
struct device *dev = rdev->dev.parent;
|
||||
|
||||
priv->enable_counter--;
|
||||
|
||||
return dev_pm_genpd_set_performance_state(dev, 0);
|
||||
}
|
||||
|
||||
static int reg_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
|
||||
|
||||
return priv->clk_enable_counter > 0;
|
||||
return priv->enable_counter > 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +158,13 @@ static const struct regulator_ops fixed_voltage_ops = {
|
||||
static const struct regulator_ops fixed_voltage_clkenabled_ops = {
|
||||
.enable = reg_clock_enable,
|
||||
.disable = reg_clock_disable,
|
||||
.is_enabled = reg_clock_is_enabled,
|
||||
.is_enabled = reg_is_enabled,
|
||||
};
|
||||
|
||||
static const struct regulator_ops fixed_voltage_domain_ops = {
|
||||
.enable = reg_domain_enable,
|
||||
.disable = reg_domain_disable,
|
||||
.is_enabled = reg_is_enabled,
|
||||
};
|
||||
|
||||
static int reg_fixed_voltage_probe(struct platform_device *pdev)
|
||||
@@ -177,6 +212,14 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev)
|
||||
dev_err(dev, "Can't get enable-clock from devicetree\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
} else if (drvtype && drvtype->has_performance_state) {
|
||||
drvdata->desc.ops = &fixed_voltage_domain_ops;
|
||||
|
||||
drvdata->performance_state = of_get_required_opp_performance_state(dev->of_node, 0);
|
||||
if (drvdata->performance_state < 0) {
|
||||
dev_err(dev, "Can't get performance state from devicetree\n");
|
||||
return drvdata->performance_state;
|
||||
}
|
||||
} else {
|
||||
drvdata->desc.ops = &fixed_voltage_ops;
|
||||
}
|
||||
@@ -260,6 +303,10 @@ static const struct fixed_dev_type fixed_clkenable_data = {
|
||||
.has_enable_clock = true,
|
||||
};
|
||||
|
||||
static const struct fixed_dev_type fixed_domain_data = {
|
||||
.has_performance_state = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id fixed_of_match[] = {
|
||||
{
|
||||
.compatible = "regulator-fixed",
|
||||
@@ -269,6 +316,10 @@ static const struct of_device_id fixed_of_match[] = {
|
||||
.compatible = "regulator-fixed-clock",
|
||||
.data = &fixed_clkenable_data,
|
||||
},
|
||||
{
|
||||
.compatible = "regulator-fixed-domain",
|
||||
.data = &fixed_domain_data,
|
||||
},
|
||||
{
|
||||
},
|
||||
};
|
||||
|
||||
@@ -649,6 +649,8 @@ int regulator_list_voltage_table(struct regulator_dev *rdev,
|
||||
|
||||
if (selector >= rdev->desc->n_voltages)
|
||||
return -EINVAL;
|
||||
if (selector < rdev->desc->linear_min_sel)
|
||||
return 0;
|
||||
|
||||
return rdev->desc->volt_table[selector];
|
||||
}
|
||||
|
||||
@@ -892,7 +892,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
|
||||
struct lp872x *lp;
|
||||
struct lp872x_platform_data *pdata;
|
||||
int ret;
|
||||
const int lp872x_num_regulators[] = {
|
||||
static const int lp872x_num_regulators[] = {
|
||||
[LP8720] = LP8720_NUM_REGULATORS,
|
||||
[LP8725] = LP8725_NUM_REGULATORS,
|
||||
};
|
||||
|
||||
@@ -269,3 +269,5 @@ module_exit(max14577_regulator_exit);
|
||||
MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
|
||||
MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:max14577-regulator");
|
||||
MODULE_ALIAS("platform:max77836-regulator");
|
||||
|
||||
@@ -582,8 +582,8 @@ static int mc13892_regulator_probe(struct platform_device *pdev)
|
||||
/* update mc13892_vcam ops */
|
||||
memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops,
|
||||
sizeof(struct regulator_ops));
|
||||
mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode,
|
||||
mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode,
|
||||
mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode;
|
||||
mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode;
|
||||
mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops;
|
||||
|
||||
mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators,
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
#define VDD_LOW_SEL 0x0D
|
||||
#define VDD_HIGH_SEL 0x3F
|
||||
|
||||
#define MCP16502_FLT BIT(7)
|
||||
#define MCP16502_ENS BIT(0)
|
||||
#define MCP16502_FLT BIT(7)
|
||||
#define MCP16502_DVSR GENMASK(3, 2)
|
||||
#define MCP16502_ENS BIT(0)
|
||||
|
||||
/*
|
||||
* The PMIC has four sets of registers corresponding to four power modes:
|
||||
@@ -54,13 +55,9 @@
|
||||
* This function is useful for iterating over all regulators and accessing their
|
||||
* registers in a generic way or accessing a regulator device by its id.
|
||||
*/
|
||||
#define MCP16502_BASE(i) (((i) + 1) << 4)
|
||||
#define MCP16502_REG_BASE(i, r) ((((i) + 1) << 4) + MCP16502_REG_##r)
|
||||
#define MCP16502_STAT_BASE(i) ((i) + 5)
|
||||
|
||||
#define MCP16502_OFFSET_MODE_A 0
|
||||
#define MCP16502_OFFSET_MODE_LPM 1
|
||||
#define MCP16502_OFFSET_MODE_HIB 2
|
||||
|
||||
#define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL
|
||||
#define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE
|
||||
#define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY
|
||||
@@ -75,6 +72,29 @@
|
||||
#define MCP16502_MIN_REG 0x0
|
||||
#define MCP16502_MAX_REG 0x65
|
||||
|
||||
/**
|
||||
* enum mcp16502_reg - MCP16502 regulators's registers
|
||||
* @MCP16502_REG_A: active state register
|
||||
* @MCP16502_REG_LPM: low power mode state register
|
||||
* @MCP16502_REG_HIB: hibernate state register
|
||||
* @MCP16502_REG_SEQ: startup sequence register
|
||||
* @MCP16502_REG_CFG: configuration register
|
||||
*/
|
||||
enum mcp16502_reg {
|
||||
MCP16502_REG_A,
|
||||
MCP16502_REG_LPM,
|
||||
MCP16502_REG_HIB,
|
||||
MCP16502_REG_HPM,
|
||||
MCP16502_REG_SEQ,
|
||||
MCP16502_REG_CFG,
|
||||
};
|
||||
|
||||
/* Ramp delay (uV/us) for buck1, ldo1, ldo2. */
|
||||
static const int mcp16502_ramp_b1l12[] = { 6250, 3125, 2083, 1563 };
|
||||
|
||||
/* Ramp delay (uV/us) for buck2, buck3, buck4. */
|
||||
static const int mcp16502_ramp_b234[] = { 3125, 1563, 1042, 781 };
|
||||
|
||||
static unsigned int mcp16502_of_map_mode(unsigned int mode)
|
||||
{
|
||||
if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE)
|
||||
@@ -93,6 +113,7 @@ static unsigned int mcp16502_of_map_mode(unsigned int mode)
|
||||
.owner = THIS_MODULE, \
|
||||
.n_voltages = MCP16502_VSEL + 1, \
|
||||
.linear_ranges = _ranges, \
|
||||
.linear_min_sel = VDD_LOW_SEL, \
|
||||
.n_linear_ranges = ARRAY_SIZE(_ranges), \
|
||||
.of_match = of_match_ptr(_name), \
|
||||
.of_map_mode = mcp16502_of_map_mode, \
|
||||
@@ -114,8 +135,6 @@ enum {
|
||||
|
||||
/*
|
||||
* struct mcp16502 - PMIC representation
|
||||
* @rdev: the regulators belonging to this chip
|
||||
* @rmap: regmap to be used for I2C communication
|
||||
* @lpm: LPM GPIO descriptor
|
||||
*/
|
||||
struct mcp16502 {
|
||||
@@ -143,22 +162,20 @@ static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode)
|
||||
}
|
||||
|
||||
/*
|
||||
* mcp16502_get_reg() - get the PMIC's configuration register for opmode
|
||||
* mcp16502_get_reg() - get the PMIC's state configuration register for opmode
|
||||
*
|
||||
* @rdev: the regulator whose register we are searching
|
||||
* @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate
|
||||
*/
|
||||
static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode)
|
||||
static int mcp16502_get_state_reg(struct regulator_dev *rdev, int opmode)
|
||||
{
|
||||
int reg = MCP16502_BASE(rdev_get_id(rdev));
|
||||
|
||||
switch (opmode) {
|
||||
case MCP16502_OPMODE_ACTIVE:
|
||||
return reg + MCP16502_OFFSET_MODE_A;
|
||||
return MCP16502_REG_BASE(rdev_get_id(rdev), A);
|
||||
case MCP16502_OPMODE_LPM:
|
||||
return reg + MCP16502_OFFSET_MODE_LPM;
|
||||
return MCP16502_REG_BASE(rdev_get_id(rdev), LPM);
|
||||
case MCP16502_OPMODE_HIB:
|
||||
return reg + MCP16502_OFFSET_MODE_HIB;
|
||||
return MCP16502_REG_BASE(rdev_get_id(rdev), HIB);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -178,7 +195,7 @@ static unsigned int mcp16502_get_mode(struct regulator_dev *rdev)
|
||||
unsigned int val;
|
||||
int ret, reg;
|
||||
|
||||
reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE);
|
||||
reg = mcp16502_get_state_reg(rdev, MCP16502_OPMODE_ACTIVE);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
@@ -209,7 +226,7 @@ static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode,
|
||||
int val;
|
||||
int reg;
|
||||
|
||||
reg = mcp16502_get_reg(rdev, op_mode);
|
||||
reg = mcp16502_get_state_reg(rdev, op_mode);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
@@ -259,6 +276,80 @@ static int mcp16502_get_status(struct regulator_dev *rdev)
|
||||
return REGULATOR_STATUS_UNDEFINED;
|
||||
}
|
||||
|
||||
static int mcp16502_set_voltage_time_sel(struct regulator_dev *rdev,
|
||||
unsigned int old_sel,
|
||||
unsigned int new_sel)
|
||||
{
|
||||
static const u8 us_ramp[] = { 8, 16, 24, 32 };
|
||||
int id = rdev_get_id(rdev);
|
||||
unsigned int uV_delta, val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rdev->regmap, MCP16502_REG_BASE(id, CFG), &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = (val & MCP16502_DVSR) >> 2;
|
||||
uV_delta = abs(new_sel * rdev->desc->linear_ranges->step -
|
||||
old_sel * rdev->desc->linear_ranges->step);
|
||||
switch (id) {
|
||||
case BUCK1:
|
||||
case LDO1:
|
||||
case LDO2:
|
||||
ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val],
|
||||
mcp16502_ramp_b1l12[val]);
|
||||
break;
|
||||
|
||||
case BUCK2:
|
||||
case BUCK3:
|
||||
case BUCK4:
|
||||
ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val],
|
||||
mcp16502_ramp_b234[val]);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp16502_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
|
||||
{
|
||||
const int *ramp;
|
||||
int id = rdev_get_id(rdev);
|
||||
unsigned int i, size;
|
||||
|
||||
switch (id) {
|
||||
case BUCK1:
|
||||
case LDO1:
|
||||
case LDO2:
|
||||
ramp = mcp16502_ramp_b1l12;
|
||||
size = ARRAY_SIZE(mcp16502_ramp_b1l12);
|
||||
break;
|
||||
|
||||
case BUCK2:
|
||||
case BUCK3:
|
||||
case BUCK4:
|
||||
ramp = mcp16502_ramp_b234;
|
||||
size = ARRAY_SIZE(mcp16502_ramp_b234);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (ramp[i] == ramp_delay)
|
||||
break;
|
||||
}
|
||||
if (i == size)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(rdev->regmap, MCP16502_REG_BASE(id, CFG),
|
||||
MCP16502_DVSR, (i << 2));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
/*
|
||||
* mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC
|
||||
@@ -268,10 +359,10 @@ static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev)
|
||||
{
|
||||
switch (pm_suspend_target_state) {
|
||||
case PM_SUSPEND_STANDBY:
|
||||
return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM);
|
||||
return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_LPM);
|
||||
case PM_SUSPEND_ON:
|
||||
case PM_SUSPEND_MEM:
|
||||
return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB);
|
||||
return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_HIB);
|
||||
default:
|
||||
dev_err(&rdev->dev, "invalid suspend target: %d\n",
|
||||
pm_suspend_target_state);
|
||||
@@ -353,6 +444,8 @@ static const struct regulator_ops mcp16502_buck_ops = {
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.get_status = mcp16502_get_status,
|
||||
.set_voltage_time_sel = mcp16502_set_voltage_time_sel,
|
||||
.set_ramp_delay = mcp16502_set_ramp_delay,
|
||||
|
||||
.set_mode = mcp16502_set_mode,
|
||||
.get_mode = mcp16502_get_mode,
|
||||
@@ -377,6 +470,8 @@ static const struct regulator_ops mcp16502_ldo_ops = {
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.get_status = mcp16502_get_status,
|
||||
.set_voltage_time_sel = mcp16502_set_voltage_time_sel,
|
||||
.set_ramp_delay = mcp16502_set_ramp_delay,
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
.set_suspend_voltage = mcp16502_set_suspend_voltage,
|
||||
|
||||
@@ -413,8 +413,12 @@ device_node *regulator_of_get_init_node(struct device *dev,
|
||||
|
||||
for_each_available_child_of_node(search, child) {
|
||||
name = of_get_property(child, "regulator-compatible", NULL);
|
||||
if (!name)
|
||||
name = child->name;
|
||||
if (!name) {
|
||||
if (!desc->of_match_full_name)
|
||||
name = child->name;
|
||||
else
|
||||
name = child->full_name;
|
||||
}
|
||||
|
||||
if (!strcmp(desc->of_match, name)) {
|
||||
of_node_put(search);
|
||||
|
||||
496
drivers/regulator/pf8x00-regulator.c
Normal file
496
drivers/regulator/pf8x00-regulator.c
Normal file
@@ -0,0 +1,496 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017 NXP
|
||||
* Copyright (C) 2019 Boundary Devices
|
||||
* Copyright (C) 2020 Amarula Solutions(India)
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
|
||||
/* registers */
|
||||
#define PF8X00_DEVICEID 0x00
|
||||
#define PF8X00_REVID 0x01
|
||||
#define PF8X00_EMREV 0x02
|
||||
#define PF8X00_PROGID 0x03
|
||||
#define PF8X00_IMS_INT 0x04
|
||||
#define PF8X00_IMS_THERM 0x07
|
||||
#define PF8X00_SW_MODE_INT 0x0a
|
||||
#define PF8X00_SW_MODE_MASK 0x0b
|
||||
#define PF8X00_IMS_SW_ILIM 0x12
|
||||
#define PF8X00_IMS_LDO_ILIM 0x15
|
||||
#define PF8X00_IMS_SW_UV 0x18
|
||||
#define PF8X00_IMS_SW_OV 0x1b
|
||||
#define PF8X00_IMS_LDO_UV 0x1e
|
||||
#define PF8X00_IMS_LDO_OV 0x21
|
||||
#define PF8X00_IMS_PWRON 0x24
|
||||
#define PF8X00_SYS_INT 0x27
|
||||
#define PF8X00_HARD_FAULT 0x29
|
||||
#define PF8X00_FSOB_FLAGS 0x2a
|
||||
#define PF8X00_FSOB_SELECT 0x2b
|
||||
#define PF8X00_ABIST_OV1 0x2c
|
||||
#define PF8X00_ABIST_OV2 0x2d
|
||||
#define PF8X00_ABIST_UV1 0x2e
|
||||
#define PF8X00_ABIST_UV2 0x2f
|
||||
#define PF8X00_TEST_FLAGS 0x30
|
||||
#define PF8X00_ABIST_RUN 0x31
|
||||
#define PF8X00_RANDOM_GEN 0x33
|
||||
#define PF8X00_RANDOM_CHK 0x34
|
||||
#define PF8X00_VMONEN1 0x35
|
||||
#define PF8X00_VMONEN2 0x36
|
||||
#define PF8X00_CTRL1 0x37
|
||||
#define PF8X00_CTRL2 0x38
|
||||
#define PF8X00_CTRL3 0x39
|
||||
#define PF8X00_PWRUP_CTRL 0x3a
|
||||
#define PF8X00_RESETBMCU 0x3c
|
||||
#define PF8X00_PGOOD 0x3d
|
||||
#define PF8X00_PWRDN_DLY1 0x3e
|
||||
#define PF8X00_PWRDN_DLY2 0x3f
|
||||
#define PF8X00_FREQ_CTRL 0x40
|
||||
#define PF8X00_COINCELL_CTRL 0x41
|
||||
#define PF8X00_PWRON 0x42
|
||||
#define PF8X00_WD_CONFIG 0x43
|
||||
#define PF8X00_WD_CLEAR 0x44
|
||||
#define PF8X00_WD_EXPIRE 0x45
|
||||
#define PF8X00_WD_COUNTER 0x46
|
||||
#define PF8X00_FAULT_COUNTER 0x47
|
||||
#define PF8X00_FSAFE_COUNTER 0x48
|
||||
#define PF8X00_FAULT_TIMER 0x49
|
||||
#define PF8X00_AMUX 0x4a
|
||||
#define PF8X00_SW1_CONFIG1 0x4d
|
||||
#define PF8X00_LDO1_CONFIG1 0x85
|
||||
#define PF8X00_VSNVS_CONFIG1 0x9d
|
||||
#define PF8X00_PAGE_SELECT 0x9f
|
||||
|
||||
/* regulators */
|
||||
enum pf8x00_regulators {
|
||||
PF8X00_LDO1,
|
||||
PF8X00_LDO2,
|
||||
PF8X00_LDO3,
|
||||
PF8X00_LDO4,
|
||||
PF8X00_BUCK1,
|
||||
PF8X00_BUCK2,
|
||||
PF8X00_BUCK3,
|
||||
PF8X00_BUCK4,
|
||||
PF8X00_BUCK5,
|
||||
PF8X00_BUCK6,
|
||||
PF8X00_BUCK7,
|
||||
PF8X00_VSNVS,
|
||||
|
||||
PF8X00_MAX_REGULATORS,
|
||||
};
|
||||
|
||||
enum pf8x00_buck_states {
|
||||
SW_CONFIG1,
|
||||
SW_CONFIG2,
|
||||
SW_PWRUP,
|
||||
SW_MODE1,
|
||||
SW_RUN_VOLT,
|
||||
SW_STBY_VOLT,
|
||||
};
|
||||
#define PF8X00_SW_BASE(i) (8 * (i - PF8X00_BUCK1) + PF8X00_SW1_CONFIG1)
|
||||
|
||||
enum pf8x00_ldo_states {
|
||||
LDO_CONFIG1,
|
||||
LDO_CONFIG2,
|
||||
LDO_PWRUP,
|
||||
LDO_RUN_VOLT,
|
||||
LDO_STBY_VOLT,
|
||||
};
|
||||
#define PF8X00_LDO_BASE(i) (6 * (i - PF8X00_LDO1) + PF8X00_LDO1_CONFIG1)
|
||||
|
||||
enum swxilim_bits {
|
||||
SWXILIM_2100_MA,
|
||||
SWXILIM_2600_MA,
|
||||
SWXILIM_3000_MA,
|
||||
SWXILIM_4500_MA,
|
||||
};
|
||||
#define PF8X00_SWXILIM_SHIFT 3
|
||||
#define PF8X00_SWXILIM_MASK GENMASK(4, 3)
|
||||
#define PF8X00_SWXPHASE_MASK GENMASK(2, 0)
|
||||
#define PF8X00_SWXPHASE_DEFAULT 0
|
||||
#define PF8X00_SWXPHASE_SHIFT 7
|
||||
|
||||
enum pf8x00_devid {
|
||||
PF8100 = 0x0,
|
||||
PF8121A = BIT(1),
|
||||
PF8200 = BIT(3),
|
||||
};
|
||||
#define PF8X00_FAM BIT(6)
|
||||
#define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4)
|
||||
#define PF8X00_DEVICE_ID_MASK GENMASK(3, 0)
|
||||
|
||||
struct pf8x00_regulator {
|
||||
struct regulator_desc desc;
|
||||
u8 ilim;
|
||||
u8 phase_shift;
|
||||
};
|
||||
|
||||
struct pf8x00_chip {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static const struct regmap_config pf8x00_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = PF8X00_PAGE_SELECT,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
/* VLDOx output: 1.5V to 5.0V */
|
||||
static const int pf8x00_ldo_voltages[] = {
|
||||
1500000, 1600000, 1800000, 1850000, 2150000, 2500000, 2800000, 3000000,
|
||||
3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000,
|
||||
};
|
||||
|
||||
#define SWV(i) (6250 * i + 400000)
|
||||
#define SWV_LINE(i) SWV(i*8+0), SWV(i*8+1), SWV(i*8+2), SWV(i*8+3), \
|
||||
SWV(i*8+4), SWV(i*8+5), SWV(i*8+6), SWV(i*8+7)
|
||||
|
||||
/* Output: 0.4V to 1.8V */
|
||||
static const int pf8x00_sw1_to_6_voltages[] = {
|
||||
SWV_LINE(0),
|
||||
SWV_LINE(1),
|
||||
SWV_LINE(2),
|
||||
SWV_LINE(3),
|
||||
SWV_LINE(4),
|
||||
SWV_LINE(5),
|
||||
SWV_LINE(6),
|
||||
SWV_LINE(7),
|
||||
SWV_LINE(8),
|
||||
SWV_LINE(9),
|
||||
SWV_LINE(10),
|
||||
SWV_LINE(11),
|
||||
SWV_LINE(12),
|
||||
SWV_LINE(13),
|
||||
SWV_LINE(14),
|
||||
SWV_LINE(15),
|
||||
SWV_LINE(16),
|
||||
SWV_LINE(17),
|
||||
SWV_LINE(18),
|
||||
SWV_LINE(19),
|
||||
SWV_LINE(20),
|
||||
SWV_LINE(21),
|
||||
1500000, 1800000,
|
||||
};
|
||||
|
||||
/* Output: 1.0V to 4.1V */
|
||||
static const int pf8x00_sw7_voltages[] = {
|
||||
1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1500000, 1600000,
|
||||
1800000, 1850000, 2000000, 2100000, 2150000, 2250000, 2300000, 2400000,
|
||||
2500000, 2800000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000,
|
||||
3500000, 3800000, 4000000, 4100000, 4100000, 4100000, 4100000, 4100000,
|
||||
};
|
||||
|
||||
/* Output: 1.8V, 3.0V, or 3.3V */
|
||||
static const int pf8x00_vsnvs_voltages[] = {
|
||||
0, 1800000, 3000000, 3300000,
|
||||
};
|
||||
|
||||
static struct pf8x00_regulator *desc_to_regulator(const struct regulator_desc *desc)
|
||||
{
|
||||
return container_of(desc, struct pf8x00_regulator, desc);
|
||||
}
|
||||
|
||||
static void swxilim_select(const struct regulator_desc *desc, int ilim)
|
||||
{
|
||||
struct pf8x00_regulator *data = desc_to_regulator(desc);
|
||||
u8 ilim_sel;
|
||||
|
||||
switch (ilim) {
|
||||
case 2100:
|
||||
ilim_sel = SWXILIM_2100_MA;
|
||||
break;
|
||||
case 2600:
|
||||
ilim_sel = SWXILIM_2600_MA;
|
||||
break;
|
||||
case 3000:
|
||||
ilim_sel = SWXILIM_3000_MA;
|
||||
break;
|
||||
case 4500:
|
||||
ilim_sel = SWXILIM_4500_MA;
|
||||
break;
|
||||
default:
|
||||
ilim_sel = SWXILIM_2100_MA;
|
||||
break;
|
||||
}
|
||||
|
||||
data->ilim = ilim_sel;
|
||||
}
|
||||
|
||||
static int pf8x00_of_parse_cb(struct device_node *np,
|
||||
const struct regulator_desc *desc,
|
||||
struct regulator_config *config)
|
||||
{
|
||||
struct pf8x00_regulator *data = desc_to_regulator(desc);
|
||||
struct pf8x00_chip *chip = config->driver_data;
|
||||
int phase;
|
||||
int val;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "nxp,ilim-ma", &val);
|
||||
if (ret)
|
||||
dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use 2100 mA\n",
|
||||
desc->id - PF8X00_LDO4);
|
||||
|
||||
swxilim_select(desc, val);
|
||||
|
||||
ret = of_property_read_u32(np, "nxp,phase-shift", &val);
|
||||
if (ret) {
|
||||
dev_dbg(chip->dev,
|
||||
"unspecified phase-shift for BUCK%d, use 0 degrees\n",
|
||||
desc->id - PF8X00_LDO4);
|
||||
val = PF8X00_SWXPHASE_DEFAULT;
|
||||
}
|
||||
|
||||
phase = val / 45;
|
||||
if ((phase * 45) != val) {
|
||||
dev_warn(config->dev,
|
||||
"invalid phase_shift %d for BUCK%d, use 0 degrees\n",
|
||||
(phase * 45), desc->id - PF8X00_LDO4);
|
||||
phase = PF8X00_SWXPHASE_SHIFT;
|
||||
}
|
||||
|
||||
data->phase_shift = (phase >= 1) ? phase - 1 : PF8X00_SWXPHASE_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regulator_ops pf8x00_ldo_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
};
|
||||
|
||||
static const struct regulator_ops pf8x00_buck_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
};
|
||||
|
||||
static const struct regulator_ops pf8x00_vsnvs_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.map_voltage = regulator_map_voltage_ascend,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
};
|
||||
|
||||
#define PF8X00LDO(_id, _name, base, voltages) \
|
||||
[PF8X00_LDO ## _id] = { \
|
||||
.desc = { \
|
||||
.name = _name, \
|
||||
.of_match = _name, \
|
||||
.regulators_node = "regulators", \
|
||||
.n_voltages = ARRAY_SIZE(voltages), \
|
||||
.ops = &pf8x00_ldo_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = PF8X00_LDO ## _id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.volt_table = voltages, \
|
||||
.vsel_reg = (base) + LDO_RUN_VOLT, \
|
||||
.vsel_mask = 0xff, \
|
||||
.enable_reg = (base) + LDO_CONFIG2, \
|
||||
.enable_val = 0x2, \
|
||||
.disable_val = 0x0, \
|
||||
.enable_mask = 2, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define PF8X00BUCK(_id, _name, base, voltages) \
|
||||
[PF8X00_BUCK ## _id] = { \
|
||||
.desc = { \
|
||||
.name = _name, \
|
||||
.of_match = _name, \
|
||||
.regulators_node = "regulators", \
|
||||
.of_parse_cb = pf8x00_of_parse_cb, \
|
||||
.n_voltages = ARRAY_SIZE(voltages), \
|
||||
.ops = &pf8x00_buck_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = PF8X00_BUCK ## _id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.volt_table = voltages, \
|
||||
.vsel_reg = (base) + SW_RUN_VOLT, \
|
||||
.vsel_mask = 0xff, \
|
||||
.enable_reg = (base) + SW_MODE1, \
|
||||
.enable_val = 0x3, \
|
||||
.disable_val = 0x0, \
|
||||
.enable_mask = 0x3, \
|
||||
.enable_time = 500, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define PF8X00VSNVS(_name, base, voltages) \
|
||||
[PF8X00_VSNVS] = { \
|
||||
.desc = { \
|
||||
.name = _name, \
|
||||
.of_match = _name, \
|
||||
.regulators_node = "regulators", \
|
||||
.n_voltages = ARRAY_SIZE(voltages), \
|
||||
.ops = &pf8x00_vsnvs_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = PF8X00_VSNVS, \
|
||||
.owner = THIS_MODULE, \
|
||||
.volt_table = voltages, \
|
||||
.vsel_reg = (base), \
|
||||
.vsel_mask = 0x3, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = {
|
||||
PF8X00LDO(1, "ldo1", PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages),
|
||||
PF8X00LDO(2, "ldo2", PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages),
|
||||
PF8X00LDO(3, "ldo3", PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages),
|
||||
PF8X00LDO(4, "ldo4", PF8X00_LDO_BASE(PF8X00_LDO4), pf8x00_ldo_voltages),
|
||||
PF8X00BUCK(1, "buck1", PF8X00_SW_BASE(PF8X00_BUCK1), pf8x00_sw1_to_6_voltages),
|
||||
PF8X00BUCK(2, "buck2", PF8X00_SW_BASE(PF8X00_BUCK2), pf8x00_sw1_to_6_voltages),
|
||||
PF8X00BUCK(3, "buck3", PF8X00_SW_BASE(PF8X00_BUCK3), pf8x00_sw1_to_6_voltages),
|
||||
PF8X00BUCK(4, "buck4", PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages),
|
||||
PF8X00BUCK(5, "buck5", PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages),
|
||||
PF8X00BUCK(6, "buck6", PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages),
|
||||
PF8X00BUCK(7, "buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages),
|
||||
PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages),
|
||||
};
|
||||
|
||||
static int pf8x00_identify(struct pf8x00_chip *chip)
|
||||
{
|
||||
unsigned int value;
|
||||
u8 dev_fam, dev_id;
|
||||
const char *name = NULL;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(chip->regmap, PF8X00_DEVICEID, &value);
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "failed to read chip family\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_fam = value & PF8X00_DEVICE_FAM_MASK;
|
||||
switch (dev_fam) {
|
||||
case PF8X00_FAM:
|
||||
break;
|
||||
default:
|
||||
dev_err(chip->dev,
|
||||
"Chip 0x%x is not from PF8X00 family\n", dev_fam);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_id = value & PF8X00_DEVICE_ID_MASK;
|
||||
switch (dev_id) {
|
||||
case PF8100:
|
||||
name = "PF8100";
|
||||
break;
|
||||
case PF8121A:
|
||||
name = "PF8121A";
|
||||
break;
|
||||
case PF8200:
|
||||
name = "PF8100";
|
||||
break;
|
||||
default:
|
||||
dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n", dev_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(chip->dev, "%s PMIC found.\n", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pf8x00_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regulator_config config = { NULL, };
|
||||
struct pf8x00_chip *chip;
|
||||
int id;
|
||||
int ret;
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
chip->dev = &client->dev;
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(client, &pf8x00_regmap_config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
ret = PTR_ERR(chip->regmap);
|
||||
dev_err(&client->dev,
|
||||
"regmap allocation failed with err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pf8x00_identify(chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (id = 0; id < ARRAY_SIZE(pf8x00_regulators_data); id++) {
|
||||
struct pf8x00_regulator *data = &pf8x00_regulators_data[id];
|
||||
struct regulator_dev *rdev;
|
||||
|
||||
config.dev = chip->dev;
|
||||
config.driver_data = chip;
|
||||
config.regmap = chip->regmap;
|
||||
|
||||
rdev = devm_regulator_register(&client->dev, &data->desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(&client->dev,
|
||||
"failed to register %s regulator\n", data->desc.name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
||||
if ((id >= PF8X00_BUCK1) && (id <= PF8X00_BUCK7)) {
|
||||
u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2;
|
||||
|
||||
regmap_update_bits(chip->regmap, reg,
|
||||
PF8X00_SWXPHASE_MASK,
|
||||
data->phase_shift);
|
||||
|
||||
regmap_update_bits(chip->regmap, reg,
|
||||
PF8X00_SWXILIM_MASK,
|
||||
data->ilim << PF8X00_SWXILIM_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id pf8x00_dt_ids[] = {
|
||||
{ .compatible = "nxp,pf8x00",},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pf8x00_dt_ids);
|
||||
|
||||
static const struct i2c_device_id pf8x00_i2c_id[] = {
|
||||
{ "pf8x00", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id);
|
||||
|
||||
static struct i2c_driver pf8x00_regulator_driver = {
|
||||
.id_table = pf8x00_i2c_id,
|
||||
.driver = {
|
||||
.name = "pf8x00",
|
||||
.of_match_table = pf8x00_dt_ids,
|
||||
},
|
||||
.probe_new = pf8x00_i2c_probe,
|
||||
};
|
||||
module_i2c_driver(pf8x00_regulator_driver);
|
||||
|
||||
MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
|
||||
MODULE_AUTHOR("Troy Kisky <troy.kisky@boundarydevices.com>");
|
||||
MODULE_DESCRIPTION("Regulator Driver for NXP's PF8100/PF8121A/PF8200 PMIC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -105,15 +105,6 @@ static const int pfuze3000_sw2hi[] = {
|
||||
2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id pfuze_device_id[] = {
|
||||
{.name = "pfuze100", .driver_data = PFUZE100},
|
||||
{.name = "pfuze200", .driver_data = PFUZE200},
|
||||
{.name = "pfuze3000", .driver_data = PFUZE3000},
|
||||
{.name = "pfuze3001", .driver_data = PFUZE3001},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pfuze_device_id);
|
||||
|
||||
static const struct of_device_id pfuze_dt_ids[] = {
|
||||
{ .compatible = "fsl,pfuze100", .data = (void *)PFUZE100},
|
||||
{ .compatible = "fsl,pfuze200", .data = (void *)PFUZE200},
|
||||
@@ -440,7 +431,6 @@ static struct pfuze_regulator pfuze3001_regulators[] = {
|
||||
PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* PFUZE100 */
|
||||
static struct of_regulator_match pfuze100_matches[] = {
|
||||
{ .name = "sw1ab", },
|
||||
@@ -578,22 +568,6 @@ static inline struct device_node *match_of_node(int index)
|
||||
{
|
||||
return pfuze_matches[index].of_node;
|
||||
}
|
||||
#else
|
||||
static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct regulator_init_data *match_init_data(int index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct device_node *match_of_node(int index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct pfuze_chip *syspm_pfuze_chip;
|
||||
|
||||
@@ -708,8 +682,6 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pfuze_chip *pfuze_chip;
|
||||
struct pfuze_regulator_platform_data *pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct regulator_config config = { };
|
||||
int i, ret;
|
||||
const struct of_device_id *match;
|
||||
@@ -802,10 +774,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
|
||||
|
||||
desc = &pfuze_chip->regulator_descs[i].desc;
|
||||
|
||||
if (pdata)
|
||||
init_data = pdata->init_data[i];
|
||||
else
|
||||
init_data = match_init_data(i);
|
||||
init_data = match_init_data(i);
|
||||
|
||||
/* SW2~SW4 high bit check and modify the voltage value table */
|
||||
if (i >= sw_check_start && i <= sw_check_end) {
|
||||
@@ -879,7 +848,6 @@ static int pfuze100_regulator_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static struct i2c_driver pfuze_driver = {
|
||||
.id_table = pfuze_device_id,
|
||||
.driver = {
|
||||
.name = "pfuze100-regulator",
|
||||
.of_match_table = pfuze_dt_ids,
|
||||
|
||||
@@ -865,6 +865,60 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct rpmh_vreg_init_data pm8350_vreg_data[] = {
|
||||
RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"),
|
||||
RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"),
|
||||
RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"),
|
||||
RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"),
|
||||
RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"),
|
||||
RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"),
|
||||
RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"),
|
||||
RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"),
|
||||
RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"),
|
||||
RPMH_VREG("smps10", "smp%s10", &pmic5_hfsmps510, "vdd-s10"),
|
||||
RPMH_VREG("smps11", "smp%s11", &pmic5_hfsmps510, "vdd-s11"),
|
||||
RPMH_VREG("smps12", "smp%s12", &pmic5_hfsmps510, "vdd-s12"),
|
||||
RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4"),
|
||||
RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"),
|
||||
RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l5"),
|
||||
RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4"),
|
||||
RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l5"),
|
||||
RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"),
|
||||
RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"),
|
||||
RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"),
|
||||
RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"),
|
||||
RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"),
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = {
|
||||
RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"),
|
||||
RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"),
|
||||
RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"),
|
||||
RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"),
|
||||
RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"),
|
||||
RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"),
|
||||
RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"),
|
||||
RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"),
|
||||
RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"),
|
||||
RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"),
|
||||
RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l12"),
|
||||
RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo_lv, "vdd-l2-l8"),
|
||||
RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
|
||||
RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
|
||||
RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
|
||||
RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l9-l11"),
|
||||
RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
|
||||
RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l2-l8"),
|
||||
RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l6-l9-l11"),
|
||||
RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l10"),
|
||||
RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l6-l9-l11"),
|
||||
RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l1-l12"),
|
||||
RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"),
|
||||
RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"),
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct rpmh_vreg_init_data pm8009_vreg_data[] = {
|
||||
RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"),
|
||||
RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"),
|
||||
@@ -930,6 +984,33 @@ static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = {
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct rpmh_vreg_init_data pmx55_vreg_data[] = {
|
||||
RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"),
|
||||
RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"),
|
||||
RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"),
|
||||
RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"),
|
||||
RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"),
|
||||
RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"),
|
||||
RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"),
|
||||
RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"),
|
||||
RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"),
|
||||
RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l9"),
|
||||
RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l12"),
|
||||
RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"),
|
||||
RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"),
|
||||
RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"),
|
||||
RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"),
|
||||
RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l3-l9"),
|
||||
RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10-l11-l13"),
|
||||
RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l10-l11-l13"),
|
||||
RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l4-l12"),
|
||||
RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l10-l11-l13"),
|
||||
RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"),
|
||||
RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"),
|
||||
RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l16"),
|
||||
{},
|
||||
};
|
||||
|
||||
static int rpmh_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -984,6 +1065,14 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = {
|
||||
.compatible = "qcom,pm8150l-rpmh-regulators",
|
||||
.data = pm8150l_vreg_data,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,pm8350-rpmh-regulators",
|
||||
.data = pm8350_vreg_data,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,pm8350c-rpmh-regulators",
|
||||
.data = pm8350c_vreg_data,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,pm8998-rpmh-regulators",
|
||||
.data = pm8998_vreg_data,
|
||||
@@ -1000,6 +1089,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = {
|
||||
.compatible = "qcom,pm6150l-rpmh-regulators",
|
||||
.data = pm6150l_vreg_data,
|
||||
},
|
||||
{
|
||||
.compatible = "qcom,pmx55-rpmh-regulators",
|
||||
.data = pmx55_vreg_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
|
||||
|
||||
417
drivers/regulator/scmi-regulator.c
Normal file
417
drivers/regulator/scmi-regulator.c
Normal file
@@ -0,0 +1,417 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// System Control and Management Interface (SCMI) based regulator driver
|
||||
//
|
||||
// Copyright (C) 2020 ARM Ltd.
|
||||
//
|
||||
// Implements a regulator driver on top of the SCMI Voltage Protocol.
|
||||
//
|
||||
// The ARM SCMI Protocol aims in general to hide as much as possible all the
|
||||
// underlying operational details while providing an abstracted interface for
|
||||
// its users to operate upon: as a consequence the resulting operational
|
||||
// capabilities and configurability of this regulator device are much more
|
||||
// limited than the ones usually available on a standard physical regulator.
|
||||
//
|
||||
// The supported SCMI regulator ops are restricted to the bare minimum:
|
||||
//
|
||||
// - 'status_ops': enable/disable/is_enabled
|
||||
// - 'voltage_ops': get_voltage_sel/set_voltage_sel
|
||||
// list_voltage/map_voltage
|
||||
//
|
||||
// Each SCMI regulator instance is associated, through the means of a proper DT
|
||||
// entry description, to a specific SCMI Voltage Domain.
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/linear_range.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct scmi_regulator {
|
||||
u32 id;
|
||||
struct scmi_device *sdev;
|
||||
struct regulator_dev *rdev;
|
||||
struct device_node *of_node;
|
||||
struct regulator_desc desc;
|
||||
struct regulator_config conf;
|
||||
};
|
||||
|
||||
struct scmi_regulator_info {
|
||||
int num_doms;
|
||||
struct scmi_regulator **sregv;
|
||||
};
|
||||
|
||||
static int scmi_reg_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
|
||||
const struct scmi_handle *handle = sreg->sdev->handle;
|
||||
|
||||
return handle->voltage_ops->config_set(handle, sreg->id,
|
||||
SCMI_VOLTAGE_ARCH_STATE_ON);
|
||||
}
|
||||
|
||||
static int scmi_reg_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
|
||||
const struct scmi_handle *handle = sreg->sdev->handle;
|
||||
|
||||
return handle->voltage_ops->config_set(handle, sreg->id,
|
||||
SCMI_VOLTAGE_ARCH_STATE_OFF);
|
||||
}
|
||||
|
||||
static int scmi_reg_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
int ret;
|
||||
u32 config;
|
||||
struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
|
||||
const struct scmi_handle *handle = sreg->sdev->handle;
|
||||
|
||||
ret = handle->voltage_ops->config_get(handle, sreg->id,
|
||||
&config);
|
||||
if (ret) {
|
||||
dev_err(&sreg->sdev->dev,
|
||||
"Error %d reading regulator %s status.\n",
|
||||
ret, sreg->desc.name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return config & SCMI_VOLTAGE_ARCH_STATE_ON;
|
||||
}
|
||||
|
||||
static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
{
|
||||
int ret;
|
||||
s32 volt_uV;
|
||||
struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
|
||||
const struct scmi_handle *handle = sreg->sdev->handle;
|
||||
|
||||
ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV);
|
||||
}
|
||||
|
||||
static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
unsigned int selector)
|
||||
{
|
||||
s32 volt_uV;
|
||||
struct scmi_regulator *sreg = rdev_get_drvdata(rdev);
|
||||
const struct scmi_handle *handle = sreg->sdev->handle;
|
||||
|
||||
volt_uV = sreg->desc.ops->list_voltage(rdev, selector);
|
||||
if (volt_uV <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
return handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV);
|
||||
}
|
||||
|
||||
static const struct regulator_ops scmi_reg_fixed_ops = {
|
||||
.enable = scmi_reg_enable,
|
||||
.disable = scmi_reg_disable,
|
||||
.is_enabled = scmi_reg_is_enabled,
|
||||
};
|
||||
|
||||
static const struct regulator_ops scmi_reg_linear_ops = {
|
||||
.enable = scmi_reg_enable,
|
||||
.disable = scmi_reg_disable,
|
||||
.is_enabled = scmi_reg_is_enabled,
|
||||
.get_voltage_sel = scmi_reg_get_voltage_sel,
|
||||
.set_voltage_sel = scmi_reg_set_voltage_sel,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.map_voltage = regulator_map_voltage_linear,
|
||||
};
|
||||
|
||||
static const struct regulator_ops scmi_reg_discrete_ops = {
|
||||
.enable = scmi_reg_enable,
|
||||
.disable = scmi_reg_disable,
|
||||
.is_enabled = scmi_reg_is_enabled,
|
||||
.get_voltage_sel = scmi_reg_get_voltage_sel,
|
||||
.set_voltage_sel = scmi_reg_set_voltage_sel,
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
.map_voltage = regulator_map_voltage_iterate,
|
||||
};
|
||||
|
||||
static int
|
||||
scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg,
|
||||
const struct scmi_voltage_info *vinfo)
|
||||
{
|
||||
s32 delta_uV;
|
||||
|
||||
/*
|
||||
* Note that SCMI voltage domains describable by linear ranges
|
||||
* (segments) {low, high, step} are guaranteed to come in one single
|
||||
* triplet by the SCMI Voltage Domain protocol support itself.
|
||||
*/
|
||||
|
||||
delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] -
|
||||
vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]);
|
||||
|
||||
/* Rule out buggy negative-intervals answers from fw */
|
||||
if (delta_uV < 0) {
|
||||
dev_err(&sreg->sdev->dev,
|
||||
"Invalid volt-range %d-%duV for domain %d\n",
|
||||
vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW],
|
||||
vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH],
|
||||
sreg->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!delta_uV) {
|
||||
/* Just one fixed voltage exposed by SCMI */
|
||||
sreg->desc.fixed_uV =
|
||||
vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW];
|
||||
sreg->desc.n_voltages = 1;
|
||||
sreg->desc.ops = &scmi_reg_fixed_ops;
|
||||
} else {
|
||||
/* One simple linear mapping. */
|
||||
sreg->desc.min_uV =
|
||||
vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW];
|
||||
sreg->desc.uV_step =
|
||||
vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP];
|
||||
sreg->desc.linear_min_sel = 0;
|
||||
sreg->desc.n_voltages = delta_uV / sreg->desc.uV_step;
|
||||
sreg->desc.ops = &scmi_reg_linear_ops;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg,
|
||||
const struct scmi_voltage_info *vinfo)
|
||||
{
|
||||
/* Discrete non linear levels are mapped to volt_table */
|
||||
sreg->desc.n_voltages = vinfo->num_levels;
|
||||
|
||||
if (sreg->desc.n_voltages > 1) {
|
||||
sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv;
|
||||
sreg->desc.ops = &scmi_reg_discrete_ops;
|
||||
} else {
|
||||
sreg->desc.fixed_uV = vinfo->levels_uv[0];
|
||||
sreg->desc.ops = &scmi_reg_fixed_ops;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_regulator_common_init(struct scmi_regulator *sreg)
|
||||
{
|
||||
int ret;
|
||||
const struct scmi_handle *handle = sreg->sdev->handle;
|
||||
struct device *dev = &sreg->sdev->dev;
|
||||
const struct scmi_voltage_info *vinfo;
|
||||
|
||||
vinfo = handle->voltage_ops->info_get(handle, sreg->id);
|
||||
if (!vinfo) {
|
||||
dev_warn(dev, "Failure to get voltage domain %d\n",
|
||||
sreg->id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Regulator framework does not fully support negative voltages
|
||||
* so we discard any voltage domain reported as supporting negative
|
||||
* voltages: as a consequence each levels_uv entry is guaranteed to
|
||||
* be non-negative from here on.
|
||||
*/
|
||||
if (vinfo->negative_volts_allowed) {
|
||||
dev_warn(dev, "Negative voltages NOT supported...skip %s\n",
|
||||
sreg->of_node->full_name);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name);
|
||||
if (!sreg->desc.name)
|
||||
return -ENOMEM;
|
||||
|
||||
sreg->desc.id = sreg->id;
|
||||
sreg->desc.type = REGULATOR_VOLTAGE;
|
||||
sreg->desc.owner = THIS_MODULE;
|
||||
sreg->desc.of_match_full_name = true;
|
||||
sreg->desc.of_match = sreg->of_node->full_name;
|
||||
sreg->desc.regulators_node = "regulators";
|
||||
if (vinfo->segmented)
|
||||
ret = scmi_config_linear_regulator_mappings(sreg, vinfo);
|
||||
else
|
||||
ret = scmi_config_discrete_regulator_mappings(sreg, vinfo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Using the scmi device here to have DT searched from Voltage
|
||||
* protocol node down.
|
||||
*/
|
||||
sreg->conf.dev = dev;
|
||||
|
||||
/* Store for later retrieval via rdev_get_drvdata() */
|
||||
sreg->conf.driver_data = sreg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_scmi_regulator_of_node(struct scmi_device *sdev,
|
||||
struct device_node *np,
|
||||
struct scmi_regulator_info *rinfo)
|
||||
{
|
||||
u32 dom, ret;
|
||||
|
||||
ret = of_property_read_u32(np, "reg", &dom);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dom >= rinfo->num_doms)
|
||||
return -ENODEV;
|
||||
|
||||
if (rinfo->sregv[dom]) {
|
||||
dev_err(&sdev->dev,
|
||||
"SCMI Voltage Domain %d already in use. Skipping: %s\n",
|
||||
dom, np->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rinfo->sregv[dom] = devm_kzalloc(&sdev->dev,
|
||||
sizeof(struct scmi_regulator),
|
||||
GFP_KERNEL);
|
||||
if (!rinfo->sregv[dom])
|
||||
return -ENOMEM;
|
||||
|
||||
rinfo->sregv[dom]->id = dom;
|
||||
rinfo->sregv[dom]->sdev = sdev;
|
||||
|
||||
/* get hold of good nodes */
|
||||
of_node_get(np);
|
||||
rinfo->sregv[dom]->of_node = np;
|
||||
|
||||
dev_dbg(&sdev->dev,
|
||||
"Found SCMI Regulator entry -- OF node [%d] -> %s\n",
|
||||
dom, np->full_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scmi_regulator_probe(struct scmi_device *sdev)
|
||||
{
|
||||
int d, ret, num_doms;
|
||||
struct device_node *np, *child;
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
struct scmi_regulator_info *rinfo;
|
||||
|
||||
if (!handle || !handle->voltage_ops)
|
||||
return -ENODEV;
|
||||
|
||||
num_doms = handle->voltage_ops->num_domains_get(handle);
|
||||
if (num_doms <= 0) {
|
||||
if (!num_doms) {
|
||||
dev_err(&sdev->dev,
|
||||
"number of voltage domains invalid\n");
|
||||
num_doms = -EINVAL;
|
||||
} else {
|
||||
dev_err(&sdev->dev,
|
||||
"failed to get voltage domains - err:%d\n",
|
||||
num_doms);
|
||||
}
|
||||
|
||||
return num_doms;
|
||||
}
|
||||
|
||||
rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL);
|
||||
if (!rinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Allocate pointers array for all possible domains */
|
||||
rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms,
|
||||
sizeof(void *), GFP_KERNEL);
|
||||
if (!rinfo->sregv)
|
||||
return -ENOMEM;
|
||||
|
||||
rinfo->num_doms = num_doms;
|
||||
|
||||
/*
|
||||
* Start collecting into rinfo->sregv possibly good SCMI Regulators as
|
||||
* described by a well-formed DT entry and associated with an existing
|
||||
* plausible SCMI Voltage Domain number, all belonging to this SCMI
|
||||
* platform instance node (handle->dev->of_node).
|
||||
*/
|
||||
np = of_find_node_by_name(handle->dev->of_node, "regulators");
|
||||
for_each_child_of_node(np, child) {
|
||||
ret = process_scmi_regulator_of_node(sdev, child, rinfo);
|
||||
/* abort on any mem issue */
|
||||
if (ret == -ENOMEM)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a regulator for each valid regulator-DT-entry that we
|
||||
* can successfully reach via SCMI and has a valid associated voltage
|
||||
* domain.
|
||||
*/
|
||||
for (d = 0; d < num_doms; d++) {
|
||||
struct scmi_regulator *sreg = rinfo->sregv[d];
|
||||
|
||||
/* Skip empty slots */
|
||||
if (!sreg)
|
||||
continue;
|
||||
|
||||
ret = scmi_regulator_common_init(sreg);
|
||||
/* Skip invalid voltage domains */
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc,
|
||||
&sreg->conf);
|
||||
if (IS_ERR(sreg->rdev)) {
|
||||
sreg->rdev = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
dev_info(&sdev->dev,
|
||||
"Regulator %s registered for domain [%d]\n",
|
||||
sreg->desc.name, sreg->id);
|
||||
}
|
||||
|
||||
dev_set_drvdata(&sdev->dev, rinfo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scmi_regulator_remove(struct scmi_device *sdev)
|
||||
{
|
||||
int d;
|
||||
struct scmi_regulator_info *rinfo;
|
||||
|
||||
rinfo = dev_get_drvdata(&sdev->dev);
|
||||
if (!rinfo)
|
||||
return;
|
||||
|
||||
for (d = 0; d < rinfo->num_doms; d++) {
|
||||
if (!rinfo->sregv[d])
|
||||
continue;
|
||||
of_node_put(rinfo->sregv[d]->of_node);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct scmi_device_id scmi_regulator_id_table[] = {
|
||||
{ SCMI_PROTOCOL_VOLTAGE, "regulator" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table);
|
||||
|
||||
static struct scmi_driver scmi_drv = {
|
||||
.name = "scmi-regulator",
|
||||
.probe = scmi_regulator_probe,
|
||||
.remove = scmi_regulator_remove,
|
||||
.id_table = scmi_regulator_id_table,
|
||||
};
|
||||
|
||||
module_scmi_driver(scmi_drv);
|
||||
|
||||
MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
|
||||
MODULE_DESCRIPTION("ARM SCMI regulator driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -255,6 +255,8 @@ config SPI_DW_MMIO
|
||||
config SPI_DW_BT1
|
||||
tristate "Baikal-T1 SPI driver for DW SPI core"
|
||||
depends on MIPS_BAIKAL_T1 || COMPILE_TEST
|
||||
select MULTIPLEXER
|
||||
select MUX_MMIO
|
||||
help
|
||||
Baikal-T1 SoC is equipped with three DW APB SSI-based MMIO SPI
|
||||
controllers. Two of them are pretty much normal: with IRQ, DMA,
|
||||
@@ -268,8 +270,6 @@ config SPI_DW_BT1
|
||||
config SPI_DW_BT1_DIRMAP
|
||||
bool "Directly mapped Baikal-T1 Boot SPI flash support"
|
||||
depends on SPI_DW_BT1
|
||||
select MULTIPLEXER
|
||||
select MUX_MMIO
|
||||
help
|
||||
Directly mapped SPI flash memory is an interface specific to the
|
||||
Baikal-T1 System Boot Controller. It is a 16MB MMIO region, which
|
||||
|
||||
@@ -365,10 +365,14 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
|
||||
if (dummy_cycles)
|
||||
ifr |= QSPI_IFR_NBDUM(dummy_cycles);
|
||||
|
||||
/* Set data enable */
|
||||
if (op->data.nbytes)
|
||||
/* Set data enable and data transfer type. */
|
||||
if (op->data.nbytes) {
|
||||
ifr |= QSPI_IFR_DATAEN;
|
||||
|
||||
if (op->addr.nbytes)
|
||||
ifr |= QSPI_IFR_TFRTYP_MEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the QSPI controller is set in regular SPI mode, set it in
|
||||
* Serial Memory Mode (SMM).
|
||||
@@ -381,27 +385,24 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
|
||||
/* Clear pending interrupts */
|
||||
(void)atmel_qspi_read(aq, QSPI_SR);
|
||||
|
||||
if (aq->caps->has_ricr) {
|
||||
if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN)
|
||||
ifr |= QSPI_IFR_APBTFRTYP_READ;
|
||||
|
||||
/* Set QSPI Instruction Frame registers */
|
||||
/* Set QSPI Instruction Frame registers. */
|
||||
if (op->addr.nbytes && !op->data.nbytes)
|
||||
atmel_qspi_write(iar, aq, QSPI_IAR);
|
||||
|
||||
if (aq->caps->has_ricr) {
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
atmel_qspi_write(icr, aq, QSPI_RICR);
|
||||
else
|
||||
atmel_qspi_write(icr, aq, QSPI_WICR);
|
||||
atmel_qspi_write(ifr, aq, QSPI_IFR);
|
||||
} else {
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
|
||||
ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR;
|
||||
|
||||
/* Set QSPI Instruction Frame registers */
|
||||
atmel_qspi_write(iar, aq, QSPI_IAR);
|
||||
atmel_qspi_write(icr, aq, QSPI_ICR);
|
||||
atmel_qspi_write(ifr, aq, QSPI_IFR);
|
||||
}
|
||||
|
||||
atmel_qspi_write(ifr, aq, QSPI_IFR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -535,7 +536,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
int irq, err = 0;
|
||||
|
||||
ctrl = spi_alloc_master(&pdev->dev, sizeof(*aq));
|
||||
ctrl = devm_spi_alloc_master(&pdev->dev, sizeof(*aq));
|
||||
if (!ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -557,8 +558,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
aq->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(aq->regs)) {
|
||||
dev_err(&pdev->dev, "missing registers\n");
|
||||
err = PTR_ERR(aq->regs);
|
||||
goto exit;
|
||||
return PTR_ERR(aq->regs);
|
||||
}
|
||||
|
||||
/* Map the AHB memory */
|
||||
@@ -566,8 +566,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
aq->mem = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(aq->mem)) {
|
||||
dev_err(&pdev->dev, "missing AHB memory\n");
|
||||
err = PTR_ERR(aq->mem);
|
||||
goto exit;
|
||||
return PTR_ERR(aq->mem);
|
||||
}
|
||||
|
||||
aq->mmap_size = resource_size(res);
|
||||
@@ -579,22 +578,21 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
if (IS_ERR(aq->pclk)) {
|
||||
dev_err(&pdev->dev, "missing peripheral clock\n");
|
||||
err = PTR_ERR(aq->pclk);
|
||||
goto exit;
|
||||
return PTR_ERR(aq->pclk);
|
||||
}
|
||||
|
||||
/* Enable the peripheral clock */
|
||||
err = clk_prepare_enable(aq->pclk);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to enable the peripheral clock\n");
|
||||
goto exit;
|
||||
return err;
|
||||
}
|
||||
|
||||
aq->caps = of_device_get_match_data(&pdev->dev);
|
||||
if (!aq->caps) {
|
||||
dev_err(&pdev->dev, "Could not retrieve QSPI caps\n");
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
goto disable_pclk;
|
||||
}
|
||||
|
||||
if (aq->caps->has_qspick) {
|
||||
@@ -638,8 +636,6 @@ disable_qspick:
|
||||
clk_disable_unprepare(aq->qspick);
|
||||
disable_pclk:
|
||||
clk_disable_unprepare(aq->pclk);
|
||||
exit:
|
||||
spi_controller_put(ctrl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,6 @@ static int amd_spi_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_master *master;
|
||||
struct amd_spi *amd_spi;
|
||||
struct resource *res;
|
||||
int err = 0;
|
||||
|
||||
/* Allocate storage for spi_master and driver private data */
|
||||
@@ -261,9 +260,7 @@ static int amd_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
amd_spi = spi_master_get_devdata(master);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
amd_spi->io_remap_addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(amd_spi->io_remap_addr)) {
|
||||
err = PTR_ERR(amd_spi->io_remap_addr);
|
||||
dev_err(dev, "error %d ioremap of SPI registers failed\n", err);
|
||||
|
||||
@@ -176,10 +176,11 @@ static int ar934x_spi_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp));
|
||||
ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*sp));
|
||||
if (!ctlr) {
|
||||
dev_info(&pdev->dev, "failed to allocate spi controller\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
/* disable flash mapping and expose spi controller registers */
|
||||
@@ -202,7 +203,13 @@ static int ar934x_spi_probe(struct platform_device *pdev)
|
||||
sp->clk_freq = clk_get_rate(clk);
|
||||
sp->ctlr = ctlr;
|
||||
|
||||
return devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
ret = spi_register_controller(ctlr);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ar934x_spi_remove(struct platform_device *pdev)
|
||||
@@ -213,6 +220,7 @@ static int ar934x_spi_remove(struct platform_device *pdev)
|
||||
ctlr = dev_get_drvdata(&pdev->dev);
|
||||
sp = spi_controller_get_devdata(ctlr);
|
||||
|
||||
spi_unregister_controller(ctlr);
|
||||
clk_disable_unprepare(sp->clk);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -512,8 +512,8 @@ static int atmel_spi_configure_dma(struct spi_master *master,
|
||||
|
||||
master->dma_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(master->dma_tx)) {
|
||||
err = dev_err_probe(dev, PTR_ERR(master->dma_tx),
|
||||
"No TX DMA channel, DMA is disabled\n");
|
||||
err = PTR_ERR(master->dma_tx);
|
||||
dev_dbg(dev, "No TX DMA channel, DMA is disabled\n");
|
||||
goto error_clear;
|
||||
}
|
||||
|
||||
@@ -524,7 +524,7 @@ static int atmel_spi_configure_dma(struct spi_master *master,
|
||||
* No reason to check EPROBE_DEFER here since we have already
|
||||
* requested tx channel.
|
||||
*/
|
||||
dev_err(dev, "No RX DMA channel, DMA is disabled\n");
|
||||
dev_dbg(dev, "No RX DMA channel, DMA is disabled\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
@@ -494,8 +494,10 @@ static int bcm63xx_hsspi_resume(struct device *dev)
|
||||
|
||||
if (bs->pll_clk) {
|
||||
ret = clk_prepare_enable(bs->pll_clk);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
clk_disable_unprepare(bs->clk);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
spi_master_resume(master);
|
||||
|
||||
@@ -1040,13 +1040,13 @@ static int davinci_spi_remove(struct platform_device *pdev)
|
||||
spi_bitbang_stop(&dspi->bitbang);
|
||||
|
||||
clk_disable_unprepare(dspi->clk);
|
||||
spi_master_put(master);
|
||||
|
||||
if (dspi->dma_rx) {
|
||||
dma_release_channel(dspi->dma_rx);
|
||||
dma_release_channel(dspi->dma_tx);
|
||||
}
|
||||
|
||||
spi_master_put(master);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ static int dw_spi_bt1_sys_init(struct platform_device *pdev,
|
||||
if (mem) {
|
||||
dwsbt1->map = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (!IS_ERR(dwsbt1->map)) {
|
||||
dwsbt1->map_len = (mem->end - mem->start + 1);
|
||||
dwsbt1->map_len = resource_size(mem);
|
||||
dws->mem_ops.dirmap_create = dw_spi_bt1_dirmap_create;
|
||||
dws->mem_ops.dirmap_read = dw_spi_bt1_dirmap_read;
|
||||
} else {
|
||||
@@ -280,8 +280,10 @@ static int dw_spi_bt1_probe(struct platform_device *pdev)
|
||||
dws->bus_num = pdev->id;
|
||||
dws->reg_io_width = 4;
|
||||
dws->max_freq = clk_get_rate(dwsbt1->clk);
|
||||
if (!dws->max_freq)
|
||||
if (!dws->max_freq) {
|
||||
ret = -EINVAL;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
init_func = device_get_match_data(&pdev->dev);
|
||||
ret = init_func(pdev, dwsbt1);
|
||||
|
||||
@@ -137,14 +137,16 @@ static inline u32 rx_max(struct dw_spi *dws)
|
||||
static void dw_writer(struct dw_spi *dws)
|
||||
{
|
||||
u32 max = tx_max(dws);
|
||||
u16 txw = 0;
|
||||
u32 txw = 0;
|
||||
|
||||
while (max--) {
|
||||
if (dws->tx) {
|
||||
if (dws->n_bytes == 1)
|
||||
txw = *(u8 *)(dws->tx);
|
||||
else
|
||||
else if (dws->n_bytes == 2)
|
||||
txw = *(u16 *)(dws->tx);
|
||||
else
|
||||
txw = *(u32 *)(dws->tx);
|
||||
|
||||
dws->tx += dws->n_bytes;
|
||||
}
|
||||
@@ -156,15 +158,17 @@ static void dw_writer(struct dw_spi *dws)
|
||||
static void dw_reader(struct dw_spi *dws)
|
||||
{
|
||||
u32 max = rx_max(dws);
|
||||
u16 rxw;
|
||||
u32 rxw;
|
||||
|
||||
while (max--) {
|
||||
rxw = dw_read_io_reg(dws, DW_SPI_DR);
|
||||
if (dws->rx) {
|
||||
if (dws->n_bytes == 1)
|
||||
*(u8 *)(dws->rx) = rxw;
|
||||
else
|
||||
else if (dws->n_bytes == 2)
|
||||
*(u16 *)(dws->rx) = rxw;
|
||||
else
|
||||
*(u32 *)(dws->rx) = rxw;
|
||||
|
||||
dws->rx += dws->n_bytes;
|
||||
}
|
||||
@@ -311,8 +315,8 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
|
||||
u32 speed_hz;
|
||||
u16 clk_div;
|
||||
|
||||
/* CTRLR0[ 4/3: 0] Data Frame Size */
|
||||
cr0 |= (cfg->dfs - 1);
|
||||
/* CTRLR0[ 4/3: 0] or CTRLR0[ 20: 16] Data Frame Size */
|
||||
cr0 |= (cfg->dfs - 1) << dws->dfs_offset;
|
||||
|
||||
if (!(dws->caps & DW_SPI_CAP_DWC_SSI))
|
||||
/* CTRLR0[ 9:8] Transfer Mode */
|
||||
@@ -828,6 +832,29 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws)
|
||||
dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect CTRLR0.DFS field size and offset by testing the lowest bits
|
||||
* writability. Note DWC SSI controller also has the extended DFS, but
|
||||
* with zero offset.
|
||||
*/
|
||||
if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) {
|
||||
u32 cr0, tmp = dw_readl(dws, DW_SPI_CTRLR0);
|
||||
|
||||
spi_enable_chip(dws, 0);
|
||||
dw_writel(dws, DW_SPI_CTRLR0, 0xffffffff);
|
||||
cr0 = dw_readl(dws, DW_SPI_CTRLR0);
|
||||
dw_writel(dws, DW_SPI_CTRLR0, tmp);
|
||||
spi_enable_chip(dws, 1);
|
||||
|
||||
if (!(cr0 & SPI_DFS_MASK)) {
|
||||
dws->caps |= DW_SPI_CAP_DFS32;
|
||||
dws->dfs_offset = SPI_DFS32_OFFSET;
|
||||
dev_dbg(dev, "Detected 32-bits max data frame size\n");
|
||||
}
|
||||
} else {
|
||||
dws->caps |= DW_SPI_CAP_DFS32;
|
||||
}
|
||||
|
||||
/* enable HW fixup for explicit CS deselect for Amazon's alpine chip */
|
||||
if (dws->caps & DW_SPI_CAP_CS_OVERRIDE)
|
||||
dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF);
|
||||
@@ -864,7 +891,10 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||
|
||||
master->use_gpio_descriptors = true;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
|
||||
if (dws->caps & DW_SPI_CAP_DFS32)
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
else
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
|
||||
master->bus_num = dws->bus_num;
|
||||
master->num_chipselect = dws->num_cs;
|
||||
master->setup = dw_spi_setup;
|
||||
|
||||
@@ -222,6 +222,21 @@ static int dw_spi_keembay_init(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_canaan_k210_init(struct platform_device *pdev,
|
||||
struct dw_spi_mmio *dwsmmio)
|
||||
{
|
||||
/*
|
||||
* The Canaan Kendryte K210 SoC DW apb_ssi v4 spi controller is
|
||||
* documented to have a 32 word deep TX and RX FIFO, which
|
||||
* spi_hw_init() detects. However, when the RX FIFO is filled up to
|
||||
* 32 entries (RXFLR = 32), an RX FIFO overrun error occurs. Avoid this
|
||||
* problem by force setting fifo_len to 31.
|
||||
*/
|
||||
dwsmmio->dws.fifo_len = 31;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_mmio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int (*init_func)(struct platform_device *pdev,
|
||||
@@ -335,6 +350,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
|
||||
{ .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_dwc_ssi_init},
|
||||
{ .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init},
|
||||
{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
|
||||
{ .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
|
||||
{ /* end of table */}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
/* Register offsets */
|
||||
#define DW_SPI_CTRLR0 0x00
|
||||
@@ -41,6 +42,8 @@
|
||||
|
||||
/* Bit fields in CTRLR0 */
|
||||
#define SPI_DFS_OFFSET 0
|
||||
#define SPI_DFS_MASK GENMASK(3, 0)
|
||||
#define SPI_DFS32_OFFSET 16
|
||||
|
||||
#define SPI_FRF_OFFSET 4
|
||||
#define SPI_FRF_SPI 0x0
|
||||
@@ -121,6 +124,7 @@ enum dw_ssi_type {
|
||||
#define DW_SPI_CAP_CS_OVERRIDE BIT(0)
|
||||
#define DW_SPI_CAP_KEEMBAY_MST BIT(1)
|
||||
#define DW_SPI_CAP_DWC_SSI BIT(2)
|
||||
#define DW_SPI_CAP_DFS32 BIT(3)
|
||||
|
||||
/* Slave spi_transfer/spi_mem_op related */
|
||||
struct dw_spi_cfg {
|
||||
@@ -148,6 +152,7 @@ struct dw_spi {
|
||||
unsigned long paddr;
|
||||
int irq;
|
||||
u32 fifo_len; /* depth of the FIFO buffer */
|
||||
unsigned int dfs_offset; /* CTRLR0 DFS field offset */
|
||||
u32 max_mem_freq; /* max mem-ops bus freq */
|
||||
u32 max_freq; /* max bus freq supported */
|
||||
|
||||
|
||||
@@ -1165,7 +1165,7 @@ static int dspi_init(struct fsl_dspi *dspi)
|
||||
unsigned int mcr;
|
||||
|
||||
/* Set idle states for all chip select signals to high */
|
||||
mcr = SPI_MCR_PCSIS(GENMASK(dspi->ctlr->num_chipselect - 1, 0));
|
||||
mcr = SPI_MCR_PCSIS(GENMASK(dspi->ctlr->max_native_cs - 1, 0));
|
||||
|
||||
if (dspi->devtype_data->trans_mode == DSPI_XSPI_MODE)
|
||||
mcr |= SPI_MCR_XSPI;
|
||||
@@ -1250,7 +1250,7 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
ctlr->num_chipselect = pdata->cs_num;
|
||||
ctlr->num_chipselect = ctlr->max_native_cs = pdata->cs_num;
|
||||
ctlr->bus_num = pdata->bus_num;
|
||||
|
||||
/* Only Coldfire uses platform data */
|
||||
@@ -1263,7 +1263,7 @@ static int dspi_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "can't get spi-num-chipselects\n");
|
||||
goto out_ctlr_put;
|
||||
}
|
||||
ctlr->num_chipselect = cs_num;
|
||||
ctlr->num_chipselect = ctlr->max_native_cs = cs_num;
|
||||
|
||||
of_property_read_u32(np, "bus-num", &bus_num);
|
||||
ctlr->bus_num = bus_num;
|
||||
|
||||
@@ -716,10 +716,11 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
|
||||
type = fsl_spi_get_type(&ofdev->dev);
|
||||
if (type == TYPE_FSL) {
|
||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||
bool spisel_boot = false;
|
||||
#if IS_ENABLED(CONFIG_FSL_SOC)
|
||||
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
|
||||
bool spisel_boot = of_property_read_bool(np, "fsl,spisel_boot");
|
||||
|
||||
spisel_boot = of_property_read_bool(np, "fsl,spisel_boot");
|
||||
if (spisel_boot) {
|
||||
pinfo->immr_spi_cs = ioremap(get_immrbase() + IMMR_SPI_CS_OFFSET, 4);
|
||||
if (!pinfo->immr_spi_cs)
|
||||
@@ -734,10 +735,14 @@ static int of_fsl_spi_probe(struct platform_device *ofdev)
|
||||
* supported on the GRLIB variant.
|
||||
*/
|
||||
ret = gpiod_count(dev, "cs");
|
||||
if (ret <= 0)
|
||||
if (ret < 0)
|
||||
ret = 0;
|
||||
if (ret == 0 && !spisel_boot) {
|
||||
pdata->max_chipselect = 1;
|
||||
else
|
||||
} else {
|
||||
pdata->max_chipselect = ret + spisel_boot;
|
||||
pdata->cs_control = fsl_spi_cs_control;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(np, 0, &mem);
|
||||
|
||||
@@ -603,7 +603,7 @@ static int spi_geni_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
spi = spi_alloc_master(dev, sizeof(*mas));
|
||||
spi = devm_spi_alloc_master(dev, sizeof(*mas));
|
||||
if (!spi)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -636,6 +636,7 @@ static int spi_geni_probe(struct platform_device *pdev)
|
||||
spi->auto_runtime_pm = true;
|
||||
spi->handle_err = handle_fifo_timeout;
|
||||
spi->set_cs = spi_geni_set_cs;
|
||||
spi->use_gpio_descriptors = true;
|
||||
|
||||
init_completion(&mas->cs_done);
|
||||
init_completion(&mas->cancel_done);
|
||||
@@ -673,7 +674,6 @@ spi_geni_probe_free_irq:
|
||||
free_irq(mas->irq, spi);
|
||||
spi_geni_probe_runtime_disable:
|
||||
pm_runtime_disable(dev);
|
||||
spi_master_put(spi);
|
||||
dev_pm_opp_of_remove_table(&pdev->dev);
|
||||
put_clkname:
|
||||
dev_pm_opp_put_clkname(mas->se.opp_table);
|
||||
|
||||
@@ -350,11 +350,6 @@ static int spi_gpio_probe_pdata(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_gpio_put(void *data)
|
||||
{
|
||||
spi_master_put(data);
|
||||
}
|
||||
|
||||
static int spi_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int status;
|
||||
@@ -363,16 +358,10 @@ static int spi_gpio_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_bitbang *bb;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(*spi_gpio));
|
||||
master = devm_spi_alloc_master(dev, sizeof(*spi_gpio));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
status = devm_add_action_or_reset(&pdev->dev, spi_gpio_put, master);
|
||||
if (status) {
|
||||
spi_master_put(master);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
status = spi_gpio_probe_dt(pdev, master);
|
||||
else
|
||||
@@ -432,7 +421,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return devm_spi_register_master(&pdev->dev, spi_master_get(master));
|
||||
return devm_spi_register_master(&pdev->dev, master);
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
|
||||
@@ -731,8 +731,10 @@ static int img_spfi_resume(struct device *dev)
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return ret;
|
||||
}
|
||||
spfi_reset(spfi);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
|
||||
@@ -1019,33 +1019,6 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = {
|
||||
.devtype = IMX53_ECSPI,
|
||||
};
|
||||
|
||||
static const struct platform_device_id spi_imx_devtype[] = {
|
||||
{
|
||||
.name = "imx1-cspi",
|
||||
.driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data,
|
||||
}, {
|
||||
.name = "imx21-cspi",
|
||||
.driver_data = (kernel_ulong_t) &imx21_cspi_devtype_data,
|
||||
}, {
|
||||
.name = "imx27-cspi",
|
||||
.driver_data = (kernel_ulong_t) &imx27_cspi_devtype_data,
|
||||
}, {
|
||||
.name = "imx31-cspi",
|
||||
.driver_data = (kernel_ulong_t) &imx31_cspi_devtype_data,
|
||||
}, {
|
||||
.name = "imx35-cspi",
|
||||
.driver_data = (kernel_ulong_t) &imx35_cspi_devtype_data,
|
||||
}, {
|
||||
.name = "imx51-ecspi",
|
||||
.driver_data = (kernel_ulong_t) &imx51_ecspi_devtype_data,
|
||||
}, {
|
||||
.name = "imx53-ecspi",
|
||||
.driver_data = (kernel_ulong_t) &imx53_ecspi_devtype_data,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id spi_imx_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, },
|
||||
{ .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, },
|
||||
@@ -1538,6 +1511,7 @@ spi_imx_prepare_message(struct spi_master *master, struct spi_message *msg)
|
||||
|
||||
ret = pm_runtime_get_sync(spi_imx->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(spi_imx->dev);
|
||||
dev_err(spi_imx->dev, "failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
@@ -1580,8 +1554,7 @@ static int spi_imx_probe(struct platform_device *pdev)
|
||||
struct spi_imx_data *spi_imx;
|
||||
struct resource *res;
|
||||
int ret, irq, spi_drctl;
|
||||
const struct spi_imx_devtype_data *devtype_data = of_id ? of_id->data :
|
||||
(struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
|
||||
const struct spi_imx_devtype_data *devtype_data = of_id->data;
|
||||
bool slave_mode;
|
||||
u32 val;
|
||||
|
||||
@@ -1748,6 +1721,7 @@ static int spi_imx_remove(struct platform_device *pdev)
|
||||
|
||||
ret = pm_runtime_get_sync(spi_imx->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(spi_imx->dev);
|
||||
dev_err(spi_imx->dev, "failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
@@ -1822,7 +1796,6 @@ static struct platform_driver spi_imx_driver = {
|
||||
.of_match_table = spi_imx_dt_ids,
|
||||
.pm = &imx_spi_pm,
|
||||
},
|
||||
.id_table = spi_imx_devtype,
|
||||
.probe = spi_imx_probe,
|
||||
.remove = spi_imx_remove,
|
||||
};
|
||||
|
||||
@@ -243,6 +243,7 @@ static int spi_mem_access_start(struct spi_mem *mem)
|
||||
|
||||
ret = pm_runtime_get_sync(ctlr->dev.parent);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(ctlr->dev.parent);
|
||||
dev_err(&ctlr->dev, "Failed to power device: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
@@ -743,7 +744,7 @@ static int spi_mem_probe(struct spi_device *spi)
|
||||
mem->name = dev_name(&spi->dev);
|
||||
|
||||
if (IS_ERR_OR_NULL(mem->name))
|
||||
return PTR_ERR(mem->name);
|
||||
return PTR_ERR_OR_ZERO(mem->name);
|
||||
|
||||
spi_set_drvdata(spi, mem);
|
||||
|
||||
|
||||
@@ -350,9 +350,10 @@ static int mt7621_spi_probe(struct platform_device *pdev)
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*rs));
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(*rs));
|
||||
if (!master) {
|
||||
dev_info(&pdev->dev, "master allocation failed\n");
|
||||
clk_disable_unprepare(clk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -377,10 +378,15 @@ static int mt7621_spi_probe(struct platform_device *pdev)
|
||||
ret = device_reset(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "SPI reset failed!\n");
|
||||
clk_disable_unprepare(clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_spi_register_controller(&pdev->dev, master);
|
||||
ret = spi_register_controller(master);
|
||||
if (ret)
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt7621_spi_remove(struct platform_device *pdev)
|
||||
@@ -391,6 +397,7 @@ static int mt7621_spi_remove(struct platform_device *pdev)
|
||||
master = dev_get_drvdata(&pdev->dev);
|
||||
rs = spi_controller_get_devdata(master);
|
||||
|
||||
spi_unregister_controller(master);
|
||||
clk_disable_unprepare(rs->clk);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -103,6 +103,7 @@ struct mtk_nor {
|
||||
dma_addr_t buffer_dma;
|
||||
struct clk *spi_clk;
|
||||
struct clk *ctlr_clk;
|
||||
struct clk *axi_clk;
|
||||
unsigned int spi_freq;
|
||||
bool wbuf_en;
|
||||
bool has_irq;
|
||||
@@ -672,6 +673,7 @@ static void mtk_nor_disable_clk(struct mtk_nor *sp)
|
||||
{
|
||||
clk_disable_unprepare(sp->spi_clk);
|
||||
clk_disable_unprepare(sp->ctlr_clk);
|
||||
clk_disable_unprepare(sp->axi_clk);
|
||||
}
|
||||
|
||||
static int mtk_nor_enable_clk(struct mtk_nor *sp)
|
||||
@@ -688,6 +690,13 @@ static int mtk_nor_enable_clk(struct mtk_nor *sp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sp->axi_clk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(sp->spi_clk);
|
||||
clk_disable_unprepare(sp->ctlr_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -746,7 +755,7 @@ static int mtk_nor_probe(struct platform_device *pdev)
|
||||
struct spi_controller *ctlr;
|
||||
struct mtk_nor *sp;
|
||||
void __iomem *base;
|
||||
struct clk *spi_clk, *ctlr_clk;
|
||||
struct clk *spi_clk, *ctlr_clk, *axi_clk;
|
||||
int ret, irq;
|
||||
unsigned long dma_bits;
|
||||
|
||||
@@ -762,13 +771,17 @@ static int mtk_nor_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(ctlr_clk))
|
||||
return PTR_ERR(ctlr_clk);
|
||||
|
||||
axi_clk = devm_clk_get_optional(&pdev->dev, "axi");
|
||||
if (IS_ERR(axi_clk))
|
||||
return PTR_ERR(axi_clk);
|
||||
|
||||
dma_bits = (unsigned long)of_device_get_match_data(&pdev->dev);
|
||||
if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(dma_bits))) {
|
||||
dev_err(&pdev->dev, "failed to set dma mask(%lu)\n", dma_bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp));
|
||||
ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*sp));
|
||||
if (!ctlr) {
|
||||
dev_err(&pdev->dev, "failed to allocate spi controller\n");
|
||||
return -ENOMEM;
|
||||
@@ -794,6 +807,7 @@ static int mtk_nor_probe(struct platform_device *pdev)
|
||||
sp->dev = &pdev->dev;
|
||||
sp->spi_clk = spi_clk;
|
||||
sp->ctlr_clk = ctlr_clk;
|
||||
sp->axi_clk = axi_clk;
|
||||
sp->high_dma = (dma_bits > 32);
|
||||
sp->buffer = dmam_alloc_coherent(&pdev->dev,
|
||||
MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN,
|
||||
|
||||
@@ -529,7 +529,7 @@ static int mxic_spi_probe(struct platform_device *pdev)
|
||||
struct mxic_spi *mxic;
|
||||
int ret;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct mxic_spi));
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(struct mxic_spi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -574,15 +574,9 @@ static int mxic_spi_probe(struct platform_device *pdev)
|
||||
ret = spi_register_master(master);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "spi_register_master failed\n");
|
||||
goto err_put_master;
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_master:
|
||||
spi_master_put(master);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -607,6 +607,7 @@ static int mxs_spi_probe(struct platform_device *pdev)
|
||||
|
||||
ret = pm_runtime_get_sync(ssp->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(ssp->dev);
|
||||
dev_err(ssp->dev, "runtime_get_sync failed\n");
|
||||
goto out_pm_runtime_disable;
|
||||
}
|
||||
|
||||
@@ -677,7 +677,7 @@ static int npcm_fiu_probe(struct platform_device *pdev)
|
||||
struct npcm_fiu_spi *fiu;
|
||||
void __iomem *regbase;
|
||||
struct resource *res;
|
||||
int id;
|
||||
int id, ret;
|
||||
|
||||
ctrl = devm_spi_alloc_master(dev, sizeof(*fiu));
|
||||
if (!ctrl)
|
||||
@@ -735,7 +735,11 @@ static int npcm_fiu_probe(struct platform_device *pdev)
|
||||
ctrl->num_chipselect = fiu->info->max_cs;
|
||||
ctrl->dev.of_node = dev->of_node;
|
||||
|
||||
return devm_spi_register_master(dev, ctrl);
|
||||
ret = devm_spi_register_master(dev, ctrl);
|
||||
if (ret)
|
||||
clk_disable_unprepare(fiu->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int npcm_fiu_remove(struct platform_device *pdev)
|
||||
|
||||
@@ -839,6 +839,7 @@ static int pic32_spi_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_bailout:
|
||||
pic32_spi_dma_unprep(pic32s);
|
||||
clk_disable_unprepare(pic32s->clk);
|
||||
err_master:
|
||||
spi_master_put(master);
|
||||
|
||||
@@ -1496,6 +1496,11 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x5ac6), LPSS_BXT_SSP },
|
||||
/* ADL-S */
|
||||
{ PCI_VDEVICE(INTEL, 0x7aaa), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x7aab), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x7af9), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x7afb), LPSS_CNL_SSP },
|
||||
/* CNL-LP */
|
||||
{ PCI_VDEVICE(INTEL, 0x9daa), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x9dab), LPSS_CNL_SSP },
|
||||
@@ -1686,9 +1691,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (platform_info->is_slave)
|
||||
controller = spi_alloc_slave(dev, sizeof(struct driver_data));
|
||||
controller = devm_spi_alloc_slave(dev, sizeof(*drv_data));
|
||||
else
|
||||
controller = spi_alloc_master(dev, sizeof(struct driver_data));
|
||||
controller = devm_spi_alloc_master(dev, sizeof(*drv_data));
|
||||
|
||||
if (!controller) {
|
||||
dev_err(&pdev->dev, "cannot alloc spi_controller\n");
|
||||
@@ -1911,7 +1916,6 @@ out_error_dma_irq_alloc:
|
||||
free_irq(ssp->irq, drv_data);
|
||||
|
||||
out_error_controller_alloc:
|
||||
spi_controller_put(controller);
|
||||
pxa_ssp_free(ssp);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
dev = &pdev->dev;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(*ctrl));
|
||||
master = devm_spi_alloc_master(dev, sizeof(*ctrl));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -473,54 +473,49 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&ctrl->lock);
|
||||
ctrl->dev = dev;
|
||||
ctrl->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ctrl->base)) {
|
||||
ret = PTR_ERR(ctrl->base);
|
||||
goto exit_probe_master_put;
|
||||
}
|
||||
if (IS_ERR(ctrl->base))
|
||||
return PTR_ERR(ctrl->base);
|
||||
|
||||
ctrl->clks = devm_kcalloc(dev, QSPI_NUM_CLKS,
|
||||
sizeof(*ctrl->clks), GFP_KERNEL);
|
||||
if (!ctrl->clks) {
|
||||
ret = -ENOMEM;
|
||||
goto exit_probe_master_put;
|
||||
}
|
||||
if (!ctrl->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
ctrl->clks[QSPI_CLK_CORE].id = "core";
|
||||
ctrl->clks[QSPI_CLK_IFACE].id = "iface";
|
||||
ret = devm_clk_bulk_get(dev, QSPI_NUM_CLKS, ctrl->clks);
|
||||
if (ret)
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
|
||||
ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config");
|
||||
if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi),
|
||||
"Failed to get cpu path\n");
|
||||
goto exit_probe_master_put;
|
||||
}
|
||||
if (IS_ERR(ctrl->icc_path_cpu_to_qspi))
|
||||
return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi),
|
||||
"Failed to get cpu path\n");
|
||||
|
||||
/* Set BW vote for register access */
|
||||
ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000),
|
||||
Bps_to_icc(1000));
|
||||
if (ret) {
|
||||
dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n",
|
||||
__func__, ret);
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = icc_disable(ctrl->icc_path_cpu_to_qspi);
|
||||
if (ret) {
|
||||
dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
|
||||
__func__, ret);
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
ret = devm_request_irq(dev, ret, qcom_qspi_irq,
|
||||
IRQF_TRIGGER_HIGH, dev_name(dev), ctrl);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request irq %d\n", ret);
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
master->max_speed_hz = 300000000;
|
||||
@@ -537,10 +532,8 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
||||
master->auto_runtime_pm = true;
|
||||
|
||||
ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core");
|
||||
if (IS_ERR(ctrl->opp_table)) {
|
||||
ret = PTR_ERR(ctrl->opp_table);
|
||||
goto exit_probe_master_put;
|
||||
}
|
||||
if (IS_ERR(ctrl->opp_table))
|
||||
return PTR_ERR(ctrl->opp_table);
|
||||
/* OPP table is optional */
|
||||
ret = dev_pm_opp_of_add_table(&pdev->dev);
|
||||
if (ret && ret != -ENODEV) {
|
||||
@@ -562,9 +555,6 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
||||
exit_probe_put_clkname:
|
||||
dev_pm_opp_put_clkname(ctrl->opp_table);
|
||||
|
||||
exit_probe_master_put:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(spi_base))
|
||||
return PTR_ERR(spi_base);
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*rbspi));
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(*rbspi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -160,6 +160,8 @@
|
||||
#define ROCKCHIP_SPI_VER2_TYPE1 0x05EC0002
|
||||
#define ROCKCHIP_SPI_VER2_TYPE2 0x00110002
|
||||
|
||||
#define ROCKCHIP_AUTOSUSPEND_TIMEOUT 2000
|
||||
|
||||
struct rockchip_spi {
|
||||
struct device *dev;
|
||||
|
||||
@@ -715,6 +717,8 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
||||
goto err_disable_spiclk;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, ROCKCHIP_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ static int rpcif_spi_probe(struct platform_device *pdev)
|
||||
struct rpcif *rpc;
|
||||
int error;
|
||||
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(*rpc));
|
||||
ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*rpc));
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -159,13 +159,8 @@ static int rpcif_spi_probe(struct platform_device *pdev)
|
||||
error = spi_register_controller(ctlr);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "spi_register_controller failed\n");
|
||||
goto err_put_ctlr;
|
||||
rpcif_disable_rpm(rpc);
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_put_ctlr:
|
||||
rpcif_disable_rpm(rpc);
|
||||
spi_controller_put(ctlr);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -238,13 +238,12 @@ static int sc18is602_probe(struct i2c_client *client,
|
||||
struct sc18is602_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct sc18is602 *hw;
|
||||
struct spi_master *master;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
|
||||
return -EINVAL;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(struct sc18is602));
|
||||
master = devm_spi_alloc_master(dev, sizeof(struct sc18is602));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -298,15 +297,7 @@ static int sc18is602_probe(struct i2c_client *client,
|
||||
master->min_speed_hz = hw->freq / 128;
|
||||
master->max_speed_hz = hw->freq / 4;
|
||||
|
||||
error = devm_spi_register_master(dev, master);
|
||||
if (error)
|
||||
goto error_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_reg:
|
||||
spi_master_put(master);
|
||||
return error;
|
||||
return devm_spi_register_master(dev, master);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sc18is602_id[] = {
|
||||
|
||||
@@ -440,7 +440,7 @@ static int spi_sh_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data));
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data));
|
||||
if (master == NULL) {
|
||||
dev_err(&pdev->dev, "spi_alloc_master error.\n");
|
||||
return -ENOMEM;
|
||||
@@ -458,16 +458,14 @@ static int spi_sh_probe(struct platform_device *pdev)
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "No support width\n");
|
||||
ret = -ENODEV;
|
||||
goto error1;
|
||||
return -ENODEV;
|
||||
}
|
||||
ss->irq = irq;
|
||||
ss->master = master;
|
||||
ss->addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (ss->addr == NULL) {
|
||||
dev_err(&pdev->dev, "ioremap error.\n");
|
||||
ret = -ENOMEM;
|
||||
goto error1;
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_LIST_HEAD(&ss->queue);
|
||||
spin_lock_init(&ss->lock);
|
||||
@@ -477,7 +475,7 @@ static int spi_sh_probe(struct platform_device *pdev)
|
||||
ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "request_irq error\n");
|
||||
goto error1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
master->num_chipselect = 2;
|
||||
@@ -496,9 +494,6 @@ static int spi_sh_probe(struct platform_device *pdev)
|
||||
|
||||
error3:
|
||||
free_irq(irq, ss);
|
||||
error1:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -1010,6 +1010,7 @@ static int sprd_spi_remove(struct platform_device *pdev)
|
||||
|
||||
ret = pm_runtime_get_sync(ss->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(ss->dev);
|
||||
dev_err(ss->dev, "failed to resume SPI controller\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -375,13 +375,14 @@ static int spi_st_probe(struct platform_device *pdev)
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register master\n");
|
||||
goto clk_disable;
|
||||
goto rpm_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
clk_disable:
|
||||
rpm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable:
|
||||
clk_disable_unprepare(spi_st->clk);
|
||||
put_master:
|
||||
spi_master_put(master);
|
||||
|
||||
@@ -434,8 +434,10 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(qspi->dev);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(qspi->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&qspi->lock);
|
||||
ret = stm32_qspi_send(mem, op);
|
||||
@@ -462,8 +464,10 @@ static int stm32_qspi_setup(struct spi_device *spi)
|
||||
return -EINVAL;
|
||||
|
||||
ret = pm_runtime_get_sync(qspi->dev);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(qspi->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
presc = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz) - 1;
|
||||
|
||||
|
||||
@@ -2062,6 +2062,7 @@ static int stm32_spi_resume(struct device *dev)
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
dev_err(dev, "Unable to power device:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,12 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define SUN6I_AUTOSUSPEND_TIMEOUT 2000
|
||||
|
||||
#define SUN6I_FIFO_DEPTH 128
|
||||
#define SUN8I_FIFO_DEPTH 64
|
||||
|
||||
@@ -52,10 +55,12 @@
|
||||
|
||||
#define SUN6I_FIFO_CTL_REG 0x18
|
||||
#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_MASK 0xff
|
||||
#define SUN6I_FIFO_CTL_RF_DRQ_EN BIT(8)
|
||||
#define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS 0
|
||||
#define SUN6I_FIFO_CTL_RF_RST BIT(15)
|
||||
#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_MASK 0xff
|
||||
#define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS 16
|
||||
#define SUN6I_FIFO_CTL_TF_DRQ_EN BIT(24)
|
||||
#define SUN6I_FIFO_CTL_TF_RST BIT(31)
|
||||
|
||||
#define SUN6I_FIFO_STA_REG 0x1c
|
||||
@@ -83,6 +88,8 @@
|
||||
struct sun6i_spi {
|
||||
struct spi_master *master;
|
||||
void __iomem *base_addr;
|
||||
dma_addr_t dma_addr_rx;
|
||||
dma_addr_t dma_addr_tx;
|
||||
struct clk *hclk;
|
||||
struct clk *mclk;
|
||||
struct reset_control *rstc;
|
||||
@@ -182,6 +189,68 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi)
|
||||
return SUN6I_MAX_XFER_SIZE - 1;
|
||||
}
|
||||
|
||||
static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi,
|
||||
struct spi_transfer *tfr)
|
||||
{
|
||||
struct dma_async_tx_descriptor *rxdesc, *txdesc;
|
||||
struct spi_master *master = sspi->master;
|
||||
|
||||
rxdesc = NULL;
|
||||
if (tfr->rx_buf) {
|
||||
struct dma_slave_config rxconf = {
|
||||
.direction = DMA_DEV_TO_MEM,
|
||||
.src_addr = sspi->dma_addr_rx,
|
||||
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
.src_maxburst = 8,
|
||||
};
|
||||
|
||||
dmaengine_slave_config(master->dma_rx, &rxconf);
|
||||
|
||||
rxdesc = dmaengine_prep_slave_sg(master->dma_rx,
|
||||
tfr->rx_sg.sgl,
|
||||
tfr->rx_sg.nents,
|
||||
DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!rxdesc)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
txdesc = NULL;
|
||||
if (tfr->tx_buf) {
|
||||
struct dma_slave_config txconf = {
|
||||
.direction = DMA_MEM_TO_DEV,
|
||||
.dst_addr = sspi->dma_addr_tx,
|
||||
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
.dst_maxburst = 8,
|
||||
};
|
||||
|
||||
dmaengine_slave_config(master->dma_tx, &txconf);
|
||||
|
||||
txdesc = dmaengine_prep_slave_sg(master->dma_tx,
|
||||
tfr->tx_sg.sgl,
|
||||
tfr->tx_sg.nents,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!txdesc) {
|
||||
if (rxdesc)
|
||||
dmaengine_terminate_sync(master->dma_rx);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (tfr->rx_buf) {
|
||||
dmaengine_submit(rxdesc);
|
||||
dma_async_issue_pending(master->dma_rx);
|
||||
}
|
||||
|
||||
if (tfr->tx_buf) {
|
||||
dmaengine_submit(txdesc);
|
||||
dma_async_issue_pending(master->dma_tx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun6i_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *tfr)
|
||||
@@ -191,6 +260,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
||||
unsigned int start, end, tx_time;
|
||||
unsigned int trig_level;
|
||||
unsigned int tx_len = 0, rx_len = 0;
|
||||
bool use_dma;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
@@ -201,6 +271,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
||||
sspi->tx_buf = tfr->tx_buf;
|
||||
sspi->rx_buf = tfr->rx_buf;
|
||||
sspi->len = tfr->len;
|
||||
use_dma = master->can_dma ? master->can_dma(master, spi, tfr) : false;
|
||||
|
||||
/* Clear pending interrupts */
|
||||
sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0);
|
||||
@@ -209,16 +280,34 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
||||
sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
|
||||
SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST);
|
||||
|
||||
/*
|
||||
* Setup FIFO interrupt trigger level
|
||||
* Here we choose 3/4 of the full fifo depth, as it's the hardcoded
|
||||
* value used in old generation of Allwinner SPI controller.
|
||||
* (See spi-sun4i.c)
|
||||
*/
|
||||
trig_level = sspi->fifo_depth / 4 * 3;
|
||||
sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
|
||||
(trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) |
|
||||
(trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS));
|
||||
reg = 0;
|
||||
|
||||
if (!use_dma) {
|
||||
/*
|
||||
* Setup FIFO interrupt trigger level
|
||||
* Here we choose 3/4 of the full fifo depth, as it's
|
||||
* the hardcoded value used in old generation of Allwinner
|
||||
* SPI controller. (See spi-sun4i.c)
|
||||
*/
|
||||
trig_level = sspi->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;
|
||||
|
||||
if (tfr->tx_buf)
|
||||
reg |= SUN6I_FIFO_CTL_TF_DRQ_EN;
|
||||
if (tfr->rx_buf)
|
||||
reg |= SUN6I_FIFO_CTL_RF_DRQ_EN;
|
||||
}
|
||||
|
||||
reg |= (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) |
|
||||
(trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS);
|
||||
|
||||
sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, reg);
|
||||
|
||||
/*
|
||||
* Setup the transfer control register: Chip Select,
|
||||
@@ -300,16 +389,28 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
||||
sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len);
|
||||
sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len);
|
||||
|
||||
/* Fill the TX FIFO */
|
||||
sun6i_spi_fill_fifo(sspi);
|
||||
if (!use_dma) {
|
||||
/* Fill the TX FIFO */
|
||||
sun6i_spi_fill_fifo(sspi);
|
||||
} else {
|
||||
ret = sun6i_spi_prepare_dma(sspi, tfr);
|
||||
if (ret) {
|
||||
dev_warn(&master->dev,
|
||||
"%s: prepare DMA failed, ret=%d",
|
||||
dev_name(&spi->dev), ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable the interrupts */
|
||||
reg = SUN6I_INT_CTL_TC;
|
||||
|
||||
if (rx_len > sspi->fifo_depth)
|
||||
reg |= SUN6I_INT_CTL_RF_RDY;
|
||||
if (tx_len > sspi->fifo_depth)
|
||||
reg |= SUN6I_INT_CTL_TF_ERQ;
|
||||
if (!use_dma) {
|
||||
if (rx_len > sspi->fifo_depth)
|
||||
reg |= SUN6I_INT_CTL_RF_RDY;
|
||||
if (tx_len > sspi->fifo_depth)
|
||||
reg |= SUN6I_INT_CTL_TF_ERQ;
|
||||
}
|
||||
|
||||
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
|
||||
|
||||
@@ -332,6 +433,11 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
|
||||
|
||||
sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
|
||||
|
||||
if (ret && use_dma) {
|
||||
dmaengine_terminate_sync(master->dma_rx);
|
||||
dmaengine_terminate_sync(master->dma_tx);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -422,10 +528,25 @@ static int sun6i_spi_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sun6i_spi_can_dma(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct sun6i_spi *sspi = spi_master_get_devdata(master);
|
||||
|
||||
/*
|
||||
* If the number of spi words to transfer is less or equal than
|
||||
* 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;
|
||||
}
|
||||
|
||||
static int sun6i_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct sun6i_spi *sspi;
|
||||
struct resource *mem;
|
||||
int ret = 0, irq;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi));
|
||||
@@ -437,7 +558,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, master);
|
||||
sspi = spi_master_get_devdata(master);
|
||||
|
||||
sspi->base_addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
sspi->base_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
|
||||
if (IS_ERR(sspi->base_addr)) {
|
||||
ret = PTR_ERR(sspi->base_addr);
|
||||
goto err_free_master;
|
||||
@@ -494,6 +615,33 @@ static int sun6i_spi_probe(struct platform_device *pdev)
|
||||
goto err_free_master;
|
||||
}
|
||||
|
||||
master->dma_tx = dma_request_chan(&pdev->dev, "tx");
|
||||
if (IS_ERR(master->dma_tx)) {
|
||||
/* Check tx to see if we need defer probing driver */
|
||||
if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err_free_master;
|
||||
}
|
||||
dev_warn(&pdev->dev, "Failed to request TX DMA channel\n");
|
||||
master->dma_tx = NULL;
|
||||
}
|
||||
|
||||
master->dma_rx = dma_request_chan(&pdev->dev, "rx");
|
||||
if (IS_ERR(master->dma_rx)) {
|
||||
if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto err_free_dma_tx;
|
||||
}
|
||||
dev_warn(&pdev->dev, "Failed to request RX DMA channel\n");
|
||||
master->dma_rx = NULL;
|
||||
}
|
||||
|
||||
if (master->dma_tx && master->dma_rx) {
|
||||
sspi->dma_addr_tx = mem->start + SUN6I_TXDATA_REG;
|
||||
sspi->dma_addr_rx = mem->start + SUN6I_RXDATA_REG;
|
||||
master->can_dma = sun6i_spi_can_dma;
|
||||
}
|
||||
|
||||
/*
|
||||
* This wake-up/shutdown pattern is to be able to have the
|
||||
* device woken up, even if runtime_pm is disabled
|
||||
@@ -501,12 +649,13 @@ static int sun6i_spi_probe(struct platform_device *pdev)
|
||||
ret = sun6i_spi_runtime_resume(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Couldn't resume the device\n");
|
||||
goto err_free_master;
|
||||
goto err_free_dma_rx;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, SUN6I_AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_idle(&pdev->dev);
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
if (ret) {
|
||||
@@ -519,6 +668,12 @@ static int sun6i_spi_probe(struct platform_device *pdev)
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
sun6i_spi_runtime_suspend(&pdev->dev);
|
||||
err_free_dma_rx:
|
||||
if (master->dma_rx)
|
||||
dma_release_channel(master->dma_rx);
|
||||
err_free_dma_tx:
|
||||
if (master->dma_tx)
|
||||
dma_release_channel(master->dma_tx);
|
||||
err_free_master:
|
||||
spi_master_put(master);
|
||||
return ret;
|
||||
@@ -526,8 +681,14 @@ err_free_master:
|
||||
|
||||
static int sun6i_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_force_suspend(&pdev->dev);
|
||||
|
||||
if (master->dma_tx)
|
||||
dma_release_channel(master->dma_tx);
|
||||
if (master->dma_rx)
|
||||
dma_release_channel(master->dma_rx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -657,7 +657,8 @@ static int synquacer_spi_probe(struct platform_device *pdev)
|
||||
|
||||
if (!master->max_speed_hz) {
|
||||
dev_err(&pdev->dev, "missing clock source\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto disable_clk;
|
||||
}
|
||||
master->min_speed_hz = master->max_speed_hz / 254;
|
||||
|
||||
@@ -670,7 +671,7 @@ static int synquacer_spi_probe(struct platform_device *pdev)
|
||||
rx_irq = platform_get_irq(pdev, 0);
|
||||
if (rx_irq <= 0) {
|
||||
ret = rx_irq;
|
||||
goto put_spi;
|
||||
goto disable_clk;
|
||||
}
|
||||
snprintf(sspi->rx_irq_name, SYNQUACER_HSSPI_IRQ_NAME_MAX, "%s-rx",
|
||||
dev_name(&pdev->dev));
|
||||
@@ -678,13 +679,13 @@ static int synquacer_spi_probe(struct platform_device *pdev)
|
||||
0, sspi->rx_irq_name, sspi);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request rx_irq failed (%d)\n", ret);
|
||||
goto put_spi;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
tx_irq = platform_get_irq(pdev, 1);
|
||||
if (tx_irq <= 0) {
|
||||
ret = tx_irq;
|
||||
goto put_spi;
|
||||
goto disable_clk;
|
||||
}
|
||||
snprintf(sspi->tx_irq_name, SYNQUACER_HSSPI_IRQ_NAME_MAX, "%s-tx",
|
||||
dev_name(&pdev->dev));
|
||||
@@ -692,7 +693,7 @@ static int synquacer_spi_probe(struct platform_device *pdev)
|
||||
0, sspi->tx_irq_name, sspi);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request tx_irq failed (%d)\n", ret);
|
||||
goto put_spi;
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
master->dev.of_node = np;
|
||||
@@ -710,7 +711,7 @@ static int synquacer_spi_probe(struct platform_device *pdev)
|
||||
|
||||
ret = synquacer_spi_enable(master);
|
||||
if (ret)
|
||||
goto fail_enable;
|
||||
goto disable_clk;
|
||||
|
||||
pm_runtime_set_active(sspi->dev);
|
||||
pm_runtime_enable(sspi->dev);
|
||||
@@ -723,7 +724,7 @@ static int synquacer_spi_probe(struct platform_device *pdev)
|
||||
|
||||
disable_pm:
|
||||
pm_runtime_disable(sspi->dev);
|
||||
fail_enable:
|
||||
disable_clk:
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
put_spi:
|
||||
spi_master_put(master);
|
||||
|
||||
@@ -966,6 +966,7 @@ static int tegra_spi_setup(struct spi_device *spi)
|
||||
|
||||
ret = pm_runtime_get_sync(tspi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(tspi->dev);
|
||||
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
|
||||
if (cdata)
|
||||
tegra_spi_cleanup(spi);
|
||||
@@ -1474,6 +1475,7 @@ static int tegra_spi_resume(struct device *dev)
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
dev_err(dev, "pm runtime failed, e = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -552,6 +552,7 @@ static int tegra_sflash_resume(struct device *dev)
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
dev_err(dev, "pm runtime failed, e = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -751,6 +751,7 @@ static int tegra_slink_setup(struct spi_device *spi)
|
||||
|
||||
ret = pm_runtime_get_sync(tspi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(tspi->dev);
|
||||
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -1188,6 +1189,7 @@ static int tegra_slink_resume(struct device *dev)
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
dev_err(dev, "pm runtime failed, e = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -174,6 +174,7 @@ static int ti_qspi_setup(struct spi_device *spi)
|
||||
|
||||
ret = pm_runtime_get_sync(qspi->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(qspi->dev);
|
||||
dev_err(qspi->dev, "pm_runtime_get_sync() failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -374,16 +374,7 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
return add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias);
|
||||
}
|
||||
|
||||
struct bus_type spi_bus_type = {
|
||||
.name = "spi",
|
||||
.dev_groups = spi_dev_groups,
|
||||
.match = spi_match_device,
|
||||
.uevent = spi_uevent,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(spi_bus_type);
|
||||
|
||||
|
||||
static int spi_drv_probe(struct device *dev)
|
||||
static int spi_probe(struct device *dev)
|
||||
{
|
||||
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
@@ -405,31 +396,55 @@ static int spi_drv_probe(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sdrv->probe(spi);
|
||||
if (ret)
|
||||
dev_pm_domain_detach(dev, true);
|
||||
if (sdrv->probe) {
|
||||
ret = sdrv->probe(spi);
|
||||
if (ret)
|
||||
dev_pm_domain_detach(dev, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_drv_remove(struct device *dev)
|
||||
static int spi_remove(struct device *dev)
|
||||
{
|
||||
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
||||
int ret;
|
||||
|
||||
ret = sdrv->remove(to_spi_device(dev));
|
||||
if (sdrv->remove) {
|
||||
int ret;
|
||||
|
||||
ret = sdrv->remove(to_spi_device(dev));
|
||||
if (ret)
|
||||
dev_warn(dev,
|
||||
"Failed to unbind driver (%pe), ignoring\n",
|
||||
ERR_PTR(ret));
|
||||
}
|
||||
|
||||
dev_pm_domain_detach(dev, true);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_drv_shutdown(struct device *dev)
|
||||
static void spi_shutdown(struct device *dev)
|
||||
{
|
||||
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
||||
if (dev->driver) {
|
||||
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
||||
|
||||
sdrv->shutdown(to_spi_device(dev));
|
||||
if (sdrv->shutdown)
|
||||
sdrv->shutdown(to_spi_device(dev));
|
||||
}
|
||||
}
|
||||
|
||||
struct bus_type spi_bus_type = {
|
||||
.name = "spi",
|
||||
.dev_groups = spi_dev_groups,
|
||||
.match = spi_match_device,
|
||||
.uevent = spi_uevent,
|
||||
.probe = spi_probe,
|
||||
.remove = spi_remove,
|
||||
.shutdown = spi_shutdown,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(spi_bus_type);
|
||||
|
||||
/**
|
||||
* __spi_register_driver - register a SPI driver
|
||||
* @owner: owner module of the driver to register
|
||||
@@ -442,12 +457,6 @@ int __spi_register_driver(struct module *owner, struct spi_driver *sdrv)
|
||||
{
|
||||
sdrv->driver.owner = owner;
|
||||
sdrv->driver.bus = &spi_bus_type;
|
||||
if (sdrv->probe)
|
||||
sdrv->driver.probe = spi_drv_probe;
|
||||
if (sdrv->remove)
|
||||
sdrv->driver.remove = spi_drv_remove;
|
||||
if (sdrv->shutdown)
|
||||
sdrv->driver.shutdown = spi_drv_shutdown;
|
||||
return driver_register(&sdrv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__spi_register_driver);
|
||||
@@ -3239,9 +3248,9 @@ static int __spi_split_transfer_maxsize(struct spi_controller *ctlr,
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_split_tranfers_maxsize - split spi transfers into multiple transfers
|
||||
* when an individual transfer exceeds a
|
||||
* certain size
|
||||
* spi_split_transfers_maxsize - split spi transfers into multiple transfers
|
||||
* when an individual transfer exceeds a
|
||||
* certain size
|
||||
* @ctlr: the @spi_controller for this transfer
|
||||
* @msg: the @spi_message to transform
|
||||
* @maxsize: the maximum when to apply this
|
||||
@@ -3370,7 +3379,8 @@ int spi_setup(struct spi_device *spi)
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (!spi->max_speed_hz)
|
||||
if (!spi->max_speed_hz ||
|
||||
spi->max_speed_hz > spi->controller->max_speed_hz)
|
||||
spi->max_speed_hz = spi->controller->max_speed_hz;
|
||||
|
||||
mutex_lock(&spi->controller->io_mutex);
|
||||
|
||||
22
include/dt-bindings/regulator/dlg,da9121-regulator.h
Normal file
22
include/dt-bindings/regulator/dlg,da9121-regulator.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
|
||||
#ifndef _DT_BINDINGS_REGULATOR_DLG_DA9121_H
|
||||
#define _DT_BINDINGS_REGULATOR_DLG_DA9121_H
|
||||
|
||||
/*
|
||||
* These buck mode constants may be used to specify values in device tree
|
||||
* properties (e.g. regulator-initial-mode).
|
||||
* A description of the following modes is in the manufacturers datasheet.
|
||||
*/
|
||||
|
||||
#define DA9121_BUCK_MODE_FORCE_PFM 0
|
||||
#define DA9121_BUCK_MODE_FORCE_PWM 1
|
||||
#define DA9121_BUCK_MODE_FORCE_PWM_SHEDDING 2
|
||||
#define DA9121_BUCK_MODE_AUTO 3
|
||||
|
||||
#define DA9121_BUCK_RIPPLE_CANCEL_NONE 0
|
||||
#define DA9121_BUCK_RIPPLE_CANCEL_SMALL 1
|
||||
#define DA9121_BUCK_RIPPLE_CANCEL_MID 2
|
||||
#define DA9121_BUCK_RIPPLE_CANCEL_LARGE 3
|
||||
|
||||
#endif
|
||||
@@ -315,6 +315,10 @@ typedef void (*regmap_unlock)(void *);
|
||||
* masks are used.
|
||||
* @zero_flag_mask: If set, read_flag_mask and write_flag_mask are used even
|
||||
* if they are both empty.
|
||||
* @use_relaxed_mmio: If set, MMIO R/W operations will not use memory barriers.
|
||||
* This can avoid load on devices which don't require strict
|
||||
* orderings, but drivers should carefully add any explicit
|
||||
* memory barriers when they may require them.
|
||||
* @use_single_read: If set, converts the bulk read operation into a series of
|
||||
* single read operations. This is useful for a device that
|
||||
* does not support bulk read.
|
||||
@@ -388,6 +392,7 @@ struct regmap_config {
|
||||
|
||||
bool use_single_read;
|
||||
bool use_single_write;
|
||||
bool use_relaxed_mmio;
|
||||
bool can_multi_write;
|
||||
|
||||
enum regmap_endian reg_format_endian;
|
||||
|
||||
36
include/linux/regulator/da9121.h
Normal file
36
include/linux/regulator/da9121.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* DA9121 Single-channel dual-phase 10A buck converter
|
||||
* DA9130 Single-channel dual-phase 10A buck converter (Automotive)
|
||||
* DA9217 Single-channel dual-phase 6A buck converter
|
||||
* DA9122 Dual-channel single-phase 5A buck converter
|
||||
* DA9131 Dual-channel single-phase 5A buck converter (Automotive)
|
||||
* DA9220 Dual-channel single-phase 3A buck converter
|
||||
* DA9132 Dual-channel single-phase 3A buck converter (Automotive)
|
||||
*
|
||||
* Copyright (C) 2020 Dialog Semiconductor
|
||||
*
|
||||
* Authors: Adam Ward, Dialog Semiconductor
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_REGULATOR_DA9121_H
|
||||
#define __LINUX_REGULATOR_DA9121_H
|
||||
|
||||
#include <linux/regulator/machine.h>
|
||||
|
||||
struct gpio_desc;
|
||||
|
||||
enum {
|
||||
DA9121_IDX_BUCK1,
|
||||
DA9121_IDX_BUCK2,
|
||||
DA9121_IDX_MAX
|
||||
};
|
||||
|
||||
struct da9121_pdata {
|
||||
int num_buck;
|
||||
struct gpio_desc *gpiod_ren[DA9121_IDX_MAX];
|
||||
struct device_node *reg_node[DA9121_IDX_MAX];
|
||||
struct regulator_init_data *init_data[DA9121_IDX_MAX];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -223,6 +223,8 @@ enum regulator_type {
|
||||
* @name: Identifying name for the regulator.
|
||||
* @supply_name: Identifying the regulator supply
|
||||
* @of_match: Name used to identify regulator in DT.
|
||||
* @of_match_full_name: A flag to indicate that the of_match string, if
|
||||
* present, should be matched against the node full_name.
|
||||
* @regulators_node: Name of node containing regulator definitions in DT.
|
||||
* @of_parse_cb: Optional callback called only if of_match is present.
|
||||
* Will be called for each regulator parsed from DT, during
|
||||
@@ -314,6 +316,7 @@ struct regulator_desc {
|
||||
const char *name;
|
||||
const char *supply_name;
|
||||
const char *of_match;
|
||||
bool of_match_full_name;
|
||||
const char *regulators_node;
|
||||
int (*of_parse_cb)(struct device_node *,
|
||||
const struct regulator_desc *,
|
||||
|
||||
@@ -63,10 +63,4 @@
|
||||
#define PFUZE3001_VLDO3 8
|
||||
#define PFUZE3001_VLDO4 9
|
||||
|
||||
struct regulator_init_data;
|
||||
|
||||
struct pfuze_regulator_platform_data {
|
||||
struct regulator_init_data *init_data[PFUZE100_MAX_REGULATOR];
|
||||
};
|
||||
|
||||
#endif /* __LINUX_REG_PFUZE100_H */
|
||||
|
||||
@@ -209,6 +209,64 @@ struct scmi_reset_ops {
|
||||
int (*deassert)(const struct scmi_handle *handle, u32 domain);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_voltage_info - describe one available SCMI Voltage Domain
|
||||
*
|
||||
* @id: the domain ID as advertised by the platform
|
||||
* @segmented: defines the layout of the entries of array @levels_uv.
|
||||
* - when True the entries are to be interpreted as triplets,
|
||||
* each defining a segment representing a range of equally
|
||||
* space voltages: <lowest_volts>, <highest_volt>, <step_uV>
|
||||
* - when False the entries simply represent a single discrete
|
||||
* supported voltage level
|
||||
* @negative_volts_allowed: True if any of the entries of @levels_uv represent
|
||||
* a negative voltage.
|
||||
* @attributes: represents Voltage Domain advertised attributes
|
||||
* @name: name assigned to the Voltage Domain by platform
|
||||
* @num_levels: number of total entries in @levels_uv.
|
||||
* @levels_uv: array of entries describing the available voltage levels for
|
||||
* this domain.
|
||||
*/
|
||||
struct scmi_voltage_info {
|
||||
unsigned int id;
|
||||
bool segmented;
|
||||
bool negative_volts_allowed;
|
||||
unsigned int attributes;
|
||||
char name[SCMI_MAX_STR_SIZE];
|
||||
unsigned int num_levels;
|
||||
#define SCMI_VOLTAGE_SEGMENT_LOW 0
|
||||
#define SCMI_VOLTAGE_SEGMENT_HIGH 1
|
||||
#define SCMI_VOLTAGE_SEGMENT_STEP 2
|
||||
int *levels_uv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_voltage_ops - represents the various operations provided
|
||||
* by SCMI Voltage Protocol
|
||||
*
|
||||
* @num_domains_get: get the count of voltage domains provided by SCMI
|
||||
* @info_get: get the information of the specified domain
|
||||
* @config_set: set the config for the specified domain
|
||||
* @config_get: get the config of the specified domain
|
||||
* @level_set: set the voltage level for the specified domain
|
||||
* @level_get: get the voltage level of the specified domain
|
||||
*/
|
||||
struct scmi_voltage_ops {
|
||||
int (*num_domains_get)(const struct scmi_handle *handle);
|
||||
const struct scmi_voltage_info __must_check *(*info_get)
|
||||
(const struct scmi_handle *handle, u32 domain_id);
|
||||
int (*config_set)(const struct scmi_handle *handle, u32 domain_id,
|
||||
u32 config);
|
||||
#define SCMI_VOLTAGE_ARCH_STATE_OFF 0x0
|
||||
#define SCMI_VOLTAGE_ARCH_STATE_ON 0x7
|
||||
int (*config_get)(const struct scmi_handle *handle, u32 domain_id,
|
||||
u32 *config);
|
||||
int (*level_set)(const struct scmi_handle *handle, u32 domain_id,
|
||||
u32 flags, s32 volt_uV);
|
||||
int (*level_get)(const struct scmi_handle *handle, u32 domain_id,
|
||||
s32 *volt_uV);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct scmi_notify_ops - represents notifications' operations provided by
|
||||
* SCMI core
|
||||
@@ -262,6 +320,7 @@ struct scmi_notify_ops {
|
||||
* @clk_ops: pointer to set of clock protocol operations
|
||||
* @sensor_ops: pointer to set of sensor protocol operations
|
||||
* @reset_ops: pointer to set of reset protocol operations
|
||||
* @voltage_ops: pointer to set of voltage protocol operations
|
||||
* @notify_ops: pointer to set of notifications related operations
|
||||
* @perf_priv: pointer to private data structure specific to performance
|
||||
* protocol(for internal use only)
|
||||
@@ -273,6 +332,8 @@ struct scmi_notify_ops {
|
||||
* protocol(for internal use only)
|
||||
* @reset_priv: pointer to private data structure specific to reset
|
||||
* protocol(for internal use only)
|
||||
* @voltage_priv: pointer to private data structure specific to voltage
|
||||
* protocol(for internal use only)
|
||||
* @notify_priv: pointer to private data structure specific to notifications
|
||||
* (for internal use only)
|
||||
*/
|
||||
@@ -284,6 +345,7 @@ struct scmi_handle {
|
||||
const struct scmi_power_ops *power_ops;
|
||||
const struct scmi_sensor_ops *sensor_ops;
|
||||
const struct scmi_reset_ops *reset_ops;
|
||||
const struct scmi_voltage_ops *voltage_ops;
|
||||
const struct scmi_notify_ops *notify_ops;
|
||||
/* for protocol internal use */
|
||||
void *perf_priv;
|
||||
@@ -291,6 +353,7 @@ struct scmi_handle {
|
||||
void *power_priv;
|
||||
void *sensor_priv;
|
||||
void *reset_priv;
|
||||
void *voltage_priv;
|
||||
void *notify_priv;
|
||||
void *system_priv;
|
||||
};
|
||||
@@ -303,6 +366,7 @@ enum scmi_std_protocol {
|
||||
SCMI_PROTOCOL_CLOCK = 0x14,
|
||||
SCMI_PROTOCOL_SENSOR = 0x15,
|
||||
SCMI_PROTOCOL_RESET = 0x16,
|
||||
SCMI_PROTOCOL_VOLTAGE = 0x17,
|
||||
};
|
||||
|
||||
enum scmi_system_events {
|
||||
|
||||
@@ -171,6 +171,7 @@ struct spi_device {
|
||||
#define SPI_MODE_1 (0|SPI_CPHA)
|
||||
#define SPI_MODE_2 (SPI_CPOL|0)
|
||||
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
|
||||
#define SPI_MODE_X_MASK (SPI_CPOL|SPI_CPHA)
|
||||
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
|
||||
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
|
||||
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
|
||||
|
||||
@@ -180,7 +180,6 @@ config SND_SOC_ALL_CODECS
|
||||
imply SND_SOC_RT700_SDW
|
||||
imply SND_SOC_RT711_SDW
|
||||
imply SND_SOC_RT715_SDW
|
||||
imply SND_SOC_RT715_SDCA_SDW
|
||||
imply SND_SOC_RT1308_SDW
|
||||
imply SND_SOC_SGTL5000
|
||||
imply SND_SOC_SI476X
|
||||
@@ -1237,12 +1236,6 @@ config SND_SOC_RT715_SDW
|
||||
select SND_SOC_RT715
|
||||
select REGMAP_SOUNDWIRE
|
||||
|
||||
config SND_SOC_RT715_SDCA_SDW
|
||||
tristate "Realtek RT715 SDCA Codec - SDW"
|
||||
depends on SOUNDWIRE
|
||||
select REGMAP_SOUNDWIRE
|
||||
select REGMAP_SOUNDWIRE_MBQ
|
||||
|
||||
#Freescale sgtl5000 codec
|
||||
config SND_SOC_SGTL5000
|
||||
tristate "Freescale SGTL5000 CODEC"
|
||||
|
||||
@@ -194,7 +194,6 @@ snd-soc-rt5682-i2c-objs := rt5682-i2c.o
|
||||
snd-soc-rt700-objs := rt700.o rt700-sdw.o
|
||||
snd-soc-rt711-objs := rt711.o rt711-sdw.o
|
||||
snd-soc-rt715-objs := rt715.o rt715-sdw.o
|
||||
snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o
|
||||
snd-soc-sgtl5000-objs := sgtl5000.o
|
||||
snd-soc-alc5623-objs := alc5623.o
|
||||
snd-soc-alc5632-objs := alc5632.o
|
||||
@@ -511,7 +510,6 @@ obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o
|
||||
obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o
|
||||
obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o
|
||||
obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o
|
||||
obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o
|
||||
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
|
||||
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
|
||||
obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver
|
||||
//
|
||||
// Copyright(c) 2020 Realtek Semiconductor Corp.
|
||||
//
|
||||
//
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_type.h>
|
||||
#include <linux/soundwire/sdw_registers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
#include "rt715-sdca.h"
|
||||
#include "rt715-sdca-sdw.h"
|
||||
|
||||
static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x201a ... 0x2027:
|
||||
case 0x2029 ... 0x202a:
|
||||
case 0x202d ... 0x2034:
|
||||
case 0x2200 ... 0x2204:
|
||||
case 0x2206 ... 0x2212:
|
||||
case 0x2230 ... 0x2239:
|
||||
case 0x2f5b:
|
||||
case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
|
||||
RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00):
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x201b:
|
||||
case 0x201c:
|
||||
case 0x201d:
|
||||
case 0x201f:
|
||||
case 0x2021:
|
||||
case 0x2023:
|
||||
case 0x2230:
|
||||
case 0x202d ... 0x202f: /* BRA */
|
||||
case 0x2200 ... 0x2212: /* i2c debug */
|
||||
case 0x2f07:
|
||||
case 0x2f1b ... 0x2f1e:
|
||||
case 0x2f30 ... 0x2f34:
|
||||
case 0x2f50 ... 0x2f51:
|
||||
case 0x2f53 ... 0x2f59:
|
||||
case 0x2f5c ... 0x2f5f:
|
||||
case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
|
||||
RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x2000000:
|
||||
case 0x200002b:
|
||||
case 0x2000036:
|
||||
case 0x2000037:
|
||||
case 0x2000039:
|
||||
case 0x6100000:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x2000000:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config rt715_sdca_regmap = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 8,
|
||||
.readable_reg = rt715_sdca_readable_register,
|
||||
.volatile_reg = rt715_sdca_volatile_register,
|
||||
.max_register = 0x43ffffff,
|
||||
.reg_defaults = rt715_reg_defaults_sdca,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
|
||||
static const struct regmap_config rt715_sdca_mbq_regmap = {
|
||||
.name = "sdw-mbq",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 16,
|
||||
.readable_reg = rt715_sdca_mbq_readable_register,
|
||||
.volatile_reg = rt715_sdca_mbq_volatile_register,
|
||||
.max_register = 0x43ffffff,
|
||||
.reg_defaults = rt715_mbq_reg_defaults_sdca,
|
||||
.num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
};
|
||||
|
||||
static int rt715_update_status(struct sdw_slave *slave,
|
||||
enum sdw_slave_status status)
|
||||
{
|
||||
struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev);
|
||||
|
||||
/* Update the status */
|
||||
rt715->status = status;
|
||||
|
||||
/*
|
||||
* Perform initialization only if slave status is present and
|
||||
* hw_init flag is false
|
||||
*/
|
||||
if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED)
|
||||
return 0;
|
||||
|
||||
/* perform I/O transfers required for Slave initialization */
|
||||
return rt715_io_init(&slave->dev, slave);
|
||||
}
|
||||
|
||||
static int rt715_read_prop(struct sdw_slave *slave)
|
||||
{
|
||||
struct sdw_slave_prop *prop = &slave->prop;
|
||||
int nval, i;
|
||||
u32 bit;
|
||||
unsigned long addr;
|
||||
struct sdw_dpn_prop *dpn;
|
||||
|
||||
prop->paging_support = true;
|
||||
|
||||
/* first we need to allocate memory for set bits in port lists */
|
||||
prop->source_ports = 0x50;/* BITMAP: 01010000 */
|
||||
prop->sink_ports = 0x0; /* BITMAP: 00000000 */
|
||||
|
||||
nval = hweight32(prop->source_ports);
|
||||
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
|
||||
sizeof(*prop->src_dpn_prop),
|
||||
GFP_KERNEL);
|
||||
if (!prop->src_dpn_prop)
|
||||
return -ENOMEM;
|
||||
|
||||
dpn = prop->src_dpn_prop;
|
||||
i = 0;
|
||||
addr = prop->source_ports;
|
||||
for_each_set_bit(bit, &addr, 32) {
|
||||
dpn[i].num = bit;
|
||||
dpn[i].simple_ch_prep_sm = true;
|
||||
dpn[i].ch_prep_timeout = 10;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* set the timeout values */
|
||||
prop->clk_stop_timeout = 20;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdw_slave_ops rt715_sdca_slave_ops = {
|
||||
.read_prop = rt715_read_prop,
|
||||
.update_status = rt715_update_status,
|
||||
};
|
||||
|
||||
static int rt715_sdca_sdw_probe(struct sdw_slave *slave,
|
||||
const struct sdw_device_id *id)
|
||||
{
|
||||
struct regmap *mbq_regmap, *regmap;
|
||||
|
||||
slave->ops = &rt715_sdca_slave_ops;
|
||||
|
||||
/* Regmap Initialization */
|
||||
mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap);
|
||||
if (!mbq_regmap)
|
||||
return -EINVAL;
|
||||
|
||||
regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap);
|
||||
if (!regmap)
|
||||
return -EINVAL;
|
||||
|
||||
return rt715_init(&slave->dev, mbq_regmap, regmap, slave);
|
||||
}
|
||||
|
||||
static const struct sdw_device_id rt715_sdca_id[] = {
|
||||
SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0),
|
||||
SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0),
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdw, rt715_sdca_id);
|
||||
|
||||
static int __maybe_unused rt715_dev_suspend(struct device *dev)
|
||||
{
|
||||
struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
|
||||
|
||||
if (!rt715->hw_init)
|
||||
return 0;
|
||||
|
||||
regcache_cache_only(rt715->regmap, true);
|
||||
regcache_mark_dirty(rt715->regmap);
|
||||
regcache_cache_only(rt715->mbq_regmap, true);
|
||||
regcache_mark_dirty(rt715->mbq_regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT715_PROBE_TIMEOUT 2000
|
||||
|
||||
static int __maybe_unused rt715_dev_resume(struct device *dev)
|
||||
{
|
||||
struct sdw_slave *slave = dev_to_sdw_dev(dev);
|
||||
struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
|
||||
if (!rt715->hw_init)
|
||||
return 0;
|
||||
|
||||
if (!slave->unattach_request)
|
||||
goto regmap_sync;
|
||||
|
||||
time = wait_for_completion_timeout(&slave->enumeration_complete,
|
||||
msecs_to_jiffies(RT715_PROBE_TIMEOUT));
|
||||
if (!time) {
|
||||
dev_err(&slave->dev, "Enumeration not complete, timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
regmap_sync:
|
||||
slave->unattach_request = 0;
|
||||
regcache_cache_only(rt715->regmap, false);
|
||||
regcache_sync_region(rt715->regmap,
|
||||
SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
|
||||
CH_00),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
|
||||
RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00));
|
||||
regcache_cache_only(rt715->mbq_regmap, false);
|
||||
regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff);
|
||||
regcache_sync_region(rt715->mbq_regmap,
|
||||
SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
|
||||
CH_00),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
|
||||
RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rt715_pm = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
|
||||
SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
|
||||
};
|
||||
|
||||
static struct sdw_driver rt715_sdw_driver = {
|
||||
.driver = {
|
||||
.name = "rt715-sdca",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &rt715_pm,
|
||||
},
|
||||
.probe = rt715_sdca_sdw_probe,
|
||||
.ops = &rt715_sdca_slave_ops,
|
||||
.id_table = rt715_sdca_id,
|
||||
};
|
||||
module_sdw_driver(rt715_sdw_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA");
|
||||
MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -1,170 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* rt715-sdca-sdw.h -- RT715 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2020 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT715_SDW_SDCA_H__
|
||||
#define __RT715_SDW_SDCA_H__
|
||||
|
||||
#include <linux/soundwire/sdw_registers.h>
|
||||
|
||||
static const struct reg_default rt715_reg_defaults_sdca[] = {
|
||||
{ 0x201a, 0x00 },
|
||||
{ 0x201e, 0x00 },
|
||||
{ 0x2020, 0x00 },
|
||||
{ 0x2021, 0x00 },
|
||||
{ 0x2022, 0x00 },
|
||||
{ 0x2023, 0x00 },
|
||||
{ 0x2024, 0x00 },
|
||||
{ 0x2025, 0x01 },
|
||||
{ 0x2026, 0x00 },
|
||||
{ 0x2027, 0x00 },
|
||||
{ 0x2029, 0x00 },
|
||||
{ 0x202a, 0x00 },
|
||||
{ 0x202d, 0x00 },
|
||||
{ 0x202e, 0x00 },
|
||||
{ 0x202f, 0x00 },
|
||||
{ 0x2030, 0x00 },
|
||||
{ 0x2031, 0x00 },
|
||||
{ 0x2032, 0x00 },
|
||||
{ 0x2033, 0x00 },
|
||||
{ 0x2034, 0x00 },
|
||||
{ 0x2230, 0x00 },
|
||||
{ 0x2231, 0x2f },
|
||||
{ 0x2232, 0x80 },
|
||||
{ 0x2233, 0x00 },
|
||||
{ 0x2234, 0x00 },
|
||||
{ 0x2235, 0x00 },
|
||||
{ 0x2236, 0x00 },
|
||||
{ 0x2237, 0x00 },
|
||||
{ 0x2238, 0x00 },
|
||||
{ 0x2239, 0x00 },
|
||||
{ 0x2f01, 0x00 },
|
||||
{ 0x2f02, 0x09 },
|
||||
{ 0x2f03, 0x0b },
|
||||
{ 0x2f04, 0x00 },
|
||||
{ 0x2f05, 0x0e },
|
||||
{ 0x2f06, 0x01 },
|
||||
{ 0x2f08, 0x00 },
|
||||
{ 0x2f09, 0x00 },
|
||||
{ 0x2f0a, 0x00 },
|
||||
{ 0x2f0b, 0x00 },
|
||||
{ 0x2f0c, 0x00 },
|
||||
{ 0x2f0d, 0x00 },
|
||||
{ 0x2f0e, 0x12 },
|
||||
{ 0x2f0f, 0x00 },
|
||||
{ 0x2f10, 0x00 },
|
||||
{ 0x2f11, 0x00 },
|
||||
{ 0x2f12, 0x00 },
|
||||
{ 0x2f13, 0x00 },
|
||||
{ 0x2f14, 0x00 },
|
||||
{ 0x2f15, 0x00 },
|
||||
{ 0x2f16, 0x00 },
|
||||
{ 0x2f17, 0x00 },
|
||||
{ 0x2f18, 0x00 },
|
||||
{ 0x2f19, 0x03 },
|
||||
{ 0x2f1a, 0x00 },
|
||||
{ 0x2f1f, 0x10 },
|
||||
{ 0x2f20, 0x00 },
|
||||
{ 0x2f21, 0x00 },
|
||||
{ 0x2f22, 0x00 },
|
||||
{ 0x2f23, 0x00 },
|
||||
{ 0x2f24, 0x00 },
|
||||
{ 0x2f25, 0x00 },
|
||||
{ 0x2f52, 0x01 },
|
||||
{ 0x2f5a, 0x02 },
|
||||
{ 0x2f5b, 0x05 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN,
|
||||
RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
|
||||
RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
|
||||
RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
|
||||
};
|
||||
|
||||
static const struct reg_default rt715_mbq_reg_defaults_sdca[] = {
|
||||
{ 0x200002b, 0x0420 },
|
||||
{ 0x2000036, 0x0000 },
|
||||
{ 0x2000037, 0x0000 },
|
||||
{ 0x2000039, 0xaa81 },
|
||||
{ 0x6100000, 0x0100 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 },
|
||||
{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 },
|
||||
};
|
||||
#endif /* __RT715_SDW_SDCA_H__ */
|
||||
@@ -1,936 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// rt715-sdca.c -- rt715 ALSA SoC audio driver
|
||||
//
|
||||
// Copyright(c) 2020 Realtek Semiconductor Corp.
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <linux/soundwire/sdw_registers.h>
|
||||
|
||||
#include "rt715-sdca.h"
|
||||
|
||||
static int rt715_index_write(struct rt715_sdca_priv *rt715, unsigned int nid,
|
||||
unsigned int reg, unsigned int value)
|
||||
{
|
||||
struct regmap *regmap = rt715->mbq_regmap;
|
||||
unsigned int addr;
|
||||
int ret;
|
||||
|
||||
addr = (nid << 20) | reg;
|
||||
|
||||
ret = regmap_write(regmap, addr, value);
|
||||
if (ret < 0)
|
||||
dev_err(&rt715->slave->dev,
|
||||
"Failed to set private value: %08x <= %04x %d\n", ret, addr,
|
||||
value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt715_index_read(struct rt715_sdca_priv *rt715,
|
||||
unsigned int nid, unsigned int reg, unsigned int *value)
|
||||
{
|
||||
struct regmap *regmap = rt715->mbq_regmap;
|
||||
unsigned int addr;
|
||||
int ret;
|
||||
|
||||
addr = (nid << 20) | reg;
|
||||
|
||||
ret = regmap_read(regmap, addr, value);
|
||||
if (ret < 0)
|
||||
dev_err(&rt715->slave->dev,
|
||||
"Failed to get private value: %06x => %04x ret=%d\n",
|
||||
addr, *value, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rt715_index_update_bits(struct rt715_sdca_priv *rt715,
|
||||
unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
ret = rt715_index_read(rt715, nid, reg, &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
set_mask_bits(&tmp, mask, val);
|
||||
|
||||
return rt715_index_write(rt715, nid, reg, tmp);
|
||||
}
|
||||
|
||||
/* SDCA Volume/Boost control */
|
||||
static int rt715_set_amp_gain_put_sdca(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int val_l, val_r, gain_l_val, gain_r_val;
|
||||
int ret;
|
||||
|
||||
/* control value to 2s complement */
|
||||
/* L channel */
|
||||
gain_l_val = ucontrol->value.integer.value[0];
|
||||
if (gain_l_val > mc->max)
|
||||
gain_l_val = mc->max;
|
||||
val_l = gain_l_val;
|
||||
|
||||
if (mc->shift == 8) {
|
||||
gain_l_val = (gain_l_val * 10) << mc->shift;
|
||||
} else {
|
||||
gain_l_val =
|
||||
((abs(gain_l_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000;
|
||||
if (val_l <= mc->shift) {
|
||||
gain_l_val = ~gain_l_val;
|
||||
gain_l_val += 1;
|
||||
}
|
||||
gain_l_val &= 0xffff;
|
||||
}
|
||||
|
||||
/* R channel */
|
||||
gain_r_val = ucontrol->value.integer.value[1];
|
||||
if (gain_r_val > mc->max)
|
||||
gain_r_val = mc->max;
|
||||
val_r = gain_r_val;
|
||||
|
||||
if (mc->shift == 8) {
|
||||
gain_r_val = (gain_r_val * 10) << mc->shift;
|
||||
} else {
|
||||
gain_r_val =
|
||||
((abs(gain_r_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000;
|
||||
if (val_r <= mc->shift) {
|
||||
gain_r_val = ~gain_r_val;
|
||||
gain_r_val += 1;
|
||||
}
|
||||
gain_r_val &= 0xffff;
|
||||
}
|
||||
|
||||
/* Lch*/
|
||||
ret = regmap_write(rt715->mbq_regmap, mc->reg, gain_l_val);
|
||||
if (ret != 0) {
|
||||
dev_err(component->dev, "Failed to write 0x%x=0x%x\n", mc->reg,
|
||||
gain_l_val);
|
||||
return ret;
|
||||
}
|
||||
/* Rch */
|
||||
ret = regmap_write(rt715->mbq_regmap, mc->rreg, gain_r_val);
|
||||
if (ret != 0) {
|
||||
dev_err(component->dev, "Failed to write 0x%x=0x%x\n", mc->rreg,
|
||||
gain_r_val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt715_set_amp_gain_get_sdca(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct soc_mixer_control *mc =
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int val_l, val_r, ctl_l, ctl_r, neg_flag = 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rt715->mbq_regmap, mc->reg, &val_l);
|
||||
if (ret < 0)
|
||||
dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->reg, ret);
|
||||
ret = regmap_read(rt715->mbq_regmap, mc->rreg, &val_r);
|
||||
if (ret < 0)
|
||||
dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->rreg,
|
||||
ret);
|
||||
|
||||
/* L channel */
|
||||
if (mc->shift == 8) {
|
||||
ctl_l = (val_l >> mc->shift) / 10;
|
||||
} else {
|
||||
ctl_l = val_l;
|
||||
if (ctl_l & BIT(15)) {
|
||||
ctl_l = ~(val_l - 1) & 0xffff;
|
||||
neg_flag = 1;
|
||||
}
|
||||
ctl_l *= 1000;
|
||||
ctl_l >>= 8;
|
||||
if (neg_flag)
|
||||
ctl_l = mc->shift - ctl_l / RT715_SDCA_DB_STEP;
|
||||
else
|
||||
ctl_l = mc->shift + ctl_l / RT715_SDCA_DB_STEP;
|
||||
}
|
||||
|
||||
neg_flag = 0;
|
||||
/* R channel */
|
||||
if (mc->shift == 8) {
|
||||
ctl_r = (val_r >> mc->shift) / 10;
|
||||
} else {
|
||||
ctl_r = val_r;
|
||||
if (ctl_r & BIT(15)) {
|
||||
ctl_r = ~(val_r - 1) & 0xffff;
|
||||
neg_flag = 1;
|
||||
}
|
||||
ctl_r *= 1000;
|
||||
ctl_r >>= 8;
|
||||
if (neg_flag)
|
||||
ctl_r = mc->shift - ctl_r / RT715_SDCA_DB_STEP;
|
||||
else
|
||||
ctl_r = mc->shift + ctl_r / RT715_SDCA_DB_STEP;
|
||||
}
|
||||
|
||||
ucontrol->value.integer.value[0] = ctl_l;
|
||||
ucontrol->value.integer.value[1] = ctl_r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -17625, 375, 0);
|
||||
static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
|
||||
|
||||
#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\
|
||||
xhandler_get, xhandler_put) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
|
||||
.info = snd_soc_info_volsw, \
|
||||
.get = xhandler_get, .put = xhandler_put, \
|
||||
.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
|
||||
xmax, xinvert) }
|
||||
|
||||
static const struct snd_kcontrol_new rt715_snd_controls_sdca[] = {
|
||||
/* Capture switch */
|
||||
SOC_DOUBLE_R("FU0A Capture Switch",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_01),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_02),
|
||||
0, 1, 1),
|
||||
SOC_DOUBLE_R("FU02 1_2 Capture Switch",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_01),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_02),
|
||||
0, 1, 1),
|
||||
SOC_DOUBLE_R("FU02 3_4 Capture Switch",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_03),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_04),
|
||||
0, 1, 1),
|
||||
SOC_DOUBLE_R("FU06 1_2 Capture Switch",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_01),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_02),
|
||||
0, 1, 1),
|
||||
SOC_DOUBLE_R("FU06 3_4 Capture Switch",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_03),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_MUTE_CTRL, CH_04),
|
||||
0, 1, 1),
|
||||
/* Volume Control */
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0A Capture Volume",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_01),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_02),
|
||||
0x2f, 0x7f, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
in_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU02 1_2 Capture Volume",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_01),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL, CH_02),
|
||||
0x2f, 0x7f, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
in_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU02 3_4 Capture Volume",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL,
|
||||
CH_03),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL,
|
||||
CH_04), 0x2f, 0x7f, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
in_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU06 1_2 Capture Volume",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL,
|
||||
CH_01),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL,
|
||||
CH_02), 0x2f, 0x7f, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
in_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU06 3_4 Capture Volume",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL,
|
||||
CH_03),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
|
||||
RT715_SDCA_FU_VOL_CTRL,
|
||||
CH_04), 0x2f, 0x7f, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
in_vol_tlv),
|
||||
/* MIC Boost Control */
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0E 1_2 Boost",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_01),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_02), 8, 3, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0E 3_4 Boost",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_03),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_04), 8, 3, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0E 5_6 Boost",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_05),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_06), 8, 3, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0E 7_8 Boost",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_07),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_08), 8, 3, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0C 1_2 Boost",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_01),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_02), 8, 3, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0C 3_4 Boost",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_03),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_04), 8, 3, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0C 5_6 Boost",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_05),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_06), 8, 3, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
mic_vol_tlv),
|
||||
SOC_DOUBLE_R_EXT_TLV("FU0C 7_8 Boost",
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_07),
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
|
||||
RT715_SDCA_FU_DMIC_GAIN_CTRL,
|
||||
CH_08), 8, 3, 0,
|
||||
rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca,
|
||||
mic_vol_tlv),
|
||||
};
|
||||
|
||||
static int rt715_mux_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_kcontrol_component(kcontrol);
|
||||
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
unsigned int val, mask_sft;
|
||||
|
||||
if (strstr(ucontrol->id.name, "ADC 22 Mux"))
|
||||
mask_sft = 12;
|
||||
else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
|
||||
mask_sft = 8;
|
||||
else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
|
||||
mask_sft = 4;
|
||||
else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
|
||||
mask_sft = 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
rt715_index_read(rt715, RT715_VENDOR_HDA_CTL,
|
||||
RT715_HDA_LEGACY_MUX_CTL1, &val);
|
||||
val = (val >> mask_sft) & 0xf;
|
||||
|
||||
/*
|
||||
* The first two indices of ADC Mux 24/25 are routed to the same
|
||||
* hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2.
|
||||
* To have a unique set of inputs, we skip the index1 of the muxes.
|
||||
*/
|
||||
if ((strstr(ucontrol->id.name, "ADC 24 Mux") ||
|
||||
strstr(ucontrol->id.name, "ADC 25 Mux")) && val > 0)
|
||||
val -= 1;
|
||||
ucontrol->value.enumerated.item[0] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt715_mux_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_kcontrol_component(kcontrol);
|
||||
struct snd_soc_dapm_context *dapm =
|
||||
snd_soc_dapm_kcontrol_dapm(kcontrol);
|
||||
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
unsigned int *item = ucontrol->value.enumerated.item;
|
||||
unsigned int val, val2 = 0, change, mask_sft;
|
||||
|
||||
if (item[0] >= e->items)
|
||||
return -EINVAL;
|
||||
|
||||
if (strstr(ucontrol->id.name, "ADC 22 Mux"))
|
||||
mask_sft = 12;
|
||||
else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
|
||||
mask_sft = 8;
|
||||
else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
|
||||
mask_sft = 4;
|
||||
else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
|
||||
mask_sft = 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* Verb ID = 0x701h, nid = e->reg */
|
||||
val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
|
||||
|
||||
rt715_index_read(rt715, RT715_VENDOR_HDA_CTL,
|
||||
RT715_HDA_LEGACY_MUX_CTL1, &val2);
|
||||
val2 = (val2 >> mask_sft) & 0xf;
|
||||
|
||||
change = val != val2;
|
||||
|
||||
if (change)
|
||||
rt715_index_update_bits(rt715, RT715_VENDOR_HDA_CTL,
|
||||
RT715_HDA_LEGACY_MUX_CTL1, 0xf << mask_sft, val << mask_sft);
|
||||
|
||||
snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
static const char * const adc_22_23_mux_text[] = {
|
||||
"MIC1",
|
||||
"MIC2",
|
||||
"LINE1",
|
||||
"LINE2",
|
||||
"DMIC1",
|
||||
"DMIC2",
|
||||
"DMIC3",
|
||||
"DMIC4",
|
||||
};
|
||||
|
||||
/*
|
||||
* Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and
|
||||
* 1 will be connected to the same dmic source, therefore we skip index 1 to
|
||||
* avoid misunderstanding on usage of dapm routing.
|
||||
*/
|
||||
static int rt715_adc_24_25_values[] = {
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
};
|
||||
|
||||
static const char * const adc_24_mux_text[] = {
|
||||
"MIC2",
|
||||
"DMIC1",
|
||||
"DMIC2",
|
||||
"DMIC3",
|
||||
"DMIC4",
|
||||
};
|
||||
|
||||
static const char * const adc_25_mux_text[] = {
|
||||
"MIC1",
|
||||
"DMIC1",
|
||||
"DMIC2",
|
||||
"DMIC3",
|
||||
"DMIC4",
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(rt715_adc22_enum, SND_SOC_NOPM, 0,
|
||||
adc_22_23_mux_text);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(rt715_adc23_enum, SND_SOC_NOPM, 0,
|
||||
adc_22_23_mux_text);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum,
|
||||
SND_SOC_NOPM, 0, 0xf,
|
||||
adc_24_mux_text, rt715_adc_24_25_values);
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum,
|
||||
SND_SOC_NOPM, 0, 0xf,
|
||||
adc_25_mux_text, rt715_adc_24_25_values);
|
||||
|
||||
static const struct snd_kcontrol_new rt715_adc22_mux =
|
||||
SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum,
|
||||
rt715_mux_get, rt715_mux_put);
|
||||
|
||||
static const struct snd_kcontrol_new rt715_adc23_mux =
|
||||
SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum,
|
||||
rt715_mux_get, rt715_mux_put);
|
||||
|
||||
static const struct snd_kcontrol_new rt715_adc24_mux =
|
||||
SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum,
|
||||
rt715_mux_get, rt715_mux_put);
|
||||
|
||||
static const struct snd_kcontrol_new rt715_adc25_mux =
|
||||
SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum,
|
||||
rt715_mux_get, rt715_mux_put);
|
||||
|
||||
static int rt715_pde23_24_event(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
struct snd_soc_component *component =
|
||||
snd_soc_dapm_to_component(w->dapm);
|
||||
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
regmap_write(rt715->regmap,
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN,
|
||||
RT715_SDCA_REQ_POW_CTRL,
|
||||
CH_00), 0x00);
|
||||
break;
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
regmap_write(rt715->regmap,
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN,
|
||||
RT715_SDCA_REQ_POW_CTRL,
|
||||
CH_00), 0x03);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_INPUT("DMIC1"),
|
||||
SND_SOC_DAPM_INPUT("DMIC2"),
|
||||
SND_SOC_DAPM_INPUT("DMIC3"),
|
||||
SND_SOC_DAPM_INPUT("DMIC4"),
|
||||
SND_SOC_DAPM_INPUT("MIC1"),
|
||||
SND_SOC_DAPM_INPUT("MIC2"),
|
||||
SND_SOC_DAPM_INPUT("LINE1"),
|
||||
SND_SOC_DAPM_INPUT("LINE2"),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("PDE23_24", SND_SOC_NOPM, 0, 0,
|
||||
rt715_pde23_24_event,
|
||||
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
|
||||
|
||||
SND_SOC_DAPM_ADC("ADC 07", NULL, SND_SOC_NOPM, 4, 0),
|
||||
SND_SOC_DAPM_ADC("ADC 08", NULL, SND_SOC_NOPM, 4, 0),
|
||||
SND_SOC_DAPM_ADC("ADC 09", NULL, SND_SOC_NOPM, 4, 0),
|
||||
SND_SOC_DAPM_ADC("ADC 27", NULL, SND_SOC_NOPM, 4, 0),
|
||||
SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt715_adc22_mux),
|
||||
SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt715_adc23_mux),
|
||||
SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt715_adc24_mux),
|
||||
SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
|
||||
&rt715_adc25_mux),
|
||||
SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt715_audio_map[] = {
|
||||
{"DP6TX", NULL, "ADC 09"},
|
||||
{"DP6TX", NULL, "ADC 08"},
|
||||
{"DP4TX", NULL, "ADC 07"},
|
||||
{"DP4TX", NULL, "ADC 27"},
|
||||
{"DP4TX", NULL, "ADC 09"},
|
||||
{"DP4TX", NULL, "ADC 08"},
|
||||
|
||||
{"LINE1", NULL, "PDE23_24"},
|
||||
{"LINE2", NULL, "PDE23_24"},
|
||||
{"MIC1", NULL, "PDE23_24"},
|
||||
{"MIC2", NULL, "PDE23_24"},
|
||||
{"DMIC1", NULL, "PDE23_24"},
|
||||
{"DMIC2", NULL, "PDE23_24"},
|
||||
{"DMIC3", NULL, "PDE23_24"},
|
||||
{"DMIC4", NULL, "PDE23_24"},
|
||||
|
||||
{"ADC 09", NULL, "ADC 22 Mux"},
|
||||
{"ADC 08", NULL, "ADC 23 Mux"},
|
||||
{"ADC 07", NULL, "ADC 24 Mux"},
|
||||
{"ADC 27", NULL, "ADC 25 Mux"},
|
||||
{"ADC 22 Mux", "MIC1", "MIC1"},
|
||||
{"ADC 22 Mux", "MIC2", "MIC2"},
|
||||
{"ADC 22 Mux", "LINE1", "LINE1"},
|
||||
{"ADC 22 Mux", "LINE2", "LINE2"},
|
||||
{"ADC 22 Mux", "DMIC1", "DMIC1"},
|
||||
{"ADC 22 Mux", "DMIC2", "DMIC2"},
|
||||
{"ADC 22 Mux", "DMIC3", "DMIC3"},
|
||||
{"ADC 22 Mux", "DMIC4", "DMIC4"},
|
||||
{"ADC 23 Mux", "MIC1", "MIC1"},
|
||||
{"ADC 23 Mux", "MIC2", "MIC2"},
|
||||
{"ADC 23 Mux", "LINE1", "LINE1"},
|
||||
{"ADC 23 Mux", "LINE2", "LINE2"},
|
||||
{"ADC 23 Mux", "DMIC1", "DMIC1"},
|
||||
{"ADC 23 Mux", "DMIC2", "DMIC2"},
|
||||
{"ADC 23 Mux", "DMIC3", "DMIC3"},
|
||||
{"ADC 23 Mux", "DMIC4", "DMIC4"},
|
||||
{"ADC 24 Mux", "MIC2", "MIC2"},
|
||||
{"ADC 24 Mux", "DMIC1", "DMIC1"},
|
||||
{"ADC 24 Mux", "DMIC2", "DMIC2"},
|
||||
{"ADC 24 Mux", "DMIC3", "DMIC3"},
|
||||
{"ADC 24 Mux", "DMIC4", "DMIC4"},
|
||||
{"ADC 25 Mux", "MIC1", "MIC1"},
|
||||
{"ADC 25 Mux", "DMIC1", "DMIC1"},
|
||||
{"ADC 25 Mux", "DMIC2", "DMIC2"},
|
||||
{"ADC 25 Mux", "DMIC3", "DMIC3"},
|
||||
{"ADC 25 Mux", "DMIC4", "DMIC4"},
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = {
|
||||
.controls = rt715_snd_controls_sdca,
|
||||
.num_controls = ARRAY_SIZE(rt715_snd_controls_sdca),
|
||||
.dapm_widgets = rt715_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets),
|
||||
.dapm_routes = rt715_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(rt715_audio_map),
|
||||
};
|
||||
|
||||
static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
|
||||
int direction)
|
||||
{
|
||||
struct rt715_sdw_stream_data *stream;
|
||||
|
||||
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
|
||||
if (!stream)
|
||||
return -ENOMEM;
|
||||
|
||||
stream->sdw_stream = sdw_stream;
|
||||
|
||||
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dai->playback_dma_data = stream;
|
||||
else
|
||||
dai->capture_dma_data = stream;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rt715_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
|
||||
{
|
||||
struct rt715_sdw_stream_data *stream;
|
||||
|
||||
stream = snd_soc_dai_get_dma_data(dai, substream);
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
snd_soc_dai_set_dma_data(dai, substream, NULL);
|
||||
kfree(stream);
|
||||
}
|
||||
|
||||
static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
struct sdw_stream_config stream_config;
|
||||
struct sdw_port_config port_config;
|
||||
enum sdw_data_direction direction;
|
||||
struct rt715_sdw_stream_data *stream;
|
||||
int retval, port, num_channels;
|
||||
unsigned int val;
|
||||
|
||||
stream = snd_soc_dai_get_dma_data(dai, substream);
|
||||
|
||||
if (!stream)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rt715->slave)
|
||||
return -EINVAL;
|
||||
|
||||
switch (dai->id) {
|
||||
case RT715_AIF1:
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
port = 6;
|
||||
rt715_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL,
|
||||
0xa500);
|
||||
break;
|
||||
case RT715_AIF2:
|
||||
direction = SDW_DATA_DIR_TX;
|
||||
port = 4;
|
||||
rt715_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL,
|
||||
0xaf00);
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Invalid DAI id %d\n", dai->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
stream_config.frame_rate = params_rate(params);
|
||||
stream_config.ch_count = params_channels(params);
|
||||
stream_config.bps = snd_pcm_format_width(params_format(params));
|
||||
stream_config.direction = direction;
|
||||
|
||||
num_channels = params_channels(params);
|
||||
port_config.ch_mask = GENMASK(num_channels - 1, 0);
|
||||
port_config.num = port;
|
||||
|
||||
retval = sdw_stream_add_slave(rt715->slave, &stream_config,
|
||||
&port_config, 1, stream->sdw_stream);
|
||||
if (retval) {
|
||||
dev_err(component->dev, "Unable to configure port, retval:%d\n",
|
||||
retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
switch (params_rate(params)) {
|
||||
case 8000:
|
||||
val = 0x1;
|
||||
break;
|
||||
case 11025:
|
||||
val = 0x2;
|
||||
break;
|
||||
case 12000:
|
||||
val = 0x3;
|
||||
break;
|
||||
case 16000:
|
||||
val = 0x4;
|
||||
break;
|
||||
case 22050:
|
||||
val = 0x5;
|
||||
break;
|
||||
case 24000:
|
||||
val = 0x6;
|
||||
break;
|
||||
case 32000:
|
||||
val = 0x7;
|
||||
break;
|
||||
case 44100:
|
||||
val = 0x8;
|
||||
break;
|
||||
case 48000:
|
||||
val = 0x9;
|
||||
break;
|
||||
case 88200:
|
||||
val = 0xa;
|
||||
break;
|
||||
case 96000:
|
||||
val = 0xb;
|
||||
break;
|
||||
case 176400:
|
||||
val = 0xc;
|
||||
break;
|
||||
case 192000:
|
||||
val = 0xd;
|
||||
break;
|
||||
case 384000:
|
||||
val = 0xe;
|
||||
break;
|
||||
case 768000:
|
||||
val = 0xf;
|
||||
break;
|
||||
default:
|
||||
dev_err(component->dev, "Unsupported sample rate %d\n",
|
||||
params_rate(params));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_write(rt715->regmap,
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CS_FREQ_IND_EN,
|
||||
RT715_SDCA_FREQ_IND_CTRL, CH_00), val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rt715_pcm_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_component *component = dai->component;
|
||||
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
|
||||
struct rt715_sdw_stream_data *stream =
|
||||
snd_soc_dai_get_dma_data(dai, substream);
|
||||
|
||||
if (!rt715->slave)
|
||||
return -EINVAL;
|
||||
|
||||
sdw_stream_remove_slave(rt715->slave, stream->sdw_stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
|
||||
#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
|
||||
|
||||
static struct snd_soc_dai_ops rt715_ops = {
|
||||
.hw_params = rt715_pcm_hw_params,
|
||||
.hw_free = rt715_pcm_hw_free,
|
||||
.set_sdw_stream = rt715_set_sdw_stream,
|
||||
.shutdown = rt715_shutdown,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver rt715_dai[] = {
|
||||
{
|
||||
.name = "rt715-aif1",
|
||||
.id = RT715_AIF1,
|
||||
.capture = {
|
||||
.stream_name = "DP6 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = RT715_STEREO_RATES,
|
||||
.formats = RT715_FORMATS,
|
||||
},
|
||||
.ops = &rt715_ops,
|
||||
},
|
||||
{
|
||||
.name = "rt715-aif2",
|
||||
.id = RT715_AIF2,
|
||||
.capture = {
|
||||
.stream_name = "DP4 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = RT715_STEREO_RATES,
|
||||
.formats = RT715_FORMATS,
|
||||
},
|
||||
.ops = &rt715_ops,
|
||||
},
|
||||
};
|
||||
|
||||
/* Bus clock frequency */
|
||||
#define RT715_CLK_FREQ_9600000HZ 9600000
|
||||
#define RT715_CLK_FREQ_12000000HZ 12000000
|
||||
#define RT715_CLK_FREQ_6000000HZ 6000000
|
||||
#define RT715_CLK_FREQ_4800000HZ 4800000
|
||||
#define RT715_CLK_FREQ_2400000HZ 2400000
|
||||
#define RT715_CLK_FREQ_12288000HZ 12288000
|
||||
|
||||
int rt715_init(struct device *dev, struct regmap *mbq_regmap,
|
||||
struct regmap *regmap, struct sdw_slave *slave)
|
||||
{
|
||||
struct rt715_sdca_priv *rt715;
|
||||
int ret;
|
||||
|
||||
rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL);
|
||||
if (!rt715)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, rt715);
|
||||
rt715->slave = slave;
|
||||
rt715->regmap = regmap;
|
||||
rt715->mbq_regmap = mbq_regmap;
|
||||
rt715->hw_sdw_ver = slave->id.sdw_version;
|
||||
/*
|
||||
* Mark hw_init to false
|
||||
* HW init will be performed when device reports present
|
||||
*/
|
||||
rt715->hw_init = false;
|
||||
rt715->first_init = false;
|
||||
|
||||
ret = devm_snd_soc_register_component(dev,
|
||||
&soc_codec_dev_rt715_sdca,
|
||||
rt715_dai,
|
||||
ARRAY_SIZE(rt715_dai));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rt715_io_init(struct device *dev, struct sdw_slave *slave)
|
||||
{
|
||||
struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
|
||||
unsigned int hw_ver;
|
||||
|
||||
if (rt715->hw_init)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* PM runtime is only enabled when a Slave reports as Attached
|
||||
*/
|
||||
if (!rt715->first_init) {
|
||||
/* set autosuspend parameters */
|
||||
pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
|
||||
pm_runtime_use_autosuspend(&slave->dev);
|
||||
|
||||
/* update count of parent 'active' children */
|
||||
pm_runtime_set_active(&slave->dev);
|
||||
|
||||
/* make sure the device does not suspend immediately */
|
||||
pm_runtime_mark_last_busy(&slave->dev);
|
||||
|
||||
pm_runtime_enable(&slave->dev);
|
||||
|
||||
rt715->first_init = true;
|
||||
}
|
||||
|
||||
pm_runtime_get_noresume(&slave->dev);
|
||||
|
||||
rt715_index_read(rt715, RT715_VENDOR_REG,
|
||||
RT715_PRODUCT_NUM, &hw_ver);
|
||||
hw_ver = hw_ver & 0x000f;
|
||||
|
||||
/* set clock selector = external */
|
||||
regmap_write(rt715->regmap,
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN,
|
||||
RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1);
|
||||
/* set GPIO_4/5/6 to be 3rd/4th DMIC usage */
|
||||
if (hw_ver == 0x0)
|
||||
rt715_index_update_bits(rt715, RT715_VENDOR_REG,
|
||||
RT715_AD_FUNC_EN, 0x54, 0x54);
|
||||
else if (hw_ver == 0x1) {
|
||||
rt715_index_update_bits(rt715, RT715_VENDOR_REG,
|
||||
RT715_AD_FUNC_EN, 0x55, 0x55);
|
||||
rt715_index_update_bits(rt715, RT715_VENDOR_REG,
|
||||
RT715_REV_1, 0x40, 0x40);
|
||||
}
|
||||
/* trigger mode = VAD enable */
|
||||
regmap_write(rt715->regmap,
|
||||
SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
|
||||
RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x2);
|
||||
/* SMPU-1 interrupt enable mask */
|
||||
regmap_update_bits(rt715->regmap, RT715_INT_MASK, 0x1, 0x1);
|
||||
|
||||
/* Mark Slave initialization complete */
|
||||
rt715->hw_init = true;
|
||||
|
||||
pm_runtime_mark_last_busy(&slave->dev);
|
||||
pm_runtime_put_autosuspend(&slave->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA");
|
||||
MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -1,124 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* rt715-sdca.h -- RT715 ALSA SoC audio driver header
|
||||
*
|
||||
* Copyright(c) 2020 Realtek Semiconductor Corp.
|
||||
*/
|
||||
|
||||
#ifndef __RT715_SDCA_H__
|
||||
#define __RT715_SDCA_H__
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/soundwire/sdw.h>
|
||||
#include <linux/soundwire/sdw_type.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
struct rt715_sdca_priv {
|
||||
struct regmap *regmap;
|
||||
struct regmap *mbq_regmap;
|
||||
struct snd_soc_codec *codec;
|
||||
struct sdw_slave *slave;
|
||||
struct delayed_work adc_mute_work;
|
||||
int dbg_nid;
|
||||
int dbg_vid;
|
||||
int dbg_payload;
|
||||
enum sdw_slave_status status;
|
||||
struct sdw_bus_params params;
|
||||
bool hw_init;
|
||||
bool first_init;
|
||||
int l_is_unmute;
|
||||
int r_is_unmute;
|
||||
int hw_sdw_ver;
|
||||
};
|
||||
|
||||
struct rt715_sdw_stream_data {
|
||||
struct sdw_stream_runtime *sdw_stream;
|
||||
};
|
||||
|
||||
/* MIPI Register */
|
||||
#define RT715_INT_CTRL 0x005a
|
||||
#define RT715_INT_MASK 0x005e
|
||||
|
||||
/* NID */
|
||||
#define RT715_AUDIO_FUNCTION_GROUP 0x01
|
||||
#define RT715_MIC_ADC 0x07
|
||||
#define RT715_LINE_ADC 0x08
|
||||
#define RT715_MIX_ADC 0x09
|
||||
#define RT715_DMIC1 0x12
|
||||
#define RT715_DMIC2 0x13
|
||||
#define RT715_MIC1 0x18
|
||||
#define RT715_MIC2 0x19
|
||||
#define RT715_LINE1 0x1a
|
||||
#define RT715_LINE2 0x1b
|
||||
#define RT715_DMIC3 0x1d
|
||||
#define RT715_DMIC4 0x29
|
||||
#define RT715_VENDOR_REG 0x20
|
||||
#define RT715_MUX_IN1 0x22
|
||||
#define RT715_MUX_IN2 0x23
|
||||
#define RT715_MUX_IN3 0x24
|
||||
#define RT715_MUX_IN4 0x25
|
||||
#define RT715_MIX_ADC2 0x27
|
||||
#define RT715_INLINE_CMD 0x55
|
||||
#define RT715_VENDOR_HDA_CTL 0x61
|
||||
|
||||
/* Index (NID:20h) */
|
||||
#define RT715_PRODUCT_NUM 0x0
|
||||
#define RT715_IRQ_CTRL 0x2b
|
||||
#define RT715_AD_FUNC_EN 0x36
|
||||
#define RT715_REV_1 0x37
|
||||
#define RT715_SDW_INPUT_SEL 0x39
|
||||
#define RT715_EXT_DMIC_CLK_CTRL2 0x54
|
||||
|
||||
/* Index (NID:61h) */
|
||||
#define RT715_HDA_LEGACY_MUX_CTL1 0x00
|
||||
|
||||
/* SDCA (Function) */
|
||||
#define FUN_JACK_CODEC 0x01
|
||||
#define FUN_MIC_ARRAY 0x02
|
||||
#define FUN_HID 0x03
|
||||
/* SDCA (Entity) */
|
||||
#define RT715_SDCA_ST_EN 0x00
|
||||
#define RT715_SDCA_CS_FREQ_IND_EN 0x01
|
||||
#define RT715_SDCA_FU_ADC8_9_VOL 0x02
|
||||
#define RT715_SDCA_SMPU_TRIG_ST_EN 0x05
|
||||
#define RT715_SDCA_FU_ADC10_11_VOL 0x06
|
||||
#define RT715_SDCA_FU_ADC7_27_VOL 0x0a
|
||||
#define RT715_SDCA_FU_AMIC_GAIN_EN 0x0c
|
||||
#define RT715_SDCA_FU_DMIC_GAIN_EN 0x0e
|
||||
#define RT715_SDCA_CX_CLK_SEL_EN 0x10
|
||||
#define RT715_SDCA_CREQ_POW_EN 0x18
|
||||
/* SDCA (Control) */
|
||||
#define RT715_SDCA_ST_CTRL 0x00
|
||||
#define RT715_SDCA_CX_CLK_SEL_CTRL 0x01
|
||||
#define RT715_SDCA_REQ_POW_CTRL 0x01
|
||||
#define RT715_SDCA_FU_MUTE_CTRL 0x01
|
||||
#define RT715_SDCA_FU_VOL_CTRL 0x02
|
||||
#define RT715_SDCA_FU_DMIC_GAIN_CTRL 0x0b
|
||||
#define RT715_SDCA_FREQ_IND_CTRL 0x10
|
||||
#define RT715_SDCA_SMPU_TRIG_EN_CTRL 0x10
|
||||
#define RT715_SDCA_SMPU_TRIG_ST_CTRL 0x11
|
||||
/* SDCA (Channel) */
|
||||
#define CH_00 0x00
|
||||
#define CH_01 0x01
|
||||
#define CH_02 0x02
|
||||
#define CH_03 0x03
|
||||
#define CH_04 0x04
|
||||
#define CH_05 0x05
|
||||
#define CH_06 0x06
|
||||
#define CH_07 0x07
|
||||
#define CH_08 0x08
|
||||
|
||||
#define RT715_SDCA_DB_STEP 375
|
||||
|
||||
enum {
|
||||
RT715_AIF1,
|
||||
RT715_AIF2,
|
||||
};
|
||||
|
||||
int rt715_io_init(struct device *dev, struct sdw_slave *slave);
|
||||
int rt715_init(struct device *dev, struct regmap *mbq_regmap,
|
||||
struct regmap *regmap, struct sdw_slave *slave);
|
||||
|
||||
#endif /* __RT715_SDCA_H__ */
|
||||
Reference in New Issue
Block a user