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:
Greg Kroah-Hartman 2021-09-02 09:42:16 +02:00
commit 7c647e3d19
174 changed files with 6783 additions and 2779 deletions

View File

@ -19,7 +19,6 @@ properties:
compatible:
enum:
- ibm,fsi2spi
- ibm,fsi2spi-restricted
reg:
items:

View File

@ -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

View File

@ -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

View File

@ -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>;
};
};
...

View File

@ -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;
};
};
};

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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>;
};
};
};
};
};

View File

@ -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;
};
};
};
};

View File

@ -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>;
};
};

View File

@ -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>;
};
...
};

View File

@ -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";
};

View File

@ -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";
};

View File

@ -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>;
};
};
...

View File

@ -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

View File

@ -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>;
};

View File

@ -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>;
};
};
...

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -15,7 +15,7 @@
struct regmap_mmio_context {
void __iomem *regs;
unsigned val_bytes;
unsigned int val_bytes;
bool relaxed_mmio;
bool attached_clk;

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View File

@ -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]);

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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, &regval);
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, &regval);
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, &regval);
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");

View File

@ -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",

View File

@ -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);

View File

@ -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;

View File

@ -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:

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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, &reg, &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, &reg, &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.

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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),

View File

@ -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

View File

@ -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),
};

View File

@ -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, &regval);
@ -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,

View File

@ -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:

View File

@ -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,

View File

@ -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];

View File

@ -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,
&regulator_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");

View File

@ -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,
&reg_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");

View File

@ -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);
}

View File

@ -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[] = {

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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>");

View File

@ -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,
},
{ },
};

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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.

View File

@ -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
*/

View File

@ -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