Merge 4520dcbe0d
("Merge tag 'for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply") into android-mainline
Steps on the way to 5.15-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: Ia88ca4292f7c791a2c9a0b256584969fa86a06c4
This commit is contained in:
commit
7c647e3d19
|
@ -19,7 +19,6 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- ibm,fsi2spi
|
||||
- ibm,fsi2spi-restricted
|
||||
|
||||
reg:
|
||||
items:
|
||||
|
|
|
@ -31,6 +31,20 @@ properties:
|
|||
compatible:
|
||||
const: simple-battery
|
||||
|
||||
device-chemistry:
|
||||
description: This describes the chemical technology of the battery.
|
||||
oneOf:
|
||||
- const: nickel-cadmium
|
||||
- const: nickel-metal-hydride
|
||||
- const: lithium-ion
|
||||
description: This is a blanket type for all lithium-ion batteries,
|
||||
including those below. If possible, a precise compatible string
|
||||
from below should be used, but sometimes it is unknown which specific
|
||||
lithium ion battery is employed and this wide compatible can be used.
|
||||
- const: lithium-ion-polymer
|
||||
- const: lithium-ion-iron-phosphate
|
||||
- const: lithium-ion-manganese-oxide
|
||||
|
||||
over-voltage-threshold-microvolt:
|
||||
description: battery over-voltage limit
|
||||
|
||||
|
|
|
@ -19,12 +19,15 @@ properties:
|
|||
- maxim,max17047
|
||||
- maxim,max17050
|
||||
- maxim,max17055
|
||||
- maxim,max77849-battery
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: |
|
||||
The ALRT pin, an open-drain interrupt.
|
||||
|
||||
maxim,rsns-microohm:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/power/supply/mt6360_charger.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Battery charger driver for MT6360 PMIC from MediaTek Integrated.
|
||||
|
||||
maintainers:
|
||||
- Gene Chen <gene_chen@richtek.com>
|
||||
|
||||
description: |
|
||||
This module is part of the MT6360 MFD device.
|
||||
Provides Battery Charger, Boost for OTG devices and BC1.2 detection.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mediatek,mt6360-chg
|
||||
|
||||
richtek,vinovp-microvolt:
|
||||
description: Maximum CHGIN regulation voltage in uV.
|
||||
enum: [ 5500000, 6500000, 11000000, 14500000 ]
|
||||
|
||||
|
||||
usb-otg-vbus-regulator:
|
||||
type: object
|
||||
description: OTG boost regulator.
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
mt6360_charger: charger {
|
||||
compatible = "mediatek,mt6360-chg";
|
||||
richtek,vinovp-microvolt = <14500000>;
|
||||
|
||||
otg_vbus_regulator: usb-otg-vbus-regulator {
|
||||
regulator-compatible = "usb-otg-vbus";
|
||||
regulator-name = "usb-otg-vbus";
|
||||
regulator-min-microvolt = <4425000>;
|
||||
regulator-max-microvolt = <5825000>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -73,6 +73,26 @@ properties:
|
|||
- 1 # SMB3XX_SOFT_TEMP_COMPENSATE_CURRENT Current compensation
|
||||
- 2 # SMB3XX_SOFT_TEMP_COMPENSATE_VOLTAGE Voltage compensation
|
||||
|
||||
summit,inok-polarity:
|
||||
description: |
|
||||
Polarity of INOK signal indicating presence of external power supply.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum:
|
||||
- 0 # SMB3XX_SYSOK_INOK_ACTIVE_LOW
|
||||
- 1 # SMB3XX_SYSOK_INOK_ACTIVE_HIGH
|
||||
|
||||
usb-vbus:
|
||||
$ref: "../../regulator/regulator.yaml#"
|
||||
type: object
|
||||
|
||||
properties:
|
||||
summit,needs-inok-toggle:
|
||||
type: boolean
|
||||
description: INOK signal is fixed and polarity needs to be toggled
|
||||
in order to enable/disable output mode.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
|
@ -134,6 +154,7 @@ examples:
|
|||
reg = <0x7f>;
|
||||
|
||||
summit,enable-charge-control = <SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH>;
|
||||
summit,inok-polarity = <SMB3XX_SYSOK_INOK_ACTIVE_LOW>;
|
||||
summit,chip-temperature-threshold-celsius = <110>;
|
||||
summit,mains-current-limit-microamp = <2000000>;
|
||||
summit,usb-current-limit-microamp = <500000>;
|
||||
|
@ -141,6 +162,15 @@ examples:
|
|||
summit,enable-mains-charging;
|
||||
|
||||
monitored-battery = <&battery>;
|
||||
|
||||
usb-vbus {
|
||||
regulator-name = "usb_vbus";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-min-microamp = <750000>;
|
||||
regulator-max-microamp = <750000>;
|
||||
summit,needs-inok-toggle;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -21,10 +21,13 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- x-powers,axp202-ac-power-supply
|
||||
- x-powers,axp221-ac-power-supply
|
||||
- x-powers,axp813-ac-power-supply
|
||||
oneOf:
|
||||
- const: x-powers,axp202-ac-power-supply
|
||||
- const: x-powers,axp221-ac-power-supply
|
||||
- items:
|
||||
- const: x-powers,axp803-ac-power-supply
|
||||
- const: x-powers,axp813-ac-power-supply
|
||||
- const: x-powers,axp813-ac-power-supply
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
@ -19,10 +19,14 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- x-powers,axp209-battery-power-supply
|
||||
- x-powers,axp221-battery-power-supply
|
||||
- x-powers,axp813-battery-power-supply
|
||||
oneOf:
|
||||
- const: x-powers,axp202-battery-power-supply
|
||||
- const: x-powers,axp209-battery-power-supply
|
||||
- const: x-powers,axp221-battery-power-supply
|
||||
- items:
|
||||
- const: x-powers,axp803-battery-power-supply
|
||||
- const: x-powers,axp813-battery-power-supply
|
||||
- const: x-powers,axp813-battery-power-supply
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
@ -20,11 +20,15 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- x-powers,axp202-usb-power-supply
|
||||
- x-powers,axp221-usb-power-supply
|
||||
- x-powers,axp223-usb-power-supply
|
||||
- x-powers,axp813-usb-power-supply
|
||||
oneOf:
|
||||
- enum:
|
||||
- x-powers,axp202-usb-power-supply
|
||||
- x-powers,axp221-usb-power-supply
|
||||
- x-powers,axp223-usb-power-supply
|
||||
- x-powers,axp813-usb-power-supply
|
||||
- items:
|
||||
- const: x-powers,axp803-usb-power-supply
|
||||
- const: x-powers,axp813-usb-power-supply
|
||||
|
||||
|
||||
required:
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/richtek,rtq2134-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Richtek RTQ2134 SubPMIC Regulator
|
||||
|
||||
maintainers:
|
||||
- ChiYuan Huang <cy_huang@richtek.com>
|
||||
|
||||
description: |
|
||||
The RTQ2134 is a multi-phase, programmable power management IC that
|
||||
integrates with four high efficient, synchronous step-down converter cores.
|
||||
|
||||
Datasheet is available at
|
||||
https://www.richtek.com/assets/product_file/RTQ2134-QA/DSQ2134-QA-01.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- richtek,rtq2134
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
|
||||
patternProperties:
|
||||
"^buck[1-3]$":
|
||||
type: object
|
||||
$ref: regulator.yaml#
|
||||
description: |
|
||||
regulator description for buck[1-3].
|
||||
|
||||
properties:
|
||||
richtek,use-vsel-dvs:
|
||||
type: boolean
|
||||
description: |
|
||||
If specified, buck will listen to 'vsel' pin for dvs config.
|
||||
Else, use dvs0 voltage by default.
|
||||
|
||||
richtek,uv-shutdown:
|
||||
type: boolean
|
||||
description: |
|
||||
If specified, use shutdown as UV action. Else, hiccup by default.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtq2134@18 {
|
||||
compatible = "richtek,rtq2134";
|
||||
reg = <0x18>;
|
||||
|
||||
regulators {
|
||||
buck1 {
|
||||
regulator-name = "rtq2134-buck1";
|
||||
regulator-min-microvolt = <300000>;
|
||||
regulator-max-microvolt = <1850000>;
|
||||
regulator-always-on;
|
||||
richtek,use-vsel-dvs;
|
||||
regulator-state-mem {
|
||||
regulator-suspend-min-microvolt = <550000>;
|
||||
regulator-suspend-max-microvolt = <550000>;
|
||||
};
|
||||
};
|
||||
buck2 {
|
||||
regulator-name = "rtq2134-buck2";
|
||||
regulator-min-microvolt = <1120000>;
|
||||
regulator-max-microvolt = <1120000>;
|
||||
regulator-always-on;
|
||||
richtek,use-vsel-dvs;
|
||||
regulator-state-mem {
|
||||
regulator-suspend-min-microvolt = <1120000>;
|
||||
regulator-suspend-max-microvolt = <1120000>;
|
||||
};
|
||||
};
|
||||
buck3 {
|
||||
regulator-name = "rtq2134-buck3";
|
||||
regulator-min-microvolt = <600000>;
|
||||
regulator-max-microvolt = <600000>;
|
||||
regulator-always-on;
|
||||
richtek,use-vsel-dvs;
|
||||
regulator-state-mem {
|
||||
regulator-suspend-min-microvolt = <600000>;
|
||||
regulator-suspend-max-microvolt = <600000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,76 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/richtek,rtq6752-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Richtek RTQ6752 TFT LCD Voltage Regulator
|
||||
|
||||
maintainers:
|
||||
- ChiYuan Huang <cy_huang@richtek.com>
|
||||
|
||||
description: |
|
||||
The RTQ6752 is an I2C interface pgorammable power management IC. It includes
|
||||
two synchronous boost converter for PAVDD, and one synchronous NAVDD
|
||||
buck-boost. The device is suitable for automotive TFT-LCD panel.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- richtek,rtq6752
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
enable-gpios:
|
||||
description: |
|
||||
A connection of the chip 'enable' gpio line. If not provided, treat it as
|
||||
external pull up.
|
||||
maxItems: 1
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
|
||||
patternProperties:
|
||||
"^(p|n)avdd$":
|
||||
type: object
|
||||
$ref: regulator.yaml#
|
||||
description: |
|
||||
regulator description for pavdd and navdd.
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtq6752@6b {
|
||||
compatible = "richtek,rtq6752";
|
||||
reg = <0x6b>;
|
||||
enable-gpios = <&gpio26 2 0>;
|
||||
|
||||
regulators {
|
||||
pavdd {
|
||||
regulator-name = "rtq6752-pavdd";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <7300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
navdd {
|
||||
regulator-name = "rtq6752-navdd";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <7300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,85 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/socionext,uniphier-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Socionext UniPhier regulator controller
|
||||
|
||||
description: |
|
||||
This regulator controls VBUS and belongs to USB3 glue layer. Before using
|
||||
the regulator, it is necessary to control the clocks and resets to enable
|
||||
this layer. These clocks and resets should be described in each property.
|
||||
|
||||
maintainers:
|
||||
- Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
|
||||
|
||||
allOf:
|
||||
- $ref: "regulator.yaml#"
|
||||
|
||||
# USB3 Controller
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- socionext,uniphier-pro4-usb3-regulator
|
||||
- socionext,uniphier-pro5-usb3-regulator
|
||||
- socionext,uniphier-pxs2-usb3-regulator
|
||||
- socionext,uniphier-ld20-usb3-regulator
|
||||
- socionext,uniphier-pxs3-usb3-regulator
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
oneOf:
|
||||
- items: # for Pro4, Pro5
|
||||
- const: gio
|
||||
- const: link
|
||||
- items: # for others
|
||||
- const: link
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
oneOf:
|
||||
- items: # for Pro4, Pro5
|
||||
- const: gio
|
||||
- const: link
|
||||
- items:
|
||||
- const: link
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
usb-glue@65b00000 {
|
||||
compatible = "simple-mfd";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0x65b00000 0x400>;
|
||||
|
||||
usb_vbus0: regulators@100 {
|
||||
compatible = "socionext,uniphier-ld20-usb3-regulator";
|
||||
reg = <0x100 0x10>;
|
||||
clock-names = "link";
|
||||
clocks = <&sys_clk 14>;
|
||||
reset-names = "link";
|
||||
resets = <&sys_rst 14>;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
Socionext UniPhier Regulator Controller
|
||||
|
||||
This describes the devicetree bindings for regulator controller implemented
|
||||
on Socionext UniPhier SoCs.
|
||||
|
||||
USB3 Controller
|
||||
---------------
|
||||
|
||||
This regulator controls VBUS and belongs to USB3 glue layer. Before using
|
||||
the regulator, it is necessary to control the clocks and resets to enable
|
||||
this layer. These clocks and resets should be described in each property.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be
|
||||
"socionext,uniphier-pro4-usb3-regulator" - for Pro4 SoC
|
||||
"socionext,uniphier-pro5-usb3-regulator" - for Pro5 SoC
|
||||
"socionext,uniphier-pxs2-usb3-regulator" - for PXs2 SoC
|
||||
"socionext,uniphier-ld20-usb3-regulator" - for LD20 SoC
|
||||
"socionext,uniphier-pxs3-usb3-regulator" - for PXs3 SoC
|
||||
- reg: Specifies offset and length of the register set for the device.
|
||||
- clocks: A list of phandles to the clock gate for USB3 glue layer.
|
||||
According to the clock-names, appropriate clocks are required.
|
||||
- clock-names: Should contain
|
||||
"gio", "link" - for Pro4 and Pro5 SoCs
|
||||
"link" - for others
|
||||
- resets: A list of phandles to the reset control for USB3 glue layer.
|
||||
According to the reset-names, appropriate resets are required.
|
||||
- reset-names: Should contain
|
||||
"gio", "link" - for Pro4 and Pro5 SoCs
|
||||
"link" - for others
|
||||
|
||||
See Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
for more details about the regulator properties.
|
||||
|
||||
Example:
|
||||
|
||||
usb-glue@65b00000 {
|
||||
compatible = "socionext,uniphier-ld20-dwc3-glue",
|
||||
"simple-mfd";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0x65b00000 0x400>;
|
||||
|
||||
usb_vbus0: regulators@100 {
|
||||
compatible = "socionext,uniphier-ld20-usb3-regulator";
|
||||
reg = <0x100 0x10>;
|
||||
clock-names = "link";
|
||||
clocks = <&sys_clk 14>;
|
||||
reset-names = "link";
|
||||
resets = <&sys_rst 14>;
|
||||
};
|
||||
|
||||
phy {
|
||||
...
|
||||
phy-supply = <&usb_vbus0>;
|
||||
};
|
||||
...
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
OMAP2+ McSPI device
|
||||
|
||||
Required properties:
|
||||
- compatible :
|
||||
- "ti,am654-mcspi" for AM654.
|
||||
- "ti,omap2-mcspi" for OMAP2 & OMAP3.
|
||||
- "ti,omap4-mcspi" for OMAP4+.
|
||||
- ti,spi-num-cs : Number of chipselect supported by the instance.
|
||||
- ti,hwmods: Name of the hwmod associated to the McSPI
|
||||
- ti,pindir-d0-out-d1-in: Select the D0 pin as output and D1 as
|
||||
input. The default is D0 as input and
|
||||
D1 as output.
|
||||
|
||||
Optional properties:
|
||||
- dmas: List of DMA specifiers with the controller specific format
|
||||
as described in the generic DMA client binding. A tx and rx
|
||||
specifier is required for each chip select.
|
||||
- dma-names: List of DMA request names. These strings correspond
|
||||
1:1 with the DMA specifiers listed in dmas. The string naming
|
||||
is to be "rxN" and "txN" for RX and TX requests,
|
||||
respectively, where N equals the chip select number.
|
||||
|
||||
Examples:
|
||||
|
||||
[hwmod populated DMA resources]
|
||||
|
||||
mcspi1: mcspi@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "ti,omap4-mcspi";
|
||||
ti,hwmods = "mcspi1";
|
||||
ti,spi-num-cs = <4>;
|
||||
};
|
||||
|
||||
[generic DMA request binding]
|
||||
|
||||
mcspi1: mcspi@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "ti,omap4-mcspi";
|
||||
ti,hwmods = "mcspi1";
|
||||
ti,spi-num-cs = <2>;
|
||||
dmas = <&edma 42
|
||||
&edma 43
|
||||
&edma 44
|
||||
&edma 45>;
|
||||
dma-names = "tx0", "rx0", "tx1", "rx1";
|
||||
};
|
|
@ -0,0 +1,117 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/omap-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: SPI controller bindings for OMAP and K3 SoCs
|
||||
|
||||
maintainers:
|
||||
- Aswath Govindraju <a-govindraju@ti.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- ti,am654-mcspi
|
||||
- ti,am4372-mcspi
|
||||
- const: ti,omap4-mcspi
|
||||
- items:
|
||||
- enum:
|
||||
- ti,omap2-mcspi
|
||||
- ti,omap4-mcspi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
ti,spi-num-cs:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Number of chipselect supported by the instance.
|
||||
minimum: 1
|
||||
maximum: 4
|
||||
|
||||
ti,hwmods:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
Must be "mcspi<n>", n being the instance number (1-based).
|
||||
This property is applicable only on legacy platforms mainly omap2/3
|
||||
and ti81xx and should not be used on other platforms.
|
||||
deprecated: true
|
||||
|
||||
ti,pindir-d0-out-d1-in:
|
||||
description:
|
||||
Select the D0 pin as output and D1 as input. The default is D0
|
||||
as input and D1 as output.
|
||||
type: boolean
|
||||
|
||||
dmas:
|
||||
description:
|
||||
List of DMA specifiers with the controller specific format as
|
||||
described in the generic DMA client binding. A tx and rx
|
||||
specifier is required for each chip select.
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
|
||||
dma-names:
|
||||
description:
|
||||
List of DMA request names. These strings correspond 1:1 with
|
||||
the DMA sepecifiers listed in dmas. The string names is to be
|
||||
"rxN" and "txN" for RX and TX requests, respectively. Where N
|
||||
is the chip select number.
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: ti,omap2-mcspi
|
||||
- const: ti,omap4-mcspi
|
||||
|
||||
then:
|
||||
properties:
|
||||
ti,hwmods:
|
||||
items:
|
||||
- pattern: "^mcspi([1-9])$"
|
||||
|
||||
else:
|
||||
properties:
|
||||
ti,hwmods: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/soc/ti,sci_pm_domain.h>
|
||||
|
||||
spi@2100000 {
|
||||
compatible = "ti,am654-mcspi","ti,omap4-mcspi";
|
||||
reg = <0x2100000 0x400>;
|
||||
interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&k3_clks 137 1>;
|
||||
power-domains = <&k3_pds 137 TI_SCI_PD_EXCLUSIVE>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
dmas = <&main_udmap 0xc500>, <&main_udmap 0x4500>;
|
||||
dma-names = "tx0", "rx0";
|
||||
};
|
|
@ -0,0 +1,91 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/rockchip-sfc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Rockchip Serial Flash Controller (SFC)
|
||||
|
||||
maintainers:
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
- Chris Morgan <macromorgan@hotmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: rockchip,sfc
|
||||
description:
|
||||
The rockchip sfc controller is a standalone IP with version register,
|
||||
and the driver can handle all the feature difference inside the IP
|
||||
depending on the version register.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: clk_sfc
|
||||
- const: hclk_sfc
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
rockchip,sfc-no-dma:
|
||||
description: Disable DMA and utilize FIFO mode only
|
||||
type: boolean
|
||||
|
||||
patternProperties:
|
||||
"^flash@[0-3]$":
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/px30-cru.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/power/px30-power.h>
|
||||
|
||||
sfc: spi@ff3a0000 {
|
||||
compatible = "rockchip,sfc";
|
||||
reg = <0xff3a0000 0x4000>;
|
||||
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru SCLK_SFC>, <&cru HCLK_SFC>;
|
||||
clock-names = "clk_sfc", "hclk_sfc";
|
||||
pinctrl-0 = <&sfc_clk &sfc_cs &sfc_bus2>;
|
||||
pinctrl-names = "default";
|
||||
power-domains = <&power PX30_PD_MMC_NAND>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
flash@0 {
|
||||
compatible = "jedec,spi-nor";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <108000000>;
|
||||
spi-rx-bus-width = <2>;
|
||||
spi-tx-bus-width = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -11,6 +11,7 @@ Required properties:
|
|||
- mediatek,mt8135-spi: for mt8135 platforms
|
||||
- mediatek,mt8173-spi: for mt8173 platforms
|
||||
- mediatek,mt8183-spi: for mt8183 platforms
|
||||
- mediatek,mt6893-spi: for mt6893 platforms
|
||||
- "mediatek,mt8192-spi", "mediatek,mt6765-spi": for mt8192 platforms
|
||||
- "mediatek,mt8195-spi", "mediatek,mt6765-spi": for mt8195 platforms
|
||||
- "mediatek,mt8516-spi", "mediatek,mt2712-spi": for mt8516 platforms
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
Spreadtrum ADI controller
|
||||
|
||||
ADI is the abbreviation of Anolog-Digital interface, which is used to access
|
||||
analog chip (such as PMIC) from digital chip. ADI controller follows the SPI
|
||||
framework for its hardware implementation is alike to SPI bus and its timing
|
||||
is compatile to SPI timing.
|
||||
|
||||
ADI controller has 50 channels including 2 software read/write channels and
|
||||
48 hardware channels to access analog chip. For 2 software read/write channels,
|
||||
users should set ADI registers to access analog chip. For hardware channels,
|
||||
we can configure them to allow other hardware components to use it independently,
|
||||
which means we can just link one analog chip address to one hardware channel,
|
||||
then users can access the mapped analog chip address by this hardware channel
|
||||
triggered by hardware components instead of ADI software channels.
|
||||
|
||||
Thus we introduce one property named "sprd,hw-channels" to configure hardware
|
||||
channels, the first value specifies the hardware channel id which is used to
|
||||
transfer data triggered by hardware automatically, and the second value specifies
|
||||
the analog chip address where user want to access by hardware components.
|
||||
|
||||
Since we have multi-subsystems will use unique ADI to access analog chip, when
|
||||
one system is reading/writing data by ADI software channels, that should be under
|
||||
one hardware spinlock protection to prevent other systems from reading/writing
|
||||
data by ADI software channels at the same time, or two parallel routine of setting
|
||||
ADI registers will make ADI controller registers chaos to lead incorrect results.
|
||||
Then we need one hardware spinlock to synchronize between the multiple subsystems.
|
||||
|
||||
The new version ADI controller supplies multiple master channels for different
|
||||
subsystem accessing, that means no need to add hardware spinlock to synchronize,
|
||||
thus change the hardware spinlock support to be optional to keep backward
|
||||
compatibility.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sprd,sc9860-adi".
|
||||
- reg: Offset and length of ADI-SPI controller register space.
|
||||
- #address-cells: Number of cells required to define a chip select address
|
||||
on the ADI-SPI bus. Should be set to 1.
|
||||
- #size-cells: Size of cells required to define a chip select address size
|
||||
on the ADI-SPI bus. Should be set to 0.
|
||||
|
||||
Optional properties:
|
||||
- hwlocks: Reference to a phandle of a hwlock provider node.
|
||||
- hwlock-names: Reference to hwlock name strings defined in the same order
|
||||
as the hwlocks, should be "adi".
|
||||
- sprd,hw-channels: This is an array of channel values up to 49 channels.
|
||||
The first value specifies the hardware channel id which is used to
|
||||
transfer data triggered by hardware automatically, and the second
|
||||
value specifies the analog chip address where user want to access
|
||||
by hardware components.
|
||||
|
||||
SPI slave nodes must be children of the SPI controller node and can contain
|
||||
properties described in Documentation/devicetree/bindings/spi/spi-bus.txt.
|
||||
|
||||
Example:
|
||||
adi_bus: spi@40030000 {
|
||||
compatible = "sprd,sc9860-adi";
|
||||
reg = <0 0x40030000 0 0x10000>;
|
||||
hwlocks = <&hwlock1 0>;
|
||||
hwlock-names = "adi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
sprd,hw-channels = <30 0x8c20>;
|
||||
};
|
|
@ -0,0 +1,104 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/spi/sprd,spi-adi.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Spreadtrum ADI controller
|
||||
|
||||
maintainers:
|
||||
- Orson Zhai <orsonzhai@gmail.com>
|
||||
- Baolin Wang <baolin.wang7@gmail.com>
|
||||
- Chunyan Zhang <zhang.lyra@gmail.com>
|
||||
|
||||
description: |
|
||||
ADI is the abbreviation of Anolog-Digital interface, which is used to access
|
||||
analog chip (such as PMIC) from digital chip. ADI controller follows the SPI
|
||||
framework for its hardware implementation is alike to SPI bus and its timing
|
||||
is compatile to SPI timing.
|
||||
|
||||
ADI controller has 50 channels including 2 software read/write channels and
|
||||
48 hardware channels to access analog chip. For 2 software read/write channels,
|
||||
users should set ADI registers to access analog chip. For hardware channels,
|
||||
we can configure them to allow other hardware components to use it independently,
|
||||
which means we can just link one analog chip address to one hardware channel,
|
||||
then users can access the mapped analog chip address by this hardware channel
|
||||
triggered by hardware components instead of ADI software channels.
|
||||
|
||||
Thus we introduce one property named "sprd,hw-channels" to configure hardware
|
||||
channels, the first value specifies the hardware channel id which is used to
|
||||
transfer data triggered by hardware automatically, and the second value specifies
|
||||
the analog chip address where user want to access by hardware components.
|
||||
|
||||
Since we have multi-subsystems will use unique ADI to access analog chip, when
|
||||
one system is reading/writing data by ADI software channels, that should be under
|
||||
one hardware spinlock protection to prevent other systems from reading/writing
|
||||
data by ADI software channels at the same time, or two parallel routine of setting
|
||||
ADI registers will make ADI controller registers chaos to lead incorrect results.
|
||||
Then we need one hardware spinlock to synchronize between the multiple subsystems.
|
||||
|
||||
The new version ADI controller supplies multiple master channels for different
|
||||
subsystem accessing, that means no need to add hardware spinlock to synchronize,
|
||||
thus change the hardware spinlock support to be optional to keep backward
|
||||
compatibility.
|
||||
|
||||
allOf:
|
||||
- $ref: /spi/spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sprd,sc9860-adi
|
||||
- sprd,sc9863-adi
|
||||
- sprd,ums512-adi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
hwlocks:
|
||||
maxItems: 1
|
||||
|
||||
hwlock-names:
|
||||
const: adi
|
||||
|
||||
sprd,hw-channels:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
description: A list of hardware channels
|
||||
minItems: 1
|
||||
maxItems: 48
|
||||
items:
|
||||
items:
|
||||
- description: The hardware channel id which is used to transfer data
|
||||
triggered by hardware automatically, channel id 0-1 are for software
|
||||
use, 2-49 are hardware channels.
|
||||
minimum: 2
|
||||
maximum: 49
|
||||
- description: The analog chip address where user want to access by
|
||||
hardware components.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
aon {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
adi_bus: spi@40030000 {
|
||||
compatible = "sprd,sc9860-adi";
|
||||
reg = <0 0x40030000 0 0x10000>;
|
||||
hwlocks = <&hwlock1 0>;
|
||||
hwlock-names = "adi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
sprd,hw-channels = <30 0x8c20>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -271,19 +271,19 @@ prototypes::
|
|||
locking rules:
|
||||
All except set_page_dirty and freepage may block
|
||||
|
||||
====================== ======================== =========
|
||||
ops PageLocked(page) i_rwsem
|
||||
====================== ======================== =========
|
||||
====================== ======================== ========= ===============
|
||||
ops PageLocked(page) i_rwsem invalidate_lock
|
||||
====================== ======================== ========= ===============
|
||||
writepage: yes, unlocks (see below)
|
||||
readpage: yes, unlocks
|
||||
readpage: yes, unlocks shared
|
||||
writepages:
|
||||
set_page_dirty no
|
||||
readahead: yes, unlocks
|
||||
readpages: no
|
||||
readahead: yes, unlocks shared
|
||||
readpages: no shared
|
||||
write_begin: locks the page exclusive
|
||||
write_end: yes, unlocks exclusive
|
||||
bmap:
|
||||
invalidatepage: yes
|
||||
invalidatepage: yes exclusive
|
||||
releasepage: yes
|
||||
freepage: yes
|
||||
direct_IO:
|
||||
|
@ -295,7 +295,7 @@ is_partially_uptodate: yes
|
|||
error_remove_page: yes
|
||||
swap_activate: no
|
||||
swap_deactivate: no
|
||||
====================== ======================== =========
|
||||
====================== ======================== ========= ===============
|
||||
|
||||
->write_begin(), ->write_end() and ->readpage() may be called from
|
||||
the request handler (/dev/loop).
|
||||
|
@ -378,7 +378,10 @@ keep it that way and don't breed new callers.
|
|||
->invalidatepage() is called when the filesystem must attempt to drop
|
||||
some or all of the buffers from the page when it is being truncated. It
|
||||
returns zero on success. If ->invalidatepage is zero, the kernel uses
|
||||
block_invalidatepage() instead.
|
||||
block_invalidatepage() instead. The filesystem must exclusively acquire
|
||||
invalidate_lock before invalidating page cache in truncate / hole punch path
|
||||
(and thus calling into ->invalidatepage) to block races between page cache
|
||||
invalidation and page cache filling functions (fault, read, ...).
|
||||
|
||||
->releasepage() is called when the kernel is about to try to drop the
|
||||
buffers from the page in preparation for freeing it. It returns zero to
|
||||
|
@ -506,6 +509,7 @@ prototypes::
|
|||
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
|
||||
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
|
||||
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
|
||||
int (*iopoll) (struct kiocb *kiocb, bool spin);
|
||||
int (*iterate) (struct file *, struct dir_context *);
|
||||
int (*iterate_shared) (struct file *, struct dir_context *);
|
||||
__poll_t (*poll) (struct file *, struct poll_table_struct *);
|
||||
|
@ -518,12 +522,6 @@ prototypes::
|
|||
int (*fsync) (struct file *, loff_t start, loff_t end, int datasync);
|
||||
int (*fasync) (int, struct file *, int);
|
||||
int (*lock) (struct file *, int, struct file_lock *);
|
||||
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
|
||||
loff_t *);
|
||||
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
|
||||
loff_t *);
|
||||
ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t,
|
||||
void __user *);
|
||||
ssize_t (*sendpage) (struct file *, struct page *, int, size_t,
|
||||
loff_t *, int);
|
||||
unsigned long (*get_unmapped_area)(struct file *, unsigned long,
|
||||
|
@ -536,6 +534,14 @@ prototypes::
|
|||
size_t, unsigned int);
|
||||
int (*setlease)(struct file *, long, struct file_lock **, void **);
|
||||
long (*fallocate)(struct file *, int, loff_t, loff_t);
|
||||
void (*show_fdinfo)(struct seq_file *m, struct file *f);
|
||||
unsigned (*mmap_capabilities)(struct file *);
|
||||
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
|
||||
loff_t, size_t, unsigned int);
|
||||
loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
|
||||
struct file *file_out, loff_t pos_out,
|
||||
loff_t len, unsigned int remap_flags);
|
||||
int (*fadvise)(struct file *, loff_t, loff_t, int);
|
||||
|
||||
locking rules:
|
||||
All may block.
|
||||
|
@ -570,6 +576,25 @@ in sys_read() and friends.
|
|||
the lease within the individual filesystem to record the result of the
|
||||
operation
|
||||
|
||||
->fallocate implementation must be really careful to maintain page cache
|
||||
consistency when punching holes or performing other operations that invalidate
|
||||
page cache contents. Usually the filesystem needs to call
|
||||
truncate_inode_pages_range() to invalidate relevant range of the page cache.
|
||||
However the filesystem usually also needs to update its internal (and on disk)
|
||||
view of file offset -> disk block mapping. Until this update is finished, the
|
||||
filesystem needs to block page faults and reads from reloading now-stale page
|
||||
cache contents from the disk. Since VFS acquires mapping->invalidate_lock in
|
||||
shared mode when loading pages from disk (filemap_fault(), filemap_read(),
|
||||
readahead paths), the fallocate implementation must take the invalidate_lock to
|
||||
prevent reloading.
|
||||
|
||||
->copy_file_range and ->remap_file_range implementations need to serialize
|
||||
against modifications of file data while the operation is running. For
|
||||
blocking changes through write(2) and similar operations inode->i_rwsem can be
|
||||
used. To block changes to file contents via a memory mapping during the
|
||||
operation, the filesystem must take mapping->invalidate_lock to coordinate
|
||||
with ->page_mkwrite.
|
||||
|
||||
dquot_operations
|
||||
================
|
||||
|
||||
|
@ -627,11 +652,11 @@ pfn_mkwrite: yes
|
|||
access: yes
|
||||
============= ========= ===========================
|
||||
|
||||
->fault() is called when a previously not present pte is about
|
||||
to be faulted in. The filesystem must find and return the page associated
|
||||
with the passed in "pgoff" in the vm_fault structure. If it is possible that
|
||||
the page may be truncated and/or invalidated, then the filesystem must lock
|
||||
the page, then ensure it is not already truncated (the page lock will block
|
||||
->fault() is called when a previously not present pte is about to be faulted
|
||||
in. The filesystem must find and return the page associated with the passed in
|
||||
"pgoff" in the vm_fault structure. If it is possible that the page may be
|
||||
truncated and/or invalidated, then the filesystem must lock invalidate_lock,
|
||||
then ensure the page is not already truncated (invalidate_lock will block
|
||||
subsequent truncate), and then return with VM_FAULT_LOCKED, and the page
|
||||
locked. The VM will unlock the page.
|
||||
|
||||
|
@ -644,12 +669,14 @@ page table entry. Pointer to entry associated with the page is passed in
|
|||
"pte" field in vm_fault structure. Pointers to entries for other offsets
|
||||
should be calculated relative to "pte".
|
||||
|
||||
->page_mkwrite() is called when a previously read-only pte is
|
||||
about to become writeable. The filesystem again must ensure that there are
|
||||
no truncate/invalidate races, and then return with the page locked. If
|
||||
the page has been truncated, the filesystem should not look up a new page
|
||||
like the ->fault() handler, but simply return with VM_FAULT_NOPAGE, which
|
||||
will cause the VM to retry the fault.
|
||||
->page_mkwrite() is called when a previously read-only pte is about to become
|
||||
writeable. The filesystem again must ensure that there are no
|
||||
truncate/invalidate races or races with operations such as ->remap_file_range
|
||||
or ->copy_file_range, and then return with the page locked. Usually
|
||||
mapping->invalidate_lock is suitable for proper serialization. If the page has
|
||||
been truncated, the filesystem should not look up a new page like the ->fault()
|
||||
handler, but simply return with VM_FAULT_NOPAGE, which will cause the VM to
|
||||
retry the fault.
|
||||
|
||||
->pfn_mkwrite() is the same as page_mkwrite but when the pte is
|
||||
VM_PFNMAP or VM_MIXEDMAP with a page-less entry. Expected return is
|
||||
|
|
|
@ -53,6 +53,10 @@ struct regmap {
|
|||
spinlock_t spinlock;
|
||||
unsigned long spinlock_flags;
|
||||
};
|
||||
struct {
|
||||
raw_spinlock_t raw_spinlock;
|
||||
unsigned long raw_spinlock_flags;
|
||||
};
|
||||
};
|
||||
regmap_lock lock;
|
||||
regmap_unlock unlock;
|
||||
|
|
|
@ -368,7 +368,7 @@ static ssize_t regmap_reg_ranges_read_file(struct file *file,
|
|||
char *buf;
|
||||
char *entry;
|
||||
int ret;
|
||||
unsigned entry_len;
|
||||
unsigned int entry_len;
|
||||
|
||||
if (*ppos < 0 || !count)
|
||||
return -EINVAL;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
struct regmap_mmio_context {
|
||||
void __iomem *regs;
|
||||
unsigned val_bytes;
|
||||
unsigned int val_bytes;
|
||||
bool relaxed_mmio;
|
||||
|
||||
bool attached_clk;
|
||||
|
|
|
@ -533,6 +533,23 @@ __releases(&map->spinlock)
|
|||
spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags);
|
||||
}
|
||||
|
||||
static void regmap_lock_raw_spinlock(void *__map)
|
||||
__acquires(&map->raw_spinlock)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&map->raw_spinlock, flags);
|
||||
map->raw_spinlock_flags = flags;
|
||||
}
|
||||
|
||||
static void regmap_unlock_raw_spinlock(void *__map)
|
||||
__releases(&map->raw_spinlock)
|
||||
{
|
||||
struct regmap *map = __map;
|
||||
raw_spin_unlock_irqrestore(&map->raw_spinlock, map->raw_spinlock_flags);
|
||||
}
|
||||
|
||||
static void dev_get_regmap_release(struct device *dev, void *res)
|
||||
{
|
||||
/*
|
||||
|
@ -770,11 +787,19 @@ struct regmap *__regmap_init(struct device *dev,
|
|||
} else {
|
||||
if ((bus && bus->fast_io) ||
|
||||
config->fast_io) {
|
||||
spin_lock_init(&map->spinlock);
|
||||
map->lock = regmap_lock_spinlock;
|
||||
map->unlock = regmap_unlock_spinlock;
|
||||
lockdep_set_class_and_name(&map->spinlock,
|
||||
lock_key, lock_name);
|
||||
if (config->use_raw_spinlock) {
|
||||
raw_spin_lock_init(&map->raw_spinlock);
|
||||
map->lock = regmap_lock_raw_spinlock;
|
||||
map->unlock = regmap_unlock_raw_spinlock;
|
||||
lockdep_set_class_and_name(&map->raw_spinlock,
|
||||
lock_key, lock_name);
|
||||
} else {
|
||||
spin_lock_init(&map->spinlock);
|
||||
map->lock = regmap_lock_spinlock;
|
||||
map->unlock = regmap_unlock_spinlock;
|
||||
lockdep_set_class_and_name(&map->spinlock,
|
||||
lock_key, lock_name);
|
||||
}
|
||||
} else {
|
||||
mutex_init(&map->mutex);
|
||||
map->lock = regmap_lock_mutex;
|
||||
|
@ -1126,10 +1151,10 @@ struct regmap *__regmap_init(struct device *dev,
|
|||
/* Make sure, that this register range has no selector
|
||||
or data window within its boundary */
|
||||
for (j = 0; j < config->num_ranges; j++) {
|
||||
unsigned sel_reg = config->ranges[j].selector_reg;
|
||||
unsigned win_min = config->ranges[j].window_start;
|
||||
unsigned win_max = win_min +
|
||||
config->ranges[j].window_len - 1;
|
||||
unsigned int sel_reg = config->ranges[j].selector_reg;
|
||||
unsigned int win_min = config->ranges[j].window_start;
|
||||
unsigned int win_max = win_min +
|
||||
config->ranges[j].window_len - 1;
|
||||
|
||||
/* Allow data window inside its own virtual range */
|
||||
if (j == i)
|
||||
|
@ -1298,7 +1323,7 @@ EXPORT_SYMBOL_GPL(devm_regmap_field_alloc);
|
|||
*/
|
||||
int regmap_field_bulk_alloc(struct regmap *regmap,
|
||||
struct regmap_field **rm_field,
|
||||
struct reg_field *reg_field,
|
||||
const struct reg_field *reg_field,
|
||||
int num_fields)
|
||||
{
|
||||
struct regmap_field *rf;
|
||||
|
@ -1334,7 +1359,7 @@ EXPORT_SYMBOL_GPL(regmap_field_bulk_alloc);
|
|||
int devm_regmap_field_bulk_alloc(struct device *dev,
|
||||
struct regmap *regmap,
|
||||
struct regmap_field **rm_field,
|
||||
struct reg_field *reg_field,
|
||||
const struct reg_field *reg_field,
|
||||
int num_fields)
|
||||
{
|
||||
struct regmap_field *rf;
|
||||
|
@ -1667,7 +1692,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
|
|||
if (ret) {
|
||||
dev_err(map->dev,
|
||||
"Error in caching of register: %x ret: %d\n",
|
||||
reg + i, ret);
|
||||
reg + regmap_get_offset(map, i), ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define RN5T618_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(500))
|
||||
|
@ -189,6 +191,19 @@ static const struct iio_chan_spec rn5t618_adc_iio_channels[] = {
|
|||
RN5T618_ADC_CHANNEL(AIN0, IIO_VOLTAGE, "AIN0")
|
||||
};
|
||||
|
||||
static struct iio_map rn5t618_maps[] = {
|
||||
IIO_MAP("VADP", "rn5t618-power", "vadp"),
|
||||
IIO_MAP("VUSB", "rn5t618-power", "vusb"),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static void unregister_map(void *data)
|
||||
{
|
||||
struct iio_dev *iio_dev = (struct iio_dev *) data;
|
||||
|
||||
iio_map_array_unregister(iio_dev);
|
||||
}
|
||||
|
||||
static int rn5t618_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -239,6 +254,14 @@ static int rn5t618_adc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_map_array_register(iio_dev, rn5t618_maps);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(adc->dev, unregister_map, iio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(adc->dev, iio_dev);
|
||||
}
|
||||
|
||||
|
|
|
@ -204,6 +204,12 @@ config POWER_RESET_ST
|
|||
help
|
||||
Reset support for STMicroelectronics boards.
|
||||
|
||||
config POWER_RESET_TPS65086
|
||||
bool "TPS65086 restart driver"
|
||||
depends on MFD_TPS65086
|
||||
help
|
||||
This driver adds support for resetting the TPS65086 PMIC on restart.
|
||||
|
||||
config POWER_RESET_VERSATILE
|
||||
bool "ARM Versatile family reboot driver"
|
||||
depends on ARM
|
||||
|
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
|
|||
obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_TPS65086) += tps65086-restart.o
|
||||
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#define MII_MARVELL_PHY_PAGE 22
|
||||
|
||||
#define MII_PHY_LED_CTRL 16
|
||||
#define MII_PHY_LED_POL_CTRL 17
|
||||
#define MII_88E1318S_PHY_LED_TCR 18
|
||||
#define MII_88E1318S_PHY_WOL_CTRL 16
|
||||
#define MII_M1011_IEVENT 19
|
||||
|
@ -29,11 +30,23 @@
|
|||
#define LED2_FORCE_ON (0x8 << 8)
|
||||
#define LEDMASK GENMASK(11,8)
|
||||
|
||||
static struct phy_device *phydev;
|
||||
#define MII_88E1318S_PHY_LED_POL_LED2 BIT(4)
|
||||
|
||||
static void mvphy_reg_intn(u16 data)
|
||||
struct power_off_cfg {
|
||||
char *mdio_node_name;
|
||||
void (*phy_set_reg)(bool restart);
|
||||
};
|
||||
|
||||
static struct phy_device *phydev;
|
||||
static const struct power_off_cfg *cfg;
|
||||
|
||||
static void linkstation_mvphy_reg_intn(bool restart)
|
||||
{
|
||||
int rc = 0, saved_page;
|
||||
u16 data = 0;
|
||||
|
||||
if (restart)
|
||||
data = MII_88E1318S_PHY_LED_TCR_FORCE_INT;
|
||||
|
||||
saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE);
|
||||
if (saved_page < 0)
|
||||
|
@ -66,11 +79,52 @@ static void mvphy_reg_intn(u16 data)
|
|||
dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc);
|
||||
}
|
||||
|
||||
static void readynas_mvphy_set_reg(bool restart)
|
||||
{
|
||||
int rc = 0, saved_page;
|
||||
u16 data = 0;
|
||||
|
||||
if (restart)
|
||||
data = MII_88E1318S_PHY_LED_POL_LED2;
|
||||
|
||||
saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE);
|
||||
if (saved_page < 0)
|
||||
goto err;
|
||||
|
||||
/* Set the LED[2].0 Polarity bit to the required state */
|
||||
__phy_modify(phydev, MII_PHY_LED_POL_CTRL,
|
||||
MII_88E1318S_PHY_LED_POL_LED2, data);
|
||||
|
||||
if (!data) {
|
||||
/* If WOL was enabled and a magic packet was received before powering
|
||||
* off, we won't be able to wake up by sending another magic packet.
|
||||
* Clear WOL status.
|
||||
*/
|
||||
__phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_WOL_PAGE);
|
||||
__phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL,
|
||||
MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS);
|
||||
}
|
||||
err:
|
||||
rc = phy_restore_page(phydev, saved_page, rc);
|
||||
if (rc < 0)
|
||||
dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc);
|
||||
}
|
||||
|
||||
static const struct power_off_cfg linkstation_power_off_cfg = {
|
||||
.mdio_node_name = "mdio",
|
||||
.phy_set_reg = linkstation_mvphy_reg_intn,
|
||||
};
|
||||
|
||||
static const struct power_off_cfg readynas_power_off_cfg = {
|
||||
.mdio_node_name = "mdio-bus",
|
||||
.phy_set_reg = readynas_mvphy_set_reg,
|
||||
};
|
||||
|
||||
static int linkstation_reboot_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *unused)
|
||||
{
|
||||
if (action == SYS_RESTART)
|
||||
mvphy_reg_intn(MII_88E1318S_PHY_LED_TCR_FORCE_INT);
|
||||
cfg->phy_set_reg(true);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
@ -82,14 +136,21 @@ static struct notifier_block linkstation_reboot_nb = {
|
|||
static void linkstation_poweroff(void)
|
||||
{
|
||||
unregister_reboot_notifier(&linkstation_reboot_nb);
|
||||
mvphy_reg_intn(0);
|
||||
cfg->phy_set_reg(false);
|
||||
|
||||
kernel_restart("Power off");
|
||||
}
|
||||
|
||||
static const struct of_device_id ls_poweroff_of_match[] = {
|
||||
{ .compatible = "buffalo,ls421d" },
|
||||
{ .compatible = "buffalo,ls421de" },
|
||||
{ .compatible = "buffalo,ls421d",
|
||||
.data = &linkstation_power_off_cfg,
|
||||
},
|
||||
{ .compatible = "buffalo,ls421de",
|
||||
.data = &linkstation_power_off_cfg,
|
||||
},
|
||||
{ .compatible = "netgear,readynas-duo-v2",
|
||||
.data = &readynas_power_off_cfg,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@ -97,13 +158,17 @@ static int __init linkstation_poweroff_init(void)
|
|||
{
|
||||
struct mii_bus *bus;
|
||||
struct device_node *dn;
|
||||
const struct of_device_id *match;
|
||||
|
||||
dn = of_find_matching_node(NULL, ls_poweroff_of_match);
|
||||
if (!dn)
|
||||
return -ENODEV;
|
||||
of_node_put(dn);
|
||||
|
||||
dn = of_find_node_by_name(NULL, "mdio");
|
||||
match = of_match_node(ls_poweroff_of_match, dn);
|
||||
cfg = match->data;
|
||||
|
||||
dn = of_find_node_by_name(NULL, cfg->mdio_node_name);
|
||||
if (!dn)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2021 Emil Renner Berthing
|
||||
*/
|
||||
|
||||
#include <linux/mfd/tps65086.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
struct tps65086_restart {
|
||||
struct notifier_block handler;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int tps65086_restart_notify(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
{
|
||||
struct tps65086_restart *tps65086_restart =
|
||||
container_of(this, struct tps65086_restart, handler);
|
||||
struct tps65086 *tps65086 = dev_get_drvdata(tps65086_restart->dev->parent);
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(tps65086->regmap, TPS65086_FORCESHUTDN, 1);
|
||||
if (ret) {
|
||||
dev_err(tps65086_restart->dev, "%s: error writing to tps65086 pmic: %d\n",
|
||||
__func__, ret);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/* give it a little time */
|
||||
mdelay(200);
|
||||
|
||||
WARN_ON(1);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int tps65086_restart_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tps65086_restart *tps65086_restart;
|
||||
int ret;
|
||||
|
||||
tps65086_restart = devm_kzalloc(&pdev->dev, sizeof(*tps65086_restart), GFP_KERNEL);
|
||||
if (!tps65086_restart)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, tps65086_restart);
|
||||
|
||||
tps65086_restart->handler.notifier_call = tps65086_restart_notify;
|
||||
tps65086_restart->handler.priority = 192;
|
||||
tps65086_restart->dev = &pdev->dev;
|
||||
|
||||
ret = register_restart_handler(&tps65086_restart->handler);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: cannot register restart handler: %d\n",
|
||||
__func__, ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps65086_restart_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tps65086_restart *tps65086_restart = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = unregister_restart_handler(&tps65086_restart->handler);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: cannot unregister restart handler: %d\n",
|
||||
__func__, ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id tps65086_restart_id_table[] = {
|
||||
{ "tps65086-reset", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, tps65086_restart_id_table);
|
||||
|
||||
static struct platform_driver tps65086_restart_driver = {
|
||||
.driver = {
|
||||
.name = "tps65086-restart",
|
||||
},
|
||||
.probe = tps65086_restart_probe,
|
||||
.remove = tps65086_restart_remove,
|
||||
.id_table = tps65086_restart_id_table,
|
||||
};
|
||||
module_platform_driver(tps65086_restart_driver);
|
||||
|
||||
MODULE_AUTHOR("Emil Renner Berthing <kernel@esmil.dk>");
|
||||
MODULE_DESCRIPTION("TPS65086 restart driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -358,7 +358,7 @@ config AXP288_CHARGER
|
|||
|
||||
config AXP288_FUEL_GAUGE
|
||||
tristate "X-Powers AXP288 Fuel Gauge"
|
||||
depends on MFD_AXP20X && IIO
|
||||
depends on MFD_AXP20X && IIO && IOSF_MBI
|
||||
help
|
||||
Say yes here to have support for X-Power power management IC (PMIC)
|
||||
Fuel Gauge. The device provides battery statistics and status
|
||||
|
@ -577,6 +577,17 @@ config CHARGER_MP2629
|
|||
Battery charger. This driver provides Battery charger power management
|
||||
functions on the systems.
|
||||
|
||||
config CHARGER_MT6360
|
||||
tristate "Mediatek MT6360 Charger Driver"
|
||||
depends on MFD_MT6360
|
||||
depends on REGULATOR
|
||||
select LINEAR_RANGES
|
||||
help
|
||||
Say Y here to enable MT6360 Charger Part.
|
||||
The device supports High-Accuracy Voltage/Current Regulation,
|
||||
Average Input Current Regulation, Battery Temperature Sensing,
|
||||
Over-Temperature Protection, DPDM Detection for BC1.2.
|
||||
|
||||
config CHARGER_QCOM_SMBB
|
||||
tristate "Qualcomm Switch-Mode Battery Charger and Boost"
|
||||
depends on MFD_SPMI_PMIC || COMPILE_TEST
|
||||
|
@ -669,6 +680,7 @@ config CHARGER_BQ256XX
|
|||
config CHARGER_SMB347
|
||||
tristate "Summit Microelectronics SMB3XX Battery Charger"
|
||||
depends on I2C
|
||||
depends on REGULATOR
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y to include support for Summit Microelectronics SMB345,
|
||||
|
@ -736,6 +748,16 @@ config CHARGER_CROS_USBPD
|
|||
what is connected to USB PD ports from the EC and converts
|
||||
that into power_supply properties.
|
||||
|
||||
config CHARGER_CROS_PCHG
|
||||
tristate "ChromeOS EC based peripheral charger"
|
||||
depends on MFD_CROS_EC_DEV
|
||||
default MFD_CROS_EC_DEV
|
||||
help
|
||||
Say Y here to enable ChromeOS EC based peripheral charge driver.
|
||||
This driver gets various information about the devices connected to
|
||||
the peripheral charge ports from the EC and converts that into
|
||||
power_supply properties.
|
||||
|
||||
config CHARGER_SC2731
|
||||
tristate "Spreadtrum SC2731 charger driver"
|
||||
depends on MFD_SC27XX_PMIC || COMPILE_TEST
|
||||
|
@ -782,6 +804,8 @@ config CHARGER_WILCO
|
|||
config RN5T618_POWER
|
||||
tristate "RN5T618 charger/fuel gauge support"
|
||||
depends on MFD_RN5T618
|
||||
depends on RN5T618_ADC
|
||||
depends on IIO
|
||||
help
|
||||
Say Y here to have support for RN5T618 PMIC family fuel gauge and charger.
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
|
|
|
@ -60,7 +60,7 @@ obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o
|
|||
obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
|
||||
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
||||
obj-$(CONFIG_BATTERY_RX51) += rx51_battery.o
|
||||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o abx500_chargalg.o
|
||||
obj-$(CONFIG_AB8500_BM) += ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o
|
||||
obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o
|
||||
obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
|
||||
|
@ -78,6 +78,7 @@ obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
|
|||
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
|
||||
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
|
||||
obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o
|
||||
obj-$(CONFIG_CHARGER_MT6360) += mt6360_charger.o
|
||||
obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o
|
||||
obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o
|
||||
obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o
|
||||
|
@ -93,6 +94,7 @@ obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
|
|||
obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
|
||||
obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o
|
||||
obj-$(CONFIG_CHARGER_CROS_USBPD) += cros_usbpd-charger.o
|
||||
obj-$(CONFIG_CHARGER_CROS_PCHG) += cros_peripheral_charger.o
|
||||
obj-$(CONFIG_CHARGER_SC2731) += sc2731_charger.o
|
||||
obj-$(CONFIG_FUEL_GAUGE_SC27XX) += sc27xx_fuel_gauge.o
|
||||
obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
|
||||
|
|
|
@ -269,43 +269,43 @@ enum bup_vch_sel {
|
|||
|
||||
/*
|
||||
* ADC for the battery thermistor.
|
||||
* When using the ABx500_ADC_THERM_BATCTRL the battery ID resistor is combined
|
||||
* When using the AB8500_ADC_THERM_BATCTRL the battery ID resistor is combined
|
||||
* with a NTC resistor to both identify the battery and to measure its
|
||||
* temperature. Different phone manufactures uses different techniques to both
|
||||
* identify the battery and to read its temperature.
|
||||
*/
|
||||
enum abx500_adc_therm {
|
||||
ABx500_ADC_THERM_BATCTRL,
|
||||
ABx500_ADC_THERM_BATTEMP,
|
||||
enum ab8500_adc_therm {
|
||||
AB8500_ADC_THERM_BATCTRL,
|
||||
AB8500_ADC_THERM_BATTEMP,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct abx500_res_to_temp - defines one point in a temp to res curve. To
|
||||
* struct ab8500_res_to_temp - defines one point in a temp to res curve. To
|
||||
* be used in battery packs that combines the identification resistor with a
|
||||
* NTC resistor.
|
||||
* @temp: battery pack temperature in Celsius
|
||||
* @resist: NTC resistor net total resistance
|
||||
*/
|
||||
struct abx500_res_to_temp {
|
||||
struct ab8500_res_to_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct abx500_v_to_cap - Table for translating voltage to capacity
|
||||
* struct ab8500_v_to_cap - Table for translating voltage to capacity
|
||||
* @voltage: Voltage in mV
|
||||
* @capacity: Capacity in percent
|
||||
*/
|
||||
struct abx500_v_to_cap {
|
||||
struct ab8500_v_to_cap {
|
||||
int voltage;
|
||||
int capacity;
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
struct abx500_fg;
|
||||
struct ab8500_fg;
|
||||
|
||||
/**
|
||||
* struct abx500_fg_parameters - Fuel gauge algorithm parameters, in seconds
|
||||
* struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds
|
||||
* if not specified
|
||||
* @recovery_sleep_timer: Time between measurements while recovering
|
||||
* @recovery_total_time: Total recovery time
|
||||
|
@ -333,7 +333,7 @@ struct abx500_fg;
|
|||
* @pcut_max_restart: Max number of restarts
|
||||
* @pcut_debounce_time: Sets battery debounce time
|
||||
*/
|
||||
struct abx500_fg_parameters {
|
||||
struct ab8500_fg_parameters {
|
||||
int recovery_sleep_timer;
|
||||
int recovery_total_time;
|
||||
int init_timer;
|
||||
|
@ -357,13 +357,13 @@ struct abx500_fg_parameters {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct abx500_charger_maximization - struct used by the board config.
|
||||
* struct ab8500_charger_maximization - struct used by the board config.
|
||||
* @use_maxi: Enable maximization for this battery type
|
||||
* @maxi_chg_curr: Maximum charger current allowed
|
||||
* @maxi_wait_cycles: cycles to wait before setting charger current
|
||||
* @charger_curr_step delta between two charger current settings (mA)
|
||||
*/
|
||||
struct abx500_maxim_parameters {
|
||||
struct ab8500_maxim_parameters {
|
||||
bool ena_maxi;
|
||||
int chg_curr;
|
||||
int wait_cycles;
|
||||
|
@ -371,7 +371,7 @@ struct abx500_maxim_parameters {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct abx500_battery_type - different batteries supported
|
||||
* struct ab8500_battery_type - different batteries supported
|
||||
* @name: battery technology
|
||||
* @resis_high: battery upper resistance limit
|
||||
* @resis_low: battery lower resistance limit
|
||||
|
@ -400,7 +400,7 @@ struct abx500_maxim_parameters {
|
|||
* @n_batres_tbl_elements number of elements in the batres_tbl
|
||||
* @batres_tbl battery internal resistance vs temperature table
|
||||
*/
|
||||
struct abx500_battery_type {
|
||||
struct ab8500_battery_type {
|
||||
int name;
|
||||
int resis_high;
|
||||
int resis_low;
|
||||
|
@ -421,210 +421,13 @@ struct abx500_battery_type {
|
|||
int low_high_vol_lvl;
|
||||
int battery_resistance;
|
||||
int n_temp_tbl_elements;
|
||||
const struct abx500_res_to_temp *r_to_t_tbl;
|
||||
const struct ab8500_res_to_temp *r_to_t_tbl;
|
||||
int n_v_cap_tbl_elements;
|
||||
const struct abx500_v_to_cap *v_to_cap_tbl;
|
||||
const struct ab8500_v_to_cap *v_to_cap_tbl;
|
||||
int n_batres_tbl_elements;
|
||||
const struct batres_vs_temp *batres_tbl;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct abx500_bm_capacity_levels - abx500 capacity level data
|
||||
* @critical: critical capacity level in percent
|
||||
* @low: low capacity level in percent
|
||||
* @normal: normal capacity level in percent
|
||||
* @high: high capacity level in percent
|
||||
* @full: full capacity level in percent
|
||||
*/
|
||||
struct abx500_bm_capacity_levels {
|
||||
int critical;
|
||||
int low;
|
||||
int normal;
|
||||
int high;
|
||||
int full;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct abx500_bm_charger_parameters - Charger specific parameters
|
||||
* @usb_volt_max: maximum allowed USB charger voltage in mV
|
||||
* @usb_curr_max: maximum allowed USB charger current in mA
|
||||
* @ac_volt_max: maximum allowed AC charger voltage in mV
|
||||
* @ac_curr_max: maximum allowed AC charger current in mA
|
||||
*/
|
||||
struct abx500_bm_charger_parameters {
|
||||
int usb_volt_max;
|
||||
int usb_curr_max;
|
||||
int ac_volt_max;
|
||||
int ac_curr_max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct abx500_bm_data - abx500 battery management data
|
||||
* @temp_under under this temp, charging is stopped
|
||||
* @temp_low between this temp and temp_under charging is reduced
|
||||
* @temp_high between this temp and temp_over charging is reduced
|
||||
* @temp_over over this temp, charging is stopped
|
||||
* @temp_now present battery temperature
|
||||
* @temp_interval_chg temperature measurement interval in s when charging
|
||||
* @temp_interval_nochg temperature measurement interval in s when not charging
|
||||
* @main_safety_tmr_h safety timer for main charger
|
||||
* @usb_safety_tmr_h safety timer for usb charger
|
||||
* @bkup_bat_v voltage which we charge the backup battery with
|
||||
* @bkup_bat_i current which we charge the backup battery with
|
||||
* @no_maintenance indicates that maintenance charging is disabled
|
||||
* @capacity_scaling indicates whether capacity scaling is to be used
|
||||
* @abx500_adc_therm placement of thermistor, batctrl or battemp adc
|
||||
* @chg_unknown_bat flag to enable charging of unknown batteries
|
||||
* @enable_overshoot flag to enable VBAT overshoot control
|
||||
* @auto_trig flag to enable auto adc trigger
|
||||
* @fg_res resistance of FG resistor in 0.1mOhm
|
||||
* @n_btypes number of elements in array bat_type
|
||||
* @batt_id index of the identified battery in array bat_type
|
||||
* @interval_charging charge alg cycle period time when charging (sec)
|
||||
* @interval_not_charging charge alg cycle period time when not charging (sec)
|
||||
* @temp_hysteresis temperature hysteresis
|
||||
* @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
|
||||
* @n_chg_out_curr number of elements in array chg_output_curr
|
||||
* @n_chg_in_curr number of elements in array chg_input_curr
|
||||
* @chg_output_curr charger output current level map
|
||||
* @chg_input_curr charger input current level map
|
||||
* @maxi maximization parameters
|
||||
* @cap_levels capacity in percent for the different capacity levels
|
||||
* @bat_type table of supported battery types
|
||||
* @chg_params charger parameters
|
||||
* @fg_params fuel gauge parameters
|
||||
*/
|
||||
struct abx500_bm_data {
|
||||
int temp_under;
|
||||
int temp_low;
|
||||
int temp_high;
|
||||
int temp_over;
|
||||
int temp_now;
|
||||
int temp_interval_chg;
|
||||
int temp_interval_nochg;
|
||||
int main_safety_tmr_h;
|
||||
int usb_safety_tmr_h;
|
||||
int bkup_bat_v;
|
||||
int bkup_bat_i;
|
||||
bool no_maintenance;
|
||||
bool capacity_scaling;
|
||||
bool chg_unknown_bat;
|
||||
bool enable_overshoot;
|
||||
bool auto_trig;
|
||||
enum abx500_adc_therm adc_therm;
|
||||
int fg_res;
|
||||
int n_btypes;
|
||||
int batt_id;
|
||||
int interval_charging;
|
||||
int interval_not_charging;
|
||||
int temp_hysteresis;
|
||||
int gnd_lift_resistance;
|
||||
int n_chg_out_curr;
|
||||
int n_chg_in_curr;
|
||||
int *chg_output_curr;
|
||||
int *chg_input_curr;
|
||||
const struct abx500_maxim_parameters *maxi;
|
||||
const struct abx500_bm_capacity_levels *cap_levels;
|
||||
struct abx500_battery_type *bat_type;
|
||||
const struct abx500_bm_charger_parameters *chg_params;
|
||||
const struct abx500_fg_parameters *fg_params;
|
||||
};
|
||||
|
||||
enum {
|
||||
NTC_EXTERNAL = 0,
|
||||
NTC_INTERNAL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct res_to_temp - defines one point in a temp to res curve. To
|
||||
* be used in battery packs that combines the identification resistor with a
|
||||
* NTC resistor.
|
||||
* @temp: battery pack temperature in Celsius
|
||||
* @resist: NTC resistor net total resistance
|
||||
*/
|
||||
struct res_to_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batres_vs_temp - defines one point in a temp vs battery internal
|
||||
* resistance curve.
|
||||
* @temp: battery pack temperature in Celsius
|
||||
* @resist: battery internal reistance in mOhm
|
||||
*/
|
||||
struct batres_vs_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
struct ab8500_fg;
|
||||
|
||||
/**
|
||||
* struct ab8500_fg_parameters - Fuel gauge algorithm parameters, in seconds
|
||||
* if not specified
|
||||
* @recovery_sleep_timer: Time between measurements while recovering
|
||||
* @recovery_total_time: Total recovery time
|
||||
* @init_timer: Measurement interval during startup
|
||||
* @init_discard_time: Time we discard voltage measurement at startup
|
||||
* @init_total_time: Total init time during startup
|
||||
* @high_curr_time: Time current has to be high to go to recovery
|
||||
* @accu_charging: FG accumulation time while charging
|
||||
* @accu_high_curr: FG accumulation time in high current mode
|
||||
* @high_curr_threshold: High current threshold, in mA
|
||||
* @lowbat_threshold: Low battery threshold, in mV
|
||||
* @battok_falling_th_sel0 Threshold in mV for battOk signal sel0
|
||||
* Resolution in 50 mV step.
|
||||
* @battok_raising_th_sel1 Threshold in mV for battOk signal sel1
|
||||
* Resolution in 50 mV step.
|
||||
* @user_cap_limit Capacity reported from user must be within this
|
||||
* limit to be considered as sane, in percentage
|
||||
* points.
|
||||
* @maint_thres This is the threshold where we stop reporting
|
||||
* battery full while in maintenance, in per cent
|
||||
* @pcut_enable: Enable power cut feature in ab8505
|
||||
* @pcut_max_time: Max time threshold
|
||||
* @pcut_flag_time: Flagtime threshold
|
||||
* @pcut_max_restart: Max number of restarts
|
||||
* @pcut_debunce_time: Sets battery debounce time
|
||||
*/
|
||||
struct ab8500_fg_parameters {
|
||||
int recovery_sleep_timer;
|
||||
int recovery_total_time;
|
||||
int init_timer;
|
||||
int init_discard_time;
|
||||
int init_total_time;
|
||||
int high_curr_time;
|
||||
int accu_charging;
|
||||
int accu_high_curr;
|
||||
int high_curr_threshold;
|
||||
int lowbat_threshold;
|
||||
int battok_falling_th_sel0;
|
||||
int battok_raising_th_sel1;
|
||||
int user_cap_limit;
|
||||
int maint_thres;
|
||||
bool pcut_enable;
|
||||
u8 pcut_max_time;
|
||||
u8 pcut_flag_time;
|
||||
u8 pcut_max_restart;
|
||||
u8 pcut_debunce_time;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab8500_charger_maximization - struct used by the board config.
|
||||
* @use_maxi: Enable maximization for this battery type
|
||||
* @maxi_chg_curr: Maximum charger current allowed
|
||||
* @maxi_wait_cycles: cycles to wait before setting charger current
|
||||
* @charger_curr_step delta between two charger current settings (mA)
|
||||
*/
|
||||
struct ab8500_maxim_parameters {
|
||||
bool ena_maxi;
|
||||
int chg_curr;
|
||||
int wait_cycles;
|
||||
int charger_curr_step;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab8500_bm_capacity_levels - ab8500 capacity level data
|
||||
* @critical: critical capacity level in percent
|
||||
|
@ -661,6 +464,7 @@ struct ab8500_bm_charger_parameters {
|
|||
* @temp_low between this temp and temp_under charging is reduced
|
||||
* @temp_high between this temp and temp_over charging is reduced
|
||||
* @temp_over over this temp, charging is stopped
|
||||
* @temp_now present battery temperature
|
||||
* @temp_interval_chg temperature measurement interval in s when charging
|
||||
* @temp_interval_nochg temperature measurement interval in s when not charging
|
||||
* @main_safety_tmr_h safety timer for main charger
|
||||
|
@ -669,9 +473,10 @@ struct ab8500_bm_charger_parameters {
|
|||
* @bkup_bat_i current which we charge the backup battery with
|
||||
* @no_maintenance indicates that maintenance charging is disabled
|
||||
* @capacity_scaling indicates whether capacity scaling is to be used
|
||||
* @adc_therm placement of thermistor, batctrl or battemp adc
|
||||
* @ab8500_adc_therm placement of thermistor, batctrl or battemp adc
|
||||
* @chg_unknown_bat flag to enable charging of unknown batteries
|
||||
* @enable_overshoot flag to enable VBAT overshoot control
|
||||
* @auto_trig flag to enable auto adc trigger
|
||||
* @fg_res resistance of FG resistor in 0.1mOhm
|
||||
* @n_btypes number of elements in array bat_type
|
||||
* @batt_id index of the identified battery in array bat_type
|
||||
|
@ -679,7 +484,11 @@ struct ab8500_bm_charger_parameters {
|
|||
* @interval_not_charging charge alg cycle period time when not charging (sec)
|
||||
* @temp_hysteresis temperature hysteresis
|
||||
* @gnd_lift_resistance Battery ground to phone ground resistance (mOhm)
|
||||
* @maxi: maximization parameters
|
||||
* @n_chg_out_curr number of elements in array chg_output_curr
|
||||
* @n_chg_in_curr number of elements in array chg_input_curr
|
||||
* @chg_output_curr charger output current level map
|
||||
* @chg_input_curr charger input current level map
|
||||
* @maxi maximization parameters
|
||||
* @cap_levels capacity in percent for the different capacity levels
|
||||
* @bat_type table of supported battery types
|
||||
* @chg_params charger parameters
|
||||
|
@ -690,6 +499,7 @@ struct ab8500_bm_data {
|
|||
int temp_low;
|
||||
int temp_high;
|
||||
int temp_over;
|
||||
int temp_now;
|
||||
int temp_interval_chg;
|
||||
int temp_interval_nochg;
|
||||
int main_safety_tmr_h;
|
||||
|
@ -700,7 +510,8 @@ struct ab8500_bm_data {
|
|||
bool capacity_scaling;
|
||||
bool chg_unknown_bat;
|
||||
bool enable_overshoot;
|
||||
enum abx500_adc_therm adc_therm;
|
||||
bool auto_trig;
|
||||
enum ab8500_adc_therm adc_therm;
|
||||
int fg_res;
|
||||
int n_btypes;
|
||||
int batt_id;
|
||||
|
@ -708,13 +519,49 @@ struct ab8500_bm_data {
|
|||
int interval_not_charging;
|
||||
int temp_hysteresis;
|
||||
int gnd_lift_resistance;
|
||||
int n_chg_out_curr;
|
||||
int n_chg_in_curr;
|
||||
int *chg_output_curr;
|
||||
int *chg_input_curr;
|
||||
const struct ab8500_maxim_parameters *maxi;
|
||||
const struct ab8500_bm_capacity_levels *cap_levels;
|
||||
struct ab8500_battery_type *bat_type;
|
||||
const struct ab8500_bm_charger_parameters *chg_params;
|
||||
const struct ab8500_fg_parameters *fg_params;
|
||||
};
|
||||
|
||||
extern struct abx500_bm_data ab8500_bm_data;
|
||||
enum {
|
||||
NTC_EXTERNAL = 0,
|
||||
NTC_INTERNAL,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct res_to_temp - defines one point in a temp to res curve. To
|
||||
* be used in battery packs that combines the identification resistor with a
|
||||
* NTC resistor.
|
||||
* @temp: battery pack temperature in Celsius
|
||||
* @resist: NTC resistor net total resistance
|
||||
*/
|
||||
struct res_to_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct batres_vs_temp - defines one point in a temp vs battery internal
|
||||
* resistance curve.
|
||||
* @temp: battery pack temperature in Celsius
|
||||
* @resist: battery internal reistance in mOhm
|
||||
*/
|
||||
struct batres_vs_temp {
|
||||
int temp;
|
||||
int resist;
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
struct ab8500_fg;
|
||||
|
||||
extern struct ab8500_bm_data ab8500_bm_data;
|
||||
|
||||
void ab8500_charger_usb_state_changed(u8 bm_usb_state, u16 mA);
|
||||
struct ab8500_fg *ab8500_fg_get(void);
|
||||
|
@ -725,10 +572,10 @@ int ab8500_fg_inst_curr_started(struct ab8500_fg *di);
|
|||
int ab8500_fg_inst_curr_done(struct ab8500_fg *di);
|
||||
int ab8500_bm_of_probe(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct abx500_bm_data *bm);
|
||||
struct ab8500_bm_data *bm);
|
||||
|
||||
extern struct platform_driver ab8500_fg_driver;
|
||||
extern struct platform_driver ab8500_btemp_driver;
|
||||
extern struct platform_driver abx500_chargalg_driver;
|
||||
extern struct platform_driver ab8500_chargalg_driver;
|
||||
|
||||
#endif /* _AB8500_CHARGER_H_ */
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
|
||||
#include "ab8500-bm.h"
|
||||
|
||||
|
@ -13,7 +11,7 @@
|
|||
* Note that the res_to_temp table must be strictly sorted by falling resistance
|
||||
* values to work.
|
||||
*/
|
||||
const struct abx500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
|
||||
const struct ab8500_res_to_temp ab8500_temp_tbl_a_thermistor[] = {
|
||||
{-5, 53407},
|
||||
{ 0, 48594},
|
||||
{ 5, 43804},
|
||||
|
@ -35,7 +33,7 @@ EXPORT_SYMBOL(ab8500_temp_tbl_a_thermistor);
|
|||
const int ab8500_temp_tbl_a_size = ARRAY_SIZE(ab8500_temp_tbl_a_thermistor);
|
||||
EXPORT_SYMBOL(ab8500_temp_tbl_a_size);
|
||||
|
||||
const struct abx500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
|
||||
const struct ab8500_res_to_temp ab8500_temp_tbl_b_thermistor[] = {
|
||||
{-5, 200000},
|
||||
{ 0, 159024},
|
||||
{ 5, 151921},
|
||||
|
@ -57,7 +55,7 @@ EXPORT_SYMBOL(ab8500_temp_tbl_b_thermistor);
|
|||
const int ab8500_temp_tbl_b_size = ARRAY_SIZE(ab8500_temp_tbl_b_thermistor);
|
||||
EXPORT_SYMBOL(ab8500_temp_tbl_b_size);
|
||||
|
||||
static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
|
||||
static const struct ab8500_v_to_cap cap_tbl_a_thermistor[] = {
|
||||
{4171, 100},
|
||||
{4114, 95},
|
||||
{4009, 83},
|
||||
|
@ -80,7 +78,7 @@ static const struct abx500_v_to_cap cap_tbl_a_thermistor[] = {
|
|||
{3247, 0},
|
||||
};
|
||||
|
||||
static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
|
||||
static const struct ab8500_v_to_cap cap_tbl_b_thermistor[] = {
|
||||
{4161, 100},
|
||||
{4124, 98},
|
||||
{4044, 90},
|
||||
|
@ -103,7 +101,7 @@ static const struct abx500_v_to_cap cap_tbl_b_thermistor[] = {
|
|||
{3250, 0},
|
||||
};
|
||||
|
||||
static const struct abx500_v_to_cap cap_tbl[] = {
|
||||
static const struct ab8500_v_to_cap cap_tbl[] = {
|
||||
{4186, 100},
|
||||
{4163, 99},
|
||||
{4114, 95},
|
||||
|
@ -134,7 +132,7 @@ static const struct abx500_v_to_cap cap_tbl[] = {
|
|||
* Note that the res_to_temp table must be strictly sorted by falling
|
||||
* resistance values to work.
|
||||
*/
|
||||
static const struct abx500_res_to_temp temp_tbl[] = {
|
||||
static const struct ab8500_res_to_temp temp_tbl[] = {
|
||||
{-5, 214834},
|
||||
{ 0, 162943},
|
||||
{ 5, 124820},
|
||||
|
@ -191,7 +189,7 @@ static const struct batres_vs_temp temp_to_batres_tbl_9100[] = {
|
|||
{-20, 180},
|
||||
};
|
||||
|
||||
static struct abx500_battery_type bat_type_thermistor[] = {
|
||||
static struct ab8500_battery_type bat_type_thermistor[] = {
|
||||
[BATTERY_UNKNOWN] = {
|
||||
/* First element always represent the UNKNOWN battery */
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
|
||||
|
@ -277,7 +275,7 @@ static struct abx500_battery_type bat_type_thermistor[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct abx500_battery_type bat_type_ext_thermistor[] = {
|
||||
static struct ab8500_battery_type bat_type_ext_thermistor[] = {
|
||||
[BATTERY_UNKNOWN] = {
|
||||
/* First element always represent the UNKNOWN battery */
|
||||
.name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
|
||||
|
@ -394,7 +392,7 @@ static struct abx500_battery_type bat_type_ext_thermistor[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct abx500_bm_capacity_levels cap_levels = {
|
||||
static const struct ab8500_bm_capacity_levels cap_levels = {
|
||||
.critical = 2,
|
||||
.low = 10,
|
||||
.normal = 70,
|
||||
|
@ -402,7 +400,7 @@ static const struct abx500_bm_capacity_levels cap_levels = {
|
|||
.full = 100,
|
||||
};
|
||||
|
||||
static const struct abx500_fg_parameters fg = {
|
||||
static const struct ab8500_fg_parameters fg = {
|
||||
.recovery_sleep_timer = 10,
|
||||
.recovery_total_time = 100,
|
||||
.init_timer = 1,
|
||||
|
@ -424,14 +422,14 @@ static const struct abx500_fg_parameters fg = {
|
|||
.pcut_debounce_time = 2,
|
||||
};
|
||||
|
||||
static const struct abx500_maxim_parameters ab8500_maxi_params = {
|
||||
static const struct ab8500_maxim_parameters ab8500_maxi_params = {
|
||||
.ena_maxi = true,
|
||||
.chg_curr = 910,
|
||||
.wait_cycles = 10,
|
||||
.charger_curr_step = 100,
|
||||
};
|
||||
|
||||
static const struct abx500_bm_charger_parameters chg = {
|
||||
static const struct ab8500_bm_charger_parameters chg = {
|
||||
.usb_volt_max = 5500,
|
||||
.usb_curr_max = 1500,
|
||||
.ac_volt_max = 7500,
|
||||
|
@ -456,7 +454,7 @@ static int ab8500_charge_input_curr_map[] = {
|
|||
700, 800, 900, 1000, 1100, 1300, 1400, 1500,
|
||||
};
|
||||
|
||||
struct abx500_bm_data ab8500_bm_data = {
|
||||
struct ab8500_bm_data ab8500_bm_data = {
|
||||
.temp_under = 3,
|
||||
.temp_low = 8,
|
||||
.temp_high = 43,
|
||||
|
@ -469,7 +467,7 @@ struct abx500_bm_data ab8500_bm_data = {
|
|||
.bkup_bat_i = BUP_ICH_SEL_150UA,
|
||||
.no_maintenance = false,
|
||||
.capacity_scaling = false,
|
||||
.adc_therm = ABx500_ADC_THERM_BATCTRL,
|
||||
.adc_therm = AB8500_ADC_THERM_BATCTRL,
|
||||
.chg_unknown_bat = false,
|
||||
.enable_overshoot = false,
|
||||
.fg_res = 100,
|
||||
|
@ -492,7 +490,7 @@ struct abx500_bm_data ab8500_bm_data = {
|
|||
|
||||
int ab8500_bm_of_probe(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct abx500_bm_data *bm)
|
||||
struct ab8500_bm_data *bm)
|
||||
{
|
||||
const struct batres_vs_temp *tmp_batres_tbl;
|
||||
struct device_node *battery_node;
|
||||
|
@ -531,7 +529,7 @@ int ab8500_bm_of_probe(struct device *dev,
|
|||
} else {
|
||||
bm->n_btypes = 4;
|
||||
bm->bat_type = bat_type_ext_thermistor;
|
||||
bm->adc_therm = ABx500_ADC_THERM_BATTEMP;
|
||||
bm->adc_therm = AB8500_ADC_THERM_BATTEMP;
|
||||
tmp_batres_tbl = temp_to_batres_tbl_ext_thermistor;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/fixp-arith.h>
|
||||
|
||||
#include "ab8500-bm.h"
|
||||
|
||||
|
@ -102,7 +103,7 @@ struct ab8500_btemp {
|
|||
struct iio_channel *btemp_ball;
|
||||
struct iio_channel *bat_ctrl;
|
||||
struct ab8500_fg *fg;
|
||||
struct abx500_bm_data *bm;
|
||||
struct ab8500_bm_data *bm;
|
||||
struct power_supply *btemp_psy;
|
||||
struct ab8500_btemp_events events;
|
||||
struct ab8500_btemp_ranges btemp_ranges;
|
||||
|
@ -144,7 +145,7 @@ static int ab8500_btemp_batctrl_volt_to_res(struct ab8500_btemp *di,
|
|||
return (450000 * (v_batctrl)) / (1800 - v_batctrl);
|
||||
}
|
||||
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL) {
|
||||
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL) {
|
||||
/*
|
||||
* If the battery has internal NTC, we use the current
|
||||
* source to calculate the resistance.
|
||||
|
@ -206,7 +207,7 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
|
|||
return 0;
|
||||
|
||||
/* Only do this for batteries with internal NTC */
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
|
||||
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && enable) {
|
||||
|
||||
if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA)
|
||||
curr = BAT_CTRL_7U_ENA;
|
||||
|
@ -239,7 +240,7 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di,
|
|||
__func__);
|
||||
goto disable_curr_source;
|
||||
}
|
||||
} else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
|
||||
} else if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL && !enable) {
|
||||
dev_dbg(di->dev, "Disable BATCTRL curr source\n");
|
||||
|
||||
/* Write 0 to the curr bits */
|
||||
|
@ -417,7 +418,7 @@ static int ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
|
|||
* based on the NTC resistance.
|
||||
*/
|
||||
static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
|
||||
const struct abx500_res_to_temp *tbl, int tbl_size, int res)
|
||||
const struct ab8500_res_to_temp *tbl, int tbl_size, int res)
|
||||
{
|
||||
int i;
|
||||
/*
|
||||
|
@ -437,8 +438,9 @@ static int ab8500_btemp_res_to_temp(struct ab8500_btemp *di,
|
|||
i++;
|
||||
}
|
||||
|
||||
return tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
|
||||
(res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
|
||||
return fixp_linear_interpolate(tbl[i].resist, tbl[i].temp,
|
||||
tbl[i + 1].resist, tbl[i + 1].temp,
|
||||
res);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -456,7 +458,7 @@ static int ab8500_btemp_measure_temp(struct ab8500_btemp *di)
|
|||
|
||||
id = di->bm->batt_id;
|
||||
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
|
||||
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL &&
|
||||
id != BATTERY_UNKNOWN) {
|
||||
|
||||
rbat = ab8500_btemp_get_batctrl_res(di);
|
||||
|
@ -525,7 +527,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
|
|||
dev_dbg(di->dev, "Battery detected on %s"
|
||||
" low %d < res %d < high: %d"
|
||||
" index: %d\n",
|
||||
di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL ?
|
||||
di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL ?
|
||||
"BATCTRL" : "BATTEMP",
|
||||
di->bm->bat_type[i].resis_low, res,
|
||||
di->bm->bat_type[i].resis_high, i);
|
||||
|
@ -545,7 +547,7 @@ static int ab8500_btemp_id(struct ab8500_btemp *di)
|
|||
* We only have to change current source if the
|
||||
* detected type is Type 1.
|
||||
*/
|
||||
if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL &&
|
||||
if (di->bm->adc_therm == AB8500_ADC_THERM_BATCTRL &&
|
||||
di->bm->batt_id == 1) {
|
||||
dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n");
|
||||
di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -292,7 +292,7 @@ struct ab8500_charger {
|
|||
struct iio_channel *adc_main_charger_c;
|
||||
struct iio_channel *adc_vbus_v;
|
||||
struct iio_channel *adc_usb_charger_c;
|
||||
struct abx500_bm_data *bm;
|
||||
struct ab8500_bm_data *bm;
|
||||
struct ab8500_charger_event_flags flags;
|
||||
struct ab8500_charger_usb_state usb_state;
|
||||
struct ab8500_charger_max_usb_in_curr max_usb_in_curr;
|
||||
|
@ -3388,7 +3388,7 @@ static const struct component_master_ops ab8500_charger_comp_ops = {
|
|||
static struct platform_driver *const ab8500_charger_component_drivers[] = {
|
||||
&ab8500_fg_driver,
|
||||
&ab8500_btemp_driver,
|
||||
&abx500_chargalg_driver,
|
||||
&ab8500_chargalg_driver,
|
||||
};
|
||||
|
||||
static int ab8500_charger_compare_dev(struct device *dev, void *data)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fixp-arith.h>
|
||||
|
||||
#include "ab8500-bm.h"
|
||||
|
||||
|
@ -56,9 +57,6 @@
|
|||
/* FG constants */
|
||||
#define BATT_OVV 0x01
|
||||
|
||||
#define interpolate(x, x1, y1, x2, y2) \
|
||||
((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
|
||||
|
||||
/**
|
||||
* struct ab8500_fg_interrupts - ab8500 fg interrupts
|
||||
* @name: name of the interrupt
|
||||
|
@ -227,7 +225,7 @@ struct ab8500_fg {
|
|||
struct ab8500_fg_avg_cap avg_cap;
|
||||
struct ab8500 *parent;
|
||||
struct iio_channel *main_bat_v;
|
||||
struct abx500_bm_data *bm;
|
||||
struct ab8500_bm_data *bm;
|
||||
struct power_supply *fg_psy;
|
||||
struct workqueue_struct *fg_wq;
|
||||
struct delayed_work fg_periodic_work;
|
||||
|
@ -856,7 +854,7 @@ static int ab8500_fg_bat_voltage(struct ab8500_fg *di)
|
|||
static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
|
||||
{
|
||||
int i, tbl_size;
|
||||
const struct abx500_v_to_cap *tbl;
|
||||
const struct ab8500_v_to_cap *tbl;
|
||||
int cap = 0;
|
||||
|
||||
tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl;
|
||||
|
@ -868,11 +866,12 @@ static int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage)
|
|||
}
|
||||
|
||||
if ((i > 0) && (i < tbl_size)) {
|
||||
cap = interpolate(voltage,
|
||||
cap = fixp_linear_interpolate(
|
||||
tbl[i].voltage,
|
||||
tbl[i].capacity * 10,
|
||||
tbl[i-1].voltage,
|
||||
tbl[i-1].capacity * 10);
|
||||
tbl[i-1].capacity * 10,
|
||||
voltage);
|
||||
} else if (i == 0) {
|
||||
cap = 1000;
|
||||
} else {
|
||||
|
@ -920,11 +919,12 @@ static int ab8500_fg_battery_resistance(struct ab8500_fg *di)
|
|||
}
|
||||
|
||||
if ((i > 0) && (i < tbl_size)) {
|
||||
resist = interpolate(di->bat_temp / 10,
|
||||
resist = fixp_linear_interpolate(
|
||||
tbl[i].temp,
|
||||
tbl[i].resist,
|
||||
tbl[i-1].temp,
|
||||
tbl[i-1].resist);
|
||||
tbl[i-1].resist,
|
||||
di->bat_temp / 10);
|
||||
} else if (i == 0) {
|
||||
resist = tbl[0].resist;
|
||||
} else {
|
||||
|
@ -2235,7 +2235,7 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data)
|
|||
case POWER_SUPPLY_TYPE_BATTERY:
|
||||
if (!di->flags.batt_id_received &&
|
||||
di->bm->batt_id != BATTERY_UNKNOWN) {
|
||||
const struct abx500_battery_type *b;
|
||||
const struct ab8500_battery_type *b;
|
||||
|
||||
b = &(di->bm->bat_type[di->bm->batt_id]);
|
||||
|
||||
|
|
|
@ -813,7 +813,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
if (val == 0)
|
||||
return -ENODEV;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -823,7 +823,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
|
||||
info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
|
||||
if (info->cable.edev == NULL) {
|
||||
dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
|
||||
dev_dbg(dev, "%s is not ready, probe deferred\n",
|
||||
AXP288_EXTCON_DEV_NAME);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
@ -834,8 +834,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
dev_info(&pdev->dev,
|
||||
"Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
|
||||
dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
@ -874,7 +873,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
|
||||
info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
|
||||
if (info->otg.cable) {
|
||||
ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable,
|
||||
ret = devm_extcon_register_notifier(dev, info->otg.cable,
|
||||
EXTCON_USB_HOST, &info->otg.id_nb);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
|
||||
|
@ -899,7 +898,7 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
NULL, axp288_charger_irq_thread_handler,
|
||||
IRQF_ONESHOT, info->pdev->name, info);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request interrupt=%d\n",
|
||||
dev_err(dev, "failed to request interrupt=%d\n",
|
||||
info->irq[i]);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
/*
|
||||
* axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver
|
||||
*
|
||||
* Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (C) 2020-2021 Andrejus Basovas <xxx@yyy.tld>
|
||||
* Copyright (C) 2016-2021 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (C) 2014 Intel Corporation
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -19,38 +20,37 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/iosf_mbi.h>
|
||||
|
||||
#define PS_STAT_VBUS_TRIGGER (1 << 0)
|
||||
#define PS_STAT_BAT_CHRG_DIR (1 << 2)
|
||||
#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3)
|
||||
#define PS_STAT_VBUS_VALID (1 << 4)
|
||||
#define PS_STAT_VBUS_PRESENT (1 << 5)
|
||||
#define PS_STAT_VBUS_TRIGGER (1 << 0)
|
||||
#define PS_STAT_BAT_CHRG_DIR (1 << 2)
|
||||
#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3)
|
||||
#define PS_STAT_VBUS_VALID (1 << 4)
|
||||
#define PS_STAT_VBUS_PRESENT (1 << 5)
|
||||
|
||||
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
|
||||
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
|
||||
#define CHRG_STAT_BAT_VALID (1 << 4)
|
||||
#define CHRG_STAT_BAT_PRESENT (1 << 5)
|
||||
#define CHRG_STAT_BAT_PRESENT (1 << 5)
|
||||
#define CHRG_STAT_CHARGING (1 << 6)
|
||||
#define CHRG_STAT_PMIC_OTP (1 << 7)
|
||||
|
||||
#define CHRG_CCCV_CC_MASK 0xf /* 4 bits */
|
||||
#define CHRG_CCCV_CC_BIT_POS 0
|
||||
#define CHRG_CCCV_CC_BIT_POS 0
|
||||
#define CHRG_CCCV_CC_OFFSET 200 /* 200mA */
|
||||
#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */
|
||||
#define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */
|
||||
#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */
|
||||
#define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */
|
||||
#define CHRG_CCCV_CV_BIT_POS 5
|
||||
#define CHRG_CCCV_CV_BIT_POS 5
|
||||
#define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */
|
||||
#define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */
|
||||
#define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */
|
||||
#define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */
|
||||
#define CHRG_CCCV_CHG_EN (1 << 7)
|
||||
|
||||
#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
|
||||
#define FG_CNTL_OCV_ADJ_STAT (1 << 2)
|
||||
#define FG_CNTL_OCV_ADJ_EN (1 << 3)
|
||||
#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
|
||||
#define FG_CNTL_CAP_ADJ_STAT (1 << 4)
|
||||
#define FG_CNTL_CAP_ADJ_EN (1 << 5)
|
||||
#define FG_CNTL_CC_EN (1 << 6)
|
||||
#define FG_CNTL_GAUGE_EN (1 << 7)
|
||||
|
@ -71,23 +71,23 @@
|
|||
#define FG_CC_CAP_VALID (1 << 7)
|
||||
#define FG_CC_CAP_VAL_MASK 0x7F
|
||||
|
||||
#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */
|
||||
#define FG_LOW_CAP_THR1_MASK 0xf0 /* 5% tp 20% */
|
||||
#define FG_LOW_CAP_THR1_VAL 0xa0 /* 15 perc */
|
||||
#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */
|
||||
#define FG_LOW_CAP_THR2_MASK 0x0f /* 0% to 15% */
|
||||
#define FG_LOW_CAP_WARN_THR 14 /* 14 perc */
|
||||
#define FG_LOW_CAP_CRIT_THR 4 /* 4 perc */
|
||||
#define FG_LOW_CAP_SHDN_THR 0 /* 0 perc */
|
||||
|
||||
#define NR_RETRY_CNT 3
|
||||
#define DEV_NAME "axp288_fuel_gauge"
|
||||
#define DEV_NAME "axp288_fuel_gauge"
|
||||
|
||||
/* 1.1mV per LSB expressed in uV */
|
||||
#define VOLTAGE_FROM_ADC(a) ((a * 11) / 10)
|
||||
/* properties converted to uV, uA */
|
||||
#define PROP_VOLT(a) ((a) * 1000)
|
||||
#define PROP_CURR(a) ((a) * 1000)
|
||||
#define PROP_VOLT(a) ((a) * 1000)
|
||||
#define PROP_CURR(a) ((a) * 1000)
|
||||
|
||||
#define AXP288_FG_INTR_NUM 6
|
||||
#define AXP288_REG_UPDATE_INTERVAL (60 * HZ)
|
||||
#define AXP288_FG_INTR_NUM 6
|
||||
enum {
|
||||
QWBTU_IRQ = 0,
|
||||
WBTU_IRQ,
|
||||
|
@ -98,9 +98,6 @@ enum {
|
|||
};
|
||||
|
||||
enum {
|
||||
BAT_TEMP = 0,
|
||||
PMIC_TEMP,
|
||||
SYSTEM_TEMP,
|
||||
BAT_CHRG_CURR,
|
||||
BAT_D_CURR,
|
||||
BAT_VOLT,
|
||||
|
@ -108,7 +105,7 @@ enum {
|
|||
};
|
||||
|
||||
struct axp288_fg_info {
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip_data *regmap_irqc;
|
||||
int irq[AXP288_FG_INTR_NUM];
|
||||
|
@ -117,7 +114,21 @@ struct axp288_fg_info {
|
|||
struct mutex lock;
|
||||
int status;
|
||||
int max_volt;
|
||||
int pwr_op;
|
||||
int low_cap;
|
||||
struct dentry *debug_file;
|
||||
|
||||
char valid; /* zero until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
int pwr_stat;
|
||||
int fg_res;
|
||||
int bat_volt;
|
||||
int d_curr;
|
||||
int c_curr;
|
||||
int ocv;
|
||||
int fg_cc_mtr1;
|
||||
int fg_des_cap1;
|
||||
};
|
||||
|
||||
static enum power_supply_property fuel_gauge_props[] = {
|
||||
|
@ -137,17 +148,12 @@ static enum power_supply_property fuel_gauge_props[] = {
|
|||
|
||||
static int fuel_gauge_reg_readb(struct axp288_fg_info *info, int reg)
|
||||
{
|
||||
int ret, i;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < NR_RETRY_CNT; i++) {
|
||||
ret = regmap_read(info->regmap, reg, &val);
|
||||
if (ret != -EBUSY)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = regmap_read(info->regmap, reg, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
|
||||
dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -161,7 +167,7 @@ static int fuel_gauge_reg_writeb(struct axp288_fg_info *info, int reg, u8 val)
|
|||
ret = regmap_write(info->regmap, reg, (unsigned int)val);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "axp288 reg write err:%d\n", ret);
|
||||
dev_err(info->dev, "Error writing reg 0x%02x err: %d\n", reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -173,15 +179,13 @@ static int fuel_gauge_read_15bit_word(struct axp288_fg_info *info, int reg)
|
|||
|
||||
ret = regmap_bulk_read(info->regmap, reg, buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
|
||||
reg, ret);
|
||||
dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = get_unaligned_be16(buf);
|
||||
if (!(ret & FG_15BIT_WORD_VALID)) {
|
||||
dev_err(&info->pdev->dev, "Error reg 0x%02x contents not valid\n",
|
||||
reg);
|
||||
dev_err(info->dev, "Error reg 0x%02x contents not valid\n", reg);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
|
@ -195,8 +199,7 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
|
|||
|
||||
ret = regmap_bulk_read(info->regmap, reg, buf, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "Error reading reg 0x%02x err: %d\n",
|
||||
reg, ret);
|
||||
dev_err(info->dev, "Error reading reg 0x%02x err: %d\n", reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -204,139 +207,78 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
|
|||
return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int fuel_gauge_debug_show(struct seq_file *s, void *data)
|
||||
static int fuel_gauge_update_registers(struct axp288_fg_info *info)
|
||||
{
|
||||
struct axp288_fg_info *info = s->private;
|
||||
int raw_val, ret;
|
||||
int ret;
|
||||
|
||||
seq_printf(s, " PWR_STATUS[%02x] : %02x\n",
|
||||
AXP20X_PWR_INPUT_STATUS,
|
||||
fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS));
|
||||
seq_printf(s, "PWR_OP_MODE[%02x] : %02x\n",
|
||||
AXP20X_PWR_OP_MODE,
|
||||
fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE));
|
||||
seq_printf(s, " CHRG_CTRL1[%02x] : %02x\n",
|
||||
AXP20X_CHRG_CTRL1,
|
||||
fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1));
|
||||
seq_printf(s, " VLTF[%02x] : %02x\n",
|
||||
AXP20X_V_LTF_DISCHRG,
|
||||
fuel_gauge_reg_readb(info, AXP20X_V_LTF_DISCHRG));
|
||||
seq_printf(s, " VHTF[%02x] : %02x\n",
|
||||
AXP20X_V_HTF_DISCHRG,
|
||||
fuel_gauge_reg_readb(info, AXP20X_V_HTF_DISCHRG));
|
||||
seq_printf(s, " CC_CTRL[%02x] : %02x\n",
|
||||
AXP20X_CC_CTRL,
|
||||
fuel_gauge_reg_readb(info, AXP20X_CC_CTRL));
|
||||
seq_printf(s, "BATTERY CAP[%02x] : %02x\n",
|
||||
AXP20X_FG_RES,
|
||||
fuel_gauge_reg_readb(info, AXP20X_FG_RES));
|
||||
seq_printf(s, " FG_RDC1[%02x] : %02x\n",
|
||||
AXP288_FG_RDC1_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_RDC1_REG));
|
||||
seq_printf(s, " FG_RDC0[%02x] : %02x\n",
|
||||
AXP288_FG_RDC0_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_RDC0_REG));
|
||||
seq_printf(s, " FG_OCV[%02x] : %04x\n",
|
||||
AXP288_FG_OCVH_REG,
|
||||
fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG));
|
||||
seq_printf(s, " FG_DES_CAP[%02x] : %04x\n",
|
||||
AXP288_FG_DES_CAP1_REG,
|
||||
fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG));
|
||||
seq_printf(s, " FG_CC_MTR[%02x] : %04x\n",
|
||||
AXP288_FG_CC_MTR1_REG,
|
||||
fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG));
|
||||
seq_printf(s, " FG_OCV_CAP[%02x] : %02x\n",
|
||||
AXP288_FG_OCV_CAP_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_OCV_CAP_REG));
|
||||
seq_printf(s, " FG_CC_CAP[%02x] : %02x\n",
|
||||
AXP288_FG_CC_CAP_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_CC_CAP_REG));
|
||||
seq_printf(s, " FG_LOW_CAP[%02x] : %02x\n",
|
||||
AXP288_FG_LOW_CAP_REG,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG));
|
||||
seq_printf(s, "TUNING_CTL0[%02x] : %02x\n",
|
||||
AXP288_FG_TUNE0,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_TUNE0));
|
||||
seq_printf(s, "TUNING_CTL1[%02x] : %02x\n",
|
||||
AXP288_FG_TUNE1,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_TUNE1));
|
||||
seq_printf(s, "TUNING_CTL2[%02x] : %02x\n",
|
||||
AXP288_FG_TUNE2,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_TUNE2));
|
||||
seq_printf(s, "TUNING_CTL3[%02x] : %02x\n",
|
||||
AXP288_FG_TUNE3,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_TUNE3));
|
||||
seq_printf(s, "TUNING_CTL4[%02x] : %02x\n",
|
||||
AXP288_FG_TUNE4,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_TUNE4));
|
||||
seq_printf(s, "TUNING_CTL5[%02x] : %02x\n",
|
||||
AXP288_FG_TUNE5,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_TUNE5));
|
||||
if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))
|
||||
return 0;
|
||||
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_TEMP], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-batttemp : %d\n", raw_val);
|
||||
ret = iio_read_channel_raw(info->iio_channel[PMIC_TEMP], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-pmictemp : %d\n", raw_val);
|
||||
ret = iio_read_channel_raw(info->iio_channel[SYSTEM_TEMP], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-systtemp : %d\n", raw_val);
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-chrgcurr : %d\n", raw_val);
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-dchrgcur : %d\n", raw_val);
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-battvolt : %d\n", raw_val);
|
||||
dev_dbg(info->dev, "Fuel Gauge updating register values...\n");
|
||||
|
||||
return 0;
|
||||
ret = iosf_mbi_block_punit_i2c_access();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
info->pwr_stat = ret;
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
info->fg_res = ret;
|
||||
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &info->bat_volt);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (info->pwr_stat & PS_STAT_BAT_CHRG_DIR) {
|
||||
info->d_curr = 0;
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &info->c_curr);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
} else {
|
||||
info->c_curr = 0;
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &info->d_curr);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
info->ocv = ret;
|
||||
|
||||
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
info->fg_cc_mtr1 = ret;
|
||||
|
||||
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
info->fg_des_cap1 = ret;
|
||||
|
||||
info->last_updated = jiffies;
|
||||
info->valid = 1;
|
||||
ret = 0;
|
||||
out:
|
||||
iosf_mbi_unblock_punit_i2c_access();
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(fuel_gauge_debug);
|
||||
|
||||
static void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
|
||||
{
|
||||
info->debug_file = debugfs_create_file("fuelgauge", 0666, NULL,
|
||||
info, &fuel_gauge_debug_fops);
|
||||
}
|
||||
|
||||
static void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
|
||||
{
|
||||
debugfs_remove(info->debug_file);
|
||||
}
|
||||
#else
|
||||
static inline void fuel_gauge_create_debugfs(struct axp288_fg_info *info)
|
||||
{
|
||||
}
|
||||
static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void fuel_gauge_get_status(struct axp288_fg_info *info)
|
||||
{
|
||||
int pwr_stat, fg_res, curr, ret;
|
||||
|
||||
pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
|
||||
if (pwr_stat < 0) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"PWR STAT read failed:%d\n", pwr_stat);
|
||||
return;
|
||||
}
|
||||
int pwr_stat = info->pwr_stat;
|
||||
int fg_res = info->fg_res;
|
||||
int curr = info->d_curr;
|
||||
|
||||
/* Report full if Vbus is valid and the reported capacity is 100% */
|
||||
if (!(pwr_stat & PS_STAT_VBUS_VALID))
|
||||
goto not_full;
|
||||
|
||||
fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
|
||||
if (fg_res < 0) {
|
||||
dev_err(&info->pdev->dev, "FG RES read failed: %d\n", fg_res);
|
||||
return;
|
||||
}
|
||||
if (!(fg_res & FG_REP_CAP_VALID))
|
||||
goto not_full;
|
||||
|
||||
|
@ -354,11 +296,6 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
|
|||
if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR))
|
||||
goto not_full;
|
||||
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &curr);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "FG get current failed: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
if (curr == 0) {
|
||||
info->status = POWER_SUPPLY_STATUS_FULL;
|
||||
return;
|
||||
|
@ -371,61 +308,16 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
|
|||
info->status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
}
|
||||
|
||||
static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
|
||||
{
|
||||
int ret = 0, raw_val;
|
||||
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
|
||||
if (ret < 0)
|
||||
goto vbatt_read_fail;
|
||||
|
||||
*vbatt = VOLTAGE_FROM_ADC(raw_val);
|
||||
vbatt_read_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)
|
||||
{
|
||||
int ret, discharge;
|
||||
|
||||
/* First check discharge current, so that we do only 1 read on bat. */
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (discharge > 0) {
|
||||
*cur = -1 * discharge;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur);
|
||||
}
|
||||
|
||||
static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = fuel_gauge_read_12bit_word(info, AXP288_FG_OCVH_REG);
|
||||
if (ret >= 0)
|
||||
*vocv = VOLTAGE_FROM_ADC(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fuel_gauge_battery_health(struct axp288_fg_info *info)
|
||||
{
|
||||
int ret, vocv, health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
|
||||
ret = fuel_gauge_get_vocv(info, &vocv);
|
||||
if (ret < 0)
|
||||
goto health_read_fail;
|
||||
int vocv = VOLTAGE_FROM_ADC(info->ocv);
|
||||
int health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
|
||||
if (vocv > info->max_volt)
|
||||
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
else
|
||||
health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
|
||||
health_read_fail:
|
||||
return health;
|
||||
}
|
||||
|
||||
|
@ -434,9 +326,14 @@ static int fuel_gauge_get_property(struct power_supply *ps,
|
|||
union power_supply_propval *val)
|
||||
{
|
||||
struct axp288_fg_info *info = power_supply_get_drvdata(ps);
|
||||
int ret = 0, value;
|
||||
int ret, value;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
ret = fuel_gauge_update_registers(info);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
switch (prop) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
fuel_gauge_get_status(info);
|
||||
|
@ -446,78 +343,52 @@ static int fuel_gauge_get_property(struct power_supply *ps,
|
|||
val->intval = fuel_gauge_battery_health(info);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = fuel_gauge_get_vbatt(info, &value);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
value = VOLTAGE_FROM_ADC(info->bat_volt);
|
||||
val->intval = PROP_VOLT(value);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
|
||||
ret = fuel_gauge_get_vocv(info, &value);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
value = VOLTAGE_FROM_ADC(info->ocv);
|
||||
val->intval = PROP_VOLT(value);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
ret = fuel_gauge_get_current(info, &value);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
if (info->d_curr > 0)
|
||||
value = -1 * info->d_curr;
|
||||
else
|
||||
value = info->c_curr;
|
||||
|
||||
val->intval = PROP_CURR(value);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
|
||||
if (ret & CHRG_STAT_BAT_PRESENT)
|
||||
if (info->pwr_op & CHRG_STAT_BAT_PRESENT)
|
||||
val->intval = 1;
|
||||
else
|
||||
val->intval = 0;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
|
||||
if (!(ret & FG_REP_CAP_VALID))
|
||||
dev_err(&info->pdev->dev,
|
||||
"capacity measurement not valid\n");
|
||||
val->intval = (ret & FG_REP_CAP_VAL_MASK);
|
||||
if (!(info->fg_res & FG_REP_CAP_VALID))
|
||||
dev_err(info->dev, "capacity measurement not valid\n");
|
||||
val->intval = (info->fg_res & FG_REP_CAP_VAL_MASK);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
val->intval = (ret & 0x0f);
|
||||
val->intval = (info->low_cap & 0x0f);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_CC_MTR1_REG);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
|
||||
val->intval = ret * FG_DES_CAP_RES_LSB;
|
||||
val->intval = info->fg_cc_mtr1 * FG_DES_CAP_RES_LSB;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
ret = fuel_gauge_read_15bit_word(info, AXP288_FG_DES_CAP1_REG);
|
||||
if (ret < 0)
|
||||
goto fuel_gauge_read_err;
|
||||
|
||||
val->intval = ret * FG_DES_CAP_RES_LSB;
|
||||
val->intval = info->fg_des_cap1 * FG_DES_CAP_RES_LSB;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
val->intval = PROP_VOLT(info->max_volt);
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&info->lock);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&info->lock);
|
||||
return 0;
|
||||
|
||||
fuel_gauge_read_err:
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -527,7 +398,7 @@ static int fuel_gauge_set_property(struct power_supply *ps,
|
|||
const union power_supply_propval *val)
|
||||
{
|
||||
struct axp288_fg_info *info = power_supply_get_drvdata(ps);
|
||||
int ret = 0;
|
||||
int new_low_cap, ret = 0;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
switch (prop) {
|
||||
|
@ -536,12 +407,12 @@ static int fuel_gauge_set_property(struct power_supply *ps,
|
|||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret &= 0xf0;
|
||||
ret |= (val->intval & 0xf);
|
||||
ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, ret);
|
||||
new_low_cap = info->low_cap;
|
||||
new_low_cap &= 0xf0;
|
||||
new_low_cap |= (val->intval & 0xf);
|
||||
ret = fuel_gauge_reg_writeb(info, AXP288_FG_LOW_CAP_REG, new_low_cap);
|
||||
if (ret == 0)
|
||||
info->low_cap = new_low_cap;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -579,37 +450,35 @@ static irqreturn_t fuel_gauge_thread_handler(int irq, void *dev)
|
|||
}
|
||||
|
||||
if (i >= AXP288_FG_INTR_NUM) {
|
||||
dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
|
||||
dev_warn(info->dev, "spurious interrupt!!\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case QWBTU_IRQ:
|
||||
dev_info(&info->pdev->dev,
|
||||
"Quit Battery under temperature in work mode IRQ (QWBTU)\n");
|
||||
dev_info(info->dev, "Quit Battery under temperature in work mode IRQ (QWBTU)\n");
|
||||
break;
|
||||
case WBTU_IRQ:
|
||||
dev_info(&info->pdev->dev,
|
||||
"Battery under temperature in work mode IRQ (WBTU)\n");
|
||||
dev_info(info->dev, "Battery under temperature in work mode IRQ (WBTU)\n");
|
||||
break;
|
||||
case QWBTO_IRQ:
|
||||
dev_info(&info->pdev->dev,
|
||||
"Quit Battery over temperature in work mode IRQ (QWBTO)\n");
|
||||
dev_info(info->dev, "Quit Battery over temperature in work mode IRQ (QWBTO)\n");
|
||||
break;
|
||||
case WBTO_IRQ:
|
||||
dev_info(&info->pdev->dev,
|
||||
"Battery over temperature in work mode IRQ (WBTO)\n");
|
||||
dev_info(info->dev, "Battery over temperature in work mode IRQ (WBTO)\n");
|
||||
break;
|
||||
case WL2_IRQ:
|
||||
dev_info(&info->pdev->dev, "Low Batt Warning(2) INTR\n");
|
||||
dev_info(info->dev, "Low Batt Warning(2) INTR\n");
|
||||
break;
|
||||
case WL1_IRQ:
|
||||
dev_info(&info->pdev->dev, "Low Batt Warning(1) INTR\n");
|
||||
dev_info(info->dev, "Low Batt Warning(1) INTR\n");
|
||||
break;
|
||||
default:
|
||||
dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
|
||||
dev_warn(info->dev, "Spurious Interrupt!!!\n");
|
||||
}
|
||||
|
||||
info->valid = 0; /* Force updating of the cached registers */
|
||||
|
||||
power_supply_changed(info->bat);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -618,6 +487,7 @@ static void fuel_gauge_external_power_changed(struct power_supply *psy)
|
|||
{
|
||||
struct axp288_fg_info *info = power_supply_get_drvdata(psy);
|
||||
|
||||
info->valid = 0; /* Force updating of the cached registers */
|
||||
power_supply_changed(info->bat);
|
||||
}
|
||||
|
||||
|
@ -632,16 +502,15 @@ static const struct power_supply_desc fuel_gauge_desc = {
|
|||
.external_power_changed = fuel_gauge_external_power_changed,
|
||||
};
|
||||
|
||||
static void fuel_gauge_init_irq(struct axp288_fg_info *info)
|
||||
static void fuel_gauge_init_irq(struct axp288_fg_info *info, struct platform_device *pdev)
|
||||
{
|
||||
int ret, i, pirq;
|
||||
|
||||
for (i = 0; i < AXP288_FG_INTR_NUM; i++) {
|
||||
pirq = platform_get_irq(info->pdev, i);
|
||||
pirq = platform_get_irq(pdev, i);
|
||||
info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
|
||||
if (info->irq[i] < 0) {
|
||||
dev_warn(&info->pdev->dev,
|
||||
"regmap_irq get virq failed for IRQ %d: %d\n",
|
||||
dev_warn(info->dev, "regmap_irq get virq failed for IRQ %d: %d\n",
|
||||
pirq, info->irq[i]);
|
||||
info->irq[i] = -1;
|
||||
goto intr_failed;
|
||||
|
@ -650,14 +519,10 @@ static void fuel_gauge_init_irq(struct axp288_fg_info *info)
|
|||
NULL, fuel_gauge_thread_handler,
|
||||
IRQF_ONESHOT, DEV_NAME, info);
|
||||
if (ret) {
|
||||
dev_warn(&info->pdev->dev,
|
||||
"request irq failed for IRQ %d: %d\n",
|
||||
dev_warn(info->dev, "request irq failed for IRQ %d: %d\n",
|
||||
pirq, info->irq[i]);
|
||||
info->irq[i] = -1;
|
||||
goto intr_failed;
|
||||
} else {
|
||||
dev_info(&info->pdev->dev, "HW IRQ %d -> VIRQ %d\n",
|
||||
pirq, info->irq[i]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -753,9 +618,6 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct power_supply_config psy_cfg = {};
|
||||
static const char * const iio_chan_name[] = {
|
||||
[BAT_TEMP] = "axp288-batt-temp",
|
||||
[PMIC_TEMP] = "axp288-pmic-temp",
|
||||
[SYSTEM_TEMP] = "axp288-system-temp",
|
||||
[BAT_CHRG_CURR] = "axp288-chrg-curr",
|
||||
[BAT_D_CURR] = "axp288-chrg-d-curr",
|
||||
[BAT_VOLT] = "axp288-batt-volt",
|
||||
|
@ -765,24 +627,15 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
if (dmi_check_system(axp288_no_battery_list))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* On some devices the fuelgauge and charger parts of the axp288 are
|
||||
* not used, check that the fuelgauge is enabled (CC_CTRL != 0).
|
||||
*/
|
||||
ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (val == 0)
|
||||
return -ENODEV;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->pdev = pdev;
|
||||
info->dev = &pdev->dev;
|
||||
info->regmap = axp20x->regmap;
|
||||
info->regmap_irqc = axp20x->regmap_irqc;
|
||||
info->status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
info->valid = 0;
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
|
@ -808,19 +661,35 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
|
||||
ret = iosf_mbi_block_punit_i2c_access();
|
||||
if (ret < 0)
|
||||
goto out_free_iio_chan;
|
||||
|
||||
/*
|
||||
* On some devices the fuelgauge and charger parts of the axp288 are
|
||||
* not used, check that the fuelgauge is enabled (CC_CTRL != 0).
|
||||
*/
|
||||
ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
|
||||
if (ret < 0)
|
||||
goto unblock_punit_i2c_access;
|
||||
if (val == 0) {
|
||||
ret = -ENODEV;
|
||||
goto unblock_punit_i2c_access;
|
||||
}
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
|
||||
if (ret < 0)
|
||||
goto unblock_punit_i2c_access;
|
||||
|
||||
if (!(ret & FG_DES_CAP1_VALID)) {
|
||||
dev_err(&pdev->dev, "axp288 not configured by firmware\n");
|
||||
ret = -ENODEV;
|
||||
goto out_free_iio_chan;
|
||||
goto unblock_punit_i2c_access;
|
||||
}
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
|
||||
if (ret < 0)
|
||||
goto out_free_iio_chan;
|
||||
goto unblock_punit_i2c_access;
|
||||
switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
|
||||
case CHRG_CCCV_CV_4100MV:
|
||||
info->max_volt = 4100;
|
||||
|
@ -836,6 +705,22 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
break;
|
||||
}
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_PWR_OP_MODE);
|
||||
if (ret < 0)
|
||||
goto unblock_punit_i2c_access;
|
||||
info->pwr_op = ret;
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_LOW_CAP_REG);
|
||||
if (ret < 0)
|
||||
goto unblock_punit_i2c_access;
|
||||
info->low_cap = ret;
|
||||
|
||||
unblock_punit_i2c_access:
|
||||
iosf_mbi_unblock_punit_i2c_access();
|
||||
/* In case we arrive here by goto because of a register access error */
|
||||
if (ret < 0)
|
||||
goto out_free_iio_chan;
|
||||
|
||||
psy_cfg.drv_data = info;
|
||||
info->bat = power_supply_register(&pdev->dev, &fuel_gauge_desc, &psy_cfg);
|
||||
if (IS_ERR(info->bat)) {
|
||||
|
@ -844,8 +729,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
goto out_free_iio_chan;
|
||||
}
|
||||
|
||||
fuel_gauge_create_debugfs(info);
|
||||
fuel_gauge_init_irq(info);
|
||||
fuel_gauge_init_irq(info, pdev);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -869,7 +753,6 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev)
|
|||
int i;
|
||||
|
||||
power_supply_unregister(info->bat);
|
||||
fuel_gauge_remove_debugfs(info);
|
||||
|
||||
for (i = 0; i < AXP288_FG_INTR_NUM; i++)
|
||||
if (info->irq[i] >= 0)
|
||||
|
|
|
@ -31,9 +31,8 @@
|
|||
|
||||
#include <linux/power/bq24735-charger.h>
|
||||
|
||||
#define BQ24735_CHG_OPT 0x12
|
||||
#define BQ24735_CHG_OPT_CHARGE_DISABLE (1 << 0)
|
||||
#define BQ24735_CHG_OPT_AC_PRESENT (1 << 4)
|
||||
/* BQ24735 available commands and their respective masks */
|
||||
#define BQ24735_CHARGE_OPT 0x12
|
||||
#define BQ24735_CHARGE_CURRENT 0x14
|
||||
#define BQ24735_CHARGE_CURRENT_MASK 0x1fc0
|
||||
#define BQ24735_CHARGE_VOLTAGE 0x15
|
||||
|
@ -43,6 +42,10 @@
|
|||
#define BQ24735_MANUFACTURER_ID 0xfe
|
||||
#define BQ24735_DEVICE_ID 0xff
|
||||
|
||||
/* ChargeOptions bits of interest */
|
||||
#define BQ24735_CHARGE_OPT_CHG_DISABLE (1 << 0)
|
||||
#define BQ24735_CHARGE_OPT_AC_PRESENT (1 << 4)
|
||||
|
||||
struct bq24735 {
|
||||
struct power_supply *charger;
|
||||
struct power_supply_desc charger_desc;
|
||||
|
@ -167,8 +170,8 @@ static inline int bq24735_enable_charging(struct bq24735 *charger)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
|
||||
BQ24735_CHG_OPT_CHARGE_DISABLE, 0);
|
||||
return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT,
|
||||
BQ24735_CHARGE_OPT_CHG_DISABLE, 0);
|
||||
}
|
||||
|
||||
static inline int bq24735_disable_charging(struct bq24735 *charger)
|
||||
|
@ -176,9 +179,9 @@ static inline int bq24735_disable_charging(struct bq24735 *charger)
|
|||
if (charger->pdata->ext_control)
|
||||
return 0;
|
||||
|
||||
return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
|
||||
BQ24735_CHG_OPT_CHARGE_DISABLE,
|
||||
BQ24735_CHG_OPT_CHARGE_DISABLE);
|
||||
return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT,
|
||||
BQ24735_CHARGE_OPT_CHG_DISABLE,
|
||||
BQ24735_CHARGE_OPT_CHG_DISABLE);
|
||||
}
|
||||
|
||||
static bool bq24735_charger_is_present(struct bq24735 *charger)
|
||||
|
@ -188,14 +191,14 @@ static bool bq24735_charger_is_present(struct bq24735 *charger)
|
|||
} else {
|
||||
int ac = 0;
|
||||
|
||||
ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
|
||||
ac = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT);
|
||||
if (ac < 0) {
|
||||
dev_dbg(&charger->client->dev,
|
||||
"Failed to read charger options : %d\n",
|
||||
ac);
|
||||
return false;
|
||||
}
|
||||
return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
|
||||
return (ac & BQ24735_CHARGE_OPT_AC_PRESENT) ? true : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -208,11 +211,11 @@ static int bq24735_charger_is_charging(struct bq24735 *charger)
|
|||
if (!bq24735_charger_is_present(charger))
|
||||
return 0;
|
||||
|
||||
ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
|
||||
ret = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
|
||||
return !(ret & BQ24735_CHARGE_OPT_CHG_DISABLE);
|
||||
}
|
||||
|
||||
static void bq24735_update(struct bq24735 *charger)
|
||||
|
|
|
@ -0,0 +1,386 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Power supply driver for ChromeOS EC based Peripheral Device Charger.
|
||||
*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_data/cros_ec_commands.h>
|
||||
#include <linux/platform_data/cros_ec_proto.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DRV_NAME "cros-ec-pchg"
|
||||
#define PCHG_DIR_PREFIX "peripheral"
|
||||
#define PCHG_DIR_NAME PCHG_DIR_PREFIX "%d"
|
||||
#define PCHG_DIR_NAME_LENGTH \
|
||||
sizeof(PCHG_DIR_PREFIX __stringify(EC_PCHG_MAX_PORTS))
|
||||
#define PCHG_CACHE_UPDATE_DELAY msecs_to_jiffies(500)
|
||||
|
||||
struct port_data {
|
||||
int port_number;
|
||||
char name[PCHG_DIR_NAME_LENGTH];
|
||||
struct power_supply *psy;
|
||||
struct power_supply_desc psy_desc;
|
||||
int psy_status;
|
||||
int battery_percentage;
|
||||
int charge_type;
|
||||
struct charger_data *charger;
|
||||
unsigned long last_update;
|
||||
};
|
||||
|
||||
struct charger_data {
|
||||
struct device *dev;
|
||||
struct cros_ec_dev *ec_dev;
|
||||
struct cros_ec_device *ec_device;
|
||||
int num_registered_psy;
|
||||
struct port_data *ports[EC_PCHG_MAX_PORTS];
|
||||
struct notifier_block notifier;
|
||||
};
|
||||
|
||||
static enum power_supply_property cros_pchg_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
};
|
||||
|
||||
static int cros_pchg_ec_command(const struct charger_data *charger,
|
||||
unsigned int version,
|
||||
unsigned int command,
|
||||
const void *outdata,
|
||||
unsigned int outsize,
|
||||
void *indata,
|
||||
unsigned int insize)
|
||||
{
|
||||
struct cros_ec_dev *ec_dev = charger->ec_dev;
|
||||
struct cros_ec_command *msg;
|
||||
int ret;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->version = version;
|
||||
msg->command = ec_dev->cmd_offset + command;
|
||||
msg->outsize = outsize;
|
||||
msg->insize = insize;
|
||||
|
||||
if (outsize)
|
||||
memcpy(msg->data, outdata, outsize);
|
||||
|
||||
ret = cros_ec_cmd_xfer_status(charger->ec_device, msg);
|
||||
if (ret >= 0 && insize)
|
||||
memcpy(indata, msg->data, insize);
|
||||
|
||||
kfree(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const unsigned int pchg_cmd_version = 1;
|
||||
|
||||
static bool cros_pchg_cmd_ver_check(const struct charger_data *charger)
|
||||
{
|
||||
struct ec_params_get_cmd_versions_v1 req;
|
||||
struct ec_response_get_cmd_versions rsp;
|
||||
int ret;
|
||||
|
||||
req.cmd = EC_CMD_PCHG;
|
||||
ret = cros_pchg_ec_command(charger, 1, EC_CMD_GET_CMD_VERSIONS,
|
||||
&req, sizeof(req), &rsp, sizeof(rsp));
|
||||
if (ret < 0) {
|
||||
dev_warn(charger->dev,
|
||||
"Unable to get versions of EC_CMD_PCHG (err:%d)\n",
|
||||
ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!(rsp.version_mask & BIT(pchg_cmd_version));
|
||||
}
|
||||
|
||||
static int cros_pchg_port_count(const struct charger_data *charger)
|
||||
{
|
||||
struct ec_response_pchg_count rsp;
|
||||
int ret;
|
||||
|
||||
ret = cros_pchg_ec_command(charger, 0, EC_CMD_PCHG_COUNT,
|
||||
NULL, 0, &rsp, sizeof(rsp));
|
||||
if (ret < 0) {
|
||||
dev_warn(charger->dev,
|
||||
"Unable to get number or ports (err:%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return rsp.port_count;
|
||||
}
|
||||
|
||||
static int cros_pchg_get_status(struct port_data *port)
|
||||
{
|
||||
struct charger_data *charger = port->charger;
|
||||
struct ec_params_pchg req;
|
||||
struct ec_response_pchg rsp;
|
||||
struct device *dev = charger->dev;
|
||||
int old_status = port->psy_status;
|
||||
int old_percentage = port->battery_percentage;
|
||||
int ret;
|
||||
|
||||
req.port = port->port_number;
|
||||
ret = cros_pchg_ec_command(charger, pchg_cmd_version, EC_CMD_PCHG,
|
||||
&req, sizeof(req), &rsp, sizeof(rsp));
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Unable to get port.%d status (err:%d)\n",
|
||||
port->port_number, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (rsp.state) {
|
||||
case PCHG_STATE_RESET:
|
||||
case PCHG_STATE_INITIALIZED:
|
||||
case PCHG_STATE_ENABLED:
|
||||
default:
|
||||
port->psy_status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
break;
|
||||
case PCHG_STATE_DETECTED:
|
||||
port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
port->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
||||
break;
|
||||
case PCHG_STATE_CHARGING:
|
||||
port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
port->charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
|
||||
break;
|
||||
case PCHG_STATE_FULL:
|
||||
port->psy_status = POWER_SUPPLY_STATUS_FULL;
|
||||
port->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
port->battery_percentage = rsp.battery_percentage;
|
||||
|
||||
if (port->psy_status != old_status ||
|
||||
port->battery_percentage != old_percentage)
|
||||
power_supply_changed(port->psy);
|
||||
|
||||
dev_dbg(dev,
|
||||
"Port %d: state=%d battery=%d%%\n",
|
||||
port->port_number, rsp.state, rsp.battery_percentage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_pchg_get_port_status(struct port_data *port, bool ratelimit)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ratelimit &&
|
||||
time_is_after_jiffies(port->last_update + PCHG_CACHE_UPDATE_DELAY))
|
||||
return 0;
|
||||
|
||||
ret = cros_pchg_get_status(port);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
port->last_update = jiffies;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_pchg_get_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct port_data *port = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
cros_pchg_get_port_status(port, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = port->psy_status;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = port->battery_percentage;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
val->intval = port->charge_type;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_pchg_event(const struct charger_data *charger,
|
||||
unsigned long host_event)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < charger->num_registered_psy; i++)
|
||||
cros_pchg_get_port_status(charger->ports[i], false);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static u32 cros_get_device_event(const struct charger_data *charger)
|
||||
{
|
||||
struct ec_params_device_event req;
|
||||
struct ec_response_device_event rsp;
|
||||
struct device *dev = charger->dev;
|
||||
int ret;
|
||||
|
||||
req.param = EC_DEVICE_EVENT_PARAM_GET_CURRENT_EVENTS;
|
||||
ret = cros_pchg_ec_command(charger, 0, EC_CMD_DEVICE_EVENT,
|
||||
&req, sizeof(req), &rsp, sizeof(rsp));
|
||||
if (ret < 0) {
|
||||
dev_warn(dev, "Unable to get device events (err:%d)\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return rsp.event_mask;
|
||||
}
|
||||
|
||||
static int cros_ec_notify(struct notifier_block *nb,
|
||||
unsigned long queued_during_suspend,
|
||||
void *data)
|
||||
{
|
||||
struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
|
||||
u32 host_event = cros_ec_get_host_event(ec_dev);
|
||||
struct charger_data *charger =
|
||||
container_of(nb, struct charger_data, notifier);
|
||||
u32 device_event_mask;
|
||||
|
||||
if (!host_event)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (!(host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_DEVICE)))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
/*
|
||||
* todo: Retrieve device event mask in common place
|
||||
* (e.g. cros_ec_proto.c).
|
||||
*/
|
||||
device_event_mask = cros_get_device_event(charger);
|
||||
if (!(device_event_mask & EC_DEVICE_EVENT_MASK(EC_DEVICE_EVENT_WLC)))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
return cros_pchg_event(charger, host_event);
|
||||
}
|
||||
|
||||
static int cros_pchg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
|
||||
struct cros_ec_device *ec_device = ec_dev->ec_dev;
|
||||
struct power_supply_desc *psy_desc;
|
||||
struct charger_data *charger;
|
||||
struct power_supply *psy;
|
||||
struct port_data *port;
|
||||
struct notifier_block *nb;
|
||||
int num_ports;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL);
|
||||
if (!charger)
|
||||
return -ENOMEM;
|
||||
|
||||
charger->dev = dev;
|
||||
charger->ec_dev = ec_dev;
|
||||
charger->ec_device = ec_device;
|
||||
|
||||
ret = cros_pchg_port_count(charger);
|
||||
if (ret <= 0) {
|
||||
/*
|
||||
* This feature is enabled by the EC and the kernel driver is
|
||||
* included by default for CrOS devices. Don't need to be loud
|
||||
* since this error can be normal.
|
||||
*/
|
||||
dev_info(dev, "No peripheral charge ports (err:%d)\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!cros_pchg_cmd_ver_check(charger)) {
|
||||
dev_err(dev, "EC_CMD_PCHG version %d isn't available.\n",
|
||||
pchg_cmd_version);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
num_ports = ret;
|
||||
if (num_ports > EC_PCHG_MAX_PORTS) {
|
||||
dev_err(dev, "Too many peripheral charge ports (%d)\n",
|
||||
num_ports);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
dev_info(dev, "%d peripheral charge ports found\n", num_ports);
|
||||
|
||||
for (i = 0; i < num_ports; i++) {
|
||||
struct power_supply_config psy_cfg = {};
|
||||
|
||||
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
port->charger = charger;
|
||||
port->port_number = i;
|
||||
snprintf(port->name, sizeof(port->name), PCHG_DIR_NAME, i);
|
||||
|
||||
psy_desc = &port->psy_desc;
|
||||
psy_desc->name = port->name;
|
||||
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
psy_desc->get_property = cros_pchg_get_prop;
|
||||
psy_desc->external_power_changed = NULL;
|
||||
psy_desc->properties = cros_pchg_props;
|
||||
psy_desc->num_properties = ARRAY_SIZE(cros_pchg_props);
|
||||
psy_cfg.drv_data = port;
|
||||
|
||||
psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
|
||||
if (IS_ERR(psy))
|
||||
return dev_err_probe(dev, PTR_ERR(psy),
|
||||
"Failed to register power supply\n");
|
||||
port->psy = psy;
|
||||
|
||||
charger->ports[charger->num_registered_psy++] = port;
|
||||
}
|
||||
|
||||
if (!charger->num_registered_psy)
|
||||
return -ENODEV;
|
||||
|
||||
nb = &charger->notifier;
|
||||
nb->notifier_call = cros_ec_notify;
|
||||
ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier,
|
||||
nb);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to register notifier (err:%d)\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cros_pchg_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
},
|
||||
.probe = cros_pchg_probe
|
||||
};
|
||||
|
||||
module_platform_driver(cros_pchg_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("ChromeOS EC peripheral device charger");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
|
@ -679,7 +679,9 @@ static int cw_bat_probe(struct i2c_client *client)
|
|||
&cw2015_bat_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(cw_bat->rk_bat)) {
|
||||
dev_err(cw_bat->dev, "Failed to register power supply\n");
|
||||
/* try again if this happens */
|
||||
dev_err_probe(&client->dev, PTR_ERR(cw_bat->rk_bat),
|
||||
"Failed to register power supply\n");
|
||||
return PTR_ERR(cw_bat->rk_bat);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,6 @@
|
|||
|
||||
/* Interrupt mask bits */
|
||||
#define CONFIG_ALRT_BIT_ENBL (1 << 2)
|
||||
#define STATUS_INTR_SOCMIN_BIT (1 << 10)
|
||||
#define STATUS_INTR_SOCMAX_BIT (1 << 14)
|
||||
|
||||
#define VFSOC0_LOCK 0x0000
|
||||
#define VFSOC0_UNLOCK 0x0080
|
||||
|
@ -285,8 +283,6 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
|
||||
ret = regmap_read(map, MAX17042_V_empty, &data);
|
||||
else if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
|
||||
ret = regmap_read(map, MAX17055_V_empty, &data);
|
||||
else
|
||||
ret = regmap_read(map, MAX17047_V_empty, &data);
|
||||
if (ret < 0)
|
||||
|
@ -748,7 +744,7 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
|
|||
struct max17042_config_data *config = chip->pdata->config_data;
|
||||
|
||||
max17042_override_por(map, MAX17042_TGAIN, config->tgain);
|
||||
max17042_override_por(map, MAx17042_TOFF, config->toff);
|
||||
max17042_override_por(map, MAX17042_TOFF, config->toff);
|
||||
max17042_override_por(map, MAX17042_CGAIN, config->cgain);
|
||||
max17042_override_por(map, MAX17042_COFF, config->coff);
|
||||
|
||||
|
@ -767,36 +763,36 @@ static inline void max17042_override_por_values(struct max17042_chip *chip)
|
|||
max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg);
|
||||
max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg);
|
||||
max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg);
|
||||
max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
|
||||
|
||||
max17042_override_por(map, MAX17042_FullCAP, config->fullcap);
|
||||
max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom);
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
|
||||
max17042_override_por(map, MAX17042_SOC_empty,
|
||||
config->socempty);
|
||||
max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
|
||||
max17042_override_por(map, MAX17042_dQacc, config->dqacc);
|
||||
max17042_override_por(map, MAX17042_dPacc, config->dpacc);
|
||||
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
|
||||
max17042_override_por(map, MAX17042_V_empty, config->vempty);
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)
|
||||
max17042_override_por(map, MAX17055_V_empty, config->vempty);
|
||||
else
|
||||
max17042_override_por(map, MAX17047_V_empty, config->vempty);
|
||||
max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
|
||||
max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
|
||||
max17042_override_por(map, MAX17042_FCTC, config->fctc);
|
||||
max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0);
|
||||
max17042_override_por(map, MAX17042_TempCo, config->tcompc0);
|
||||
if (chip->chip_type &&
|
||||
((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ||
|
||||
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) {
|
||||
max17042_override_por(map, MAX17042_MaskSOC, config->masksoc);
|
||||
max17042_override_por(map, MAX17042_SOC_empty, config->socempty);
|
||||
max17042_override_por(map, MAX17042_V_empty, config->vempty);
|
||||
max17042_override_por(map, MAX17042_EmptyTempCo, config->empty_tempco);
|
||||
max17042_override_por(map, MAX17042_K_empty0, config->kempty0);
|
||||
}
|
||||
|
||||
if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ||
|
||||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
|
||||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050))) {
|
||||
max17042_override_por(map, MAX17042_EmptyTempCo,
|
||||
config->empty_tempco);
|
||||
max17042_override_por(map, MAX17042_K_empty0,
|
||||
config->kempty0);
|
||||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) {
|
||||
max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty);
|
||||
max17042_override_por(map, MAX17042_TempNom, config->temp_nom);
|
||||
max17042_override_por(map, MAX17042_TempLim, config->temp_lim);
|
||||
max17042_override_por(map, MAX17042_FCTC, config->fctc);
|
||||
}
|
||||
|
||||
if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) ||
|
||||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) ||
|
||||
(chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055)) {
|
||||
max17042_override_por(map, MAX17047_V_empty, config->vempty);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -869,11 +865,14 @@ static irqreturn_t max17042_thread_handler(int id, void *dev)
|
|||
{
|
||||
struct max17042_chip *chip = dev;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
regmap_read(chip->regmap, MAX17042_STATUS, &val);
|
||||
if ((val & STATUS_INTR_SOCMIN_BIT) ||
|
||||
(val & STATUS_INTR_SOCMAX_BIT)) {
|
||||
dev_info(&chip->client->dev, "SOC threshold INTR\n");
|
||||
ret = regmap_read(chip->regmap, MAX17042_STATUS, &val);
|
||||
if (ret)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if ((val & STATUS_SMN_BIT) || (val & STATUS_SMX_BIT)) {
|
||||
dev_dbg(&chip->client->dev, "SOC threshold INTR\n");
|
||||
max17042_set_soc_threshold(chip, 1);
|
||||
}
|
||||
|
||||
|
@ -1196,6 +1195,7 @@ static const struct of_device_id max17042_dt_match[] = {
|
|||
{ .compatible = "maxim,max17047" },
|
||||
{ .compatible = "maxim,max17050" },
|
||||
{ .compatible = "maxim,max17055" },
|
||||
{ .compatible = "maxim,max77849-battery" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max17042_dt_match);
|
||||
|
@ -1206,6 +1206,7 @@ static const struct i2c_device_id max17042_id[] = {
|
|||
{ "max17047", MAXIM_DEVICE_TYPE_MAX17047 },
|
||||
{ "max17050", MAXIM_DEVICE_TYPE_MAX17050 },
|
||||
{ "max17055", MAXIM_DEVICE_TYPE_MAX17055 },
|
||||
{ "max77849-battery", MAXIM_DEVICE_TYPE_MAX17047 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max17042_id);
|
||||
|
|
|
@ -0,0 +1,867 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2021 MediaTek Inc.
|
||||
*/
|
||||
|
||||
#include <linux/devm-helpers.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/linear_range.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
#define MT6360_PMU_CHG_CTRL1 0x311
|
||||
#define MT6360_PMU_CHG_CTRL2 0x312
|
||||
#define MT6360_PMU_CHG_CTRL3 0x313
|
||||
#define MT6360_PMU_CHG_CTRL4 0x314
|
||||
#define MT6360_PMU_CHG_CTRL5 0x315
|
||||
#define MT6360_PMU_CHG_CTRL6 0x316
|
||||
#define MT6360_PMU_CHG_CTRL7 0x317
|
||||
#define MT6360_PMU_CHG_CTRL8 0x318
|
||||
#define MT6360_PMU_CHG_CTRL9 0x319
|
||||
#define MT6360_PMU_CHG_CTRL10 0x31A
|
||||
#define MT6360_PMU_DEVICE_TYPE 0x322
|
||||
#define MT6360_PMU_USB_STATUS1 0x327
|
||||
#define MT6360_PMU_CHG_STAT 0x34A
|
||||
#define MT6360_PMU_CHG_CTRL19 0x361
|
||||
#define MT6360_PMU_FOD_STAT 0x3E7
|
||||
|
||||
/* MT6360_PMU_CHG_CTRL1 */
|
||||
#define MT6360_FSLP_SHFT (3)
|
||||
#define MT6360_FSLP_MASK BIT(MT6360_FSLP_SHFT)
|
||||
#define MT6360_OPA_MODE_SHFT (0)
|
||||
#define MT6360_OPA_MODE_MASK BIT(MT6360_OPA_MODE_SHFT)
|
||||
/* MT6360_PMU_CHG_CTRL2 */
|
||||
#define MT6360_IINLMTSEL_SHFT (2)
|
||||
#define MT6360_IINLMTSEL_MASK GENMASK(3, 2)
|
||||
/* MT6360_PMU_CHG_CTRL3 */
|
||||
#define MT6360_IAICR_SHFT (2)
|
||||
#define MT6360_IAICR_MASK GENMASK(7, 2)
|
||||
#define MT6360_ILIM_EN_MASK BIT(0)
|
||||
/* MT6360_PMU_CHG_CTRL4 */
|
||||
#define MT6360_VOREG_SHFT (1)
|
||||
#define MT6360_VOREG_MASK GENMASK(7, 1)
|
||||
/* MT6360_PMU_CHG_CTRL5 */
|
||||
#define MT6360_VOBST_MASK GENMASK(7, 2)
|
||||
/* MT6360_PMU_CHG_CTRL6 */
|
||||
#define MT6360_VMIVR_SHFT (1)
|
||||
#define MT6360_VMIVR_MASK GENMASK(7, 1)
|
||||
/* MT6360_PMU_CHG_CTRL7 */
|
||||
#define MT6360_ICHG_SHFT (2)
|
||||
#define MT6360_ICHG_MASK GENMASK(7, 2)
|
||||
/* MT6360_PMU_CHG_CTRL8 */
|
||||
#define MT6360_IPREC_SHFT (0)
|
||||
#define MT6360_IPREC_MASK GENMASK(3, 0)
|
||||
/* MT6360_PMU_CHG_CTRL9 */
|
||||
#define MT6360_IEOC_SHFT (4)
|
||||
#define MT6360_IEOC_MASK GENMASK(7, 4)
|
||||
/* MT6360_PMU_CHG_CTRL10 */
|
||||
#define MT6360_OTG_OC_MASK GENMASK(3, 0)
|
||||
/* MT6360_PMU_DEVICE_TYPE */
|
||||
#define MT6360_USBCHGEN_MASK BIT(7)
|
||||
/* MT6360_PMU_USB_STATUS1 */
|
||||
#define MT6360_USB_STATUS_SHFT (4)
|
||||
#define MT6360_USB_STATUS_MASK GENMASK(6, 4)
|
||||
/* MT6360_PMU_CHG_STAT */
|
||||
#define MT6360_CHG_STAT_SHFT (6)
|
||||
#define MT6360_CHG_STAT_MASK GENMASK(7, 6)
|
||||
#define MT6360_VBAT_LVL_MASK BIT(5)
|
||||
/* MT6360_PMU_CHG_CTRL19 */
|
||||
#define MT6360_VINOVP_SHFT (5)
|
||||
#define MT6360_VINOVP_MASK GENMASK(6, 5)
|
||||
/* MT6360_PMU_FOD_STAT */
|
||||
#define MT6360_CHRDET_EXT_MASK BIT(4)
|
||||
|
||||
/* uV */
|
||||
#define MT6360_VMIVR_MIN 3900000
|
||||
#define MT6360_VMIVR_MAX 13400000
|
||||
#define MT6360_VMIVR_STEP 100000
|
||||
/* uA */
|
||||
#define MT6360_ICHG_MIN 100000
|
||||
#define MT6360_ICHG_MAX 5000000
|
||||
#define MT6360_ICHG_STEP 100000
|
||||
/* uV */
|
||||
#define MT6360_VOREG_MIN 3900000
|
||||
#define MT6360_VOREG_MAX 4710000
|
||||
#define MT6360_VOREG_STEP 10000
|
||||
/* uA */
|
||||
#define MT6360_AICR_MIN 100000
|
||||
#define MT6360_AICR_MAX 3250000
|
||||
#define MT6360_AICR_STEP 50000
|
||||
/* uA */
|
||||
#define MT6360_IPREC_MIN 100000
|
||||
#define MT6360_IPREC_MAX 850000
|
||||
#define MT6360_IPREC_STEP 50000
|
||||
/* uA */
|
||||
#define MT6360_IEOC_MIN 100000
|
||||
#define MT6360_IEOC_MAX 850000
|
||||
#define MT6360_IEOC_STEP 50000
|
||||
|
||||
enum {
|
||||
MT6360_RANGE_VMIVR,
|
||||
MT6360_RANGE_ICHG,
|
||||
MT6360_RANGE_VOREG,
|
||||
MT6360_RANGE_AICR,
|
||||
MT6360_RANGE_IPREC,
|
||||
MT6360_RANGE_IEOC,
|
||||
MT6360_RANGE_MAX,
|
||||
};
|
||||
|
||||
#define MT6360_LINEAR_RANGE(idx, _min, _min_sel, _max_sel, _step) \
|
||||
[idx] = REGULATOR_LINEAR_RANGE(_min, _min_sel, _max_sel, _step)
|
||||
|
||||
static const struct linear_range mt6360_chg_range[MT6360_RANGE_MAX] = {
|
||||
MT6360_LINEAR_RANGE(MT6360_RANGE_VMIVR, 3900000, 0, 0x5F, 100000),
|
||||
MT6360_LINEAR_RANGE(MT6360_RANGE_ICHG, 100000, 0, 0x31, 100000),
|
||||
MT6360_LINEAR_RANGE(MT6360_RANGE_VOREG, 3900000, 0, 0x51, 10000),
|
||||
MT6360_LINEAR_RANGE(MT6360_RANGE_AICR, 100000, 0, 0x3F, 50000),
|
||||
MT6360_LINEAR_RANGE(MT6360_RANGE_IPREC, 100000, 0, 0x0F, 50000),
|
||||
MT6360_LINEAR_RANGE(MT6360_RANGE_IEOC, 100000, 0, 0x0F, 50000),
|
||||
};
|
||||
|
||||
struct mt6360_chg_info {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct power_supply_desc psy_desc;
|
||||
struct power_supply *psy;
|
||||
struct regulator_dev *otg_rdev;
|
||||
struct mutex chgdet_lock;
|
||||
u32 vinovp;
|
||||
bool pwr_rdy;
|
||||
bool bc12_en;
|
||||
int psy_usb_type;
|
||||
struct work_struct chrdet_work;
|
||||
};
|
||||
|
||||
enum mt6360_iinlmtsel {
|
||||
MT6360_IINLMTSEL_AICR_3250 = 0,
|
||||
MT6360_IINLMTSEL_CHG_TYPE,
|
||||
MT6360_IINLMTSEL_AICR,
|
||||
MT6360_IINLMTSEL_LOWER_LEVEL,
|
||||
};
|
||||
|
||||
enum mt6360_pmu_chg_type {
|
||||
MT6360_CHG_TYPE_NOVBUS = 0,
|
||||
MT6360_CHG_TYPE_UNDER_GOING,
|
||||
MT6360_CHG_TYPE_SDP,
|
||||
MT6360_CHG_TYPE_SDPNSTD,
|
||||
MT6360_CHG_TYPE_DCP,
|
||||
MT6360_CHG_TYPE_CDP,
|
||||
MT6360_CHG_TYPE_DISABLE_BC12,
|
||||
MT6360_CHG_TYPE_MAX,
|
||||
};
|
||||
|
||||
static enum power_supply_usb_type mt6360_charger_usb_types[] = {
|
||||
POWER_SUPPLY_USB_TYPE_UNKNOWN,
|
||||
POWER_SUPPLY_USB_TYPE_SDP,
|
||||
POWER_SUPPLY_USB_TYPE_DCP,
|
||||
POWER_SUPPLY_USB_TYPE_CDP,
|
||||
};
|
||||
|
||||
static int mt6360_get_chrdet_ext_stat(struct mt6360_chg_info *mci,
|
||||
bool *pwr_rdy)
|
||||
{
|
||||
int ret;
|
||||
unsigned int regval;
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_FOD_STAT, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*pwr_rdy = (regval & MT6360_CHRDET_EXT_MASK) ? true : false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_online(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret;
|
||||
bool pwr_rdy;
|
||||
|
||||
ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
val->intval = pwr_rdy ? true : false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_status(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int status, ret;
|
||||
unsigned int regval;
|
||||
bool pwr_rdy;
|
||||
|
||||
ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!pwr_rdy) {
|
||||
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
regval &= MT6360_CHG_STAT_MASK;
|
||||
regval >>= MT6360_CHG_STAT_SHFT;
|
||||
switch (regval) {
|
||||
case 0x0:
|
||||
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
case 0x1:
|
||||
status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
case 0x2:
|
||||
status = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
default:
|
||||
ret = -EIO;
|
||||
}
|
||||
out:
|
||||
if (!ret)
|
||||
val->intval = status;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_charge_type(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int type, ret;
|
||||
unsigned int regval;
|
||||
u8 chg_stat;
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
chg_stat = (regval & MT6360_CHG_STAT_MASK) >> MT6360_CHG_STAT_SHFT;
|
||||
switch (chg_stat) {
|
||||
case 0x01: /* Charge in Progress */
|
||||
if (regval & MT6360_VBAT_LVL_MASK)
|
||||
type = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
||||
else
|
||||
type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
|
||||
break;
|
||||
case 0x00: /* Not Charging */
|
||||
case 0x02: /* Charge Done */
|
||||
case 0x03: /* Charge Fault */
|
||||
default:
|
||||
type = POWER_SUPPLY_CHARGE_TYPE_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
val->intval = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_ichg(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret;
|
||||
u32 sel, value;
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL7, &sel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
sel = (sel & MT6360_ICHG_MASK) >> MT6360_ICHG_SHFT;
|
||||
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_ICHG], sel, &value);
|
||||
if (!ret)
|
||||
val->intval = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_max_ichg(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
val->intval = MT6360_ICHG_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_cv(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret;
|
||||
u32 sel, value;
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL4, &sel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
sel = (sel & MT6360_VOREG_MASK) >> MT6360_VOREG_SHFT;
|
||||
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VOREG], sel, &value);
|
||||
if (!ret)
|
||||
val->intval = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_max_cv(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
val->intval = MT6360_VOREG_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_aicr(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret;
|
||||
u32 sel, value;
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL3, &sel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
sel = (sel & MT6360_IAICR_MASK) >> MT6360_IAICR_SHFT;
|
||||
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_AICR], sel, &value);
|
||||
if (!ret)
|
||||
val->intval = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_mivr(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret;
|
||||
u32 sel, value;
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL6, &sel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
sel = (sel & MT6360_VMIVR_MASK) >> MT6360_VMIVR_SHFT;
|
||||
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VMIVR], sel, &value);
|
||||
if (!ret)
|
||||
val->intval = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_iprechg(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret;
|
||||
u32 sel, value;
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL8, &sel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
sel = (sel & MT6360_IPREC_MASK) >> MT6360_IPREC_SHFT;
|
||||
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IPREC], sel, &value);
|
||||
if (!ret)
|
||||
val->intval = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_ieoc(struct mt6360_chg_info *mci,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
int ret;
|
||||
u32 sel, value;
|
||||
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL9, &sel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
sel = (sel & MT6360_IEOC_MASK) >> MT6360_IEOC_SHFT;
|
||||
ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IEOC], sel, &value);
|
||||
if (!ret)
|
||||
val->intval = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_set_online(struct mt6360_chg_info *mci,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u8 force_sleep = val->intval ? 0 : 1;
|
||||
|
||||
return regmap_update_bits(mci->regmap,
|
||||
MT6360_PMU_CHG_CTRL1,
|
||||
MT6360_FSLP_MASK,
|
||||
force_sleep << MT6360_FSLP_SHFT);
|
||||
}
|
||||
|
||||
static int mt6360_charger_set_ichg(struct mt6360_chg_info *mci,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u32 sel;
|
||||
|
||||
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_ICHG], val->intval, &sel);
|
||||
return regmap_update_bits(mci->regmap,
|
||||
MT6360_PMU_CHG_CTRL7,
|
||||
MT6360_ICHG_MASK,
|
||||
sel << MT6360_ICHG_SHFT);
|
||||
}
|
||||
|
||||
static int mt6360_charger_set_cv(struct mt6360_chg_info *mci,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u32 sel;
|
||||
|
||||
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VOREG], val->intval, &sel);
|
||||
return regmap_update_bits(mci->regmap,
|
||||
MT6360_PMU_CHG_CTRL4,
|
||||
MT6360_VOREG_MASK,
|
||||
sel << MT6360_VOREG_SHFT);
|
||||
}
|
||||
|
||||
static int mt6360_charger_set_aicr(struct mt6360_chg_info *mci,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u32 sel;
|
||||
|
||||
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_AICR], val->intval, &sel);
|
||||
return regmap_update_bits(mci->regmap,
|
||||
MT6360_PMU_CHG_CTRL3,
|
||||
MT6360_IAICR_MASK,
|
||||
sel << MT6360_IAICR_SHFT);
|
||||
}
|
||||
|
||||
static int mt6360_charger_set_mivr(struct mt6360_chg_info *mci,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u32 sel;
|
||||
|
||||
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VMIVR], val->intval, &sel);
|
||||
return regmap_update_bits(mci->regmap,
|
||||
MT6360_PMU_CHG_CTRL3,
|
||||
MT6360_VMIVR_MASK,
|
||||
sel << MT6360_VMIVR_SHFT);
|
||||
}
|
||||
|
||||
static int mt6360_charger_set_iprechg(struct mt6360_chg_info *mci,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u32 sel;
|
||||
|
||||
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IPREC], val->intval, &sel);
|
||||
return regmap_update_bits(mci->regmap,
|
||||
MT6360_PMU_CHG_CTRL8,
|
||||
MT6360_IPREC_MASK,
|
||||
sel << MT6360_IPREC_SHFT);
|
||||
}
|
||||
|
||||
static int mt6360_charger_set_ieoc(struct mt6360_chg_info *mci,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
u32 sel;
|
||||
|
||||
linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IEOC], val->intval, &sel);
|
||||
return regmap_update_bits(mci->regmap,
|
||||
MT6360_PMU_CHG_CTRL9,
|
||||
MT6360_IEOC_MASK,
|
||||
sel << MT6360_IEOC_SHFT);
|
||||
}
|
||||
|
||||
static int mt6360_charger_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct mt6360_chg_info *mci = power_supply_get_drvdata(psy);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
ret = mt6360_charger_get_online(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
ret = mt6360_charger_get_status(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
ret = mt6360_charger_get_charge_type(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
ret = mt6360_charger_get_ichg(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
||||
ret = mt6360_charger_get_max_ichg(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
ret = mt6360_charger_get_cv(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
|
||||
ret = mt6360_charger_get_max_cv(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = mt6360_charger_get_aicr(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
ret = mt6360_charger_get_mivr(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
|
||||
ret = mt6360_charger_get_iprechg(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
ret = mt6360_charger_get_ieoc(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_USB_TYPE:
|
||||
val->intval = mci->psy_usb_type;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct mt6360_chg_info *mci = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
ret = mt6360_charger_set_online(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
ret = mt6360_charger_set_ichg(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
ret = mt6360_charger_set_cv(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = mt6360_charger_set_aicr(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
ret = mt6360_charger_set_mivr(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
|
||||
ret = mt6360_charger_set_iprechg(mci, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
ret = mt6360_charger_set_ieoc(mci, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6360_charger_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
|
||||
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static enum power_supply_property mt6360_charger_properties[] = {
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
|
||||
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
|
||||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc mt6360_charger_desc = {
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.properties = mt6360_charger_properties,
|
||||
.num_properties = ARRAY_SIZE(mt6360_charger_properties),
|
||||
.get_property = mt6360_charger_get_property,
|
||||
.set_property = mt6360_charger_set_property,
|
||||
.property_is_writeable = mt6360_charger_property_is_writeable,
|
||||
.usb_types = mt6360_charger_usb_types,
|
||||
.num_usb_types = ARRAY_SIZE(mt6360_charger_usb_types),
|
||||
};
|
||||
|
||||
static const struct regulator_ops mt6360_chg_otg_ops = {
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
};
|
||||
|
||||
static const struct regulator_desc mt6360_otg_rdesc = {
|
||||
.of_match = "usb-otg-vbus",
|
||||
.name = "usb-otg-vbus",
|
||||
.ops = &mt6360_chg_otg_ops,
|
||||
.owner = THIS_MODULE,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.min_uV = 4425000,
|
||||
.uV_step = 25000,
|
||||
.n_voltages = 57,
|
||||
.vsel_reg = MT6360_PMU_CHG_CTRL5,
|
||||
.vsel_mask = MT6360_VOBST_MASK,
|
||||
.enable_reg = MT6360_PMU_CHG_CTRL1,
|
||||
.enable_mask = MT6360_OPA_MODE_MASK,
|
||||
};
|
||||
|
||||
static irqreturn_t mt6360_pmu_attach_i_handler(int irq, void *data)
|
||||
{
|
||||
struct mt6360_chg_info *mci = data;
|
||||
int ret;
|
||||
unsigned int usb_status;
|
||||
int last_usb_type;
|
||||
|
||||
mutex_lock(&mci->chgdet_lock);
|
||||
if (!mci->bc12_en) {
|
||||
dev_warn(mci->dev, "Received attach interrupt, bc12 disabled, ignore irq\n");
|
||||
goto out;
|
||||
}
|
||||
last_usb_type = mci->psy_usb_type;
|
||||
/* Plug in */
|
||||
ret = regmap_read(mci->regmap, MT6360_PMU_USB_STATUS1, &usb_status);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
usb_status &= MT6360_USB_STATUS_MASK;
|
||||
usb_status >>= MT6360_USB_STATUS_SHFT;
|
||||
switch (usb_status) {
|
||||
case MT6360_CHG_TYPE_NOVBUS:
|
||||
dev_dbg(mci->dev, "Received attach interrupt, no vbus\n");
|
||||
goto out;
|
||||
case MT6360_CHG_TYPE_UNDER_GOING:
|
||||
dev_dbg(mci->dev, "Received attach interrupt, under going...\n");
|
||||
goto out;
|
||||
case MT6360_CHG_TYPE_SDP:
|
||||
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
|
||||
break;
|
||||
case MT6360_CHG_TYPE_SDPNSTD:
|
||||
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
|
||||
break;
|
||||
case MT6360_CHG_TYPE_CDP:
|
||||
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP;
|
||||
break;
|
||||
case MT6360_CHG_TYPE_DCP:
|
||||
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
|
||||
break;
|
||||
case MT6360_CHG_TYPE_DISABLE_BC12:
|
||||
dev_dbg(mci->dev, "Received attach interrupt, bc12 detect not enable\n");
|
||||
goto out;
|
||||
default:
|
||||
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
|
||||
dev_dbg(mci->dev, "Received attach interrupt, reserved address\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_dbg(mci->dev, "Received attach interrupt, chg_type = %d\n", mci->psy_usb_type);
|
||||
if (last_usb_type != mci->psy_usb_type)
|
||||
power_supply_changed(mci->psy);
|
||||
out:
|
||||
mutex_unlock(&mci->chgdet_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void mt6360_handle_chrdet_ext_evt(struct mt6360_chg_info *mci)
|
||||
{
|
||||
int ret;
|
||||
bool pwr_rdy;
|
||||
|
||||
mutex_lock(&mci->chgdet_lock);
|
||||
ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (mci->pwr_rdy == pwr_rdy) {
|
||||
dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy is same(%d)\n", pwr_rdy);
|
||||
goto out;
|
||||
}
|
||||
mci->pwr_rdy = pwr_rdy;
|
||||
dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy = %d\n", pwr_rdy);
|
||||
if (!pwr_rdy) {
|
||||
mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
|
||||
power_supply_changed(mci->psy);
|
||||
|
||||
}
|
||||
ret = regmap_update_bits(mci->regmap,
|
||||
MT6360_PMU_DEVICE_TYPE,
|
||||
MT6360_USBCHGEN_MASK,
|
||||
pwr_rdy ? MT6360_USBCHGEN_MASK : 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
mci->bc12_en = pwr_rdy;
|
||||
out:
|
||||
mutex_unlock(&mci->chgdet_lock);
|
||||
}
|
||||
|
||||
static void mt6360_chrdet_work(struct work_struct *work)
|
||||
{
|
||||
struct mt6360_chg_info *mci = (struct mt6360_chg_info *)container_of(
|
||||
work, struct mt6360_chg_info, chrdet_work);
|
||||
|
||||
mt6360_handle_chrdet_ext_evt(mci);
|
||||
}
|
||||
|
||||
static irqreturn_t mt6360_pmu_chrdet_ext_evt_handler(int irq, void *data)
|
||||
{
|
||||
struct mt6360_chg_info *mci = data;
|
||||
|
||||
mt6360_handle_chrdet_ext_evt(mci);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mt6360_chg_irq_register(struct platform_device *pdev)
|
||||
{
|
||||
const struct {
|
||||
const char *name;
|
||||
irq_handler_t handler;
|
||||
} irq_descs[] = {
|
||||
{ "attach_i", mt6360_pmu_attach_i_handler },
|
||||
{ "chrdet_ext_evt", mt6360_pmu_chrdet_ext_evt_handler }
|
||||
};
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(irq_descs); i++) {
|
||||
ret = platform_get_irq_byname(pdev, irq_descs[i].name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,
|
||||
irq_descs[i].handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
irq_descs[i].name,
|
||||
platform_get_drvdata(pdev));
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to request %s irq\n",
|
||||
irq_descs[i].name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 mt6360_vinovp_trans_to_sel(u32 val)
|
||||
{
|
||||
u32 vinovp_tbl[] = { 5500000, 6500000, 11000000, 14500000 };
|
||||
int i;
|
||||
|
||||
/* Select the smaller and equal supported value */
|
||||
for (i = 0; i < ARRAY_SIZE(vinovp_tbl)-1; i++) {
|
||||
if (val < vinovp_tbl[i+1])
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int mt6360_chg_init_setting(struct mt6360_chg_info *mci)
|
||||
{
|
||||
int ret;
|
||||
u32 sel;
|
||||
|
||||
sel = mt6360_vinovp_trans_to_sel(mci->vinovp);
|
||||
ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL19,
|
||||
MT6360_VINOVP_MASK, sel << MT6360_VINOVP_SHFT);
|
||||
if (ret)
|
||||
return dev_err_probe(mci->dev, ret, "%s: Failed to apply vinovp\n", __func__);
|
||||
ret = regmap_update_bits(mci->regmap, MT6360_PMU_DEVICE_TYPE,
|
||||
MT6360_USBCHGEN_MASK, 0);
|
||||
if (ret)
|
||||
return dev_err_probe(mci->dev, ret, "%s: Failed to disable bc12\n", __func__);
|
||||
ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL2,
|
||||
MT6360_IINLMTSEL_MASK,
|
||||
MT6360_IINLMTSEL_AICR <<
|
||||
MT6360_IINLMTSEL_SHFT);
|
||||
if (ret)
|
||||
return dev_err_probe(mci->dev, ret,
|
||||
"%s: Failed to switch iinlmtsel to aicr\n", __func__);
|
||||
usleep_range(5000, 6000);
|
||||
ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL3,
|
||||
MT6360_ILIM_EN_MASK, 0);
|
||||
if (ret)
|
||||
return dev_err_probe(mci->dev, ret,
|
||||
"%s: Failed to disable ilim\n", __func__);
|
||||
ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL10,
|
||||
MT6360_OTG_OC_MASK, MT6360_OTG_OC_MASK);
|
||||
if (ret)
|
||||
return dev_err_probe(mci->dev, ret,
|
||||
"%s: Failed to config otg oc to 3A\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt6360_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mt6360_chg_info *mci;
|
||||
struct power_supply_config charger_cfg = {};
|
||||
struct regulator_config config = { };
|
||||
int ret;
|
||||
|
||||
mci = devm_kzalloc(&pdev->dev, sizeof(*mci), GFP_KERNEL);
|
||||
if (!mci)
|
||||
return -ENOMEM;
|
||||
|
||||
mci->dev = &pdev->dev;
|
||||
mci->vinovp = 6500000;
|
||||
mutex_init(&mci->chgdet_lock);
|
||||
platform_set_drvdata(pdev, mci);
|
||||
devm_work_autocancel(&pdev->dev, &mci->chrdet_work, mt6360_chrdet_work);
|
||||
|
||||
ret = device_property_read_u32(&pdev->dev, "richtek,vinovp-microvolt", &mci->vinovp);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "Failed to parse vinovp in DT, keep default 6.5v\n");
|
||||
|
||||
mci->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!mci->regmap)
|
||||
return dev_err_probe(&pdev->dev, -ENODEV, "Failed to get parent regmap\n");
|
||||
|
||||
ret = mt6360_chg_init_setting(mci);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to initial setting\n");
|
||||
|
||||
memcpy(&mci->psy_desc, &mt6360_charger_desc, sizeof(mci->psy_desc));
|
||||
mci->psy_desc.name = dev_name(&pdev->dev);
|
||||
charger_cfg.drv_data = mci;
|
||||
charger_cfg.of_node = pdev->dev.of_node;
|
||||
mci->psy = devm_power_supply_register(&pdev->dev,
|
||||
&mci->psy_desc, &charger_cfg);
|
||||
if (IS_ERR(mci->psy))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(mci->psy),
|
||||
"Failed to register power supply dev\n");
|
||||
|
||||
|
||||
ret = mt6360_chg_irq_register(pdev);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to register irqs\n");
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.regmap = mci->regmap;
|
||||
mci->otg_rdev = devm_regulator_register(&pdev->dev, &mt6360_otg_rdesc,
|
||||
&config);
|
||||
if (IS_ERR(mci->otg_rdev))
|
||||
return PTR_ERR(mci->otg_rdev);
|
||||
|
||||
schedule_work(&mci->chrdet_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id __maybe_unused mt6360_charger_of_id[] = {
|
||||
{ .compatible = "mediatek,mt6360-chg", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt6360_charger_of_id);
|
||||
|
||||
static const struct platform_device_id mt6360_charger_id[] = {
|
||||
{ "mt6360-chg", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, mt6360_charger_id);
|
||||
|
||||
static struct platform_driver mt6360_charger_driver = {
|
||||
.driver = {
|
||||
.name = "mt6360-chg",
|
||||
.of_match_table = of_match_ptr(mt6360_charger_of_id),
|
||||
},
|
||||
.probe = mt6360_charger_probe,
|
||||
.id_table = mt6360_charger_id,
|
||||
};
|
||||
module_platform_driver(mt6360_charger_driver);
|
||||
|
||||
MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
|
||||
MODULE_DESCRIPTION("MT6360 Charger Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -649,6 +649,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
|
|||
int err, len, index;
|
||||
const __be32 *list;
|
||||
|
||||
info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
|
||||
info->energy_full_design_uwh = -EINVAL;
|
||||
info->charge_full_design_uah = -EINVAL;
|
||||
info->voltage_min_design_uv = -EINVAL;
|
||||
|
@ -696,6 +697,24 @@ int power_supply_get_battery_info(struct power_supply *psy,
|
|||
* Documentation/power/power_supply_class.rst.
|
||||
*/
|
||||
|
||||
if (!of_property_read_string(battery_np, "device-chemistry", &value)) {
|
||||
if (!strcmp("nickel-cadmium", value))
|
||||
info->technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
|
||||
else if (!strcmp("nickel-metal-hydride", value))
|
||||
info->technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
|
||||
else if (!strcmp("lithium-ion", value))
|
||||
/* Imprecise lithium-ion type */
|
||||
info->technology = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
else if (!strcmp("lithium-ion-polymer", value))
|
||||
info->technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
|
||||
else if (!strcmp("lithium-ion-iron-phosphate", value))
|
||||
info->technology = POWER_SUPPLY_TECHNOLOGY_LiFe;
|
||||
else if (!strcmp("lithium-ion-manganese-oxide", value))
|
||||
info->technology = POWER_SUPPLY_TECHNOLOGY_LiMn;
|
||||
else
|
||||
dev_warn(&psy->dev, "%s unknown battery type\n", value);
|
||||
}
|
||||
|
||||
of_property_read_u32(battery_np, "energy-full-design-microwatt-hours",
|
||||
&info->energy_full_design_uwh);
|
||||
of_property_read_u32(battery_np, "charge-full-design-microamp-hours",
|
||||
|
|
|
@ -929,11 +929,8 @@ static int smbb_charger_probe(struct platform_device *pdev)
|
|||
int irq;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get irq '%s'\n",
|
||||
smbb_charger_irqs[i].name);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
smbb_charger_irqs[i].handler(irq, chg);
|
||||
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/rn5t618.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
|
@ -64,6 +66,8 @@ struct rn5t618_power_info {
|
|||
struct power_supply *battery;
|
||||
struct power_supply *usb;
|
||||
struct power_supply *adp;
|
||||
struct iio_channel *channel_vusb;
|
||||
struct iio_channel *channel_vadp;
|
||||
int irq;
|
||||
};
|
||||
|
||||
|
@ -77,6 +81,7 @@ static enum power_supply_usb_type rn5t618_usb_types[] = {
|
|||
static enum power_supply_property rn5t618_usb_props[] = {
|
||||
/* input current limit is not very accurate */
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_USB_TYPE,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
|
@ -85,6 +90,7 @@ static enum power_supply_property rn5t618_usb_props[] = {
|
|||
static enum power_supply_property rn5t618_adp_props[] = {
|
||||
/* input current limit is not very accurate */
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
};
|
||||
|
@ -463,6 +469,15 @@ static int rn5t618_adp_get_property(struct power_supply *psy,
|
|||
return ret;
|
||||
|
||||
val->intval = FROM_CUR_REG(regval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
if (!info->channel_vadp)
|
||||
return -ENODATA;
|
||||
|
||||
ret = iio_read_channel_processed_scale(info->channel_vadp, &val->intval, 1000);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -588,6 +603,15 @@ static int rn5t618_usb_get_property(struct power_supply *psy,
|
|||
|
||||
val->intval = FROM_CUR_REG(regval);
|
||||
}
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
if (!info->channel_vusb)
|
||||
return -ENODATA;
|
||||
|
||||
ret = iio_read_channel_processed_scale(info->channel_vusb, &val->intval, 1000);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -711,6 +735,20 @@ static int rn5t618_power_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
info->channel_vusb = devm_iio_channel_get(&pdev->dev, "vusb");
|
||||
if (IS_ERR(info->channel_vusb)) {
|
||||
if (PTR_ERR(info->channel_vusb) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(info->channel_vusb);
|
||||
}
|
||||
|
||||
info->channel_vadp = devm_iio_channel_get(&pdev->dev, "vadp");
|
||||
if (IS_ERR(info->channel_vadp)) {
|
||||
if (PTR_ERR(info->channel_vadp) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(info->channel_vadp);
|
||||
}
|
||||
|
||||
ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -31,8 +31,9 @@ enum {
|
|||
REG_CURRENT_AVG,
|
||||
REG_MAX_ERR,
|
||||
REG_CAPACITY,
|
||||
REG_TIME_TO_EMPTY,
|
||||
REG_TIME_TO_FULL,
|
||||
REG_TIME_TO_EMPTY_NOW,
|
||||
REG_TIME_TO_EMPTY_AVG,
|
||||
REG_TIME_TO_FULL_AVG,
|
||||
REG_STATUS,
|
||||
REG_CAPACITY_LEVEL,
|
||||
REG_CYCLE_COUNT,
|
||||
|
@ -102,7 +103,7 @@ static const struct chip_data {
|
|||
[REG_TEMPERATURE] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
|
||||
[REG_VOLTAGE] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
|
||||
SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 65535),
|
||||
[REG_CURRENT_NOW] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
|
||||
[REG_CURRENT_AVG] =
|
||||
|
@ -119,9 +120,11 @@ static const struct chip_data {
|
|||
SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
|
||||
[REG_FULL_CHARGE_CAPACITY_CHARGE] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535),
|
||||
[REG_TIME_TO_EMPTY] =
|
||||
[REG_TIME_TO_EMPTY_NOW] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 0x11, 0, 65535),
|
||||
[REG_TIME_TO_EMPTY_AVG] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535),
|
||||
[REG_TIME_TO_FULL] =
|
||||
[REG_TIME_TO_FULL_AVG] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535),
|
||||
[REG_CHARGE_CURRENT] =
|
||||
SBS_DATA(POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 0x14, 0, 65535),
|
||||
|
@ -165,6 +168,7 @@ static const enum power_supply_property sbs_properties[] = {
|
|||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_ERROR_MARGIN,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
|
||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
|
||||
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
|
||||
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
||||
|
@ -748,6 +752,7 @@ static void sbs_unit_adjustment(struct i2c_client *client,
|
|||
val->intval -= TEMP_KELVIN_TO_CELSIUS;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
|
||||
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
|
||||
/* sbs provides time to empty and time to full in minutes.
|
||||
|
@ -966,6 +971,7 @@ static int sbs_get_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
|
||||
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
|
||||
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
|
|
|
@ -1229,10 +1229,8 @@ static int sc27xx_fgu_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq resource specified\n");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(data->dev, irq, NULL,
|
||||
sc27xx_fgu_interrupt,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/power_supply.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
#include <dt-bindings/power/summit,smb347-charger.h>
|
||||
|
||||
|
@ -55,6 +56,7 @@
|
|||
#define CFG_PIN_EN_CTRL_ACTIVE_LOW 0x60
|
||||
#define CFG_PIN_EN_APSD_IRQ BIT(1)
|
||||
#define CFG_PIN_EN_CHARGER_ERROR BIT(2)
|
||||
#define CFG_PIN_EN_CTRL BIT(4)
|
||||
#define CFG_THERM 0x07
|
||||
#define CFG_THERM_SOFT_HOT_COMPENSATION_MASK 0x03
|
||||
#define CFG_THERM_SOFT_HOT_COMPENSATION_SHIFT 0
|
||||
|
@ -62,12 +64,15 @@
|
|||
#define CFG_THERM_SOFT_COLD_COMPENSATION_SHIFT 2
|
||||
#define CFG_THERM_MONITOR_DISABLED BIT(4)
|
||||
#define CFG_SYSOK 0x08
|
||||
#define CFG_SYSOK_INOK_ACTIVE_HIGH BIT(0)
|
||||
#define CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED BIT(2)
|
||||
#define CFG_OTHER 0x09
|
||||
#define CFG_OTHER_RID_MASK 0xc0
|
||||
#define CFG_OTHER_RID_ENABLED_AUTO_OTG 0xc0
|
||||
#define CFG_OTG 0x0a
|
||||
#define CFG_OTG_TEMP_THRESHOLD_MASK 0x30
|
||||
#define CFG_OTG_CURRENT_LIMIT_250mA BIT(2)
|
||||
#define CFG_OTG_CURRENT_LIMIT_750mA BIT(3)
|
||||
#define CFG_OTG_TEMP_THRESHOLD_SHIFT 4
|
||||
#define CFG_OTG_CC_COMPENSATION_MASK 0xc0
|
||||
#define CFG_OTG_CC_COMPENSATION_SHIFT 6
|
||||
|
@ -91,6 +96,7 @@
|
|||
#define CMD_A 0x30
|
||||
#define CMD_A_CHG_ENABLED BIT(1)
|
||||
#define CMD_A_SUSPEND_ENABLED BIT(2)
|
||||
#define CMD_A_OTG_ENABLED BIT(4)
|
||||
#define CMD_A_ALLOW_WRITE BIT(7)
|
||||
#define CMD_B 0x31
|
||||
#define CMD_C 0x33
|
||||
|
@ -132,11 +138,12 @@
|
|||
* @regmap: pointer to driver regmap
|
||||
* @mains: power_supply instance for AC/DC power
|
||||
* @usb: power_supply instance for USB power
|
||||
* @usb_rdev: USB VBUS regulator device
|
||||
* @id: SMB charger ID
|
||||
* @mains_online: is AC/DC input connected
|
||||
* @usb_online: is USB input connected
|
||||
* @charging_enabled: is charging enabled
|
||||
* @irq_unsupported: is interrupt unsupported by SMB hardware
|
||||
* @usb_vbus_enabled: is USB VBUS powered by SMB charger
|
||||
* @max_charge_current: maximum current (in uA) the battery can be charged
|
||||
* @max_charge_voltage: maximum voltage (in uV) the battery can be charged
|
||||
* @pre_charge_current: current (in uA) to use in pre-charging phase
|
||||
|
@ -167,6 +174,8 @@
|
|||
* @use_usb_otg: USB OTG output can be used (not implemented yet)
|
||||
* @enable_control: how charging enable/disable is controlled
|
||||
* (driver/pin controls)
|
||||
* @inok_polarity: polarity of INOK signal which denotes presence of external
|
||||
* power supply
|
||||
*
|
||||
* @use_main, @use_usb, and @use_usb_otg are means to enable/disable
|
||||
* hardware support for these. This is useful when we want to have for
|
||||
|
@ -189,11 +198,12 @@ struct smb347_charger {
|
|||
struct regmap *regmap;
|
||||
struct power_supply *mains;
|
||||
struct power_supply *usb;
|
||||
struct regulator_dev *usb_rdev;
|
||||
unsigned int id;
|
||||
bool mains_online;
|
||||
bool usb_online;
|
||||
bool charging_enabled;
|
||||
bool irq_unsupported;
|
||||
bool usb_vbus_enabled;
|
||||
|
||||
unsigned int max_charge_current;
|
||||
unsigned int max_charge_voltage;
|
||||
|
@ -214,6 +224,7 @@ struct smb347_charger {
|
|||
bool use_usb;
|
||||
bool use_usb_otg;
|
||||
unsigned int enable_control;
|
||||
unsigned int inok_polarity;
|
||||
};
|
||||
|
||||
enum smb_charger_chipid {
|
||||
|
@ -358,21 +369,18 @@ static int smb347_charging_status(struct smb347_charger *smb)
|
|||
|
||||
static int smb347_charging_set(struct smb347_charger *smb, bool enable)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) {
|
||||
dev_dbg(smb->dev, "charging enable/disable in SW disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (smb->charging_enabled != enable) {
|
||||
ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
|
||||
enable ? CMD_A_CHG_ENABLED : 0);
|
||||
if (!ret)
|
||||
smb->charging_enabled = enable;
|
||||
if (enable && smb->usb_vbus_enabled) {
|
||||
dev_dbg(smb->dev, "charging not enabled because USB is in host mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED,
|
||||
enable ? CMD_A_CHG_ENABLED : 0);
|
||||
}
|
||||
|
||||
static inline int smb347_charging_enable(struct smb347_charger *smb)
|
||||
|
@ -671,10 +679,22 @@ static int smb347_set_temp_limits(struct smb347_charger *smb)
|
|||
*
|
||||
* Returns %0 on success and negative errno in case of failure.
|
||||
*/
|
||||
static int smb347_set_writable(struct smb347_charger *smb, bool writable)
|
||||
static int smb347_set_writable(struct smb347_charger *smb, bool writable,
|
||||
bool irq_toggle)
|
||||
{
|
||||
return regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
|
||||
writable ? CMD_A_ALLOW_WRITE : 0);
|
||||
struct i2c_client *client = to_i2c_client(smb->dev);
|
||||
int ret;
|
||||
|
||||
if (writable && irq_toggle && !smb->irq_unsupported)
|
||||
disable_irq(client->irq);
|
||||
|
||||
ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_ALLOW_WRITE,
|
||||
writable ? CMD_A_ALLOW_WRITE : 0);
|
||||
|
||||
if ((!writable || ret) && irq_toggle && !smb->irq_unsupported)
|
||||
enable_irq(client->irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int smb347_hw_init(struct smb347_charger *smb)
|
||||
|
@ -682,7 +702,7 @@ static int smb347_hw_init(struct smb347_charger *smb)
|
|||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = smb347_set_writable(smb, true);
|
||||
ret = smb347_set_writable(smb, true, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -724,6 +744,15 @@ static int smb347_hw_init(struct smb347_charger *smb)
|
|||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
/* Activate pin control, making it writable. */
|
||||
switch (smb->enable_control) {
|
||||
case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW:
|
||||
case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH:
|
||||
ret = regmap_set_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CTRL);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make the charging functionality controllable by a write to the
|
||||
* command register unless pin control is specified in the platform
|
||||
|
@ -758,7 +787,7 @@ static int smb347_hw_init(struct smb347_charger *smb)
|
|||
ret = smb347_start_stop_charging(smb);
|
||||
|
||||
fail:
|
||||
smb347_set_writable(smb, false);
|
||||
smb347_set_writable(smb, false, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -866,7 +895,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable)
|
|||
if (smb->irq_unsupported)
|
||||
return 0;
|
||||
|
||||
ret = smb347_set_writable(smb, true);
|
||||
ret = smb347_set_writable(smb, true, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -891,7 +920,7 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable)
|
|||
ret = regmap_update_bits(smb->regmap, CFG_PIN, CFG_PIN_EN_CHARGER_ERROR,
|
||||
enable ? CFG_PIN_EN_CHARGER_ERROR : 0);
|
||||
fail:
|
||||
smb347_set_writable(smb, false);
|
||||
smb347_set_writable(smb, false, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -919,7 +948,7 @@ static int smb347_irq_init(struct smb347_charger *smb,
|
|||
if (!client->irq)
|
||||
return 0;
|
||||
|
||||
ret = smb347_set_writable(smb, true);
|
||||
ret = smb347_set_writable(smb, true, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -931,7 +960,7 @@ static int smb347_irq_init(struct smb347_charger *smb,
|
|||
CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED,
|
||||
CFG_STAT_DISABLED);
|
||||
|
||||
smb347_set_writable(smb, false);
|
||||
smb347_set_writable(smb, false, false);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_warn(smb->dev, "failed to initialize IRQ: %d\n", ret);
|
||||
|
@ -1241,6 +1270,13 @@ static void smb347_dt_parse_dev_info(struct smb347_charger *smb)
|
|||
/* Select charging control */
|
||||
device_property_read_u32(dev, "summit,enable-charge-control",
|
||||
&smb->enable_control);
|
||||
|
||||
/*
|
||||
* Polarity of INOK signal indicating presence of external power
|
||||
* supply connected to the charger.
|
||||
*/
|
||||
device_property_read_u32(dev, "summit,inok-polarity",
|
||||
&smb->inok_polarity);
|
||||
}
|
||||
|
||||
static int smb347_get_battery_info(struct smb347_charger *smb)
|
||||
|
@ -1292,12 +1328,176 @@ static int smb347_get_battery_info(struct smb347_charger *smb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int smb347_usb_vbus_get_current_limit(struct regulator_dev *rdev)
|
||||
{
|
||||
struct smb347_charger *smb = rdev_get_drvdata(rdev);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(smb->regmap, CFG_OTG, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* It's unknown what happens if this bit is unset due to lack of
|
||||
* access to the datasheet, assume it's limit-enable.
|
||||
*/
|
||||
if (!(val & CFG_OTG_CURRENT_LIMIT_250mA))
|
||||
return 0;
|
||||
|
||||
return val & CFG_OTG_CURRENT_LIMIT_750mA ? 750000 : 250000;
|
||||
}
|
||||
|
||||
static int smb347_usb_vbus_set_new_current_limit(struct smb347_charger *smb,
|
||||
int max_uA)
|
||||
{
|
||||
const unsigned int mask = CFG_OTG_CURRENT_LIMIT_750mA |
|
||||
CFG_OTG_CURRENT_LIMIT_250mA;
|
||||
unsigned int val = CFG_OTG_CURRENT_LIMIT_250mA;
|
||||
int ret;
|
||||
|
||||
if (max_uA >= 750000)
|
||||
val |= CFG_OTG_CURRENT_LIMIT_750mA;
|
||||
|
||||
ret = regmap_update_bits(smb->regmap, CFG_OTG, mask, val);
|
||||
if (ret < 0)
|
||||
dev_err(smb->dev, "failed to change USB current limit\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int smb347_usb_vbus_set_current_limit(struct regulator_dev *rdev,
|
||||
int min_uA, int max_uA)
|
||||
{
|
||||
struct smb347_charger *smb = rdev_get_drvdata(rdev);
|
||||
int ret;
|
||||
|
||||
ret = smb347_set_writable(smb, true, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA);
|
||||
smb347_set_writable(smb, false, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int smb347_usb_vbus_regulator_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct smb347_charger *smb = rdev_get_drvdata(rdev);
|
||||
int ret, max_uA;
|
||||
|
||||
ret = smb347_set_writable(smb, true, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
smb347_charging_disable(smb);
|
||||
|
||||
if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) {
|
||||
unsigned int sysok = 0;
|
||||
|
||||
if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_LOW)
|
||||
sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
|
||||
|
||||
/*
|
||||
* VBUS won't be powered if INOK is active, so we need to
|
||||
* manually disable INOK on some platforms.
|
||||
*/
|
||||
ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
|
||||
CFG_SYSOK_INOK_ACTIVE_HIGH, sysok);
|
||||
if (ret < 0) {
|
||||
dev_err(smb->dev, "failed to disable INOK\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ret = smb347_usb_vbus_get_current_limit(rdev);
|
||||
if (ret < 0) {
|
||||
dev_err(smb->dev, "failed to get USB VBUS current limit\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
max_uA = ret;
|
||||
|
||||
ret = smb347_usb_vbus_set_new_current_limit(smb, 250000);
|
||||
if (ret < 0) {
|
||||
dev_err(smb->dev, "failed to preset USB VBUS current limit\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = regmap_set_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED);
|
||||
if (ret < 0) {
|
||||
dev_err(smb->dev, "failed to enable USB VBUS\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
smb->usb_vbus_enabled = true;
|
||||
|
||||
ret = smb347_usb_vbus_set_new_current_limit(smb, max_uA);
|
||||
if (ret < 0) {
|
||||
dev_err(smb->dev, "failed to restore USB VBUS current limit\n");
|
||||
goto done;
|
||||
}
|
||||
done:
|
||||
smb347_set_writable(smb, false, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int smb347_usb_vbus_regulator_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct smb347_charger *smb = rdev_get_drvdata(rdev);
|
||||
int ret;
|
||||
|
||||
ret = smb347_set_writable(smb, true, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_clear_bits(smb->regmap, CMD_A, CMD_A_OTG_ENABLED);
|
||||
if (ret < 0) {
|
||||
dev_err(smb->dev, "failed to disable USB VBUS\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
smb->usb_vbus_enabled = false;
|
||||
|
||||
if (device_property_read_bool(&rdev->dev, "summit,needs-inok-toggle")) {
|
||||
unsigned int sysok = 0;
|
||||
|
||||
if (smb->inok_polarity == SMB3XX_SYSOK_INOK_ACTIVE_HIGH)
|
||||
sysok = CFG_SYSOK_INOK_ACTIVE_HIGH;
|
||||
|
||||
ret = regmap_update_bits(smb->regmap, CFG_SYSOK,
|
||||
CFG_SYSOK_INOK_ACTIVE_HIGH, sysok);
|
||||
if (ret < 0) {
|
||||
dev_err(smb->dev, "failed to enable INOK\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
smb347_start_stop_charging(smb);
|
||||
done:
|
||||
smb347_set_writable(smb, false, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct regmap_config smb347_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = SMB347_MAX_REGISTER,
|
||||
.volatile_reg = smb347_volatile_reg,
|
||||
.readable_reg = smb347_readable_reg,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
.num_reg_defaults_raw = SMB347_MAX_REGISTER,
|
||||
};
|
||||
|
||||
static const struct regulator_ops smb347_usb_vbus_regulator_ops = {
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = smb347_usb_vbus_regulator_enable,
|
||||
.disable = smb347_usb_vbus_regulator_disable,
|
||||
.get_current_limit = smb347_usb_vbus_get_current_limit,
|
||||
.set_current_limit = smb347_usb_vbus_set_current_limit,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc smb347_mains_desc = {
|
||||
|
@ -1316,10 +1516,24 @@ static const struct power_supply_desc smb347_usb_desc = {
|
|||
.num_properties = ARRAY_SIZE(smb347_properties),
|
||||
};
|
||||
|
||||
static const struct regulator_desc smb347_usb_vbus_regulator_desc = {
|
||||
.name = "smb347-usb-vbus",
|
||||
.of_match = of_match_ptr("usb-vbus"),
|
||||
.ops = &smb347_usb_vbus_regulator_ops,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.enable_reg = CMD_A,
|
||||
.enable_mask = CMD_A_OTG_ENABLED,
|
||||
.enable_val = CMD_A_OTG_ENABLED,
|
||||
.fixed_uV = 5000000,
|
||||
.n_voltages = 1,
|
||||
};
|
||||
|
||||
static int smb347_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct power_supply_config mains_usb_cfg = {};
|
||||
struct regulator_config usb_rdev_cfg = {};
|
||||
struct device *dev = &client->dev;
|
||||
struct smb347_charger *smb;
|
||||
int ret;
|
||||
|
@ -1367,6 +1581,18 @@ static int smb347_probe(struct i2c_client *client,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
usb_rdev_cfg.dev = dev;
|
||||
usb_rdev_cfg.driver_data = smb;
|
||||
usb_rdev_cfg.regmap = smb->regmap;
|
||||
|
||||
smb->usb_rdev = devm_regulator_register(dev,
|
||||
&smb347_usb_vbus_regulator_desc,
|
||||
&usb_rdev_cfg);
|
||||
if (IS_ERR(smb->usb_rdev)) {
|
||||
smb347_irq_disable(smb);
|
||||
return PTR_ERR(smb->usb_rdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1374,11 +1600,17 @@ static int smb347_remove(struct i2c_client *client)
|
|||
{
|
||||
struct smb347_charger *smb = i2c_get_clientdata(client);
|
||||
|
||||
smb347_usb_vbus_regulator_disable(smb->usb_rdev);
|
||||
smb347_irq_disable(smb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smb347_shutdown(struct i2c_client *client)
|
||||
{
|
||||
smb347_remove(client);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id smb347_id[] = {
|
||||
{ "smb345", SMB345 },
|
||||
{ "smb347", SMB347 },
|
||||
|
@ -1402,6 +1634,7 @@ static struct i2c_driver smb347_driver = {
|
|||
},
|
||||
.probe = smb347_probe,
|
||||
.remove = smb347_remove,
|
||||
.shutdown = smb347_shutdown,
|
||||
.id_table = smb347_id,
|
||||
};
|
||||
module_i2c_driver(smb347_driver);
|
||||
|
|
|
@ -1044,7 +1044,7 @@ config REGULATOR_RT6160
|
|||
help
|
||||
This adds support for voltage regulator in Richtek RT6160.
|
||||
This device automatically change voltage output mode from
|
||||
Buck or Boost. The mode transistion depend on the input source voltage.
|
||||
Buck or Boost. The mode transition depend on the input source voltage.
|
||||
The wide output range is from 2025mV to 5200mV and can be used on most
|
||||
common application scenario.
|
||||
|
||||
|
@ -1053,10 +1053,21 @@ config REGULATOR_RT6245
|
|||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This adds supprot for Richtek RT6245 voltage regulator.
|
||||
This adds support for Richtek RT6245 voltage regulator.
|
||||
It can support up to 14A output current and adjustable output voltage
|
||||
from 0.4375V to 1.3875V, per step 12.5mV.
|
||||
|
||||
config REGULATOR_RTQ2134
|
||||
tristate "Richtek RTQ2134 SubPMIC Regulator"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver adds support for RTQ2134 SubPMIC regulators.
|
||||
The RTQ2134 is a multi-phase, programmable power management IC that
|
||||
integrate with four high efficient, synchronous step-down converter
|
||||
cores. It features wide output voltage range and the capability to
|
||||
configure the corresponding power stages.
|
||||
|
||||
config REGULATOR_RTMV20
|
||||
tristate "Richtek RTMV20 Laser Diode Regulator"
|
||||
depends on I2C
|
||||
|
@ -1066,6 +1077,15 @@ config REGULATOR_RTMV20
|
|||
the Richtek RTMV20. It can support the load current up to 6A and
|
||||
integrate strobe/vsync/fsin signal to synchronize the IR camera.
|
||||
|
||||
config REGULATOR_RTQ6752
|
||||
tristate "Richtek RTQ6752 TFT LCD voltage regulator"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver adds support for Richtek RTQ6752. RTQ6752 includes two
|
||||
synchronous boost converters for PAVDD, and one synchronous NAVDD
|
||||
buck-boost. This device is suitable for automotive TFT-LCD panel.
|
||||
|
||||
config REGULATOR_S2MPA01
|
||||
tristate "Samsung S2MPA01 voltage regulator"
|
||||
depends on MFD_SEC_CORE || COMPILE_TEST
|
||||
|
|
|
@ -128,6 +128,8 @@ obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_RTQ2134) += rtq2134-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_RTQ6752) += rtq6752-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
|
||||
obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
|
||||
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
|
||||
|
|
|
@ -55,7 +55,8 @@
|
|||
#define BD718XX_HWOPNAME(swopname) swopname##_hwcontrol
|
||||
|
||||
#define BD718XX_OPS(name, _list_voltage, _map_voltage, _set_voltage_sel, \
|
||||
_get_voltage_sel, _set_voltage_time_sel, _set_ramp_delay) \
|
||||
_get_voltage_sel, _set_voltage_time_sel, _set_ramp_delay, \
|
||||
_set_uvp, _set_ovp) \
|
||||
static const struct regulator_ops name = { \
|
||||
.enable = regulator_enable_regmap, \
|
||||
.disable = regulator_disable_regmap, \
|
||||
|
@ -66,6 +67,8 @@ static const struct regulator_ops name = { \
|
|||
.get_voltage_sel = (_get_voltage_sel), \
|
||||
.set_voltage_time_sel = (_set_voltage_time_sel), \
|
||||
.set_ramp_delay = (_set_ramp_delay), \
|
||||
.set_under_voltage_protection = (_set_uvp), \
|
||||
.set_over_voltage_protection = (_set_ovp), \
|
||||
}; \
|
||||
\
|
||||
static const struct regulator_ops BD718XX_HWOPNAME(name) = { \
|
||||
|
@ -76,6 +79,8 @@ static const struct regulator_ops BD718XX_HWOPNAME(name) = { \
|
|||
.get_voltage_sel = (_get_voltage_sel), \
|
||||
.set_voltage_time_sel = (_set_voltage_time_sel), \
|
||||
.set_ramp_delay = (_set_ramp_delay), \
|
||||
.set_under_voltage_protection = (_set_uvp), \
|
||||
.set_over_voltage_protection = (_set_ovp), \
|
||||
} \
|
||||
|
||||
/*
|
||||
|
@ -154,17 +159,9 @@ static void voltage_change_done(struct regulator_dev *rdev, unsigned int sel,
|
|||
* exceed it due to the scheduling.
|
||||
*/
|
||||
msleep(1);
|
||||
/*
|
||||
* Note for next hacker. The PWRGOOD should not be masked on
|
||||
* BD71847 so we will just unconditionally enable detection
|
||||
* when voltage is set.
|
||||
* If someone want's to disable PWRGOOD he must implement
|
||||
* caching and restoring the old value here. I am not
|
||||
* aware of such use-cases so for the sake of the simplicity
|
||||
* we just always enable PWRGOOD here.
|
||||
*/
|
||||
ret = regmap_update_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
|
||||
*mask, 0);
|
||||
|
||||
ret = regmap_clear_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
|
||||
*mask);
|
||||
if (ret)
|
||||
dev_err(&rdev->dev,
|
||||
"Failed to re-enable voltage monitoring (%d)\n",
|
||||
|
@ -208,12 +205,27 @@ static int voltage_change_prepare(struct regulator_dev *rdev, unsigned int sel,
|
|||
* time configurable.
|
||||
*/
|
||||
if (new > now) {
|
||||
int tmp;
|
||||
int prot_bit;
|
||||
int ldo_offset = rdev->desc->id - BD718XX_LDO1;
|
||||
|
||||
*mask = BD718XX_LDO1_VRMON80 << ldo_offset;
|
||||
ret = regmap_update_bits(rdev->regmap,
|
||||
BD718XX_REG_MVRFLTMASK2,
|
||||
*mask, *mask);
|
||||
prot_bit = BD718XX_LDO1_VRMON80 << ldo_offset;
|
||||
ret = regmap_read(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
|
||||
&tmp);
|
||||
if (ret) {
|
||||
dev_err(&rdev->dev,
|
||||
"Failed to read voltage monitoring state\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!(tmp & prot_bit)) {
|
||||
/* We disable protection if it was enabled... */
|
||||
ret = regmap_set_bits(rdev->regmap,
|
||||
BD718XX_REG_MVRFLTMASK2,
|
||||
prot_bit);
|
||||
/* ...and we also want to re-enable it */
|
||||
*mask = prot_bit;
|
||||
}
|
||||
if (ret) {
|
||||
dev_err(&rdev->dev,
|
||||
"Failed to stop voltage monitoring\n");
|
||||
|
@ -266,99 +278,6 @@ static int bd71837_set_voltage_sel_pickable_restricted(
|
|||
return regulator_set_voltage_sel_pickable_regmap(rdev, sel);
|
||||
}
|
||||
|
||||
/*
|
||||
* OPS common for BD71847 and BD71850
|
||||
*/
|
||||
BD718XX_OPS(bd718xx_pickable_range_ldo_ops,
|
||||
regulator_list_voltage_pickable_linear_range, NULL,
|
||||
bd718xx_set_voltage_sel_pickable_restricted,
|
||||
regulator_get_voltage_sel_pickable_regmap, NULL, NULL);
|
||||
|
||||
/* BD71847 and BD71850 LDO 5 is by default OFF at RUN state */
|
||||
static const struct regulator_ops bd718xx_ldo5_ops_hwstate = {
|
||||
.is_enabled = never_enabled_by_hwstate,
|
||||
.list_voltage = regulator_list_voltage_pickable_linear_range,
|
||||
.set_voltage_sel = bd718xx_set_voltage_sel_pickable_restricted,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
|
||||
};
|
||||
|
||||
BD718XX_OPS(bd718xx_pickable_range_buck_ops,
|
||||
regulator_list_voltage_pickable_linear_range, NULL,
|
||||
regulator_set_voltage_sel_pickable_regmap,
|
||||
regulator_get_voltage_sel_pickable_regmap,
|
||||
regulator_set_voltage_time_sel, NULL);
|
||||
|
||||
BD718XX_OPS(bd718xx_ldo_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, bd718xx_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, NULL, NULL);
|
||||
|
||||
BD718XX_OPS(bd718xx_ldo_regulator_nolinear_ops, regulator_list_voltage_table,
|
||||
NULL, bd718xx_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, NULL, NULL);
|
||||
|
||||
BD718XX_OPS(bd718xx_buck_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, regulator_set_voltage_sel_regmap,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
NULL);
|
||||
|
||||
BD718XX_OPS(bd718xx_buck_regulator_nolinear_ops, regulator_list_voltage_table,
|
||||
regulator_map_voltage_ascend, regulator_set_voltage_sel_regmap,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
NULL);
|
||||
|
||||
/*
|
||||
* OPS for BD71837
|
||||
*/
|
||||
BD718XX_OPS(bd71837_pickable_range_ldo_ops,
|
||||
regulator_list_voltage_pickable_linear_range, NULL,
|
||||
bd71837_set_voltage_sel_pickable_restricted,
|
||||
regulator_get_voltage_sel_pickable_regmap, NULL, NULL);
|
||||
|
||||
BD718XX_OPS(bd71837_pickable_range_buck_ops,
|
||||
regulator_list_voltage_pickable_linear_range, NULL,
|
||||
bd71837_set_voltage_sel_pickable_restricted,
|
||||
regulator_get_voltage_sel_pickable_regmap,
|
||||
regulator_set_voltage_time_sel, NULL);
|
||||
|
||||
BD718XX_OPS(bd71837_ldo_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, bd71837_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, NULL, NULL);
|
||||
|
||||
BD718XX_OPS(bd71837_ldo_regulator_nolinear_ops, regulator_list_voltage_table,
|
||||
NULL, bd71837_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, NULL, NULL);
|
||||
|
||||
BD718XX_OPS(bd71837_buck_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, bd71837_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
NULL);
|
||||
|
||||
BD718XX_OPS(bd71837_buck_regulator_nolinear_ops, regulator_list_voltage_table,
|
||||
regulator_map_voltage_ascend, bd71837_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
NULL);
|
||||
/*
|
||||
* BD71837 bucks 3 and 4 support defining their enable/disable state also
|
||||
* when buck enable state is under HW state machine control. In that case the
|
||||
* bit [2] in CTRL register is used to indicate if regulator should be ON.
|
||||
*/
|
||||
static const struct regulator_ops bd71837_buck34_ops_hwctrl = {
|
||||
.is_enabled = bd71837_get_buck34_enable_hwctrl,
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
||||
.set_ramp_delay = regulator_set_ramp_delay_regmap,
|
||||
};
|
||||
|
||||
/*
|
||||
* OPS for all of the ICs - BD718(37/47/50)
|
||||
*/
|
||||
BD718XX_OPS(bd718xx_dvs_buck_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, regulator_set_voltage_sel_regmap,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
/* bd718xx_buck1234_set_ramp_delay */ regulator_set_ramp_delay_regmap);
|
||||
|
||||
/*
|
||||
* BD71837 BUCK1/2/3/4
|
||||
* BD71847 BUCK1/2
|
||||
|
@ -536,6 +455,238 @@ struct bd718xx_regulator_data {
|
|||
int additional_init_amnt;
|
||||
};
|
||||
|
||||
static int bd718x7_xvp_sanity_check(struct regulator_dev *rdev, int lim_uV,
|
||||
int severity)
|
||||
{
|
||||
/*
|
||||
* BD71837/47/50 ... (ICs supported by this driver) do not provide
|
||||
* warnings, only protection
|
||||
*/
|
||||
if (severity != REGULATOR_SEVERITY_PROT) {
|
||||
dev_err(&rdev->dev,
|
||||
"Unsupported Under Voltage protection level\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* And protection limit is not changeable. It can only be enabled
|
||||
* or disabled
|
||||
*/
|
||||
if (lim_uV)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd718x7_set_ldo_uvp(struct regulator_dev *rdev, int lim_uV,
|
||||
int severity, bool enable)
|
||||
{
|
||||
int ldo_offset = rdev->desc->id - BD718XX_LDO1;
|
||||
int prot_bit, ret;
|
||||
|
||||
ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
prot_bit = BD718XX_LDO1_VRMON80 << ldo_offset;
|
||||
|
||||
if (enable)
|
||||
return regmap_clear_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
|
||||
prot_bit);
|
||||
|
||||
return regmap_set_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
|
||||
prot_bit);
|
||||
}
|
||||
|
||||
static int bd718x7_get_buck_prot_reg(int id, int *reg)
|
||||
{
|
||||
|
||||
if (id > BD718XX_BUCK8) {
|
||||
WARN_ON(id > BD718XX_BUCK8);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (id > BD718XX_BUCK4)
|
||||
*reg = BD718XX_REG_MVRFLTMASK0;
|
||||
else
|
||||
*reg = BD718XX_REG_MVRFLTMASK1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd718x7_get_buck_ovp_info(int id, int *reg, int *bit)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bd718x7_get_buck_prot_reg(id, reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*bit = BIT((id % 4) * 2 + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd718x7_get_buck_uvp_info(int id, int *reg, int *bit)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bd718x7_get_buck_prot_reg(id, reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*bit = BIT((id % 4) * 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd718x7_set_buck_uvp(struct regulator_dev *rdev, int lim_uV,
|
||||
int severity, bool enable)
|
||||
{
|
||||
int bit, reg, ret;
|
||||
|
||||
ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bd718x7_get_buck_uvp_info(rdev->desc->id, ®, &bit);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
return regmap_clear_bits(rdev->regmap, reg, bit);
|
||||
|
||||
return regmap_set_bits(rdev->regmap, reg, bit);
|
||||
|
||||
}
|
||||
|
||||
static int bd718x7_set_buck_ovp(struct regulator_dev *rdev, int lim_uV,
|
||||
int severity,
|
||||
bool enable)
|
||||
{
|
||||
int bit, reg, ret;
|
||||
|
||||
ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bd718x7_get_buck_ovp_info(rdev->desc->id, ®, &bit);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
return regmap_clear_bits(rdev->regmap, reg, bit);
|
||||
|
||||
return regmap_set_bits(rdev->regmap, reg, bit);
|
||||
}
|
||||
|
||||
/*
|
||||
* OPS common for BD71847 and BD71850
|
||||
*/
|
||||
BD718XX_OPS(bd718xx_pickable_range_ldo_ops,
|
||||
regulator_list_voltage_pickable_linear_range, NULL,
|
||||
bd718xx_set_voltage_sel_pickable_restricted,
|
||||
regulator_get_voltage_sel_pickable_regmap, NULL, NULL,
|
||||
bd718x7_set_ldo_uvp, NULL);
|
||||
|
||||
/* BD71847 and BD71850 LDO 5 is by default OFF at RUN state */
|
||||
static const struct regulator_ops bd718xx_ldo5_ops_hwstate = {
|
||||
.is_enabled = never_enabled_by_hwstate,
|
||||
.list_voltage = regulator_list_voltage_pickable_linear_range,
|
||||
.set_voltage_sel = bd718xx_set_voltage_sel_pickable_restricted,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
|
||||
.set_under_voltage_protection = bd718x7_set_ldo_uvp,
|
||||
};
|
||||
|
||||
BD718XX_OPS(bd718xx_pickable_range_buck_ops,
|
||||
regulator_list_voltage_pickable_linear_range, NULL,
|
||||
regulator_set_voltage_sel_pickable_regmap,
|
||||
regulator_get_voltage_sel_pickable_regmap,
|
||||
regulator_set_voltage_time_sel, NULL, bd718x7_set_buck_uvp,
|
||||
bd718x7_set_buck_ovp);
|
||||
|
||||
BD718XX_OPS(bd718xx_ldo_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, bd718xx_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp,
|
||||
NULL);
|
||||
|
||||
BD718XX_OPS(bd718xx_ldo_regulator_nolinear_ops, regulator_list_voltage_table,
|
||||
NULL, bd718xx_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp,
|
||||
NULL);
|
||||
|
||||
BD718XX_OPS(bd718xx_buck_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, regulator_set_voltage_sel_regmap,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp);
|
||||
|
||||
BD718XX_OPS(bd718xx_buck_regulator_nolinear_ops, regulator_list_voltage_table,
|
||||
regulator_map_voltage_ascend, regulator_set_voltage_sel_regmap,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp);
|
||||
|
||||
/*
|
||||
* OPS for BD71837
|
||||
*/
|
||||
BD718XX_OPS(bd71837_pickable_range_ldo_ops,
|
||||
regulator_list_voltage_pickable_linear_range, NULL,
|
||||
bd71837_set_voltage_sel_pickable_restricted,
|
||||
regulator_get_voltage_sel_pickable_regmap, NULL, NULL,
|
||||
bd718x7_set_ldo_uvp, NULL);
|
||||
|
||||
BD718XX_OPS(bd71837_pickable_range_buck_ops,
|
||||
regulator_list_voltage_pickable_linear_range, NULL,
|
||||
bd71837_set_voltage_sel_pickable_restricted,
|
||||
regulator_get_voltage_sel_pickable_regmap,
|
||||
regulator_set_voltage_time_sel, NULL, bd718x7_set_buck_uvp,
|
||||
bd718x7_set_buck_ovp);
|
||||
|
||||
BD718XX_OPS(bd71837_ldo_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, bd71837_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp,
|
||||
NULL);
|
||||
|
||||
BD718XX_OPS(bd71837_ldo_regulator_nolinear_ops, regulator_list_voltage_table,
|
||||
NULL, bd71837_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, NULL, NULL, bd718x7_set_ldo_uvp,
|
||||
NULL);
|
||||
|
||||
BD718XX_OPS(bd71837_buck_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, bd71837_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp);
|
||||
|
||||
BD718XX_OPS(bd71837_buck_regulator_nolinear_ops, regulator_list_voltage_table,
|
||||
regulator_map_voltage_ascend, bd71837_set_voltage_sel_restricted,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp);
|
||||
/*
|
||||
* BD71837 bucks 3 and 4 support defining their enable/disable state also
|
||||
* when buck enable state is under HW state machine control. In that case the
|
||||
* bit [2] in CTRL register is used to indicate if regulator should be ON.
|
||||
*/
|
||||
static const struct regulator_ops bd71837_buck34_ops_hwctrl = {
|
||||
.is_enabled = bd71837_get_buck34_enable_hwctrl,
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
||||
.set_ramp_delay = regulator_set_ramp_delay_regmap,
|
||||
.set_under_voltage_protection = bd718x7_set_buck_uvp,
|
||||
.set_over_voltage_protection = bd718x7_set_buck_ovp,
|
||||
};
|
||||
|
||||
/*
|
||||
* OPS for all of the ICs - BD718(37/47/50)
|
||||
*/
|
||||
BD718XX_OPS(bd718xx_dvs_buck_regulator_ops, regulator_list_voltage_linear_range,
|
||||
NULL, regulator_set_voltage_sel_regmap,
|
||||
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
|
||||
regulator_set_ramp_delay_regmap, bd718x7_set_buck_uvp,
|
||||
bd718x7_set_buck_ovp);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* There is a HW quirk in BD71837. The shutdown sequence timings for
|
||||
* bucks/LDOs which are controlled via register interface are changed.
|
||||
|
|
|
@ -412,6 +412,134 @@ static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev,
|
|||
return regmap_field_write(regl->suspend_sleep, val);
|
||||
}
|
||||
|
||||
static unsigned int da9063_get_overdrive_mask(const struct regulator_desc *desc)
|
||||
{
|
||||
switch (desc->id) {
|
||||
case DA9063_ID_BCORES_MERGED:
|
||||
case DA9063_ID_BCORE1:
|
||||
return DA9063_BCORE1_OD;
|
||||
case DA9063_ID_BCORE2:
|
||||
return DA9063_BCORE2_OD;
|
||||
case DA9063_ID_BPRO:
|
||||
return DA9063_BPRO_OD;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int da9063_buck_set_limit_set_overdrive(struct regulator_dev *rdev,
|
||||
int min_uA, int max_uA,
|
||||
unsigned int overdrive_mask)
|
||||
{
|
||||
/*
|
||||
* When enabling overdrive, do it before changing the current limit to
|
||||
* ensure sufficient supply throughout the switch.
|
||||
*/
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
int ret;
|
||||
unsigned int orig_overdrive;
|
||||
|
||||
ret = regmap_read(regl->hw->regmap, DA9063_REG_CONFIG_H,
|
||||
&orig_overdrive);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
orig_overdrive &= overdrive_mask;
|
||||
|
||||
if (orig_overdrive == 0) {
|
||||
ret = regmap_set_bits(regl->hw->regmap, DA9063_REG_CONFIG_H,
|
||||
overdrive_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_set_current_limit_regmap(rdev, min_uA / 2, max_uA / 2);
|
||||
if (ret < 0 && orig_overdrive == 0)
|
||||
/*
|
||||
* regulator_set_current_limit_regmap may have rejected the
|
||||
* change because of unusable min_uA and/or max_uA inputs.
|
||||
* Attempt to restore original overdrive state, ignore failure-
|
||||
* on-failure.
|
||||
*/
|
||||
regmap_clear_bits(regl->hw->regmap, DA9063_REG_CONFIG_H,
|
||||
overdrive_mask);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int da9063_buck_set_limit_clear_overdrive(struct regulator_dev *rdev,
|
||||
int min_uA, int max_uA,
|
||||
unsigned int overdrive_mask)
|
||||
{
|
||||
/*
|
||||
* When disabling overdrive, do it after changing the current limit to
|
||||
* ensure sufficient supply throughout the switch.
|
||||
*/
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
int ret, orig_limit;
|
||||
|
||||
ret = regmap_read(rdev->regmap, rdev->desc->csel_reg, &orig_limit);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regulator_set_current_limit_regmap(rdev, min_uA, max_uA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_clear_bits(regl->hw->regmap, DA9063_REG_CONFIG_H,
|
||||
overdrive_mask);
|
||||
if (ret < 0)
|
||||
/*
|
||||
* Attempt to restore original current limit, ignore failure-
|
||||
* on-failure.
|
||||
*/
|
||||
regmap_write(rdev->regmap, rdev->desc->csel_reg, orig_limit);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int da9063_buck_set_current_limit(struct regulator_dev *rdev,
|
||||
int min_uA, int max_uA)
|
||||
{
|
||||
unsigned int overdrive_mask, n_currents;
|
||||
|
||||
overdrive_mask = da9063_get_overdrive_mask(rdev->desc);
|
||||
if (overdrive_mask) {
|
||||
n_currents = rdev->desc->n_current_limits;
|
||||
if (n_currents == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (max_uA > rdev->desc->curr_table[n_currents - 1])
|
||||
return da9063_buck_set_limit_set_overdrive(rdev, min_uA,
|
||||
max_uA,
|
||||
overdrive_mask);
|
||||
|
||||
return da9063_buck_set_limit_clear_overdrive(rdev, min_uA,
|
||||
max_uA,
|
||||
overdrive_mask);
|
||||
}
|
||||
return regulator_set_current_limit_regmap(rdev, min_uA, max_uA);
|
||||
}
|
||||
|
||||
static int da9063_buck_get_current_limit(struct regulator_dev *rdev)
|
||||
{
|
||||
struct da9063_regulator *regl = rdev_get_drvdata(rdev);
|
||||
int val, ret, limit;
|
||||
unsigned int mask;
|
||||
|
||||
limit = regulator_get_current_limit_regmap(rdev);
|
||||
if (limit < 0)
|
||||
return limit;
|
||||
mask = da9063_get_overdrive_mask(rdev->desc);
|
||||
if (mask) {
|
||||
ret = regmap_read(regl->hw->regmap, DA9063_REG_CONFIG_H, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (val & mask)
|
||||
limit *= 2;
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
static const struct regulator_ops da9063_buck_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
|
@ -419,8 +547,8 @@ static const struct regulator_ops da9063_buck_ops = {
|
|||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.set_current_limit = regulator_set_current_limit_regmap,
|
||||
.get_current_limit = regulator_get_current_limit_regmap,
|
||||
.set_current_limit = da9063_buck_set_current_limit,
|
||||
.get_current_limit = da9063_buck_get_current_limit,
|
||||
.set_mode = da9063_buck_set_mode,
|
||||
.get_mode = da9063_buck_get_mode,
|
||||
.get_status = da9063_buck_get_status,
|
||||
|
|
|
@ -117,11 +117,11 @@ ux500_regulator_debug_init(struct platform_device *pdev,
|
|||
rdebug.dir = debugfs_create_dir("ux500-regulator", NULL);
|
||||
|
||||
/* create "status" file */
|
||||
debugfs_create_file("status", S_IRUGO, rdebug.dir, &pdev->dev,
|
||||
debugfs_create_file("status", 0444, rdebug.dir, &pdev->dev,
|
||||
&ux500_regulator_status_fops);
|
||||
|
||||
/* create "power-state-count" file */
|
||||
debugfs_create_file("power-state-count", S_IRUGO, rdebug.dir,
|
||||
debugfs_create_file("power-state-count", 0444, rdebug.dir,
|
||||
&pdev->dev, &ux500_regulator_power_state_cnt_fops);
|
||||
|
||||
rdebug.regulator_array = regulator_info;
|
||||
|
|
|
@ -205,35 +205,6 @@ struct regulator_dev *devm_regulator_register(struct device *dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regulator_register);
|
||||
|
||||
static int devm_rdev_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct regulator_dev **r = res;
|
||||
if (!r || !*r) {
|
||||
WARN_ON(!r || !*r);
|
||||
return 0;
|
||||
}
|
||||
return *r == data;
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_regulator_unregister - Resource managed regulator_unregister()
|
||||
* @dev: device to supply
|
||||
* @rdev: regulator to free
|
||||
*
|
||||
* Unregister a regulator registered with devm_regulator_register().
|
||||
* Normally this function will not need to be called and the resource
|
||||
* management code will ensure that the resource is freed.
|
||||
*/
|
||||
void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = devres_release(dev, devm_rdev_release, devm_rdev_match, rdev);
|
||||
if (rc != 0)
|
||||
WARN_ON(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regulator_unregister);
|
||||
|
||||
struct regulator_supply_alias_match {
|
||||
struct device *dev;
|
||||
const char *id;
|
||||
|
@ -296,19 +267,8 @@ int devm_regulator_register_supply_alias(struct device *dev, const char *id,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regulator_register_supply_alias);
|
||||
|
||||
/**
|
||||
* devm_regulator_unregister_supply_alias - Resource managed
|
||||
* regulator_unregister_supply_alias()
|
||||
*
|
||||
* @dev: device to supply
|
||||
* @id: supply name or regulator ID
|
||||
*
|
||||
* Unregister an alias registered with
|
||||
* devm_regulator_register_supply_alias(). Normally this function
|
||||
* will not need to be called and the resource management code
|
||||
* will ensure that the resource is freed.
|
||||
*/
|
||||
void devm_regulator_unregister_supply_alias(struct device *dev, const char *id)
|
||||
static void devm_regulator_unregister_supply_alias(struct device *dev,
|
||||
const char *id)
|
||||
{
|
||||
struct regulator_supply_alias_match match;
|
||||
int rc;
|
||||
|
@ -321,7 +281,6 @@ void devm_regulator_unregister_supply_alias(struct device *dev, const char *id)
|
|||
if (rc != 0)
|
||||
WARN_ON(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regulator_unregister_supply_alias);
|
||||
|
||||
/**
|
||||
* devm_regulator_bulk_register_supply_alias - Managed register
|
||||
|
@ -373,30 +332,6 @@ int devm_regulator_bulk_register_supply_alias(struct device *dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regulator_bulk_register_supply_alias);
|
||||
|
||||
/**
|
||||
* devm_regulator_bulk_unregister_supply_alias - Managed unregister
|
||||
* multiple aliases
|
||||
*
|
||||
* @dev: device to supply
|
||||
* @id: list of supply names or regulator IDs
|
||||
* @num_id: number of aliases to unregister
|
||||
*
|
||||
* Unregister aliases registered with
|
||||
* devm_regulator_bulk_register_supply_alias(). Normally this function
|
||||
* will not need to be called and the resource management code
|
||||
* will ensure that the resource is freed.
|
||||
*/
|
||||
void devm_regulator_bulk_unregister_supply_alias(struct device *dev,
|
||||
const char *const *id,
|
||||
int num_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_id; ++i)
|
||||
devm_regulator_unregister_supply_alias(dev, id[i]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regulator_bulk_unregister_supply_alias);
|
||||
|
||||
struct regulator_notifier_match {
|
||||
struct regulator *regulator;
|
||||
struct notifier_block *nb;
|
||||
|
|
|
@ -287,8 +287,9 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev)
|
|||
drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc,
|
||||
&cfg);
|
||||
if (IS_ERR(drvdata->dev)) {
|
||||
ret = PTR_ERR(drvdata->dev);
|
||||
dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(drvdata->dev),
|
||||
"Failed to register regulator: %ld\n",
|
||||
PTR_ERR(drvdata->dev));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
// Copyright (c) 2013 Linaro Ltd.
|
||||
// Copyright (c) 2011 HiSilicon Ltd.
|
||||
// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd
|
||||
// Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
|
||||
//
|
||||
// Guodong Xu <guodong.xu@linaro.org>
|
||||
|
||||
|
@ -27,34 +27,34 @@ struct hi6421_spmi_reg_info {
|
|||
u32 eco_uA;
|
||||
};
|
||||
|
||||
static const unsigned int ldo3_voltages[] = {
|
||||
static const unsigned int range_1v5_to_2v0[] = {
|
||||
1500000, 1550000, 1600000, 1650000,
|
||||
1700000, 1725000, 1750000, 1775000,
|
||||
1800000, 1825000, 1850000, 1875000,
|
||||
1900000, 1925000, 1950000, 2000000
|
||||
};
|
||||
|
||||
static const unsigned int ldo4_voltages[] = {
|
||||
static const unsigned int range_1v725_to_1v9[] = {
|
||||
1725000, 1750000, 1775000, 1800000,
|
||||
1825000, 1850000, 1875000, 1900000
|
||||
};
|
||||
|
||||
static const unsigned int ldo9_voltages[] = {
|
||||
static const unsigned int range_1v75_to_3v3[] = {
|
||||
1750000, 1800000, 1825000, 2800000,
|
||||
2850000, 2950000, 3000000, 3300000
|
||||
};
|
||||
|
||||
static const unsigned int ldo15_voltages[] = {
|
||||
static const unsigned int range_1v8_to_3v0[] = {
|
||||
1800000, 1850000, 2400000, 2600000,
|
||||
2700000, 2850000, 2950000, 3000000
|
||||
};
|
||||
|
||||
static const unsigned int ldo17_voltages[] = {
|
||||
static const unsigned int range_2v5_to_3v3[] = {
|
||||
2500000, 2600000, 2700000, 2800000,
|
||||
3000000, 3100000, 3200000, 3300000
|
||||
};
|
||||
|
||||
static const unsigned int ldo34_voltages[] = {
|
||||
static const unsigned int range_2v6_to_3v3[] = {
|
||||
2600000, 2700000, 2800000, 2900000,
|
||||
3000000, 3100000, 3200000, 3300000
|
||||
};
|
||||
|
@ -73,14 +73,14 @@ static const unsigned int ldo34_voltages[] = {
|
|||
*/
|
||||
#define HI6421V600_LDO(_id, vtable, ereg, emask, vreg, \
|
||||
odelay, etime, ecomask, ecoamp) \
|
||||
[HI6421V600_##_id] = { \
|
||||
[hi6421v600_##_id] = { \
|
||||
.desc = { \
|
||||
.name = #_id, \
|
||||
.of_match = of_match_ptr(#_id), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.ops = &hi6421_spmi_ldo_rops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.id = HI6421V600_##_id, \
|
||||
.id = hi6421v600_##_id, \
|
||||
.owner = THIS_MODULE, \
|
||||
.volt_table = vtable, \
|
||||
.n_voltages = ARRAY_SIZE(vtable), \
|
||||
|
@ -185,46 +185,46 @@ static const struct regulator_ops hi6421_spmi_ldo_rops = {
|
|||
|
||||
/* HI6421v600 regulators with known registers */
|
||||
enum hi6421_spmi_regulator_id {
|
||||
HI6421V600_LDO3,
|
||||
HI6421V600_LDO4,
|
||||
HI6421V600_LDO9,
|
||||
HI6421V600_LDO15,
|
||||
HI6421V600_LDO16,
|
||||
HI6421V600_LDO17,
|
||||
HI6421V600_LDO33,
|
||||
HI6421V600_LDO34,
|
||||
hi6421v600_ldo3,
|
||||
hi6421v600_ldo4,
|
||||
hi6421v600_ldo9,
|
||||
hi6421v600_ldo15,
|
||||
hi6421v600_ldo16,
|
||||
hi6421v600_ldo17,
|
||||
hi6421v600_ldo33,
|
||||
hi6421v600_ldo34,
|
||||
};
|
||||
|
||||
static struct hi6421_spmi_reg_info regulator_info[] = {
|
||||
HI6421V600_LDO(LDO3, ldo3_voltages,
|
||||
HI6421V600_LDO(ldo3, range_1v5_to_2v0,
|
||||
0x16, 0x01, 0x51,
|
||||
20000, 120,
|
||||
0, 0),
|
||||
HI6421V600_LDO(LDO4, ldo4_voltages,
|
||||
HI6421V600_LDO(ldo4, range_1v725_to_1v9,
|
||||
0x17, 0x01, 0x52,
|
||||
20000, 120,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO9, ldo9_voltages,
|
||||
HI6421V600_LDO(ldo9, range_1v75_to_3v3,
|
||||
0x1c, 0x01, 0x57,
|
||||
20000, 360,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO15, ldo15_voltages,
|
||||
HI6421V600_LDO(ldo15, range_1v8_to_3v0,
|
||||
0x21, 0x01, 0x5c,
|
||||
20000, 360,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO16, ldo15_voltages,
|
||||
HI6421V600_LDO(ldo16, range_1v8_to_3v0,
|
||||
0x22, 0x01, 0x5d,
|
||||
20000, 360,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO17, ldo17_voltages,
|
||||
HI6421V600_LDO(ldo17, range_2v5_to_3v3,
|
||||
0x23, 0x01, 0x5e,
|
||||
20000, 120,
|
||||
0x10, 10000),
|
||||
HI6421V600_LDO(LDO33, ldo17_voltages,
|
||||
HI6421V600_LDO(ldo33, range_2v5_to_3v3,
|
||||
0x32, 0x01, 0x6d,
|
||||
20000, 120,
|
||||
0, 0),
|
||||
HI6421V600_LDO(LDO34, ldo34_voltages,
|
||||
HI6421V600_LDO(ldo34, range_2v6_to_3v3,
|
||||
0x33, 0x01, 0x6e,
|
||||
20000, 120,
|
||||
0, 0),
|
||||
|
|
|
@ -184,7 +184,7 @@ static irqreturn_t regulator_notifier_isr(int irq, void *data)
|
|||
* If retry_count exceeds the given safety limit we call IC specific die
|
||||
* handler which can try disabling regulator(s).
|
||||
*
|
||||
* If no die handler is given we will just bug() as a last resort.
|
||||
* If no die handler is given we will just power-off as a last resort.
|
||||
*
|
||||
* We could try disabling all associated rdevs - but we might shoot
|
||||
* ourselves in the head and leave the problematic regulator enabled. So
|
||||
|
|
|
@ -28,18 +28,15 @@ struct mt6358_regulator_info {
|
|||
u32 qi;
|
||||
const u32 *index_table;
|
||||
unsigned int n_table;
|
||||
u32 vsel_shift;
|
||||
u32 da_vsel_reg;
|
||||
u32 da_vsel_mask;
|
||||
u32 da_vsel_shift;
|
||||
u32 modeset_reg;
|
||||
u32 modeset_mask;
|
||||
u32 modeset_shift;
|
||||
};
|
||||
|
||||
#define MT6358_BUCK(match, vreg, min, max, step, \
|
||||
volt_ranges, vosel_mask, _da_vsel_reg, _da_vsel_mask, \
|
||||
_da_vsel_shift, _modeset_reg, _modeset_shift) \
|
||||
_modeset_reg, _modeset_shift) \
|
||||
[MT6358_ID_##vreg] = { \
|
||||
.desc = { \
|
||||
.name = #vreg, \
|
||||
|
@ -61,15 +58,13 @@ struct mt6358_regulator_info {
|
|||
.qi = BIT(0), \
|
||||
.da_vsel_reg = _da_vsel_reg, \
|
||||
.da_vsel_mask = _da_vsel_mask, \
|
||||
.da_vsel_shift = _da_vsel_shift, \
|
||||
.modeset_reg = _modeset_reg, \
|
||||
.modeset_mask = BIT(_modeset_shift), \
|
||||
.modeset_shift = _modeset_shift \
|
||||
}
|
||||
|
||||
#define MT6358_LDO(match, vreg, ldo_volt_table, \
|
||||
ldo_index_table, enreg, enbit, vosel, \
|
||||
vosel_mask, vosel_shift) \
|
||||
vosel_mask) \
|
||||
[MT6358_ID_##vreg] = { \
|
||||
.desc = { \
|
||||
.name = #vreg, \
|
||||
|
@ -89,12 +84,11 @@ struct mt6358_regulator_info {
|
|||
.qi = BIT(15), \
|
||||
.index_table = ldo_index_table, \
|
||||
.n_table = ARRAY_SIZE(ldo_index_table), \
|
||||
.vsel_shift = vosel_shift, \
|
||||
}
|
||||
|
||||
#define MT6358_LDO1(match, vreg, min, max, step, \
|
||||
volt_ranges, _da_vsel_reg, _da_vsel_mask, \
|
||||
_da_vsel_shift, vosel, vosel_mask) \
|
||||
vosel, vosel_mask) \
|
||||
[MT6358_ID_##vreg] = { \
|
||||
.desc = { \
|
||||
.name = #vreg, \
|
||||
|
@ -113,7 +107,6 @@ struct mt6358_regulator_info {
|
|||
}, \
|
||||
.da_vsel_reg = _da_vsel_reg, \
|
||||
.da_vsel_mask = _da_vsel_mask, \
|
||||
.da_vsel_shift = _da_vsel_shift, \
|
||||
.status_reg = MT6358_LDO_##vreg##_DBG1, \
|
||||
.qi = BIT(0), \
|
||||
}
|
||||
|
@ -260,9 +253,9 @@ static int mt6358_set_voltage_sel(struct regulator_dev *rdev,
|
|||
pvol = info->index_table;
|
||||
|
||||
idx = pvol[selector];
|
||||
idx <<= ffs(info->desc.vsel_mask) - 1;
|
||||
ret = regmap_update_bits(rdev->regmap, info->desc.vsel_reg,
|
||||
info->desc.vsel_mask,
|
||||
idx << info->vsel_shift);
|
||||
info->desc.vsel_mask, idx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -282,7 +275,8 @@ static int mt6358_get_voltage_sel(struct regulator_dev *rdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
selector = (selector & info->desc.vsel_mask) >> info->vsel_shift;
|
||||
selector = (selector & info->desc.vsel_mask) >>
|
||||
(ffs(info->desc.vsel_mask) - 1);
|
||||
pvol = info->index_table;
|
||||
for (idx = 0; idx < info->desc.n_voltages; idx++) {
|
||||
if (pvol[idx] == selector)
|
||||
|
@ -305,7 +299,7 @@ static int mt6358_get_buck_voltage_sel(struct regulator_dev *rdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = (regval >> info->da_vsel_shift) & info->da_vsel_mask;
|
||||
ret = (regval & info->da_vsel_mask) >> (ffs(info->da_vsel_mask) - 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -342,11 +336,10 @@ static int mt6358_regulator_set_mode(struct regulator_dev *rdev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&rdev->dev, "mt6358 buck set_mode %#x, %#x, %#x, %#x\n",
|
||||
info->modeset_reg, info->modeset_mask,
|
||||
info->modeset_shift, val);
|
||||
dev_dbg(&rdev->dev, "mt6358 buck set_mode %#x, %#x, %#x\n",
|
||||
info->modeset_reg, info->modeset_mask, val);
|
||||
|
||||
val <<= info->modeset_shift;
|
||||
val <<= ffs(info->modeset_mask) - 1;
|
||||
|
||||
return regmap_update_bits(rdev->regmap, info->modeset_reg,
|
||||
info->modeset_mask, val);
|
||||
|
@ -364,7 +357,7 @@ static unsigned int mt6358_regulator_get_mode(struct regulator_dev *rdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
switch ((regval & info->modeset_mask) >> info->modeset_shift) {
|
||||
switch ((regval & info->modeset_mask) >> (ffs(info->modeset_mask) - 1)) {
|
||||
case MT6358_BUCK_MODE_AUTO:
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
case MT6358_BUCK_MODE_FORCE_PWM:
|
||||
|
@ -412,30 +405,30 @@ static const struct regulator_ops mt6358_volt_fixed_ops = {
|
|||
static struct mt6358_regulator_info mt6358_regulators[] = {
|
||||
MT6358_BUCK("buck_vdram1", VDRAM1, 500000, 2087500, 12500,
|
||||
buck_volt_range2, 0x7f, MT6358_BUCK_VDRAM1_DBG0, 0x7f,
|
||||
0, MT6358_VDRAM1_ANA_CON0, 8),
|
||||
MT6358_VDRAM1_ANA_CON0, 8),
|
||||
MT6358_BUCK("buck_vcore", VCORE, 500000, 1293750, 6250,
|
||||
buck_volt_range1, 0x7f, MT6358_BUCK_VCORE_DBG0, 0x7f,
|
||||
0, MT6358_VCORE_VGPU_ANA_CON0, 1),
|
||||
MT6358_VCORE_VGPU_ANA_CON0, 1),
|
||||
MT6358_BUCK("buck_vpa", VPA, 500000, 3650000, 50000,
|
||||
buck_volt_range3, 0x3f, MT6358_BUCK_VPA_DBG0, 0x3f, 0,
|
||||
buck_volt_range3, 0x3f, MT6358_BUCK_VPA_DBG0, 0x3f,
|
||||
MT6358_VPA_ANA_CON0, 3),
|
||||
MT6358_BUCK("buck_vproc11", VPROC11, 500000, 1293750, 6250,
|
||||
buck_volt_range1, 0x7f, MT6358_BUCK_VPROC11_DBG0, 0x7f,
|
||||
0, MT6358_VPROC_ANA_CON0, 1),
|
||||
MT6358_VPROC_ANA_CON0, 1),
|
||||
MT6358_BUCK("buck_vproc12", VPROC12, 500000, 1293750, 6250,
|
||||
buck_volt_range1, 0x7f, MT6358_BUCK_VPROC12_DBG0, 0x7f,
|
||||
0, MT6358_VPROC_ANA_CON0, 2),
|
||||
MT6358_VPROC_ANA_CON0, 2),
|
||||
MT6358_BUCK("buck_vgpu", VGPU, 500000, 1293750, 6250,
|
||||
buck_volt_range1, 0x7f, MT6358_BUCK_VGPU_ELR0, 0x7f, 0,
|
||||
buck_volt_range1, 0x7f, MT6358_BUCK_VGPU_ELR0, 0x7f,
|
||||
MT6358_VCORE_VGPU_ANA_CON0, 2),
|
||||
MT6358_BUCK("buck_vs2", VS2, 500000, 2087500, 12500,
|
||||
buck_volt_range2, 0x7f, MT6358_BUCK_VS2_DBG0, 0x7f, 0,
|
||||
buck_volt_range2, 0x7f, MT6358_BUCK_VS2_DBG0, 0x7f,
|
||||
MT6358_VS2_ANA_CON0, 8),
|
||||
MT6358_BUCK("buck_vmodem", VMODEM, 500000, 1293750, 6250,
|
||||
buck_volt_range1, 0x7f, MT6358_BUCK_VMODEM_DBG0, 0x7f,
|
||||
0, MT6358_VMODEM_ANA_CON0, 8),
|
||||
MT6358_VMODEM_ANA_CON0, 8),
|
||||
MT6358_BUCK("buck_vs1", VS1, 1000000, 2587500, 12500,
|
||||
buck_volt_range4, 0x7f, MT6358_BUCK_VS1_DBG0, 0x7f, 0,
|
||||
buck_volt_range4, 0x7f, MT6358_BUCK_VS1_DBG0, 0x7f,
|
||||
MT6358_VS1_ANA_CON0, 8),
|
||||
MT6358_REG_FIXED("ldo_vrf12", VRF12,
|
||||
MT6358_LDO_VRF12_CON0, 0, 1200000),
|
||||
|
@ -457,49 +450,49 @@ static struct mt6358_regulator_info mt6358_regulators[] = {
|
|||
MT6358_REG_FIXED("ldo_vaud28", VAUD28,
|
||||
MT6358_LDO_VAUD28_CON0, 0, 2800000),
|
||||
MT6358_LDO("ldo_vdram2", VDRAM2, vdram2_voltages, vdram2_idx,
|
||||
MT6358_LDO_VDRAM2_CON0, 0, MT6358_LDO_VDRAM2_ELR0, 0xf, 0),
|
||||
MT6358_LDO_VDRAM2_CON0, 0, MT6358_LDO_VDRAM2_ELR0, 0xf),
|
||||
MT6358_LDO("ldo_vsim1", VSIM1, vsim_voltages, vsim_idx,
|
||||
MT6358_LDO_VSIM1_CON0, 0, MT6358_VSIM1_ANA_CON0, 0xf00, 8),
|
||||
MT6358_LDO_VSIM1_CON0, 0, MT6358_VSIM1_ANA_CON0, 0xf00),
|
||||
MT6358_LDO("ldo_vibr", VIBR, vibr_voltages, vibr_idx,
|
||||
MT6358_LDO_VIBR_CON0, 0, MT6358_VIBR_ANA_CON0, 0xf00, 8),
|
||||
MT6358_LDO_VIBR_CON0, 0, MT6358_VIBR_ANA_CON0, 0xf00),
|
||||
MT6358_LDO("ldo_vusb", VUSB, vusb_voltages, vusb_idx,
|
||||
MT6358_LDO_VUSB_CON0_0, 0, MT6358_VUSB_ANA_CON0, 0x700, 8),
|
||||
MT6358_LDO_VUSB_CON0_0, 0, MT6358_VUSB_ANA_CON0, 0x700),
|
||||
MT6358_LDO("ldo_vcamd", VCAMD, vcamd_voltages, vcamd_idx,
|
||||
MT6358_LDO_VCAMD_CON0, 0, MT6358_VCAMD_ANA_CON0, 0xf00, 8),
|
||||
MT6358_LDO_VCAMD_CON0, 0, MT6358_VCAMD_ANA_CON0, 0xf00),
|
||||
MT6358_LDO("ldo_vefuse", VEFUSE, vefuse_voltages, vefuse_idx,
|
||||
MT6358_LDO_VEFUSE_CON0, 0, MT6358_VEFUSE_ANA_CON0, 0xf00, 8),
|
||||
MT6358_LDO_VEFUSE_CON0, 0, MT6358_VEFUSE_ANA_CON0, 0xf00),
|
||||
MT6358_LDO("ldo_vmch", VMCH, vmch_vemc_voltages, vmch_vemc_idx,
|
||||
MT6358_LDO_VMCH_CON0, 0, MT6358_VMCH_ANA_CON0, 0x700, 8),
|
||||
MT6358_LDO_VMCH_CON0, 0, MT6358_VMCH_ANA_CON0, 0x700),
|
||||
MT6358_LDO("ldo_vcama1", VCAMA1, vcama_voltages, vcama_idx,
|
||||
MT6358_LDO_VCAMA1_CON0, 0, MT6358_VCAMA1_ANA_CON0, 0xf00, 8),
|
||||
MT6358_LDO_VCAMA1_CON0, 0, MT6358_VCAMA1_ANA_CON0, 0xf00),
|
||||
MT6358_LDO("ldo_vemc", VEMC, vmch_vemc_voltages, vmch_vemc_idx,
|
||||
MT6358_LDO_VEMC_CON0, 0, MT6358_VEMC_ANA_CON0, 0x700, 8),
|
||||
MT6358_LDO_VEMC_CON0, 0, MT6358_VEMC_ANA_CON0, 0x700),
|
||||
MT6358_LDO("ldo_vcn33_bt", VCN33_BT, vcn33_bt_wifi_voltages,
|
||||
vcn33_bt_wifi_idx, MT6358_LDO_VCN33_CON0_0,
|
||||
0, MT6358_VCN33_ANA_CON0, 0x300, 8),
|
||||
0, MT6358_VCN33_ANA_CON0, 0x300),
|
||||
MT6358_LDO("ldo_vcn33_wifi", VCN33_WIFI, vcn33_bt_wifi_voltages,
|
||||
vcn33_bt_wifi_idx, MT6358_LDO_VCN33_CON0_1,
|
||||
0, MT6358_VCN33_ANA_CON0, 0x300, 8),
|
||||
0, MT6358_VCN33_ANA_CON0, 0x300),
|
||||
MT6358_LDO("ldo_vcama2", VCAMA2, vcama_voltages, vcama_idx,
|
||||
MT6358_LDO_VCAMA2_CON0, 0, MT6358_VCAMA2_ANA_CON0, 0xf00, 8),
|
||||
MT6358_LDO_VCAMA2_CON0, 0, MT6358_VCAMA2_ANA_CON0, 0xf00),
|
||||
MT6358_LDO("ldo_vmc", VMC, vmc_voltages, vmc_idx,
|
||||
MT6358_LDO_VMC_CON0, 0, MT6358_VMC_ANA_CON0, 0xf00, 8),
|
||||
MT6358_LDO_VMC_CON0, 0, MT6358_VMC_ANA_CON0, 0xf00),
|
||||
MT6358_LDO("ldo_vldo28", VLDO28, vldo28_voltages, vldo28_idx,
|
||||
MT6358_LDO_VLDO28_CON0_0, 0,
|
||||
MT6358_VLDO28_ANA_CON0, 0x300, 8),
|
||||
MT6358_VLDO28_ANA_CON0, 0x300),
|
||||
MT6358_LDO("ldo_vsim2", VSIM2, vsim_voltages, vsim_idx,
|
||||
MT6358_LDO_VSIM2_CON0, 0, MT6358_VSIM2_ANA_CON0, 0xf00, 8),
|
||||
MT6358_LDO_VSIM2_CON0, 0, MT6358_VSIM2_ANA_CON0, 0xf00),
|
||||
MT6358_LDO1("ldo_vsram_proc11", VSRAM_PROC11, 500000, 1293750, 6250,
|
||||
buck_volt_range1, MT6358_LDO_VSRAM_PROC11_DBG0, 0x7f, 8,
|
||||
buck_volt_range1, MT6358_LDO_VSRAM_PROC11_DBG0, 0x7f00,
|
||||
MT6358_LDO_VSRAM_CON0, 0x7f),
|
||||
MT6358_LDO1("ldo_vsram_others", VSRAM_OTHERS, 500000, 1293750, 6250,
|
||||
buck_volt_range1, MT6358_LDO_VSRAM_OTHERS_DBG0, 0x7f, 8,
|
||||
buck_volt_range1, MT6358_LDO_VSRAM_OTHERS_DBG0, 0x7f00,
|
||||
MT6358_LDO_VSRAM_CON2, 0x7f),
|
||||
MT6358_LDO1("ldo_vsram_gpu", VSRAM_GPU, 500000, 1293750, 6250,
|
||||
buck_volt_range1, MT6358_LDO_VSRAM_GPU_DBG0, 0x7f, 8,
|
||||
buck_volt_range1, MT6358_LDO_VSRAM_GPU_DBG0, 0x7f00,
|
||||
MT6358_LDO_VSRAM_CON3, 0x7f),
|
||||
MT6358_LDO1("ldo_vsram_proc12", VSRAM_PROC12, 500000, 1293750, 6250,
|
||||
buck_volt_range1, MT6358_LDO_VSRAM_PROC12_DBG0, 0x7f, 8,
|
||||
buck_volt_range1, MT6358_LDO_VSRAM_PROC12_DBG0, 0x7f00,
|
||||
MT6358_LDO_VSRAM_CON1, 0x7f),
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
* @qi: Mask for query enable signal status of regulators.
|
||||
* @modeset_reg: for operating AUTO/PWM mode register.
|
||||
* @modeset_mask: MASK for operating modeset register.
|
||||
* @modeset_shift: SHIFT for operating modeset register.
|
||||
*/
|
||||
struct mt6359_regulator_info {
|
||||
struct regulator_desc desc;
|
||||
|
@ -35,10 +34,8 @@ struct mt6359_regulator_info {
|
|||
u32 qi;
|
||||
u32 modeset_reg;
|
||||
u32 modeset_mask;
|
||||
u32 modeset_shift;
|
||||
u32 lp_mode_reg;
|
||||
u32 lp_mode_mask;
|
||||
u32 lp_mode_shift;
|
||||
};
|
||||
|
||||
#define MT6359_BUCK(match, _name, min, max, step, \
|
||||
|
@ -68,10 +65,8 @@ struct mt6359_regulator_info {
|
|||
.qi = BIT(0), \
|
||||
.lp_mode_reg = _lp_mode_reg, \
|
||||
.lp_mode_mask = BIT(_lp_mode_shift), \
|
||||
.lp_mode_shift = _lp_mode_shift, \
|
||||
.modeset_reg = _modeset_reg, \
|
||||
.modeset_mask = BIT(_modeset_shift), \
|
||||
.modeset_shift = _modeset_shift \
|
||||
}
|
||||
|
||||
#define MT6359_LDO_LINEAR(match, _name, min, max, step, \
|
||||
|
@ -282,8 +277,10 @@ static unsigned int mt6359_regulator_get_mode(struct regulator_dev *rdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if ((regval & info->modeset_mask) >> info->modeset_shift ==
|
||||
MT6359_BUCK_MODE_FORCE_PWM)
|
||||
regval &= info->modeset_mask;
|
||||
regval >>= ffs(info->modeset_mask) - 1;
|
||||
|
||||
if (regval == MT6359_BUCK_MODE_FORCE_PWM)
|
||||
return REGULATOR_MODE_FAST;
|
||||
|
||||
ret = regmap_read(rdev->regmap, info->lp_mode_reg, ®val);
|
||||
|
@ -310,7 +307,7 @@ static int mt6359_regulator_set_mode(struct regulator_dev *rdev,
|
|||
switch (mode) {
|
||||
case REGULATOR_MODE_FAST:
|
||||
val = MT6359_BUCK_MODE_FORCE_PWM;
|
||||
val <<= info->modeset_shift;
|
||||
val <<= ffs(info->modeset_mask) - 1;
|
||||
ret = regmap_update_bits(rdev->regmap,
|
||||
info->modeset_reg,
|
||||
info->modeset_mask,
|
||||
|
@ -319,14 +316,14 @@ static int mt6359_regulator_set_mode(struct regulator_dev *rdev,
|
|||
case REGULATOR_MODE_NORMAL:
|
||||
if (curr_mode == REGULATOR_MODE_FAST) {
|
||||
val = MT6359_BUCK_MODE_AUTO;
|
||||
val <<= info->modeset_shift;
|
||||
val <<= ffs(info->modeset_mask) - 1;
|
||||
ret = regmap_update_bits(rdev->regmap,
|
||||
info->modeset_reg,
|
||||
info->modeset_mask,
|
||||
val);
|
||||
} else if (curr_mode == REGULATOR_MODE_IDLE) {
|
||||
val = MT6359_BUCK_MODE_NORMAL;
|
||||
val <<= info->lp_mode_shift;
|
||||
val <<= ffs(info->lp_mode_mask) - 1;
|
||||
ret = regmap_update_bits(rdev->regmap,
|
||||
info->lp_mode_reg,
|
||||
info->lp_mode_mask,
|
||||
|
@ -336,7 +333,7 @@ static int mt6359_regulator_set_mode(struct regulator_dev *rdev,
|
|||
break;
|
||||
case REGULATOR_MODE_IDLE:
|
||||
val = MT6359_BUCK_MODE_LP >> 1;
|
||||
val <<= info->lp_mode_shift;
|
||||
val <<= ffs(info->lp_mode_mask) - 1;
|
||||
ret = regmap_update_bits(rdev->regmap,
|
||||
info->lp_mode_reg,
|
||||
info->lp_mode_mask,
|
||||
|
|
|
@ -32,7 +32,6 @@ struct mt6397_regulator_info {
|
|||
u32 vselctrl_mask;
|
||||
u32 modeset_reg;
|
||||
u32 modeset_mask;
|
||||
u32 modeset_shift;
|
||||
};
|
||||
|
||||
#define MT6397_BUCK(match, vreg, min, max, step, volt_ranges, enreg, \
|
||||
|
@ -61,7 +60,6 @@ struct mt6397_regulator_info {
|
|||
.vselctrl_mask = BIT(1), \
|
||||
.modeset_reg = _modeset_reg, \
|
||||
.modeset_mask = BIT(_modeset_shift), \
|
||||
.modeset_shift = _modeset_shift \
|
||||
}
|
||||
|
||||
#define MT6397_LDO(match, vreg, ldo_volt_table, enreg, enbit, vosel, \
|
||||
|
@ -175,11 +173,11 @@ static int mt6397_regulator_set_mode(struct regulator_dev *rdev,
|
|||
goto err_mode;
|
||||
}
|
||||
|
||||
dev_dbg(&rdev->dev, "mt6397 buck set_mode %#x, %#x, %#x, %#x\n",
|
||||
info->modeset_reg, info->modeset_mask,
|
||||
info->modeset_shift, val);
|
||||
dev_dbg(&rdev->dev, "mt6397 buck set_mode %#x, %#x, %#x\n",
|
||||
info->modeset_reg, info->modeset_mask, val);
|
||||
|
||||
val <<= ffs(info->modeset_mask) - 1;
|
||||
|
||||
val <<= info->modeset_shift;
|
||||
ret = regmap_update_bits(rdev->regmap, info->modeset_reg,
|
||||
info->modeset_mask, val);
|
||||
err_mode:
|
||||
|
@ -204,7 +202,10 @@ static unsigned int mt6397_regulator_get_mode(struct regulator_dev *rdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
switch ((regval & info->modeset_mask) >> info->modeset_shift) {
|
||||
regval &= info->modeset_mask;
|
||||
regval >>= ffs(info->modeset_mask) - 1;
|
||||
|
||||
switch (regval) {
|
||||
case MT6397_BUCK_MODE_AUTO:
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
case MT6397_BUCK_MODE_FORCE_PWM:
|
||||
|
|
|
@ -13,6 +13,16 @@
|
|||
#include <linux/mfd/rt5033-private.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
|
||||
static const struct linear_range rt5033_buck_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE(1000000, 0, 20, 100000),
|
||||
REGULATOR_LINEAR_RANGE(3000000, 21, 31, 0),
|
||||
};
|
||||
|
||||
static const struct linear_range rt5033_ldo_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE(1200000, 0, 18, 100000),
|
||||
REGULATOR_LINEAR_RANGE(3000000, 19, 31, 0),
|
||||
};
|
||||
|
||||
static const struct regulator_ops rt5033_safe_ldo_ops = {
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = regulator_enable_regmap,
|
||||
|
@ -24,8 +34,7 @@ static const struct regulator_ops rt5033_buck_ops = {
|
|||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.map_voltage = regulator_map_voltage_linear,
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
};
|
||||
|
@ -40,8 +49,8 @@ static const struct regulator_desc rt5033_supported_regulators[] = {
|
|||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.n_voltages = RT5033_REGULATOR_BUCK_VOLTAGE_STEP_NUM,
|
||||
.min_uV = RT5033_REGULATOR_BUCK_VOLTAGE_MIN,
|
||||
.uV_step = RT5033_REGULATOR_BUCK_VOLTAGE_STEP,
|
||||
.linear_ranges = rt5033_buck_ranges,
|
||||
.n_linear_ranges = ARRAY_SIZE(rt5033_buck_ranges),
|
||||
.enable_reg = RT5033_REG_CTRL,
|
||||
.enable_mask = RT5033_CTRL_EN_BUCK_MASK,
|
||||
.vsel_reg = RT5033_REG_BUCK_CTRL,
|
||||
|
@ -56,8 +65,8 @@ static const struct regulator_desc rt5033_supported_regulators[] = {
|
|||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.n_voltages = RT5033_REGULATOR_LDO_VOLTAGE_STEP_NUM,
|
||||
.min_uV = RT5033_REGULATOR_LDO_VOLTAGE_MIN,
|
||||
.uV_step = RT5033_REGULATOR_LDO_VOLTAGE_STEP,
|
||||
.linear_ranges = rt5033_ldo_ranges,
|
||||
.n_linear_ranges = ARRAY_SIZE(rt5033_ldo_ranges),
|
||||
.enable_reg = RT5033_REG_CTRL,
|
||||
.enable_mask = RT5033_CTRL_EN_LDO_MASK,
|
||||
.vsel_reg = RT5033_REG_LDO_CTRL,
|
||||
|
|
|
@ -144,7 +144,7 @@ static int rt6245_init_device_properties(struct device *dev)
|
|||
static int rt6245_reg_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct i2c_client *i2c = context;
|
||||
const u8 func_base[] = { 0x6F, 0x73, 0x78, 0x61, 0x7C, 0 };
|
||||
static const u8 func_base[] = { 0x6F, 0x73, 0x78, 0x61, 0x7C, 0 };
|
||||
unsigned int code, bit_count;
|
||||
|
||||
code = func_base[reg];
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
enum {
|
||||
RTQ2134_IDX_BUCK1 = 0,
|
||||
RTQ2134_IDX_BUCK2,
|
||||
RTQ2134_IDX_BUCK3,
|
||||
RTQ2134_IDX_MAX
|
||||
};
|
||||
|
||||
#define RTQ2134_AUTO_MODE 0
|
||||
#define RTQ2134_FCCM_MODE 1
|
||||
|
||||
#define RTQ2134_BUCK_DVS0_CTRL 0
|
||||
#define RTQ2134_BUCK_VSEL_CTRL 2
|
||||
|
||||
#define RTQ2134_REG_IO_CHIPNAME 0x01
|
||||
#define RTQ2134_REG_FLT_RECORDTEMP 0x13
|
||||
#define RTQ2134_REG_FLT_RECORDBUCK(_id) (0x14 + (_id))
|
||||
#define RTQ2134_REG_FLT_BUCKCTRL(_id) (0x37 + (_id))
|
||||
#define RTQ2134_REG_BUCK1_CFG0 0x42
|
||||
#define RTQ2134_REG_BUCK1_DVS0CFG1 0x48
|
||||
#define RTQ2134_REG_BUCK1_DVS0CFG0 0x49
|
||||
#define RTQ2134_REG_BUCK1_DVS1CFG1 0x4A
|
||||
#define RTQ2134_REG_BUCK1_DVS1CFG0 0x4B
|
||||
#define RTQ2134_REG_BUCK1_DVSCFG 0x52
|
||||
#define RTQ2134_REG_BUCK1_RSPCFG 0x54
|
||||
#define RTQ2134_REG_BUCK2_CFG0 0x5F
|
||||
#define RTQ2134_REG_BUCK2_DVS0CFG1 0x62
|
||||
#define RTQ2134_REG_BUCK2_DVS0CFG0 0x63
|
||||
#define RTQ2134_REG_BUCK2_DVS1CFG1 0x64
|
||||
#define RTQ2134_REG_BUCK2_DVS1CFG0 0x65
|
||||
#define RTQ2134_REG_BUCK2_DVSCFG 0x6C
|
||||
#define RTQ2134_REG_BUCK2_RSPCFG 0x6E
|
||||
#define RTQ2134_REG_BUCK3_CFG0 0x79
|
||||
#define RTQ2134_REG_BUCK3_DVS0CFG1 0x7C
|
||||
#define RTQ2134_REG_BUCK3_DVS0CFG0 0x7D
|
||||
#define RTQ2134_REG_BUCK3_DVS1CFG1 0x7E
|
||||
#define RTQ2134_REG_BUCK3_DVS1CFG0 0x7F
|
||||
#define RTQ2134_REG_BUCK3_DVSCFG 0x86
|
||||
#define RTQ2134_REG_BUCK3_RSPCFG 0x88
|
||||
#define RTQ2134_REG_BUCK3_SLEWCTRL 0x89
|
||||
|
||||
#define RTQ2134_VOUT_MAXNUM 256
|
||||
#define RTQ2134_VOUT_MASK 0xFF
|
||||
#define RTQ2134_VOUTEN_MASK BIT(0)
|
||||
#define RTQ2134_ACTDISCHG_MASK BIT(0)
|
||||
#define RTQ2134_RSPUP_MASK GENMASK(6, 4)
|
||||
#define RTQ2134_FCCM_MASK BIT(5)
|
||||
#define RTQ2134_UVHICCUP_MASK BIT(3)
|
||||
#define RTQ2134_BUCKDVS_CTRL_MASK GENMASK(1, 0)
|
||||
#define RTQ2134_CHIPOT_MASK BIT(2)
|
||||
#define RTQ2134_BUCKOV_MASK BIT(5)
|
||||
#define RTQ2134_BUCKUV_MASK BIT(4)
|
||||
|
||||
struct rtq2134_regulator_desc {
|
||||
struct regulator_desc desc;
|
||||
/* Extension for proprietary register and mask */
|
||||
unsigned int mode_reg;
|
||||
unsigned int mode_mask;
|
||||
unsigned int suspend_enable_reg;
|
||||
unsigned int suspend_enable_mask;
|
||||
unsigned int suspend_vsel_reg;
|
||||
unsigned int suspend_vsel_mask;
|
||||
unsigned int suspend_mode_reg;
|
||||
unsigned int suspend_mode_mask;
|
||||
unsigned int dvs_ctrl_reg;
|
||||
};
|
||||
|
||||
static int rtq2134_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
|
||||
{
|
||||
struct rtq2134_regulator_desc *desc =
|
||||
(struct rtq2134_regulator_desc *)rdev->desc;
|
||||
unsigned int val;
|
||||
|
||||
if (mode == REGULATOR_MODE_NORMAL)
|
||||
val = RTQ2134_AUTO_MODE;
|
||||
else if (mode == REGULATOR_MODE_FAST)
|
||||
val = RTQ2134_FCCM_MODE;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
val <<= ffs(desc->mode_mask) - 1;
|
||||
return regmap_update_bits(rdev->regmap, desc->mode_reg, desc->mode_mask,
|
||||
val);
|
||||
}
|
||||
|
||||
static unsigned int rtq2134_buck_get_mode(struct regulator_dev *rdev)
|
||||
{
|
||||
struct rtq2134_regulator_desc *desc =
|
||||
(struct rtq2134_regulator_desc *)rdev->desc;
|
||||
unsigned int mode;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rdev->regmap, desc->mode_reg, &mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mode & desc->mode_mask)
|
||||
return REGULATOR_MODE_FAST;
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
}
|
||||
|
||||
static int rtq2134_buck_set_suspend_voltage(struct regulator_dev *rdev, int uV)
|
||||
{
|
||||
struct rtq2134_regulator_desc *desc =
|
||||
(struct rtq2134_regulator_desc *)rdev->desc;
|
||||
int sel;
|
||||
|
||||
sel = regulator_map_voltage_linear_range(rdev, uV, uV);
|
||||
if (sel < 0)
|
||||
return sel;
|
||||
|
||||
sel <<= ffs(desc->suspend_vsel_mask) - 1;
|
||||
|
||||
return regmap_update_bits(rdev->regmap, desc->suspend_vsel_reg,
|
||||
desc->suspend_vsel_mask, sel);
|
||||
}
|
||||
|
||||
static int rtq2134_buck_set_suspend_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct rtq2134_regulator_desc *desc =
|
||||
(struct rtq2134_regulator_desc *)rdev->desc;
|
||||
unsigned int val = desc->suspend_enable_mask;
|
||||
|
||||
return regmap_update_bits(rdev->regmap, desc->suspend_enable_reg,
|
||||
desc->suspend_enable_mask, val);
|
||||
}
|
||||
|
||||
static int rtq2134_buck_set_suspend_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct rtq2134_regulator_desc *desc =
|
||||
(struct rtq2134_regulator_desc *)rdev->desc;
|
||||
|
||||
return regmap_update_bits(rdev->regmap, desc->suspend_enable_reg,
|
||||
desc->suspend_enable_mask, 0);
|
||||
}
|
||||
|
||||
static int rtq2134_buck_set_suspend_mode(struct regulator_dev *rdev,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct rtq2134_regulator_desc *desc =
|
||||
(struct rtq2134_regulator_desc *)rdev->desc;
|
||||
unsigned int val;
|
||||
|
||||
if (mode == REGULATOR_MODE_NORMAL)
|
||||
val = RTQ2134_AUTO_MODE;
|
||||
else if (mode == REGULATOR_MODE_FAST)
|
||||
val = RTQ2134_FCCM_MODE;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
val <<= ffs(desc->suspend_mode_mask) - 1;
|
||||
return regmap_update_bits(rdev->regmap, desc->suspend_mode_reg,
|
||||
desc->suspend_mode_mask, val);
|
||||
}
|
||||
|
||||
static int rtq2134_buck_get_error_flags(struct regulator_dev *rdev,
|
||||
unsigned int *flags)
|
||||
{
|
||||
int rid = rdev_get_id(rdev);
|
||||
unsigned int chip_error, buck_error, events = 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rdev->regmap, RTQ2134_REG_FLT_RECORDTEMP,
|
||||
&chip_error);
|
||||
if (ret) {
|
||||
dev_err(&rdev->dev, "Failed to get chip error flag\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(rdev->regmap, RTQ2134_REG_FLT_RECORDBUCK(rid),
|
||||
&buck_error);
|
||||
if (ret) {
|
||||
dev_err(&rdev->dev, "Failed to get buck error flag\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chip_error & RTQ2134_CHIPOT_MASK)
|
||||
events |= REGULATOR_ERROR_OVER_TEMP;
|
||||
|
||||
if (buck_error & RTQ2134_BUCKUV_MASK)
|
||||
events |= REGULATOR_ERROR_UNDER_VOLTAGE;
|
||||
|
||||
if (buck_error & RTQ2134_BUCKOV_MASK)
|
||||
events |= REGULATOR_ERROR_REGULATION_OUT;
|
||||
|
||||
*flags = events;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regulator_ops rtq2134_buck_ops = {
|
||||
.list_voltage = regulator_list_voltage_linear_range,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.set_active_discharge = regulator_set_active_discharge_regmap,
|
||||
.set_ramp_delay = regulator_set_ramp_delay_regmap,
|
||||
.set_mode = rtq2134_buck_set_mode,
|
||||
.get_mode = rtq2134_buck_get_mode,
|
||||
.set_suspend_voltage = rtq2134_buck_set_suspend_voltage,
|
||||
.set_suspend_enable = rtq2134_buck_set_suspend_enable,
|
||||
.set_suspend_disable = rtq2134_buck_set_suspend_disable,
|
||||
.set_suspend_mode = rtq2134_buck_set_suspend_mode,
|
||||
.get_error_flags = rtq2134_buck_get_error_flags,
|
||||
};
|
||||
|
||||
static const struct linear_range rtq2134_buck_vout_ranges[] = {
|
||||
REGULATOR_LINEAR_RANGE(300000, 0, 200, 5000),
|
||||
REGULATOR_LINEAR_RANGE(1310000, 201, 255, 10000)
|
||||
};
|
||||
|
||||
static unsigned int rtq2134_buck_of_map_mode(unsigned int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case RTQ2134_AUTO_MODE:
|
||||
return REGULATOR_MODE_NORMAL;
|
||||
case RTQ2134_FCCM_MODE:
|
||||
return REGULATOR_MODE_FAST;
|
||||
}
|
||||
|
||||
return REGULATOR_MODE_INVALID;
|
||||
}
|
||||
|
||||
static int rtq2134_buck_of_parse_cb(struct device_node *np,
|
||||
const struct regulator_desc *desc,
|
||||
struct regulator_config *cfg)
|
||||
{
|
||||
struct rtq2134_regulator_desc *rdesc =
|
||||
(struct rtq2134_regulator_desc *)desc;
|
||||
int rid = desc->id;
|
||||
bool uv_shutdown, vsel_dvs;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
vsel_dvs = of_property_read_bool(np, "richtek,use-vsel-dvs");
|
||||
if (vsel_dvs)
|
||||
val = RTQ2134_BUCK_VSEL_CTRL;
|
||||
else
|
||||
val = RTQ2134_BUCK_DVS0_CTRL;
|
||||
|
||||
ret = regmap_update_bits(cfg->regmap, rdesc->dvs_ctrl_reg,
|
||||
RTQ2134_BUCKDVS_CTRL_MASK, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uv_shutdown = of_property_read_bool(np, "richtek,uv-shutdown");
|
||||
if (uv_shutdown)
|
||||
val = 0;
|
||||
else
|
||||
val = RTQ2134_UVHICCUP_MASK;
|
||||
|
||||
return regmap_update_bits(cfg->regmap, RTQ2134_REG_FLT_BUCKCTRL(rid),
|
||||
RTQ2134_UVHICCUP_MASK, val);
|
||||
}
|
||||
|
||||
static const unsigned int rtq2134_buck_ramp_delay_table[] = {
|
||||
0, 16000, 0, 8000, 4000, 2000, 1000, 500
|
||||
};
|
||||
|
||||
#define RTQ2134_BUCK_DESC(_id) { \
|
||||
.desc = { \
|
||||
.name = "rtq2134_buck" #_id, \
|
||||
.of_match = of_match_ptr("buck" #_id), \
|
||||
.regulators_node = of_match_ptr("regulators"), \
|
||||
.id = RTQ2134_IDX_BUCK##_id, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.owner = THIS_MODULE, \
|
||||
.ops = &rtq2134_buck_ops, \
|
||||
.n_voltages = RTQ2134_VOUT_MAXNUM, \
|
||||
.linear_ranges = rtq2134_buck_vout_ranges, \
|
||||
.n_linear_ranges = ARRAY_SIZE(rtq2134_buck_vout_ranges), \
|
||||
.vsel_reg = RTQ2134_REG_BUCK##_id##_DVS0CFG1, \
|
||||
.vsel_mask = RTQ2134_VOUT_MASK, \
|
||||
.enable_reg = RTQ2134_REG_BUCK##_id##_DVS0CFG0, \
|
||||
.enable_mask = RTQ2134_VOUTEN_MASK, \
|
||||
.active_discharge_reg = RTQ2134_REG_BUCK##_id##_CFG0, \
|
||||
.active_discharge_mask = RTQ2134_ACTDISCHG_MASK, \
|
||||
.ramp_reg = RTQ2134_REG_BUCK##_id##_RSPCFG, \
|
||||
.ramp_mask = RTQ2134_RSPUP_MASK, \
|
||||
.ramp_delay_table = rtq2134_buck_ramp_delay_table, \
|
||||
.n_ramp_values = ARRAY_SIZE(rtq2134_buck_ramp_delay_table), \
|
||||
.of_map_mode = rtq2134_buck_of_map_mode, \
|
||||
.of_parse_cb = rtq2134_buck_of_parse_cb, \
|
||||
}, \
|
||||
.mode_reg = RTQ2134_REG_BUCK##_id##_DVS0CFG0, \
|
||||
.mode_mask = RTQ2134_FCCM_MASK, \
|
||||
.suspend_mode_reg = RTQ2134_REG_BUCK##_id##_DVS1CFG0, \
|
||||
.suspend_mode_mask = RTQ2134_FCCM_MASK, \
|
||||
.suspend_enable_reg = RTQ2134_REG_BUCK##_id##_DVS1CFG0, \
|
||||
.suspend_enable_mask = RTQ2134_VOUTEN_MASK, \
|
||||
.suspend_vsel_reg = RTQ2134_REG_BUCK##_id##_DVS1CFG1, \
|
||||
.suspend_vsel_mask = RTQ2134_VOUT_MASK, \
|
||||
.dvs_ctrl_reg = RTQ2134_REG_BUCK##_id##_DVSCFG, \
|
||||
}
|
||||
|
||||
static const struct rtq2134_regulator_desc rtq2134_regulator_descs[] = {
|
||||
RTQ2134_BUCK_DESC(1),
|
||||
RTQ2134_BUCK_DESC(2),
|
||||
RTQ2134_BUCK_DESC(3)
|
||||
};
|
||||
|
||||
static bool rtq2134_is_accissible_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg >= RTQ2134_REG_IO_CHIPNAME && reg <= RTQ2134_REG_BUCK3_SLEWCTRL)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config rtq2134_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = RTQ2134_REG_BUCK3_SLEWCTRL,
|
||||
|
||||
.readable_reg = rtq2134_is_accissible_reg,
|
||||
.writeable_reg = rtq2134_is_accissible_reg,
|
||||
};
|
||||
|
||||
static int rtq2134_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
struct regulator_dev *rdev;
|
||||
struct regulator_config regulator_cfg = {};
|
||||
int i;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &rtq2134_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&i2c->dev, "Failed to allocate regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
regulator_cfg.dev = &i2c->dev;
|
||||
regulator_cfg.regmap = regmap;
|
||||
for (i = 0; i < ARRAY_SIZE(rtq2134_regulator_descs); i++) {
|
||||
rdev = devm_regulator_register(&i2c->dev,
|
||||
&rtq2134_regulator_descs[i].desc,
|
||||
®ulator_cfg);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(&i2c->dev, "Failed to init %d regulator\n", i);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id __maybe_unused rtq2134_device_tables[] = {
|
||||
{ .compatible = "richtek,rtq2134", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rtq2134_device_tables);
|
||||
|
||||
static struct i2c_driver rtq2134_driver = {
|
||||
.driver = {
|
||||
.name = "rtq2134",
|
||||
.of_match_table = rtq2134_device_tables,
|
||||
},
|
||||
.probe_new = rtq2134_probe,
|
||||
};
|
||||
module_i2c_driver(rtq2134_driver);
|
||||
|
||||
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
|
||||
MODULE_DESCRIPTION("Richtek RTQ2134 Regulator Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,289 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
enum {
|
||||
RTQ6752_IDX_PAVDD = 0,
|
||||
RTQ6752_IDX_NAVDD = 1,
|
||||
RTQ6752_IDX_MAX
|
||||
};
|
||||
|
||||
#define RTQ6752_REG_PAVDD 0x00
|
||||
#define RTQ6752_REG_NAVDD 0x01
|
||||
#define RTQ6752_REG_PAVDDONDLY 0x07
|
||||
#define RTQ6752_REG_PAVDDSSTIME 0x08
|
||||
#define RTQ6752_REG_NAVDDONDLY 0x0D
|
||||
#define RTQ6752_REG_NAVDDSSTIME 0x0E
|
||||
#define RTQ6752_REG_OPTION1 0x12
|
||||
#define RTQ6752_REG_CHSWITCH 0x16
|
||||
#define RTQ6752_REG_FAULT 0x1D
|
||||
|
||||
#define RTQ6752_VOUT_MASK GENMASK(5, 0)
|
||||
#define RTQ6752_NAVDDEN_MASK BIT(3)
|
||||
#define RTQ6752_PAVDDEN_MASK BIT(0)
|
||||
#define RTQ6752_PAVDDAD_MASK BIT(4)
|
||||
#define RTQ6752_NAVDDAD_MASK BIT(3)
|
||||
#define RTQ6752_PAVDDF_MASK BIT(3)
|
||||
#define RTQ6752_NAVDDF_MASK BIT(0)
|
||||
#define RTQ6752_ENABLE_MASK (BIT(RTQ6752_IDX_MAX) - 1)
|
||||
|
||||
#define RTQ6752_VOUT_MINUV 5000000
|
||||
#define RTQ6752_VOUT_STEPUV 50000
|
||||
#define RTQ6752_VOUT_NUM 47
|
||||
#define RTQ6752_I2CRDY_TIMEUS 1000
|
||||
#define RTQ6752_MINSS_TIMEUS 5000
|
||||
|
||||
struct rtq6752_priv {
|
||||
struct regmap *regmap;
|
||||
struct gpio_desc *enable_gpio;
|
||||
struct mutex lock;
|
||||
unsigned char enable_flag;
|
||||
};
|
||||
|
||||
static int rtq6752_set_vdd_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct rtq6752_priv *priv = rdev_get_drvdata(rdev);
|
||||
int rid = rdev_get_id(rdev), ret;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
if (priv->enable_gpio) {
|
||||
gpiod_set_value(priv->enable_gpio, 1);
|
||||
|
||||
usleep_range(RTQ6752_I2CRDY_TIMEUS,
|
||||
RTQ6752_I2CRDY_TIMEUS + 100);
|
||||
}
|
||||
|
||||
if (!priv->enable_flag) {
|
||||
regcache_cache_only(priv->regmap, false);
|
||||
ret = regcache_sync(priv->regmap);
|
||||
if (ret) {
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
priv->enable_flag |= BIT(rid);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return regulator_enable_regmap(rdev);
|
||||
}
|
||||
|
||||
static int rtq6752_set_vdd_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct rtq6752_priv *priv = rdev_get_drvdata(rdev);
|
||||
int rid = rdev_get_id(rdev), ret;
|
||||
|
||||
ret = regulator_disable_regmap(rdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
priv->enable_flag &= ~BIT(rid);
|
||||
|
||||
if (!priv->enable_flag) {
|
||||
regcache_cache_only(priv->regmap, true);
|
||||
regcache_mark_dirty(priv->regmap);
|
||||
}
|
||||
|
||||
if (priv->enable_gpio)
|
||||
gpiod_set_value(priv->enable_gpio, 0);
|
||||
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtq6752_get_error_flags(struct regulator_dev *rdev,
|
||||
unsigned int *flags)
|
||||
{
|
||||
unsigned int val, events = 0;
|
||||
const unsigned int fault_mask[] = {
|
||||
RTQ6752_PAVDDF_MASK, RTQ6752_NAVDDF_MASK };
|
||||
int rid = rdev_get_id(rdev), ret;
|
||||
|
||||
ret = regmap_read(rdev->regmap, RTQ6752_REG_FAULT, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & fault_mask[rid])
|
||||
events = REGULATOR_ERROR_REGULATION_OUT;
|
||||
|
||||
*flags = events;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regulator_ops rtq6752_regulator_ops = {
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.enable = rtq6752_set_vdd_enable,
|
||||
.disable = rtq6752_set_vdd_disable,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
.set_active_discharge = regulator_set_active_discharge_regmap,
|
||||
.get_error_flags = rtq6752_get_error_flags,
|
||||
};
|
||||
|
||||
static const struct regulator_desc rtq6752_regulator_descs[] = {
|
||||
{
|
||||
.name = "rtq6752-pavdd",
|
||||
.of_match = of_match_ptr("pavdd"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = RTQ6752_IDX_PAVDD,
|
||||
.n_voltages = RTQ6752_VOUT_NUM,
|
||||
.ops = &rtq6752_regulator_ops,
|
||||
.owner = THIS_MODULE,
|
||||
.min_uV = RTQ6752_VOUT_MINUV,
|
||||
.uV_step = RTQ6752_VOUT_STEPUV,
|
||||
.enable_time = RTQ6752_MINSS_TIMEUS,
|
||||
.vsel_reg = RTQ6752_REG_PAVDD,
|
||||
.vsel_mask = RTQ6752_VOUT_MASK,
|
||||
.enable_reg = RTQ6752_REG_CHSWITCH,
|
||||
.enable_mask = RTQ6752_PAVDDEN_MASK,
|
||||
.active_discharge_reg = RTQ6752_REG_OPTION1,
|
||||
.active_discharge_mask = RTQ6752_PAVDDAD_MASK,
|
||||
.active_discharge_off = RTQ6752_PAVDDAD_MASK,
|
||||
},
|
||||
{
|
||||
.name = "rtq6752-navdd",
|
||||
.of_match = of_match_ptr("navdd"),
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.id = RTQ6752_IDX_NAVDD,
|
||||
.n_voltages = RTQ6752_VOUT_NUM,
|
||||
.ops = &rtq6752_regulator_ops,
|
||||
.owner = THIS_MODULE,
|
||||
.min_uV = RTQ6752_VOUT_MINUV,
|
||||
.uV_step = RTQ6752_VOUT_STEPUV,
|
||||
.enable_time = RTQ6752_MINSS_TIMEUS,
|
||||
.vsel_reg = RTQ6752_REG_NAVDD,
|
||||
.vsel_mask = RTQ6752_VOUT_MASK,
|
||||
.enable_reg = RTQ6752_REG_CHSWITCH,
|
||||
.enable_mask = RTQ6752_NAVDDEN_MASK,
|
||||
.active_discharge_reg = RTQ6752_REG_OPTION1,
|
||||
.active_discharge_mask = RTQ6752_NAVDDAD_MASK,
|
||||
.active_discharge_off = RTQ6752_NAVDDAD_MASK,
|
||||
}
|
||||
};
|
||||
|
||||
static int rtq6752_init_device_properties(struct rtq6752_priv *priv)
|
||||
{
|
||||
u8 raw_vals[] = { 0, 0 };
|
||||
int ret;
|
||||
|
||||
/* Configure PAVDD on and softstart delay time to the minimum */
|
||||
ret = regmap_raw_write(priv->regmap, RTQ6752_REG_PAVDDONDLY, raw_vals,
|
||||
ARRAY_SIZE(raw_vals));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Configure NAVDD on and softstart delay time to the minimum */
|
||||
return regmap_raw_write(priv->regmap, RTQ6752_REG_NAVDDONDLY, raw_vals,
|
||||
ARRAY_SIZE(raw_vals));
|
||||
}
|
||||
|
||||
static bool rtq6752_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg == RTQ6752_REG_FAULT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct reg_default rtq6752_reg_defaults[] = {
|
||||
{ RTQ6752_REG_PAVDD, 0x14 },
|
||||
{ RTQ6752_REG_NAVDD, 0x14 },
|
||||
{ RTQ6752_REG_PAVDDONDLY, 0x01 },
|
||||
{ RTQ6752_REG_PAVDDSSTIME, 0x01 },
|
||||
{ RTQ6752_REG_NAVDDONDLY, 0x01 },
|
||||
{ RTQ6752_REG_NAVDDSSTIME, 0x01 },
|
||||
{ RTQ6752_REG_OPTION1, 0x07 },
|
||||
{ RTQ6752_REG_CHSWITCH, 0x29 },
|
||||
};
|
||||
|
||||
static const struct regmap_config rtq6752_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.max_register = RTQ6752_REG_FAULT,
|
||||
.reg_defaults = rtq6752_reg_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(rtq6752_reg_defaults),
|
||||
.volatile_reg = rtq6752_is_volatile_reg,
|
||||
};
|
||||
|
||||
static int rtq6752_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct rtq6752_priv *priv;
|
||||
struct regulator_config reg_cfg = {};
|
||||
struct regulator_dev *rdev;
|
||||
int i, ret;
|
||||
|
||||
priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
priv->enable_gpio = devm_gpiod_get_optional(&i2c->dev, "enable",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(priv->enable_gpio)) {
|
||||
dev_err(&i2c->dev, "Failed to get 'enable' gpio\n");
|
||||
return PTR_ERR(priv->enable_gpio);
|
||||
}
|
||||
|
||||
usleep_range(RTQ6752_I2CRDY_TIMEUS, RTQ6752_I2CRDY_TIMEUS + 100);
|
||||
/* Default EN pin to high, PAVDD and NAVDD will be on */
|
||||
priv->enable_flag = RTQ6752_ENABLE_MASK;
|
||||
|
||||
priv->regmap = devm_regmap_init_i2c(i2c, &rtq6752_regmap_config);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
dev_err(&i2c->dev, "Failed to init regmap\n");
|
||||
return PTR_ERR(priv->regmap);
|
||||
}
|
||||
|
||||
ret = rtq6752_init_device_properties(priv);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "Failed to init device properties\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_cfg.dev = &i2c->dev;
|
||||
reg_cfg.regmap = priv->regmap;
|
||||
reg_cfg.driver_data = priv;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rtq6752_regulator_descs); i++) {
|
||||
rdev = devm_regulator_register(&i2c->dev,
|
||||
rtq6752_regulator_descs + i,
|
||||
®_cfg);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(&i2c->dev, "Failed to init %d regulator\n", i);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id __maybe_unused rtq6752_device_table[] = {
|
||||
{ .compatible = "richtek,rtq6752", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rtq6752_device_table);
|
||||
|
||||
static struct i2c_driver rtq6752_driver = {
|
||||
.driver = {
|
||||
.name = "rtq6752",
|
||||
.of_match_table = rtq6752_device_table,
|
||||
},
|
||||
.probe_new = rtq6752_probe,
|
||||
};
|
||||
module_i2c_driver(rtq6752_driver);
|
||||
|
||||
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
|
||||
MODULE_DESCRIPTION("Richtek RTQ6752 Regulator Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -13,7 +13,10 @@
|
|||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mfd/sy7636a.h>
|
||||
|
||||
#define SY7636A_POLL_ENABLED_TIME 500
|
||||
struct sy7636a_data {
|
||||
struct regmap *regmap;
|
||||
struct gpio_desc *pgood_gpio;
|
||||
};
|
||||
|
||||
static int sy7636a_get_vcom_voltage_op(struct regulator_dev *rdev)
|
||||
{
|
||||
|
@ -35,10 +38,10 @@ static int sy7636a_get_vcom_voltage_op(struct regulator_dev *rdev)
|
|||
|
||||
static int sy7636a_get_status(struct regulator_dev *rdev)
|
||||
{
|
||||
struct sy7636a *sy7636a = rdev_get_drvdata(rdev);
|
||||
struct sy7636a_data *data = dev_get_drvdata(rdev->dev.parent);
|
||||
int ret = 0;
|
||||
|
||||
ret = gpiod_get_value_cansleep(sy7636a->pgood_gpio);
|
||||
ret = gpiod_get_value_cansleep(data->pgood_gpio);
|
||||
if (ret < 0)
|
||||
dev_err(&rdev->dev, "Failed to read pgood gpio: %d\n", ret);
|
||||
|
||||
|
@ -61,46 +64,50 @@ static const struct regulator_desc desc = {
|
|||
.owner = THIS_MODULE,
|
||||
.enable_reg = SY7636A_REG_OPERATION_MODE_CRL,
|
||||
.enable_mask = SY7636A_OPERATION_MODE_CRL_ONOFF,
|
||||
.poll_enabled_time = SY7636A_POLL_ENABLED_TIME,
|
||||
.regulators_node = of_match_ptr("regulators"),
|
||||
.of_match = of_match_ptr("vcom"),
|
||||
};
|
||||
|
||||
static int sy7636a_regulator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sy7636a *sy7636a = dev_get_drvdata(pdev->dev.parent);
|
||||
struct regmap *regmap = dev_get_drvdata(pdev->dev.parent);
|
||||
struct regulator_config config = { };
|
||||
struct regulator_dev *rdev;
|
||||
struct gpio_desc *gdp;
|
||||
struct sy7636a_data *data;
|
||||
int ret;
|
||||
|
||||
if (!sy7636a)
|
||||
if (!regmap)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
platform_set_drvdata(pdev, sy7636a);
|
||||
|
||||
gdp = devm_gpiod_get(sy7636a->dev, "epd-pwr-good", GPIOD_IN);
|
||||
gdp = devm_gpiod_get(pdev->dev.parent, "epd-pwr-good", GPIOD_IN);
|
||||
if (IS_ERR(gdp)) {
|
||||
dev_err(sy7636a->dev, "Power good GPIO fault %ld\n", PTR_ERR(gdp));
|
||||
dev_err(pdev->dev.parent, "Power good GPIO fault %ld\n", PTR_ERR(gdp));
|
||||
return PTR_ERR(gdp);
|
||||
}
|
||||
|
||||
sy7636a->pgood_gpio = gdp;
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct sy7636a_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = regmap_write(sy7636a->regmap, SY7636A_REG_POWER_ON_DELAY_TIME, 0x0);
|
||||
data->regmap = regmap;
|
||||
data->pgood_gpio = gdp;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
ret = regmap_write(regmap, SY7636A_REG_POWER_ON_DELAY_TIME, 0x0);
|
||||
if (ret) {
|
||||
dev_err(sy7636a->dev, "Failed to initialize regulator: %d\n", ret);
|
||||
dev_err(pdev->dev.parent, "Failed to initialize regulator: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.dev->of_node = sy7636a->dev->of_node;
|
||||
config.driver_data = sy7636a;
|
||||
config.regmap = sy7636a->regmap;
|
||||
config.dev->of_node = pdev->dev.parent->of_node;
|
||||
config.regmap = regmap;
|
||||
|
||||
rdev = devm_regulator_register(&pdev->dev, &desc, &config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(sy7636a->dev, "Failed to register %s regulator\n",
|
||||
dev_err(pdev->dev.parent, "Failed to register %s regulator\n",
|
||||
pdev->name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ struct sy8824_config {
|
|||
unsigned int vsel_min;
|
||||
unsigned int vsel_step;
|
||||
unsigned int vsel_count;
|
||||
const struct regmap_config *config;
|
||||
};
|
||||
|
||||
struct sy8824_device_info {
|
||||
|
@ -110,6 +111,15 @@ static int sy8824_regulator_register(struct sy8824_device_info *di,
|
|||
static const struct regmap_config sy8824_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.num_reg_defaults_raw = 1,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static const struct regmap_config sy20276_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.num_reg_defaults_raw = 2,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static int sy8824_i2c_probe(struct i2c_client *client)
|
||||
|
@ -134,7 +144,7 @@ static int sy8824_i2c_probe(struct i2c_client *client)
|
|||
di->dev = dev;
|
||||
di->cfg = of_device_get_match_data(dev);
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &sy8824_regmap_config);
|
||||
regmap = devm_regmap_init_i2c(client, di->cfg->config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to allocate regmap!\n");
|
||||
return PTR_ERR(regmap);
|
||||
|
@ -160,6 +170,7 @@ static const struct sy8824_config sy8824c_cfg = {
|
|||
.vsel_min = 762500,
|
||||
.vsel_step = 12500,
|
||||
.vsel_count = 64,
|
||||
.config = &sy8824_regmap_config,
|
||||
};
|
||||
|
||||
static const struct sy8824_config sy8824e_cfg = {
|
||||
|
@ -169,6 +180,7 @@ static const struct sy8824_config sy8824e_cfg = {
|
|||
.vsel_min = 700000,
|
||||
.vsel_step = 12500,
|
||||
.vsel_count = 64,
|
||||
.config = &sy8824_regmap_config,
|
||||
};
|
||||
|
||||
static const struct sy8824_config sy20276_cfg = {
|
||||
|
@ -178,6 +190,7 @@ static const struct sy8824_config sy20276_cfg = {
|
|||
.vsel_min = 600000,
|
||||
.vsel_step = 10000,
|
||||
.vsel_count = 128,
|
||||
.config = &sy20276_regmap_config,
|
||||
};
|
||||
|
||||
static const struct sy8824_config sy20278_cfg = {
|
||||
|
@ -187,6 +200,7 @@ static const struct sy8824_config sy20278_cfg = {
|
|||
.vsel_min = 762500,
|
||||
.vsel_step = 12500,
|
||||
.vsel_count = 64,
|
||||
.config = &sy20276_regmap_config,
|
||||
};
|
||||
|
||||
static const struct of_device_id sy8824_dt_ids[] = {
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
#define SY8827N_MODE (1 << 6)
|
||||
#define SY8827N_VSEL1 1
|
||||
#define SY8827N_CTRL 2
|
||||
#define SY8827N_ID1 3
|
||||
#define SY8827N_ID2 4
|
||||
#define SY8827N_PGOOD 5
|
||||
#define SY8827N_MAX (SY8827N_PGOOD + 1)
|
||||
|
||||
#define SY8827N_NVOLTAGES 64
|
||||
#define SY8827N_VSELMIN 600000
|
||||
|
@ -102,9 +106,19 @@ static int sy8827n_regulator_register(struct sy8827n_device_info *di,
|
|||
return PTR_ERR_OR_ZERO(rdev);
|
||||
}
|
||||
|
||||
static bool sy8827n_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (reg == SY8827N_PGOOD)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config sy8827n_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_reg = sy8827n_volatile_reg,
|
||||
.num_reg_defaults_raw = SY8827N_MAX,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static int sy8827n_i2c_probe(struct i2c_client *client)
|
||||
|
|
|
@ -1211,12 +1211,10 @@ static int tps65910_probe(struct platform_device *pdev)
|
|||
|
||||
rdev = devm_regulator_register(&pdev->dev, &pmic->desc[i],
|
||||
&config);
|
||||
if (IS_ERR(rdev)) {
|
||||
dev_err(tps65910->dev,
|
||||
"failed to register %s regulator\n",
|
||||
pdev->name);
|
||||
return PTR_ERR(rdev);
|
||||
}
|
||||
if (IS_ERR(rdev))
|
||||
return dev_err_probe(tps65910->dev, PTR_ERR(rdev),
|
||||
"failed to register %s regulator\n",
|
||||
pdev->name);
|
||||
|
||||
/* Save regulator for cleanup */
|
||||
pmic->rdev[i] = rdev;
|
||||
|
|
|
@ -37,7 +37,6 @@ struct vctrl_voltage_table {
|
|||
struct vctrl_data {
|
||||
struct regulator_dev *rdev;
|
||||
struct regulator_desc desc;
|
||||
struct regulator *ctrl_reg;
|
||||
bool enabled;
|
||||
unsigned int min_slew_down_rate;
|
||||
unsigned int ovp_threshold;
|
||||
|
@ -82,7 +81,12 @@ static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV)
|
|||
static int vctrl_get_voltage(struct regulator_dev *rdev)
|
||||
{
|
||||
struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
|
||||
int ctrl_uV = regulator_get_voltage_rdev(vctrl->ctrl_reg->rdev);
|
||||
int ctrl_uV;
|
||||
|
||||
if (!rdev->supply)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev);
|
||||
|
||||
return vctrl_calc_output_voltage(vctrl, ctrl_uV);
|
||||
}
|
||||
|
@ -92,14 +96,19 @@ static int vctrl_set_voltage(struct regulator_dev *rdev,
|
|||
unsigned int *selector)
|
||||
{
|
||||
struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
|
||||
struct regulator *ctrl_reg = vctrl->ctrl_reg;
|
||||
int orig_ctrl_uV = regulator_get_voltage_rdev(ctrl_reg->rdev);
|
||||
int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV);
|
||||
int orig_ctrl_uV;
|
||||
int uV;
|
||||
int ret;
|
||||
|
||||
if (!rdev->supply)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
orig_ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev);
|
||||
uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV);
|
||||
|
||||
if (req_min_uV >= uV || !vctrl->ovp_threshold)
|
||||
/* voltage rising or no OVP */
|
||||
return regulator_set_voltage_rdev(ctrl_reg->rdev,
|
||||
return regulator_set_voltage_rdev(rdev->supply->rdev,
|
||||
vctrl_calc_ctrl_voltage(vctrl, req_min_uV),
|
||||
vctrl_calc_ctrl_voltage(vctrl, req_max_uV),
|
||||
PM_SUSPEND_ON);
|
||||
|
@ -117,7 +126,7 @@ static int vctrl_set_voltage(struct regulator_dev *rdev,
|
|||
next_uV = max_t(int, req_min_uV, uV - max_drop_uV);
|
||||
next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV);
|
||||
|
||||
ret = regulator_set_voltage_rdev(ctrl_reg->rdev,
|
||||
ret = regulator_set_voltage_rdev(rdev->supply->rdev,
|
||||
next_ctrl_uV,
|
||||
next_ctrl_uV,
|
||||
PM_SUSPEND_ON);
|
||||
|
@ -134,7 +143,7 @@ static int vctrl_set_voltage(struct regulator_dev *rdev,
|
|||
|
||||
err:
|
||||
/* Try to go back to original voltage */
|
||||
regulator_set_voltage_rdev(ctrl_reg->rdev, orig_ctrl_uV, orig_ctrl_uV,
|
||||
regulator_set_voltage_rdev(rdev->supply->rdev, orig_ctrl_uV, orig_ctrl_uV,
|
||||
PM_SUSPEND_ON);
|
||||
|
||||
return ret;
|
||||
|
@ -151,16 +160,18 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
|
|||
unsigned int selector)
|
||||
{
|
||||
struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
|
||||
struct regulator *ctrl_reg = vctrl->ctrl_reg;
|
||||
unsigned int orig_sel = vctrl->sel;
|
||||
int ret;
|
||||
|
||||
if (!rdev->supply)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (selector >= rdev->desc->n_voltages)
|
||||
return -EINVAL;
|
||||
|
||||
if (selector >= vctrl->sel || !vctrl->ovp_threshold) {
|
||||
/* voltage rising or no OVP */
|
||||
ret = regulator_set_voltage_rdev(ctrl_reg->rdev,
|
||||
ret = regulator_set_voltage_rdev(rdev->supply->rdev,
|
||||
vctrl->vtable[selector].ctrl,
|
||||
vctrl->vtable[selector].ctrl,
|
||||
PM_SUSPEND_ON);
|
||||
|
@ -179,7 +190,7 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
|
|||
else
|
||||
next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel;
|
||||
|
||||
ret = regulator_set_voltage_rdev(ctrl_reg->rdev,
|
||||
ret = regulator_set_voltage_rdev(rdev->supply->rdev,
|
||||
vctrl->vtable[next_sel].ctrl,
|
||||
vctrl->vtable[next_sel].ctrl,
|
||||
PM_SUSPEND_ON);
|
||||
|
@ -202,7 +213,7 @@ static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
|
|||
err:
|
||||
if (vctrl->sel != orig_sel) {
|
||||
/* Try to go back to original voltage */
|
||||
if (!regulator_set_voltage_rdev(ctrl_reg->rdev,
|
||||
if (!regulator_set_voltage_rdev(rdev->supply->rdev,
|
||||
vctrl->vtable[orig_sel].ctrl,
|
||||
vctrl->vtable[orig_sel].ctrl,
|
||||
PM_SUSPEND_ON))
|
||||
|
@ -234,10 +245,6 @@ static int vctrl_parse_dt(struct platform_device *pdev,
|
|||
u32 pval;
|
||||
u32 vrange_ctrl[2];
|
||||
|
||||
vctrl->ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl");
|
||||
if (IS_ERR(vctrl->ctrl_reg))
|
||||
return PTR_ERR(vctrl->ctrl_reg);
|
||||
|
||||
ret = of_property_read_u32(np, "ovp-threshold-percent", &pval);
|
||||
if (!ret) {
|
||||
vctrl->ovp_threshold = pval;
|
||||
|
@ -315,11 +322,11 @@ static int vctrl_cmp_ctrl_uV(const void *a, const void *b)
|
|||
return at->ctrl - bt->ctrl;
|
||||
}
|
||||
|
||||
static int vctrl_init_vtable(struct platform_device *pdev)
|
||||
static int vctrl_init_vtable(struct platform_device *pdev,
|
||||
struct regulator *ctrl_reg)
|
||||
{
|
||||
struct vctrl_data *vctrl = platform_get_drvdata(pdev);
|
||||
struct regulator_desc *rdesc = &vctrl->desc;
|
||||
struct regulator *ctrl_reg = vctrl->ctrl_reg;
|
||||
struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl;
|
||||
int n_voltages;
|
||||
int ctrl_uV;
|
||||
|
@ -395,23 +402,19 @@ static int vctrl_init_vtable(struct platform_device *pdev)
|
|||
static int vctrl_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
|
||||
int ret = regulator_enable(vctrl->ctrl_reg);
|
||||
|
||||
if (!ret)
|
||||
vctrl->enabled = true;
|
||||
vctrl->enabled = true;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vctrl_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
|
||||
int ret = regulator_disable(vctrl->ctrl_reg);
|
||||
|
||||
if (!ret)
|
||||
vctrl->enabled = false;
|
||||
vctrl->enabled = false;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vctrl_is_enabled(struct regulator_dev *rdev)
|
||||
|
@ -447,6 +450,7 @@ static int vctrl_probe(struct platform_device *pdev)
|
|||
struct regulator_desc *rdesc;
|
||||
struct regulator_config cfg = { };
|
||||
struct vctrl_voltage_range *vrange_ctrl;
|
||||
struct regulator *ctrl_reg;
|
||||
int ctrl_uV;
|
||||
int ret;
|
||||
|
||||
|
@ -461,15 +465,20 @@ static int vctrl_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl");
|
||||
if (IS_ERR(ctrl_reg))
|
||||
return PTR_ERR(ctrl_reg);
|
||||
|
||||
vrange_ctrl = &vctrl->vrange.ctrl;
|
||||
|
||||
rdesc = &vctrl->desc;
|
||||
rdesc->name = "vctrl";
|
||||
rdesc->type = REGULATOR_VOLTAGE;
|
||||
rdesc->owner = THIS_MODULE;
|
||||
rdesc->supply_name = "ctrl";
|
||||
|
||||
if ((regulator_get_linear_step(vctrl->ctrl_reg) == 1) ||
|
||||
(regulator_count_voltages(vctrl->ctrl_reg) == -EINVAL)) {
|
||||
if ((regulator_get_linear_step(ctrl_reg) == 1) ||
|
||||
(regulator_count_voltages(ctrl_reg) == -EINVAL)) {
|
||||
rdesc->continuous_voltage_range = true;
|
||||
rdesc->ops = &vctrl_ops_cont;
|
||||
} else {
|
||||
|
@ -486,11 +495,12 @@ static int vctrl_probe(struct platform_device *pdev)
|
|||
cfg.init_data = init_data;
|
||||
|
||||
if (!rdesc->continuous_voltage_range) {
|
||||
ret = vctrl_init_vtable(pdev);
|
||||
ret = vctrl_init_vtable(pdev, ctrl_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl_uV = regulator_get_voltage_rdev(vctrl->ctrl_reg->rdev);
|
||||
/* Use locked consumer API when not in regulator framework */
|
||||
ctrl_uV = regulator_get_voltage(ctrl_reg);
|
||||
if (ctrl_uV < 0) {
|
||||
dev_err(&pdev->dev, "failed to get control voltage\n");
|
||||
return ctrl_uV;
|
||||
|
@ -513,6 +523,9 @@ static int vctrl_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
/* Drop ctrl-supply here in favor of regulator core managed supply */
|
||||
devm_regulator_put(ctrl_reg);
|
||||
|
||||
vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg);
|
||||
if (IS_ERR(vctrl->rdev)) {
|
||||
ret = PTR_ERR(vctrl->rdev);
|
||||
|
|
|
@ -658,6 +658,18 @@ config SPI_ROCKCHIP
|
|||
The main usecase of this controller is to use spi flash as boot
|
||||
device.
|
||||
|
||||
config SPI_ROCKCHIP_SFC
|
||||
tristate "Rockchip Serial Flash Controller (SFC)"
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
depends on HAS_IOMEM && HAS_DMA
|
||||
help
|
||||
This enables support for Rockchip serial flash controller. This
|
||||
is a specialized controller used to access SPI flash on some
|
||||
Rockchip SOCs.
|
||||
|
||||
ROCKCHIP SFC supports DMA and PIO modes. When DMA is not available,
|
||||
the driver automatically falls back to PIO mode.
|
||||
|
||||
config SPI_RB4XX
|
||||
tristate "Mikrotik RB4XX SPI master"
|
||||
depends on SPI_MASTER && ATH79
|
||||
|
|
|
@ -95,6 +95,7 @@ obj-$(CONFIG_SPI_QCOM_GENI) += spi-geni-qcom.o
|
|||
obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom-qspi.o
|
||||
obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
||||
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
|
||||
obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o
|
||||
obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
|
||||
obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
|
||||
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
|
||||
|
|
|
@ -143,12 +143,12 @@ static void bcm2835aux_debugfs_remove(struct bcm2835aux_spi *bs)
|
|||
}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg)
|
||||
static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned int reg)
|
||||
{
|
||||
return readl(bs->regs + reg);
|
||||
}
|
||||
|
||||
static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned reg,
|
||||
static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned int reg,
|
||||
u32 val)
|
||||
{
|
||||
writel(val, bs->regs + reg);
|
||||
|
|
|
@ -444,7 +444,7 @@ static int mcfqspi_remove(struct platform_device *pdev)
|
|||
mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR);
|
||||
|
||||
mcfqspi_cs_teardown(mcfqspi);
|
||||
clk_disable(mcfqspi->clk);
|
||||
clk_disable_unprepare(mcfqspi->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -213,12 +213,6 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value)
|
|||
* line for the controller
|
||||
*/
|
||||
if (spi->cs_gpiod) {
|
||||
/*
|
||||
* FIXME: is this code ever executed? This host does not
|
||||
* set SPI_MASTER_GPIO_SS so this chipselect callback should
|
||||
* not get called from the SPI core when we are using
|
||||
* GPIOs for chip select.
|
||||
*/
|
||||
if (value == BITBANG_CS_ACTIVE)
|
||||
gpiod_set_value(spi->cs_gpiod, 1);
|
||||
else
|
||||
|
@ -945,7 +939,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
|
|||
master->bus_num = pdev->id;
|
||||
master->num_chipselect = pdata->num_chipselect;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 16);
|
||||
master->flags = SPI_MASTER_MUST_RX;
|
||||
master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_GPIO_SS;
|
||||
master->setup = davinci_spi_setup;
|
||||
master->cleanup = davinci_spi_cleanup;
|
||||
master->can_dma = davinci_spi_can_dma;
|
||||
|
|
|
@ -550,7 +550,7 @@ static int ep93xx_spi_prepare_hardware(struct spi_master *master)
|
|||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(espi->clk);
|
||||
ret = clk_prepare_enable(espi->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -570,7 +570,7 @@ static int ep93xx_spi_unprepare_hardware(struct spi_master *master)
|
|||
val &= ~SSPCR1_SSE;
|
||||
writel(val, espi->mmio + SSPCR1);
|
||||
|
||||
clk_disable(espi->clk);
|
||||
clk_disable_unprepare(espi->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -25,16 +25,11 @@
|
|||
|
||||
#define SPI_FSI_BASE 0x70000
|
||||
#define SPI_FSI_INIT_TIMEOUT_MS 1000
|
||||
#define SPI_FSI_MAX_XFR_SIZE 2048
|
||||
#define SPI_FSI_MAX_XFR_SIZE_RESTRICTED 8
|
||||
#define SPI_FSI_MAX_RX_SIZE 8
|
||||
#define SPI_FSI_MAX_TX_SIZE 40
|
||||
|
||||
#define SPI_FSI_ERROR 0x0
|
||||
#define SPI_FSI_COUNTER_CFG 0x1
|
||||
#define SPI_FSI_COUNTER_CFG_LOOPS(x) (((u64)(x) & 0xffULL) << 32)
|
||||
#define SPI_FSI_COUNTER_CFG_N2_RX BIT_ULL(8)
|
||||
#define SPI_FSI_COUNTER_CFG_N2_TX BIT_ULL(9)
|
||||
#define SPI_FSI_COUNTER_CFG_N2_IMPLICIT BIT_ULL(10)
|
||||
#define SPI_FSI_COUNTER_CFG_N2_RELOAD BIT_ULL(11)
|
||||
#define SPI_FSI_CFG1 0x2
|
||||
#define SPI_FSI_CLOCK_CFG 0x3
|
||||
#define SPI_FSI_CLOCK_CFG_MM_ENABLE BIT_ULL(32)
|
||||
|
@ -76,8 +71,6 @@ struct fsi_spi {
|
|||
struct device *dev; /* SPI controller device */
|
||||
struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
|
||||
u32 base;
|
||||
size_t max_xfr_size;
|
||||
bool restricted;
|
||||
};
|
||||
|
||||
struct fsi_spi_sequence {
|
||||
|
@ -241,7 +234,7 @@ static int fsi_spi_reset(struct fsi_spi *ctx)
|
|||
return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL);
|
||||
}
|
||||
|
||||
static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
|
||||
static void fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
|
||||
{
|
||||
/*
|
||||
* Add the next byte of instruction to the 8-byte sequence register.
|
||||
|
@ -251,8 +244,6 @@ static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
|
|||
*/
|
||||
seq->data |= (u64)val << seq->bit;
|
||||
seq->bit -= 8;
|
||||
|
||||
return ((64 - seq->bit) / 8) - 2;
|
||||
}
|
||||
|
||||
static void fsi_spi_sequence_init(struct fsi_spi_sequence *seq)
|
||||
|
@ -261,71 +252,11 @@ static void fsi_spi_sequence_init(struct fsi_spi_sequence *seq)
|
|||
seq->data = 0ULL;
|
||||
}
|
||||
|
||||
static int fsi_spi_sequence_transfer(struct fsi_spi *ctx,
|
||||
struct fsi_spi_sequence *seq,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
int loops;
|
||||
int idx;
|
||||
int rc;
|
||||
u8 val = 0;
|
||||
u8 len = min(transfer->len, 8U);
|
||||
u8 rem = transfer->len % len;
|
||||
|
||||
loops = transfer->len / len;
|
||||
|
||||
if (transfer->tx_buf) {
|
||||
val = SPI_FSI_SEQUENCE_SHIFT_OUT(len);
|
||||
idx = fsi_spi_sequence_add(seq, val);
|
||||
|
||||
if (rem)
|
||||
rem = SPI_FSI_SEQUENCE_SHIFT_OUT(rem);
|
||||
} else if (transfer->rx_buf) {
|
||||
val = SPI_FSI_SEQUENCE_SHIFT_IN(len);
|
||||
idx = fsi_spi_sequence_add(seq, val);
|
||||
|
||||
if (rem)
|
||||
rem = SPI_FSI_SEQUENCE_SHIFT_IN(rem);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ctx->restricted && loops > 1) {
|
||||
dev_warn(ctx->dev,
|
||||
"Transfer too large; no branches permitted.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (loops > 1) {
|
||||
u64 cfg = SPI_FSI_COUNTER_CFG_LOOPS(loops - 1);
|
||||
|
||||
fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx));
|
||||
|
||||
if (transfer->rx_buf)
|
||||
cfg |= SPI_FSI_COUNTER_CFG_N2_RX |
|
||||
SPI_FSI_COUNTER_CFG_N2_TX |
|
||||
SPI_FSI_COUNTER_CFG_N2_IMPLICIT |
|
||||
SPI_FSI_COUNTER_CFG_N2_RELOAD;
|
||||
|
||||
rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, cfg);
|
||||
if (rc)
|
||||
return rc;
|
||||
} else {
|
||||
fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL);
|
||||
}
|
||||
|
||||
if (rem)
|
||||
fsi_spi_sequence_add(seq, rem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsi_spi_transfer_data(struct fsi_spi *ctx,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
int rc = 0;
|
||||
u64 status = 0ULL;
|
||||
u64 cfg = 0ULL;
|
||||
|
||||
if (transfer->tx_buf) {
|
||||
int nb;
|
||||
|
@ -363,16 +294,6 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
|
|||
u64 in = 0ULL;
|
||||
u8 *rx = transfer->rx_buf;
|
||||
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_COUNTER_CFG, &cfg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (cfg & SPI_FSI_COUNTER_CFG_N2_IMPLICIT) {
|
||||
rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
while (transfer->len > recv) {
|
||||
do {
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
|
||||
|
@ -439,6 +360,10 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx)
|
|||
}
|
||||
} while (seq_state && (seq_state != SPI_FSI_STATUS_SEQ_STATE_IDLE));
|
||||
|
||||
rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_CLOCK_CFG, &clock_cfg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -459,6 +384,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
{
|
||||
int rc;
|
||||
u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(mesg->spi->chip_select + 1);
|
||||
unsigned int len;
|
||||
struct spi_transfer *transfer;
|
||||
struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
|
||||
|
||||
|
@ -471,8 +397,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
struct spi_transfer *next = NULL;
|
||||
|
||||
/* Sequencer must do shift out (tx) first. */
|
||||
if (!transfer->tx_buf ||
|
||||
transfer->len > (ctx->max_xfr_size + 8)) {
|
||||
if (!transfer->tx_buf || transfer->len > SPI_FSI_MAX_TX_SIZE) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
@ -486,9 +411,13 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
fsi_spi_sequence_init(&seq);
|
||||
fsi_spi_sequence_add(&seq, seq_slave);
|
||||
|
||||
rc = fsi_spi_sequence_transfer(ctx, &seq, transfer);
|
||||
if (rc)
|
||||
goto error;
|
||||
len = transfer->len;
|
||||
while (len > 8) {
|
||||
fsi_spi_sequence_add(&seq,
|
||||
SPI_FSI_SEQUENCE_SHIFT_OUT(8));
|
||||
len -= 8;
|
||||
}
|
||||
fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SHIFT_OUT(len));
|
||||
|
||||
if (!list_is_last(&transfer->transfer_list,
|
||||
&mesg->transfers)) {
|
||||
|
@ -496,7 +425,9 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
|
||||
/* Sequencer can only do shift in (rx) after tx. */
|
||||
if (next->rx_buf) {
|
||||
if (next->len > ctx->max_xfr_size) {
|
||||
u8 shift;
|
||||
|
||||
if (next->len > SPI_FSI_MAX_RX_SIZE) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
@ -504,10 +435,8 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
dev_dbg(ctx->dev, "Sequence rx of %d bytes.\n",
|
||||
next->len);
|
||||
|
||||
rc = fsi_spi_sequence_transfer(ctx, &seq,
|
||||
next);
|
||||
if (rc)
|
||||
goto error;
|
||||
shift = SPI_FSI_SEQUENCE_SHIFT_IN(next->len);
|
||||
fsi_spi_sequence_add(&seq, shift);
|
||||
} else {
|
||||
next = NULL;
|
||||
}
|
||||
|
@ -541,9 +470,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
|
||||
static size_t fsi_spi_max_transfer_size(struct spi_device *spi)
|
||||
{
|
||||
struct fsi_spi *ctx = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
return ctx->max_xfr_size;
|
||||
return SPI_FSI_MAX_RX_SIZE;
|
||||
}
|
||||
|
||||
static int fsi_spi_probe(struct device *dev)
|
||||
|
@ -582,14 +509,6 @@ static int fsi_spi_probe(struct device *dev)
|
|||
ctx->fsi = fsi;
|
||||
ctx->base = base + SPI_FSI_BASE;
|
||||
|
||||
if (of_device_is_compatible(np, "ibm,fsi2spi-restricted")) {
|
||||
ctx->restricted = true;
|
||||
ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE_RESTRICTED;
|
||||
} else {
|
||||
ctx->restricted = false;
|
||||
ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE;
|
||||
}
|
||||
|
||||
rc = devm_spi_register_controller(dev, ctlr);
|
||||
if (rc)
|
||||
spi_controller_put(ctlr);
|
||||
|
|
|
@ -530,6 +530,7 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
|
|||
goto err_rx_dma_buf;
|
||||
}
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.src_addr = phy_addr + SPI_POPR;
|
||||
cfg.dst_addr = phy_addr + SPI_PUSHR;
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
|
|
@ -549,12 +549,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
|
|||
*/
|
||||
spin_lock_irq(&mas->lock);
|
||||
geni_se_setup_m_cmd(se, m_cmd, FRAGMENTATION);
|
||||
|
||||
/*
|
||||
* TX_WATERMARK_REG should be set after SPI configuration and
|
||||
* setting up GENI SE engine, as driver starts data transfer
|
||||
* for the watermark interrupt.
|
||||
*/
|
||||
if (m_cmd & SPI_TX_ONLY) {
|
||||
if (geni_spi_handle_tx(mas))
|
||||
writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG);
|
||||
|
|
|
@ -1052,12 +1052,8 @@ static void spi_imx_set_burst_len(struct spi_imx_data *spi_imx, int n_bits)
|
|||
|
||||
static void spi_imx_push(struct spi_imx_data *spi_imx)
|
||||
{
|
||||
unsigned int burst_len, fifo_words;
|
||||
unsigned int burst_len;
|
||||
|
||||
if (spi_imx->dynamic_burst)
|
||||
fifo_words = 4;
|
||||
else
|
||||
fifo_words = spi_imx_bytes_per_word(spi_imx->bits_per_word);
|
||||
/*
|
||||
* Reload the FIFO when the remaining bytes to be transferred in the
|
||||
* current burst is 0. This only applies when bits_per_word is a
|
||||
|
@ -1076,7 +1072,7 @@ static void spi_imx_push(struct spi_imx_data *spi_imx)
|
|||
|
||||
spi_imx->remainder = burst_len;
|
||||
} else {
|
||||
spi_imx->remainder = fifo_words;
|
||||
spi_imx->remainder = spi_imx_bytes_per_word(spi_imx->bits_per_word);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1084,8 +1080,7 @@ static void spi_imx_push(struct spi_imx_data *spi_imx)
|
|||
if (!spi_imx->count)
|
||||
break;
|
||||
if (spi_imx->dynamic_burst &&
|
||||
spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder,
|
||||
fifo_words))
|
||||
spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder, 4))
|
||||
break;
|
||||
spi_imx->tx(spi_imx);
|
||||
spi_imx->txfifo++;
|
||||
|
@ -1195,6 +1190,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
|||
* dynamic_burst in that case.
|
||||
*/
|
||||
if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode &&
|
||||
!(spi->mode & SPI_CS_WORD) &&
|
||||
(spi_imx->bits_per_word == 8 ||
|
||||
spi_imx->bits_per_word == 16 ||
|
||||
spi_imx->bits_per_word == 32)) {
|
||||
|
@ -1630,6 +1626,15 @@ static int spi_imx_probe(struct platform_device *pdev)
|
|||
is_imx53_ecspi(spi_imx))
|
||||
spi_imx->bitbang.master->mode_bits |= SPI_LOOP | SPI_READY;
|
||||
|
||||
if (is_imx51_ecspi(spi_imx) &&
|
||||
device_property_read_u32(&pdev->dev, "cs-gpios", NULL))
|
||||
/*
|
||||
* When using HW-CS implementing SPI_CS_WORD can be done by just
|
||||
* setting the burst length to the word size. This is
|
||||
* considerably faster than manually controlling the CS.
|
||||
*/
|
||||
spi_imx->bitbang.master->mode_bits |= SPI_CS_WORD;
|
||||
|
||||
spi_imx->spi_drctl = spi_drctl;
|
||||
|
||||
init_completion(&spi_imx->xfer_done);
|
||||
|
|
|
@ -42,8 +42,9 @@
|
|||
#define SPI_CFG1_CS_IDLE_OFFSET 0
|
||||
#define SPI_CFG1_PACKET_LOOP_OFFSET 8
|
||||
#define SPI_CFG1_PACKET_LENGTH_OFFSET 16
|
||||
#define SPI_CFG1_GET_TICK_DLY_OFFSET 30
|
||||
#define SPI_CFG1_GET_TICK_DLY_OFFSET 29
|
||||
|
||||
#define SPI_CFG1_GET_TICK_DLY_MASK 0xe0000000
|
||||
#define SPI_CFG1_CS_IDLE_MASK 0xff
|
||||
#define SPI_CFG1_PACKET_LOOP_MASK 0xff00
|
||||
#define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000
|
||||
|
@ -90,6 +91,8 @@ struct mtk_spi_compatible {
|
|||
bool enhance_timing;
|
||||
/* some IC support DMA addr extension */
|
||||
bool dma_ext;
|
||||
/* some IC no need unprepare SPI clk */
|
||||
bool no_need_unprepare;
|
||||
};
|
||||
|
||||
struct mtk_spi {
|
||||
|
@ -104,6 +107,7 @@ struct mtk_spi {
|
|||
struct scatterlist *tx_sgl, *rx_sgl;
|
||||
u32 tx_sgl_len, rx_sgl_len;
|
||||
const struct mtk_spi_compatible *dev_comp;
|
||||
u32 spi_clk_hz;
|
||||
};
|
||||
|
||||
static const struct mtk_spi_compatible mtk_common_compat;
|
||||
|
@ -135,12 +139,21 @@ static const struct mtk_spi_compatible mt8183_compat = {
|
|||
.enhance_timing = true,
|
||||
};
|
||||
|
||||
static const struct mtk_spi_compatible mt6893_compat = {
|
||||
.need_pad_sel = true,
|
||||
.must_tx = true,
|
||||
.enhance_timing = true,
|
||||
.dma_ext = true,
|
||||
.no_need_unprepare = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* A piece of default chip info unless the platform
|
||||
* supplies it.
|
||||
*/
|
||||
static const struct mtk_chip_config mtk_default_chip_info = {
|
||||
.sample_sel = 0,
|
||||
.tick_delay = 0,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_spi_of_match[] = {
|
||||
|
@ -174,6 +187,9 @@ static const struct of_device_id mtk_spi_of_match[] = {
|
|||
{ .compatible = "mediatek,mt8192-spi",
|
||||
.data = (void *)&mt6765_compat,
|
||||
},
|
||||
{ .compatible = "mediatek,mt6893-spi",
|
||||
.data = (void *)&mt6893_compat,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_spi_of_match);
|
||||
|
@ -192,6 +208,65 @@ static void mtk_spi_reset(struct mtk_spi *mdata)
|
|||
writel(reg_val, mdata->base + SPI_CMD_REG);
|
||||
}
|
||||
|
||||
static int mtk_spi_set_hw_cs_timing(struct spi_device *spi)
|
||||
{
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
|
||||
struct spi_delay *cs_setup = &spi->cs_setup;
|
||||
struct spi_delay *cs_hold = &spi->cs_hold;
|
||||
struct spi_delay *cs_inactive = &spi->cs_inactive;
|
||||
u32 setup, hold, inactive;
|
||||
u32 reg_val;
|
||||
int delay;
|
||||
|
||||
delay = spi_delay_to_ns(cs_setup, NULL);
|
||||
if (delay < 0)
|
||||
return delay;
|
||||
setup = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000;
|
||||
|
||||
delay = spi_delay_to_ns(cs_hold, NULL);
|
||||
if (delay < 0)
|
||||
return delay;
|
||||
hold = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000;
|
||||
|
||||
delay = spi_delay_to_ns(cs_inactive, NULL);
|
||||
if (delay < 0)
|
||||
return delay;
|
||||
inactive = (delay * DIV_ROUND_UP(mdata->spi_clk_hz, 1000000)) / 1000;
|
||||
|
||||
setup = setup ? setup : 1;
|
||||
hold = hold ? hold : 1;
|
||||
inactive = inactive ? inactive : 1;
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CFG0_REG);
|
||||
if (mdata->dev_comp->enhance_timing) {
|
||||
hold = min_t(u32, hold, 0x10000);
|
||||
setup = min_t(u32, setup, 0x10000);
|
||||
reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val |= (((hold - 1) & 0xffff)
|
||||
<< SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
|
||||
reg_val |= (((setup - 1) & 0xffff)
|
||||
<< SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
|
||||
} else {
|
||||
hold = min_t(u32, hold, 0x100);
|
||||
setup = min_t(u32, setup, 0x100);
|
||||
reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val |= (((hold - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET);
|
||||
reg_val |= (((setup - 1) & 0xff)
|
||||
<< SPI_CFG0_CS_SETUP_OFFSET);
|
||||
}
|
||||
writel(reg_val, mdata->base + SPI_CFG0_REG);
|
||||
|
||||
inactive = min_t(u32, inactive, 0x100);
|
||||
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
||||
reg_val &= ~SPI_CFG1_CS_IDLE_MASK;
|
||||
reg_val |= (((inactive - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET);
|
||||
writel(reg_val, mdata->base + SPI_CFG1_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_spi_prepare_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
|
@ -261,6 +336,15 @@ static int mtk_spi_prepare_message(struct spi_master *master,
|
|||
writel(mdata->pad_sel[spi->chip_select],
|
||||
mdata->base + SPI_PAD_SEL_REG);
|
||||
|
||||
/* tick delay */
|
||||
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
||||
reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK;
|
||||
reg_val |= ((chip_config->tick_delay & 0x7)
|
||||
<< SPI_CFG1_GET_TICK_DLY_OFFSET);
|
||||
writel(reg_val, mdata->base + SPI_CFG1_REG);
|
||||
|
||||
/* set hw cs timing */
|
||||
mtk_spi_set_hw_cs_timing(spi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -287,12 +371,11 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
|
|||
static void mtk_spi_prepare_transfer(struct spi_master *master,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
u32 spi_clk_hz, div, sck_time, reg_val;
|
||||
u32 div, sck_time, reg_val;
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
spi_clk_hz = clk_get_rate(mdata->spi_clk);
|
||||
if (xfer->speed_hz < spi_clk_hz / 2)
|
||||
div = DIV_ROUND_UP(spi_clk_hz, xfer->speed_hz);
|
||||
if (xfer->speed_hz < mdata->spi_clk_hz / 2)
|
||||
div = DIV_ROUND_UP(mdata->spi_clk_hz, xfer->speed_hz);
|
||||
else
|
||||
div = 1;
|
||||
|
||||
|
@ -507,52 +590,6 @@ static bool mtk_spi_can_dma(struct spi_master *master,
|
|||
(unsigned long)xfer->rx_buf % 4 == 0);
|
||||
}
|
||||
|
||||
static int mtk_spi_set_hw_cs_timing(struct spi_device *spi,
|
||||
struct spi_delay *setup,
|
||||
struct spi_delay *hold,
|
||||
struct spi_delay *inactive)
|
||||
{
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
|
||||
u16 setup_dly, hold_dly, inactive_dly;
|
||||
u32 reg_val;
|
||||
|
||||
if ((setup && setup->unit != SPI_DELAY_UNIT_SCK) ||
|
||||
(hold && hold->unit != SPI_DELAY_UNIT_SCK) ||
|
||||
(inactive && inactive->unit != SPI_DELAY_UNIT_SCK)) {
|
||||
dev_err(&spi->dev,
|
||||
"Invalid delay unit, should be SPI_DELAY_UNIT_SCK\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
setup_dly = setup ? setup->value : 1;
|
||||
hold_dly = hold ? hold->value : 1;
|
||||
inactive_dly = inactive ? inactive->value : 1;
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CFG0_REG);
|
||||
if (mdata->dev_comp->enhance_timing) {
|
||||
reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val |= (((hold_dly - 1) & 0xffff)
|
||||
<< SPI_ADJUST_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val &= ~(0xffff << SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
|
||||
reg_val |= (((setup_dly - 1) & 0xffff)
|
||||
<< SPI_ADJUST_CFG0_CS_SETUP_OFFSET);
|
||||
} else {
|
||||
reg_val &= ~(0xff << SPI_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val |= (((hold_dly - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET);
|
||||
reg_val &= ~(0xff << SPI_CFG0_CS_SETUP_OFFSET);
|
||||
reg_val |= (((setup_dly - 1) & 0xff)
|
||||
<< SPI_CFG0_CS_SETUP_OFFSET);
|
||||
}
|
||||
writel(reg_val, mdata->base + SPI_CFG0_REG);
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CFG1_REG);
|
||||
reg_val &= ~SPI_CFG1_CS_IDLE_MASK;
|
||||
reg_val |= (((inactive_dly - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET);
|
||||
writel(reg_val, mdata->base + SPI_CFG1_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
|
||||
|
@ -790,7 +827,12 @@ static int mtk_spi_probe(struct platform_device *pdev)
|
|||
goto err_put_master;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk);
|
||||
|
||||
if (mdata->dev_comp->no_need_unprepare)
|
||||
clk_disable(mdata->spi_clk);
|
||||
else
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
|
@ -858,6 +900,9 @@ static int mtk_spi_remove(struct platform_device *pdev)
|
|||
|
||||
mtk_spi_reset(mdata);
|
||||
|
||||
if (mdata->dev_comp->no_need_unprepare)
|
||||
clk_unprepare(mdata->spi_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -906,7 +951,10 @@ static int mtk_spi_runtime_suspend(struct device *dev)
|
|||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
if (mdata->dev_comp->no_need_unprepare)
|
||||
clk_disable(mdata->spi_clk);
|
||||
else
|
||||
clk_disable_unprepare(mdata->spi_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -917,7 +965,10 @@ static int mtk_spi_runtime_resume(struct device *dev)
|
|||
struct mtk_spi *mdata = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(mdata->spi_clk);
|
||||
if (mdata->dev_comp->no_need_unprepare)
|
||||
ret = clk_enable(mdata->spi_clk);
|
||||
else
|
||||
ret = clk_prepare_enable(mdata->spi_clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable spi_clk (%d)\n", ret);
|
||||
return ret;
|
||||
|
|
|
@ -335,8 +335,10 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf,
|
|||
static bool mxic_spi_mem_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (op->data.buswidth > 4 || op->addr.buswidth > 4 ||
|
||||
op->dummy.buswidth > 4 || op->cmd.buswidth > 4)
|
||||
bool all_false;
|
||||
|
||||
if (op->data.buswidth > 8 || op->addr.buswidth > 8 ||
|
||||
op->dummy.buswidth > 8 || op->cmd.buswidth > 8)
|
||||
return false;
|
||||
|
||||
if (op->data.nbytes && op->dummy.nbytes &&
|
||||
|
@ -346,7 +348,13 @@ static bool mxic_spi_mem_supports_op(struct spi_mem *mem,
|
|||
if (op->addr.nbytes > 7)
|
||||
return false;
|
||||
|
||||
return spi_mem_default_supports_op(mem, op);
|
||||
all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr &&
|
||||
!op->data.dtr;
|
||||
|
||||
if (all_false)
|
||||
return spi_mem_default_supports_op(mem, op);
|
||||
else
|
||||
return spi_mem_dtr_supports_op(mem, op);
|
||||
}
|
||||
|
||||
static int mxic_spi_mem_exec_op(struct spi_mem *mem,
|
||||
|
@ -355,14 +363,15 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
|
|||
struct mxic_spi *mxic = spi_master_get_devdata(mem->spi->master);
|
||||
int nio = 1, i, ret;
|
||||
u32 ss_ctrl;
|
||||
u8 addr[8];
|
||||
u8 opcode = op->cmd.opcode;
|
||||
u8 addr[8], cmd[2];
|
||||
|
||||
ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mem->spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD))
|
||||
if (mem->spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL))
|
||||
nio = 8;
|
||||
else if (mem->spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD))
|
||||
nio = 4;
|
||||
else if (mem->spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL))
|
||||
nio = 2;
|
||||
|
@ -374,19 +383,26 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
|
|||
mxic->regs + HC_CFG);
|
||||
writel(HC_EN_BIT, mxic->regs + HC_EN);
|
||||
|
||||
ss_ctrl = OP_CMD_BYTES(1) | OP_CMD_BUSW(fls(op->cmd.buswidth) - 1);
|
||||
ss_ctrl = OP_CMD_BYTES(op->cmd.nbytes) |
|
||||
OP_CMD_BUSW(fls(op->cmd.buswidth) - 1) |
|
||||
(op->cmd.dtr ? OP_CMD_DDR : 0);
|
||||
|
||||
if (op->addr.nbytes)
|
||||
ss_ctrl |= OP_ADDR_BYTES(op->addr.nbytes) |
|
||||
OP_ADDR_BUSW(fls(op->addr.buswidth) - 1);
|
||||
OP_ADDR_BUSW(fls(op->addr.buswidth) - 1) |
|
||||
(op->addr.dtr ? OP_ADDR_DDR : 0);
|
||||
|
||||
if (op->dummy.nbytes)
|
||||
ss_ctrl |= OP_DUMMY_CYC(op->dummy.nbytes);
|
||||
|
||||
if (op->data.nbytes) {
|
||||
ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1) |
|
||||
(op->data.dtr ? OP_DATA_DDR : 0);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
ss_ctrl |= OP_READ;
|
||||
if (op->data.dtr)
|
||||
ss_ctrl |= OP_DQS_EN;
|
||||
}
|
||||
}
|
||||
|
||||
writel(ss_ctrl, mxic->regs + SS_CTRL(mem->spi->chip_select));
|
||||
|
@ -394,7 +410,10 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
|
|||
writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT,
|
||||
mxic->regs + HC_CFG);
|
||||
|
||||
ret = mxic_spi_data_xfer(mxic, &opcode, NULL, 1);
|
||||
for (i = 0; i < op->cmd.nbytes; i++)
|
||||
cmd[i] = op->cmd.opcode >> (8 * (op->cmd.nbytes - i - 1));
|
||||
|
||||
ret = mxic_spi_data_xfer(mxic, cmd, NULL, op->cmd.nbytes);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -567,7 +586,8 @@ static int mxic_spi_probe(struct platform_device *pdev)
|
|||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA |
|
||||
SPI_RX_DUAL | SPI_TX_DUAL |
|
||||
SPI_RX_QUAD | SPI_TX_QUAD;
|
||||
SPI_RX_QUAD | SPI_TX_QUAD |
|
||||
SPI_RX_OCTAL | SPI_TX_OCTAL;
|
||||
|
||||
mxic_spi_hw_init(mxic);
|
||||
|
||||
|
|
|
@ -328,8 +328,16 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
|
|||
static void orion_spi_set_cs(struct spi_device *spi, bool enable)
|
||||
{
|
||||
struct orion_spi *orion_spi;
|
||||
void __iomem *ctrl_reg;
|
||||
u32 val;
|
||||
|
||||
orion_spi = spi_master_get_devdata(spi->master);
|
||||
ctrl_reg = spi_reg(orion_spi, ORION_SPI_IF_CTRL_REG);
|
||||
|
||||
val = readl(ctrl_reg);
|
||||
|
||||
/* Clear existing chip-select and assertion state */
|
||||
val &= ~(ORION_SPI_CS_MASK | 0x1);
|
||||
|
||||
/*
|
||||
* If this line is using a GPIO to control chip select, this internal
|
||||
|
@ -338,9 +346,7 @@ static void orion_spi_set_cs(struct spi_device *spi, bool enable)
|
|||
* as it is handled by a GPIO, but that doesn't matter. What we need
|
||||
* is to deassert the old chip select and assert some other chip select.
|
||||
*/
|
||||
orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK);
|
||||
orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG,
|
||||
ORION_SPI_CS(spi->chip_select));
|
||||
val |= ORION_SPI_CS(spi->chip_select);
|
||||
|
||||
/*
|
||||
* Chip select logic is inverted from spi_set_cs(). For lines using a
|
||||
|
@ -350,9 +356,13 @@ static void orion_spi_set_cs(struct spi_device *spi, bool enable)
|
|||
* doesn't matter.
|
||||
*/
|
||||
if (!enable)
|
||||
orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
|
||||
else
|
||||
orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
|
||||
val |= 0x1;
|
||||
|
||||
/*
|
||||
* To avoid toggling unwanted chip selects update the register
|
||||
* with a single write.
|
||||
*/
|
||||
writel(val, ctrl_reg);
|
||||
}
|
||||
|
||||
static inline int orion_spi_wait_till_ready(struct orion_spi *orion_spi)
|
||||
|
|
|
@ -361,6 +361,7 @@ static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width)
|
|||
struct dma_slave_config cfg;
|
||||
int ret;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.device_fc = true;
|
||||
cfg.src_addr = pic32s->dma_base + buf_offset;
|
||||
cfg.dst_addr = pic32s->dma_base + buf_offset;
|
||||
|
|
|
@ -594,24 +594,29 @@ static int u32_reader(struct driver_data *drv_data)
|
|||
|
||||
static void reset_sccr1(struct driver_data *drv_data)
|
||||
{
|
||||
struct chip_data *chip =
|
||||
spi_get_ctldata(drv_data->controller->cur_msg->spi);
|
||||
u32 sccr1_reg;
|
||||
u32 mask = drv_data->int_cr1 | drv_data->dma_cr1, threshold;
|
||||
struct chip_data *chip;
|
||||
|
||||
if (drv_data->controller->cur_msg) {
|
||||
chip = spi_get_ctldata(drv_data->controller->cur_msg->spi);
|
||||
threshold = chip->threshold;
|
||||
} else {
|
||||
threshold = 0;
|
||||
}
|
||||
|
||||
sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1;
|
||||
switch (drv_data->ssp_type) {
|
||||
case QUARK_X1000_SSP:
|
||||
sccr1_reg &= ~QUARK_X1000_SSCR1_RFT;
|
||||
mask |= QUARK_X1000_SSCR1_RFT;
|
||||
break;
|
||||
case CE4100_SSP:
|
||||
sccr1_reg &= ~CE4100_SSCR1_RFT;
|
||||
mask |= CE4100_SSCR1_RFT;
|
||||
break;
|
||||
default:
|
||||
sccr1_reg &= ~SSCR1_RFT;
|
||||
mask |= SSCR1_RFT;
|
||||
break;
|
||||
}
|
||||
sccr1_reg |= chip->threshold;
|
||||
pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg);
|
||||
|
||||
pxa2xx_spi_update(drv_data, SSCR1, mask, threshold);
|
||||
}
|
||||
|
||||
static void int_stop_and_reset(struct driver_data *drv_data)
|
||||
|
@ -724,11 +729,8 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
|
|||
|
||||
static void handle_bad_msg(struct driver_data *drv_data)
|
||||
{
|
||||
int_stop_and_reset(drv_data);
|
||||
pxa2xx_spi_off(drv_data);
|
||||
clear_SSCR1_bits(drv_data, drv_data->int_cr1);
|
||||
if (!pxa25x_ssp_comp(drv_data))
|
||||
pxa2xx_spi_write(drv_data, SSTO, 0);
|
||||
write_SSSR_CS(drv_data, drv_data->clear_sr);
|
||||
|
||||
dev_err(drv_data->ssp->dev, "bad message state in interrupt handler\n");
|
||||
}
|
||||
|
@ -1156,13 +1158,10 @@ static void pxa2xx_spi_handle_err(struct spi_controller *controller,
|
|||
{
|
||||
struct driver_data *drv_data = spi_controller_get_devdata(controller);
|
||||
|
||||
int_stop_and_reset(drv_data);
|
||||
|
||||
/* Disable the SSP */
|
||||
pxa2xx_spi_off(drv_data);
|
||||
/* Clear and disable interrupts and service requests */
|
||||
write_SSSR_CS(drv_data, drv_data->clear_sr);
|
||||
clear_SSCR1_bits(drv_data, drv_data->int_cr1 | drv_data->dma_cr1);
|
||||
if (!pxa25x_ssp_comp(drv_data))
|
||||
pxa2xx_spi_write(drv_data, SSTO, 0);
|
||||
|
||||
/*
|
||||
* Stop the DMA if running. Note DMA callback handler may have unset
|
||||
|
|
|
@ -0,0 +1,694 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Rockchip Serial Flash Controller Driver
|
||||
*
|
||||
* Copyright (c) 2017-2021, Rockchip Inc.
|
||||
* Author: Shawn Lin <shawn.lin@rock-chips.com>
|
||||
* Chris Morgan <macroalpha82@gmail.com>
|
||||
* Jon Lin <Jon.lin@rock-chips.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
/* System control */
|
||||
#define SFC_CTRL 0x0
|
||||
#define SFC_CTRL_PHASE_SEL_NEGETIVE BIT(1)
|
||||
#define SFC_CTRL_CMD_BITS_SHIFT 8
|
||||
#define SFC_CTRL_ADDR_BITS_SHIFT 10
|
||||
#define SFC_CTRL_DATA_BITS_SHIFT 12
|
||||
|
||||
/* Interrupt mask */
|
||||
#define SFC_IMR 0x4
|
||||
#define SFC_IMR_RX_FULL BIT(0)
|
||||
#define SFC_IMR_RX_UFLOW BIT(1)
|
||||
#define SFC_IMR_TX_OFLOW BIT(2)
|
||||
#define SFC_IMR_TX_EMPTY BIT(3)
|
||||
#define SFC_IMR_TRAN_FINISH BIT(4)
|
||||
#define SFC_IMR_BUS_ERR BIT(5)
|
||||
#define SFC_IMR_NSPI_ERR BIT(6)
|
||||
#define SFC_IMR_DMA BIT(7)
|
||||
|
||||
/* Interrupt clear */
|
||||
#define SFC_ICLR 0x8
|
||||
#define SFC_ICLR_RX_FULL BIT(0)
|
||||
#define SFC_ICLR_RX_UFLOW BIT(1)
|
||||
#define SFC_ICLR_TX_OFLOW BIT(2)
|
||||
#define SFC_ICLR_TX_EMPTY BIT(3)
|
||||
#define SFC_ICLR_TRAN_FINISH BIT(4)
|
||||
#define SFC_ICLR_BUS_ERR BIT(5)
|
||||
#define SFC_ICLR_NSPI_ERR BIT(6)
|
||||
#define SFC_ICLR_DMA BIT(7)
|
||||
|
||||
/* FIFO threshold level */
|
||||
#define SFC_FTLR 0xc
|
||||
#define SFC_FTLR_TX_SHIFT 0
|
||||
#define SFC_FTLR_TX_MASK 0x1f
|
||||
#define SFC_FTLR_RX_SHIFT 8
|
||||
#define SFC_FTLR_RX_MASK 0x1f
|
||||
|
||||
/* Reset FSM and FIFO */
|
||||
#define SFC_RCVR 0x10
|
||||
#define SFC_RCVR_RESET BIT(0)
|
||||
|
||||
/* Enhanced mode */
|
||||
#define SFC_AX 0x14
|
||||
|
||||
/* Address Bit number */
|
||||
#define SFC_ABIT 0x18
|
||||
|
||||
/* Interrupt status */
|
||||
#define SFC_ISR 0x1c
|
||||
#define SFC_ISR_RX_FULL_SHIFT BIT(0)
|
||||
#define SFC_ISR_RX_UFLOW_SHIFT BIT(1)
|
||||
#define SFC_ISR_TX_OFLOW_SHIFT BIT(2)
|
||||
#define SFC_ISR_TX_EMPTY_SHIFT BIT(3)
|
||||
#define SFC_ISR_TX_FINISH_SHIFT BIT(4)
|
||||
#define SFC_ISR_BUS_ERR_SHIFT BIT(5)
|
||||
#define SFC_ISR_NSPI_ERR_SHIFT BIT(6)
|
||||
#define SFC_ISR_DMA_SHIFT BIT(7)
|
||||
|
||||
/* FIFO status */
|
||||
#define SFC_FSR 0x20
|
||||
#define SFC_FSR_TX_IS_FULL BIT(0)
|
||||
#define SFC_FSR_TX_IS_EMPTY BIT(1)
|
||||
#define SFC_FSR_RX_IS_EMPTY BIT(2)
|
||||
#define SFC_FSR_RX_IS_FULL BIT(3)
|
||||
#define SFC_FSR_TXLV_MASK GENMASK(12, 8)
|
||||
#define SFC_FSR_TXLV_SHIFT 8
|
||||
#define SFC_FSR_RXLV_MASK GENMASK(20, 16)
|
||||
#define SFC_FSR_RXLV_SHIFT 16
|
||||
|
||||
/* FSM status */
|
||||
#define SFC_SR 0x24
|
||||
#define SFC_SR_IS_IDLE 0x0
|
||||
#define SFC_SR_IS_BUSY 0x1
|
||||
|
||||
/* Raw interrupt status */
|
||||
#define SFC_RISR 0x28
|
||||
#define SFC_RISR_RX_FULL BIT(0)
|
||||
#define SFC_RISR_RX_UNDERFLOW BIT(1)
|
||||
#define SFC_RISR_TX_OVERFLOW BIT(2)
|
||||
#define SFC_RISR_TX_EMPTY BIT(3)
|
||||
#define SFC_RISR_TRAN_FINISH BIT(4)
|
||||
#define SFC_RISR_BUS_ERR BIT(5)
|
||||
#define SFC_RISR_NSPI_ERR BIT(6)
|
||||
#define SFC_RISR_DMA BIT(7)
|
||||
|
||||
/* Version */
|
||||
#define SFC_VER 0x2C
|
||||
#define SFC_VER_3 0x3
|
||||
#define SFC_VER_4 0x4
|
||||
#define SFC_VER_5 0x5
|
||||
|
||||
/* Delay line controller resiter */
|
||||
#define SFC_DLL_CTRL0 0x3C
|
||||
#define SFC_DLL_CTRL0_SCLK_SMP_DLL BIT(15)
|
||||
#define SFC_DLL_CTRL0_DLL_MAX_VER4 0xFFU
|
||||
#define SFC_DLL_CTRL0_DLL_MAX_VER5 0x1FFU
|
||||
|
||||
/* Master trigger */
|
||||
#define SFC_DMA_TRIGGER 0x80
|
||||
#define SFC_DMA_TRIGGER_START 1
|
||||
|
||||
/* Src or Dst addr for master */
|
||||
#define SFC_DMA_ADDR 0x84
|
||||
|
||||
/* Length control register extension 32GB */
|
||||
#define SFC_LEN_CTRL 0x88
|
||||
#define SFC_LEN_CTRL_TRB_SEL 1
|
||||
#define SFC_LEN_EXT 0x8C
|
||||
|
||||
/* Command */
|
||||
#define SFC_CMD 0x100
|
||||
#define SFC_CMD_IDX_SHIFT 0
|
||||
#define SFC_CMD_DUMMY_SHIFT 8
|
||||
#define SFC_CMD_DIR_SHIFT 12
|
||||
#define SFC_CMD_DIR_RD 0
|
||||
#define SFC_CMD_DIR_WR 1
|
||||
#define SFC_CMD_ADDR_SHIFT 14
|
||||
#define SFC_CMD_ADDR_0BITS 0
|
||||
#define SFC_CMD_ADDR_24BITS 1
|
||||
#define SFC_CMD_ADDR_32BITS 2
|
||||
#define SFC_CMD_ADDR_XBITS 3
|
||||
#define SFC_CMD_TRAN_BYTES_SHIFT 16
|
||||
#define SFC_CMD_CS_SHIFT 30
|
||||
|
||||
/* Address */
|
||||
#define SFC_ADDR 0x104
|
||||
|
||||
/* Data */
|
||||
#define SFC_DATA 0x108
|
||||
|
||||
/* The controller and documentation reports that it supports up to 4 CS
|
||||
* devices (0-3), however I have only been able to test a single CS (CS 0)
|
||||
* due to the configuration of my device.
|
||||
*/
|
||||
#define SFC_MAX_CHIPSELECT_NUM 4
|
||||
|
||||
/* The SFC can transfer max 16KB - 1 at one time
|
||||
* we set it to 15.5KB here for alignment.
|
||||
*/
|
||||
#define SFC_MAX_IOSIZE_VER3 (512 * 31)
|
||||
|
||||
/* DMA is only enabled for large data transmission */
|
||||
#define SFC_DMA_TRANS_THRETHOLD (0x40)
|
||||
|
||||
/* Maximum clock values from datasheet suggest keeping clock value under
|
||||
* 150MHz. No minimum or average value is suggested.
|
||||
*/
|
||||
#define SFC_MAX_SPEED (150 * 1000 * 1000)
|
||||
|
||||
struct rockchip_sfc {
|
||||
struct device *dev;
|
||||
void __iomem *regbase;
|
||||
struct clk *hclk;
|
||||
struct clk *clk;
|
||||
u32 frequency;
|
||||
/* virtual mapped addr for dma_buffer */
|
||||
void *buffer;
|
||||
dma_addr_t dma_buffer;
|
||||
struct completion cp;
|
||||
bool use_dma;
|
||||
u32 max_iosize;
|
||||
u16 version;
|
||||
};
|
||||
|
||||
static int rockchip_sfc_reset(struct rockchip_sfc *sfc)
|
||||
{
|
||||
int err;
|
||||
u32 status;
|
||||
|
||||
writel_relaxed(SFC_RCVR_RESET, sfc->regbase + SFC_RCVR);
|
||||
|
||||
err = readl_poll_timeout(sfc->regbase + SFC_RCVR, status,
|
||||
!(status & SFC_RCVR_RESET), 20,
|
||||
jiffies_to_usecs(HZ));
|
||||
if (err)
|
||||
dev_err(sfc->dev, "SFC reset never finished\n");
|
||||
|
||||
/* Still need to clear the masked interrupt from RISR */
|
||||
writel_relaxed(0xFFFFFFFF, sfc->regbase + SFC_ICLR);
|
||||
|
||||
dev_dbg(sfc->dev, "reset\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static u16 rockchip_sfc_get_version(struct rockchip_sfc *sfc)
|
||||
{
|
||||
return (u16)(readl(sfc->regbase + SFC_VER) & 0xffff);
|
||||
}
|
||||
|
||||
static u32 rockchip_sfc_get_max_iosize(struct rockchip_sfc *sfc)
|
||||
{
|
||||
return SFC_MAX_IOSIZE_VER3;
|
||||
}
|
||||
|
||||
static void rockchip_sfc_irq_unmask(struct rockchip_sfc *sfc, u32 mask)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Enable transfer complete interrupt */
|
||||
reg = readl(sfc->regbase + SFC_IMR);
|
||||
reg &= ~mask;
|
||||
writel(reg, sfc->regbase + SFC_IMR);
|
||||
}
|
||||
|
||||
static void rockchip_sfc_irq_mask(struct rockchip_sfc *sfc, u32 mask)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Disable transfer finish interrupt */
|
||||
reg = readl(sfc->regbase + SFC_IMR);
|
||||
reg |= mask;
|
||||
writel(reg, sfc->regbase + SFC_IMR);
|
||||
}
|
||||
|
||||
static int rockchip_sfc_init(struct rockchip_sfc *sfc)
|
||||
{
|
||||
writel(0, sfc->regbase + SFC_CTRL);
|
||||
writel(0xFFFFFFFF, sfc->regbase + SFC_ICLR);
|
||||
rockchip_sfc_irq_mask(sfc, 0xFFFFFFFF);
|
||||
if (rockchip_sfc_get_version(sfc) >= SFC_VER_4)
|
||||
writel(SFC_LEN_CTRL_TRB_SEL, sfc->regbase + SFC_LEN_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_wait_txfifo_ready(struct rockchip_sfc *sfc, u32 timeout_us)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 status;
|
||||
|
||||
ret = readl_poll_timeout(sfc->regbase + SFC_FSR, status,
|
||||
status & SFC_FSR_TXLV_MASK, 0,
|
||||
timeout_us);
|
||||
if (ret) {
|
||||
dev_dbg(sfc->dev, "sfc wait tx fifo timeout\n");
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return (status & SFC_FSR_TXLV_MASK) >> SFC_FSR_TXLV_SHIFT;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_wait_rxfifo_ready(struct rockchip_sfc *sfc, u32 timeout_us)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 status;
|
||||
|
||||
ret = readl_poll_timeout(sfc->regbase + SFC_FSR, status,
|
||||
status & SFC_FSR_RXLV_MASK, 0,
|
||||
timeout_us);
|
||||
if (ret) {
|
||||
dev_dbg(sfc->dev, "sfc wait rx fifo timeout\n");
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return (status & SFC_FSR_RXLV_MASK) >> SFC_FSR_RXLV_SHIFT;
|
||||
}
|
||||
|
||||
static void rockchip_sfc_adjust_op_work(struct spi_mem_op *op)
|
||||
{
|
||||
if (unlikely(op->dummy.nbytes && !op->addr.nbytes)) {
|
||||
/*
|
||||
* SFC not support output DUMMY cycles right after CMD cycles, so
|
||||
* treat it as ADDR cycles.
|
||||
*/
|
||||
op->addr.nbytes = op->dummy.nbytes;
|
||||
op->addr.buswidth = op->dummy.buswidth;
|
||||
op->addr.val = 0xFFFFFFFFF;
|
||||
|
||||
op->dummy.nbytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
|
||||
struct spi_mem *mem,
|
||||
const struct spi_mem_op *op,
|
||||
u32 len)
|
||||
{
|
||||
u32 ctrl = 0, cmd = 0;
|
||||
|
||||
/* set CMD */
|
||||
cmd = op->cmd.opcode;
|
||||
ctrl |= ((op->cmd.buswidth >> 1) << SFC_CTRL_CMD_BITS_SHIFT);
|
||||
|
||||
/* set ADDR */
|
||||
if (op->addr.nbytes) {
|
||||
if (op->addr.nbytes == 4) {
|
||||
cmd |= SFC_CMD_ADDR_32BITS << SFC_CMD_ADDR_SHIFT;
|
||||
} else if (op->addr.nbytes == 3) {
|
||||
cmd |= SFC_CMD_ADDR_24BITS << SFC_CMD_ADDR_SHIFT;
|
||||
} else {
|
||||
cmd |= SFC_CMD_ADDR_XBITS << SFC_CMD_ADDR_SHIFT;
|
||||
writel(op->addr.nbytes * 8 - 1, sfc->regbase + SFC_ABIT);
|
||||
}
|
||||
|
||||
ctrl |= ((op->addr.buswidth >> 1) << SFC_CTRL_ADDR_BITS_SHIFT);
|
||||
}
|
||||
|
||||
/* set DUMMY */
|
||||
if (op->dummy.nbytes) {
|
||||
if (op->dummy.buswidth == 4)
|
||||
cmd |= op->dummy.nbytes * 2 << SFC_CMD_DUMMY_SHIFT;
|
||||
else if (op->dummy.buswidth == 2)
|
||||
cmd |= op->dummy.nbytes * 4 << SFC_CMD_DUMMY_SHIFT;
|
||||
else
|
||||
cmd |= op->dummy.nbytes * 8 << SFC_CMD_DUMMY_SHIFT;
|
||||
}
|
||||
|
||||
/* set DATA */
|
||||
if (sfc->version >= SFC_VER_4) /* Clear it if no data to transfer */
|
||||
writel(len, sfc->regbase + SFC_LEN_EXT);
|
||||
else
|
||||
cmd |= len << SFC_CMD_TRAN_BYTES_SHIFT;
|
||||
if (len) {
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
cmd |= SFC_CMD_DIR_WR << SFC_CMD_DIR_SHIFT;
|
||||
|
||||
ctrl |= ((op->data.buswidth >> 1) << SFC_CTRL_DATA_BITS_SHIFT);
|
||||
}
|
||||
if (!len && op->addr.nbytes)
|
||||
cmd |= SFC_CMD_DIR_WR << SFC_CMD_DIR_SHIFT;
|
||||
|
||||
/* set the Controller */
|
||||
ctrl |= SFC_CTRL_PHASE_SEL_NEGETIVE;
|
||||
cmd |= mem->spi->chip_select << SFC_CMD_CS_SHIFT;
|
||||
|
||||
dev_dbg(sfc->dev, "sfc addr.nbytes=%x(x%d) dummy.nbytes=%x(x%d)\n",
|
||||
op->addr.nbytes, op->addr.buswidth,
|
||||
op->dummy.nbytes, op->dummy.buswidth);
|
||||
dev_dbg(sfc->dev, "sfc ctrl=%x cmd=%x addr=%llx len=%x\n",
|
||||
ctrl, cmd, op->addr.val, len);
|
||||
|
||||
writel(ctrl, sfc->regbase + SFC_CTRL);
|
||||
writel(cmd, sfc->regbase + SFC_CMD);
|
||||
if (op->addr.nbytes)
|
||||
writel(op->addr.val, sfc->regbase + SFC_ADDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_write_fifo(struct rockchip_sfc *sfc, const u8 *buf, int len)
|
||||
{
|
||||
u8 bytes = len & 0x3;
|
||||
u32 dwords;
|
||||
int tx_level;
|
||||
u32 write_words;
|
||||
u32 tmp = 0;
|
||||
|
||||
dwords = len >> 2;
|
||||
while (dwords) {
|
||||
tx_level = rockchip_sfc_wait_txfifo_ready(sfc, 1000);
|
||||
if (tx_level < 0)
|
||||
return tx_level;
|
||||
write_words = min_t(u32, tx_level, dwords);
|
||||
iowrite32_rep(sfc->regbase + SFC_DATA, buf, write_words);
|
||||
buf += write_words << 2;
|
||||
dwords -= write_words;
|
||||
}
|
||||
|
||||
/* write the rest non word aligned bytes */
|
||||
if (bytes) {
|
||||
tx_level = rockchip_sfc_wait_txfifo_ready(sfc, 1000);
|
||||
if (tx_level < 0)
|
||||
return tx_level;
|
||||
memcpy(&tmp, buf, bytes);
|
||||
writel(tmp, sfc->regbase + SFC_DATA);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_read_fifo(struct rockchip_sfc *sfc, u8 *buf, int len)
|
||||
{
|
||||
u8 bytes = len & 0x3;
|
||||
u32 dwords;
|
||||
u8 read_words;
|
||||
int rx_level;
|
||||
int tmp;
|
||||
|
||||
/* word aligned access only */
|
||||
dwords = len >> 2;
|
||||
while (dwords) {
|
||||
rx_level = rockchip_sfc_wait_rxfifo_ready(sfc, 1000);
|
||||
if (rx_level < 0)
|
||||
return rx_level;
|
||||
read_words = min_t(u32, rx_level, dwords);
|
||||
ioread32_rep(sfc->regbase + SFC_DATA, buf, read_words);
|
||||
buf += read_words << 2;
|
||||
dwords -= read_words;
|
||||
}
|
||||
|
||||
/* read the rest non word aligned bytes */
|
||||
if (bytes) {
|
||||
rx_level = rockchip_sfc_wait_rxfifo_ready(sfc, 1000);
|
||||
if (rx_level < 0)
|
||||
return rx_level;
|
||||
tmp = readl(sfc->regbase + SFC_DATA);
|
||||
memcpy(buf, &tmp, bytes);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_fifo_transfer_dma(struct rockchip_sfc *sfc, dma_addr_t dma_buf, size_t len)
|
||||
{
|
||||
writel(0xFFFFFFFF, sfc->regbase + SFC_ICLR);
|
||||
writel((u32)dma_buf, sfc->regbase + SFC_DMA_ADDR);
|
||||
writel(SFC_DMA_TRIGGER_START, sfc->regbase + SFC_DMA_TRIGGER);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_xfer_data_poll(struct rockchip_sfc *sfc,
|
||||
const struct spi_mem_op *op, u32 len)
|
||||
{
|
||||
dev_dbg(sfc->dev, "sfc xfer_poll len=%x\n", len);
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
return rockchip_sfc_write_fifo(sfc, op->data.buf.out, len);
|
||||
else
|
||||
return rockchip_sfc_read_fifo(sfc, op->data.buf.in, len);
|
||||
}
|
||||
|
||||
static int rockchip_sfc_xfer_data_dma(struct rockchip_sfc *sfc,
|
||||
const struct spi_mem_op *op, u32 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(sfc->dev, "sfc xfer_dma len=%x\n", len);
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
memcpy(sfc->buffer, op->data.buf.out, len);
|
||||
|
||||
ret = rockchip_sfc_fifo_transfer_dma(sfc, sfc->dma_buffer, len);
|
||||
if (!wait_for_completion_timeout(&sfc->cp, msecs_to_jiffies(2000))) {
|
||||
dev_err(sfc->dev, "DMA wait for transfer finish timeout\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
rockchip_sfc_irq_mask(sfc, SFC_IMR_DMA);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
memcpy(op->data.buf.in, sfc->buffer, len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_xfer_done(struct rockchip_sfc *sfc, u32 timeout_us)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 status;
|
||||
|
||||
ret = readl_poll_timeout(sfc->regbase + SFC_SR, status,
|
||||
!(status & SFC_SR_IS_BUSY),
|
||||
20, timeout_us);
|
||||
if (ret) {
|
||||
dev_err(sfc->dev, "wait sfc idle timeout\n");
|
||||
rockchip_sfc_reset(sfc);
|
||||
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct rockchip_sfc *sfc = spi_master_get_devdata(mem->spi->master);
|
||||
u32 len = op->data.nbytes;
|
||||
int ret;
|
||||
|
||||
if (unlikely(mem->spi->max_speed_hz != sfc->frequency)) {
|
||||
ret = clk_set_rate(sfc->clk, mem->spi->max_speed_hz);
|
||||
if (ret)
|
||||
return ret;
|
||||
sfc->frequency = mem->spi->max_speed_hz;
|
||||
dev_dbg(sfc->dev, "set_freq=%dHz real_freq=%ldHz\n",
|
||||
sfc->frequency, clk_get_rate(sfc->clk));
|
||||
}
|
||||
|
||||
rockchip_sfc_adjust_op_work((struct spi_mem_op *)op);
|
||||
rockchip_sfc_xfer_setup(sfc, mem, op, len);
|
||||
if (len) {
|
||||
if (likely(sfc->use_dma) && len >= SFC_DMA_TRANS_THRETHOLD) {
|
||||
init_completion(&sfc->cp);
|
||||
rockchip_sfc_irq_unmask(sfc, SFC_IMR_DMA);
|
||||
ret = rockchip_sfc_xfer_data_dma(sfc, op, len);
|
||||
} else {
|
||||
ret = rockchip_sfc_xfer_data_poll(sfc, op, len);
|
||||
}
|
||||
|
||||
if (ret != len) {
|
||||
dev_err(sfc->dev, "xfer data failed ret %d dir %d\n", ret, op->data.dir);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return rockchip_sfc_xfer_done(sfc, 100000);
|
||||
}
|
||||
|
||||
static int rockchip_sfc_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
struct rockchip_sfc *sfc = spi_master_get_devdata(mem->spi->master);
|
||||
|
||||
op->data.nbytes = min(op->data.nbytes, sfc->max_iosize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops rockchip_sfc_mem_ops = {
|
||||
.exec_op = rockchip_sfc_exec_mem_op,
|
||||
.adjust_op_size = rockchip_sfc_adjust_op_size,
|
||||
};
|
||||
|
||||
static irqreturn_t rockchip_sfc_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct rockchip_sfc *sfc = dev_id;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(sfc->regbase + SFC_RISR);
|
||||
|
||||
/* Clear interrupt */
|
||||
writel_relaxed(reg, sfc->regbase + SFC_ICLR);
|
||||
|
||||
if (reg & SFC_RISR_DMA) {
|
||||
complete(&sfc->cp);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_master *master;
|
||||
struct resource *res;
|
||||
struct rockchip_sfc *sfc;
|
||||
int ret;
|
||||
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(*sfc));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||
master->mem_ops = &rockchip_sfc_mem_ops;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->mode_bits = SPI_TX_QUAD | SPI_TX_DUAL | SPI_RX_QUAD | SPI_RX_DUAL;
|
||||
master->max_speed_hz = SFC_MAX_SPEED;
|
||||
master->num_chipselect = SFC_MAX_CHIPSELECT_NUM;
|
||||
|
||||
sfc = spi_master_get_devdata(master);
|
||||
sfc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sfc->regbase = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(sfc->regbase))
|
||||
return PTR_ERR(sfc->regbase);
|
||||
|
||||
sfc->clk = devm_clk_get(&pdev->dev, "clk_sfc");
|
||||
if (IS_ERR(sfc->clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get sfc interface clk\n");
|
||||
return PTR_ERR(sfc->clk);
|
||||
}
|
||||
|
||||
sfc->hclk = devm_clk_get(&pdev->dev, "hclk_sfc");
|
||||
if (IS_ERR(sfc->hclk)) {
|
||||
dev_err(&pdev->dev, "Failed to get sfc ahb clk\n");
|
||||
return PTR_ERR(sfc->hclk);
|
||||
}
|
||||
|
||||
sfc->use_dma = !of_property_read_bool(sfc->dev->of_node,
|
||||
"rockchip,sfc-no-dma");
|
||||
|
||||
if (sfc->use_dma) {
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
dev_warn(dev, "Unable to set dma mask\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sfc->buffer = dmam_alloc_coherent(dev, SFC_MAX_IOSIZE_VER3,
|
||||
&sfc->dma_buffer,
|
||||
GFP_KERNEL);
|
||||
if (!sfc->buffer)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sfc->hclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to enable ahb clk\n");
|
||||
goto err_hclk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sfc->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to enable interface clk\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
/* Find the irq */
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to get the irq\n");
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, ret, rockchip_sfc_irq_handler,
|
||||
0, pdev->name, sfc);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request irq\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = rockchip_sfc_init(sfc);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc);
|
||||
sfc->version = rockchip_sfc_get_version(sfc);
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
clk_disable_unprepare(sfc->clk);
|
||||
err_clk:
|
||||
clk_disable_unprepare(sfc->hclk);
|
||||
err_hclk:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct rockchip_sfc *sfc = platform_get_drvdata(pdev);
|
||||
|
||||
spi_unregister_master(master);
|
||||
|
||||
clk_disable_unprepare(sfc->clk);
|
||||
clk_disable_unprepare(sfc->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rockchip_sfc_dt_ids[] = {
|
||||
{ .compatible = "rockchip,sfc"},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_sfc_dt_ids);
|
||||
|
||||
static struct platform_driver rockchip_sfc_driver = {
|
||||
.driver = {
|
||||
.name = "rockchip-sfc",
|
||||
.of_match_table = rockchip_sfc_dt_ids,
|
||||
},
|
||||
.probe = rockchip_sfc_probe,
|
||||
.remove = rockchip_sfc_remove,
|
||||
};
|
||||
module_platform_driver(rockchip_sfc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Rockchip Serial Flash Controller Driver");
|
||||
MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
|
||||
MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");
|
||||
MODULE_AUTHOR("Jon Lin <Jon.lin@rock-chips.com>");
|
|
@ -52,10 +52,20 @@
|
|||
|
||||
/*
|
||||
* ADI slave devices include RTC, ADC, regulator, charger, thermal and so on.
|
||||
* The slave devices address offset is always 0x8000 and size is 4K.
|
||||
* ADI supports 12/14bit address for r2p0, and additional 17bit for r3p0 or
|
||||
* later versions. Since bit[1:0] are zero, so the spec describe them as
|
||||
* 10/12/15bit address mode.
|
||||
* The 10bit mode supports sigle slave, 12/15bit mode supports 3 slave, the
|
||||
* high two bits is slave_id.
|
||||
* The slave devices address offset is 0x8000 for 10/12bit address mode,
|
||||
* and 0x20000 for 15bit mode.
|
||||
*/
|
||||
#define ADI_SLAVE_ADDR_SIZE SZ_4K
|
||||
#define ADI_SLAVE_OFFSET 0x8000
|
||||
#define ADI_10BIT_SLAVE_ADDR_SIZE SZ_4K
|
||||
#define ADI_10BIT_SLAVE_OFFSET 0x8000
|
||||
#define ADI_12BIT_SLAVE_ADDR_SIZE SZ_16K
|
||||
#define ADI_12BIT_SLAVE_OFFSET 0x8000
|
||||
#define ADI_15BIT_SLAVE_ADDR_SIZE SZ_128K
|
||||
#define ADI_15BIT_SLAVE_OFFSET 0x20000
|
||||
|
||||
/* Timeout (ms) for the trylock of hardware spinlocks */
|
||||
#define ADI_HWSPINLOCK_TIMEOUT 5000
|
||||
|
@ -67,24 +77,35 @@
|
|||
|
||||
#define ADI_FIFO_DRAIN_TIMEOUT 1000
|
||||
#define ADI_READ_TIMEOUT 2000
|
||||
#define REG_ADDR_LOW_MASK GENMASK(11, 0)
|
||||
|
||||
/*
|
||||
* Read back address from REG_ADI_RD_DATA bit[30:16] which maps to:
|
||||
* REG_ADI_RD_CMD bit[14:0] for r2p0
|
||||
* REG_ADI_RD_CMD bit[16:2] for r3p0
|
||||
*/
|
||||
#define RDBACK_ADDR_MASK_R2 GENMASK(14, 0)
|
||||
#define RDBACK_ADDR_MASK_R3 GENMASK(16, 2)
|
||||
#define RDBACK_ADDR_SHIFT_R3 2
|
||||
|
||||
/* Registers definitions for PMIC watchdog controller */
|
||||
#define REG_WDG_LOAD_LOW 0x80
|
||||
#define REG_WDG_LOAD_HIGH 0x84
|
||||
#define REG_WDG_CTRL 0x88
|
||||
#define REG_WDG_LOCK 0xa0
|
||||
#define REG_WDG_LOAD_LOW 0x0
|
||||
#define REG_WDG_LOAD_HIGH 0x4
|
||||
#define REG_WDG_CTRL 0x8
|
||||
#define REG_WDG_LOCK 0x20
|
||||
|
||||
/* Bits definitions for register REG_WDG_CTRL */
|
||||
#define BIT_WDG_RUN BIT(1)
|
||||
#define BIT_WDG_NEW BIT(2)
|
||||
#define BIT_WDG_RST BIT(3)
|
||||
|
||||
/* Bits definitions for register REG_MODULE_EN */
|
||||
#define BIT_WDG_EN BIT(2)
|
||||
|
||||
/* Registers definitions for PMIC */
|
||||
#define PMIC_RST_STATUS 0xee8
|
||||
#define PMIC_MODULE_EN 0xc08
|
||||
#define PMIC_CLK_EN 0xc18
|
||||
#define BIT_WDG_EN BIT(2)
|
||||
#define PMIC_WDG_BASE 0x80
|
||||
|
||||
/* Definition of PMIC reset status register */
|
||||
#define HWRST_STATUS_SECURITY 0x02
|
||||
|
@ -103,10 +124,26 @@
|
|||
#define HWRST_STATUS_WATCHDOG 0xf0
|
||||
|
||||
/* Use default timeout 50 ms that converts to watchdog values */
|
||||
#define WDG_LOAD_VAL ((50 * 1000) / 32768)
|
||||
#define WDG_LOAD_VAL ((50 * 32768) / 1000)
|
||||
#define WDG_LOAD_MASK GENMASK(15, 0)
|
||||
#define WDG_UNLOCK_KEY 0xe551
|
||||
|
||||
struct sprd_adi_wdg {
|
||||
u32 base;
|
||||
u32 rst_sts;
|
||||
u32 wdg_en;
|
||||
u32 wdg_clk;
|
||||
};
|
||||
|
||||
struct sprd_adi_data {
|
||||
u32 slave_offset;
|
||||
u32 slave_addr_size;
|
||||
int (*read_check)(u32 val, u32 reg);
|
||||
int (*restart)(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd);
|
||||
void (*wdg_rst)(void *p);
|
||||
};
|
||||
|
||||
struct sprd_adi {
|
||||
struct spi_controller *ctlr;
|
||||
struct device *dev;
|
||||
|
@ -115,26 +152,21 @@ struct sprd_adi {
|
|||
unsigned long slave_vbase;
|
||||
unsigned long slave_pbase;
|
||||
struct notifier_block restart_handler;
|
||||
const struct sprd_adi_data *data;
|
||||
};
|
||||
|
||||
static int sprd_adi_check_paddr(struct sprd_adi *sadi, u32 paddr)
|
||||
static int sprd_adi_check_addr(struct sprd_adi *sadi, u32 reg)
|
||||
{
|
||||
if (paddr < sadi->slave_pbase || paddr >
|
||||
(sadi->slave_pbase + ADI_SLAVE_ADDR_SIZE)) {
|
||||
if (reg >= sadi->data->slave_addr_size) {
|
||||
dev_err(sadi->dev,
|
||||
"slave physical address is incorrect, addr = 0x%x\n",
|
||||
paddr);
|
||||
"slave address offset is incorrect, reg = 0x%x\n",
|
||||
reg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long sprd_adi_to_vaddr(struct sprd_adi *sadi, u32 paddr)
|
||||
{
|
||||
return (paddr - sadi->slave_pbase + sadi->slave_vbase);
|
||||
}
|
||||
|
||||
static int sprd_adi_drain_fifo(struct sprd_adi *sadi)
|
||||
{
|
||||
u32 timeout = ADI_FIFO_DRAIN_TIMEOUT;
|
||||
|
@ -161,11 +193,35 @@ static int sprd_adi_fifo_is_full(struct sprd_adi *sadi)
|
|||
return readl_relaxed(sadi->base + REG_ADI_ARM_FIFO_STS) & BIT_FIFO_FULL;
|
||||
}
|
||||
|
||||
static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
|
||||
static int sprd_adi_read_check(u32 val, u32 addr)
|
||||
{
|
||||
u32 rd_addr;
|
||||
|
||||
rd_addr = (val & RD_ADDR_MASK) >> RD_ADDR_SHIFT;
|
||||
|
||||
if (rd_addr != addr) {
|
||||
pr_err("ADI read error, addr = 0x%x, val = 0x%x\n", addr, val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_adi_read_check_r2(u32 val, u32 reg)
|
||||
{
|
||||
return sprd_adi_read_check(val, reg & RDBACK_ADDR_MASK_R2);
|
||||
}
|
||||
|
||||
static int sprd_adi_read_check_r3(u32 val, u32 reg)
|
||||
{
|
||||
return sprd_adi_read_check(val, (reg & RDBACK_ADDR_MASK_R3) >> RDBACK_ADDR_SHIFT_R3);
|
||||
}
|
||||
|
||||
static int sprd_adi_read(struct sprd_adi *sadi, u32 reg, u32 *read_val)
|
||||
{
|
||||
int read_timeout = ADI_READ_TIMEOUT;
|
||||
unsigned long flags;
|
||||
u32 val, rd_addr;
|
||||
u32 val;
|
||||
int ret = 0;
|
||||
|
||||
if (sadi->hwlock) {
|
||||
|
@ -178,11 +234,15 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
|
|||
}
|
||||
}
|
||||
|
||||
ret = sprd_adi_check_addr(sadi, reg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Set the physical register address need to read into RD_CMD register,
|
||||
* Set the slave address offset need to read into RD_CMD register,
|
||||
* then ADI controller will start to transfer automatically.
|
||||
*/
|
||||
writel_relaxed(reg_paddr, sadi->base + REG_ADI_RD_CMD);
|
||||
writel_relaxed(reg, sadi->base + REG_ADI_RD_CMD);
|
||||
|
||||
/*
|
||||
* Wait read operation complete, the BIT_RD_CMD_BUSY will be set
|
||||
|
@ -205,18 +265,15 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
|
|||
}
|
||||
|
||||
/*
|
||||
* The return value includes data and read register address, from bit 0
|
||||
* to bit 15 are data, and from bit 16 to bit 30 are read register
|
||||
* address. Then we can check the returned register address to validate
|
||||
* data.
|
||||
* The return value before adi r5p0 includes data and read register
|
||||
* address, from bit 0to bit 15 are data, and from bit 16 to bit 30
|
||||
* are read register address. Then we can check the returned register
|
||||
* address to validate data.
|
||||
*/
|
||||
rd_addr = (val & RD_ADDR_MASK) >> RD_ADDR_SHIFT;
|
||||
|
||||
if (rd_addr != (reg_paddr & REG_ADDR_LOW_MASK)) {
|
||||
dev_err(sadi->dev, "read error, reg addr = 0x%x, val = 0x%x\n",
|
||||
reg_paddr, val);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
if (sadi->data->read_check) {
|
||||
ret = sadi->data->read_check(val, reg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
*read_val = val & RD_VALUE_MASK;
|
||||
|
@ -227,9 +284,8 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val)
|
||||
static int sprd_adi_write(struct sprd_adi *sadi, u32 reg, u32 val)
|
||||
{
|
||||
unsigned long reg = sprd_adi_to_vaddr(sadi, reg_paddr);
|
||||
u32 timeout = ADI_FIFO_DRAIN_TIMEOUT;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
@ -244,6 +300,10 @@ static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val)
|
|||
}
|
||||
}
|
||||
|
||||
ret = sprd_adi_check_addr(sadi, reg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = sprd_adi_drain_fifo(sadi);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
@ -254,7 +314,8 @@ static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val)
|
|||
*/
|
||||
do {
|
||||
if (!sprd_adi_fifo_is_full(sadi)) {
|
||||
writel_relaxed(val, (void __iomem *)reg);
|
||||
/* we need virtual register address to write. */
|
||||
writel_relaxed(val, (void __iomem *)(sadi->slave_vbase + reg));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -277,60 +338,41 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr,
|
|||
struct spi_transfer *t)
|
||||
{
|
||||
struct sprd_adi *sadi = spi_controller_get_devdata(ctlr);
|
||||
u32 phy_reg, val;
|
||||
u32 reg, val;
|
||||
int ret;
|
||||
|
||||
if (t->rx_buf) {
|
||||
phy_reg = *(u32 *)t->rx_buf + sadi->slave_pbase;
|
||||
|
||||
ret = sprd_adi_check_paddr(sadi, phy_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprd_adi_read(sadi, phy_reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = *(u32 *)t->rx_buf;
|
||||
ret = sprd_adi_read(sadi, reg, &val);
|
||||
*(u32 *)t->rx_buf = val;
|
||||
} else if (t->tx_buf) {
|
||||
u32 *p = (u32 *)t->tx_buf;
|
||||
|
||||
/*
|
||||
* Get the physical register address need to write and convert
|
||||
* the physical address to virtual address. Since we need
|
||||
* virtual register address to write.
|
||||
*/
|
||||
phy_reg = *p++ + sadi->slave_pbase;
|
||||
ret = sprd_adi_check_paddr(sadi, phy_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = *p++;
|
||||
val = *p;
|
||||
ret = sprd_adi_write(sadi, phy_reg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = sprd_adi_write(sadi, reg, val);
|
||||
} else {
|
||||
dev_err(sadi->dev, "no buffer for transfer\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi)
|
||||
static void sprd_adi_set_wdt_rst_mode(void *p)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SPRD_WATCHDOG)
|
||||
u32 val;
|
||||
struct sprd_adi *sadi = (struct sprd_adi *)p;
|
||||
|
||||
/* Set default watchdog reboot mode */
|
||||
sprd_adi_read(sadi, sadi->slave_pbase + PMIC_RST_STATUS, &val);
|
||||
/* Init watchdog reset mode */
|
||||
sprd_adi_read(sadi, PMIC_RST_STATUS, &val);
|
||||
val |= HWRST_STATUS_WATCHDOG;
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + PMIC_RST_STATUS, val);
|
||||
sprd_adi_write(sadi, PMIC_RST_STATUS, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sprd_adi_restart_handler(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
static int sprd_adi_restart(struct notifier_block *this, unsigned long mode,
|
||||
void *cmd, struct sprd_adi_wdg *wdg)
|
||||
{
|
||||
struct sprd_adi *sadi = container_of(this, struct sprd_adi,
|
||||
restart_handler);
|
||||
|
@ -366,40 +408,40 @@ static int sprd_adi_restart_handler(struct notifier_block *this,
|
|||
reboot_mode = HWRST_STATUS_NORMAL;
|
||||
|
||||
/* Record the reboot mode */
|
||||
sprd_adi_read(sadi, sadi->slave_pbase + PMIC_RST_STATUS, &val);
|
||||
sprd_adi_read(sadi, wdg->rst_sts, &val);
|
||||
val &= ~HWRST_STATUS_WATCHDOG;
|
||||
val |= reboot_mode;
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + PMIC_RST_STATUS, val);
|
||||
sprd_adi_write(sadi, wdg->rst_sts, val);
|
||||
|
||||
/* Enable the interface clock of the watchdog */
|
||||
sprd_adi_read(sadi, sadi->slave_pbase + PMIC_MODULE_EN, &val);
|
||||
sprd_adi_read(sadi, wdg->wdg_en, &val);
|
||||
val |= BIT_WDG_EN;
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + PMIC_MODULE_EN, val);
|
||||
sprd_adi_write(sadi, wdg->wdg_en, val);
|
||||
|
||||
/* Enable the work clock of the watchdog */
|
||||
sprd_adi_read(sadi, sadi->slave_pbase + PMIC_CLK_EN, &val);
|
||||
sprd_adi_read(sadi, wdg->wdg_clk, &val);
|
||||
val |= BIT_WDG_EN;
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + PMIC_CLK_EN, val);
|
||||
sprd_adi_write(sadi, wdg->wdg_clk, val);
|
||||
|
||||
/* Unlock the watchdog */
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOCK, WDG_UNLOCK_KEY);
|
||||
sprd_adi_write(sadi, wdg->base + REG_WDG_LOCK, WDG_UNLOCK_KEY);
|
||||
|
||||
sprd_adi_read(sadi, sadi->slave_pbase + REG_WDG_CTRL, &val);
|
||||
sprd_adi_read(sadi, wdg->base + REG_WDG_CTRL, &val);
|
||||
val |= BIT_WDG_NEW;
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_CTRL, val);
|
||||
sprd_adi_write(sadi, wdg->base + REG_WDG_CTRL, val);
|
||||
|
||||
/* Load the watchdog timeout value, 50ms is always enough. */
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOAD_HIGH, 0);
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOAD_LOW,
|
||||
sprd_adi_write(sadi, wdg->base + REG_WDG_LOAD_HIGH, 0);
|
||||
sprd_adi_write(sadi, wdg->base + REG_WDG_LOAD_LOW,
|
||||
WDG_LOAD_VAL & WDG_LOAD_MASK);
|
||||
|
||||
/* Start the watchdog to reset system */
|
||||
sprd_adi_read(sadi, sadi->slave_pbase + REG_WDG_CTRL, &val);
|
||||
sprd_adi_read(sadi, wdg->base + REG_WDG_CTRL, &val);
|
||||
val |= BIT_WDG_RUN | BIT_WDG_RST;
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_CTRL, val);
|
||||
sprd_adi_write(sadi, wdg->base + REG_WDG_CTRL, val);
|
||||
|
||||
/* Lock the watchdog */
|
||||
sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOCK, ~WDG_UNLOCK_KEY);
|
||||
sprd_adi_write(sadi, wdg->base + REG_WDG_LOCK, ~WDG_UNLOCK_KEY);
|
||||
|
||||
mdelay(1000);
|
||||
|
||||
|
@ -407,6 +449,19 @@ static int sprd_adi_restart_handler(struct notifier_block *this,
|
|||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int sprd_adi_restart_sc9860(struct notifier_block *this,
|
||||
unsigned long mode, void *cmd)
|
||||
{
|
||||
struct sprd_adi_wdg wdg = {
|
||||
.base = PMIC_WDG_BASE,
|
||||
.rst_sts = PMIC_RST_STATUS,
|
||||
.wdg_en = PMIC_MODULE_EN,
|
||||
.wdg_clk = PMIC_CLK_EN,
|
||||
};
|
||||
|
||||
return sprd_adi_restart(this, mode, cmd, &wdg);
|
||||
}
|
||||
|
||||
static void sprd_adi_hw_init(struct sprd_adi *sadi)
|
||||
{
|
||||
struct device_node *np = sadi->dev->of_node;
|
||||
|
@ -458,10 +513,11 @@ static void sprd_adi_hw_init(struct sprd_adi *sadi)
|
|||
static int sprd_adi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct sprd_adi_data *data;
|
||||
struct spi_controller *ctlr;
|
||||
struct sprd_adi *sadi;
|
||||
struct resource *res;
|
||||
u32 num_chipselect;
|
||||
u16 num_chipselect;
|
||||
int ret;
|
||||
|
||||
if (!np) {
|
||||
|
@ -469,6 +525,12 @@ static int sprd_adi_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
data = of_device_get_match_data(&pdev->dev);
|
||||
if (!data) {
|
||||
dev_err(&pdev->dev, "no matching driver data found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdev->id = of_alias_get_id(np, "spi");
|
||||
num_chipselect = of_get_child_count(np);
|
||||
|
||||
|
@ -486,10 +548,12 @@ static int sprd_adi_probe(struct platform_device *pdev)
|
|||
goto put_ctlr;
|
||||
}
|
||||
|
||||
sadi->slave_vbase = (unsigned long)sadi->base + ADI_SLAVE_OFFSET;
|
||||
sadi->slave_pbase = res->start + ADI_SLAVE_OFFSET;
|
||||
sadi->slave_vbase = (unsigned long)sadi->base +
|
||||
data->slave_offset;
|
||||
sadi->slave_pbase = res->start + data->slave_offset;
|
||||
sadi->ctlr = ctlr;
|
||||
sadi->dev = &pdev->dev;
|
||||
sadi->data = data;
|
||||
ret = of_hwspin_lock_get_id(np, 0);
|
||||
if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) {
|
||||
sadi->hwlock =
|
||||
|
@ -510,7 +574,9 @@ static int sprd_adi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
sprd_adi_hw_init(sadi);
|
||||
sprd_adi_set_wdt_rst_mode(sadi);
|
||||
|
||||
if (sadi->data->wdg_rst)
|
||||
sadi->data->wdg_rst(sadi);
|
||||
|
||||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
ctlr->bus_num = pdev->id;
|
||||
|
@ -525,12 +591,14 @@ static int sprd_adi_probe(struct platform_device *pdev)
|
|||
goto put_ctlr;
|
||||
}
|
||||
|
||||
sadi->restart_handler.notifier_call = sprd_adi_restart_handler;
|
||||
sadi->restart_handler.priority = 128;
|
||||
ret = register_restart_handler(&sadi->restart_handler);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can not register restart handler\n");
|
||||
goto put_ctlr;
|
||||
if (sadi->data->restart) {
|
||||
sadi->restart_handler.notifier_call = sadi->data->restart;
|
||||
sadi->restart_handler.priority = 128;
|
||||
ret = register_restart_handler(&sadi->restart_handler);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can not register restart handler\n");
|
||||
goto put_ctlr;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -549,9 +617,38 @@ static int sprd_adi_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct sprd_adi_data sc9860_data = {
|
||||
.slave_offset = ADI_10BIT_SLAVE_OFFSET,
|
||||
.slave_addr_size = ADI_10BIT_SLAVE_ADDR_SIZE,
|
||||
.read_check = sprd_adi_read_check_r2,
|
||||
.restart = sprd_adi_restart_sc9860,
|
||||
.wdg_rst = sprd_adi_set_wdt_rst_mode,
|
||||
};
|
||||
|
||||
static struct sprd_adi_data sc9863_data = {
|
||||
.slave_offset = ADI_12BIT_SLAVE_OFFSET,
|
||||
.slave_addr_size = ADI_12BIT_SLAVE_ADDR_SIZE,
|
||||
.read_check = sprd_adi_read_check_r3,
|
||||
};
|
||||
|
||||
static struct sprd_adi_data ums512_data = {
|
||||
.slave_offset = ADI_15BIT_SLAVE_OFFSET,
|
||||
.slave_addr_size = ADI_15BIT_SLAVE_ADDR_SIZE,
|
||||
.read_check = sprd_adi_read_check_r3,
|
||||
};
|
||||
|
||||
static const struct of_device_id sprd_adi_of_match[] = {
|
||||
{
|
||||
.compatible = "sprd,sc9860-adi",
|
||||
.data = &sc9860_data,
|
||||
},
|
||||
{
|
||||
.compatible = "sprd,sc9863-adi",
|
||||
.data = &sc9863_data,
|
||||
},
|
||||
{
|
||||
.compatible = "sprd,ums512-adi",
|
||||
.data = &ums512_data,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -162,6 +162,8 @@
|
|||
#define SPI_3WIRE_TX 3
|
||||
#define SPI_3WIRE_RX 4
|
||||
|
||||
#define STM32_SPI_AUTOSUSPEND_DELAY 1 /* 1 ms */
|
||||
|
||||
/*
|
||||
* use PIO for small transfers, avoiding DMA setup/teardown overhead for drivers
|
||||
* without fifo buffers.
|
||||
|
@ -568,29 +570,30 @@ static void stm32f4_spi_read_rx(struct stm32_spi *spi)
|
|||
/**
|
||||
* stm32h7_spi_read_rxfifo - Read bytes in Receive Data Register
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @flush: boolean indicating that FIFO should be flushed
|
||||
*
|
||||
* Write in rx_buf depends on remaining bytes to avoid to write beyond
|
||||
* rx_buf end.
|
||||
*/
|
||||
static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi, bool flush)
|
||||
static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi)
|
||||
{
|
||||
u32 sr = readl_relaxed(spi->base + STM32H7_SPI_SR);
|
||||
u32 rxplvl = FIELD_GET(STM32H7_SPI_SR_RXPLVL, sr);
|
||||
|
||||
while ((spi->rx_len > 0) &&
|
||||
((sr & STM32H7_SPI_SR_RXP) ||
|
||||
(flush && ((sr & STM32H7_SPI_SR_RXWNE) || (rxplvl > 0))))) {
|
||||
((sr & STM32H7_SPI_SR_EOT) &&
|
||||
((sr & STM32H7_SPI_SR_RXWNE) || (rxplvl > 0))))) {
|
||||
u32 offs = spi->cur_xferlen - spi->rx_len;
|
||||
|
||||
if ((spi->rx_len >= sizeof(u32)) ||
|
||||
(flush && (sr & STM32H7_SPI_SR_RXWNE))) {
|
||||
(sr & STM32H7_SPI_SR_RXWNE)) {
|
||||
u32 *rx_buf32 = (u32 *)(spi->rx_buf + offs);
|
||||
|
||||
*rx_buf32 = readl_relaxed(spi->base + STM32H7_SPI_RXDR);
|
||||
spi->rx_len -= sizeof(u32);
|
||||
} else if ((spi->rx_len >= sizeof(u16)) ||
|
||||
(flush && (rxplvl >= 2 || spi->cur_bpw > 8))) {
|
||||
(!(sr & STM32H7_SPI_SR_RXWNE) &&
|
||||
(rxplvl >= 2 || spi->cur_bpw > 8))) {
|
||||
u16 *rx_buf16 = (u16 *)(spi->rx_buf + offs);
|
||||
|
||||
*rx_buf16 = readw_relaxed(spi->base + STM32H7_SPI_RXDR);
|
||||
|
@ -606,8 +609,8 @@ static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi, bool flush)
|
|||
rxplvl = FIELD_GET(STM32H7_SPI_SR_RXPLVL, sr);
|
||||
}
|
||||
|
||||
dev_dbg(spi->dev, "%s%s: %d bytes left\n", __func__,
|
||||
flush ? "(flush)" : "", spi->rx_len);
|
||||
dev_dbg(spi->dev, "%s: %d bytes left (sr=%08x)\n",
|
||||
__func__, spi->rx_len, sr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -674,18 +677,12 @@ static void stm32f4_spi_disable(struct stm32_spi *spi)
|
|||
* stm32h7_spi_disable - Disable SPI controller
|
||||
* @spi: pointer to the spi controller data structure
|
||||
*
|
||||
* RX-Fifo is flushed when SPI controller is disabled. To prevent any data
|
||||
* loss, use stm32h7_spi_read_rxfifo(flush) to read the remaining bytes in
|
||||
* RX-Fifo.
|
||||
* Normally, if TSIZE has been configured, we should relax the hardware at the
|
||||
* reception of the EOT interrupt. But in case of error, EOT will not be
|
||||
* raised. So the subsystem unprepare_message call allows us to properly
|
||||
* complete the transfer from an hardware point of view.
|
||||
* RX-Fifo is flushed when SPI controller is disabled.
|
||||
*/
|
||||
static void stm32h7_spi_disable(struct stm32_spi *spi)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 cr1, sr;
|
||||
u32 cr1;
|
||||
|
||||
dev_dbg(spi->dev, "disable controller\n");
|
||||
|
||||
|
@ -698,25 +695,6 @@ static void stm32h7_spi_disable(struct stm32_spi *spi)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Wait on EOT or suspend the flow */
|
||||
if (readl_relaxed_poll_timeout_atomic(spi->base + STM32H7_SPI_SR,
|
||||
sr, !(sr & STM32H7_SPI_SR_EOT),
|
||||
10, 100000) < 0) {
|
||||
if (cr1 & STM32H7_SPI_CR1_CSTART) {
|
||||
writel_relaxed(cr1 | STM32H7_SPI_CR1_CSUSP,
|
||||
spi->base + STM32H7_SPI_CR1);
|
||||
if (readl_relaxed_poll_timeout_atomic(
|
||||
spi->base + STM32H7_SPI_SR,
|
||||
sr, !(sr & STM32H7_SPI_SR_SUSP),
|
||||
10, 100000) < 0)
|
||||
dev_warn(spi->dev,
|
||||
"Suspend request timeout\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!spi->cur_usedma && spi->rx_buf && (spi->rx_len > 0))
|
||||
stm32h7_spi_read_rxfifo(spi, true);
|
||||
|
||||
if (spi->cur_usedma && spi->dma_tx)
|
||||
dmaengine_terminate_all(spi->dma_tx);
|
||||
if (spi->cur_usedma && spi->dma_rx)
|
||||
|
@ -911,7 +889,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
|||
if (__ratelimit(&rs))
|
||||
dev_dbg_ratelimited(spi->dev, "Communication suspended\n");
|
||||
if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0)))
|
||||
stm32h7_spi_read_rxfifo(spi, false);
|
||||
stm32h7_spi_read_rxfifo(spi);
|
||||
/*
|
||||
* If communication is suspended while using DMA, it means
|
||||
* that something went wrong, so stop the current transfer
|
||||
|
@ -932,8 +910,10 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
|||
|
||||
if (sr & STM32H7_SPI_SR_EOT) {
|
||||
if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0)))
|
||||
stm32h7_spi_read_rxfifo(spi, true);
|
||||
end = true;
|
||||
stm32h7_spi_read_rxfifo(spi);
|
||||
if (!spi->cur_usedma ||
|
||||
(spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX))
|
||||
end = true;
|
||||
}
|
||||
|
||||
if (sr & STM32H7_SPI_SR_TXP)
|
||||
|
@ -942,7 +922,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
|||
|
||||
if (sr & STM32H7_SPI_SR_RXP)
|
||||
if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0)))
|
||||
stm32h7_spi_read_rxfifo(spi, false);
|
||||
stm32h7_spi_read_rxfifo(spi);
|
||||
|
||||
writel_relaxed(sr & mask, spi->base + STM32H7_SPI_IFCR);
|
||||
|
||||
|
@ -1041,42 +1021,17 @@ static void stm32f4_spi_dma_tx_cb(void *data)
|
|||
}
|
||||
|
||||
/**
|
||||
* stm32f4_spi_dma_rx_cb - dma callback
|
||||
* stm32_spi_dma_rx_cb - dma callback
|
||||
* @data: pointer to the spi controller data structure
|
||||
*
|
||||
* DMA callback is called when the transfer is complete for DMA RX channel.
|
||||
*/
|
||||
static void stm32f4_spi_dma_rx_cb(void *data)
|
||||
static void stm32_spi_dma_rx_cb(void *data)
|
||||
{
|
||||
struct stm32_spi *spi = data;
|
||||
|
||||
spi_finalize_current_transfer(spi->master);
|
||||
stm32f4_spi_disable(spi);
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32h7_spi_dma_cb - dma callback
|
||||
* @data: pointer to the spi controller data structure
|
||||
*
|
||||
* DMA callback is called when the transfer is complete or when an error
|
||||
* occurs. If the transfer is complete, EOT flag is raised.
|
||||
*/
|
||||
static void stm32h7_spi_dma_cb(void *data)
|
||||
{
|
||||
struct stm32_spi *spi = data;
|
||||
unsigned long flags;
|
||||
u32 sr;
|
||||
|
||||
spin_lock_irqsave(&spi->lock, flags);
|
||||
|
||||
sr = readl_relaxed(spi->base + STM32H7_SPI_SR);
|
||||
|
||||
spin_unlock_irqrestore(&spi->lock, flags);
|
||||
|
||||
if (!(sr & STM32H7_SPI_SR_EOT))
|
||||
dev_warn(spi->dev, "DMA error (sr=0x%08x)\n", sr);
|
||||
|
||||
/* Now wait for EOT, or SUSP or OVR in case of error */
|
||||
spi->cfg->disable(spi);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1242,11 +1197,13 @@ static void stm32f4_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
|||
*/
|
||||
static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
||||
{
|
||||
/* Enable the interrupts relative to the end of transfer */
|
||||
stm32_spi_set_bits(spi, STM32H7_SPI_IER, STM32H7_SPI_IER_EOTIE |
|
||||
STM32H7_SPI_IER_TXTFIE |
|
||||
STM32H7_SPI_IER_OVRIE |
|
||||
STM32H7_SPI_IER_MODFIE);
|
||||
uint32_t ier = STM32H7_SPI_IER_OVRIE | STM32H7_SPI_IER_MODFIE;
|
||||
|
||||
/* Enable the interrupts */
|
||||
if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX)
|
||||
ier |= STM32H7_SPI_IER_EOTIE | STM32H7_SPI_IER_TXTFIE;
|
||||
|
||||
stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier);
|
||||
|
||||
stm32_spi_enable(spi);
|
||||
|
||||
|
@ -1645,10 +1602,6 @@ static int stm32_spi_transfer_one(struct spi_master *master,
|
|||
struct stm32_spi *spi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
/* Don't do anything on 0 bytes transfers */
|
||||
if (transfer->len == 0)
|
||||
return 0;
|
||||
|
||||
spi->tx_buf = transfer->tx_buf;
|
||||
spi->rx_buf = transfer->rx_buf;
|
||||
spi->tx_len = spi->tx_buf ? transfer->len : 0;
|
||||
|
@ -1762,7 +1715,7 @@ static const struct stm32_spi_cfg stm32f4_spi_cfg = {
|
|||
.set_mode = stm32f4_spi_set_mode,
|
||||
.transfer_one_dma_start = stm32f4_spi_transfer_one_dma_start,
|
||||
.dma_tx_cb = stm32f4_spi_dma_tx_cb,
|
||||
.dma_rx_cb = stm32f4_spi_dma_rx_cb,
|
||||
.dma_rx_cb = stm32_spi_dma_rx_cb,
|
||||
.transfer_one_irq = stm32f4_spi_transfer_one_irq,
|
||||
.irq_handler_event = stm32f4_spi_irq_event,
|
||||
.irq_handler_thread = stm32f4_spi_irq_thread,
|
||||
|
@ -1782,8 +1735,11 @@ static const struct stm32_spi_cfg stm32h7_spi_cfg = {
|
|||
.set_data_idleness = stm32h7_spi_data_idleness,
|
||||
.set_number_of_data = stm32h7_spi_number_of_data,
|
||||
.transfer_one_dma_start = stm32h7_spi_transfer_one_dma_start,
|
||||
.dma_rx_cb = stm32h7_spi_dma_cb,
|
||||
.dma_tx_cb = stm32h7_spi_dma_cb,
|
||||
.dma_rx_cb = stm32_spi_dma_rx_cb,
|
||||
/*
|
||||
* dma_tx_cb is not necessary since in case of TX, dma is followed by
|
||||
* SPI access hence handling is performed within the SPI interrupt
|
||||
*/
|
||||
.transfer_one_irq = stm32h7_spi_transfer_one_irq,
|
||||
.irq_handler_thread = stm32h7_spi_irq_thread,
|
||||
.baud_rate_div_min = STM32H7_SPI_MBR_DIV_MIN,
|
||||
|
@ -1927,6 +1883,9 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
|||
if (spi->dma_tx || spi->dma_rx)
|
||||
master->can_dma = stm32_spi_can_dma;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev,
|
||||
STM32_SPI_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
@ -1938,6 +1897,9 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
|||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
dev_info(&pdev->dev, "driver initialized\n");
|
||||
|
||||
return 0;
|
||||
|
@ -1946,6 +1908,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
|||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
err_dma_release:
|
||||
if (spi->dma_tx)
|
||||
dma_release_channel(spi->dma_tx);
|
||||
|
@ -1970,6 +1933,8 @@ static int stm32_spi_remove(struct platform_device *pdev)
|
|||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
||||
if (master->dma_tx)
|
||||
dma_release_channel(master->dma_tx);
|
||||
if (master->dma_rx)
|
||||
|
|
|
@ -717,12 +717,12 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi,
|
|||
dma_release_channel(dma_chan);
|
||||
}
|
||||
|
||||
static int tegra_spi_set_hw_cs_timing(struct spi_device *spi,
|
||||
struct spi_delay *setup,
|
||||
struct spi_delay *hold,
|
||||
struct spi_delay *inactive)
|
||||
static int tegra_spi_set_hw_cs_timing(struct spi_device *spi)
|
||||
{
|
||||
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
|
||||
struct spi_delay *setup = &spi->cs_setup;
|
||||
struct spi_delay *hold = &spi->cs_hold;
|
||||
struct spi_delay *inactive = &spi->cs_inactive;
|
||||
u8 setup_dly, hold_dly, inactive_dly;
|
||||
u32 setup_hold;
|
||||
u32 spi_cs_timing;
|
||||
|
|
|
@ -1061,33 +1061,12 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "Can not get clock %d\n", ret);
|
||||
goto exit_free_master;
|
||||
}
|
||||
ret = clk_prepare(tspi->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Clock prepare failed %d\n", ret);
|
||||
goto exit_free_master;
|
||||
}
|
||||
ret = clk_enable(tspi->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Clock enable failed %d\n", ret);
|
||||
goto exit_clk_unprepare;
|
||||
}
|
||||
|
||||
spi_irq = platform_get_irq(pdev, 0);
|
||||
tspi->irq = spi_irq;
|
||||
ret = request_threaded_irq(tspi->irq, tegra_slink_isr,
|
||||
tegra_slink_isr_thread, IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), tspi);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
|
||||
tspi->irq);
|
||||
goto exit_clk_disable;
|
||||
}
|
||||
|
||||
tspi->rst = devm_reset_control_get_exclusive(&pdev->dev, "spi");
|
||||
if (IS_ERR(tspi->rst)) {
|
||||
dev_err(&pdev->dev, "can not get reset\n");
|
||||
ret = PTR_ERR(tspi->rst);
|
||||
goto exit_free_irq;
|
||||
goto exit_free_master;
|
||||
}
|
||||
|
||||
tspi->max_buf_size = SLINK_FIFO_DEPTH << 2;
|
||||
|
@ -1095,7 +1074,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
|||
|
||||
ret = tegra_slink_init_dma_param(tspi, true);
|
||||
if (ret < 0)
|
||||
goto exit_free_irq;
|
||||
goto exit_free_master;
|
||||
ret = tegra_slink_init_dma_param(tspi, false);
|
||||
if (ret < 0)
|
||||
goto exit_rx_dma_free;
|
||||
|
@ -1106,16 +1085,9 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
|||
init_completion(&tspi->xfer_completion);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
if (!pm_runtime_enabled(&pdev->dev)) {
|
||||
ret = tegra_slink_runtime_resume(&pdev->dev);
|
||||
if (ret)
|
||||
goto exit_pm_disable;
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
goto exit_pm_disable;
|
||||
}
|
||||
|
||||
|
@ -1123,33 +1095,43 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
|||
udelay(2);
|
||||
reset_control_deassert(tspi->rst);
|
||||
|
||||
spi_irq = platform_get_irq(pdev, 0);
|
||||
tspi->irq = spi_irq;
|
||||
ret = request_threaded_irq(tspi->irq, tegra_slink_isr,
|
||||
tegra_slink_isr_thread, IRQF_ONESHOT,
|
||||
dev_name(&pdev->dev), tspi);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
|
||||
tspi->irq);
|
||||
goto exit_pm_put;
|
||||
}
|
||||
|
||||
tspi->def_command_reg = SLINK_M_S;
|
||||
tspi->def_command2_reg = SLINK_CS_ACTIVE_BETWEEN;
|
||||
tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
|
||||
tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
ret = spi_register_master(master);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "can not register to master err %d\n", ret);
|
||||
goto exit_pm_disable;
|
||||
goto exit_free_irq;
|
||||
}
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
|
||||
exit_free_irq:
|
||||
free_irq(spi_irq, tspi);
|
||||
exit_pm_put:
|
||||
pm_runtime_put(&pdev->dev);
|
||||
exit_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
tegra_slink_runtime_suspend(&pdev->dev);
|
||||
|
||||
tegra_slink_deinit_dma_param(tspi, false);
|
||||
exit_rx_dma_free:
|
||||
tegra_slink_deinit_dma_param(tspi, true);
|
||||
exit_free_irq:
|
||||
free_irq(spi_irq, tspi);
|
||||
exit_clk_disable:
|
||||
clk_disable(tspi->clk);
|
||||
exit_clk_unprepare:
|
||||
clk_unprepare(tspi->clk);
|
||||
exit_free_master:
|
||||
spi_master_put(master);
|
||||
return ret;
|
||||
|
@ -1160,10 +1142,11 @@ static int tegra_slink_remove(struct platform_device *pdev)
|
|||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct tegra_slink_data *tspi = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
|
||||
free_irq(tspi->irq, tspi);
|
||||
|
||||
clk_disable(tspi->clk);
|
||||
clk_unprepare(tspi->clk);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (tspi->tx_dma_chan)
|
||||
tegra_slink_deinit_dma_param(tspi, false);
|
||||
|
@ -1171,10 +1154,6 @@ static int tegra_slink_remove(struct platform_device *pdev)
|
|||
if (tspi->rx_dma_chan)
|
||||
tegra_slink_deinit_dma_param(tspi, true);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
tegra_slink_runtime_suspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -545,7 +545,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
|
|||
zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
|
||||
zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
|
||||
ZYNQ_QSPI_IXR_RXTX_MASK);
|
||||
if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion,
|
||||
if (!wait_for_completion_timeout(&xqspi->data_completion,
|
||||
msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
}
|
||||
|
@ -563,7 +563,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
|
|||
zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
|
||||
zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
|
||||
ZYNQ_QSPI_IXR_RXTX_MASK);
|
||||
if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion,
|
||||
if (!wait_for_completion_timeout(&xqspi->data_completion,
|
||||
msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
}
|
||||
|
@ -579,7 +579,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
|
|||
zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
|
||||
zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
|
||||
ZYNQ_QSPI_IXR_RXTX_MASK);
|
||||
if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion,
|
||||
if (!wait_for_completion_timeout(&xqspi->data_completion,
|
||||
msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
|
||||
|
@ -603,7 +603,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
|
|||
zynq_qspi_write_op(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
|
||||
zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
|
||||
ZYNQ_QSPI_IXR_RXTX_MASK);
|
||||
if (!wait_for_completion_interruptible_timeout(&xqspi->data_completion,
|
||||
if (!wait_for_completion_timeout(&xqspi->data_completion,
|
||||
msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
}
|
||||
|
|
|
@ -846,9 +846,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
|||
if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) ||
|
||||
!spi->controller->set_cs_timing) {
|
||||
if (activate)
|
||||
spi_delay_exec(&spi->controller->cs_setup, NULL);
|
||||
spi_delay_exec(&spi->cs_setup, NULL);
|
||||
else
|
||||
spi_delay_exec(&spi->controller->cs_hold, NULL);
|
||||
spi_delay_exec(&spi->cs_hold, NULL);
|
||||
}
|
||||
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
|
@ -891,7 +891,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
|||
if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) ||
|
||||
!spi->controller->set_cs_timing) {
|
||||
if (!activate)
|
||||
spi_delay_exec(&spi->controller->cs_inactive, NULL);
|
||||
spi_delay_exec(&spi->cs_inactive, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2059,7 +2059,7 @@ static void restore_cur(struct vc_data *vc)
|
|||
|
||||
enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey,
|
||||
EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd,
|
||||
ESpalette, ESosc };
|
||||
ESpalette, ESosc, ESapc, ESpm, ESdcs };
|
||||
|
||||
/* console_lock is held (except via vc_init()) */
|
||||
static void reset_terminal(struct vc_data *vc, int do_clear)
|
||||
|
@ -2133,20 +2133,28 @@ static void vc_setGx(struct vc_data *vc, unsigned int which, int c)
|
|||
vc->vc_translate = set_translate(*charset, vc);
|
||||
}
|
||||
|
||||
/* is this state an ANSI control string? */
|
||||
static bool ansi_control_string(unsigned int state)
|
||||
{
|
||||
if (state == ESosc || state == ESapc || state == ESpm || state == ESdcs)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* console_lock is held */
|
||||
static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
|
||||
{
|
||||
/*
|
||||
* Control characters can be used in the _middle_
|
||||
* of an escape sequence.
|
||||
* of an escape sequence, aside from ANSI control strings.
|
||||
*/
|
||||
if (vc->vc_state == ESosc && c>=8 && c<=13) /* ... except for OSC */
|
||||
if (ansi_control_string(vc->vc_state) && c >= 8 && c <= 13)
|
||||
return;
|
||||
switch (c) {
|
||||
case 0:
|
||||
return;
|
||||
case 7:
|
||||
if (vc->vc_state == ESosc)
|
||||
if (ansi_control_string(vc->vc_state))
|
||||
vc->vc_state = ESnormal;
|
||||
else if (vc->vc_bell_duration)
|
||||
kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration);
|
||||
|
@ -2207,6 +2215,12 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
|
|||
case ']':
|
||||
vc->vc_state = ESnonstd;
|
||||
return;
|
||||
case '_':
|
||||
vc->vc_state = ESapc;
|
||||
return;
|
||||
case '^':
|
||||
vc->vc_state = ESpm;
|
||||
return;
|
||||
case '%':
|
||||
vc->vc_state = ESpercent;
|
||||
return;
|
||||
|
@ -2224,6 +2238,9 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
|
|||
if (vc->state.x < VC_TABSTOPS_COUNT)
|
||||
set_bit(vc->state.x, vc->vc_tab_stop);
|
||||
return;
|
||||
case 'P':
|
||||
vc->vc_state = ESdcs;
|
||||
return;
|
||||
case 'Z':
|
||||
respond_ID(tty);
|
||||
return;
|
||||
|
@ -2520,8 +2537,14 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
|
|||
vc_setGx(vc, 1, c);
|
||||
vc->vc_state = ESnormal;
|
||||
return;
|
||||
case ESapc:
|
||||
return;
|
||||
case ESosc:
|
||||
return;
|
||||
case ESpm:
|
||||
return;
|
||||
case ESdcs:
|
||||
return;
|
||||
default:
|
||||
vc->vc_state = ESnormal;
|
||||
}
|
||||
|
|
|
@ -246,6 +246,8 @@ int vt_waitactive(int n)
|
|||
*
|
||||
* XXX It should at least call into the driver, fbdev's definitely need to
|
||||
* restore their engine state. --BenH
|
||||
*
|
||||
* Called with the console lock held.
|
||||
*/
|
||||
static int vt_kdsetmode(struct vc_data *vc, unsigned long mode)
|
||||
{
|
||||
|
@ -262,7 +264,6 @@ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME: this needs the console lock extending */
|
||||
if (vc->vc_mode == mode)
|
||||
return 0;
|
||||
|
||||
|
@ -271,12 +272,10 @@ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode)
|
|||
return 0;
|
||||
|
||||
/* explicitly blank/unblank the screen if switching modes */
|
||||
console_lock();
|
||||
if (mode == KD_TEXT)
|
||||
do_unblank_screen(1);
|
||||
else
|
||||
do_blank_screen(1);
|
||||
console_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -378,7 +377,10 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
|
|||
if (!perm)
|
||||
return -EPERM;
|
||||
|
||||
return vt_kdsetmode(vc, arg);
|
||||
console_lock();
|
||||
ret = vt_kdsetmode(vc, arg);
|
||||
console_unlock();
|
||||
return ret;
|
||||
|
||||
case KDGETMODE:
|
||||
return put_user(vc->vc_mode, (int __user *)arg);
|
||||
|
|
|
@ -1395,9 +1395,11 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf)
|
|||
ret = VM_FAULT_SIGBUS;
|
||||
} else {
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct page *page = find_or_create_page(mapping, 0,
|
||||
mapping_gfp_constraint(mapping,
|
||||
~__GFP_FS));
|
||||
struct page *page;
|
||||
|
||||
filemap_invalidate_lock_shared(mapping);
|
||||
page = find_or_create_page(mapping, 0,
|
||||
mapping_gfp_constraint(mapping, ~__GFP_FS));
|
||||
if (!page) {
|
||||
ret = VM_FAULT_OOM;
|
||||
goto out_inline;
|
||||
|
@ -1418,6 +1420,7 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf)
|
|||
vmf->page = page;
|
||||
ret = VM_FAULT_MAJOR | VM_FAULT_LOCKED;
|
||||
out_inline:
|
||||
filemap_invalidate_unlock_shared(mapping);
|
||||
dout("filemap_fault %p %llu read inline data ret %x\n",
|
||||
inode, off, ret);
|
||||
}
|
||||
|
|
|
@ -2088,6 +2088,7 @@ static long ceph_fallocate(struct file *file, int mode,
|
|||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
ceph_zero_pagecache_range(inode, offset, length);
|
||||
ret = ceph_zero_objects(inode, offset, length);
|
||||
|
||||
|
@ -2100,6 +2101,7 @@ static long ceph_fallocate(struct file *file, int mode,
|
|||
if (dirty)
|
||||
__mark_inode_dirty(inode, dirty);
|
||||
}
|
||||
filemap_invalidate_unlock(inode->i_mapping);
|
||||
|
||||
ceph_put_cap_refs(ci, got);
|
||||
unlock:
|
||||
|
|
|
@ -3590,6 +3590,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
|
|||
return rc;
|
||||
}
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
/*
|
||||
* We implement the punch hole through ioctl, so we need remove the page
|
||||
* caches first, otherwise the data may be inconsistent with the server.
|
||||
|
@ -3607,6 +3608,7 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
|
|||
sizeof(struct file_zero_data_information),
|
||||
CIFSMaxBufSize, NULL, NULL);
|
||||
free_xid(xid);
|
||||
filemap_invalidate_unlock(inode->i_mapping);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config EXT2_FS
|
||||
tristate "Second extended fs support"
|
||||
select FS_IOMAP
|
||||
help
|
||||
Ext2 is a standard Linux file system for hard disks.
|
||||
|
||||
|
|
|
@ -667,9 +667,6 @@ struct ext2_inode_info {
|
|||
struct rw_semaphore xattr_sem;
|
||||
#endif
|
||||
rwlock_t i_meta_lock;
|
||||
#ifdef CONFIG_FS_DAX
|
||||
struct rw_semaphore dax_sem;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* truncate_mutex is for serialising ext2_truncate() against
|
||||
|
@ -685,14 +682,6 @@ struct ext2_inode_info {
|
|||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FS_DAX
|
||||
#define dax_sem_down_write(ext2_inode) down_write(&(ext2_inode)->dax_sem)
|
||||
#define dax_sem_up_write(ext2_inode) up_write(&(ext2_inode)->dax_sem)
|
||||
#else
|
||||
#define dax_sem_down_write(ext2_inode)
|
||||
#define dax_sem_up_write(ext2_inode)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Inode dynamic state flags
|
||||
*/
|
||||
|
|
|
@ -81,7 +81,7 @@ static ssize_t ext2_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
*
|
||||
* mmap_lock (MM)
|
||||
* sb_start_pagefault (vfs, freeze)
|
||||
* ext2_inode_info->dax_sem
|
||||
* address_space->invalidate_lock
|
||||
* address_space->i_mmap_rwsem or page_lock (mutually exclusive in DAX)
|
||||
* ext2_inode_info->truncate_mutex
|
||||
*
|
||||
|
@ -91,7 +91,6 @@ static ssize_t ext2_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
static vm_fault_t ext2_dax_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||
struct ext2_inode_info *ei = EXT2_I(inode);
|
||||
vm_fault_t ret;
|
||||
bool write = (vmf->flags & FAULT_FLAG_WRITE) &&
|
||||
(vmf->vma->vm_flags & VM_SHARED);
|
||||
|
@ -100,11 +99,11 @@ static vm_fault_t ext2_dax_fault(struct vm_fault *vmf)
|
|||
sb_start_pagefault(inode->i_sb);
|
||||
file_update_time(vmf->vma->vm_file);
|
||||
}
|
||||
down_read(&ei->dax_sem);
|
||||
filemap_invalidate_lock_shared(inode->i_mapping);
|
||||
|
||||
ret = dax_iomap_fault(vmf, PE_SIZE_PTE, NULL, NULL, &ext2_iomap_ops);
|
||||
|
||||
up_read(&ei->dax_sem);
|
||||
filemap_invalidate_unlock_shared(inode->i_mapping);
|
||||
if (write)
|
||||
sb_end_pagefault(inode->i_sb);
|
||||
return ret;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue