- Core Frameworks

- Add support for Software Nodes to MFD Core
    - Remove support for Device Properties from MFD Core
    - Use standard APIs in MFD Core
 
  - New Drivers
    - Add support for ROHM BD9576MUF and BD9573MUF PMICs
    - Add support for Netronix Embedded Controller, PWM and RTC
    - Add support for Actions Semi ATC260x PMICs and OnKey
 
  - New Device Support
    - Add support for DG1 PCIe Graphics Card to Intel PMT
    - Add support for ROHM BD71815 PMIC to ROHM BD71828
    - Add support for Tolino Shine 2 HD to Netronix Embedded Controller
    - Add support for AX10 BMC Secure Updates to Intel M10 BMC
 
  - Removed Device Support
    - Remove Arizona Extcon support from MFD
    - Remove ST-E AB8500 Power Supply code from MFD
    - Remove AB3100 altogether
 
  - New Functionality
    - Add support for SMBus and I2C modes to Dialog DA9063
    - Switch to using Software Nodes in Intel (various)
 
  - New/converted Device Tree bindings; rohm,bd71815-pmic, rohm,bd9576-pmic,
                                        netronix,ntxec, actions,atc260x,
 				       ricoh,rn5t618, qcom-pm8xxx
 
 - Fix-ups
    - Fix error handling/path; intel_pmt
    - Simplify code; rohm-bd718x7, ab8500-core, intel-m10-bmc
    - Trivial clean-ups (reordering, spelling); rohm-generic, rn5t618, max8997
    - Use correct data-type; db8500-prcmu
    - Remove superfluous code; lp87565, intel_quark_i2c_gpi, lpc_sch, twl
    - Use generic APIs/defines; lm3533-core, intel_quark_i2c_gpio
    - Regmap related fix-ups; intel-m10-bmc, sec-core
    - Reorder resource freeing during remove; intel_quark_i2c_gpio
    - Make table indexing more robust; intel_quark_i2c_gpio
    - Fix reference imbalances; arizona-irq
    - Staticify and (un)constify things; arizona-spi, stmpe, ene-kb3930,
                                         intel-lpss-acpi, intel-lpss-pci,
                                         atc260x-i2c, intel_quark_i2c_gpio
 
  - Bug Fixes
    - Fix incorrect (register) values; intel-m10-bmc
    - Kconfig related fixes; ABX500_CORE
    - Do not clear the Auto Reload Register; stm32-timers
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmCJIPEACgkQUa+KL4f8
 d2FRZA//Xu9f8u2uLuIfuhxIjUUXOqIjRAFnkhKlgGZhKsY8BohjQ80Tj9yp6UKy
 St6ABwACO0hJap4zL4FxPW9+HXTmqZvAibnvHnvZdYSQ3ai6x9h6kTNvhSNLeRQU
 fuY7eN8kpAHHiHNKNJCsQLQMvcIyP7+0KAP6qir5GYsMjiXspWq7THUnfBi2JXC6
 y60guDo9XrgmQTO+pB870UJrKLM/h+iiohNRGxLFlShKhFCgbTB/wyw6bFeKy1SB
 0/6XuY6fOt1IQyBDuzw383Q2faMWO9U+es29bwvFxdqJDK0MHQXC47zBba2q94wL
 /9i/HSoz9dRHnTJNYUKWsVcPv4T84w/Iq7scyDvE00ubehJ+oo/M7Au3M6Tt3M1/
 6lBAwFYXiwhQnp9EP3nwPwgJF6JzX1IGuMOsUAqrVFOEMuIkZKbRdUlatUhqepJT
 spV4/TOfztAhY/7BzEOZLnF8cFNjmL5sn42/UzSRW708V5SxuTNsS48KJ4l0c7Er
 CZSTlR/T1rKkWqf7ejaS2TNqMCdYyB3vZW0quDxZTHTZHv9huNUvtbKPR7jmd+4p
 mrMIik7EE4BzC5m8tBPnXXZl+Og0keeYv4LUDBuLDX1agrxYIErl4ITvQTqqMfX1
 Jt14SIjSO56iu2ngQuvGWwegVK4/urO2kBJKUAH1QN1OocNaajQ=
 =IJSL
 -----END PGP SIGNATURE-----

Merge tag 'mfd-next-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd

Pull MFD updates from Lee Jones:
 "Core Framework:
   - Add support for Software Nodes to MFD Core
   - Remove support for Device Properties from MFD Core
   - Use standard APIs in MFD Core

  New Drivers:
   - Add support for ROHM BD9576MUF and BD9573MUF PMICs
   - Add support for Netronix Embedded Controller, PWM and RTC
   - Add support for Actions Semi ATC260x PMICs and OnKey

  New Device Support:
   - Add support for DG1 PCIe Graphics Card to Intel PMT
   - Add support for ROHM BD71815 PMIC to ROHM BD71828
   - Add support for Tolino Shine 2 HD to Netronix Embedded Controller
   - Add support for AX10 BMC Secure Updates to Intel M10 BMC

  Removed Device Support:
   - Remove Arizona Extcon support from MFD
   - Remove ST-E AB8500 Power Supply code from MFD
   - Remove AB3100 altogether

  New Functionality:
   - Add support for SMBus and I2C modes to Dialog DA9063
   - Switch to using Software Nodes in Intel (various)

  New/converted Device Tree bindings:
   - rohm bd71815-pmic, rohm bd9576-pmic, netronix ntxec, actions
     atc260x, ricoh rn5t618, qcom pm8xxx

- Fix-ups:
   - Fix error handling/path; intel_pmt
   - Simplify code; rohm-bd718x7, ab8500-core, intel-m10-bmc
   - Trivial clean-ups (reordering, spelling); rohm-generic, rn5t618,
     max8997
   - Use correct data-type; db8500-prcmu
   - Remove superfluous code; lp87565, intel_quark_i2c_gpi, lpc_sch, twl
   - Use generic APIs/defines; lm3533-core, intel_quark_i2c_gpio
   - Regmap related fix-ups; intel-m10-bmc, sec-core
   - Reorder resource freeing during remove; intel_quark_i2c_gpio
   - Make table indexing more robust; intel_quark_i2c_gpio
   - Fix reference imbalances; arizona-irq
   - Staticify and (un)constify things; arizona-spi, stmpe, ene-kb3930,
     intel-lpss-acpi, intel-lpss-pci, atc260x-i2c, intel_quark_i2c_gpio

  Bug Fixes:
   - Fix incorrect (register) values; intel-m10-bmc
   - Kconfig related fixes; ABX500_CORE
   - Do not clear the Auto Reload Register; stm32-timers"

* tag 'mfd-next-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (84 commits)
  mfd: intel-m10-bmc: Add support for MAX10 BMC Secure Updates
  Revert "mfd: max8997: Add of_compatible to Extcon and Charger mfd_cell"
  mfd: twl: Remove unused inline function twl4030charger_usb_en()
  dt-bindings: mfd: Convert pm8xxx bindings to yaml
  dt-bindings: mfd: Add compatible for pmk8350 rtc
  i2c: designware: Get rid of legacy platform data
  mfd: intel_quark_i2c_gpio: Convert I²C to use software nodes
  mfd: lpc_sch: Partially revert "Add support for Intel Quark X1000"
  mfd: arizona: Fix rumtime PM imbalance on error
  mfd: max8997: Replace 8998 with 8997
  mfd: core: Use acpi_find_child_device() for child devices lookup
  mfd: intel_quark_i2c_gpio: Don't play dirty trick with const
  mfd: intel_quark_i2c_gpio: Enable MSI interrupt
  mfd: intel_quark_i2c_gpio: Reuse BAR definitions for MFD cell indexing
  mfd: ntxec: Support for EC in Tolino Shine 2 HD
  mfd: stm32-timers: Avoid clearing auto reload register
  mfd: intel_quark_i2c_gpio: Replace I²C speeds with descriptive definitions
  mfd: intel_quark_i2c_gpio: Remove unused struct device member
  mfd: intel_quark_i2c_gpio: Unregister resources in reversed order
  mfd: Kconfig: ABX500_CORE should depend on ARCH_U8500
  ...
This commit is contained in:
Linus Torvalds 2021-04-28 15:59:13 -07:00
commit 71a5cc28e8
93 changed files with 5926 additions and 2057 deletions

View File

@ -1933,6 +1933,9 @@ N: Kukjin Kim
E: kgene@kernel.org
D: Samsung S3C, S5P and Exynos ARM architectures
N: Milo Kim
D: TI LP855x, LP8727 and LP8788 drivers
N: Sangbeom Kim
E: sbkim73@samsung.com
D: Samsung SoC Audio (ASoC) drivers

View File

@ -33,4 +33,11 @@ properties:
power off automatically. Device with key pressed shutdown feature can
specify this property.
reset-time-sec:
description:
Duration in seconds which the key should be kept pressed for device to
reset automatically. Device with key pressed reset feature can specify
this property.
$ref: /schemas/types.yaml#/definitions/uint32
additionalProperties: true

View File

@ -0,0 +1,183 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/actions,atc260x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Actions Semi ATC260x Power Management IC bindings
maintainers:
- Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
- Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
description: |
ATC260x series PMICs integrates Audio Codec, Power Management, RTC, IR
and GPIO controller blocks. Currently only the PM related functionalities
(i.e. regulators and system power-off/reboot) for the ATC2603C and ATC2609A
chip variants are supported.
ATC2603C includes 3 programmable DC-DC converters, 9 programmable LDO
regulators and 1 fixed LDO regulator.
ATC2609A includes 5 programmable DC-DC converters and 10 programmable LDO
regulators.
allOf:
- $ref: ../input/input.yaml
properties:
compatible:
enum:
- actions,atc2603c
- actions,atc2609a
reg:
maxItems: 1
interrupts:
maxItems: 1
reset-time-sec:
description: |
Duration in seconds which the key should be kept pressed for device
to reset automatically. The hardware default is 8. Use 0 to disable
this functionality.
enum: [0, 6, 8, 10, 12]
regulators:
type: object
description: |
List of child nodes specifying the regulators, depending on chip variant:
* ATC2603C: dcdc[1-3], ldo[1-3,5-8,11,12], switchldo1
* ATC2609A: dcdc[0-4], ldo[0-9]
properties:
compatible:
enum:
- actions,atc2603c-regulator
- actions,atc2609a-regulator
switchldo1:
type: object
$ref: ../regulator/regulator.yaml
properties:
regulator-name: true
regulator-boot-on: true
regulator-always-on: true
regulator-min-microvolt: true
regulator-max-microvolt: true
regulator-allow-bypass: true
regulator-active-discharge: true
additionalProperties: false
patternProperties:
"^(dcdc[0-4]|ldo[0-9]|ldo1[1-2]|switchldo1)-supply$":
description: ATC260x voltage regulators supplies
"^(dcdc[0-4]|ldo[0-9]|ldo1[1-2])$":
type: object
$ref: ../regulator/regulator.yaml
properties:
regulator-name: true
regulator-boot-on: true
regulator-always-on: true
regulator-min-microvolt: true
regulator-max-microvolt: true
regulator-allow-bypass: true
additionalProperties: false
allOf:
- if:
properties:
compatible:
contains:
const: actions,atc2603c-regulator
then:
patternProperties:
"^(dcdc[0,4]|ldo[0,4,9])(-supply)?$": false
"^(ldo|dcdc)":
properties:
regulator-allow-bypass: false
- if:
properties:
compatible:
contains:
const: actions,atc2609a-regulator
then:
patternProperties:
"^(ldo1[1-2]|switchldo1)(-supply)?$": false
"^(dcdc|ldo[3-9])":
properties:
regulator-allow-bypass: false
required:
- compatible
additionalProperties: false
additionalProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
pmic@65 {
compatible = "actions,atc2603c";
reg = <0x65>;
interrupt-parent = <&sirq>;
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
reset-time-sec = <6>;
regulators {
compatible = "actions,atc2603c-regulator";
dcdc1-supply = <&reg_5v0>;
dcdc3-supply = <&reg_5v0>;
ldo5-supply = <&reg_5v0>;
switchldo1-supply = <&vcc>;
vdd_cpu: dcdc1 {
regulator-name = "VDD_CPU";
regulator-min-microvolt = <700000>;
regulator-max-microvolt = <1400000>;
regulator-always-on;
};
vcc: dcdc3 {
regulator-name = "VCC";
regulator-min-microvolt = <2600000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
vcc_3v1: ldo5 {
regulator-name = "VCC_3V1";
regulator-min-microvolt = <2600000>;
regulator-max-microvolt = <3300000>;
};
sd_vcc: switchldo1 {
regulator-name = "SD_VCC";
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-boot-on;
};
};
};
};
...

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/mfd/netronix,ntxec.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Netronix Embedded Controller
maintainers:
- Jonathan Neuschäfer <j.neuschaefer@gmx.net>
description: |
This EC is found in e-book readers of multiple brands (e.g. Kobo, Tolino), and
is typically implemented as a TI MSP430 microcontroller.
properties:
compatible:
const: netronix,ntxec
reg:
items:
- description: The I2C address of the EC
system-power-controller:
type: boolean
description: See Documentation/devicetree/bindings/power/power-controller.txt
interrupts:
minItems: 1
description:
The EC can signal interrupts via a GPIO line
"#pwm-cells":
const: 2
description: |
Number of cells in a PWM specifier.
The following PWM channels are supported:
- 0: The PWM channel controlled by registers 0xa1-0xa7
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
ec: embedded-controller@43 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ntxec>;
compatible = "netronix,ntxec";
reg = <0x43>;
system-power-controller;
interrupt-parent = <&gpio4>;
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
#pwm-cells = <2>;
};
};
backlight {
compatible = "pwm-backlight";
pwms = <&ec 0 50000>;
power-supply = <&backlight_regulator>;
};
backlight_regulator: regulator-dummy {
compatible = "regulator-fixed";
regulator-name = "backlight";
};

View File

@ -1,99 +0,0 @@
Qualcomm PM8xxx PMIC multi-function devices
The PM8xxx family of Power Management ICs are used to provide regulated
voltages and other various functionality to Qualcomm SoCs.
= PROPERTIES
- compatible:
Usage: required
Value type: <string>
Definition: must be one of:
"qcom,pm8058"
"qcom,pm8821"
"qcom,pm8921"
- #address-cells:
Usage: required
Value type: <u32>
Definition: must be 1
- #size-cells:
Usage: required
Value type: <u32>
Definition: must be 0
- interrupts:
Usage: required
Value type: <prop-encoded-array>
Definition: specifies the interrupt that indicates a subdevice
has generated an interrupt (summary interrupt). The
format of the specifier is defined by the binding document
describing the node's interrupt parent.
- #interrupt-cells:
Usage: required
Value type : <u32>
Definition: must be 2. Specifies the number of cells needed to encode
an interrupt source. The 1st cell contains the interrupt
number. The 2nd cell is the trigger type and level flags
encoded as follows:
1 = low-to-high edge triggered
2 = high-to-low edge triggered
4 = active high level-sensitive
8 = active low level-sensitive
- interrupt-controller:
Usage: required
Value type: <empty>
Definition: identifies this node as an interrupt controller
= SUBCOMPONENTS
The PMIC contains multiple independent functions, each described in a subnode.
The below bindings specify the set of valid subnodes.
== Real-Time Clock
- compatible:
Usage: required
Value type: <string>
Definition: must be one of:
"qcom,pm8058-rtc"
"qcom,pm8921-rtc"
"qcom,pm8941-rtc"
"qcom,pm8018-rtc"
- reg:
Usage: required
Value type: <prop-encoded-array>
Definition: single entry specifying the base address of the RTC registers
- interrupts:
Usage: required
Value type: <prop-encoded-array>
Definition: single entry specifying the RTC's alarm interrupt
- allow-set-time:
Usage: optional
Value type: <empty>
Definition: indicates that the setting of RTC time is allowed by
the host CPU
= EXAMPLE
pmicintc: pmic@0 {
compatible = "qcom,pm8921";
interrupts = <104 8>;
#interrupt-cells = <2>;
interrupt-controller;
#address-cells = <1>;
#size-cells = <0>;
rtc@11d {
compatible = "qcom,pm8921-rtc";
reg = <0x11d>;
interrupts = <0x27 0>;
};
};

View File

@ -0,0 +1,54 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/qcom-pm8xxx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm PM8xxx PMIC multi-function devices
maintainers:
- Satya Priya <skakit@codeaurora.org>
description: |
The PM8xxx family of Power Management ICs are used to provide regulated
voltages and other various functionality to Qualcomm SoCs.
properties:
compatible:
enum:
- qcom,pm8058
- qcom,pm8821
- qcom,pm8921
reg:
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
interrupts:
maxItems: 1
'#interrupt-cells':
const: 2
interrupt-controller: true
patternProperties:
"rtc@[0-9a-f]+$":
type: object
$ref: "../rtc/qcom-pm8xxx-rtc.yaml"
required:
- compatible
- '#address-cells'
- '#size-cells'
- interrupts
- '#interrupt-cells'
- interrupt-controller
additionalProperties: false
...

View File

@ -0,0 +1,111 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/ricoh,rn5t618.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ricoh RN5T567/RN5T618/RC5T619 PMIC
maintainers:
- Andreas Kemnade <andreas@kemnade.info>
description: |
Ricoh RN5T567/RN5T618/RC5T619 is a power management IC family which
integrates 3 to 5 step-down DCDC converters, 7 to 10 low-dropout regulators,
GPIOs, and a watchdog timer. It can be controlled through an I2C interface.
The RN5T618/RC5T619 provides additionally a Li-ion battery charger,
fuel gauge, and an ADC.
The RC5T619 additionally includes USB charger detection and an RTC.
allOf:
- if:
properties:
compatible:
contains:
const: ricoh,rn5t567
then:
properties:
regulators:
patternProperties:
"^(DCDC[1-4]|LDO[1-5]|LDORTC[12])$":
$ref: ../regulator/regulator.yaml
additionalProperties: false
- if:
properties:
compatible:
contains:
const: ricoh,rn5t618
then:
properties:
regulators:
patternProperties:
"^(DCDC[1-3]|LDO[1-5]|LDORTC[12])$":
$ref: ../regulator/regulator.yaml
additionalProperties: false
- if:
properties:
compatible:
contains:
const: ricoh,rc5t619
then:
properties:
regulators:
patternProperties:
"^(DCDC[1-5]|LDO[1-9]|LDO10|LDORTC[12])$":
$ref: ../regulator/regulator.yaml
additionalProperties: false
properties:
compatible:
enum:
- ricoh,rn5t567
- ricoh,rn5t618
- ricoh,rc5t619
reg:
maxItems: 1
interrupts:
maxItems: 1
system-power-controller:
type: boolean
description: |
See Documentation/devicetree/bindings/power/power-controller.txt
regulators:
type: object
additionalProperties: false
required:
- compatible
- reg
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@32 {
compatible = "ricoh,rn5t618";
reg = <0x32>;
interrupt-parent = <&gpio5>;
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
system-power-controller;
regulators {
DCDC1 {
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1050000>;
};
DCDC2 {
regulator-min-microvolt = <1175000>;
regulator-max-microvolt = <1175000>;
};
};
};
};

View File

@ -1,52 +0,0 @@
* Ricoh RN5T567/RN5T618 PMIC
Ricoh RN5T567/RN5T618/RC5T619 is a power management IC family which
integrates 3 to 5 step-down DCDC converters, 7 to 10 low-dropout regulators,
GPIOs, and a watchdog timer. It can be controlled through an I2C interface.
The RN5T618/RC5T619 provides additionally a Li-ion battery charger,
fuel gauge, and an ADC.
The RC5T619 additionnally includes USB charger detection and an RTC.
Required properties:
- compatible: must be one of
"ricoh,rn5t567"
"ricoh,rn5t618"
"ricoh,rc5t619"
- reg: the I2C slave address of the device
Optional properties:
- interrupts: interrupt mapping for IRQ
See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
- system-power-controller:
See Documentation/devicetree/bindings/power/power-controller.txt
Sub-nodes:
- regulators: the node is required if the regulator functionality is
needed. The valid regulator names are: DCDC1, DCDC2, DCDC3, DCDC4
(RN5T567/RC5T619), LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7, LDO8,
LDO9, LDO10, LDORTC1 and LDORTC2.
LDO7-10 are specific to RC5T619.
The common bindings for each individual regulator can be found in:
Documentation/devicetree/bindings/regulator/regulator.txt
Example:
pmic@32 {
compatible = "ricoh,rn5t618";
reg = <0x32>;
interrupt-parent = <&gpio5>;
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
system-power-controller;
regulators {
DCDC1 {
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1050000>;
};
DCDC2 {
regulator-min-microvolt = <1175000>;
regulator-max-microvolt = <1175000>;
};
};
};

View File

@ -0,0 +1,201 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/rohm,bd71815-pmic.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BD71815 Power Management Integrated Circuit bindings
maintainers:
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
description: |
BD71815AGW is a single-chip power management ICs for battery-powered
portable devices. It integrates 5 buck converters, 8 LDOs, a boost driver
for LED and a 500 mA single-cell linear charger. Also included is a Coulomb
counter, a real-time clock (RTC), and a 32.768 kHz clock gate and two GPOs.
properties:
compatible:
const: rohm,bd71815
reg:
description:
I2C slave address.
maxItems: 1
interrupts:
maxItems: 1
gpio-controller: true
"#gpio-cells":
const: 2
description: |
The first cell is the pin number and the second cell is used to specify
flags. See ../gpio/gpio.txt for more information.
clocks:
maxItems: 1
"#clock-cells":
const: 0
clock-output-names:
const: bd71815-32k-out
rohm,clkout-open-drain:
description: clk32kout mode. Set to 1 for "open-drain" or 0 for "cmos".
$ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 0
maximum: 1
rohm,charger-sense-resistor-ohms:
minimum: 10000000
maximum: 50000000
description: |
BD71827 and BD71828 have SAR ADC for measuring charging currents.
External sense resistor (RSENSE in data sheet) should be used. If
something other but 30MOhm resistor is used the resistance value
should be given here in Ohms.
default: 30000000
regulators:
$ref: ../regulator/rohm,bd71815-regulator.yaml
description:
List of child nodes that specify the regulators.
gpio-reserved-ranges:
description: |
Usage of BD71828 GPIO pins can be changed via OTP. This property can be
used to mark the pins which should not be configured for GPIO. Please see
the ../gpio/gpio.txt for more information.
rohm,enable-hidden-gpo:
description: |
The BD71815 has undocumented GPO at pin E5. Pin is marked as GND at the
data-sheet as it's location in the middle of GND pins makes it hard to
use on PCB. If your board has managed to use this pin you can enable the
second GPO by defining this property. Dont enable this if you are unsure
about how the E5 pin is connected on your board.
type: boolean
required:
- compatible
- reg
- interrupts
- clocks
- "#clock-cells"
- regulators
- gpio-controller
- "#gpio-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/leds/common.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic: pmic@4b {
compatible = "rohm,bd71815";
reg = <0x4b>;
interrupt-parent = <&gpio1>;
interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
clocks = <&osc 0>;
#clock-cells = <0>;
clock-output-names = "bd71815-32k-out";
gpio-controller;
#gpio-cells = <2>;
rohm,charger-sense-resistor-ohms = <10000000>;
regulators {
buck1: buck1 {
regulator-name = "buck1";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <2000000>;
regulator-always-on;
regulator-ramp-delay = <1250>;
rohm,dvs-run-voltage = <1150000>;
rohm,dvs-suspend-voltage = <950000>;
};
buck2: buck2 {
regulator-name = "buck2";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <2000000>;
regulator-always-on;
regulator-ramp-delay = <1250>;
rohm,dvs-run-voltage = <1150000>;
rohm,dvs-suspend-voltage = <950000>;
};
buck3: buck3 {
regulator-name = "buck3";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <2700000>;
regulator-always-on;
};
buck4: buck4 {
regulator-name = "buck4";
regulator-min-microvolt = <1100000>;
regulator-max-microvolt = <1850000>;
regulator-always-on;
};
buck5: buck5 {
regulator-name = "buck5";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
ldo1: ldo1 {
regulator-name = "ldo1";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
ldo2: ldo2 {
regulator-name = "ldo2";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
ldo3: ldo3 {
regulator-name = "ldo3";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
ldo4: ldo4 {
regulator-name = "ldo4";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
ldo5: ldo5 {
regulator-name = "ldo5";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
ldo6: ldodvref {
regulator-name = "ldodvref";
regulator-always-on;
};
ldo7: ldolpsr {
regulator-name = "ldolpsr";
regulator-always-on;
};
boost: wled {
regulator-name = "wled";
regulator-min-microamp = <10>;
regulator-max-microamp = <25000>;
};
};
};
};

View File

@ -44,6 +44,12 @@ properties:
clock-output-names:
const: bd71828-32k-out
rohm,clkout-open-drain:
description: clk32kout mode. Set to 1 for "open-drain" or 0 for "cmos".
$ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 0
maximum: 1
rohm,charger-sense-resistor-ohms:
minimum: 10000000
maximum: 50000000

View File

@ -0,0 +1,123 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/rohm,bd9576-pmic.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BD9576MUF and BD9573MUF Power Management Integrated Circuit bindings
maintainers:
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
description: |
BD9576MUF and BD9573MUF are power management ICs primarily intended for
powering the R-Car series processors.
The IC provides 6 power outputs with configurable sequencing and safety
monitoring. A watchdog logic with slow ping/windowed modes is also included.
properties:
compatible:
enum:
- rohm,bd9576
- rohm,bd9573
reg:
description:
I2C slave address.
maxItems: 1
interrupts:
maxItems: 1
rohm,vout1-en-low:
description:
BD9576 and BD9573 VOUT1 regulator enable state can be individually
controlled by a GPIO. This is dictated by state of vout1-en pin during
the PMIC startup. If vout1-en is LOW during PMIC startup then the VOUT1
enable sate is controlled via this pin. Set this property if vout1-en
is wired to be down at PMIC start-up.
type: boolean
rohm,vout1-en-gpios:
description:
GPIO specifier to specify the GPIO connected to vout1-en for vout1 ON/OFF
state control.
maxItems: 1
rohm,ddr-sel-low:
description:
The BD9576 and BD9573 output voltage for DDR can be selected by setting
the ddr-sel pin low or high. Set this property if ddr-sel is grounded.
type: boolean
rohm,watchdog-enable-gpios:
description: The GPIO line used to enable the watchdog.
maxItems: 1
rohm,watchdog-ping-gpios:
description: The GPIO line used to ping the watchdog.
maxItems: 1
rohm,hw-timeout-ms:
maxItems: 2
description:
Watchog timeout in milliseconds. If single value is given it is
the maximum timeout. Eg. if pinging watchdog is not done within this time
limit the watchdog will be triggered. If two values are given watchdog
is configured in "window mode". Then first value is limit for short-ping
Eg. if watchdog is pinged sooner than that the watchdog will trigger.
When two values is given the second value is the maximum timeout.
# (HW) minimum for short timeout is 2ms, maximum 220 ms.
# (HW) minimum for max timeout is 4ms, maximum 4416 ms.
regulators:
$ref: ../regulator/rohm,bd9576-regulator.yaml
description:
List of child nodes that specify the regulators.
required:
- compatible
- reg
- regulators
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic: pmic@30 {
compatible = "rohm,bd9576";
reg = <0x30>;
rohm,vout1-en-low;
rohm,vout1-en-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;
rohm,ddr-sel-low;
rohm,watchdog-enable-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;
rohm,watchdog-ping-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>;
rohm,hw-timeout-ms = <150>, <2300>;
regulators {
boost1: regulator-vd50 {
regulator-name = "VD50";
};
buck1: regulator-vd18 {
regulator-name = "VD18";
};
buck2: regulator-vdddr {
regulator-name = "VDDDR";
};
buck3: regulator-vd10 {
regulator-name = "VD10";
};
ldo: regulator-voutl1 {
regulator-name = "VOUTL1";
};
sw: regulator-vouts1 {
regulator-name = "VOUTS1";
};
};
};
};

View File

@ -17,6 +17,10 @@ properties:
description: I2C slave address
const: 0x60
reset-gpios:
description: GPIO connected to NRST pin (active low reset, pin 20)
maxItems: 1
gpio-controller: true
'#gpio-cells':

View File

@ -17,6 +17,10 @@ properties:
description: I2C slave address
const: 0x60
reset-gpios:
description: GPIO connected to NRST pin (active low reset, pin 20)
maxItems: 1
gpio-controller: true
'#gpio-cells':

View File

@ -19,6 +19,10 @@ properties:
description: I2C slave address
const: 0x60
reset-gpios:
description: GPIO connected to NRST pin (active low reset, pin 20)
maxItems: 1
gpio-controller: true
'#gpio-cells':

View File

@ -0,0 +1,116 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/rohm,bd71815-regulator.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BD71815 Power Management Integrated Circuit regulators
maintainers:
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
description: |
This module is part of the ROHM BD718215 MFD device. For more details
see Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml.
The regulator controller is represented as a sub-node of the PMIC node
on the device tree.
The valid names for BD71815 regulator nodes are
buck1, buck2, buck3, buck4, buck5,
ldo1, ldo2, ldo3, ldo4, ldo5,
ldodvref, ldolpsr, wled
properties:
wled:
type: object
description:
properties for wled regulator
$ref: regulator.yaml#
properties:
regulator-name:
const: wled
patternProperties:
"^((ldo|buck)[1-5]|ldolpsr|ldodvref)$":
type: object
description:
Properties for single LDO/BUCK regulator.
$ref: regulator.yaml#
properties:
regulator-name:
pattern: "^((ldo|buck)[1-5]|ldolpsr|ldodvref)$"
description:
should be "ldo1", ..., "ldo5", "buck1", ..., "buck5" and "ldolpsr"
for ldolpsr regulator, "ldodvref" for ldodvref reglator.
rohm,vsel-gpios:
description:
GPIO used to control ldo4 state (when ldo4 is controlled by GPIO).
rohm,dvs-run-voltage:
description:
PMIC "RUN" state voltage in uV when PMIC HW states are used. See
comments below for bucks/LDOs which support this. 0 means
regulator should be disabled at RUN state.
$ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 0
maximum: 3300000
rohm,dvs-snvs-voltage:
description:
Whether to keep regulator enabled at "SNVS" state or not.
0 means regulator should be disabled at SNVS state, non zero voltage
keeps regulator enabled. BD71815 does not change voltage level
when PMIC transitions to SNVS.SNVS voltage depends on the previous
state (from which the PMIC transitioned to SNVS).
$ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 0
maximum: 3300000
rohm,dvs-suspend-voltage:
description:
PMIC "SUSPEND" state voltage in uV when PMIC HW states are used. See
comments below for bucks/LDOs which support this. 0 means
regulator should be disabled at SUSPEND state.
$ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 0
maximum: 3300000
rohm,dvs-lpsr-voltage:
description:
PMIC "LPSR" state voltage in uV when PMIC HW states are used. See
comments below for bucks/LDOs which support this. 0 means
regulator should be disabled at LPSR state.
$ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 0
maximum: 3300000
# Bucks 1 and 2 support giving separate voltages for operational states
# (RUN /CLEAN according to data-sheet) and non operational states
# (LPSR/SUSPEND). The voltage is automatically changed when HW
# state changes. Omitting these properties from bucks 1 and 2 leave
# buck voltages to not be toggled by HW state. Enable status may still
# be toggled by state changes depending on HW default settings.
#
# Bucks 3-5 and ldos 1-5 support setting the RUN state voltage here.
# Given RUN voltage is used at all states if regulator is enabled at
# given state.
# Values given for other states are regarded as enable/disable at
# given state (see below).
#
# All regulators except WLED support specifying enable/disable status
# for each of the HW states (RUN/SNVS/SUSPEND/LPSR). HW defaults can
# be overridden by setting voltage to 0 (regulator disabled at given
# state) or non-zero (regulator enabled at given state). Please note
# that setting non zero voltages for bucks 1/2 will also enable voltage
# changes according to state change.
required:
- regulator-name
unevaluatedProperties: false
additionalProperties: false

View File

@ -772,6 +772,8 @@ patternProperties:
description: Broadcom Corporation (formerly NetLogic Microsystems)
"^netron-dy,.*":
description: Netron DY
"^netronix,.*":
description: Netronix, Inc.
"^netxeon,.*":
description: Shenzhen Netxeon Technology CO., LTD
"^neweast,.*":

View File

@ -2894,6 +2894,18 @@ W: http://www.openaoe.org/
F: Documentation/admin-guide/aoe/
F: drivers/block/aoe/
ATC260X PMIC MFD DRIVER
M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
M: Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
L: linux-actions@lists.infradead.org
S: Maintained
F: Documentation/devicetree/bindings/mfd/actions,atc260x.yaml
F: drivers/input/misc/atc260x-onkey.c
F: drivers/mfd/atc260*
F: drivers/power/reset/atc260x-poweroff.c
F: drivers/regulator/atc260x-regulator.c
F: include/linux/mfd/atc260x/*
ATHEROS 71XX/9XXX GPIO DRIVER
M: Alban Bedel <albeu@free.fr>
S: Maintained
@ -9221,6 +9233,26 @@ F: include/linux/mei_cl_bus.h
F: include/uapi/linux/mei.h
F: samples/mei/*
INTEL MAX 10 BMC MFD DRIVER
M: Xu Yilun <yilun.xu@intel.com>
R: Tom Rix <trix@redhat.com>
S: Maintained
F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc
F: Documentation/hwmon/intel-m10-bmc-hwmon.rst
F: drivers/hwmon/intel-m10-bmc-hwmon.c
F: drivers/mfd/intel-m10-bmc.c
F: include/linux/mfd/intel-m10-bmc.h
INTEL MAX 10 BMC MFD DRIVER
M: Xu Yilun <yilun.xu@intel.com>
R: Tom Rix <trix@redhat.com>
S: Maintained
F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc
F: Documentation/hwmon/intel-m10-bmc-hwmon.rst
F: drivers/hwmon/intel-m10-bmc-hwmon.c
F: drivers/mfd/intel-m10-bmc.c
F: include/linux/mfd/intel-m10-bmc.h
INTEL MENLOW THERMAL DRIVER
M: Sujith Thomas <sujith.thomas@intel.com>
L: platform-driver-x86@vger.kernel.org
@ -12543,6 +12575,15 @@ F: include/net/netrom.h
F: include/uapi/linux/netrom.h
F: net/netrom/
NETRONIX EMBEDDED CONTROLLER
M: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
S: Maintained
F: Documentation/devicetree/bindings/mfd/netronix,ntxec.yaml
F: drivers/mfd/ntxec.c
F: drivers/pwm/pwm-ntxec.c
F: drivers/rtc/rtc-ntxec.c
F: include/linux/mfd/ntxec.h
NETRONOME ETHERNET DRIVERS
M: Simon Horman <simon.horman@netronome.com>
R: Jakub Kicinski <kuba@kernel.org>
@ -15634,20 +15675,27 @@ F: Documentation/devicetree/bindings/mfd/rohm,bd70528-pmic.txt
F: Documentation/devicetree/bindings/regulator/rohm,bd70528-regulator.txt
F: drivers/clk/clk-bd718x7.c
F: drivers/gpio/gpio-bd70528.c
F: drivers/gpio/gpio-bd71815.c
F: drivers/gpio/gpio-bd71828.c
F: drivers/mfd/rohm-bd70528.c
F: drivers/mfd/rohm-bd71828.c
F: drivers/mfd/rohm-bd718x7.c
F: drivers/mfd/rohm-bd9576.c
F: drivers/power/supply/bd70528-charger.c
F: drivers/regulator/bd70528-regulator.c
F: drivers/regulator/bd71815-regulator.c
F: drivers/regulator/bd71828-regulator.c
F: drivers/regulator/bd718x7-regulator.c
F: drivers/regulator/bd9576-regulator.c
F: drivers/regulator/rohm-regulator.c
F: drivers/rtc/rtc-bd70528.c
F: drivers/watchdog/bd70528_wdt.c
F: drivers/watchdog/bd9576_wdt.c
F: include/linux/mfd/rohm-bd70528.h
F: include/linux/mfd/rohm-bd71815.h
F: include/linux/mfd/rohm-bd71828.h
F: include/linux/mfd/rohm-bd718x7.h
F: include/linux/mfd/rohm-bd957x.h
F: include/linux/mfd/rohm-generic.h
F: include/linux/mfd/rohm-shared.h
@ -18147,29 +18195,6 @@ S: Maintained
F: sound/soc/codecs/isabelle*
F: sound/soc/codecs/lm49453*
TI LP855x BACKLIGHT DRIVER
M: Milo Kim <milo.kim@ti.com>
S: Maintained
F: Documentation/driver-api/backlight/lp855x-driver.rst
F: drivers/video/backlight/lp855x_bl.c
F: include/linux/platform_data/lp855x.h
TI LP8727 CHARGER DRIVER
M: Milo Kim <milo.kim@ti.com>
S: Maintained
F: drivers/power/supply/lp8727_charger.c
F: include/linux/platform_data/lp8727.h
TI LP8788 MFD DRIVER
M: Milo Kim <milo.kim@ti.com>
S: Maintained
F: drivers/iio/adc/lp8788_adc.c
F: drivers/leds/leds-lp8788.c
F: drivers/mfd/lp8788*.c
F: drivers/power/supply/lp8788-charger.c
F: drivers/regulator/lp8788-*.c
F: include/linux/mfd/lp8788*.h
TI NETCP ETHERNET DRIVER
M: Wingman Kwok <w-kwok2@ti.com>
M: Murali Karicheri <m-karicheri2@ti.com>
@ -19587,7 +19612,6 @@ F: Documentation/devicetree/bindings/sound/wlf,arizona.yaml
F: Documentation/hwmon/wm83??.rst
F: arch/arm/mach-s3c/mach-crag6410*
F: drivers/clk/clk-wm83*.c
F: drivers/extcon/extcon-arizona.c
F: drivers/gpio/gpio-*wm*.c
F: drivers/gpio/gpio-arizona.c
F: drivers/hwmon/wm83??-hwmon.c
@ -19611,7 +19635,7 @@ F: include/linux/mfd/wm8400*
F: include/linux/regulator/arizona*
F: include/linux/wm97xx.h
F: include/sound/wm????.h
F: sound/soc/codecs/arizona.?
F: sound/soc/codecs/arizona*
F: sound/soc/codecs/cs47l24*
F: sound/soc/codecs/wm*

View File

@ -13,6 +13,8 @@
#include <linux/regmap.h>
/* clk control registers */
/* BD71815 */
#define BD71815_REG_OUT32K 0x1d
/* BD70528 */
#define BD70528_REG_OUT32K 0x2c
/* BD71828 */
@ -118,6 +120,10 @@ static int bd71837_clk_probe(struct platform_device *pdev)
c->reg = BD70528_REG_OUT32K;
c->mask = CLK_OUT_EN_MASK;
break;
case ROHM_CHIP_TYPE_BD71815:
c->reg = BD71815_REG_OUT32K;
c->mask = CLK_OUT_EN_MASK;
break;
default:
dev_err(&pdev->dev, "Unknown clk chip\n");
return -EINVAL;
@ -146,6 +152,7 @@ static const struct platform_device_id bd718x7_clk_id[] = {
{ "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
{ "bd70528-clk", ROHM_CHIP_TYPE_BD70528 },
{ "bd71828-clk", ROHM_CHIP_TYPE_BD71828 },
{ "bd71815-clk", ROHM_CHIP_TYPE_BD71815 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
@ -161,6 +168,6 @@ static struct platform_driver bd71837_clk = {
module_platform_driver(bd71837_clk);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("BD71837/BD71847/BD70528 chip clk driver");
MODULE_DESCRIPTION("BD718(15/18/28/37/47/50) and BD70528 chip clk driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bd718xx-clk");

View File

@ -21,14 +21,6 @@ config EXTCON_ADC_JACK
help
Say Y here to enable extcon device driver based on ADC values.
config EXTCON_ARIZONA
tristate "Wolfson Arizona EXTCON support"
depends on MFD_ARIZONA && INPUT && SND_SOC
help
Say Y here to enable support for external accessory detection
with Wolfson Arizona devices. These are audio CODECs with
advanced audio accessory detection support.
config EXTCON_AXP288
tristate "X-Power AXP288 EXTCON support"
depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI

View File

@ -6,7 +6,6 @@
obj-$(CONFIG_EXTCON) += extcon-core.o
extcon-core-objs += extcon.o devres.o
obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o
obj-$(CONFIG_EXTCON_FSA9480) += extcon-fsa9480.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o

View File

@ -1105,6 +1105,16 @@ config GPIO_BD70528
This driver can also be built as a module. If so, the module
will be called gpio-bd70528.
config GPIO_BD71815
tristate "ROHM BD71815 PMIC GPIO support"
depends on MFD_ROHM_BD71828
help
Support for GPO(s) on ROHM BD71815 PMIC. There are two GPOs
available on the ROHM PMIC.
This driver can also be built as a module. If so, the module
will be called gpio-bd71815.
config GPIO_BD71828
tristate "ROHM BD71828 GPIO support"
depends on MFD_ROHM_BD71828

View File

@ -39,6 +39,7 @@ obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o
obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o
obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o
obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o

185
drivers/gpio/gpio-bd71815.c Normal file
View File

@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Support to GPOs on ROHM BD71815
* Copyright 2021 ROHM Semiconductors.
* Author: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
*
* Copyright 2014 Embest Technology Co. Ltd. Inc.
* Author: yanglsh@embest-tech.com
*/
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
/* For the BD71815 register definitions */
#include <linux/mfd/rohm-bd71815.h>
struct bd71815_gpio {
/* chip.parent points the MFD which provides DT node and regmap */
struct gpio_chip chip;
/* dev points to the platform device for devm and prints */
struct device *dev;
struct regmap *regmap;
};
static int bd71815gpo_get(struct gpio_chip *chip, unsigned int offset)
{
struct bd71815_gpio *bd71815 = gpiochip_get_data(chip);
int ret, val;
ret = regmap_read(bd71815->regmap, BD71815_REG_GPO, &val);
if (ret)
return ret;
return (val >> offset) & 1;
}
static void bd71815gpo_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct bd71815_gpio *bd71815 = gpiochip_get_data(chip);
int ret, bit;
bit = BIT(offset);
if (value)
ret = regmap_set_bits(bd71815->regmap, BD71815_REG_GPO, bit);
else
ret = regmap_clear_bits(bd71815->regmap, BD71815_REG_GPO, bit);
if (ret)
dev_warn(bd71815->dev, "failed to toggle GPO\n");
}
static int bd71815_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
unsigned long config)
{
struct bd71815_gpio *bdgpio = gpiochip_get_data(chip);
switch (pinconf_to_config_param(config)) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
return regmap_update_bits(bdgpio->regmap,
BD71815_REG_GPO,
BD71815_GPIO_DRIVE_MASK << offset,
BD71815_GPIO_OPEN_DRAIN << offset);
case PIN_CONFIG_DRIVE_PUSH_PULL:
return regmap_update_bits(bdgpio->regmap,
BD71815_REG_GPO,
BD71815_GPIO_DRIVE_MASK << offset,
BD71815_GPIO_CMOS << offset);
default:
break;
}
return -ENOTSUPP;
}
/* BD71815 GPIO is actually GPO */
static int bd71815gpo_direction_get(struct gpio_chip *gc, unsigned int offset)
{
return GPIO_LINE_DIRECTION_OUT;
}
/* Template for GPIO chip */
static const struct gpio_chip bd71815gpo_chip = {
.label = "bd71815",
.owner = THIS_MODULE,
.get = bd71815gpo_get,
.get_direction = bd71815gpo_direction_get,
.set = bd71815gpo_set,
.set_config = bd71815_gpio_set_config,
.can_sleep = true,
};
#define BD71815_TWO_GPIOS GENMASK(1, 0)
#define BD71815_ONE_GPIO BIT(0)
/*
* Sigh. The BD71815 and BD71817 were originally designed to support two GPO
* pins. At some point it was noticed the second GPO pin which is the E5 pin
* located at the center of IC is hard to use on PCB (due to the location). It
* was decided to not promote this second GPO and the pin is marked as GND in
* the datasheet. The functionality is still there though! I guess driving a GPO
* connected to the ground is a bad idea. Thus we do not support it by default.
* OTOH - the original driver written by colleagues at Embest did support
* controlling this second GPO. It is thus possible this is used in some of the
* products.
*
* This driver does not by default support configuring this second GPO
* but allows using it by providing the DT property
* "rohm,enable-hidden-gpo".
*/
static int bd71815_init_valid_mask(struct gpio_chip *gc,
unsigned long *valid_mask,
unsigned int ngpios)
{
if (ngpios != 2)
return 0;
if (gc->parent && device_property_present(gc->parent,
"rohm,enable-hidden-gpo"))
*valid_mask = BD71815_TWO_GPIOS;
else
*valid_mask = BD71815_ONE_GPIO;
return 0;
}
static int gpo_bd71815_probe(struct platform_device *pdev)
{
struct bd71815_gpio *g;
struct device *parent, *dev;
/*
* Bind devm lifetime to this platform device => use dev for devm.
* also the prints should originate from this device.
*/
dev = &pdev->dev;
/* The device-tree and regmap come from MFD => use parent for that */
parent = dev->parent;
g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL);
if (!g)
return -ENOMEM;
g->chip = bd71815gpo_chip;
/*
* FIXME: As writing of this the sysfs interface for GPIO control does
* not respect the valid_mask. Do not trust it but rather set the ngpios
* to 1 if "rohm,enable-hidden-gpo" is not given.
*
* This check can be removed later if the sysfs export is fixed and
* if the fix is backported.
*
* For now it is safest to just set the ngpios though.
*/
if (device_property_present(parent, "rohm,enable-hidden-gpo"))
g->chip.ngpio = 2;
else
g->chip.ngpio = 1;
g->chip.init_valid_mask = bd71815_init_valid_mask;
g->chip.base = -1;
g->chip.parent = parent;
g->regmap = dev_get_regmap(parent, NULL);
g->dev = dev;
return devm_gpiochip_add_data(dev, &g->chip, g);
}
static struct platform_driver gpo_bd71815_driver = {
.driver = {
.name = "bd71815-gpo",
},
.probe = gpo_bd71815_probe,
};
module_platform_driver(gpo_bd71815_driver);
MODULE_ALIAS("platform:bd71815-gpo");
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_AUTHOR("Peter Yang <yanglsh@embest-tech.com>");
MODULE_DESCRIPTION("GPO interface for BD71815");
MODULE_LICENSE("GPL");

View File

@ -22,7 +22,6 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_data/i2c-designware.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@ -206,7 +205,6 @@ static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = {
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct i2c_adapter *adap;
struct dw_i2c_dev *dev;
struct i2c_timings *t;
@ -236,9 +234,6 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
reset_control_deassert(dev->rst);
t = &dev->timings;
if (pdata)
t->bus_freq_hz = pdata->i2c_scl_freq;
else
i2c_parse_fw_timings(&pdev->dev, t, false);
i2c_dw_adjust_bus_speed(dev);

View File

@ -94,6 +94,17 @@ config INPUT_ARIZONA_HAPTICS
To compile this driver as a module, choose M here: the
module will be called arizona-haptics.
config INPUT_ATC260X_ONKEY
tristate "Actions Semi ATC260x PMIC ONKEY"
depends on MFD_ATC260X
help
Support the ONKEY of ATC260x PMICs as an input device reporting
power button status. ONKEY can be used to wakeup from low power
modes and force a reset on long press.
To compile this driver as a module, choose M here: the
module will be called atc260x-onkey.
config INPUT_ATMEL_CAPTOUCH
tristate "Atmel Capacitive Touch Button Driver"
depends on OF || COMPILE_TEST

View File

@ -17,6 +17,7 @@ obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_ARIEL_PWRBUTTON) += ariel-pwrbutton.o
obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
obj-$(CONFIG_INPUT_ATC260X_ONKEY) += atc260x-onkey.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o
@ -86,4 +87,3 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o

View File

@ -0,0 +1,305 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Onkey driver for Actions Semi ATC260x PMICs.
*
* Copyright (c) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
*/
#include <linux/bitfield.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/mfd/atc260x/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
/* <2s for short press, >2s for long press */
#define KEY_PRESS_TIME_SEC 2
/* Driver internals */
enum atc260x_onkey_reset_status {
KEY_RESET_HW_DEFAULT,
KEY_RESET_DISABLED,
KEY_RESET_USER_SEL,
};
struct atc260x_onkey_params {
u32 reg_int_ctl;
u32 kdwn_state_bm;
u32 long_int_pnd_bm;
u32 short_int_pnd_bm;
u32 kdwn_int_pnd_bm;
u32 press_int_en_bm;
u32 kdwn_int_en_bm;
u32 press_time_bm;
u32 reset_en_bm;
u32 reset_time_bm;
};
struct atc260x_onkey {
struct atc260x *atc260x;
const struct atc260x_onkey_params *params;
struct input_dev *input_dev;
struct delayed_work work;
int irq;
};
static const struct atc260x_onkey_params atc2603c_onkey_params = {
.reg_int_ctl = ATC2603C_PMU_SYS_CTL2,
.long_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS,
.short_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS,
.kdwn_int_pnd_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD,
.press_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN,
.kdwn_int_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN,
.kdwn_state_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS,
.press_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
.reset_en_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN,
.reset_time_bm = ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
};
static const struct atc260x_onkey_params atc2609a_onkey_params = {
.reg_int_ctl = ATC2609A_PMU_SYS_CTL2,
.long_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS,
.short_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS,
.kdwn_int_pnd_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD,
.press_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN,
.kdwn_int_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN,
.kdwn_state_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS,
.press_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
.reset_en_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN,
.reset_time_bm = ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
};
static int atc2603x_onkey_hw_init(struct atc260x_onkey *onkey,
enum atc260x_onkey_reset_status reset_status,
u32 reset_time, u32 press_time)
{
u32 reg_bm, reg_val;
reg_bm = onkey->params->long_int_pnd_bm |
onkey->params->short_int_pnd_bm |
onkey->params->kdwn_int_pnd_bm |
onkey->params->press_int_en_bm |
onkey->params->kdwn_int_en_bm;
reg_val = reg_bm | press_time;
reg_bm |= onkey->params->press_time_bm;
if (reset_status == KEY_RESET_DISABLED) {
reg_bm |= onkey->params->reset_en_bm;
} else if (reset_status == KEY_RESET_USER_SEL) {
reg_bm |= onkey->params->reset_en_bm |
onkey->params->reset_time_bm;
reg_val |= onkey->params->reset_en_bm | reset_time;
}
return regmap_update_bits(onkey->atc260x->regmap,
onkey->params->reg_int_ctl, reg_bm, reg_val);
}
static void atc260x_onkey_query(struct atc260x_onkey *onkey)
{
u32 reg_bits;
int ret, key_down;
ret = regmap_read(onkey->atc260x->regmap,
onkey->params->reg_int_ctl, &key_down);
if (ret) {
key_down = 1;
dev_err(onkey->atc260x->dev,
"Failed to read onkey status: %d\n", ret);
} else {
key_down &= onkey->params->kdwn_state_bm;
}
/*
* The hardware generates interrupt only when the onkey pin is
* asserted. Hence, the deassertion of the pin is simulated through
* work queue.
*/
if (key_down) {
schedule_delayed_work(&onkey->work, msecs_to_jiffies(200));
return;
}
/*
* The key-down status bit is cleared when the On/Off button
* is released.
*/
input_report_key(onkey->input_dev, KEY_POWER, 0);
input_sync(onkey->input_dev);
reg_bits = onkey->params->long_int_pnd_bm |
onkey->params->short_int_pnd_bm |
onkey->params->kdwn_int_pnd_bm |
onkey->params->press_int_en_bm |
onkey->params->kdwn_int_en_bm;
/* Clear key press pending events and enable key press interrupts. */
regmap_update_bits(onkey->atc260x->regmap, onkey->params->reg_int_ctl,
reg_bits, reg_bits);
}
static void atc260x_onkey_work(struct work_struct *work)
{
struct atc260x_onkey *onkey = container_of(work, struct atc260x_onkey,
work.work);
atc260x_onkey_query(onkey);
}
static irqreturn_t atc260x_onkey_irq(int irq, void *data)
{
struct atc260x_onkey *onkey = data;
int ret;
/* Disable key press interrupts. */
ret = regmap_update_bits(onkey->atc260x->regmap,
onkey->params->reg_int_ctl,
onkey->params->press_int_en_bm |
onkey->params->kdwn_int_en_bm, 0);
if (ret)
dev_err(onkey->atc260x->dev,
"Failed to disable interrupts: %d\n", ret);
input_report_key(onkey->input_dev, KEY_POWER, 1);
input_sync(onkey->input_dev);
atc260x_onkey_query(onkey);
return IRQ_HANDLED;
}
static int atc260x_onkey_open(struct input_dev *dev)
{
struct atc260x_onkey *onkey = input_get_drvdata(dev);
enable_irq(onkey->irq);
return 0;
}
static void atc260x_onkey_close(struct input_dev *dev)
{
struct atc260x_onkey *onkey = input_get_drvdata(dev);
disable_irq(onkey->irq);
cancel_delayed_work_sync(&onkey->work);
}
static int atc260x_onkey_probe(struct platform_device *pdev)
{
struct atc260x *atc260x = dev_get_drvdata(pdev->dev.parent);
struct atc260x_onkey *onkey;
struct input_dev *input_dev;
enum atc260x_onkey_reset_status reset_status;
u32 press_time = KEY_PRESS_TIME_SEC, reset_time = 0;
int val, error;
onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
if (!onkey)
return -ENOMEM;
error = device_property_read_u32(pdev->dev.parent,
"reset-time-sec", &val);
if (error) {
reset_status = KEY_RESET_HW_DEFAULT;
} else if (val) {
if (val < 6 || val > 12) {
dev_err(&pdev->dev, "reset-time-sec out of range\n");
return -EINVAL;
}
reset_status = KEY_RESET_USER_SEL;
reset_time = (val - 6) / 2;
} else {
reset_status = KEY_RESET_DISABLED;
dev_dbg(&pdev->dev, "Disabled reset on long-press\n");
}
switch (atc260x->ic_type) {
case ATC2603C:
onkey->params = &atc2603c_onkey_params;
press_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
press_time);
reset_time = FIELD_PREP(ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
reset_time);
break;
case ATC2609A:
onkey->params = &atc2609a_onkey_params;
press_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME,
press_time);
reset_time = FIELD_PREP(ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL,
reset_time);
break;
default:
dev_err(&pdev->dev,
"OnKey not supported for ATC260x PMIC type: %u\n",
atc260x->ic_type);
return -EINVAL;
}
input_dev = devm_input_allocate_device(&pdev->dev);
if (!input_dev) {
dev_err(&pdev->dev, "Failed to allocate input device\n");
return -ENOMEM;
}
onkey->input_dev = input_dev;
onkey->atc260x = atc260x;
input_dev->name = "atc260x-onkey";
input_dev->phys = "atc260x-onkey/input0";
input_dev->open = atc260x_onkey_open;
input_dev->close = atc260x_onkey_close;
input_set_capability(input_dev, EV_KEY, KEY_POWER);
input_set_drvdata(input_dev, onkey);
INIT_DELAYED_WORK(&onkey->work, atc260x_onkey_work);
onkey->irq = platform_get_irq(pdev, 0);
if (onkey->irq < 0)
return onkey->irq;
error = devm_request_threaded_irq(&pdev->dev, onkey->irq, NULL,
atc260x_onkey_irq, IRQF_ONESHOT,
dev_name(&pdev->dev), onkey);
if (error) {
dev_err(&pdev->dev,
"Failed to register IRQ %d: %d\n", onkey->irq, error);
return error;
}
/* Keep IRQ disabled until atc260x_onkey_open() is called. */
disable_irq(onkey->irq);
error = input_register_device(input_dev);
if (error) {
dev_err(&pdev->dev,
"Failed to register input device: %d\n", error);
return error;
}
error = atc2603x_onkey_hw_init(onkey, reset_status,
reset_time, press_time);
if (error)
return error;
device_init_wakeup(&pdev->dev, true);
return 0;
}
static struct platform_driver atc260x_onkey_driver = {
.probe = atc260x_onkey_probe,
.driver = {
.name = "atc260x-onkey",
},
};
module_platform_driver(atc260x_onkey_driver);
MODULE_DESCRIPTION("Onkey driver for ATC260x PMICs");
MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -967,6 +967,17 @@ config MFD_VIPERBOARD
You need to select the mfd cell drivers separately.
The drivers do not support all features the board exposes.
config MFD_NTXEC
tristate "Netronix embedded controller (EC)"
depends on OF || COMPILE_TEST
depends on I2C
select REGMAP_I2C
select MFD_CORE
help
Say yes here if you want to support the embedded controller found in
certain e-book readers designed by the original design manufacturer
Netronix.
config MFD_RETU
tristate "Nokia Retu and Tahvo multi-function device"
select MFD_CORE
@ -1224,7 +1235,8 @@ config MFD_SC27XX_PMIC
config ABX500_CORE
bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
default y if ARCH_U300 || ARCH_U8500 || COMPILE_TEST
depends on ARCH_U8500 || COMPILE_TEST
default y if ARCH_U8500
help
Say yes here if you have the ABX500 Mixed Signal IC family
chips. This core driver expose register access functions.
@ -1232,30 +1244,6 @@ config ABX500_CORE
remain unchanged when IC changes. Binding of the functions to
actual register access is done by the IC core driver.
config AB3100_CORE
bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
depends on I2C=y && ABX500_CORE
select MFD_CORE
default y if ARCH_U300
help
Select this to enable the AB3100 Mixed Signal IC core
functionality. This connects to a AB3100 on the I2C bus
and expose a number of symbols needed for dependent devices
to read and write registers and subscribe to events from
this multi-functional IC. This is needed to use other features
of the AB3100 such as battery-backed RTC, charging control,
LEDs, vibrator, system power and temperature, power management
and ALSA sound.
config AB3100_OTP
tristate "ST-Ericsson AB3100 OTP functions"
depends on AB3100_CORE
default y if AB3100_CORE
help
Select this to enable the AB3100 Mixed Signal IC OTP (one-time
programmable memory) support. This exposes a sysfs file to read
out OTP values.
config AB8500_CORE
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
depends on ABX500_CORE && MFD_DB8500_PRCMU
@ -1975,19 +1963,31 @@ config MFD_ROHM_BD70528
charger.
config MFD_ROHM_BD71828
tristate "ROHM BD71828 Power Management IC"
tristate "ROHM BD71828 and BD71815 Power Management IC"
depends on I2C=y
depends on OF
select REGMAP_I2C
select REGMAP_IRQ
select MFD_CORE
help
Select this option to get support for the ROHM BD71828 Power
Management IC. BD71828GW is a single-chip power management IC for
battery-powered portable devices. The IC integrates 7 buck
converters, 7 LDOs, and a 1500 mA single-cell linear charger.
Also included is a Coulomb counter, a real-time clock (RTC), and
a 32.768 kHz clock gate.
Select this option to get support for the ROHM BD71828 and BD71815
Power Management ICs. BD71828GW and BD71815AGW are single-chip power
management ICs mainly for battery-powered portable devices.
The BD71828 integrates 7 buck converters and 7 LDOs. The BD71815
has 5 bucks, 7 LDOs, and a boost for driving LEDs. Both ICs provide
also a single-cell linear charger, a Coulomb counter, a real-time
clock (RTC), GPIOs and a 32.768 kHz clock gate.
config MFD_ROHM_BD957XMUF
tristate "ROHM BD9576MUF and BD9573MUF Power Management ICs"
depends on I2C=y
depends on OF
select REGMAP_I2C
select MFD_CORE
help
Select this option to get support for the ROHM BD9576MUF and
BD9573MUF Power Management ICs. BD9576 and BD9573 are primarily
designed to be used to power R-Car series processors.
config MFD_STM32_LPTIMER
tristate "Support for STM32 Low-Power Timer"
@ -2055,6 +2055,24 @@ config MFD_WCD934X
This driver provides common support WCD934x audio codec and its
associated Pin Controller, Soundwire Controller and Audio codec.
config MFD_ATC260X
tristate
select MFD_CORE
select REGMAP
select REGMAP_IRQ
config MFD_ATC260X_I2C
tristate "Actions Semi ATC260x PMICs with I2C"
select MFD_ATC260X
select REGMAP_I2C
depends on I2C
help
Support for the Actions Semi ATC260x PMICs controlled via I2C.
This driver provides common support for accessing the ATC2603C
and ATC2609A chip variants, additional drivers must be enabled
in order to use the functionality of the device.
config MFD_KHADAS_MCU
tristate "Support for Khadas System control Microcontroller"
depends on I2C

View File

@ -178,8 +178,6 @@ obj-$(CONFIG_MFD_PCF50633) += pcf50633.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
# ab8500-core need to come after db8500-prcmu (which provides the channel)
@ -218,6 +216,7 @@ obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o
obj-$(CONFIG_MFD_INTEL_PMT) += intel_pmt.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_NTXEC) += ntxec.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_RK808) += rk808.o
obj-$(CONFIG_MFD_RN5T618) += rn5t618.o
@ -261,6 +260,7 @@ obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o
obj-$(CONFIG_MFD_ROHM_BD70528) += rohm-bd70528.o
obj-$(CONFIG_MFD_ROHM_BD71828) += rohm-bd71828.o
obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
obj-$(CONFIG_MFD_ROHM_BD957XMUF) += rohm-bd9576.o
obj-$(CONFIG_MFD_STMFX) += stmfx.o
obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o
obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
@ -268,3 +268,6 @@ obj-$(CONFIG_MFD_ACER_A500_EC) += acer-ec-a500.o
obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o
obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o
obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o

View File

@ -1,929 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2007-2010 ST-Ericsson
* Low-level core for exclusive access to the AB3100 IC on the I2C bus
* and some basic chip-configuration.
* Author: Linus Walleij <linus.walleij@stericsson.com>
*/
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/random.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
#include <linux/mfd/ab3100.h>
#include <linux/mfd/abx500.h>
/* These are the only registers inside AB3100 used in this main file */
/* Interrupt event registers */
#define AB3100_EVENTA1 0x21
#define AB3100_EVENTA2 0x22
#define AB3100_EVENTA3 0x23
/* AB3100 DAC converter registers */
#define AB3100_DIS 0x00
#define AB3100_D0C 0x01
#define AB3100_D1C 0x02
#define AB3100_D2C 0x03
#define AB3100_D3C 0x04
/* Chip ID register */
#define AB3100_CID 0x20
/* AB3100 interrupt registers */
#define AB3100_IMRA1 0x24
#define AB3100_IMRA2 0x25
#define AB3100_IMRA3 0x26
#define AB3100_IMRB1 0x2B
#define AB3100_IMRB2 0x2C
#define AB3100_IMRB3 0x2D
/* System Power Monitoring and control registers */
#define AB3100_MCA 0x2E
#define AB3100_MCB 0x2F
/* SIM power up */
#define AB3100_SUP 0x50
/*
* I2C communication
*
* The AB3100 is usually assigned address 0x48 (7-bit)
* The chip is defined in the platform i2c_board_data section.
*/
static int ab3100_get_chip_id(struct device *dev)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
return (int)ab3100->chip_id;
}
static int ab3100_set_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 regval)
{
u8 regandval[2] = {reg, regval};
int err;
err = mutex_lock_interruptible(&ab3100->access_mutex);
if (err)
return err;
/*
* A two-byte write message with the first byte containing the register
* number and the second byte containing the value to be written
* effectively sets a register in the AB3100.
*/
err = i2c_master_send(ab3100->i2c_client, regandval, 2);
if (err < 0) {
dev_err(ab3100->dev,
"write error (write register): %d\n",
err);
} else if (err != 2) {
dev_err(ab3100->dev,
"write error (write register)\n"
" %d bytes transferred (expected 2)\n",
err);
err = -EIO;
} else {
/* All is well */
err = 0;
}
mutex_unlock(&ab3100->access_mutex);
return err;
}
static int set_register_interruptible(struct device *dev,
u8 bank, u8 reg, u8 value)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
return ab3100_set_register_interruptible(ab3100, reg, value);
}
/*
* The test registers exist at an I2C bus address up one
* from the ordinary base. They are not supposed to be used
* in production code, but sometimes you have to do that
* anyway. It's currently only used from this file so declare
* it static and do not export.
*/
static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 regval)
{
u8 regandval[2] = {reg, regval};
int err;
err = mutex_lock_interruptible(&ab3100->access_mutex);
if (err)
return err;
err = i2c_master_send(ab3100->testreg_client, regandval, 2);
if (err < 0) {
dev_err(ab3100->dev,
"write error (write test register): %d\n",
err);
} else if (err != 2) {
dev_err(ab3100->dev,
"write error (write test register)\n"
" %d bytes transferred (expected 2)\n",
err);
err = -EIO;
} else {
/* All is well */
err = 0;
}
mutex_unlock(&ab3100->access_mutex);
return err;
}
static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 *regval)
{
int err;
err = mutex_lock_interruptible(&ab3100->access_mutex);
if (err)
return err;
/*
* AB3100 require an I2C "stop" command between each message, else
* it will not work. The only way of achieveing this with the
* message transport layer is to send the read and write messages
* separately.
*/
err = i2c_master_send(ab3100->i2c_client, &reg, 1);
if (err < 0) {
dev_err(ab3100->dev,
"write error (send register address): %d\n",
err);
goto get_reg_out_unlock;
} else if (err != 1) {
dev_err(ab3100->dev,
"write error (send register address)\n"
" %d bytes transferred (expected 1)\n",
err);
err = -EIO;
goto get_reg_out_unlock;
} else {
/* All is well */
err = 0;
}
err = i2c_master_recv(ab3100->i2c_client, regval, 1);
if (err < 0) {
dev_err(ab3100->dev,
"write error (read register): %d\n",
err);
goto get_reg_out_unlock;
} else if (err != 1) {
dev_err(ab3100->dev,
"write error (read register)\n"
" %d bytes transferred (expected 1)\n",
err);
err = -EIO;
goto get_reg_out_unlock;
} else {
/* All is well */
err = 0;
}
get_reg_out_unlock:
mutex_unlock(&ab3100->access_mutex);
return err;
}
static int get_register_interruptible(struct device *dev, u8 bank, u8 reg,
u8 *value)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
return ab3100_get_register_interruptible(ab3100, reg, value);
}
static int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
u8 first_reg, u8 *regvals, u8 numregs)
{
int err;
if (ab3100->chip_id == 0xa0 ||
ab3100->chip_id == 0xa1)
/* These don't support paged reads */
return -EIO;
err = mutex_lock_interruptible(&ab3100->access_mutex);
if (err)
return err;
/*
* Paged read also require an I2C "stop" command.
*/
err = i2c_master_send(ab3100->i2c_client, &first_reg, 1);
if (err < 0) {
dev_err(ab3100->dev,
"write error (send first register address): %d\n",
err);
goto get_reg_page_out_unlock;
} else if (err != 1) {
dev_err(ab3100->dev,
"write error (send first register address)\n"
" %d bytes transferred (expected 1)\n",
err);
err = -EIO;
goto get_reg_page_out_unlock;
}
err = i2c_master_recv(ab3100->i2c_client, regvals, numregs);
if (err < 0) {
dev_err(ab3100->dev,
"write error (read register page): %d\n",
err);
goto get_reg_page_out_unlock;
} else if (err != numregs) {
dev_err(ab3100->dev,
"write error (read register page)\n"
" %d bytes transferred (expected %d)\n",
err, numregs);
err = -EIO;
goto get_reg_page_out_unlock;
}
/* All is well */
err = 0;
get_reg_page_out_unlock:
mutex_unlock(&ab3100->access_mutex);
return err;
}
static int get_register_page_interruptible(struct device *dev, u8 bank,
u8 first_reg, u8 *regvals, u8 numregs)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
return ab3100_get_register_page_interruptible(ab3100,
first_reg, regvals, numregs);
}
static int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
u8 reg, u8 andmask, u8 ormask)
{
u8 regandval[2] = {reg, 0};
int err;
err = mutex_lock_interruptible(&ab3100->access_mutex);
if (err)
return err;
/* First read out the target register */
err = i2c_master_send(ab3100->i2c_client, &reg, 1);
if (err < 0) {
dev_err(ab3100->dev,
"write error (maskset send address): %d\n",
err);
goto get_maskset_unlock;
} else if (err != 1) {
dev_err(ab3100->dev,
"write error (maskset send address)\n"
" %d bytes transferred (expected 1)\n",
err);
err = -EIO;
goto get_maskset_unlock;
}
err = i2c_master_recv(ab3100->i2c_client, &regandval[1], 1);
if (err < 0) {
dev_err(ab3100->dev,
"write error (maskset read register): %d\n",
err);
goto get_maskset_unlock;
} else if (err != 1) {
dev_err(ab3100->dev,
"write error (maskset read register)\n"
" %d bytes transferred (expected 1)\n",
err);
err = -EIO;
goto get_maskset_unlock;
}
/* Modify the register */
regandval[1] &= andmask;
regandval[1] |= ormask;
/* Write the register */
err = i2c_master_send(ab3100->i2c_client, regandval, 2);
if (err < 0) {
dev_err(ab3100->dev,
"write error (write register): %d\n",
err);
goto get_maskset_unlock;
} else if (err != 2) {
dev_err(ab3100->dev,
"write error (write register)\n"
" %d bytes transferred (expected 2)\n",
err);
err = -EIO;
goto get_maskset_unlock;
}
/* All is well */
err = 0;
get_maskset_unlock:
mutex_unlock(&ab3100->access_mutex);
return err;
}
static int mask_and_set_register_interruptible(struct device *dev, u8 bank,
u8 reg, u8 bitmask, u8 bitvalues)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
return ab3100_mask_and_set_register_interruptible(ab3100,
reg, bitmask, (bitmask & bitvalues));
}
/*
* Register a simple callback for handling any AB3100 events.
*/
int ab3100_event_register(struct ab3100 *ab3100,
struct notifier_block *nb)
{
return blocking_notifier_chain_register(&ab3100->event_subscribers,
nb);
}
EXPORT_SYMBOL(ab3100_event_register);
/*
* Remove a previously registered callback.
*/
int ab3100_event_unregister(struct ab3100 *ab3100,
struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&ab3100->event_subscribers,
nb);
}
EXPORT_SYMBOL(ab3100_event_unregister);
static int ab3100_event_registers_startup_state_get(struct device *dev,
u8 *event)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
if (!ab3100->startup_events_read)
return -EAGAIN; /* Try again later */
memcpy(event, ab3100->startup_events, 3);
return 0;
}
static struct abx500_ops ab3100_ops = {
.get_chip_id = ab3100_get_chip_id,
.set_register = set_register_interruptible,
.get_register = get_register_interruptible,
.get_register_page = get_register_page_interruptible,
.set_register_page = NULL,
.mask_and_set_register = mask_and_set_register_interruptible,
.event_registers_startup_state_get =
ab3100_event_registers_startup_state_get,
.startup_irq_enabled = NULL,
};
/*
* This is a threaded interrupt handler so we can make some
* I2C calls etc.
*/
static irqreturn_t ab3100_irq_handler(int irq, void *data)
{
struct ab3100 *ab3100 = data;
u8 event_regs[3];
u32 fatevent;
int err;
err = ab3100_get_register_page_interruptible(ab3100, AB3100_EVENTA1,
event_regs, 3);
if (err)
goto err_event;
fatevent = (event_regs[0] << 16) |
(event_regs[1] << 8) |
event_regs[2];
if (!ab3100->startup_events_read) {
ab3100->startup_events[0] = event_regs[0];
ab3100->startup_events[1] = event_regs[1];
ab3100->startup_events[2] = event_regs[2];
ab3100->startup_events_read = true;
}
/*
* The notified parties will have to mask out the events
* they're interested in and react to them. They will be
* notified on all events, then they use the fatevent value
* to determine if they're interested.
*/
blocking_notifier_call_chain(&ab3100->event_subscribers,
fatevent, NULL);
dev_dbg(ab3100->dev,
"IRQ Event: 0x%08x\n", fatevent);
return IRQ_HANDLED;
err_event:
dev_dbg(ab3100->dev,
"error reading event status\n");
return IRQ_HANDLED;
}
#ifdef CONFIG_DEBUG_FS
/*
* Some debugfs entries only exposed if we're using debug
*/
static int ab3100_registers_print(struct seq_file *s, void *p)
{
struct ab3100 *ab3100 = s->private;
u8 value;
u8 reg;
seq_puts(s, "AB3100 registers:\n");
for (reg = 0; reg < 0xff; reg++) {
ab3100_get_register_interruptible(ab3100, reg, &value);
seq_printf(s, "[0x%x]: 0x%x\n", reg, value);
}
return 0;
}
static int ab3100_registers_open(struct inode *inode, struct file *file)
{
return single_open(file, ab3100_registers_print, inode->i_private);
}
static const struct file_operations ab3100_registers_fops = {
.open = ab3100_registers_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
struct ab3100_get_set_reg_priv {
struct ab3100 *ab3100;
bool mode;
};
static ssize_t ab3100_get_set_reg(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ab3100_get_set_reg_priv *priv = file->private_data;
struct ab3100 *ab3100 = priv->ab3100;
char buf[32];
ssize_t buf_size;
int regp;
u8 user_reg;
int err;
int i = 0;
/* Get userspace string and assure termination */
buf_size = min((ssize_t)count, (ssize_t)(sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
/*
* The idea is here to parse a string which is either
* "0xnn" for reading a register, or "0xaa 0xbb" for
* writing 0xbb to the register 0xaa. First move past
* whitespace and then begin to parse the register.
*/
while ((i < buf_size) && (buf[i] == ' '))
i++;
regp = i;
/*
* Advance pointer to end of string then terminate
* the register string. This is needed to satisfy
* the kstrtou8() function.
*/
while ((i < buf_size) && (buf[i] != ' '))
i++;
buf[i] = '\0';
err = kstrtou8(&buf[regp], 16, &user_reg);
if (err)
return err;
/* Either we read or we write a register here */
if (!priv->mode) {
/* Reading */
u8 regvalue;
ab3100_get_register_interruptible(ab3100, user_reg, &regvalue);
dev_info(ab3100->dev,
"debug read AB3100 reg[0x%02x]: 0x%02x\n",
user_reg, regvalue);
} else {
int valp;
u8 user_value;
u8 regvalue;
/*
* Writing, we need some value to write to
* the register so keep parsing the string
* from userspace.
*/
i++;
while ((i < buf_size) && (buf[i] == ' '))
i++;
valp = i;
while ((i < buf_size) && (buf[i] != ' '))
i++;
buf[i] = '\0';
err = kstrtou8(&buf[valp], 16, &user_value);
if (err)
return err;
ab3100_set_register_interruptible(ab3100, user_reg, user_value);
ab3100_get_register_interruptible(ab3100, user_reg, &regvalue);
dev_info(ab3100->dev,
"debug write reg[0x%02x]\n"
" with 0x%02x, after readback: 0x%02x\n",
user_reg, user_value, regvalue);
}
return buf_size;
}
static const struct file_operations ab3100_get_set_reg_fops = {
.open = simple_open,
.write = ab3100_get_set_reg,
.llseek = noop_llseek,
};
static struct ab3100_get_set_reg_priv ab3100_get_priv;
static struct ab3100_get_set_reg_priv ab3100_set_priv;
static void ab3100_setup_debugfs(struct ab3100 *ab3100)
{
struct dentry *ab3100_dir;
ab3100_dir = debugfs_create_dir("ab3100", NULL);
debugfs_create_file("registers", S_IRUGO, ab3100_dir, ab3100,
&ab3100_registers_fops);
ab3100_get_priv.ab3100 = ab3100;
ab3100_get_priv.mode = false;
debugfs_create_file("get_reg", S_IWUSR, ab3100_dir, &ab3100_get_priv,
&ab3100_get_set_reg_fops);
ab3100_set_priv.ab3100 = ab3100;
ab3100_set_priv.mode = true;
debugfs_create_file("set_reg", S_IWUSR, ab3100_dir, &ab3100_set_priv,
&ab3100_get_set_reg_fops);
}
#else
static inline void ab3100_setup_debugfs(struct ab3100 *ab3100)
{
}
#endif
/*
* Basic set-up, datastructure creation/destruction and I2C interface.
* This sets up a default config in the AB3100 chip so that it
* will work as expected.
*/
struct ab3100_init_setting {
u8 abreg;
u8 setting;
};
static const struct ab3100_init_setting ab3100_init_settings[] = {
{
.abreg = AB3100_MCA,
.setting = 0x01
}, {
.abreg = AB3100_MCB,
.setting = 0x30
}, {
.abreg = AB3100_IMRA1,
.setting = 0x00
}, {
.abreg = AB3100_IMRA2,
.setting = 0xFF
}, {
.abreg = AB3100_IMRA3,
.setting = 0x01
}, {
.abreg = AB3100_IMRB1,
.setting = 0xBF
}, {
.abreg = AB3100_IMRB2,
.setting = 0xFF
}, {
.abreg = AB3100_IMRB3,
.setting = 0xFF
}, {
.abreg = AB3100_SUP,
.setting = 0x00
}, {
.abreg = AB3100_DIS,
.setting = 0xF0
}, {
.abreg = AB3100_D0C,
.setting = 0x00
}, {
.abreg = AB3100_D1C,
.setting = 0x00
}, {
.abreg = AB3100_D2C,
.setting = 0x00
}, {
.abreg = AB3100_D3C,
.setting = 0x00
},
};
static int ab3100_setup(struct ab3100 *ab3100)
{
int err = 0;
int i;
for (i = 0; i < ARRAY_SIZE(ab3100_init_settings); i++) {
err = ab3100_set_register_interruptible(ab3100,
ab3100_init_settings[i].abreg,
ab3100_init_settings[i].setting);
if (err)
goto exit_no_setup;
}
/*
* Special trick to make the AB3100 use the 32kHz clock (RTC)
* bit 3 in test register 0x02 is a special, undocumented test
* register bit that only exist in AB3100 P1E
*/
if (ab3100->chip_id == 0xc4) {
dev_warn(ab3100->dev,
"AB3100 P1E variant detected forcing chip to 32KHz\n");
err = ab3100_set_test_register_interruptible(ab3100,
0x02, 0x08);
}
exit_no_setup:
return err;
}
/* The subdevices of the AB3100 */
static struct mfd_cell ab3100_devs[] = {
{
.name = "ab3100-dac",
.id = -1,
},
{
.name = "ab3100-leds",
.id = -1,
},
{
.name = "ab3100-power",
.id = -1,
},
{
.name = "ab3100-regulators",
.of_compatible = "stericsson,ab3100-regulators",
.id = -1,
},
{
.name = "ab3100-sim",
.id = -1,
},
{
.name = "ab3100-uart",
.id = -1,
},
{
.name = "ab3100-rtc",
.id = -1,
},
{
.name = "ab3100-charger",
.id = -1,
},
{
.name = "ab3100-boost",
.id = -1,
},
{
.name = "ab3100-adc",
.id = -1,
},
{
.name = "ab3100-fuelgauge",
.id = -1,
},
{
.name = "ab3100-vibrator",
.id = -1,
},
{
.name = "ab3100-otp",
.id = -1,
},
{
.name = "ab3100-codec",
.id = -1,
},
};
struct ab_family_id {
u8 id;
char *name;
};
static const struct ab_family_id ids[] = {
/* AB3100 */
{
.id = 0xc0,
.name = "P1A"
}, {
.id = 0xc1,
.name = "P1B"
}, {
.id = 0xc2,
.name = "P1C"
}, {
.id = 0xc3,
.name = "P1D"
}, {
.id = 0xc4,
.name = "P1E"
}, {
.id = 0xc5,
.name = "P1F/R1A"
}, {
.id = 0xc6,
.name = "P1G/R1A"
}, {
.id = 0xc7,
.name = "P2A/R2A"
}, {
.id = 0xc8,
.name = "P2B/R2B"
},
/* AB3000 variants, not supported */
{
.id = 0xa0
}, {
.id = 0xa1
}, {
.id = 0xa2
}, {
.id = 0xa3
}, {
.id = 0xa4
}, {
.id = 0xa5
}, {
.id = 0xa6
}, {
.id = 0xa7
},
/* Terminator */
{
.id = 0x00,
},
};
static int ab3100_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ab3100 *ab3100;
struct ab3100_platform_data *ab3100_plf_data =
dev_get_platdata(&client->dev);
int err;
int i;
ab3100 = devm_kzalloc(&client->dev, sizeof(struct ab3100), GFP_KERNEL);
if (!ab3100)
return -ENOMEM;
/* Initialize data structure */
mutex_init(&ab3100->access_mutex);
BLOCKING_INIT_NOTIFIER_HEAD(&ab3100->event_subscribers);
ab3100->i2c_client = client;
ab3100->dev = &ab3100->i2c_client->dev;
i2c_set_clientdata(client, ab3100);
/* Read chip ID register */
err = ab3100_get_register_interruptible(ab3100, AB3100_CID,
&ab3100->chip_id);
if (err) {
dev_err(&client->dev,
"failed to communicate with AB3100 chip\n");
goto exit_no_detect;
}
for (i = 0; ids[i].id != 0x0; i++) {
if (ids[i].id == ab3100->chip_id) {
if (ids[i].name)
break;
dev_err(&client->dev, "AB3000 is not supported\n");
goto exit_no_detect;
}
}
snprintf(&ab3100->chip_name[0],
sizeof(ab3100->chip_name) - 1, "AB3100 %s", ids[i].name);
if (ids[i].id == 0x0) {
dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n",
ab3100->chip_id);
dev_err(&client->dev,
"accepting it anyway. Please update the driver.\n");
goto exit_no_detect;
}
dev_info(&client->dev, "Detected chip: %s\n",
&ab3100->chip_name[0]);
/* Attach a second dummy i2c_client to the test register address */
ab3100->testreg_client = i2c_new_dummy_device(client->adapter,
client->addr + 1);
if (IS_ERR(ab3100->testreg_client)) {
err = PTR_ERR(ab3100->testreg_client);
goto exit_no_testreg_client;
}
err = ab3100_setup(ab3100);
if (err)
goto exit_no_setup;
err = devm_request_threaded_irq(&client->dev,
client->irq, NULL, ab3100_irq_handler,
IRQF_ONESHOT, "ab3100-core", ab3100);
if (err)
goto exit_no_irq;
err = abx500_register_ops(&client->dev, &ab3100_ops);
if (err)
goto exit_no_ops;
/* Set up and register the platform devices. */
for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) {
ab3100_devs[i].platform_data = ab3100_plf_data;
ab3100_devs[i].pdata_size = sizeof(struct ab3100_platform_data);
}
err = mfd_add_devices(&client->dev, 0, ab3100_devs,
ARRAY_SIZE(ab3100_devs), NULL, 0, NULL);
ab3100_setup_debugfs(ab3100);
return 0;
exit_no_ops:
exit_no_irq:
exit_no_setup:
i2c_unregister_device(ab3100->testreg_client);
exit_no_testreg_client:
exit_no_detect:
return err;
}
static const struct i2c_device_id ab3100_id[] = {
{ "ab3100", 0 },
{ }
};
static struct i2c_driver ab3100_driver = {
.driver = {
.name = "ab3100",
.suppress_bind_attrs = true,
},
.id_table = ab3100_id,
.probe = ab3100_probe,
};
static int __init ab3100_i2c_init(void)
{
return i2c_add_driver(&ab3100_driver);
}
subsys_initcall(ab3100_i2c_init);

View File

@ -1,240 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/mfd/ab3100_otp.c
*
* Copyright (C) 2007-2009 ST-Ericsson AB
* Driver to read out OTP from the AB3100 Mixed-signal circuit
* Author: Linus Walleij <linus.walleij@stericsson.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mfd/abx500.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
/* The OTP registers */
#define AB3100_OTP0 0xb0
#define AB3100_OTP1 0xb1
#define AB3100_OTP2 0xb2
#define AB3100_OTP3 0xb3
#define AB3100_OTP4 0xb4
#define AB3100_OTP5 0xb5
#define AB3100_OTP6 0xb6
#define AB3100_OTP7 0xb7
#define AB3100_OTPP 0xbf
/**
* struct ab3100_otp
* @dev: containing device
* @locked: whether the OTP is locked, after locking, no more bits
* can be changed but before locking it is still possible
* to change bits from 1->0.
* @freq: clocking frequency for the OTP, this frequency is either
* 32768Hz or 1MHz/30
* @paf: product activation flag, indicates whether this is a real
* product (paf true) or a lab board etc (paf false)
* @imeich: if this is set it is possible to override the
* IMEI number found in the tac, fac and svn fields with
* (secured) software
* @cid: customer ID
* @tac: type allocation code of the IMEI
* @fac: final assembly code of the IMEI
* @svn: software version number of the IMEI
* @debugfs: a debugfs file used when dumping to file
*/
struct ab3100_otp {
struct device *dev;
bool locked;
u32 freq;
bool paf;
bool imeich;
u16 cid:14;
u32 tac:20;
u8 fac;
u32 svn:20;
struct dentry *debugfs;
};
static int __init ab3100_otp_read(struct ab3100_otp *otp)
{
u8 otpval[8];
u8 otpp;
int err;
err = abx500_get_register_interruptible(otp->dev, 0,
AB3100_OTPP, &otpp);
if (err) {
dev_err(otp->dev, "unable to read OTPP register\n");
return err;
}
err = abx500_get_register_page_interruptible(otp->dev, 0,
AB3100_OTP0, otpval, 8);
if (err) {
dev_err(otp->dev, "unable to read OTP register page\n");
return err;
}
/* Cache OTP properties, they never change by nature */
otp->locked = (otpp & 0x80);
otp->freq = (otpp & 0x40) ? 32768 : 34100;
otp->paf = (otpval[1] & 0x80);
otp->imeich = (otpval[1] & 0x40);
otp->cid = ((otpval[1] << 8) | otpval[0]) & 0x3fff;
otp->tac = ((otpval[4] & 0x0f) << 16) | (otpval[3] << 8) | otpval[2];
otp->fac = ((otpval[5] & 0x0f) << 4) | (otpval[4] >> 4);
otp->svn = (otpval[7] << 12) | (otpval[6] << 4) | (otpval[5] >> 4);
return 0;
}
/*
* This is a simple debugfs human-readable file that dumps out
* the contents of the OTP.
*/
#ifdef CONFIG_DEBUG_FS
static int ab3100_show_otp(struct seq_file *s, void *v)
{
struct ab3100_otp *otp = s->private;
seq_printf(s, "OTP is %s\n", otp->locked ? "LOCKED" : "UNLOCKED");
seq_printf(s, "OTP clock switch startup is %uHz\n", otp->freq);
seq_printf(s, "PAF is %s\n", otp->paf ? "SET" : "NOT SET");
seq_printf(s, "IMEI is %s\n", otp->imeich ?
"CHANGEABLE" : "NOT CHANGEABLE");
seq_printf(s, "CID: 0x%04x (decimal: %d)\n", otp->cid, otp->cid);
seq_printf(s, "IMEI: %u-%u-%u\n", otp->tac, otp->fac, otp->svn);
return 0;
}
static int ab3100_otp_open(struct inode *inode, struct file *file)
{
return single_open(file, ab3100_show_otp, inode->i_private);
}
static const struct file_operations ab3100_otp_operations = {
.open = ab3100_otp_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void __init ab3100_otp_init_debugfs(struct device *dev,
struct ab3100_otp *otp)
{
otp->debugfs = debugfs_create_file("ab3100_otp", S_IFREG | S_IRUGO,
NULL, otp, &ab3100_otp_operations);
}
static void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
{
debugfs_remove(otp->debugfs);
}
#else
/* Compile this out if debugfs not selected */
static inline void __init ab3100_otp_init_debugfs(struct device *dev,
struct ab3100_otp *otp)
{
}
static inline void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
{
}
#endif
#define SHOW_AB3100_ATTR(name) \
static ssize_t ab3100_otp_##name##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{\
struct ab3100_otp *otp = dev_get_drvdata(dev); \
return sprintf(buf, "%u\n", otp->name); \
}
SHOW_AB3100_ATTR(locked)
SHOW_AB3100_ATTR(freq)
SHOW_AB3100_ATTR(paf)
SHOW_AB3100_ATTR(imeich)
SHOW_AB3100_ATTR(cid)
SHOW_AB3100_ATTR(fac)
SHOW_AB3100_ATTR(tac)
SHOW_AB3100_ATTR(svn)
static struct device_attribute ab3100_otp_attrs[] = {
__ATTR(locked, S_IRUGO, ab3100_otp_locked_show, NULL),
__ATTR(freq, S_IRUGO, ab3100_otp_freq_show, NULL),
__ATTR(paf, S_IRUGO, ab3100_otp_paf_show, NULL),
__ATTR(imeich, S_IRUGO, ab3100_otp_imeich_show, NULL),
__ATTR(cid, S_IRUGO, ab3100_otp_cid_show, NULL),
__ATTR(fac, S_IRUGO, ab3100_otp_fac_show, NULL),
__ATTR(tac, S_IRUGO, ab3100_otp_tac_show, NULL),
__ATTR(svn, S_IRUGO, ab3100_otp_svn_show, NULL),
};
static int __init ab3100_otp_probe(struct platform_device *pdev)
{
struct ab3100_otp *otp;
int err = 0;
int i;
otp = devm_kzalloc(&pdev->dev, sizeof(struct ab3100_otp), GFP_KERNEL);
if (!otp)
return -ENOMEM;
otp->dev = &pdev->dev;
/* Replace platform data coming in with a local struct */
platform_set_drvdata(pdev, otp);
err = ab3100_otp_read(otp);
if (err)
return err;
dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
/* sysfs entries */
for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++) {
err = device_create_file(&pdev->dev,
&ab3100_otp_attrs[i]);
if (err)
goto err;
}
/* debugfs entries */
ab3100_otp_init_debugfs(&pdev->dev, otp);
return 0;
err:
while (--i >= 0)
device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]);
return err;
}
static int __exit ab3100_otp_remove(struct platform_device *pdev)
{
struct ab3100_otp *otp = platform_get_drvdata(pdev);
int i;
for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
device_remove_file(&pdev->dev,
&ab3100_otp_attrs[i]);
ab3100_otp_exit_debugfs(otp);
return 0;
}
static struct platform_driver ab3100_otp_driver = {
.driver = {
.name = "ab3100-otp",
},
.remove = __exit_p(ab3100_otp_remove),
};
module_platform_driver_probe(ab3100_otp_driver, ab3100_otp_probe);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
MODULE_DESCRIPTION("AB3100 OTP Readout Driver");
MODULE_LICENSE("GPL");

View File

@ -120,12 +120,6 @@
static DEFINE_SPINLOCK(on_stat_lock);
static u8 turn_on_stat_mask = 0xFF;
static u8 turn_on_stat_set;
static bool no_bm; /* No battery management */
/*
* not really modular, but the easiest way to keep compat with existing
* bootargs behaviour is to continue using module_param here.
*/
module_param(no_bm, bool, S_IRUGO);
#define AB9540_MODEM_CTRL2_REG 0x23
#define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT BIT(2)
@ -1254,14 +1248,12 @@ static int ab8500_probe(struct platform_device *pdev)
if (ret)
return ret;
if (!no_bm) {
/* Add battery management devices */
ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
ARRAY_SIZE(ab8500_bm_devs), NULL,
0, ab8500->domain);
if (ret)
dev_err(ab8500->dev, "error adding bm devices\n");
}
if (((is_ab8505(ab8500) || is_ab9540(ab8500)) &&
ab8500->chip_id >= AB8500_CUT2P0) || is_ab8540(ab8500))

View File

@ -881,11 +881,6 @@ static const char * const wm5102_supplies[] = {
static const struct mfd_cell wm5102_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-gpio" },
{
.name = "arizona-extcon",
.parent_supplies = wm5102_supplies,
.num_parent_supplies = 1, /* We only need MICVDD */
},
{ .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
{
@ -898,11 +893,6 @@ static const struct mfd_cell wm5102_devs[] = {
static const struct mfd_cell wm5110_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-gpio" },
{
.name = "arizona-extcon",
.parent_supplies = wm5102_supplies,
.num_parent_supplies = 1, /* We only need MICVDD */
},
{ .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
{
@ -939,11 +929,6 @@ static const char * const wm8997_supplies[] = {
static const struct mfd_cell wm8997_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-gpio" },
{
.name = "arizona-extcon",
.parent_supplies = wm8997_supplies,
.num_parent_supplies = 1, /* We only need MICVDD */
},
{ .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
{
@ -956,11 +941,6 @@ static const struct mfd_cell wm8997_devs[] = {
static const struct mfd_cell wm8998_devs[] = {
{ .name = "arizona-micsupp" },
{ .name = "arizona-gpio" },
{
.name = "arizona-extcon",
.parent_supplies = wm5102_supplies,
.num_parent_supplies = 1, /* We only need MICVDD */
},
{ .name = "arizona-haptics" },
{ .name = "arizona-pwm" },
{

View File

@ -100,7 +100,7 @@ static irqreturn_t arizona_irq_thread(int irq, void *data)
unsigned int val;
int ret;
ret = pm_runtime_get_sync(arizona->dev);
ret = pm_runtime_resume_and_get(arizona->dev);
if (ret < 0) {
dev_err(arizona->dev, "Failed to resume device: %d\n", ret);
return IRQ_NONE;

View File

@ -25,8 +25,8 @@
#include "arizona.h"
#ifdef CONFIG_ACPI
const struct acpi_gpio_params reset_gpios = { 1, 0, false };
const struct acpi_gpio_params ldoena_gpios = { 2, 0, false };
static const struct acpi_gpio_params reset_gpios = { 1, 0, false };
static const struct acpi_gpio_params ldoena_gpios = { 2, 0, false };
static const struct acpi_gpio_mapping arizona_acpi_gpios[] = {
{ "reset-gpios", &reset_gpios, 1, },

310
drivers/mfd/atc260x-core.c Normal file
View File

@ -0,0 +1,310 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Core support for ATC260x PMICs
*
* Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
* Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
*/
#include <linux/interrupt.h>
#include <linux/mfd/atc260x/core.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#define ATC260X_CHIP_REV_MAX 31
struct atc260x_init_regs {
unsigned int cmu_devrst;
unsigned int cmu_devrst_ints;
unsigned int ints_msk;
unsigned int pad_en;
unsigned int pad_en_extirq;
};
static void regmap_lock_mutex(void *__mutex)
{
struct mutex *mutex = __mutex;
/*
* Using regmap within an atomic context (e.g. accessing a PMIC when
* powering system down) is normally allowed only if the regmap type
* is MMIO and the regcache type is either REGCACHE_NONE or
* REGCACHE_FLAT. For slow buses like I2C and SPI, the regmap is
* internally protected by a mutex which is acquired non-atomically.
*
* Let's improve this by using a customized locking scheme inspired
* from I2C atomic transfer. See i2c_in_atomic_xfer_mode() for a
* starting point.
*/
if (system_state > SYSTEM_RUNNING && irqs_disabled())
mutex_trylock(mutex);
else
mutex_lock(mutex);
}
static void regmap_unlock_mutex(void *__mutex)
{
struct mutex *mutex = __mutex;
mutex_unlock(mutex);
}
static const struct regmap_config atc2603c_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = ATC2603C_SADDR,
.cache_type = REGCACHE_NONE,
};
static const struct regmap_config atc2609a_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = ATC2609A_SADDR,
.cache_type = REGCACHE_NONE,
};
static const struct regmap_irq atc2603c_regmap_irqs[] = {
REGMAP_IRQ_REG(ATC2603C_IRQ_AUDIO, 0, ATC2603C_INTS_MSK_AUDIO),
REGMAP_IRQ_REG(ATC2603C_IRQ_OV, 0, ATC2603C_INTS_MSK_OV),
REGMAP_IRQ_REG(ATC2603C_IRQ_OC, 0, ATC2603C_INTS_MSK_OC),
REGMAP_IRQ_REG(ATC2603C_IRQ_OT, 0, ATC2603C_INTS_MSK_OT),
REGMAP_IRQ_REG(ATC2603C_IRQ_UV, 0, ATC2603C_INTS_MSK_UV),
REGMAP_IRQ_REG(ATC2603C_IRQ_ALARM, 0, ATC2603C_INTS_MSK_ALARM),
REGMAP_IRQ_REG(ATC2603C_IRQ_ONOFF, 0, ATC2603C_INTS_MSK_ONOFF),
REGMAP_IRQ_REG(ATC2603C_IRQ_SGPIO, 0, ATC2603C_INTS_MSK_SGPIO),
REGMAP_IRQ_REG(ATC2603C_IRQ_IR, 0, ATC2603C_INTS_MSK_IR),
REGMAP_IRQ_REG(ATC2603C_IRQ_REMCON, 0, ATC2603C_INTS_MSK_REMCON),
REGMAP_IRQ_REG(ATC2603C_IRQ_POWER_IN, 0, ATC2603C_INTS_MSK_POWERIN),
};
static const struct regmap_irq atc2609a_regmap_irqs[] = {
REGMAP_IRQ_REG(ATC2609A_IRQ_AUDIO, 0, ATC2609A_INTS_MSK_AUDIO),
REGMAP_IRQ_REG(ATC2609A_IRQ_OV, 0, ATC2609A_INTS_MSK_OV),
REGMAP_IRQ_REG(ATC2609A_IRQ_OC, 0, ATC2609A_INTS_MSK_OC),
REGMAP_IRQ_REG(ATC2609A_IRQ_OT, 0, ATC2609A_INTS_MSK_OT),
REGMAP_IRQ_REG(ATC2609A_IRQ_UV, 0, ATC2609A_INTS_MSK_UV),
REGMAP_IRQ_REG(ATC2609A_IRQ_ALARM, 0, ATC2609A_INTS_MSK_ALARM),
REGMAP_IRQ_REG(ATC2609A_IRQ_ONOFF, 0, ATC2609A_INTS_MSK_ONOFF),
REGMAP_IRQ_REG(ATC2609A_IRQ_WKUP, 0, ATC2609A_INTS_MSK_WKUP),
REGMAP_IRQ_REG(ATC2609A_IRQ_IR, 0, ATC2609A_INTS_MSK_IR),
REGMAP_IRQ_REG(ATC2609A_IRQ_REMCON, 0, ATC2609A_INTS_MSK_REMCON),
REGMAP_IRQ_REG(ATC2609A_IRQ_POWER_IN, 0, ATC2609A_INTS_MSK_POWERIN),
};
static const struct regmap_irq_chip atc2603c_regmap_irq_chip = {
.name = "atc2603c",
.irqs = atc2603c_regmap_irqs,
.num_irqs = ARRAY_SIZE(atc2603c_regmap_irqs),
.num_regs = 1,
.status_base = ATC2603C_INTS_PD,
.mask_base = ATC2603C_INTS_MSK,
.mask_invert = true,
};
static const struct regmap_irq_chip atc2609a_regmap_irq_chip = {
.name = "atc2609a",
.irqs = atc2609a_regmap_irqs,
.num_irqs = ARRAY_SIZE(atc2609a_regmap_irqs),
.num_regs = 1,
.status_base = ATC2609A_INTS_PD,
.mask_base = ATC2609A_INTS_MSK,
.mask_invert = true,
};
static const struct resource atc2603c_onkey_resources[] = {
DEFINE_RES_IRQ(ATC2603C_IRQ_ONOFF),
};
static const struct resource atc2609a_onkey_resources[] = {
DEFINE_RES_IRQ(ATC2609A_IRQ_ONOFF),
};
static const struct mfd_cell atc2603c_mfd_cells[] = {
{ .name = "atc260x-regulator" },
{ .name = "atc260x-pwrc" },
{
.name = "atc260x-onkey",
.num_resources = ARRAY_SIZE(atc2603c_onkey_resources),
.resources = atc2603c_onkey_resources,
},
};
static const struct mfd_cell atc2609a_mfd_cells[] = {
{ .name = "atc260x-regulator" },
{ .name = "atc260x-pwrc" },
{
.name = "atc260x-onkey",
.num_resources = ARRAY_SIZE(atc2609a_onkey_resources),
.resources = atc2609a_onkey_resources,
},
};
static const struct atc260x_init_regs atc2603c_init_regs = {
.cmu_devrst = ATC2603C_CMU_DEVRST,
.cmu_devrst_ints = ATC2603C_CMU_DEVRST_INTS,
.ints_msk = ATC2603C_INTS_MSK,
.pad_en = ATC2603C_PAD_EN,
.pad_en_extirq = ATC2603C_PAD_EN_EXTIRQ,
};
static const struct atc260x_init_regs atc2609a_init_regs = {
.cmu_devrst = ATC2609A_CMU_DEVRST,
.cmu_devrst_ints = ATC2609A_CMU_DEVRST_INTS,
.ints_msk = ATC2609A_INTS_MSK,
.pad_en = ATC2609A_PAD_EN,
.pad_en_extirq = ATC2609A_PAD_EN_EXTIRQ,
};
static void atc260x_cmu_reset(struct atc260x *atc260x)
{
const struct atc260x_init_regs *regs = atc260x->init_regs;
/* Assert reset */
regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
regs->cmu_devrst_ints, ~regs->cmu_devrst_ints);
/* De-assert reset */
regmap_update_bits(atc260x->regmap, regs->cmu_devrst,
regs->cmu_devrst_ints, regs->cmu_devrst_ints);
}
static void atc260x_dev_init(struct atc260x *atc260x)
{
const struct atc260x_init_regs *regs = atc260x->init_regs;
/* Initialize interrupt block */
atc260x_cmu_reset(atc260x);
/* Disable all interrupt sources */
regmap_write(atc260x->regmap, regs->ints_msk, 0);
/* Enable EXTIRQ pad */
regmap_update_bits(atc260x->regmap, regs->pad_en,
regs->pad_en_extirq, regs->pad_en_extirq);
}
/**
* atc260x_match_device(): Setup ATC260x variant related fields
*
* @atc260x: ATC260x device to setup (.dev field must be set)
* @regmap_cfg: regmap config associated with this ATC260x device
*
* This lets the ATC260x core configure the MFD cells and register maps
* for later use.
*/
int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg)
{
struct device *dev = atc260x->dev;
const void *of_data;
of_data = of_device_get_match_data(dev);
if (!of_data)
return -ENODEV;
atc260x->ic_type = (unsigned long)of_data;
switch (atc260x->ic_type) {
case ATC2603C:
*regmap_cfg = atc2603c_regmap_config;
atc260x->regmap_irq_chip = &atc2603c_regmap_irq_chip;
atc260x->cells = atc2603c_mfd_cells;
atc260x->nr_cells = ARRAY_SIZE(atc2603c_mfd_cells);
atc260x->type_name = "atc2603c";
atc260x->rev_reg = ATC2603C_CHIP_VER;
atc260x->init_regs = &atc2603c_init_regs;
break;
case ATC2609A:
*regmap_cfg = atc2609a_regmap_config;
atc260x->regmap_irq_chip = &atc2609a_regmap_irq_chip;
atc260x->cells = atc2609a_mfd_cells;
atc260x->nr_cells = ARRAY_SIZE(atc2609a_mfd_cells);
atc260x->type_name = "atc2609a";
atc260x->rev_reg = ATC2609A_CHIP_VER;
atc260x->init_regs = &atc2609a_init_regs;
break;
default:
dev_err(dev, "Unsupported ATC260x device type: %u\n",
atc260x->ic_type);
return -EINVAL;
}
atc260x->regmap_mutex = devm_kzalloc(dev, sizeof(*atc260x->regmap_mutex),
GFP_KERNEL);
if (!atc260x->regmap_mutex)
return -ENOMEM;
mutex_init(atc260x->regmap_mutex);
regmap_cfg->lock = regmap_lock_mutex,
regmap_cfg->unlock = regmap_unlock_mutex,
regmap_cfg->lock_arg = atc260x->regmap_mutex;
return 0;
}
EXPORT_SYMBOL_GPL(atc260x_match_device);
/**
* atc260x_device_probe(): Probe a configured ATC260x device
*
* @atc260x: ATC260x device to probe (must be configured)
*
* This function lets the ATC260x core register the ATC260x MFD devices
* and IRQCHIP. The ATC260x device passed in must be fully configured
* with atc260x_match_device, its IRQ set, and regmap created.
*/
int atc260x_device_probe(struct atc260x *atc260x)
{
struct device *dev = atc260x->dev;
unsigned int chip_rev;
int ret;
if (!atc260x->irq) {
dev_err(dev, "No interrupt support\n");
return -EINVAL;
}
/* Initialize the hardware */
atc260x_dev_init(atc260x);
ret = regmap_read(atc260x->regmap, atc260x->rev_reg, &chip_rev);
if (ret) {
dev_err(dev, "Failed to get chip revision\n");
return ret;
}
if (chip_rev > ATC260X_CHIP_REV_MAX) {
dev_err(dev, "Unknown chip revision: %u\n", chip_rev);
return -EINVAL;
}
atc260x->ic_ver = __ffs(chip_rev + 1U);
dev_info(dev, "Detected chip type %s rev.%c\n",
atc260x->type_name, 'A' + atc260x->ic_ver);
ret = devm_regmap_add_irq_chip(dev, atc260x->regmap, atc260x->irq, IRQF_ONESHOT,
-1, atc260x->regmap_irq_chip, &atc260x->irq_data);
if (ret) {
dev_err(dev, "Failed to add IRQ chip: %d\n", ret);
return ret;
}
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
atc260x->cells, atc260x->nr_cells, NULL, 0,
regmap_irq_get_domain(atc260x->irq_data));
if (ret) {
dev_err(dev, "Failed to add child devices: %d\n", ret);
regmap_del_irq_chip(atc260x->irq, atc260x->irq_data);
}
return ret;
}
EXPORT_SYMBOL_GPL(atc260x_device_probe);
MODULE_DESCRIPTION("ATC260x PMICs Core support");
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
MODULE_LICENSE("GPL");

64
drivers/mfd/atc260x-i2c.c Normal file
View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* I2C bus interface for ATC260x PMICs
*
* Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
* Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
*/
#include <linux/i2c.h>
#include <linux/mfd/atc260x/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
static int atc260x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct atc260x *atc260x;
struct regmap_config regmap_cfg;
int ret;
atc260x = devm_kzalloc(&client->dev, sizeof(*atc260x), GFP_KERNEL);
if (!atc260x)
return -ENOMEM;
atc260x->dev = &client->dev;
atc260x->irq = client->irq;
ret = atc260x_match_device(atc260x, &regmap_cfg);
if (ret)
return ret;
i2c_set_clientdata(client, atc260x);
atc260x->regmap = devm_regmap_init_i2c(client, &regmap_cfg);
if (IS_ERR(atc260x->regmap)) {
ret = PTR_ERR(atc260x->regmap);
dev_err(&client->dev, "failed to init regmap: %d\n", ret);
return ret;
}
return atc260x_device_probe(atc260x);
}
static const struct of_device_id atc260x_i2c_of_match[] = {
{ .compatible = "actions,atc2603c", .data = (void *)ATC2603C },
{ .compatible = "actions,atc2609a", .data = (void *)ATC2609A },
{ }
};
MODULE_DEVICE_TABLE(of, atc260x_i2c_of_match);
static struct i2c_driver atc260x_i2c_driver = {
.driver = {
.name = "atc260x",
.of_match_table = of_match_ptr(atc260x_i2c_of_match),
},
.probe = atc260x_i2c_probe,
};
module_i2c_driver(atc260x_i2c_driver);
MODULE_DESCRIPTION("ATC260x PMICs I2C bus interface");
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@gmail.com>");
MODULE_LICENSE("GPL");

View File

@ -442,6 +442,16 @@ static int da9063_i2c_probe(struct i2c_client *i2c,
return ret;
}
/* If SMBus is not available and only I2C is possible, enter I2C mode */
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
ret = regmap_clear_bits(da9063->regmap, DA9063_REG_CONFIG_J,
DA9063_TWOWIRE_TO);
if (ret < 0) {
dev_err(da9063->dev, "Failed to set Two-Wire Bus Mode.\n");
return -EIO;
}
}
return da9063_device_init(da9063, i2c->irq);
}

View File

@ -33,7 +33,7 @@ struct kb3930 {
struct gpio_descs *off_gpios;
};
struct kb3930 *kb3930_power_off;
static struct kb3930 *kb3930_power_off;
#define EC_GPIO_WAVE 0
#define EC_GPIO_OFF_MODE 1

View File

@ -22,55 +22,71 @@ static const struct intel_lpss_platform_info spt_info = {
.clk_rate = 120000000,
};
static struct property_entry spt_i2c_properties[] = {
static const struct property_entry spt_i2c_properties[] = {
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 230),
{ },
};
static const struct intel_lpss_platform_info spt_i2c_info = {
.clk_rate = 120000000,
static const struct software_node spt_i2c_node = {
.properties = spt_i2c_properties,
};
static struct property_entry uart_properties[] = {
static const struct intel_lpss_platform_info spt_i2c_info = {
.clk_rate = 120000000,
.swnode = &spt_i2c_node,
};
static const struct property_entry uart_properties[] = {
PROPERTY_ENTRY_U32("reg-io-width", 4),
PROPERTY_ENTRY_U32("reg-shift", 2),
PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
{ },
};
static const struct software_node uart_node = {
.properties = uart_properties,
};
static const struct intel_lpss_platform_info spt_uart_info = {
.clk_rate = 120000000,
.clk_con_id = "baudclk",
.properties = uart_properties,
.swnode = &uart_node,
};
static const struct intel_lpss_platform_info bxt_info = {
.clk_rate = 100000000,
};
static struct property_entry bxt_i2c_properties[] = {
static const struct property_entry bxt_i2c_properties[] = {
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 42),
PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171),
PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208),
{ },
};
static const struct intel_lpss_platform_info bxt_i2c_info = {
.clk_rate = 133000000,
static const struct software_node bxt_i2c_node = {
.properties = bxt_i2c_properties,
};
static struct property_entry apl_i2c_properties[] = {
static const struct intel_lpss_platform_info bxt_i2c_info = {
.clk_rate = 133000000,
.swnode = &bxt_i2c_node,
};
static const struct property_entry apl_i2c_properties[] = {
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 207),
PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171),
PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208),
{ },
};
static const struct software_node apl_i2c_node = {
.properties = apl_i2c_properties,
};
static const struct intel_lpss_platform_info apl_i2c_info = {
.clk_rate = 133000000,
.properties = apl_i2c_properties,
.swnode = &apl_i2c_node,
};
static const struct acpi_device_id intel_lpss_acpi_ids[] = {

View File

@ -65,27 +65,35 @@ static const struct intel_lpss_platform_info spt_info = {
.clk_rate = 120000000,
};
static struct property_entry spt_i2c_properties[] = {
static const struct property_entry spt_i2c_properties[] = {
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 230),
{ },
};
static const struct intel_lpss_platform_info spt_i2c_info = {
.clk_rate = 120000000,
static const struct software_node spt_i2c_node = {
.properties = spt_i2c_properties,
};
static struct property_entry uart_properties[] = {
static const struct intel_lpss_platform_info spt_i2c_info = {
.clk_rate = 120000000,
.swnode = &spt_i2c_node,
};
static const struct property_entry uart_properties[] = {
PROPERTY_ENTRY_U32("reg-io-width", 4),
PROPERTY_ENTRY_U32("reg-shift", 2),
PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
{ },
};
static const struct software_node uart_node = {
.properties = uart_properties,
};
static const struct intel_lpss_platform_info spt_uart_info = {
.clk_rate = 120000000,
.clk_con_id = "baudclk",
.properties = uart_properties,
.swnode = &uart_node,
};
static const struct intel_lpss_platform_info bxt_info = {
@ -95,53 +103,65 @@ static const struct intel_lpss_platform_info bxt_info = {
static const struct intel_lpss_platform_info bxt_uart_info = {
.clk_rate = 100000000,
.clk_con_id = "baudclk",
.properties = uart_properties,
.swnode = &uart_node,
};
static struct property_entry bxt_i2c_properties[] = {
static const struct property_entry bxt_i2c_properties[] = {
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 42),
PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171),
PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208),
{ },
};
static const struct intel_lpss_platform_info bxt_i2c_info = {
.clk_rate = 133000000,
static const struct software_node bxt_i2c_node = {
.properties = bxt_i2c_properties,
};
static struct property_entry apl_i2c_properties[] = {
static const struct intel_lpss_platform_info bxt_i2c_info = {
.clk_rate = 133000000,
.swnode = &bxt_i2c_node,
};
static const struct property_entry apl_i2c_properties[] = {
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 207),
PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171),
PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 208),
{ },
};
static const struct intel_lpss_platform_info apl_i2c_info = {
.clk_rate = 133000000,
static const struct software_node apl_i2c_node = {
.properties = apl_i2c_properties,
};
static struct property_entry glk_i2c_properties[] = {
static const struct intel_lpss_platform_info apl_i2c_info = {
.clk_rate = 133000000,
.swnode = &apl_i2c_node,
};
static const struct property_entry glk_i2c_properties[] = {
PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 313),
PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171),
PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 290),
{ },
};
static const struct software_node glk_i2c_node = {
.properties = glk_i2c_properties,
};
static const struct intel_lpss_platform_info glk_i2c_info = {
.clk_rate = 133000000,
.properties = glk_i2c_properties,
.swnode = &glk_i2c_node,
};
static const struct intel_lpss_platform_info cnl_i2c_info = {
.clk_rate = 216000000,
.properties = spt_i2c_properties,
.swnode = &spt_i2c_node,
};
static const struct intel_lpss_platform_info ehl_i2c_info = {
.clk_rate = 100000000,
.properties = bxt_i2c_properties,
.swnode = &bxt_i2c_node,
};
static const struct pci_device_id intel_lpss_pci_ids[] = {

View File

@ -399,7 +399,7 @@ int intel_lpss_probe(struct device *dev,
if (ret)
return ret;
lpss->cell->properties = info->properties;
lpss->cell->swnode = info->swnode;
intel_lpss_init_dev(lpss);

View File

@ -15,14 +15,14 @@
struct device;
struct resource;
struct property_entry;
struct software_node;
struct intel_lpss_platform_info {
struct resource *mem;
int irq;
unsigned long clk_rate;
const char *clk_con_id;
struct property_entry *properties;
const struct software_node *swnode;
};
int intel_lpss_probe(struct device *dev,

View File

@ -28,10 +28,23 @@ static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
{ .name = "n3000bmc-secure" },
};
static const struct regmap_range m10bmc_regmap_range[] = {
regmap_reg_range(M10BMC_LEGACY_BUILD_VER, M10BMC_LEGACY_BUILD_VER),
regmap_reg_range(M10BMC_SYS_BASE, M10BMC_SYS_END),
regmap_reg_range(M10BMC_FLASH_BASE, M10BMC_FLASH_END),
};
static const struct regmap_access_table m10bmc_access_table = {
.yes_ranges = m10bmc_regmap_range,
.n_yes_ranges = ARRAY_SIZE(m10bmc_regmap_range),
};
static struct regmap_config intel_m10bmc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.wr_table = &m10bmc_access_table,
.rd_table = &m10bmc_access_table,
.max_register = M10BMC_MEM_END,
};
@ -121,17 +134,14 @@ static int check_m10bmc_version(struct intel_m10bmc *ddata)
int ret;
/*
* This check is to filter out the very old legacy BMC versions,
* M10BMC_LEGACY_SYS_BASE is the offset to this old block of mmio
* registers. In the old BMC chips, the BMC version info is stored
* in this old version register (M10BMC_LEGACY_SYS_BASE +
* M10BMC_BUILD_VER), so its read out value would have not been
* LEGACY_INVALID (0xffffffff). But in new BMC chips that the
* driver supports, the value of this register should be
* LEGACY_INVALID.
* This check is to filter out the very old legacy BMC versions. In the
* old BMC chips, the BMC version info is stored in the old version
* register (M10BMC_LEGACY_BUILD_VER), so its read out value would have
* not been M10BMC_VER_LEGACY_INVALID (0xffffffff). But in new BMC
* chips that the driver supports, the value of this register should be
* M10BMC_VER_LEGACY_INVALID.
*/
ret = m10bmc_raw_read(ddata,
M10BMC_LEGACY_SYS_BASE + M10BMC_BUILD_VER, &v);
ret = m10bmc_raw_read(ddata, M10BMC_LEGACY_BUILD_VER, &v);
if (ret)
return -ENODEV;

View File

@ -16,8 +16,9 @@
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/dmi.h>
#include <linux/i2c.h>
#include <linux/platform_data/gpio-dwapb.h>
#include <linux/platform_data/i2c-designware.h>
#include <linux/property.h>
/* PCI BAR for register base address */
#define MFD_I2C_BAR 0
@ -45,29 +46,48 @@
#define INTEL_QUARK_I2C_CLK_HZ 33000000
struct intel_quark_mfd {
struct device *dev;
struct clk *i2c_clk;
struct clk_lookup *i2c_clk_lookup;
};
static const struct property_entry intel_quark_i2c_controller_standard_properties[] = {
PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_STANDARD_MODE_FREQ),
{ }
};
static const struct software_node intel_quark_i2c_controller_standard_node = {
.name = "intel-quark-i2c-controller",
.properties = intel_quark_i2c_controller_standard_properties,
};
static const struct property_entry intel_quark_i2c_controller_fast_properties[] = {
PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_FAST_MODE_FREQ),
{ }
};
static const struct software_node intel_quark_i2c_controller_fast_node = {
.name = "intel-quark-i2c-controller",
.properties = intel_quark_i2c_controller_fast_properties,
};
static const struct dmi_system_id dmi_platform_info[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "Galileo"),
},
.driver_data = (void *)100000,
.driver_data = (void *)&intel_quark_i2c_controller_standard_node,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"),
},
.driver_data = (void *)400000,
.driver_data = (void *)&intel_quark_i2c_controller_fast_node,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
},
.driver_data = (void *)400000,
.driver_data = (void *)&intel_quark_i2c_controller_fast_node,
},
{}
};
@ -98,15 +118,7 @@ static struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = {
};
static struct mfd_cell intel_quark_mfd_cells[] = {
{
.id = MFD_GPIO_BAR,
.name = "gpio-dwapb",
.acpi_match = &intel_quark_acpi_match_gpio,
.num_resources = ARRAY_SIZE(intel_quark_gpio_res),
.resources = intel_quark_gpio_res,
.ignore_resource_conflicts = true,
},
{
[MFD_I2C_BAR] = {
.id = MFD_I2C_BAR,
.name = "i2c_designware",
.acpi_match = &intel_quark_acpi_match_i2c,
@ -114,6 +126,14 @@ static struct mfd_cell intel_quark_mfd_cells[] = {
.resources = intel_quark_i2c_res,
.ignore_resource_conflicts = true,
},
[MFD_GPIO_BAR] = {
.id = MFD_GPIO_BAR,
.name = "gpio-dwapb",
.acpi_match = &intel_quark_acpi_match_gpio,
.num_resources = ARRAY_SIZE(intel_quark_gpio_res),
.resources = intel_quark_gpio_res,
.ignore_resource_conflicts = true,
},
};
static const struct pci_device_id intel_quark_mfd_ids[] = {
@ -157,48 +177,37 @@ static void intel_quark_unregister_i2c_clk(struct device *dev)
clk_unregister(quark_mfd->i2c_clk);
}
static int intel_quark_i2c_setup(struct pci_dev *pdev, struct mfd_cell *cell)
static int intel_quark_i2c_setup(struct pci_dev *pdev)
{
struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_I2C_BAR];
struct resource *res = intel_quark_i2c_res;
const struct dmi_system_id *dmi_id;
struct dw_i2c_platform_data *pdata;
struct resource *res = (struct resource *)cell->resources;
struct device *dev = &pdev->dev;
res[INTEL_QUARK_IORES_MEM].start =
pci_resource_start(pdev, MFD_I2C_BAR);
res[INTEL_QUARK_IORES_MEM].end =
pci_resource_end(pdev, MFD_I2C_BAR);
res[INTEL_QUARK_IORES_MEM].start = pci_resource_start(pdev, MFD_I2C_BAR);
res[INTEL_QUARK_IORES_MEM].end = pci_resource_end(pdev, MFD_I2C_BAR);
res[INTEL_QUARK_IORES_IRQ].start = pdev->irq;
res[INTEL_QUARK_IORES_IRQ].end = pdev->irq;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
res[INTEL_QUARK_IORES_IRQ].start = pci_irq_vector(pdev, 0);
res[INTEL_QUARK_IORES_IRQ].end = pci_irq_vector(pdev, 0);
/* Normal mode by default */
pdata->i2c_scl_freq = 100000;
cell->swnode = &intel_quark_i2c_controller_standard_node;
dmi_id = dmi_first_match(dmi_platform_info);
if (dmi_id)
pdata->i2c_scl_freq = (uintptr_t)dmi_id->driver_data;
cell->platform_data = pdata;
cell->pdata_size = sizeof(*pdata);
cell->swnode = (struct software_node *)dmi_id->driver_data;
return 0;
}
static int intel_quark_gpio_setup(struct pci_dev *pdev, struct mfd_cell *cell)
static int intel_quark_gpio_setup(struct pci_dev *pdev)
{
struct mfd_cell *cell = &intel_quark_mfd_cells[MFD_GPIO_BAR];
struct resource *res = intel_quark_gpio_res;
struct dwapb_platform_data *pdata;
struct resource *res = (struct resource *)cell->resources;
struct device *dev = &pdev->dev;
res[INTEL_QUARK_IORES_MEM].start =
pci_resource_start(pdev, MFD_GPIO_BAR);
res[INTEL_QUARK_IORES_MEM].end =
pci_resource_end(pdev, MFD_GPIO_BAR);
res[INTEL_QUARK_IORES_MEM].start = pci_resource_start(pdev, MFD_GPIO_BAR);
res[INTEL_QUARK_IORES_MEM].end = pci_resource_end(pdev, MFD_GPIO_BAR);
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@ -217,7 +226,7 @@ static int intel_quark_gpio_setup(struct pci_dev *pdev, struct mfd_cell *cell)
pdata->properties->idx = 0;
pdata->properties->ngpio = INTEL_QUARK_MFD_NGPIO;
pdata->properties->gpio_base = INTEL_QUARK_MFD_GPIO_BASE;
pdata->properties->irq[0] = pdev->irq;
pdata->properties->irq[0] = pci_irq_vector(pdev, 0);
pdata->properties->irq_shared = true;
cell->platform_data = pdata;
@ -240,29 +249,37 @@ static int intel_quark_mfd_probe(struct pci_dev *pdev,
if (!quark_mfd)
return -ENOMEM;
quark_mfd->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, quark_mfd);
ret = intel_quark_register_i2c_clk(&pdev->dev);
if (ret)
return ret;
ret = intel_quark_i2c_setup(pdev, &intel_quark_mfd_cells[1]);
if (ret)
pci_set_master(pdev);
/* This driver only requires 1 IRQ vector */
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
if (ret < 0)
goto err_unregister_i2c_clk;
ret = intel_quark_gpio_setup(pdev, &intel_quark_mfd_cells[0]);
ret = intel_quark_i2c_setup(pdev);
if (ret)
goto err_unregister_i2c_clk;
goto err_free_irq_vectors;
ret = intel_quark_gpio_setup(pdev);
if (ret)
goto err_free_irq_vectors;
ret = mfd_add_devices(&pdev->dev, 0, intel_quark_mfd_cells,
ARRAY_SIZE(intel_quark_mfd_cells), NULL, 0,
NULL);
if (ret)
goto err_unregister_i2c_clk;
goto err_free_irq_vectors;
return 0;
err_free_irq_vectors:
pci_free_irq_vectors(pdev);
err_unregister_i2c_clk:
intel_quark_unregister_i2c_clk(&pdev->dev);
return ret;
@ -270,8 +287,9 @@ err_unregister_i2c_clk:
static void intel_quark_mfd_remove(struct pci_dev *pdev)
{
intel_quark_unregister_i2c_clk(&pdev->dev);
mfd_remove_devices(&pdev->dev);
pci_free_irq_vectors(pdev);
intel_quark_unregister_i2c_clk(&pdev->dev);
}
static struct pci_driver intel_quark_mfd_driver = {

View File

@ -358,7 +358,7 @@ static struct attribute *lm3533_attributes[] = {
static umode_t lm3533_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct device *dev = kobj_to_dev(kobj);
struct lm3533 *lm3533 = dev_get_drvdata(dev);
struct device_attribute *dattr = to_dev_attr(attr);
struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(dattr);

View File

@ -26,9 +26,6 @@
#define GPIO_IO_SIZE 64
#define GPIO_IO_SIZE_CENTERTON 128
/* Intel Quark X1000 GPIO IRQ Number */
#define GPIO_IRQ_QUARK_X1000 9
#define WDTBASE 0x84
#define WDT_IO_SIZE 64
@ -43,30 +40,25 @@ struct lpc_sch_info {
unsigned int io_size_smbus;
unsigned int io_size_gpio;
unsigned int io_size_wdt;
int irq_gpio;
};
static struct lpc_sch_info sch_chipset_info[] = {
[LPC_SCH] = {
.io_size_smbus = SMBUS_IO_SIZE,
.io_size_gpio = GPIO_IO_SIZE,
.irq_gpio = -1,
},
[LPC_ITC] = {
.io_size_smbus = SMBUS_IO_SIZE,
.io_size_gpio = GPIO_IO_SIZE,
.io_size_wdt = WDT_IO_SIZE,
.irq_gpio = -1,
},
[LPC_CENTERTON] = {
.io_size_smbus = SMBUS_IO_SIZE,
.io_size_gpio = GPIO_IO_SIZE_CENTERTON,
.io_size_wdt = WDT_IO_SIZE,
.irq_gpio = -1,
},
[LPC_QUARK_X1000] = {
.io_size_gpio = GPIO_IO_SIZE,
.irq_gpio = GPIO_IRQ_QUARK_X1000,
.io_size_wdt = WDT_IO_SIZE,
},
};
@ -113,13 +105,13 @@ static int lpc_sch_get_io(struct pci_dev *pdev, int where, const char *name,
}
static int lpc_sch_populate_cell(struct pci_dev *pdev, int where,
const char *name, int size, int irq,
int id, struct mfd_cell *cell)
const char *name, int size, int id,
struct mfd_cell *cell)
{
struct resource *res;
int ret;
res = devm_kcalloc(&pdev->dev, 2, sizeof(*res), GFP_KERNEL);
res = devm_kzalloc(&pdev->dev, sizeof(*res), GFP_KERNEL);
if (!res)
return -ENOMEM;
@ -135,18 +127,6 @@ static int lpc_sch_populate_cell(struct pci_dev *pdev, int where,
cell->ignore_resource_conflicts = true;
cell->id = id;
/* Check if we need to add an IRQ resource */
if (irq < 0)
return 0;
res++;
res->start = irq;
res->end = irq;
res->flags = IORESOURCE_IRQ;
cell->num_resources++;
return 0;
}
@ -158,7 +138,7 @@ static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id)
int ret;
ret = lpc_sch_populate_cell(dev, SMBASE, "isch_smbus",
info->io_size_smbus, -1,
info->io_size_smbus,
id->device, &lpc_sch_cells[cells]);
if (ret < 0)
return ret;
@ -166,7 +146,7 @@ static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id)
cells++;
ret = lpc_sch_populate_cell(dev, GPIOBASE, "sch_gpio",
info->io_size_gpio, info->irq_gpio,
info->io_size_gpio,
id->device, &lpc_sch_cells[cells]);
if (ret < 0)
return ret;
@ -174,7 +154,7 @@ static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id)
cells++;
ret = lpc_sch_populate_cell(dev, WDTBASE, "ie6xx_wdt",
info->io_size_wdt, -1,
info->io_size_wdt,
id->device, &lpc_sch_cells[cells]);
if (ret < 0)
return ret;

View File

@ -29,9 +29,9 @@
static const struct mfd_cell max8997_devs[] = {
{ .name = "max8997-pmic", },
{ .name = "max8997-rtc", },
{ .name = "max8997-battery", .of_compatible = "maxim,max8997-battery", },
{ .name = "max8997-battery", },
{ .name = "max8997-haptic", },
{ .name = "max8997-muic", .of_compatible = "maxim,max8997-muic", },
{ .name = "max8997-muic", },
{ .name = "max8997-led", .id = 1 },
{ .name = "max8997-led", .id = 2 },
};

View File

@ -65,7 +65,7 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
{
const struct mfd_cell_acpi_match *match = cell->acpi_match;
struct acpi_device *parent, *child;
struct acpi_device *adev;
struct acpi_device *adev = NULL;
parent = ACPI_COMPANION(pdev->dev.parent);
if (!parent)
@ -77,10 +77,9 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
* _ADR or it will use the parent handle if is no ID is given.
*
* Note that use of _ADR is a grey area in the ACPI specification,
* though Intel Galileo Gen2 is using it to distinguish the children
* devices.
* though at least Intel Galileo Gen 2 is using it to distinguish
* the children devices.
*/
adev = parent;
if (match) {
if (match->pnpid) {
struct acpi_device_id ids[2] = {};
@ -93,22 +92,11 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
}
}
} else {
unsigned long long adr;
acpi_status status;
list_for_each_entry(child, &parent->children, node) {
status = acpi_evaluate_integer(child->handle,
"_ADR", NULL,
&adr);
if (ACPI_SUCCESS(status) && match->adr == adr) {
adev = child;
break;
}
}
adev = acpi_find_child_device(parent, match->adr, false);
}
}
ACPI_COMPANION_SET(&pdev->dev, adev);
ACPI_COMPANION_SET(&pdev->dev, adev ?: parent);
}
#else
static inline void mfd_acpi_add_device(const struct mfd_cell *cell,
@ -238,8 +226,8 @@ static int mfd_add_device(struct device *parent, int id,
goto fail_of_entry;
}
if (cell->properties) {
ret = platform_device_add_properties(pdev, cell->properties);
if (cell->swnode) {
ret = device_add_software_node(&pdev->dev, cell->swnode);
if (ret)
goto fail_of_entry;
}
@ -304,6 +292,7 @@ fail_of_entry:
list_del(&of_entry->list);
kfree(of_entry);
}
device_remove_software_node(&pdev->dev);
fail_alias:
regulator_bulk_unregister_supply_alias(&pdev->dev,
cell->parent_supplies,
@ -372,6 +361,8 @@ static int mfd_remove_devices_fn(struct device *dev, void *data)
regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
cell->num_parent_supplies);
device_remove_software_node(&pdev->dev);
platform_device_unregister(pdev);
return 0;
}

271
drivers/mfd/ntxec.c Normal file
View File

@ -0,0 +1,271 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The Netronix embedded controller is a microcontroller found in some
* e-book readers designed by the original design manufacturer Netronix, Inc.
* It contains RTC, battery monitoring, system power management, and PWM
* functionality.
*
* This driver implements register access, version detection, and system
* power-off/reset.
*
* Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
*/
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/ntxec.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <asm/unaligned.h>
#define NTXEC_REG_VERSION 0x00
#define NTXEC_REG_POWEROFF 0x50
#define NTXEC_REG_POWERKEEP 0x70
#define NTXEC_REG_RESET 0x90
#define NTXEC_POWEROFF_VALUE 0x0100
#define NTXEC_POWERKEEP_VALUE 0x0800
#define NTXEC_RESET_VALUE 0xff00
static struct i2c_client *poweroff_restart_client;
static void ntxec_poweroff(void)
{
int res;
u8 buf[3] = { NTXEC_REG_POWEROFF };
struct i2c_msg msgs[] = {
{
.addr = poweroff_restart_client->addr,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
},
};
put_unaligned_be16(NTXEC_POWEROFF_VALUE, buf + 1);
res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs));
if (res < 0)
dev_warn(&poweroff_restart_client->dev,
"Failed to power off (err = %d)\n", res);
/*
* The time from the register write until the host CPU is powered off
* has been observed to be about 2.5 to 3 seconds. Sleep long enough to
* safely avoid returning from the poweroff handler.
*/
msleep(5000);
}
static int ntxec_restart(struct notifier_block *nb,
unsigned long action, void *data)
{
int res;
u8 buf[3] = { NTXEC_REG_RESET };
/*
* NOTE: The lower half of the reset value is not sent, because sending
* it causes an I2C error. (The reset handler in the downstream driver
* does send the full two-byte value, but doesn't check the result).
*/
struct i2c_msg msgs[] = {
{
.addr = poweroff_restart_client->addr,
.flags = 0,
.len = sizeof(buf) - 1,
.buf = buf,
},
};
put_unaligned_be16(NTXEC_RESET_VALUE, buf + 1);
res = i2c_transfer(poweroff_restart_client->adapter, msgs, ARRAY_SIZE(msgs));
if (res < 0)
dev_warn(&poweroff_restart_client->dev,
"Failed to restart (err = %d)\n", res);
return NOTIFY_DONE;
}
static struct notifier_block ntxec_restart_handler = {
.notifier_call = ntxec_restart,
.priority = 128,
};
static int regmap_ignore_write(void *context,
unsigned int reg, unsigned int val)
{
struct regmap *regmap = context;
regmap_write(regmap, reg, val);
return 0;
}
static int regmap_wrap_read(void *context, unsigned int reg,
unsigned int *val)
{
struct regmap *regmap = context;
return regmap_read(regmap, reg, val);
}
/*
* Some firmware versions do not ack written data, add a wrapper. It
* is used to stack another regmap on top.
*/
static const struct regmap_config regmap_config_noack = {
.name = "ntxec_noack",
.reg_bits = 8,
.val_bits = 16,
.cache_type = REGCACHE_NONE,
.reg_write = regmap_ignore_write,
.reg_read = regmap_wrap_read
};
static const struct regmap_config regmap_config = {
.name = "ntxec",
.reg_bits = 8,
.val_bits = 16,
.cache_type = REGCACHE_NONE,
.val_format_endian = REGMAP_ENDIAN_BIG,
};
static const struct mfd_cell ntxec_subdev[] = {
{ .name = "ntxec-rtc" },
{ .name = "ntxec-pwm" },
};
static const struct mfd_cell ntxec_subdev_pwm[] = {
{ .name = "ntxec-pwm" },
};
static int ntxec_probe(struct i2c_client *client)
{
struct ntxec *ec;
unsigned int version;
int res;
const struct mfd_cell *subdevs;
size_t n_subdevs;
ec = devm_kmalloc(&client->dev, sizeof(*ec), GFP_KERNEL);
if (!ec)
return -ENOMEM;
ec->dev = &client->dev;
ec->regmap = devm_regmap_init_i2c(client, &regmap_config);
if (IS_ERR(ec->regmap)) {
dev_err(ec->dev, "Failed to set up regmap for device\n");
return PTR_ERR(ec->regmap);
}
/* Determine the firmware version */
res = regmap_read(ec->regmap, NTXEC_REG_VERSION, &version);
if (res < 0) {
dev_err(ec->dev, "Failed to read firmware version number\n");
return res;
}
/* Bail out if we encounter an unknown firmware version */
switch (version) {
case NTXEC_VERSION_KOBO_AURA:
subdevs = ntxec_subdev;
n_subdevs = ARRAY_SIZE(ntxec_subdev);
break;
case NTXEC_VERSION_TOLINO_SHINE2:
subdevs = ntxec_subdev_pwm;
n_subdevs = ARRAY_SIZE(ntxec_subdev_pwm);
/* Another regmap stacked on top of the other */
ec->regmap = devm_regmap_init(ec->dev, NULL,
ec->regmap,
&regmap_config_noack);
if (IS_ERR(ec->regmap))
return PTR_ERR(ec->regmap);
break;
default:
dev_err(ec->dev,
"Netronix embedded controller version %04x is not supported.\n",
version);
return -ENODEV;
}
dev_info(ec->dev,
"Netronix embedded controller version %04x detected.\n", version);
if (of_device_is_system_power_controller(ec->dev->of_node)) {
/*
* Set the 'powerkeep' bit. This is necessary on some boards
* in order to keep the system running.
*/
res = regmap_write(ec->regmap, NTXEC_REG_POWERKEEP,
NTXEC_POWERKEEP_VALUE);
if (res < 0)
return res;
if (poweroff_restart_client)
/*
* Another instance of the driver already took
* poweroff/restart duties.
*/
dev_err(ec->dev, "poweroff_restart_client already assigned\n");
else
poweroff_restart_client = client;
if (pm_power_off)
/* Another driver already registered a poweroff handler. */
dev_err(ec->dev, "pm_power_off already assigned\n");
else
pm_power_off = ntxec_poweroff;
res = register_restart_handler(&ntxec_restart_handler);
if (res)
dev_err(ec->dev,
"Failed to register restart handler: %d\n", res);
}
i2c_set_clientdata(client, ec);
res = devm_mfd_add_devices(ec->dev, PLATFORM_DEVID_NONE,
subdevs, n_subdevs, NULL, 0, NULL);
if (res)
dev_err(ec->dev, "Failed to add subdevices: %d\n", res);
return res;
}
static int ntxec_remove(struct i2c_client *client)
{
if (client == poweroff_restart_client) {
poweroff_restart_client = NULL;
pm_power_off = NULL;
unregister_restart_handler(&ntxec_restart_handler);
}
return 0;
}
static const struct of_device_id of_ntxec_match_table[] = {
{ .compatible = "netronix,ntxec", },
{}
};
MODULE_DEVICE_TABLE(of, of_ntxec_match_table);
static struct i2c_driver ntxec_driver = {
.driver = {
.name = "ntxec",
.of_match_table = of_ntxec_match_table,
},
.probe_new = ntxec_probe,
.remove = ntxec_remove,
};
module_i2c_driver(ntxec_driver);
MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
MODULE_DESCRIPTION("Core driver for Netronix EC");
MODULE_LICENSE("GPL");

View File

@ -45,8 +45,11 @@ static bool rn5t618_volatile_reg(struct device *dev, unsigned int reg)
case RN5T618_INTMON:
case RN5T618_RTC_CTRL1 ... RN5T618_RTC_CTRL2:
case RN5T618_RTC_SECONDS ... RN5T618_RTC_YEAR:
case RN5T618_CHGCTL1:
case RN5T618_REGISET1 ... RN5T618_REGISET2:
case RN5T618_CHGSTATE:
case RN5T618_CHGCTRL_IRR ... RN5T618_CHGERR_MONI:
case RN5T618_GCHGDET:
case RN5T618_CONTROL ... RN5T618_CC_AVEREG0:
return true;
default:

View File

@ -2,7 +2,7 @@
//
// Copyright (C) 2019 ROHM Semiconductors
//
// ROHM BD71828 PMIC driver
// ROHM BD71828/BD71815 PMIC driver
#include <linux/gpio_keys.h>
#include <linux/i2c.h>
@ -11,7 +11,9 @@
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
#include <linux/mfd/rohm-generic.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
@ -29,12 +31,84 @@ static struct gpio_keys_platform_data bd71828_powerkey_data = {
.name = "bd71828-pwrkey",
};
static const struct resource rtc_irqs[] = {
static const struct resource bd71815_rtc_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC0, "bd71815-rtc-alm-0"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC1, "bd71815-rtc-alm-1"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_RTC2, "bd71815-rtc-alm-2"),
};
static const struct resource bd71828_rtc_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC0, "bd71828-rtc-alm-0"),
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC1, "bd71828-rtc-alm-1"),
DEFINE_RES_IRQ_NAMED(BD71828_INT_RTC2, "bd71828-rtc-alm-2"),
};
static struct resource bd71815_power_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_RMV, "bd71815-dcin-rmv"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_OUT, "bd71815-clps-out"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_IN, "bd71815-clps-in"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_OVP_RES, "bd71815-dcin-ovp-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_OVP_DET, "bd71815-dcin-ovp-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_MON_RES, "bd71815-dcin-mon-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_MON_DET, "bd71815-dcin-mon-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_UV_RES, "bd71815-vsys-uv-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_UV_DET, "bd71815-vsys-uv-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_LOW_RES, "bd71815-vsys-low-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_LOW_DET, "bd71815-vsys-low-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_MON_RES, "bd71815-vsys-mon-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_VSYS_MON_RES, "bd71815-vsys-mon-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_WDG_TEMP, "bd71815-chg-wdg-temp"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_WDG_TIME, "bd71815-chg-wdg"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_RECHARGE_RES, "bd71815-rechg-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_RECHARGE_DET, "bd71815-rechg-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_RANGED_TEMP_TRANSITION, "bd71815-ranged-temp-transit"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CHG_STATE_TRANSITION, "bd71815-chg-state-change"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_TEMP_NORMAL, "bd71815-bat-temp-normal"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_TEMP_ERANGE, "bd71815-bat-temp-erange"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_REMOVED, "bd71815-bat-rmv"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_DETECTED, "bd71815-bat-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_THERM_REMOVED, "bd71815-therm-rmv"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_THERM_DETECTED, "bd71815-therm-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_DEAD, "bd71815-bat-dead"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_SHORTC_RES, "bd71815-bat-short-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_SHORTC_DET, "bd71815-bat-short-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_LOW_VOLT_RES, "bd71815-bat-low-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_LOW_VOLT_DET, "bd71815-bat-low-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_VOLT_RES, "bd71815-bat-over-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_VOLT_DET, "bd71815-bat-over-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_MON_RES, "bd71815-bat-mon-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_MON_DET, "bd71815-bat-mon-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_CC_MON1, "bd71815-bat-cc-mon1"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_CC_MON2, "bd71815-bat-cc-mon2"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_CC_MON3, "bd71815-bat-cc-mon3"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_1_RES, "bd71815-bat-oc1-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_1_DET, "bd71815-bat-oc1-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_2_RES, "bd71815-bat-oc2-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_2_DET, "bd71815-bat-oc2-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_3_RES, "bd71815-bat-oc3-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_BAT_OVER_CURR_3_DET, "bd71815-bat-oc3-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_LOW_RES, "bd71815-bat-low-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_LOW_DET, "bd71815-bat-low-det"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_HI_RES, "bd71815-bat-hi-res"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_TEMP_BAT_HI_DET, "bd71815-bat-hi-det"),
};
static struct mfd_cell bd71815_mfd_cells[] = {
{ .name = "bd71815-pmic", },
{ .name = "bd71815-clk", },
{ .name = "bd71815-gpo", },
{
.name = "bd71815-power",
.num_resources = ARRAY_SIZE(bd71815_power_irqs),
.resources = &bd71815_power_irqs[0],
},
{
.name = "bd71815-rtc",
.num_resources = ARRAY_SIZE(bd71815_rtc_irqs),
.resources = &bd71815_rtc_irqs[0],
},
};
static struct mfd_cell bd71828_mfd_cells[] = {
{ .name = "bd71828-pmic", },
{ .name = "bd71828-gpio", },
@ -47,8 +121,8 @@ static struct mfd_cell bd71828_mfd_cells[] = {
{ .name = "bd71827-power", },
{
.name = "bd71828-rtc",
.resources = rtc_irqs,
.num_resources = ARRAY_SIZE(rtc_irqs),
.resources = bd71828_rtc_irqs,
.num_resources = ARRAY_SIZE(bd71828_rtc_irqs),
}, {
.name = "gpio-keys",
.platform_data = &bd71828_powerkey_data,
@ -56,7 +130,35 @@ static struct mfd_cell bd71828_mfd_cells[] = {
},
};
static const struct regmap_range volatile_ranges[] = {
static const struct regmap_range bd71815_volatile_ranges[] = {
{
.range_min = BD71815_REG_SEC,
.range_max = BD71815_REG_YEAR,
}, {
.range_min = BD71815_REG_CONF,
.range_max = BD71815_REG_BAT_TEMP,
}, {
.range_min = BD71815_REG_VM_IBAT_U,
.range_max = BD71815_REG_CC_CTRL,
}, {
.range_min = BD71815_REG_CC_STAT,
.range_max = BD71815_REG_CC_CURCD_L,
}, {
.range_min = BD71815_REG_VM_BTMP_MON,
.range_max = BD71815_REG_VM_BTMP_MON,
}, {
.range_min = BD71815_REG_INT_STAT,
.range_max = BD71815_REG_INT_UPDATE,
}, {
.range_min = BD71815_REG_VM_VSYS_U,
.range_max = BD71815_REG_REX_CTRL_1,
}, {
.range_min = BD71815_REG_FULL_CCNTD_3,
.range_max = BD71815_REG_CCNTD_CHG_2,
},
};
static const struct regmap_range bd71828_volatile_ranges[] = {
{
.range_min = BD71828_REG_PS_CTRL_1,
.range_max = BD71828_REG_PS_CTRL_1,
@ -80,15 +182,28 @@ static const struct regmap_range volatile_ranges[] = {
},
};
static const struct regmap_access_table volatile_regs = {
.yes_ranges = &volatile_ranges[0],
.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
static const struct regmap_access_table bd71815_volatile_regs = {
.yes_ranges = &bd71815_volatile_ranges[0],
.n_yes_ranges = ARRAY_SIZE(bd71815_volatile_ranges),
};
static struct regmap_config bd71828_regmap = {
static const struct regmap_access_table bd71828_volatile_regs = {
.yes_ranges = &bd71828_volatile_ranges[0],
.n_yes_ranges = ARRAY_SIZE(bd71828_volatile_ranges),
};
static const struct regmap_config bd71815_regmap = {
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &volatile_regs,
.volatile_table = &bd71815_volatile_regs,
.max_register = BD71815_MAX_REGISTER - 1,
.cache_type = REGCACHE_RBTREE,
};
static const struct regmap_config bd71828_regmap = {
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &bd71828_volatile_regs,
.max_register = BD71828_MAX_REGISTER,
.cache_type = REGCACHE_RBTREE,
};
@ -96,7 +211,7 @@ static struct regmap_config bd71828_regmap = {
/*
* Mapping of main IRQ register bits to sub-IRQ register offsets so that we can
* access corect sub-IRQ registers based on bits that are set in main IRQ
* register.
* register. BD71815 and BD71828 have same sub-register-block offests.
*/
static unsigned int bit0_offsets[] = {11}; /* RTC IRQ */
@ -108,7 +223,7 @@ static unsigned int bit5_offsets[] = {3}; /* VSYS IRQ */
static unsigned int bit6_offsets[] = {1, 2}; /* DCIN IRQ */
static unsigned int bit7_offsets[] = {0}; /* BUCK IRQ */
static struct regmap_irq_sub_irq_map bd71828_sub_irq_offsets[] = {
static struct regmap_irq_sub_irq_map bd718xx_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit1_offsets),
REGMAP_IRQ_MAIN_REG_OFFSET(bit2_offsets),
@ -119,6 +234,88 @@ static struct regmap_irq_sub_irq_map bd71828_sub_irq_offsets[] = {
REGMAP_IRQ_MAIN_REG_OFFSET(bit7_offsets),
};
static const struct regmap_irq bd71815_irqs[] = {
REGMAP_IRQ_REG(BD71815_INT_BUCK1_OCP, 0, BD71815_INT_BUCK1_OCP_MASK),
REGMAP_IRQ_REG(BD71815_INT_BUCK2_OCP, 0, BD71815_INT_BUCK2_OCP_MASK),
REGMAP_IRQ_REG(BD71815_INT_BUCK3_OCP, 0, BD71815_INT_BUCK3_OCP_MASK),
REGMAP_IRQ_REG(BD71815_INT_BUCK4_OCP, 0, BD71815_INT_BUCK4_OCP_MASK),
REGMAP_IRQ_REG(BD71815_INT_BUCK5_OCP, 0, BD71815_INT_BUCK5_OCP_MASK),
REGMAP_IRQ_REG(BD71815_INT_LED_OVP, 0, BD71815_INT_LED_OVP_MASK),
REGMAP_IRQ_REG(BD71815_INT_LED_OCP, 0, BD71815_INT_LED_OCP_MASK),
REGMAP_IRQ_REG(BD71815_INT_LED_SCP, 0, BD71815_INT_LED_SCP_MASK),
/* DCIN1 interrupts */
REGMAP_IRQ_REG(BD71815_INT_DCIN_RMV, 1, BD71815_INT_DCIN_RMV_MASK),
REGMAP_IRQ_REG(BD71815_INT_CLPS_OUT, 1, BD71815_INT_CLPS_OUT_MASK),
REGMAP_IRQ_REG(BD71815_INT_CLPS_IN, 1, BD71815_INT_CLPS_IN_MASK),
REGMAP_IRQ_REG(BD71815_INT_DCIN_OVP_RES, 1, BD71815_INT_DCIN_OVP_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_DCIN_OVP_DET, 1, BD71815_INT_DCIN_OVP_DET_MASK),
/* DCIN2 interrupts */
REGMAP_IRQ_REG(BD71815_INT_DCIN_MON_RES, 2, BD71815_INT_DCIN_MON_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_DCIN_MON_DET, 2, BD71815_INT_DCIN_MON_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_WDOG, 2, BD71815_INT_WDOG_MASK),
/* Vsys */
REGMAP_IRQ_REG(BD71815_INT_VSYS_UV_RES, 3, BD71815_INT_VSYS_UV_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_VSYS_UV_DET, 3, BD71815_INT_VSYS_UV_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_VSYS_LOW_RES, 3, BD71815_INT_VSYS_LOW_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_VSYS_LOW_DET, 3, BD71815_INT_VSYS_LOW_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_VSYS_MON_RES, 3, BD71815_INT_VSYS_MON_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_VSYS_MON_DET, 3, BD71815_INT_VSYS_MON_DET_MASK),
/* Charger */
REGMAP_IRQ_REG(BD71815_INT_CHG_WDG_TEMP, 4, BD71815_INT_CHG_WDG_TEMP_MASK),
REGMAP_IRQ_REG(BD71815_INT_CHG_WDG_TIME, 4, BD71815_INT_CHG_WDG_TIME_MASK),
REGMAP_IRQ_REG(BD71815_INT_CHG_RECHARGE_RES, 4, BD71815_INT_CHG_RECHARGE_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_CHG_RECHARGE_DET, 4, BD71815_INT_CHG_RECHARGE_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_CHG_RANGED_TEMP_TRANSITION, 4,
BD71815_INT_CHG_RANGED_TEMP_TRANSITION_MASK),
REGMAP_IRQ_REG(BD71815_INT_CHG_STATE_TRANSITION, 4, BD71815_INT_CHG_STATE_TRANSITION_MASK),
/* Battery */
REGMAP_IRQ_REG(BD71815_INT_BAT_TEMP_NORMAL, 5, BD71815_INT_BAT_TEMP_NORMAL_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_TEMP_ERANGE, 5, BD71815_INT_BAT_TEMP_ERANGE_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_REMOVED, 5, BD71815_INT_BAT_REMOVED_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_DETECTED, 5, BD71815_INT_BAT_DETECTED_MASK),
REGMAP_IRQ_REG(BD71815_INT_THERM_REMOVED, 5, BD71815_INT_THERM_REMOVED_MASK),
REGMAP_IRQ_REG(BD71815_INT_THERM_DETECTED, 5, BD71815_INT_THERM_DETECTED_MASK),
/* Battery Mon 1 */
REGMAP_IRQ_REG(BD71815_INT_BAT_DEAD, 6, BD71815_INT_BAT_DEAD_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_SHORTC_RES, 6, BD71815_INT_BAT_SHORTC_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_SHORTC_DET, 6, BD71815_INT_BAT_SHORTC_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_LOW_VOLT_RES, 6, BD71815_INT_BAT_LOW_VOLT_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_LOW_VOLT_DET, 6, BD71815_INT_BAT_LOW_VOLT_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_VOLT_RES, 6, BD71815_INT_BAT_OVER_VOLT_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_VOLT_DET, 6, BD71815_INT_BAT_OVER_VOLT_DET_MASK),
/* Battery Mon 2 */
REGMAP_IRQ_REG(BD71815_INT_BAT_MON_RES, 7, BD71815_INT_BAT_MON_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_MON_DET, 7, BD71815_INT_BAT_MON_DET_MASK),
/* Battery Mon 3 (Coulomb counter) */
REGMAP_IRQ_REG(BD71815_INT_BAT_CC_MON1, 8, BD71815_INT_BAT_CC_MON1_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_CC_MON2, 8, BD71815_INT_BAT_CC_MON2_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_CC_MON3, 8, BD71815_INT_BAT_CC_MON3_MASK),
/* Battery Mon 4 */
REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_1_RES, 9, BD71815_INT_BAT_OVER_CURR_1_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_1_DET, 9, BD71815_INT_BAT_OVER_CURR_1_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_2_RES, 9, BD71815_INT_BAT_OVER_CURR_2_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_2_DET, 9, BD71815_INT_BAT_OVER_CURR_2_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_3_RES, 9, BD71815_INT_BAT_OVER_CURR_3_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_BAT_OVER_CURR_3_DET, 9, BD71815_INT_BAT_OVER_CURR_3_DET_MASK),
/* Temperature */
REGMAP_IRQ_REG(BD71815_INT_TEMP_BAT_LOW_RES, 10, BD71815_INT_TEMP_BAT_LOW_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_TEMP_BAT_LOW_DET, 10, BD71815_INT_TEMP_BAT_LOW_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_TEMP_BAT_HI_RES, 10, BD71815_INT_TEMP_BAT_HI_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_TEMP_BAT_HI_DET, 10, BD71815_INT_TEMP_BAT_HI_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_TEMP_CHIP_OVER_125_RES, 10,
BD71815_INT_TEMP_CHIP_OVER_125_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_TEMP_CHIP_OVER_125_DET, 10,
BD71815_INT_TEMP_CHIP_OVER_125_DET_MASK),
REGMAP_IRQ_REG(BD71815_INT_TEMP_CHIP_OVER_VF_RES, 10,
BD71815_INT_TEMP_CHIP_OVER_VF_RES_MASK),
REGMAP_IRQ_REG(BD71815_INT_TEMP_CHIP_OVER_VF_DET, 10,
BD71815_INT_TEMP_CHIP_OVER_VF_DET_MASK),
/* RTC Alarm */
REGMAP_IRQ_REG(BD71815_INT_RTC0, 11, BD71815_INT_RTC0_MASK),
REGMAP_IRQ_REG(BD71815_INT_RTC1, 11, BD71815_INT_RTC1_MASK),
REGMAP_IRQ_REG(BD71815_INT_RTC2, 11, BD71815_INT_RTC2_MASK),
};
static struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_BUCK1_OCP, 0, BD71828_INT_BUCK1_OCP_MASK),
REGMAP_IRQ_REG(BD71828_INT_BUCK2_OCP, 0, BD71828_INT_BUCK2_OCP_MASK),
@ -134,10 +331,8 @@ static struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_CLPS_OUT, 1, BD71828_INT_CLPS_OUT_MASK),
REGMAP_IRQ_REG(BD71828_INT_CLPS_IN, 1, BD71828_INT_CLPS_IN_MASK),
/* DCIN2 interrupts */
REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_RES, 2,
BD71828_INT_DCIN_MON_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_DET, 2,
BD71828_INT_DCIN_MON_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_RES, 2, BD71828_INT_DCIN_MON_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_DCIN_MON_DET, 2, BD71828_INT_DCIN_MON_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_LONGPUSH, 2, BD71828_INT_LONGPUSH_MASK),
REGMAP_IRQ_REG(BD71828_INT_MIDPUSH, 2, BD71828_INT_MIDPUSH_MASK),
REGMAP_IRQ_REG(BD71828_INT_SHORTPUSH, 2, BD71828_INT_SHORTPUSH_MASK),
@ -145,102 +340,59 @@ static struct regmap_irq bd71828_irqs[] = {
REGMAP_IRQ_REG(BD71828_INT_WDOG, 2, BD71828_INT_WDOG_MASK),
REGMAP_IRQ_REG(BD71828_INT_SWRESET, 2, BD71828_INT_SWRESET_MASK),
/* Vsys */
REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_RES, 3,
BD71828_INT_VSYS_UV_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_DET, 3,
BD71828_INT_VSYS_UV_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_RES, 3,
BD71828_INT_VSYS_LOW_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_DET, 3,
BD71828_INT_VSYS_LOW_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_IN, 3,
BD71828_INT_VSYS_HALL_IN_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_TOGGLE, 3,
BD71828_INT_VSYS_HALL_TOGGLE_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_RES, 3,
BD71828_INT_VSYS_MON_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_DET, 3,
BD71828_INT_VSYS_MON_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_RES, 3, BD71828_INT_VSYS_UV_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_UV_DET, 3, BD71828_INT_VSYS_UV_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_RES, 3, BD71828_INT_VSYS_LOW_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_LOW_DET, 3, BD71828_INT_VSYS_LOW_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_IN, 3, BD71828_INT_VSYS_HALL_IN_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_HALL_TOGGLE, 3, BD71828_INT_VSYS_HALL_TOGGLE_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_RES, 3, BD71828_INT_VSYS_MON_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_VSYS_MON_DET, 3, BD71828_INT_VSYS_MON_DET_MASK),
/* Charger */
REGMAP_IRQ_REG(BD71828_INT_CHG_DCIN_ILIM, 4,
BD71828_INT_CHG_DCIN_ILIM_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_TOPOFF_TO_DONE, 4,
BD71828_INT_CHG_TOPOFF_TO_DONE_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TEMP, 4,
BD71828_INT_CHG_WDG_TEMP_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TIME, 4,
BD71828_INT_CHG_WDG_TIME_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_RES, 4,
BD71828_INT_CHG_RECHARGE_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_DET, 4,
BD71828_INT_CHG_RECHARGE_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_DCIN_ILIM, 4, BD71828_INT_CHG_DCIN_ILIM_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_TOPOFF_TO_DONE, 4, BD71828_INT_CHG_TOPOFF_TO_DONE_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TEMP, 4, BD71828_INT_CHG_WDG_TEMP_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_WDG_TIME, 4, BD71828_INT_CHG_WDG_TIME_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_RES, 4, BD71828_INT_CHG_RECHARGE_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_RECHARGE_DET, 4, BD71828_INT_CHG_RECHARGE_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_RANGED_TEMP_TRANSITION, 4,
BD71828_INT_CHG_RANGED_TEMP_TRANSITION_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_STATE_TRANSITION, 4,
BD71828_INT_CHG_STATE_TRANSITION_MASK),
REGMAP_IRQ_REG(BD71828_INT_CHG_STATE_TRANSITION, 4, BD71828_INT_CHG_STATE_TRANSITION_MASK),
/* Battery */
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_NORMAL, 5,
BD71828_INT_BAT_TEMP_NORMAL_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_ERANGE, 5,
BD71828_INT_BAT_TEMP_ERANGE_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_WARN, 5,
BD71828_INT_BAT_TEMP_WARN_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_REMOVED, 5,
BD71828_INT_BAT_REMOVED_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_DETECTED, 5,
BD71828_INT_BAT_DETECTED_MASK),
REGMAP_IRQ_REG(BD71828_INT_THERM_REMOVED, 5,
BD71828_INT_THERM_REMOVED_MASK),
REGMAP_IRQ_REG(BD71828_INT_THERM_DETECTED, 5,
BD71828_INT_THERM_DETECTED_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_NORMAL, 5, BD71828_INT_BAT_TEMP_NORMAL_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_ERANGE, 5, BD71828_INT_BAT_TEMP_ERANGE_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_TEMP_WARN, 5, BD71828_INT_BAT_TEMP_WARN_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_REMOVED, 5, BD71828_INT_BAT_REMOVED_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_DETECTED, 5, BD71828_INT_BAT_DETECTED_MASK),
REGMAP_IRQ_REG(BD71828_INT_THERM_REMOVED, 5, BD71828_INT_THERM_REMOVED_MASK),
REGMAP_IRQ_REG(BD71828_INT_THERM_DETECTED, 5, BD71828_INT_THERM_DETECTED_MASK),
/* Battery Mon 1 */
REGMAP_IRQ_REG(BD71828_INT_BAT_DEAD, 6, BD71828_INT_BAT_DEAD_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_RES, 6,
BD71828_INT_BAT_SHORTC_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_DET, 6,
BD71828_INT_BAT_SHORTC_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_RES, 6,
BD71828_INT_BAT_LOW_VOLT_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_DET, 6,
BD71828_INT_BAT_LOW_VOLT_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_RES, 6,
BD71828_INT_BAT_OVER_VOLT_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_DET, 6,
BD71828_INT_BAT_OVER_VOLT_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_RES, 6, BD71828_INT_BAT_SHORTC_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_SHORTC_DET, 6, BD71828_INT_BAT_SHORTC_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_RES, 6, BD71828_INT_BAT_LOW_VOLT_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_LOW_VOLT_DET, 6, BD71828_INT_BAT_LOW_VOLT_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_RES, 6, BD71828_INT_BAT_OVER_VOLT_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_VOLT_DET, 6, BD71828_INT_BAT_OVER_VOLT_DET_MASK),
/* Battery Mon 2 */
REGMAP_IRQ_REG(BD71828_INT_BAT_MON_RES, 7,
BD71828_INT_BAT_MON_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_MON_DET, 7,
BD71828_INT_BAT_MON_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_MON_RES, 7, BD71828_INT_BAT_MON_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_MON_DET, 7, BD71828_INT_BAT_MON_DET_MASK),
/* Battery Mon 3 (Coulomb counter) */
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON1, 8,
BD71828_INT_BAT_CC_MON1_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON2, 8,
BD71828_INT_BAT_CC_MON2_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON3, 8,
BD71828_INT_BAT_CC_MON3_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON1, 8, BD71828_INT_BAT_CC_MON1_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON2, 8, BD71828_INT_BAT_CC_MON2_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_CC_MON3, 8, BD71828_INT_BAT_CC_MON3_MASK),
/* Battery Mon 4 */
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_RES, 9,
BD71828_INT_BAT_OVER_CURR_1_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_DET, 9,
BD71828_INT_BAT_OVER_CURR_1_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_RES, 9,
BD71828_INT_BAT_OVER_CURR_2_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_DET, 9,
BD71828_INT_BAT_OVER_CURR_2_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_RES, 9,
BD71828_INT_BAT_OVER_CURR_3_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_DET, 9,
BD71828_INT_BAT_OVER_CURR_3_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_RES, 9, BD71828_INT_BAT_OVER_CURR_1_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_1_DET, 9, BD71828_INT_BAT_OVER_CURR_1_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_RES, 9, BD71828_INT_BAT_OVER_CURR_2_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_2_DET, 9, BD71828_INT_BAT_OVER_CURR_2_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_RES, 9, BD71828_INT_BAT_OVER_CURR_3_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_BAT_OVER_CURR_3_DET, 9, BD71828_INT_BAT_OVER_CURR_3_DET_MASK),
/* Temperature */
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_RES, 10,
BD71828_INT_TEMP_BAT_LOW_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_DET, 10,
BD71828_INT_TEMP_BAT_LOW_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_RES, 10,
BD71828_INT_TEMP_BAT_HI_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_DET, 10,
BD71828_INT_TEMP_BAT_HI_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_RES, 10, BD71828_INT_TEMP_BAT_LOW_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_LOW_DET, 10, BD71828_INT_TEMP_BAT_LOW_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_RES, 10, BD71828_INT_TEMP_BAT_HI_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_BAT_HI_DET, 10, BD71828_INT_TEMP_BAT_HI_DET_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_RES, 10,
BD71828_INT_TEMP_CHIP_OVER_125_RES_MASK),
REGMAP_IRQ_REG(BD71828_INT_TEMP_CHIP_OVER_125_DET, 10,
@ -267,57 +419,133 @@ static struct regmap_irq_chip bd71828_irq_chip = {
.init_ack_masked = true,
.num_regs = 12,
.num_main_regs = 1,
.sub_reg_offsets = &bd71828_sub_irq_offsets[0],
.sub_reg_offsets = &bd718xx_sub_irq_offsets[0],
.num_main_status_bits = 8,
.irq_reg_stride = 1,
};
static struct regmap_irq_chip bd71815_irq_chip = {
.name = "bd71815_irq",
.main_status = BD71815_REG_INT_STAT,
.irqs = &bd71815_irqs[0],
.num_irqs = ARRAY_SIZE(bd71815_irqs),
.status_base = BD71815_REG_INT_STAT_01,
.mask_base = BD71815_REG_INT_EN_01,
.ack_base = BD71815_REG_INT_STAT_01,
.mask_invert = true,
.init_ack_masked = true,
.num_regs = 12,
.num_main_regs = 1,
.sub_reg_offsets = &bd718xx_sub_irq_offsets[0],
.num_main_status_bits = 8,
.irq_reg_stride = 1,
};
static int set_clk_mode(struct device *dev, struct regmap *regmap,
int clkmode_reg)
{
int ret;
unsigned int open_drain;
ret = of_property_read_u32(dev->of_node, "rohm,clkout-open-drain", &open_drain);
if (ret) {
if (ret == -EINVAL)
return 0;
return ret;
}
if (open_drain > 1) {
dev_err(dev, "bad clk32kout mode configuration");
return -EINVAL;
}
if (open_drain)
return regmap_update_bits(regmap, clkmode_reg, OUT32K_MODE,
OUT32K_MODE_OPEN_DRAIN);
return regmap_update_bits(regmap, clkmode_reg, OUT32K_MODE,
OUT32K_MODE_CMOS);
}
static int bd71828_i2c_probe(struct i2c_client *i2c)
{
struct rohm_regmap_dev *chip;
struct regmap_irq_chip_data *irq_data;
int ret;
struct regmap *regmap;
const struct regmap_config *regmap_config;
struct regmap_irq_chip *irqchip;
unsigned int chip_type;
struct mfd_cell *mfd;
int cells;
int button_irq;
int clkmode_reg;
if (!i2c->irq) {
dev_err(&i2c->dev, "No IRQ configured\n");
return -EINVAL;
}
chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
dev_set_drvdata(&i2c->dev, chip);
chip->regmap = devm_regmap_init_i2c(i2c, &bd71828_regmap);
if (IS_ERR(chip->regmap)) {
dev_err(&i2c->dev, "Failed to initialize Regmap\n");
return PTR_ERR(chip->regmap);
chip_type = (unsigned int)(uintptr_t)
of_device_get_match_data(&i2c->dev);
switch (chip_type) {
case ROHM_CHIP_TYPE_BD71828:
mfd = bd71828_mfd_cells;
cells = ARRAY_SIZE(bd71828_mfd_cells);
regmap_config = &bd71828_regmap;
irqchip = &bd71828_irq_chip;
clkmode_reg = BD71828_REG_OUT32K;
button_irq = BD71828_INT_SHORTPUSH;
break;
case ROHM_CHIP_TYPE_BD71815:
mfd = bd71815_mfd_cells;
cells = ARRAY_SIZE(bd71815_mfd_cells);
regmap_config = &bd71815_regmap;
irqchip = &bd71815_irq_chip;
clkmode_reg = BD71815_REG_OUT32K;
/*
* If BD71817 support is needed we should be able to handle it
* with proper DT configs + BD71815 drivers + power-button.
* BD71815 data-sheet does not list the power-button IRQ so we
* don't use it.
*/
button_irq = 0;
break;
default:
dev_err(&i2c->dev, "Unknown device type");
return -EINVAL;
}
ret = devm_regmap_add_irq_chip(&i2c->dev, chip->regmap,
i2c->irq, IRQF_ONESHOT, 0,
&bd71828_irq_chip, &irq_data);
regmap = devm_regmap_init_i2c(i2c, regmap_config);
if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "Failed to initialize Regmap\n");
return PTR_ERR(regmap);
}
ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,
IRQF_ONESHOT, 0, irqchip, &irq_data);
if (ret) {
dev_err(&i2c->dev, "Failed to add IRQ chip\n");
return ret;
}
dev_dbg(&i2c->dev, "Registered %d IRQs for chip\n",
bd71828_irq_chip.num_irqs);
irqchip->num_irqs);
ret = regmap_irq_get_virq(irq_data, BD71828_INT_SHORTPUSH);
if (button_irq) {
ret = regmap_irq_get_virq(irq_data, button_irq);
if (ret < 0) {
dev_err(&i2c->dev, "Failed to get the power-key IRQ\n");
return ret;
}
button.irq = ret;
}
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
bd71828_mfd_cells,
ARRAY_SIZE(bd71828_mfd_cells), NULL, 0,
regmap_irq_get_domain(irq_data));
ret = set_clk_mode(&i2c->dev, regmap, clkmode_reg);
if (ret)
return ret;
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, mfd, cells,
NULL, 0, regmap_irq_get_domain(irq_data));
if (ret)
dev_err(&i2c->dev, "Failed to create subdevices\n");
@ -325,7 +553,13 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
}
static const struct of_device_id bd71828_of_match[] = {
{ .compatible = "rohm,bd71828", },
{
.compatible = "rohm,bd71828",
.data = (void *)ROHM_CHIP_TYPE_BD71828,
}, {
.compatible = "rohm,bd71815",
.data = (void *)ROHM_CHIP_TYPE_BD71815,
},
{ },
};
MODULE_DEVICE_TABLE(of, bd71828_of_match);

View File

@ -91,9 +91,9 @@ static const struct regmap_config bd718xx_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
static int bd718xx_init_press_duration(struct bd718xx *bd718xx)
static int bd718xx_init_press_duration(struct regmap *regmap,
struct device *dev)
{
struct device* dev = bd718xx->chip.dev;
u32 short_press_ms, long_press_ms;
u32 short_press_value, long_press_value;
int ret;
@ -102,8 +102,7 @@ static int bd718xx_init_press_duration(struct bd718xx *bd718xx)
&short_press_ms);
if (!ret) {
short_press_value = min(15u, (short_press_ms + 250) / 500);
ret = regmap_update_bits(bd718xx->chip.regmap,
BD718XX_REG_PWRONCONFIG0,
ret = regmap_update_bits(regmap, BD718XX_REG_PWRONCONFIG0,
BD718XX_PWRBTN_PRESS_DURATION_MASK,
short_press_value);
if (ret) {
@ -116,8 +115,7 @@ static int bd718xx_init_press_duration(struct bd718xx *bd718xx)
&long_press_ms);
if (!ret) {
long_press_value = min(15u, (long_press_ms + 500) / 1000);
ret = regmap_update_bits(bd718xx->chip.regmap,
BD718XX_REG_PWRONCONFIG1,
ret = regmap_update_bits(regmap, BD718XX_REG_PWRONCONFIG1,
BD718XX_PWRBTN_PRESS_DURATION_MASK,
long_press_value);
if (ret) {
@ -132,7 +130,8 @@ static int bd718xx_init_press_duration(struct bd718xx *bd718xx)
static int bd718xx_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct bd718xx *bd718xx;
struct regmap *regmap;
struct regmap_irq_chip_data *irq_data;
int ret;
unsigned int chip_type;
struct mfd_cell *mfd;
@ -142,13 +141,6 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev, "No IRQ configured\n");
return -EINVAL;
}
bd718xx = devm_kzalloc(&i2c->dev, sizeof(struct bd718xx), GFP_KERNEL);
if (!bd718xx)
return -ENOMEM;
bd718xx->chip_irq = i2c->irq;
chip_type = (unsigned int)(uintptr_t)
of_device_get_match_data(&i2c->dev);
switch (chip_type) {
@ -164,29 +156,26 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
dev_err(&i2c->dev, "Unknown device type");
return -EINVAL;
}
bd718xx->chip.dev = &i2c->dev;
dev_set_drvdata(&i2c->dev, bd718xx);
bd718xx->chip.regmap = devm_regmap_init_i2c(i2c,
&bd718xx_regmap_config);
if (IS_ERR(bd718xx->chip.regmap)) {
regmap = devm_regmap_init_i2c(i2c, &bd718xx_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "regmap initialization failed\n");
return PTR_ERR(bd718xx->chip.regmap);
return PTR_ERR(regmap);
}
ret = devm_regmap_add_irq_chip(&i2c->dev, bd718xx->chip.regmap,
bd718xx->chip_irq, IRQF_ONESHOT, 0,
&bd718xx_irq_chip, &bd718xx->irq_data);
ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,
IRQF_ONESHOT, 0, &bd718xx_irq_chip,
&irq_data);
if (ret) {
dev_err(&i2c->dev, "Failed to add irq_chip\n");
return ret;
}
ret = bd718xx_init_press_duration(bd718xx);
ret = bd718xx_init_press_duration(regmap, &i2c->dev);
if (ret)
return ret;
ret = regmap_irq_get_virq(bd718xx->irq_data, BD718XX_INT_PWRBTN_S);
ret = regmap_irq_get_virq(irq_data, BD718XX_INT_PWRBTN_S);
if (ret < 0) {
dev_err(&i2c->dev, "Failed to get the IRQ\n");
@ -195,9 +184,9 @@ static int bd718xx_i2c_probe(struct i2c_client *i2c,
button.irq = ret;
ret = devm_mfd_add_devices(bd718xx->chip.dev, PLATFORM_DEVID_AUTO,
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
mfd, cells, NULL, 0,
regmap_irq_get_domain(bd718xx->irq_data));
regmap_irq_get_domain(irq_data));
if (ret)
dev_err(&i2c->dev, "Failed to create subdevices\n");

189
drivers/mfd/rohm-bd9576.c Normal file
View File

@ -0,0 +1,189 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2021 ROHM Semiconductors
*
* ROHM BD9576MUF and BD9573MUF PMIC driver
*/
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/mfd/rohm-bd957x.h>
#include <linux/mfd/rohm-generic.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/types.h>
enum {
BD957X_REGULATOR_CELL,
BD957X_WDT_CELL,
};
/*
* Due to the BD9576MUF nasty IRQ behaiour we don't always populate IRQs.
* These will be added to regulator resources only if IRQ information for the
* PMIC is populated in device-tree.
*/
static const struct resource bd9576_regulator_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD9576_INT_THERM, "bd9576-temp"),
DEFINE_RES_IRQ_NAMED(BD9576_INT_OVD, "bd9576-ovd"),
DEFINE_RES_IRQ_NAMED(BD9576_INT_UVD, "bd9576-uvd"),
};
static struct mfd_cell bd9573_mfd_cells[] = {
[BD957X_REGULATOR_CELL] = { .name = "bd9573-regulator", },
[BD957X_WDT_CELL] = { .name = "bd9576-wdt", },
};
static struct mfd_cell bd9576_mfd_cells[] = {
[BD957X_REGULATOR_CELL] = { .name = "bd9576-regulator", },
[BD957X_WDT_CELL] = { .name = "bd9576-wdt", },
};
static const struct regmap_range volatile_ranges[] = {
regmap_reg_range(BD957X_REG_SMRB_ASSERT, BD957X_REG_SMRB_ASSERT),
regmap_reg_range(BD957X_REG_PMIC_INTERNAL_STAT,
BD957X_REG_PMIC_INTERNAL_STAT),
regmap_reg_range(BD957X_REG_INT_THERM_STAT, BD957X_REG_INT_THERM_STAT),
regmap_reg_range(BD957X_REG_INT_OVP_STAT, BD957X_REG_INT_SYS_STAT),
regmap_reg_range(BD957X_REG_INT_MAIN_STAT, BD957X_REG_INT_MAIN_STAT),
};
static const struct regmap_access_table volatile_regs = {
.yes_ranges = &volatile_ranges[0],
.n_yes_ranges = ARRAY_SIZE(volatile_ranges),
};
static struct regmap_config bd957x_regmap = {
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &volatile_regs,
.max_register = BD957X_MAX_REGISTER,
.cache_type = REGCACHE_RBTREE,
};
static struct regmap_irq bd9576_irqs[] = {
REGMAP_IRQ_REG(BD9576_INT_THERM, 0, BD957X_MASK_INT_MAIN_THERM),
REGMAP_IRQ_REG(BD9576_INT_OVP, 0, BD957X_MASK_INT_MAIN_OVP),
REGMAP_IRQ_REG(BD9576_INT_SCP, 0, BD957X_MASK_INT_MAIN_SCP),
REGMAP_IRQ_REG(BD9576_INT_OCP, 0, BD957X_MASK_INT_MAIN_OCP),
REGMAP_IRQ_REG(BD9576_INT_OVD, 0, BD957X_MASK_INT_MAIN_OVD),
REGMAP_IRQ_REG(BD9576_INT_UVD, 0, BD957X_MASK_INT_MAIN_UVD),
REGMAP_IRQ_REG(BD9576_INT_UVP, 0, BD957X_MASK_INT_MAIN_UVP),
REGMAP_IRQ_REG(BD9576_INT_SYS, 0, BD957X_MASK_INT_MAIN_SYS),
};
static struct regmap_irq_chip bd9576_irq_chip = {
.name = "bd9576_irq",
.irqs = &bd9576_irqs[0],
.num_irqs = ARRAY_SIZE(bd9576_irqs),
.status_base = BD957X_REG_INT_MAIN_STAT,
.mask_base = BD957X_REG_INT_MAIN_MASK,
.ack_base = BD957X_REG_INT_MAIN_STAT,
.init_ack_masked = true,
.num_regs = 1,
.irq_reg_stride = 1,
};
static int bd957x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
int ret;
struct regmap *regmap;
struct mfd_cell *cells;
int num_cells;
unsigned long chip_type;
struct irq_domain *domain;
bool usable_irqs;
chip_type = (unsigned long)of_device_get_match_data(&i2c->dev);
switch (chip_type) {
case ROHM_CHIP_TYPE_BD9576:
cells = bd9576_mfd_cells;
num_cells = ARRAY_SIZE(bd9576_mfd_cells);
usable_irqs = !!i2c->irq;
break;
case ROHM_CHIP_TYPE_BD9573:
cells = bd9573_mfd_cells;
num_cells = ARRAY_SIZE(bd9573_mfd_cells);
/*
* BD9573 only supports fatal IRQs which we can not handle
* because SoC is going to lose the power.
*/
usable_irqs = false;
break;
default:
dev_err(&i2c->dev, "Unknown device type");
return -EINVAL;
}
regmap = devm_regmap_init_i2c(i2c, &bd957x_regmap);
if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "Failed to initialize Regmap\n");
return PTR_ERR(regmap);
}
/*
* BD9576 behaves badly. It kepts IRQ line asserted for the whole
* duration of detected HW condition (like over temperature). So we
* don't require IRQ to be populated.
* If IRQ information is not given, then we mask all IRQs and do not
* provide IRQ resources to regulator driver - which then just omits
* the notifiers.
*/
if (usable_irqs) {
struct regmap_irq_chip_data *irq_data;
struct mfd_cell *regulators;
regulators = &bd9576_mfd_cells[BD957X_REGULATOR_CELL];
regulators->resources = bd9576_regulator_irqs;
regulators->num_resources = ARRAY_SIZE(bd9576_regulator_irqs);
ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, i2c->irq,
IRQF_ONESHOT, 0,
&bd9576_irq_chip, &irq_data);
if (ret) {
dev_err(&i2c->dev, "Failed to add IRQ chip\n");
return ret;
}
domain = regmap_irq_get_domain(irq_data);
} else {
ret = regmap_update_bits(regmap, BD957X_REG_INT_MAIN_MASK,
BD957X_MASK_INT_ALL,
BD957X_MASK_INT_ALL);
if (ret)
return ret;
domain = NULL;
}
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, cells,
num_cells, NULL, 0, domain);
if (ret)
dev_err(&i2c->dev, "Failed to create subdevices\n");
return ret;
}
static const struct of_device_id bd957x_of_match[] = {
{ .compatible = "rohm,bd9576", .data = (void *)ROHM_CHIP_TYPE_BD9576, },
{ .compatible = "rohm,bd9573", .data = (void *)ROHM_CHIP_TYPE_BD9573, },
{ },
};
MODULE_DEVICE_TABLE(of, bd957x_of_match);
static struct i2c_driver bd957x_drv = {
.driver = {
.name = "rohm-bd957x",
.of_match_table = bd957x_of_match,
},
.probe = &bd957x_i2c_probe,
};
module_i2c_driver(bd957x_drv);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("ROHM BD9576MUF and BD9573MUF Power Management IC driver");
MODULE_LICENSE("GPL");

View File

@ -549,19 +549,7 @@ static struct i2c_driver sec_pmic_driver = {
.shutdown = sec_pmic_shutdown,
.id_table = sec_pmic_id,
};
static int __init sec_pmic_init(void)
{
return i2c_add_driver(&sec_pmic_driver);
}
subsys_initcall(sec_pmic_init);
static void __exit sec_pmic_exit(void)
{
i2c_del_driver(&sec_pmic_driver);
}
module_exit(sec_pmic_exit);
module_i2c_driver(sec_pmic_driver);
MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
MODULE_DESCRIPTION("Core support for the S5M MFD");

View File

@ -158,13 +158,18 @@ static const struct regmap_config stm32_timers_regmap_cfg = {
static void stm32_timers_get_arr_size(struct stm32_timers *ddata)
{
u32 arr;
/* Backup ARR to restore it after getting the maximum value */
regmap_read(ddata->regmap, TIM_ARR, &arr);
/*
* Only the available bits will be written so when readback
* we get the maximum value of auto reload register
*/
regmap_write(ddata->regmap, TIM_ARR, ~0L);
regmap_read(ddata->regmap, TIM_ARR, &ddata->max_arr);
regmap_write(ddata->regmap, TIM_ARR, 0x0);
regmap_write(ddata->regmap, TIM_ARR, arr);
}
static int stm32_timers_dma_probe(struct device *dev,

View File

@ -312,7 +312,7 @@ EXPORT_SYMBOL_GPL(stmpe_set_altfunc);
* GPIO (all variants)
*/
static const struct resource stmpe_gpio_resources[] = {
static struct resource stmpe_gpio_resources[] = {
/* Start and end filled dynamically */
{
.flags = IORESOURCE_IRQ,
@ -336,7 +336,8 @@ static const struct mfd_cell stmpe_gpio_cell_noirq = {
* Keypad (1601, 2401, 2403)
*/
static const struct resource stmpe_keypad_resources[] = {
static struct resource stmpe_keypad_resources[] = {
/* Start and end filled dynamically */
{
.name = "KEYPAD",
.flags = IORESOURCE_IRQ,
@ -357,7 +358,8 @@ static const struct mfd_cell stmpe_keypad_cell = {
/*
* PWM (1601, 2401, 2403)
*/
static const struct resource stmpe_pwm_resources[] = {
static struct resource stmpe_pwm_resources[] = {
/* Start and end filled dynamically */
{
.name = "PWM0",
.flags = IORESOURCE_IRQ,
@ -445,7 +447,8 @@ static struct stmpe_variant_info stmpe801_noirq = {
* Touchscreen (STMPE811 or STMPE610)
*/
static const struct resource stmpe_ts_resources[] = {
static struct resource stmpe_ts_resources[] = {
/* Start and end filled dynamically */
{
.name = "TOUCH_DET",
.flags = IORESOURCE_IRQ,
@ -467,7 +470,8 @@ static const struct mfd_cell stmpe_ts_cell = {
* ADC (STMPE811)
*/
static const struct resource stmpe_adc_resources[] = {
static struct resource stmpe_adc_resources[] = {
/* Start and end filled dynamically */
{
.name = "STMPE_TEMP_SENS",
.flags = IORESOURCE_IRQ,

View File

@ -393,6 +393,14 @@ config PWM_MXS
To compile this driver as a module, choose M here: the module
will be called pwm-mxs.
config PWM_NTXEC
tristate "Netronix embedded controller PWM support"
depends on MFD_NTXEC
help
Say yes here if you want to support the PWM output of the embedded
controller found in certain e-book readers designed by the original
design manufacturer Netronix.
config PWM_OMAP_DMTIMER
tristate "OMAP Dual-Mode Timer PWM support"
depends on OF

View File

@ -35,6 +35,7 @@ obj-$(CONFIG_PWM_MESON) += pwm-meson.o
obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o
obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o
obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
obj-$(CONFIG_PWM_PXA) += pwm-pxa.o

184
drivers/pwm/pwm-ntxec.c Normal file
View File

@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The Netronix embedded controller is a microcontroller found in some
* e-book readers designed by the original design manufacturer Netronix, Inc.
* It contains RTC, battery monitoring, system power management, and PWM
* functionality.
*
* This driver implements PWM output.
*
* Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
*
* Limitations:
* - The get_state callback is not implemented, because the current state of
* the PWM output can't be read back from the hardware.
* - The hardware can only generate normal polarity output.
* - The period and duty cycle can't be changed together in one atomic action.
*/
#include <linux/mfd/ntxec.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/types.h>
struct ntxec_pwm {
struct device *dev;
struct ntxec *ec;
struct pwm_chip chip;
};
static struct ntxec_pwm *ntxec_pwm_from_chip(struct pwm_chip *chip)
{
return container_of(chip, struct ntxec_pwm, chip);
}
#define NTXEC_REG_AUTO_OFF_HI 0xa1
#define NTXEC_REG_AUTO_OFF_LO 0xa2
#define NTXEC_REG_ENABLE 0xa3
#define NTXEC_REG_PERIOD_LOW 0xa4
#define NTXEC_REG_PERIOD_HIGH 0xa5
#define NTXEC_REG_DUTY_LOW 0xa6
#define NTXEC_REG_DUTY_HIGH 0xa7
/*
* The time base used in the EC is 8MHz, or 125ns. Period and duty cycle are
* measured in this unit.
*/
#define TIME_BASE_NS 125
/*
* The maximum input value (in nanoseconds) is determined by the time base and
* the range of the hardware registers that hold the converted value.
* It fits into 32 bits, so we can do our calculations in 32 bits as well.
*/
#define MAX_PERIOD_NS (TIME_BASE_NS * 0xffff)
static int ntxec_pwm_set_raw_period_and_duty_cycle(struct pwm_chip *chip,
int period, int duty)
{
struct ntxec_pwm *priv = ntxec_pwm_from_chip(chip);
/*
* Changes to the period and duty cycle take effect as soon as the
* corresponding low byte is written, so the hardware may be configured
* to an inconsistent state after the period is written and before the
* duty cycle is fully written. If, in such a case, the old duty cycle
* is longer than the new period, the EC may output 100% for a moment.
*
* To minimize the time between the changes to period and duty cycle
* taking effect, the writes are interleaved.
*/
struct reg_sequence regs[] = {
{ NTXEC_REG_PERIOD_HIGH, ntxec_reg8(period >> 8) },
{ NTXEC_REG_DUTY_HIGH, ntxec_reg8(duty >> 8) },
{ NTXEC_REG_PERIOD_LOW, ntxec_reg8(period) },
{ NTXEC_REG_DUTY_LOW, ntxec_reg8(duty) },
};
return regmap_multi_reg_write(priv->ec->regmap, regs, ARRAY_SIZE(regs));
}
static int ntxec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm_dev,
const struct pwm_state *state)
{
struct ntxec_pwm *priv = ntxec_pwm_from_chip(chip);
unsigned int period, duty;
int res;
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
period = min_t(u64, state->period, MAX_PERIOD_NS);
duty = min_t(u64, state->duty_cycle, period);
period /= TIME_BASE_NS;
duty /= TIME_BASE_NS;
/*
* Writing a duty cycle of zero puts the device into a state where
* writing a higher duty cycle doesn't result in the brightness that it
* usually results in. This can be fixed by cycling the ENABLE register.
*
* As a workaround, write ENABLE=0 when the duty cycle is zero.
* The case that something has previously set the duty cycle to zero
* but ENABLE=1, is not handled.
*/
if (state->enabled && duty != 0) {
res = ntxec_pwm_set_raw_period_and_duty_cycle(chip, period, duty);
if (res)
return res;
res = regmap_write(priv->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(1));
if (res)
return res;
/* Disable the auto-off timer */
res = regmap_write(priv->ec->regmap, NTXEC_REG_AUTO_OFF_HI, ntxec_reg8(0xff));
if (res)
return res;
return regmap_write(priv->ec->regmap, NTXEC_REG_AUTO_OFF_LO, ntxec_reg8(0xff));
} else {
return regmap_write(priv->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(0));
}
}
static const struct pwm_ops ntxec_pwm_ops = {
.owner = THIS_MODULE,
.apply = ntxec_pwm_apply,
/*
* No .get_state callback, because the current state cannot be read
* back from the hardware.
*/
};
static int ntxec_pwm_probe(struct platform_device *pdev)
{
struct ntxec *ec = dev_get_drvdata(pdev->dev.parent);
struct ntxec_pwm *priv;
struct pwm_chip *chip;
pdev->dev.of_node = pdev->dev.parent->of_node;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->ec = ec;
priv->dev = &pdev->dev;
platform_set_drvdata(pdev, priv);
chip = &priv->chip;
chip->dev = &pdev->dev;
chip->ops = &ntxec_pwm_ops;
chip->base = -1;
chip->npwm = 1;
return pwmchip_add(chip);
}
static int ntxec_pwm_remove(struct platform_device *pdev)
{
struct ntxec_pwm *priv = platform_get_drvdata(pdev);
struct pwm_chip *chip = &priv->chip;
return pwmchip_remove(chip);
}
static struct platform_driver ntxec_pwm_driver = {
.driver = {
.name = "ntxec-pwm",
},
.probe = ntxec_pwm_probe,
.remove = ntxec_pwm_remove,
};
module_platform_driver(ntxec_pwm_driver);
MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
MODULE_DESCRIPTION("PWM driver for Netronix EC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ntxec-pwm");

View File

@ -204,6 +204,17 @@ config REGULATOR_BD70528
This driver can also be built as a module. If so, the module
will be called bd70528-regulator.
config REGULATOR_BD71815
tristate "ROHM BD71815 Power Regulator"
depends on MFD_ROHM_BD71828
help
This driver supports voltage regulators on ROHM BD71815 PMIC.
This will enable support for the software controllable buck
and LDO regulators and a current regulator for LEDs.
This driver can also be built as a module. If so, the module
will be called bd71815-regulator.
config REGULATOR_BD71828
tristate "ROHM BD71828 Power Regulator"
depends on MFD_ROHM_BD71828

View File

@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-regulator.o
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
obj-$(CONFIG_REGULATOR_BD70528) += bd70528-regulator.o
obj-$(CONFIG_REGULATOR_BD71815) += bd71815-regulator.o
obj-$(CONFIG_REGULATOR_BD71828) += bd71828-regulator.o
obj-$(CONFIG_REGULATOR_BD718XX) += bd718x7-regulator.o
obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o

View File

@ -0,0 +1,652 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright 2014 Embest Technology Co. Ltd. Inc.
// bd71815-regulator.c ROHM BD71815 regulator driver
//
// Author: Tony Luo <luofc@embedinfo.com>
//
// Partially rewritten at 2021 by
// Matti Vaittinen <matti.vaitinen@fi.rohmeurope.com>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/mfd/rohm-generic.h>
#include <linux/mfd/rohm-bd71815.h>
#include <linux/regulator/of_regulator.h>
struct bd71815_regulator {
struct regulator_desc desc;
const struct rohm_dvs_config *dvs;
};
struct bd71815_pmic {
struct bd71815_regulator descs[BD71815_REGULATOR_CNT];
struct regmap *regmap;
struct device *dev;
struct gpio_descs *gps;
struct regulator_dev *rdev[BD71815_REGULATOR_CNT];
};
static const int bd7181x_wled_currents[] = {
10, 20, 30, 50, 70, 100, 200, 300, 500, 700, 1000, 2000, 3000, 4000,
5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000,
16000, 17000, 18000, 19000, 20000, 21000, 22000, 23000, 24000, 25000,
};
static const struct rohm_dvs_config buck1_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_BUCK1_VOLT_H,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = BD71815_BUCK_RUN_ON,
.snvs_on_mask = BD71815_BUCK_SNVS_ON,
.suspend_reg = BD71815_REG_BUCK1_VOLT_L,
.suspend_mask = BD71815_VOLT_MASK,
.suspend_on_mask = BD71815_BUCK_SUSP_ON,
.lpsr_reg = BD71815_REG_BUCK1_VOLT_L,
.lpsr_mask = BD71815_VOLT_MASK,
.lpsr_on_mask = BD71815_BUCK_LPSR_ON,
};
static const struct rohm_dvs_config buck2_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_BUCK2_VOLT_H,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = BD71815_BUCK_RUN_ON,
.snvs_on_mask = BD71815_BUCK_SNVS_ON,
.suspend_reg = BD71815_REG_BUCK2_VOLT_L,
.suspend_mask = BD71815_VOLT_MASK,
.suspend_on_mask = BD71815_BUCK_SUSP_ON,
.lpsr_reg = BD71815_REG_BUCK2_VOLT_L,
.lpsr_mask = BD71815_VOLT_MASK,
.lpsr_on_mask = BD71815_BUCK_LPSR_ON,
};
static const struct rohm_dvs_config buck3_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_BUCK3_VOLT,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = BD71815_BUCK_RUN_ON,
.snvs_on_mask = BD71815_BUCK_SNVS_ON,
.suspend_on_mask = BD71815_BUCK_SUSP_ON,
.lpsr_on_mask = BD71815_BUCK_LPSR_ON,
};
static const struct rohm_dvs_config buck4_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_BUCK4_VOLT,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = BD71815_BUCK_RUN_ON,
.snvs_on_mask = BD71815_BUCK_SNVS_ON,
.suspend_on_mask = BD71815_BUCK_SUSP_ON,
.lpsr_on_mask = BD71815_BUCK_LPSR_ON,
};
static const struct rohm_dvs_config ldo1_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_LDO_MODE1,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = LDO1_RUN_ON,
.snvs_on_mask = LDO1_SNVS_ON,
.suspend_on_mask = LDO1_SUSP_ON,
.lpsr_on_mask = LDO1_LPSR_ON,
};
static const struct rohm_dvs_config ldo2_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_LDO_MODE2,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = LDO2_RUN_ON,
.snvs_on_mask = LDO2_SNVS_ON,
.suspend_on_mask = LDO2_SUSP_ON,
.lpsr_on_mask = LDO2_LPSR_ON,
};
static const struct rohm_dvs_config ldo3_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_LDO_MODE2,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = LDO3_RUN_ON,
.snvs_on_mask = LDO3_SNVS_ON,
.suspend_on_mask = LDO3_SUSP_ON,
.lpsr_on_mask = LDO3_LPSR_ON,
};
static const struct rohm_dvs_config ldo4_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_LDO_MODE3,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = LDO4_RUN_ON,
.snvs_on_mask = LDO4_SNVS_ON,
.suspend_on_mask = LDO4_SUSP_ON,
.lpsr_on_mask = LDO4_LPSR_ON,
};
static const struct rohm_dvs_config ldo5_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_LDO_MODE3,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = LDO5_RUN_ON,
.snvs_on_mask = LDO5_SNVS_ON,
.suspend_on_mask = LDO5_SUSP_ON,
.lpsr_on_mask = LDO5_LPSR_ON,
};
static const struct rohm_dvs_config dvref_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_on_mask = DVREF_RUN_ON,
.snvs_on_mask = DVREF_SNVS_ON,
.suspend_on_mask = DVREF_SUSP_ON,
.lpsr_on_mask = DVREF_LPSR_ON,
};
static const struct rohm_dvs_config ldolpsr_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_on_mask = DVREF_RUN_ON,
.snvs_on_mask = DVREF_SNVS_ON,
.suspend_on_mask = DVREF_SUSP_ON,
.lpsr_on_mask = DVREF_LPSR_ON,
};
static const struct rohm_dvs_config buck5_dvs = {
.level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_SNVS |
ROHM_DVS_LEVEL_SUSPEND | ROHM_DVS_LEVEL_LPSR,
.run_reg = BD71815_REG_BUCK5_VOLT,
.run_mask = BD71815_VOLT_MASK,
.run_on_mask = BD71815_BUCK_RUN_ON,
.snvs_on_mask = BD71815_BUCK_SNVS_ON,
.suspend_on_mask = BD71815_BUCK_SUSP_ON,
.lpsr_on_mask = BD71815_BUCK_LPSR_ON,
};
static int set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc,
struct regulator_config *cfg)
{
struct bd71815_regulator *data;
data = container_of(desc, struct bd71815_regulator, desc);
return rohm_regulator_set_dvs_levels(data->dvs, np, desc, cfg->regmap);
}
/*
* Bucks 1 and 2 have two voltage selection registers where selected
* voltage can be set. Which of the registers is used can be either controlled
* by a control bit in register - or by HW state. If HW state specific voltages
* are given - then we assume HW state based control should be used.
*
* If volatge value is updated to currently selected register - then output
* voltage is immediately changed no matter what is set as ramp rate. Thus we
* default changing voltage by writing new value to inactive register and
* then updating the 'register selection' bit. This naturally only works when
* HW state machine is not used to select the voltage.
*/
static int buck12_set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc,
struct regulator_config *cfg)
{
struct bd71815_regulator *data;
int ret = 0, val;
data = container_of(desc, struct bd71815_regulator, desc);
if (of_find_property(np, "rohm,dvs-run-voltage", NULL) ||
of_find_property(np, "rohm,dvs-suspend-voltage", NULL) ||
of_find_property(np, "rohm,dvs-lpsr-voltage", NULL) ||
of_find_property(np, "rohm,dvs-snvs-voltage", NULL)) {
ret = regmap_read(cfg->regmap, desc->vsel_reg, &val);
if (ret)
return ret;
if (!(BD71815_BUCK_STBY_DVS & val) &&
!(BD71815_BUCK_DVSSEL & val)) {
int val2;
/*
* We are currently using voltage from _L.
* We'd better copy it to _H and switch to it to
* avoid shutting us down if LPSR or SUSPEND is set to
* disabled. _L value is at reg _H + 1
*/
ret = regmap_read(cfg->regmap, desc->vsel_reg + 1,
&val2);
if (ret)
return ret;
ret = regmap_update_bits(cfg->regmap, desc->vsel_reg,
BD71815_VOLT_MASK |
BD71815_BUCK_DVSSEL,
val2 | BD71815_BUCK_DVSSEL);
if (ret)
return ret;
}
ret = rohm_regulator_set_dvs_levels(data->dvs, np, desc,
cfg->regmap);
if (ret)
return ret;
/*
* DVS levels were given => use HW-state machine for voltage
* controls. NOTE: AFAIK, This means that if voltage is changed
* by SW the ramp-rate is not respected. Should we disable
* SW voltage control when the HW state machine is used?
*/
ret = regmap_update_bits(cfg->regmap, desc->vsel_reg,
BD71815_BUCK_STBY_DVS,
BD71815_BUCK_STBY_DVS);
}
return ret;
}
/*
* BUCK1/2
* BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting
* 00: 10.00mV/usec 10mV 1uS
* 01: 5.00mV/usec 10mV 2uS
* 10: 2.50mV/usec 10mV 4uS
* 11: 1.25mV/usec 10mV 8uS
*/
static const unsigned int bd7181x_ramp_table[] = { 1250, 2500, 5000, 10000 };
static int bd7181x_led_set_current_limit(struct regulator_dev *rdev,
int min_uA, int max_uA)
{
int ret;
int onstatus;
onstatus = regulator_is_enabled_regmap(rdev);
ret = regulator_set_current_limit_regmap(rdev, min_uA, max_uA);
if (!ret) {
int newstatus;
newstatus = regulator_is_enabled_regmap(rdev);
if (onstatus != newstatus) {
/*
* HW FIX: spurious led status change detected. Toggle
* state as a workaround
*/
if (onstatus)
ret = regulator_enable_regmap(rdev);
else
ret = regulator_disable_regmap(rdev);
if (ret)
dev_err(rdev_get_dev(rdev),
"failed to revert the LED state (%d)\n",
ret);
}
}
return ret;
}
static int bd7181x_buck12_get_voltage_sel(struct regulator_dev *rdev)
{
struct bd71815_pmic *pmic = rdev_get_drvdata(rdev);
int rid = rdev_get_id(rdev);
int ret, regh, regl, val;
regh = BD71815_REG_BUCK1_VOLT_H + rid * 0x2;
regl = BD71815_REG_BUCK1_VOLT_L + rid * 0x2;
ret = regmap_read(pmic->regmap, regh, &val);
if (ret)
return ret;
/*
* If we use HW state machine based voltage reg selection - then we
* return BD71815_REG_BUCK1_VOLT_H which is used at RUN.
* Else we do return the BD71815_REG_BUCK1_VOLT_H or
* BD71815_REG_BUCK1_VOLT_L depending on which is selected to be used
* by BD71815_BUCK_DVSSEL bit
*/
if ((!(val & BD71815_BUCK_STBY_DVS)) && (!(val & BD71815_BUCK_DVSSEL)))
ret = regmap_read(pmic->regmap, regl, &val);
if (ret)
return ret;
return val & BD71815_VOLT_MASK;
}
/*
* For Buck 1/2.
*/
static int bd7181x_buck12_set_voltage_sel(struct regulator_dev *rdev,
unsigned int sel)
{
struct bd71815_pmic *pmic = rdev_get_drvdata(rdev);
int rid = rdev_get_id(rdev);
int ret, val, reg, regh, regl;
regh = BD71815_REG_BUCK1_VOLT_H + rid*0x2;
regl = BD71815_REG_BUCK1_VOLT_L + rid*0x2;
ret = regmap_read(pmic->regmap, regh, &val);
if (ret)
return ret;
/*
* If bucks 1 & 2 are controlled by state machine - then the RUN state
* voltage is set to BD71815_REG_BUCK1_VOLT_H. Changing SUSPEND/LPSR
* voltages at runtime is not supported by this driver.
*/
if (((val & BD71815_BUCK_STBY_DVS))) {
return regmap_update_bits(pmic->regmap, regh, BD71815_VOLT_MASK,
sel);
}
/* Update new voltage to the register which is not selected now */
if (val & BD71815_BUCK_DVSSEL)
reg = regl;
else
reg = regh;
ret = regmap_update_bits(pmic->regmap, reg, BD71815_VOLT_MASK, sel);
if (ret)
return ret;
/* Select the other DVS register to be used */
return regmap_update_bits(pmic->regmap, regh, BD71815_BUCK_DVSSEL, ~val);
}
static const struct regulator_ops bd7181x_ldo_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
static const struct regulator_ops bd7181x_fixed_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
};
static const struct regulator_ops bd7181x_buck_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.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,
};
static const struct regulator_ops bd7181x_buck12_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = bd7181x_buck12_set_voltage_sel,
.get_voltage_sel = bd7181x_buck12_get_voltage_sel,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_ramp_delay = regulator_set_ramp_delay_regmap,
};
static const struct regulator_ops bd7181x_led_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_current_limit = bd7181x_led_set_current_limit,
.get_current_limit = regulator_get_current_limit_regmap,
};
#define BD71815_FIXED_REG(_name, _id, ereg, emsk, voltage, _dvs) \
[(_id)] = { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(#_name), \
.regulators_node = of_match_ptr("regulators"), \
.n_voltages = 1, \
.ops = &bd7181x_fixed_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = (_id), \
.owner = THIS_MODULE, \
.min_uV = (voltage), \
.enable_reg = (ereg), \
.enable_mask = (emsk), \
.of_parse_cb = set_hw_dvs_levels, \
}, \
.dvs = (_dvs), \
}
#define BD71815_BUCK_REG(_name, _id, vsel, ereg, min, max, step, _dvs) \
[(_id)] = { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(#_name), \
.regulators_node = of_match_ptr("regulators"), \
.n_voltages = ((max) - (min)) / (step) + 1, \
.ops = &bd7181x_buck_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = (_id), \
.owner = THIS_MODULE, \
.min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (vsel), \
.vsel_mask = BD71815_VOLT_MASK, \
.enable_reg = (ereg), \
.enable_mask = BD71815_BUCK_RUN_ON, \
.of_parse_cb = set_hw_dvs_levels, \
}, \
.dvs = (_dvs), \
}
#define BD71815_BUCK12_REG(_name, _id, vsel, ereg, min, max, step, \
_dvs) \
[(_id)] = { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(#_name), \
.regulators_node = of_match_ptr("regulators"), \
.n_voltages = ((max) - (min)) / (step) + 1, \
.ops = &bd7181x_buck12_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = (_id), \
.owner = THIS_MODULE, \
.min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (vsel), \
.vsel_mask = 0x3f, \
.enable_reg = (ereg), \
.enable_mask = 0x04, \
.ramp_reg = (ereg), \
.ramp_mask = BD71815_BUCK_RAMPRATE_MASK, \
.ramp_delay_table = bd7181x_ramp_table, \
.n_ramp_values = ARRAY_SIZE(bd7181x_ramp_table),\
.of_parse_cb = buck12_set_hw_dvs_levels, \
}, \
.dvs = (_dvs), \
}
#define BD71815_LED_REG(_name, _id, csel, mask, ereg, emsk, currents) \
[(_id)] = { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(#_name), \
.regulators_node = of_match_ptr("regulators"), \
.n_current_limits = ARRAY_SIZE(currents), \
.ops = &bd7181x_led_regulator_ops, \
.type = REGULATOR_CURRENT, \
.id = (_id), \
.owner = THIS_MODULE, \
.curr_table = currents, \
.csel_reg = (csel), \
.csel_mask = (mask), \
.enable_reg = (ereg), \
.enable_mask = (emsk), \
}, \
}
#define BD71815_LDO_REG(_name, _id, vsel, ereg, emsk, min, max, step, \
_dvs) \
[(_id)] = { \
.desc = { \
.name = #_name, \
.of_match = of_match_ptr(#_name), \
.regulators_node = of_match_ptr("regulators"), \
.n_voltages = ((max) - (min)) / (step) + 1, \
.ops = &bd7181x_ldo_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.id = (_id), \
.owner = THIS_MODULE, \
.min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (vsel), \
.vsel_mask = BD71815_VOLT_MASK, \
.enable_reg = (ereg), \
.enable_mask = (emsk), \
.of_parse_cb = set_hw_dvs_levels, \
}, \
.dvs = (_dvs), \
}
static struct bd71815_regulator bd71815_regulators[] = {
BD71815_BUCK12_REG(buck1, BD71815_BUCK1, BD71815_REG_BUCK1_VOLT_H,
BD71815_REG_BUCK1_MODE, 800000, 2000000, 25000,
&buck1_dvs),
BD71815_BUCK12_REG(buck2, BD71815_BUCK2, BD71815_REG_BUCK2_VOLT_H,
BD71815_REG_BUCK2_MODE, 800000, 2000000, 25000,
&buck2_dvs),
BD71815_BUCK_REG(buck3, BD71815_BUCK3, BD71815_REG_BUCK3_VOLT,
BD71815_REG_BUCK3_MODE, 1200000, 2700000, 50000,
&buck3_dvs),
BD71815_BUCK_REG(buck4, BD71815_BUCK4, BD71815_REG_BUCK4_VOLT,
BD71815_REG_BUCK4_MODE, 1100000, 1850000, 25000,
&buck4_dvs),
BD71815_BUCK_REG(buck5, BD71815_BUCK5, BD71815_REG_BUCK5_VOLT,
BD71815_REG_BUCK5_MODE, 1800000, 3300000, 50000,
&buck5_dvs),
BD71815_LDO_REG(ldo1, BD71815_LDO1, BD71815_REG_LDO1_VOLT,
BD71815_REG_LDO_MODE1, LDO1_RUN_ON, 800000, 3300000,
50000, &ldo1_dvs),
BD71815_LDO_REG(ldo2, BD71815_LDO2, BD71815_REG_LDO2_VOLT,
BD71815_REG_LDO_MODE2, LDO2_RUN_ON, 800000, 3300000,
50000, &ldo2_dvs),
/*
* Let's default LDO3 to be enabled by SW. We can override ops if DT
* says LDO3 should be enabled by HW when DCIN is connected.
*/
BD71815_LDO_REG(ldo3, BD71815_LDO3, BD71815_REG_LDO3_VOLT,
BD71815_REG_LDO_MODE2, LDO3_RUN_ON, 800000, 3300000,
50000, &ldo3_dvs),
BD71815_LDO_REG(ldo4, BD71815_LDO4, BD71815_REG_LDO4_VOLT,
BD71815_REG_LDO_MODE3, LDO4_RUN_ON, 800000, 3300000,
50000, &ldo4_dvs),
BD71815_LDO_REG(ldo5, BD71815_LDO5, BD71815_REG_LDO5_VOLT_H,
BD71815_REG_LDO_MODE3, LDO5_RUN_ON, 800000, 3300000,
50000, &ldo5_dvs),
BD71815_FIXED_REG(ldodvref, BD71815_LDODVREF, BD71815_REG_LDO_MODE4,
DVREF_RUN_ON, 3000000, &dvref_dvs),
BD71815_FIXED_REG(ldolpsr, BD71815_LDOLPSR, BD71815_REG_LDO_MODE4,
LDO_LPSR_RUN_ON, 1800000, &ldolpsr_dvs),
BD71815_LED_REG(wled, BD71815_WLED, BD71815_REG_LED_DIMM, LED_DIMM_MASK,
BD71815_REG_LED_CTRL, LED_RUN_ON,
bd7181x_wled_currents),
};
static int bd7181x_probe(struct platform_device *pdev)
{
struct bd71815_pmic *pmic;
struct regulator_config config = {};
int i, ret;
struct gpio_desc *ldo4_en;
pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
if (!pmic)
return -ENOMEM;
memcpy(pmic->descs, bd71815_regulators, sizeof(pmic->descs));
pmic->dev = &pdev->dev;
pmic->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!pmic->regmap) {
dev_err(pmic->dev, "No parent regmap\n");
return -ENODEV;
}
platform_set_drvdata(pdev, pmic);
ldo4_en = devm_gpiod_get_from_of_node(&pdev->dev,
pdev->dev.parent->of_node,
"rohm,vsel-gpios", 0,
GPIOD_ASIS, "ldo4-en");
if (IS_ERR(ldo4_en)) {
ret = PTR_ERR(ldo4_en);
if (ret != -ENOENT)
return ret;
ldo4_en = NULL;
}
/* Disable to go to ship-mode */
ret = regmap_update_bits(pmic->regmap, BD71815_REG_PWRCTRL,
RESTARTEN, 0);
if (ret)
return ret;
config.dev = pdev->dev.parent;
config.regmap = pmic->regmap;
for (i = 0; i < BD71815_REGULATOR_CNT; i++) {
struct regulator_desc *desc;
struct regulator_dev *rdev;
desc = &pmic->descs[i].desc;
if (i == BD71815_LDO4)
config.ena_gpiod = ldo4_en;
config.driver_data = pmic;
rdev = devm_regulator_register(&pdev->dev, desc, &config);
if (IS_ERR(rdev)) {
dev_err(&pdev->dev,
"failed to register %s regulator\n",
desc->name);
return PTR_ERR(rdev);
}
config.ena_gpiod = NULL;
pmic->rdev[i] = rdev;
}
return 0;
}
static const struct platform_device_id bd7181x_pmic_id[] = {
{ "bd71815-pmic", ROHM_CHIP_TYPE_BD71815 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd7181x_pmic_id);
static struct platform_driver bd7181x_regulator = {
.driver = {
.name = "bd7181x-pmic",
.owner = THIS_MODULE,
},
.probe = bd7181x_probe,
.id_table = bd7181x_pmic_id,
};
module_platform_driver(bd7181x_regulator);
MODULE_AUTHOR("Tony Luo <luofc@embedinfo.com>");
MODULE_DESCRIPTION("BD71815 voltage regulator driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:bd7181x-pmic");

View File

@ -90,38 +90,7 @@ static const struct linear_range bd71828_ldo_volts[] = {
REGULATOR_LINEAR_RANGE(3300000, 0x32, 0x3f, 0),
};
static int bd71828_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
{
unsigned int val;
switch (ramp_delay) {
case 1 ... 2500:
val = 0;
break;
case 2501 ... 5000:
val = 1;
break;
case 5001 ... 10000:
val = 2;
break;
case 10001 ... 20000:
val = 3;
break;
default:
val = 3;
dev_err(&rdev->dev,
"ramp_delay: %d not supported, setting 20mV/uS",
ramp_delay);
}
/*
* On BD71828 the ramp delay level control reg is at offset +2 to
* enable reg
*/
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg + 2,
BD71828_MASK_RAMP_DELAY,
val << (ffs(BD71828_MASK_RAMP_DELAY) - 1));
}
static const unsigned int bd71828_ramp_delay[] = { 2500, 5000, 10000, 20000 };
static int buck_set_hw_dvs_levels(struct device_node *np,
const struct regulator_desc *desc,
@ -185,7 +154,7 @@ static const struct regulator_ops bd71828_dvs_buck_ops = {
.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 = bd71828_set_ramp_delay,
.set_ramp_delay = regulator_set_ramp_delay_regmap,
};
static const struct regulator_ops bd71828_ldo_ops = {
@ -219,6 +188,10 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
.enable_mask = BD71828_MASK_RUN_EN,
.vsel_reg = BD71828_REG_BUCK1_VOLT,
.vsel_mask = BD71828_MASK_BUCK1267_VOLT,
.ramp_delay_table = bd71828_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
.ramp_reg = BD71828_REG_BUCK1_MODE,
.ramp_mask = BD71828_MASK_RAMP_DELAY,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},
@ -261,6 +234,10 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
.enable_mask = BD71828_MASK_RUN_EN,
.vsel_reg = BD71828_REG_BUCK2_VOLT,
.vsel_mask = BD71828_MASK_BUCK1267_VOLT,
.ramp_delay_table = bd71828_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
.ramp_reg = BD71828_REG_BUCK2_MODE,
.ramp_mask = BD71828_MASK_RAMP_DELAY,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},
@ -421,6 +398,10 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
.enable_mask = BD71828_MASK_RUN_EN,
.vsel_reg = BD71828_REG_BUCK6_VOLT,
.vsel_mask = BD71828_MASK_BUCK1267_VOLT,
.ramp_delay_table = bd71828_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
.ramp_reg = BD71828_REG_BUCK6_MODE,
.ramp_mask = BD71828_MASK_RAMP_DELAY,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},
@ -458,6 +439,10 @@ static const struct bd71828_regulator_data bd71828_rdata[] = {
.enable_mask = BD71828_MASK_RUN_EN,
.vsel_reg = BD71828_REG_BUCK7_VOLT,
.vsel_mask = BD71828_MASK_BUCK1267_VOLT,
.ramp_delay_table = bd71828_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
.ramp_reg = BD71828_REG_BUCK7_MODE,
.ramp_mask = BD71828_MASK_RAMP_DELAY,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},

View File

@ -86,37 +86,7 @@ static const struct regulator_ops BD718XX_HWOPNAME(name) = { \
* 10: 2.50mV/usec 10mV 4uS
* 11: 1.25mV/usec 10mV 8uS
*/
static int bd718xx_buck1234_set_ramp_delay(struct regulator_dev *rdev,
int ramp_delay)
{
int id = rdev_get_id(rdev);
unsigned int ramp_value;
dev_dbg(&rdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1,
ramp_delay);
switch (ramp_delay) {
case 1 ... 1250:
ramp_value = BUCK_RAMPRATE_1P25MV;
break;
case 1251 ... 2500:
ramp_value = BUCK_RAMPRATE_2P50MV;
break;
case 2501 ... 5000:
ramp_value = BUCK_RAMPRATE_5P00MV;
break;
case 5001 ... 10000:
ramp_value = BUCK_RAMPRATE_10P00MV;
break;
default:
ramp_value = BUCK_RAMPRATE_10P00MV;
dev_err(&rdev->dev,
"%s: ramp_delay: %d not supported, setting 10000mV//us\n",
rdev->desc->name, ramp_delay);
}
return regmap_update_bits(rdev->regmap, BD718XX_REG_BUCK1_CTRL + id,
BUCK_RAMPRATE_MASK, ramp_value << 6);
}
static const unsigned int bd718xx_ramp_delay[] = { 10000, 5000, 2500, 1250 };
/* These functions are used when regulators are under HW state machine control.
* We assume PMIC is in RUN state because SW running and able to query the
@ -378,7 +348,7 @@ static const struct regulator_ops bd71837_buck34_ops_hwctrl = {
.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 = bd718xx_buck1234_set_ramp_delay,
.set_ramp_delay = regulator_set_ramp_delay_regmap,
};
/*
@ -387,7 +357,7 @@ static const struct regulator_ops bd71837_buck34_ops_hwctrl = {
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);
/* bd718xx_buck1234_set_ramp_delay */ regulator_set_ramp_delay_regmap);
/*
* BD71837 BUCK1/2/3/4
@ -645,6 +615,10 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
.enable_mask = BD718XX_BUCK_EN,
.enable_time = BD71847_BUCK1_STARTUP_TIME,
.owner = THIS_MODULE,
.ramp_delay_table = bd718xx_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay),
.ramp_reg = BD718XX_REG_BUCK1_CTRL,
.ramp_mask = BUCK_RAMPRATE_MASK,
.of_parse_cb = buck_set_hw_dvs_levels,
},
.dvs = {
@ -678,6 +652,10 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
.enable_reg = BD718XX_REG_BUCK2_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.enable_time = BD71847_BUCK2_STARTUP_TIME,
.ramp_delay_table = bd718xx_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay),
.ramp_reg = BD718XX_REG_BUCK2_CTRL,
.ramp_mask = BUCK_RAMPRATE_MASK,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},
@ -985,6 +963,10 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
.enable_reg = BD718XX_REG_BUCK1_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.enable_time = BD71837_BUCK1_STARTUP_TIME,
.ramp_delay_table = bd718xx_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay),
.ramp_reg = BD718XX_REG_BUCK1_CTRL,
.ramp_mask = BUCK_RAMPRATE_MASK,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},
@ -1019,6 +1001,10 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
.enable_reg = BD718XX_REG_BUCK2_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.enable_time = BD71837_BUCK2_STARTUP_TIME,
.ramp_delay_table = bd718xx_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay),
.ramp_reg = BD718XX_REG_BUCK2_CTRL,
.ramp_mask = BUCK_RAMPRATE_MASK,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},
@ -1050,6 +1036,10 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
.enable_reg = BD71837_REG_BUCK3_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.enable_time = BD71837_BUCK3_STARTUP_TIME,
.ramp_delay_table = bd718xx_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay),
.ramp_reg = BD71837_REG_BUCK3_CTRL,
.ramp_mask = BUCK_RAMPRATE_MASK,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},
@ -1079,6 +1069,10 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
.enable_reg = BD71837_REG_BUCK4_CTRL,
.enable_mask = BD718XX_BUCK_EN,
.enable_time = BD71837_BUCK4_STARTUP_TIME,
.ramp_delay_table = bd718xx_ramp_delay,
.n_ramp_values = ARRAY_SIZE(bd718xx_ramp_delay),
.ramp_reg = BD71837_REG_BUCK4_CTRL,
.ramp_mask = BUCK_RAMPRATE_MASK,
.owner = THIS_MODULE,
.of_parse_cb = buck_set_hw_dvs_levels,
},

View File

@ -22,13 +22,26 @@ static int set_dvs_level(const struct regulator_desc *desc,
return ret;
return 0;
}
/* If voltage is set to 0 => disable */
if (uv == 0) {
if (omask)
return regmap_update_bits(regmap, oreg, omask, 0);
}
/* Some setups don't allow setting own voltage but do allow enabling */
if (!mask) {
if (omask)
return regmap_update_bits(regmap, oreg, omask, omask);
return -EINVAL;
}
for (i = 0; i < desc->n_voltages; i++) {
/* NOTE to next hacker - Does not support pickable ranges */
if (desc->linear_range_selectors)
return -EINVAL;
if (desc->n_linear_ranges)
ret = regulator_desc_list_voltage_linear_range(desc, i);
else
ret = regulator_desc_list_voltage_linear(desc, i);
if (ret < 0)
continue;
if (ret == uv) {
@ -82,6 +95,12 @@ int rohm_regulator_set_dvs_levels(const struct rohm_dvs_config *dvs,
mask = dvs->lpsr_mask;
omask = dvs->lpsr_on_mask;
break;
case ROHM_DVS_LEVEL_SNVS:
prop = "rohm,dvs-snvs-voltage";
reg = dvs->snvs_reg;
mask = dvs->snvs_mask;
omask = dvs->snvs_on_mask;
break;
default:
return -EINVAL;
}

View File

@ -501,11 +501,11 @@ config RTC_DRV_M41T80_WDT
watchdog timer in the ST M41T60 and M41T80 RTC chips series.
config RTC_DRV_BD70528
tristate "ROHM BD70528 PMIC RTC"
depends on MFD_ROHM_BD70528 && (BD70528_WATCHDOG || !BD70528_WATCHDOG)
tristate "ROHM BD70528, BD71815 and BD71828 PMIC RTC"
depends on MFD_ROHM_BD71828 || MFD_ROHM_BD70528 && (BD70528_WATCHDOG || !BD70528_WATCHDOG)
help
If you say Y here you will get support for the RTC
block on ROHM BD70528 and BD71828 Power Management IC.
block on ROHM BD70528, BD71815 and BD71828 Power Management IC.
This driver can also be built as a module. If so, the module
will be called rtc-bd70528.
@ -1296,6 +1296,14 @@ config RTC_DRV_CROS_EC
This driver can also be built as a module. If so, the module
will be called rtc-cros-ec.
config RTC_DRV_NTXEC
tristate "Netronix embedded controller RTC"
depends on MFD_NTXEC
help
Say yes here if you want to support the RTC functionality of the
embedded controller found in certain e-book readers designed by the
original design manufacturer Netronix.
comment "on-CPU RTC drivers"
config RTC_DRV_ASM9260

View File

@ -108,6 +108,7 @@ obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o

View File

@ -6,6 +6,7 @@
#include <linux/bcd.h>
#include <linux/mfd/rohm-bd70528.h>
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
#include <linux/module.h>
#include <linux/of.h>
@ -13,6 +14,12 @@
#include <linux/regmap.h>
#include <linux/rtc.h>
/*
* On BD71828 and BD71815 the ALM0 MASK is 14 bytes after the ALM0
* block start
*/
#define BD718XX_ALM_EN_OFFSET 14
/*
* We read regs RTC_SEC => RTC_YEAR
* this struct is ordered according to chip registers.
@ -52,8 +59,10 @@ struct bd70528_rtc_alm {
struct bd70528_rtc {
struct rohm_regmap_dev *parent;
struct regmap *regmap;
struct device *dev;
u8 reg_time_start;
u8 bd718xx_alm_block_start;
bool has_rtc_timers;
};
@ -234,10 +243,9 @@ static int bd71828_set_alarm(struct device *dev, struct rtc_wkalrm *a)
int ret;
struct bd71828_rtc_alm alm;
struct bd70528_rtc *r = dev_get_drvdata(dev);
struct rohm_regmap_dev *parent = r->parent;
ret = regmap_bulk_read(parent->regmap, BD71828_REG_RTC_ALM_START,
&alm, sizeof(alm));
ret = regmap_bulk_read(r->regmap, r->bd718xx_alm_block_start, &alm,
sizeof(alm));
if (ret) {
dev_err(dev, "Failed to read alarm regs\n");
return ret;
@ -250,8 +258,8 @@ static int bd71828_set_alarm(struct device *dev, struct rtc_wkalrm *a)
else
alm.alm_mask |= BD70528_MASK_ALM_EN;
ret = regmap_bulk_write(parent->regmap, BD71828_REG_RTC_ALM_START,
&alm, sizeof(alm));
ret = regmap_bulk_write(r->regmap, r->bd718xx_alm_block_start, &alm,
sizeof(alm));
if (ret)
dev_err(dev, "Failed to set alarm time\n");
@ -265,17 +273,16 @@ static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
struct bd70528_rtc_alm alm;
int ret;
struct bd70528_rtc *r = dev_get_drvdata(dev);
struct rohm_regmap_dev *parent = r->parent;
ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_WAKE_START,
&wake, sizeof(wake));
ret = regmap_bulk_read(r->regmap, BD70528_REG_RTC_WAKE_START, &wake,
sizeof(wake));
if (ret) {
dev_err(dev, "Failed to read wake regs\n");
return ret;
}
ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_ALM_START,
&alm, sizeof(alm));
ret = regmap_bulk_read(r->regmap, BD70528_REG_RTC_ALM_START, &alm,
sizeof(alm));
if (ret) {
dev_err(dev, "Failed to read alarm regs\n");
return ret;
@ -292,15 +299,14 @@ static int bd70528_set_alarm(struct device *dev, struct rtc_wkalrm *a)
wake.ctrl &= ~BD70528_MASK_WAKE_EN;
}
ret = regmap_bulk_write(parent->regmap,
BD70528_REG_RTC_WAKE_START, &wake,
ret = regmap_bulk_write(r->regmap, BD70528_REG_RTC_WAKE_START, &wake,
sizeof(wake));
if (ret) {
dev_err(dev, "Failed to set wake time\n");
return ret;
}
ret = regmap_bulk_write(parent->regmap, BD70528_REG_RTC_ALM_START,
&alm, sizeof(alm));
ret = regmap_bulk_write(r->regmap, BD70528_REG_RTC_ALM_START, &alm,
sizeof(alm));
if (ret)
dev_err(dev, "Failed to set alarm time\n");
@ -312,10 +318,9 @@ static int bd71828_read_alarm(struct device *dev, struct rtc_wkalrm *a)
int ret;
struct bd71828_rtc_alm alm;
struct bd70528_rtc *r = dev_get_drvdata(dev);
struct rohm_regmap_dev *parent = r->parent;
ret = regmap_bulk_read(parent->regmap, BD71828_REG_RTC_ALM_START,
&alm, sizeof(alm));
ret = regmap_bulk_read(r->regmap, r->bd718xx_alm_block_start, &alm,
sizeof(alm));
if (ret) {
dev_err(dev, "Failed to read alarm regs\n");
return ret;
@ -336,10 +341,9 @@ static int bd70528_read_alarm(struct device *dev, struct rtc_wkalrm *a)
struct bd70528_rtc_alm alm;
int ret;
struct bd70528_rtc *r = dev_get_drvdata(dev);
struct rohm_regmap_dev *parent = r->parent;
ret = regmap_bulk_read(parent->regmap, BD70528_REG_RTC_ALM_START,
&alm, sizeof(alm));
ret = regmap_bulk_read(r->regmap, BD70528_REG_RTC_ALM_START, &alm,
sizeof(alm));
if (ret) {
dev_err(dev, "Failed to read alarm regs\n");
return ret;
@ -360,14 +364,12 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
int ret, tmpret, old_states;
struct bd70528_rtc_data rtc_data;
struct bd70528_rtc *r = dev_get_drvdata(dev);
struct rohm_regmap_dev *parent = r->parent;
ret = bd70528_disable_rtc_based_timers(r, &old_states);
if (ret)
return ret;
tmpret = regmap_bulk_read(parent->regmap,
r->reg_time_start, &rtc_data,
tmpret = regmap_bulk_read(r->regmap, r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (tmpret) {
dev_err(dev, "Failed to read RTC time registers\n");
@ -375,8 +377,7 @@ static int bd70528_set_time_locked(struct device *dev, struct rtc_time *t)
}
tm2rtc(t, &rtc_data);
tmpret = regmap_bulk_write(parent->regmap,
r->reg_time_start, &rtc_data,
tmpret = regmap_bulk_write(r->regmap, r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (tmpret) {
dev_err(dev, "Failed to set RTC time\n");
@ -410,13 +411,11 @@ static int bd70528_set_time(struct device *dev, struct rtc_time *t)
static int bd70528_get_time(struct device *dev, struct rtc_time *t)
{
struct bd70528_rtc *r = dev_get_drvdata(dev);
struct rohm_regmap_dev *parent = r->parent;
struct bd70528_rtc_data rtc_data;
int ret;
/* read the RTC date and time registers all at once */
ret = regmap_bulk_read(parent->regmap,
r->reg_time_start, &rtc_data,
ret = regmap_bulk_read(r->regmap, r->reg_time_start, &rtc_data,
sizeof(rtc_data));
if (ret) {
dev_err(dev, "Failed to read RTC time (err %d)\n", ret);
@ -443,7 +442,7 @@ static int bd70528_alm_enable(struct device *dev, unsigned int enabled)
dev_err(dev, "Failed to change wake state\n");
goto out_unlock;
}
ret = regmap_update_bits(r->parent->regmap, BD70528_REG_RTC_ALM_MASK,
ret = regmap_update_bits(r->regmap, BD70528_REG_RTC_ALM_MASK,
BD70528_MASK_ALM_EN, enableval);
if (ret)
dev_err(dev, "Failed to change alarm state\n");
@ -462,8 +461,9 @@ static int bd71828_alm_enable(struct device *dev, unsigned int enabled)
if (!enabled)
enableval = 0;
ret = regmap_update_bits(r->parent->regmap, BD71828_REG_RTC_ALM0_MASK,
BD70528_MASK_ALM_EN, enableval);
ret = regmap_update_bits(r->regmap, r->bd718xx_alm_block_start +
BD718XX_ALM_EN_OFFSET, BD70528_MASK_ALM_EN,
enableval);
if (ret)
dev_err(dev, "Failed to change alarm state\n");
@ -498,7 +498,6 @@ static int bd70528_probe(struct platform_device *pdev)
{
struct bd70528_rtc *bd_rtc;
const struct rtc_class_ops *rtc_ops;
struct rohm_regmap_dev *parent;
const char *irq_name;
int ret;
struct rtc_device *rtc;
@ -508,20 +507,25 @@ static int bd70528_probe(struct platform_device *pdev)
u8 hour_reg;
enum rohm_chip_type chip = platform_get_device_id(pdev)->driver_data;
parent = dev_get_drvdata(pdev->dev.parent);
if (!parent) {
dev_err(&pdev->dev, "No MFD driver data\n");
return -EINVAL;
}
bd_rtc = devm_kzalloc(&pdev->dev, sizeof(*bd_rtc), GFP_KERNEL);
if (!bd_rtc)
return -ENOMEM;
bd_rtc->parent = parent;
bd_rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!bd_rtc->regmap) {
dev_err(&pdev->dev, "No regmap\n");
return -EINVAL;
}
bd_rtc->dev = &pdev->dev;
switch (chip) {
case ROHM_CHIP_TYPE_BD70528:
bd_rtc->parent = dev_get_drvdata(pdev->dev.parent);
if (!bd_rtc->parent) {
dev_err(&pdev->dev, "No MFD data\n");
return -EINVAL;
}
irq_name = "bd70528-rtc-alm";
bd_rtc->has_rtc_timers = true;
bd_rtc->reg_time_start = BD70528_REG_RTC_START;
@ -529,9 +533,28 @@ static int bd70528_probe(struct platform_device *pdev)
enable_main_irq = true;
rtc_ops = &bd70528_rtc_ops;
break;
case ROHM_CHIP_TYPE_BD71815:
irq_name = "bd71815-rtc-alm-0";
bd_rtc->reg_time_start = BD71815_REG_RTC_START;
/*
* See also BD718XX_ALM_EN_OFFSET:
* This works for BD71828 and BD71815 as they have same offset
* between ALM0 start and ALM0_MASK. If new ICs are to be
* added this requires proper check as ALM0_MASK is not located
* at the end of ALM0 block - but after all ALM blocks so if
* amount of ALMs differ the offset to enable/disable is likely
* to be incorrect and enable/disable must be given as own
* reg address here.
*/
bd_rtc->bd718xx_alm_block_start = BD71815_REG_RTC_ALM_START;
hour_reg = BD71815_REG_HOUR;
rtc_ops = &bd71828_rtc_ops;
break;
case ROHM_CHIP_TYPE_BD71828:
irq_name = "bd71828-rtc-alm-0";
bd_rtc->reg_time_start = BD71828_REG_RTC_START;
bd_rtc->bd718xx_alm_block_start = BD71828_REG_RTC_ALM_START;
hour_reg = BD71828_REG_RTC_HOUR;
rtc_ops = &bd71828_rtc_ops;
break;
@ -547,7 +570,7 @@ static int bd70528_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bd_rtc);
ret = regmap_read(parent->regmap, hour_reg, &hr);
ret = regmap_read(bd_rtc->regmap, hour_reg, &hr);
if (ret) {
dev_err(&pdev->dev, "Failed to reag RTC clock\n");
@ -595,7 +618,7 @@ static int bd70528_probe(struct platform_device *pdev)
* from sub-registers when IRQ is disabled or freed.
*/
if (enable_main_irq) {
ret = regmap_update_bits(parent->regmap,
ret = regmap_update_bits(bd_rtc->regmap,
BD70528_REG_INT_MAIN_MASK,
BD70528_INT_RTC_MASK, 0);
if (ret) {
@ -610,6 +633,7 @@ static int bd70528_probe(struct platform_device *pdev)
static const struct platform_device_id bd718x7_rtc_id[] = {
{ "bd70528-rtc", ROHM_CHIP_TYPE_BD70528 },
{ "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 },
{ "bd71815-rtc", ROHM_CHIP_TYPE_BD71815 },
{ },
};
MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id);

145
drivers/rtc/rtc-ntxec.c Normal file
View File

@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The Netronix embedded controller is a microcontroller found in some
* e-book readers designed by the original design manufacturer Netronix, Inc.
* It contains RTC, battery monitoring, system power management, and PWM
* functionality.
*
* This driver implements access to the RTC time and date.
*
* Copyright 2020 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
*/
#include <linux/mfd/ntxec.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
#include <linux/types.h>
struct ntxec_rtc {
struct device *dev;
struct ntxec *ec;
};
#define NTXEC_REG_WRITE_YEAR 0x10
#define NTXEC_REG_WRITE_MONTH 0x11
#define NTXEC_REG_WRITE_DAY 0x12
#define NTXEC_REG_WRITE_HOUR 0x13
#define NTXEC_REG_WRITE_MINUTE 0x14
#define NTXEC_REG_WRITE_SECOND 0x15
#define NTXEC_REG_READ_YEAR_MONTH 0x20
#define NTXEC_REG_READ_MDAY_HOUR 0x21
#define NTXEC_REG_READ_MINUTE_SECOND 0x23
static int ntxec_read_time(struct device *dev, struct rtc_time *tm)
{
struct ntxec_rtc *rtc = dev_get_drvdata(dev);
unsigned int value;
int res;
retry:
res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MINUTE_SECOND, &value);
if (res < 0)
return res;
tm->tm_min = value >> 8;
tm->tm_sec = value & 0xff;
res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MDAY_HOUR, &value);
if (res < 0)
return res;
tm->tm_mday = value >> 8;
tm->tm_hour = value & 0xff;
res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_YEAR_MONTH, &value);
if (res < 0)
return res;
tm->tm_year = (value >> 8) + 100;
tm->tm_mon = (value & 0xff) - 1;
/*
* Read the minutes/seconds field again. If it changed since the first
* read, we can't assume that the values read so far are consistent,
* and should start from the beginning.
*/
res = regmap_read(rtc->ec->regmap, NTXEC_REG_READ_MINUTE_SECOND, &value);
if (res < 0)
return res;
if (tm->tm_min != value >> 8 || tm->tm_sec != (value & 0xff))
goto retry;
return 0;
}
static int ntxec_set_time(struct device *dev, struct rtc_time *tm)
{
struct ntxec_rtc *rtc = dev_get_drvdata(dev);
/*
* To avoid time overflows while we're writing the full date/time,
* set the seconds field to zero before doing anything else. For the
* next 59 seconds (plus however long it takes until the RTC's next
* update of the second field), the seconds field will not overflow
* into the other fields.
*/
struct reg_sequence regs[] = {
{ NTXEC_REG_WRITE_SECOND, ntxec_reg8(0) },
{ NTXEC_REG_WRITE_YEAR, ntxec_reg8(tm->tm_year - 100) },
{ NTXEC_REG_WRITE_MONTH, ntxec_reg8(tm->tm_mon + 1) },
{ NTXEC_REG_WRITE_DAY, ntxec_reg8(tm->tm_mday) },
{ NTXEC_REG_WRITE_HOUR, ntxec_reg8(tm->tm_hour) },
{ NTXEC_REG_WRITE_MINUTE, ntxec_reg8(tm->tm_min) },
{ NTXEC_REG_WRITE_SECOND, ntxec_reg8(tm->tm_sec) },
};
return regmap_multi_reg_write(rtc->ec->regmap, regs, ARRAY_SIZE(regs));
}
static const struct rtc_class_ops ntxec_rtc_ops = {
.read_time = ntxec_read_time,
.set_time = ntxec_set_time,
};
static int ntxec_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *dev;
struct ntxec_rtc *rtc;
pdev->dev.of_node = pdev->dev.parent->of_node;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
rtc->dev = &pdev->dev;
rtc->ec = dev_get_drvdata(pdev->dev.parent);
platform_set_drvdata(pdev, rtc);
dev = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(dev))
return PTR_ERR(dev);
dev->ops = &ntxec_rtc_ops;
dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
dev->range_max = 9025257599LL; /* 2255-12-31 23:59:59 */
return devm_rtc_register_device(dev);
}
static struct platform_driver ntxec_rtc_driver = {
.driver = {
.name = "ntxec-rtc",
},
.probe = ntxec_rtc_probe,
};
module_platform_driver(ntxec_rtc_driver);
MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>");
MODULE_DESCRIPTION("RTC driver for Netronix EC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ntxec-rtc");

View File

@ -172,6 +172,19 @@ config BD70528_WATCHDOG
Alternatively say M to compile the driver as a module,
which will be called bd70528_wdt.
config BD957XMUF_WATCHDOG
tristate "ROHM BD9576MUF and BD9573MUF PMIC Watchdog"
depends on MFD_ROHM_BD957XMUF
select WATCHDOG_CORE
help
Support for the watchdog in the ROHM BD9576 and BD9573 PMICs.
These PMIC ICs contain watchdog block which can be configured
to toggle reset line if SoC fails to ping watchdog via GPIO.
Say Y here to include support for the ROHM BD9576 or BD9573
watchdog. Alternatively say M to compile the driver as a module,
which will be called bd9576_wdt.
config DA9052_WATCHDOG
tristate "Dialog DA9052 Watchdog"
depends on PMIC_DA9052 || COMPILE_TEST

View File

@ -204,6 +204,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o
# Architecture Independent
obj-$(CONFIG_BD70528_WATCHDOG) += bd70528_wdt.o
obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o
obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o

View File

@ -0,0 +1,291 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2020 ROHM Semiconductors
*
* ROHM BD9576MUF and BD9573MUF Watchdog driver
*/
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/mfd/rohm-bd957x.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/watchdog.h>
static bool nowayout;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default=\"false\")");
#define HW_MARGIN_MIN 2
#define HW_MARGIN_MAX 4416
#define BD957X_WDT_DEFAULT_MARGIN 4416
#define WATCHDOG_TIMEOUT 30
struct bd9576_wdt_priv {
struct gpio_desc *gpiod_ping;
struct gpio_desc *gpiod_en;
struct device *dev;
struct regmap *regmap;
bool always_running;
struct watchdog_device wdd;
};
static void bd9576_wdt_disable(struct bd9576_wdt_priv *priv)
{
gpiod_set_value_cansleep(priv->gpiod_en, 0);
}
static int bd9576_wdt_ping(struct watchdog_device *wdd)
{
struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd);
/* Pulse */
gpiod_set_value_cansleep(priv->gpiod_ping, 1);
gpiod_set_value_cansleep(priv->gpiod_ping, 0);
return 0;
}
static int bd9576_wdt_start(struct watchdog_device *wdd)
{
struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd);
gpiod_set_value_cansleep(priv->gpiod_en, 1);
return bd9576_wdt_ping(wdd);
}
static int bd9576_wdt_stop(struct watchdog_device *wdd)
{
struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd);
if (!priv->always_running)
bd9576_wdt_disable(priv);
else
set_bit(WDOG_HW_RUNNING, &wdd->status);
return 0;
}
static const struct watchdog_info bd957x_wdt_ident = {
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT,
.identity = "BD957x Watchdog",
};
static const struct watchdog_ops bd957x_wdt_ops = {
.owner = THIS_MODULE,
.start = bd9576_wdt_start,
.stop = bd9576_wdt_stop,
.ping = bd9576_wdt_ping,
};
/* Unit is hundreds of uS */
#define FASTNG_MIN 23
static int find_closest_fast(int target, int *sel, int *val)
{
int i;
int window = FASTNG_MIN;
for (i = 0; i < 8 && window < target; i++)
window <<= 1;
*val = window;
*sel = i;
if (i == 8)
return -EINVAL;
return 0;
}
static int find_closest_slow_by_fast(int fast_val, int target, int *slowsel)
{
int sel;
static const int multipliers[] = {2, 3, 7, 15};
for (sel = 0; sel < ARRAY_SIZE(multipliers) &&
multipliers[sel] * fast_val < target; sel++)
;
if (sel == ARRAY_SIZE(multipliers))
return -EINVAL;
*slowsel = sel;
return 0;
}
static int find_closest_slow(int target, int *slow_sel, int *fast_sel)
{
static const int multipliers[] = {2, 3, 7, 15};
int i, j;
int val = 0;
int window = FASTNG_MIN;
for (i = 0; i < 8; i++) {
for (j = 0; j < ARRAY_SIZE(multipliers); j++) {
int slow;
slow = window * multipliers[j];
if (slow >= target && (!val || slow < val)) {
val = slow;
*fast_sel = i;
*slow_sel = j;
}
}
window <<= 1;
}
if (!val)
return -EINVAL;
return 0;
}
#define BD957X_WDG_TYPE_WINDOW BIT(5)
#define BD957X_WDG_TYPE_SLOW 0
#define BD957X_WDG_TYPE_MASK BIT(5)
#define BD957X_WDG_NG_RATIO_MASK 0x18
#define BD957X_WDG_FASTNG_MASK 0x7
static int bd957x_set_wdt_mode(struct bd9576_wdt_priv *priv, int hw_margin,
int hw_margin_min)
{
int ret, fastng, slowng, type, reg, mask;
struct device *dev = priv->dev;
/* convert to 100uS */
hw_margin *= 10;
hw_margin_min *= 10;
if (hw_margin_min) {
int min;
type = BD957X_WDG_TYPE_WINDOW;
dev_dbg(dev, "Setting type WINDOW 0x%x\n", type);
ret = find_closest_fast(hw_margin_min, &fastng, &min);
if (ret) {
dev_err(dev, "bad WDT window for fast timeout\n");
return ret;
}
ret = find_closest_slow_by_fast(min, hw_margin, &slowng);
if (ret) {
dev_err(dev, "bad WDT window\n");
return ret;
}
} else {
type = BD957X_WDG_TYPE_SLOW;
dev_dbg(dev, "Setting type SLOW 0x%x\n", type);
ret = find_closest_slow(hw_margin, &slowng, &fastng);
if (ret) {
dev_err(dev, "bad WDT window\n");
return ret;
}
}
slowng <<= ffs(BD957X_WDG_NG_RATIO_MASK) - 1;
reg = type | slowng | fastng;
mask = BD957X_WDG_TYPE_MASK | BD957X_WDG_NG_RATIO_MASK |
BD957X_WDG_FASTNG_MASK;
ret = regmap_update_bits(priv->regmap, BD957X_REG_WDT_CONF,
mask, reg);
return ret;
}
static int bd9576_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->parent->of_node;
struct bd9576_wdt_priv *priv;
u32 hw_margin[2];
u32 hw_margin_max = BD957X_WDT_DEFAULT_MARGIN, hw_margin_min = 0;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
platform_set_drvdata(pdev, priv);
priv->dev = dev;
priv->regmap = dev_get_regmap(dev->parent, NULL);
if (!priv->regmap) {
dev_err(dev, "No regmap found\n");
return -ENODEV;
}
priv->gpiod_en = devm_gpiod_get_from_of_node(dev, dev->parent->of_node,
"rohm,watchdog-enable-gpios",
0, GPIOD_OUT_LOW,
"watchdog-enable");
if (IS_ERR(priv->gpiod_en))
return dev_err_probe(dev, PTR_ERR(priv->gpiod_en),
"getting watchdog-enable GPIO failed\n");
priv->gpiod_ping = devm_gpiod_get_from_of_node(dev, dev->parent->of_node,
"rohm,watchdog-ping-gpios",
0, GPIOD_OUT_LOW,
"watchdog-ping");
if (IS_ERR(priv->gpiod_ping))
return dev_err_probe(dev, PTR_ERR(priv->gpiod_ping),
"getting watchdog-ping GPIO failed\n");
ret = of_property_read_variable_u32_array(np, "rohm,hw-timeout-ms",
&hw_margin[0], 1, 2);
if (ret < 0 && ret != -EINVAL)
return ret;
if (ret == 1)
hw_margin_max = hw_margin[0];
if (ret == 2) {
hw_margin_max = hw_margin[1];
hw_margin_min = hw_margin[0];
}
ret = bd957x_set_wdt_mode(priv, hw_margin_max, hw_margin_min);
if (ret)
return ret;
priv->always_running = of_property_read_bool(np, "always-running");
watchdog_set_drvdata(&priv->wdd, priv);
priv->wdd.info = &bd957x_wdt_ident;
priv->wdd.ops = &bd957x_wdt_ops;
priv->wdd.min_hw_heartbeat_ms = hw_margin_min;
priv->wdd.max_hw_heartbeat_ms = hw_margin_max;
priv->wdd.parent = dev;
priv->wdd.timeout = WATCHDOG_TIMEOUT;
watchdog_init_timeout(&priv->wdd, 0, dev);
watchdog_set_nowayout(&priv->wdd, nowayout);
watchdog_stop_on_reboot(&priv->wdd);
if (priv->always_running)
bd9576_wdt_start(&priv->wdd);
return devm_watchdog_register_device(dev, &priv->wdd);
}
static struct platform_driver bd9576_wdt_driver = {
.driver = {
.name = "bd9576-wdt",
},
.probe = bd9576_wdt_probe,
};
module_platform_driver(bd9576_wdt_driver);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("ROHM BD9576/BD9573 Watchdog driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:bd9576-wdt");

View File

@ -1,128 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2007-2009 ST-Ericsson AB
* AB3100 core access functions
* Author: Linus Walleij <linus.walleij@stericsson.com>
*/
#include <linux/regulator/machine.h>
struct device;
#ifndef MFD_AB3100_H
#define MFD_AB3100_H
#define AB3100_P1A 0xc0
#define AB3100_P1B 0xc1
#define AB3100_P1C 0xc2
#define AB3100_P1D 0xc3
#define AB3100_P1E 0xc4
#define AB3100_P1F 0xc5
#define AB3100_P1G 0xc6
#define AB3100_R2A 0xc7
#define AB3100_R2B 0xc8
/*
* AB3100, EVENTA1, A2 and A3 event register flags
* these are catenated into a single 32-bit flag in the code
* for event notification broadcasts.
*/
#define AB3100_EVENTA1_ONSWA (0x01<<16)
#define AB3100_EVENTA1_ONSWB (0x02<<16)
#define AB3100_EVENTA1_ONSWC (0x04<<16)
#define AB3100_EVENTA1_DCIO (0x08<<16)
#define AB3100_EVENTA1_OVER_TEMP (0x10<<16)
#define AB3100_EVENTA1_SIM_OFF (0x20<<16)
#define AB3100_EVENTA1_VBUS (0x40<<16)
#define AB3100_EVENTA1_VSET_USB (0x80<<16)
#define AB3100_EVENTA2_READY_TX (0x01<<8)
#define AB3100_EVENTA2_READY_RX (0x02<<8)
#define AB3100_EVENTA2_OVERRUN_ERROR (0x04<<8)
#define AB3100_EVENTA2_FRAMING_ERROR (0x08<<8)
#define AB3100_EVENTA2_CHARG_OVERCURRENT (0x10<<8)
#define AB3100_EVENTA2_MIDR (0x20<<8)
#define AB3100_EVENTA2_BATTERY_REM (0x40<<8)
#define AB3100_EVENTA2_ALARM (0x80<<8)
#define AB3100_EVENTA3_ADC_TRIG5 (0x01)
#define AB3100_EVENTA3_ADC_TRIG4 (0x02)
#define AB3100_EVENTA3_ADC_TRIG3 (0x04)
#define AB3100_EVENTA3_ADC_TRIG2 (0x08)
#define AB3100_EVENTA3_ADC_TRIGVBAT (0x10)
#define AB3100_EVENTA3_ADC_TRIGVTX (0x20)
#define AB3100_EVENTA3_ADC_TRIG1 (0x40)
#define AB3100_EVENTA3_ADC_TRIG0 (0x80)
/* AB3100, STR register flags */
#define AB3100_STR_ONSWA (0x01)
#define AB3100_STR_ONSWB (0x02)
#define AB3100_STR_ONSWC (0x04)
#define AB3100_STR_DCIO (0x08)
#define AB3100_STR_BOOT_MODE (0x10)
#define AB3100_STR_SIM_OFF (0x20)
#define AB3100_STR_BATT_REMOVAL (0x40)
#define AB3100_STR_VBUS (0x80)
/*
* AB3100 contains 8 regulators, one external regulator controller
* and a buck converter, further the LDO E and buck converter can
* have separate settings if they are in sleep mode, this is
* modeled as a separate regulator.
*/
#define AB3100_NUM_REGULATORS 10
/**
* struct ab3100
* @access_mutex: lock out concurrent accesses to the AB3100 registers
* @dev: pointer to the containing device
* @i2c_client: I2C client for this chip
* @testreg_client: secondary client for test registers
* @chip_name: name of this chip variant
* @chip_id: 8 bit chip ID for this chip variant
* @event_subscribers: event subscribers are listed here
* @startup_events: a copy of the first reading of the event registers
* @startup_events_read: whether the first events have been read
*
* This struct is PRIVATE and devices using it should NOT
* access ANY fields. It is used as a token for calling the
* AB3100 functions.
*/
struct ab3100 {
struct mutex access_mutex;
struct device *dev;
struct i2c_client *i2c_client;
struct i2c_client *testreg_client;
char chip_name[32];
u8 chip_id;
struct blocking_notifier_head event_subscribers;
u8 startup_events[3];
bool startup_events_read;
};
/**
* struct ab3100_platform_data
* Data supplied to initialize board connections to the AB3100
* @reg_constraints: regulator constraints for target board
* the order of these constraints are: LDO A, C, D, E,
* F, G, H, K, EXT and BUCK.
* @reg_initvals: initial values for the regulator registers
* plus two sleep settings for LDO E and the BUCK converter.
* exactly AB3100_NUM_REGULATORS+2 values must be sent in.
* Order: LDO A, C, E, E sleep, F, G, H, K, EXT, BUCK,
* BUCK sleep, LDO D. (LDO D need to be initialized last.)
* @external_voltage: voltage level of the external regulator.
*/
struct ab3100_platform_data {
struct regulator_init_data reg_constraints[AB3100_NUM_REGULATORS];
u8 reg_initvals[AB3100_NUM_REGULATORS+2];
int external_voltage;
};
int ab3100_event_register(struct ab3100 *ab3100,
struct notifier_block *nb);
int ab3100_event_unregister(struct ab3100 *ab3100,
struct notifier_block *nb);
#endif /* MFD_AB3100_H */

View File

@ -0,0 +1,281 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* ATC2603C PMIC register definitions
*
* Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
*/
#ifndef __LINUX_MFD_ATC260X_ATC2603C_H
#define __LINUX_MFD_ATC260X_ATC2603C_H
enum atc2603c_irq_def {
ATC2603C_IRQ_AUDIO = 0,
ATC2603C_IRQ_OV,
ATC2603C_IRQ_OC,
ATC2603C_IRQ_OT,
ATC2603C_IRQ_UV,
ATC2603C_IRQ_ALARM,
ATC2603C_IRQ_ONOFF,
ATC2603C_IRQ_SGPIO,
ATC2603C_IRQ_IR,
ATC2603C_IRQ_REMCON,
ATC2603C_IRQ_POWER_IN,
};
/* PMU Registers */
#define ATC2603C_PMU_SYS_CTL0 0x00
#define ATC2603C_PMU_SYS_CTL1 0x01
#define ATC2603C_PMU_SYS_CTL2 0x02
#define ATC2603C_PMU_SYS_CTL3 0x03
#define ATC2603C_PMU_SYS_CTL4 0x04
#define ATC2603C_PMU_SYS_CTL5 0x05
#define ATC2603C_PMU_SYS_CTL6 0x06
#define ATC2603C_PMU_SYS_CTL7 0x07
#define ATC2603C_PMU_SYS_CTL8 0x08
#define ATC2603C_PMU_SYS_CTL9 0x09
#define ATC2603C_PMU_BAT_CTL0 0x0A
#define ATC2603C_PMU_BAT_CTL1 0x0B
#define ATC2603C_PMU_VBUS_CTL0 0x0C
#define ATC2603C_PMU_VBUS_CTL1 0x0D
#define ATC2603C_PMU_WALL_CTL0 0x0E
#define ATC2603C_PMU_WALL_CTL1 0x0F
#define ATC2603C_PMU_SYS_PENDING 0x10
#define ATC2603C_PMU_DC1_CTL0 0x11
#define ATC2603C_PMU_DC1_CTL1 0x12 // Undocumented
#define ATC2603C_PMU_DC1_CTL2 0x13 // Undocumented
#define ATC2603C_PMU_DC2_CTL0 0x14
#define ATC2603C_PMU_DC2_CTL1 0x15 // Undocumented
#define ATC2603C_PMU_DC2_CTL2 0x16 // Undocumented
#define ATC2603C_PMU_DC3_CTL0 0x17
#define ATC2603C_PMU_DC3_CTL1 0x18 // Undocumented
#define ATC2603C_PMU_DC3_CTL2 0x19 // Undocumented
#define ATC2603C_PMU_DC4_CTL0 0x1A // Undocumented
#define ATC2603C_PMU_DC4_CTL1 0x1B // Undocumented
#define ATC2603C_PMU_DC5_CTL0 0x1C // Undocumented
#define ATC2603C_PMU_DC5_CTL1 0x1D // Undocumented
#define ATC2603C_PMU_LDO1_CTL 0x1E
#define ATC2603C_PMU_LDO2_CTL 0x1F
#define ATC2603C_PMU_LDO3_CTL 0x20
#define ATC2603C_PMU_LDO4_CTL 0x21 // Undocumented
#define ATC2603C_PMU_LDO5_CTL 0x22
#define ATC2603C_PMU_LDO6_CTL 0x23
#define ATC2603C_PMU_LDO7_CTL 0x24
#define ATC2603C_PMU_LDO8_CTL 0x25 // Undocumented
#define ATC2603C_PMU_LDO9_CTL 0x26 // Undocumented
#define ATC2603C_PMU_LDO10_CTL 0x27 // Undocumented
#define ATC2603C_PMU_LDO11_CTL 0x28
#define ATC2603C_PMU_SWITCH_CTL 0x29
#define ATC2603C_PMU_OV_CTL0 0x2A
#define ATC2603C_PMU_OV_CTL1 0x2B
#define ATC2603C_PMU_OV_STATUS 0x2C
#define ATC2603C_PMU_OV_EN 0x2D
#define ATC2603C_PMU_OV_INT_EN 0x2E
#define ATC2603C_PMU_OC_CTL 0x2F
#define ATC2603C_PMU_OC_STATUS 0x30
#define ATC2603C_PMU_OC_EN 0x31
#define ATC2603C_PMU_OC_INT_EN 0x32
#define ATC2603C_PMU_UV_CTL0 0x33
#define ATC2603C_PMU_UV_CTL1 0x34
#define ATC2603C_PMU_UV_STATUS 0x35
#define ATC2603C_PMU_UV_EN 0x36
#define ATC2603C_PMU_UV_INT_EN 0x37
#define ATC2603C_PMU_OT_CTL 0x38
#define ATC2603C_PMU_CHARGER_CTL0 0x39
#define ATC2603C_PMU_CHARGER_CTL1 0x3A
#define ATC2603C_PMU_CHARGER_CTL2 0x3B
#define ATC2603C_PMU_BAKCHARGER_CTL 0x3C // Undocumented
#define ATC2603C_PMU_APDS_CTL 0x3D
#define ATC2603C_PMU_AUXADC_CTL0 0x3E
#define ATC2603C_PMU_AUXADC_CTL1 0x3F
#define ATC2603C_PMU_BATVADC 0x40
#define ATC2603C_PMU_BATIADC 0x41
#define ATC2603C_PMU_WALLVADC 0x42
#define ATC2603C_PMU_WALLIADC 0x43
#define ATC2603C_PMU_VBUSVADC 0x44
#define ATC2603C_PMU_VBUSIADC 0x45
#define ATC2603C_PMU_SYSPWRADC 0x46
#define ATC2603C_PMU_REMCONADC 0x47
#define ATC2603C_PMU_SVCCADC 0x48
#define ATC2603C_PMU_CHGIADC 0x49
#define ATC2603C_PMU_IREFADC 0x4A
#define ATC2603C_PMU_BAKBATADC 0x4B
#define ATC2603C_PMU_ICTEMPADC 0x4C
#define ATC2603C_PMU_AUXADC0 0x4D
#define ATC2603C_PMU_AUXADC1 0x4E
#define ATC2603C_PMU_AUXADC2 0x4F
#define ATC2603C_PMU_ICMADC 0x50
#define ATC2603C_PMU_BDG_CTL 0x51 // Undocumented
#define ATC2603C_RTC_CTL 0x52
#define ATC2603C_RTC_MSALM 0x53
#define ATC2603C_RTC_HALM 0x54
#define ATC2603C_RTC_YMDALM 0x55
#define ATC2603C_RTC_MS 0x56
#define ATC2603C_RTC_H 0x57
#define ATC2603C_RTC_DC 0x58
#define ATC2603C_RTC_YMD 0x59
#define ATC2603C_EFUSE_DAT 0x5A // Undocumented
#define ATC2603C_EFUSECRTL1 0x5B // Undocumented
#define ATC2603C_EFUSECRTL2 0x5C // Undocumented
#define ATC2603C_PMU_FW_USE0 0x5D // Undocumented
#define ATC2603C_PMU_FW_USE1 0x5E // Undocumented
#define ATC2603C_PMU_FW_USE2 0x5F // Undocumented
#define ATC2603C_PMU_FW_USE3 0x60 // Undocumented
#define ATC2603C_PMU_FW_USE4 0x61 // Undocumented
#define ATC2603C_PMU_ABNORMAL_STATUS 0x62
#define ATC2603C_PMU_WALL_APDS_CTL 0x63
#define ATC2603C_PMU_REMCON_CTL0 0x64
#define ATC2603C_PMU_REMCON_CTL1 0x65
#define ATC2603C_PMU_MUX_CTL0 0x66
#define ATC2603C_PMU_SGPIO_CTL0 0x67
#define ATC2603C_PMU_SGPIO_CTL1 0x68
#define ATC2603C_PMU_SGPIO_CTL2 0x69
#define ATC2603C_PMU_SGPIO_CTL3 0x6A
#define ATC2603C_PMU_SGPIO_CTL4 0x6B
#define ATC2603C_PWMCLK_CTL 0x6C
#define ATC2603C_PWM0_CTL 0x6D
#define ATC2603C_PWM1_CTL 0x6E
#define ATC2603C_PMU_ADC_DBG0 0x70
#define ATC2603C_PMU_ADC_DBG1 0x71
#define ATC2603C_PMU_ADC_DBG2 0x72
#define ATC2603C_PMU_ADC_DBG3 0x73
#define ATC2603C_PMU_ADC_DBG4 0x74
#define ATC2603C_IRC_CTL 0x80
#define ATC2603C_IRC_STAT 0x81
#define ATC2603C_IRC_CC 0x82
#define ATC2603C_IRC_KDC 0x83
#define ATC2603C_IRC_WK 0x84
#define ATC2603C_IRC_RCC 0x85
#define ATC2603C_IRC_FILTER 0x86
/* AUDIO_OUT Registers */
#define ATC2603C_AUDIOINOUT_CTL 0xA0
#define ATC2603C_AUDIO_DEBUGOUTCTL 0xA1
#define ATC2603C_DAC_DIGITALCTL 0xA2
#define ATC2603C_DAC_VOLUMECTL0 0xA3
#define ATC2603C_DAC_ANALOG0 0xA4
#define ATC2603C_DAC_ANALOG1 0xA5
#define ATC2603C_DAC_ANALOG2 0xA6
#define ATC2603C_DAC_ANALOG3 0xA7
/* AUDIO_IN Registers */
#define ATC2603C_ADC_DIGITALCTL 0xA8
#define ATC2603C_ADC_HPFCTL 0xA9
#define ATC2603C_ADC_CTL 0xAA
#define ATC2603C_AGC_CTL0 0xAB
#define ATC2603C_AGC_CTL1 0xAC // Undocumented
#define ATC2603C_AGC_CTL2 0xAD
#define ATC2603C_ADC_ANALOG0 0xAE
#define ATC2603C_ADC_ANALOG1 0xAF
/* PCM_IF Registers */
#define ATC2603C_PCM0_CTL 0xB0 // Undocumented
#define ATC2603C_PCM1_CTL 0xB1 // Undocumented
#define ATC2603C_PCM2_CTL 0xB2 // Undocumented
#define ATC2603C_PCMIF_CTL 0xB3 // Undocumented
/* CMU_CONTROL Registers */
#define ATC2603C_CMU_DEVRST 0xC1 // Undocumented
/* INTS Registers */
#define ATC2603C_INTS_PD 0xC8
#define ATC2603C_INTS_MSK 0xC9
/* MFP Registers */
#define ATC2603C_MFP_CTL 0xD0
#define ATC2603C_PAD_VSEL 0xD1 // Undocumented
#define ATC2603C_GPIO_OUTEN 0xD2
#define ATC2603C_GPIO_INEN 0xD3
#define ATC2603C_GPIO_DAT 0xD4
#define ATC2603C_PAD_DRV 0xD5
#define ATC2603C_PAD_EN 0xD6
#define ATC2603C_DEBUG_SEL 0xD7 // Undocumented
#define ATC2603C_DEBUG_IE 0xD8 // Undocumented
#define ATC2603C_DEBUG_OE 0xD9 // Undocumented
#define ATC2603C_BIST_START 0x0A // Undocumented
#define ATC2603C_BIST_RESULT 0x0B // Undocumented
#define ATC2603C_CHIP_VER 0xDC
/* TWSI Registers */
#define ATC2603C_SADDR 0xFF
/* PMU_SYS_CTL0 Register Mask Bits */
#define ATC2603C_PMU_SYS_CTL0_IR_WK_EN BIT(5)
#define ATC2603C_PMU_SYS_CTL0_RESET_WK_EN BIT(6)
#define ATC2603C_PMU_SYS_CTL0_HDSW_WK_EN BIT(7)
#define ATC2603C_PMU_SYS_CTL0_ALARM_WK_EN BIT(8)
#define ATC2603C_PMU_SYS_CTL0_REM_CON_WK_EN BIT(9)
#define ATC2603C_PMU_SYS_CTL0_RESTART_EN BIT(10)
#define ATC2603C_PMU_SYS_CTL0_SGPIOIRQ_WK_EN BIT(11)
#define ATC2603C_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN BIT(12)
#define ATC2603C_PMU_SYS_CTL0_ONOFF_LONG_WK_EN BIT(13)
#define ATC2603C_PMU_SYS_CTL0_WALL_WK_EN BIT(14)
#define ATC2603C_PMU_SYS_CTL0_USB_WK_EN BIT(15)
#define ATC2603C_PMU_SYS_CTL0_WK_ALL (GENMASK(15, 5) & (~BIT(10)))
/* PMU_SYS_CTL1 Register Mask Bits */
#define ATC2603C_PMU_SYS_CTL1_EN_S1 BIT(0)
#define ATC2603C_PMU_SYS_CTL1_LB_S4_EN BIT(2)
#define ATC2603C_PMU_SYS_CTL1_LB_S4 GENMASK(4, 3)
#define ATC2603C_PMU_SYS_CTL1_LB_S4_3_1V BIT(4)
#define ATC2603C_PMU_SYS_CTL1_IR_WK_FLAG BIT(5)
#define ATC2603C_PMU_SYS_CTL1_RESET_WK_FLAG BIT(6)
#define ATC2603C_PMU_SYS_CTL1_HDSW_WK_FLAG BIT(7)
#define ATC2603C_PMU_SYS_CTL1_ALARM_WK_FLAG BIT(8)
#define ATC2603C_PMU_SYS_CTL1_REM_CON_WK_FLAG BIT(9)
#define ATC2603C_PMU_SYS_CTL1_ONOFF_PRESS_RESET_IRQ_PD BIT(10)
#define ATC2603C_PMU_SYS_CTL1_SGPIOIRQ_WK_FLAG BIT(11)
#define ATC2603C_PMU_SYS_CTL1_ONOFF_SHORT_WK_FLAG BIT(12)
#define ATC2603C_PMU_SYS_CTL1_ONOFF_LONG_WK_FLAG BIT(13)
#define ATC2603C_PMU_SYS_CTL1_WALL_WK_FLAG BIT(14)
#define ATC2603C_PMU_SYS_CTL1_USB_WK_FLAG BIT(15)
/* PMU_SYS_CTL2 Register Mask Bits */
#define ATC2603C_PMU_SYS_CTL2_PMU_A_EN BIT(0)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN BIT(1)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_PD BIT(2)
#define ATC2603C_PMU_SYS_CTL2_S2TIMER GENMASK(5, 3)
#define ATC2603C_PMU_SYS_CTL2_S2_TIMER_EN BIT(6)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL GENMASK(8, 7)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_RESET_EN BIT(9)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS_TIME GENMASK(11, 10)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_INT_EN BIT(12)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_LONG_PRESS BIT(13)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_SHORT_PRESS BIT(14)
#define ATC2603C_PMU_SYS_CTL2_ONOFF_PRESS BIT(15)
/* PMU_SYS_CTL3 Register Mask Bits */
#define ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER GENMASK(8, 7)
#define ATC2603C_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN BIT(9)
#define ATC2603C_PMU_SYS_CTL3_S3_TIMER GENMASK(12, 10)
#define ATC2603C_PMU_SYS_CTL3_S3_TIMER_EN BIT(13)
#define ATC2603C_PMU_SYS_CTL3_EN_S3 BIT(14)
#define ATC2603C_PMU_SYS_CTL3_EN_S2 BIT(15)
/* PMU_SYS_CTL5 Register Mask Bits */
#define ATC2603C_PMU_SYS_CTL5_WALLWKDTEN BIT(7)
#define ATC2603C_PMU_SYS_CTL5_VBUSWKDTEN BIT(8)
#define ATC2603C_PMU_SYS_CTL5_REMCON_DECT_EN BIT(9)
#define ATC2603C_PMU_SYS_CTL5_ONOFF_8S_SEL BIT(10)
/* INTS_MSK Register Mask Bits */
#define ATC2603C_INTS_MSK_AUDIO BIT(0)
#define ATC2603C_INTS_MSK_OV BIT(1)
#define ATC2603C_INTS_MSK_OC BIT(2)
#define ATC2603C_INTS_MSK_OT BIT(3)
#define ATC2603C_INTS_MSK_UV BIT(4)
#define ATC2603C_INTS_MSK_ALARM BIT(5)
#define ATC2603C_INTS_MSK_ONOFF BIT(6)
#define ATC2603C_INTS_MSK_SGPIO BIT(7)
#define ATC2603C_INTS_MSK_IR BIT(8)
#define ATC2603C_INTS_MSK_REMCON BIT(9)
#define ATC2603C_INTS_MSK_POWERIN BIT(10)
/* CMU_DEVRST Register Mask Bits */
#define ATC2603C_CMU_DEVRST_MFP BIT(1)
#define ATC2603C_CMU_DEVRST_INTS BIT(2)
#define ATC2603C_CMU_DEVRST_AUDIO BIT(4)
/* PAD_EN Register Mask Bits */
#define ATC2603C_PAD_EN_EXTIRQ BIT(0)
#endif /* __LINUX_MFD_ATC260X_ATC2603C_H */

View File

@ -0,0 +1,308 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* ATC2609A PMIC register definitions
*
* Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
*/
#ifndef __LINUX_MFD_ATC260X_ATC2609A_H
#define __LINUX_MFD_ATC260X_ATC2609A_H
enum atc2609a_irq_def {
ATC2609A_IRQ_AUDIO = 0,
ATC2609A_IRQ_OV,
ATC2609A_IRQ_OC,
ATC2609A_IRQ_OT,
ATC2609A_IRQ_UV,
ATC2609A_IRQ_ALARM,
ATC2609A_IRQ_ONOFF,
ATC2609A_IRQ_WKUP,
ATC2609A_IRQ_IR,
ATC2609A_IRQ_REMCON,
ATC2609A_IRQ_POWER_IN,
};
/* PMU Registers */
#define ATC2609A_PMU_SYS_CTL0 0x00
#define ATC2609A_PMU_SYS_CTL1 0x01
#define ATC2609A_PMU_SYS_CTL2 0x02
#define ATC2609A_PMU_SYS_CTL3 0x03
#define ATC2609A_PMU_SYS_CTL4 0x04
#define ATC2609A_PMU_SYS_CTL5 0x05
#define ATC2609A_PMU_SYS_CTL6 0x06
#define ATC2609A_PMU_SYS_CTL7 0x07
#define ATC2609A_PMU_SYS_CTL8 0x08
#define ATC2609A_PMU_SYS_CTL9 0x09
#define ATC2609A_PMU_BAT_CTL0 0x0A
#define ATC2609A_PMU_BAT_CTL1 0x0B
#define ATC2609A_PMU_VBUS_CTL0 0x0C
#define ATC2609A_PMU_VBUS_CTL1 0x0D
#define ATC2609A_PMU_WALL_CTL0 0x0E
#define ATC2609A_PMU_WALL_CTL1 0x0F
#define ATC2609A_PMU_SYS_PENDING 0x10
#define ATC2609A_PMU_APDS_CTL0 0x11
#define ATC2609A_PMU_APDS_CTL1 0x12
#define ATC2609A_PMU_APDS_CTL2 0x13
#define ATC2609A_PMU_CHARGER_CTL 0x14
#define ATC2609A_PMU_BAKCHARGER_CTL 0x15
#define ATC2609A_PMU_SWCHG_CTL0 0x16
#define ATC2609A_PMU_SWCHG_CTL1 0x17
#define ATC2609A_PMU_SWCHG_CTL2 0x18
#define ATC2609A_PMU_SWCHG_CTL3 0x19
#define ATC2609A_PMU_SWCHG_CTL4 0x1A
#define ATC2609A_PMU_DC_OSC 0x1B
#define ATC2609A_PMU_DC0_CTL0 0x1C
#define ATC2609A_PMU_DC0_CTL1 0x1D
#define ATC2609A_PMU_DC0_CTL2 0x1E
#define ATC2609A_PMU_DC0_CTL3 0x1F
#define ATC2609A_PMU_DC0_CTL4 0x20
#define ATC2609A_PMU_DC0_CTL5 0x21
#define ATC2609A_PMU_DC0_CTL6 0x22
#define ATC2609A_PMU_DC1_CTL0 0x23
#define ATC2609A_PMU_DC1_CTL1 0x24
#define ATC2609A_PMU_DC1_CTL2 0x25
#define ATC2609A_PMU_DC1_CTL3 0x26
#define ATC2609A_PMU_DC1_CTL4 0x27
#define ATC2609A_PMU_DC1_CTL5 0x28
#define ATC2609A_PMU_DC1_CTL6 0x29
#define ATC2609A_PMU_DC2_CTL0 0x2A
#define ATC2609A_PMU_DC2_CTL1 0x2B
#define ATC2609A_PMU_DC2_CTL2 0x2C
#define ATC2609A_PMU_DC2_CTL3 0x2D
#define ATC2609A_PMU_DC2_CTL4 0x2E
#define ATC2609A_PMU_DC2_CTL5 0x2F
#define ATC2609A_PMU_DC2_CTL6 0x30
#define ATC2609A_PMU_DC3_CTL0 0x31
#define ATC2609A_PMU_DC3_CTL1 0x32
#define ATC2609A_PMU_DC3_CTL2 0x33
#define ATC2609A_PMU_DC3_CTL3 0x34
#define ATC2609A_PMU_DC3_CTL4 0x35
#define ATC2609A_PMU_DC3_CTL5 0x36
#define ATC2609A_PMU_DC3_CTL6 0x37
#define ATC2609A_PMU_DC_ZR 0x38
#define ATC2609A_PMU_LDO0_CTL0 0x39
#define ATC2609A_PMU_LDO0_CTL1 0x3A
#define ATC2609A_PMU_LDO1_CTL0 0x3B
#define ATC2609A_PMU_LDO1_CTL1 0x3C
#define ATC2609A_PMU_LDO2_CTL0 0x3D
#define ATC2609A_PMU_LDO2_CTL1 0x3E
#define ATC2609A_PMU_LDO3_CTL0 0x3F
#define ATC2609A_PMU_LDO3_CTL1 0x40
#define ATC2609A_PMU_LDO4_CTL0 0x41
#define ATC2609A_PMU_LDO4_CTL1 0x42
#define ATC2609A_PMU_LDO5_CTL0 0x43
#define ATC2609A_PMU_LDO5_CTL1 0x44
#define ATC2609A_PMU_LDO6_CTL0 0x45
#define ATC2609A_PMU_LDO6_CTL1 0x46
#define ATC2609A_PMU_LDO7_CTL0 0x47
#define ATC2609A_PMU_LDO7_CTL1 0x48
#define ATC2609A_PMU_LDO8_CTL0 0x49
#define ATC2609A_PMU_LDO8_CTL1 0x4A
#define ATC2609A_PMU_LDO9_CTL 0x4B
#define ATC2609A_PMU_OV_INT_EN 0x4C
#define ATC2609A_PMU_OV_STATUS 0x4D
#define ATC2609A_PMU_UV_INT_EN 0x4E
#define ATC2609A_PMU_UV_STATUS 0x4F
#define ATC2609A_PMU_OC_INT_EN 0x50
#define ATC2609A_PMU_OC_STATUS 0x51
#define ATC2609A_PMU_OT_CTL 0x52
#define ATC2609A_PMU_CM_CTL0 0x53
#define ATC2609A_PMU_FW_USE0 0x54
#define ATC2609A_PMU_FW_USE1 0x55
#define ATC2609A_PMU_ADC12B_I 0x56
#define ATC2609A_PMU_ADC12B_V 0x57
#define ATC2609A_PMU_ADC12B_DUMMY 0x58
#define ATC2609A_PMU_AUXADC_CTL0 0x59
#define ATC2609A_PMU_AUXADC_CTL1 0x5A
#define ATC2609A_PMU_BATVADC 0x5B
#define ATC2609A_PMU_BATIADC 0x5C
#define ATC2609A_PMU_WALLVADC 0x5D
#define ATC2609A_PMU_WALLIADC 0x5E
#define ATC2609A_PMU_VBUSVADC 0x5F
#define ATC2609A_PMU_VBUSIADC 0x60
#define ATC2609A_PMU_SYSPWRADC 0x61
#define ATC2609A_PMU_REMCONADC 0x62
#define ATC2609A_PMU_SVCCADC 0x63
#define ATC2609A_PMU_CHGIADC 0x64
#define ATC2609A_PMU_IREFADC 0x65
#define ATC2609A_PMU_BAKBATADC 0x66
#define ATC2609A_PMU_ICTEMPADC 0x67
#define ATC2609A_PMU_AUXADC0 0x68
#define ATC2609A_PMU_AUXADC1 0x69
#define ATC2609A_PMU_AUXADC2 0x6A
#define ATC2609A_PMU_AUXADC3 0x6B
#define ATC2609A_PMU_ICTEMPADC_ADJ 0x6C
#define ATC2609A_PMU_BDG_CTL 0x6D
#define ATC2609A_RTC_CTL 0x6E
#define ATC2609A_RTC_MSALM 0x6F
#define ATC2609A_RTC_HALM 0x70
#define ATC2609A_RTC_YMDALM 0x71
#define ATC2609A_RTC_MS 0x72
#define ATC2609A_RTC_H 0x73
#define ATC2609A_RTC_DC 0x74
#define ATC2609A_RTC_YMD 0x75
#define ATC2609A_EFUSE_DAT 0x76
#define ATC2609A_EFUSECRTL1 0x77
#define ATC2609A_EFUSECRTL2 0x78
#define ATC2609A_PMU_DC4_CTL0 0x79
#define ATC2609A_PMU_DC4_CTL1 0x7A
#define ATC2609A_PMU_DC4_CTL2 0x7B
#define ATC2609A_PMU_DC4_CTL3 0x7C
#define ATC2609A_PMU_DC4_CTL4 0x7D
#define ATC2609A_PMU_DC4_CTL5 0x7E
#define ATC2609A_PMU_DC4_CTL6 0x7F
#define ATC2609A_PMU_PWR_STATUS 0x80
#define ATC2609A_PMU_S2_PWR 0x81
#define ATC2609A_CLMT_CTL0 0x82
#define ATC2609A_CLMT_DATA0 0x83
#define ATC2609A_CLMT_DATA1 0x84
#define ATC2609A_CLMT_DATA2 0x85
#define ATC2609A_CLMT_DATA3 0x86
#define ATC2609A_CLMT_ADD0 0x87
#define ATC2609A_CLMT_ADD1 0x88
#define ATC2609A_CLMT_OCV_TABLE 0x89
#define ATC2609A_CLMT_R_TABLE 0x8A
#define ATC2609A_PMU_PWRON_CTL0 0x8D
#define ATC2609A_PMU_PWRON_CTL1 0x8E
#define ATC2609A_PMU_PWRON_CTL2 0x8F
#define ATC2609A_IRC_CTL 0x90
#define ATC2609A_IRC_STAT 0x91
#define ATC2609A_IRC_CC 0x92
#define ATC2609A_IRC_KDC 0x93
#define ATC2609A_IRC_WK 0x94
#define ATC2609A_IRC_RCC 0x95
/* AUDIO_OUT Registers */
#define ATC2609A_AUDIOINOUT_CTL 0xA0
#define ATC2609A_AUDIO_DEBUGOUTCTL 0xA1
#define ATC2609A_DAC_DIGITALCTL 0xA2
#define ATC2609A_DAC_VOLUMECTL0 0xA3
#define ATC2609A_DAC_ANALOG0 0xA4
#define ATC2609A_DAC_ANALOG1 0xA5
#define ATC2609A_DAC_ANALOG2 0xA6
#define ATC2609A_DAC_ANALOG3 0xA7
/* AUDIO_IN Registers */
#define ATC2609A_ADC_DIGITALCTL 0xA8
#define ATC2609A_ADC_HPFCTL 0xA9
#define ATC2609A_ADC_CTL 0xAA
#define ATC2609A_AGC_CTL0 0xAB
#define ATC2609A_AGC_CTL1 0xAC
#define ATC2609A_AGC_CTL2 0xAD
#define ATC2609A_ADC_ANALOG0 0xAE
#define ATC2609A_ADC_ANALOG1 0xAF
/* PCM_IF Registers */
#define ATC2609A_PCM0_CTL 0xB0
#define ATC2609A_PCM1_CTL 0xB1
#define ATC2609A_PCM2_CTL 0xB2
#define ATC2609A_PCMIF_CTL 0xB3
/* CMU_CONTROL Registers */
#define ATC2609A_CMU_DEVRST 0xC1
/* INTS Registers */
#define ATC2609A_INTS_PD 0xC8
#define ATC2609A_INTS_MSK 0xC9
/* MFP Registers */
#define ATC2609A_MFP_CTL 0xD0
#define ATC2609A_PAD_VSEL 0xD1
#define ATC2609A_GPIO_OUTEN 0xD2
#define ATC2609A_GPIO_INEN 0xD3
#define ATC2609A_GPIO_DAT 0xD4
#define ATC2609A_PAD_DRV 0xD5
#define ATC2609A_PAD_EN 0xD6
#define ATC2609A_DEBUG_SEL 0xD7
#define ATC2609A_DEBUG_IE 0xD8
#define ATC2609A_DEBUG_OE 0xD9
#define ATC2609A_CHIP_VER 0xDC
/* PWSI Registers */
#define ATC2609A_PWSI_CTL 0xF0
#define ATC2609A_PWSI_STATUS 0xF1
/* TWSI Registers */
#define ATC2609A_SADDR 0xFF
/* PMU_SYS_CTL0 Register Mask Bits */
#define ATC2609A_PMU_SYS_CTL0_IR_WK_EN BIT(5)
#define ATC2609A_PMU_SYS_CTL0_RESET_WK_EN BIT(6)
#define ATC2609A_PMU_SYS_CTL0_HDSW_WK_EN BIT(7)
#define ATC2609A_PMU_SYS_CTL0_ALARM_WK_EN BIT(8)
#define ATC2609A_PMU_SYS_CTL0_REM_CON_WK_EN BIT(9)
#define ATC2609A_PMU_SYS_CTL0_RESTART_EN BIT(10)
#define ATC2609A_PMU_SYS_CTL0_WKIRQ_WK_EN BIT(11)
#define ATC2609A_PMU_SYS_CTL0_ONOFF_SHORT_WK_EN BIT(12)
#define ATC2609A_PMU_SYS_CTL0_ONOFF_LONG_WK_EN BIT(13)
#define ATC2609A_PMU_SYS_CTL0_WALL_WK_EN BIT(14)
#define ATC2609A_PMU_SYS_CTL0_USB_WK_EN BIT(15)
#define ATC2609A_PMU_SYS_CTL0_WK_ALL (GENMASK(15, 5) & (~BIT(10)))
/* PMU_SYS_CTL1 Register Mask Bits */
#define ATC2609A_PMU_SYS_CTL1_EN_S1 BIT(0)
#define ATC2609A_PMU_SYS_CTL1_LB_S4_EN BIT(2)
#define ATC2609A_PMU_SYS_CTL1_LB_S4 GENMASK(4, 3)
#define ATC2609A_PMU_SYS_CTL1_LB_S4_3_1V BIT(4)
#define ATC2609A_PMU_SYS_CTL1_IR_WK_FLAG BIT(5)
#define ATC2609A_PMU_SYS_CTL1_RESET_WK_FLAG BIT(6)
#define ATC2609A_PMU_SYS_CTL1_HDSW_WK_FLAG BIT(7)
#define ATC2609A_PMU_SYS_CTL1_ALARM_WK_FLAG BIT(8)
#define ATC2609A_PMU_SYS_CTL1_REM_CON_WK_FLAG BIT(9)
#define ATC2609A_PMU_SYS_CTL1_RESTART_WK_FLAG BIT(10)
#define ATC2609A_PMU_SYS_CTL1_WKIRQ_WK_FLAG BIT(11)
#define ATC2609A_PMU_SYS_CTL1_ONOFF_SHORT_WK_FLAG BIT(12)
#define ATC2609A_PMU_SYS_CTL1_ONOFF_LONG_WK_FLAG BIT(13)
#define ATC2609A_PMU_SYS_CTL1_WALL_WK_FLAG BIT(14)
#define ATC2609A_PMU_SYS_CTL1_USB_WK_FLAG BIT(15)
/* PMU_SYS_CTL2 Register Mask Bits */
#define ATC2609A_PMU_SYS_CTL2_PMU_A_EN BIT(0)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_INT_EN BIT(1)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_PD BIT(2)
#define ATC2609A_PMU_SYS_CTL2_S2TIMER GENMASK(5, 3)
#define ATC2609A_PMU_SYS_CTL2_S2_TIMER_EN BIT(6)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_TIME_SEL GENMASK(8, 7)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_RESET_EN BIT(9)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS_TIME GENMASK(11, 10)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_LSP_INT_EN BIT(12)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_LONG_PRESS BIT(13)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_SHORT_PRESS BIT(14)
#define ATC2609A_PMU_SYS_CTL2_ONOFF_PRESS BIT(15)
/* PMU_SYS_CTL3 Register Mask Bits */
#define ATC2609A_PMU_SYS_CTL3_S2S3TOS1_TIMER GENMASK(8, 7)
#define ATC2609A_PMU_SYS_CTL3_S2S3TOS1_TIMER_EN BIT(9)
#define ATC2609A_PMU_SYS_CTL3_S3_TIMER GENMASK(12, 10)
#define ATC2609A_PMU_SYS_CTL3_S3_TIMER_EN BIT(13)
#define ATC2609A_PMU_SYS_CTL3_EN_S3 BIT(14)
#define ATC2609A_PMU_SYS_CTL3_EN_S2 BIT(15)
/* PMU_SYS_CTL5 Register Mask Bits */
#define ATC2609A_PMU_SYS_CTL5_WALLWKDTEN BIT(7)
#define ATC2609A_PMU_SYS_CTL5_VBUSWKDTEN BIT(8)
#define ATC2609A_PMU_SYS_CTL5_REMCON_DECT_EN BIT(9)
#define ATC2609A_PMU_SYS_CTL5_ONOFF_8S_SEL BIT(10)
/* INTS_MSK Register Mask Bits */
#define ATC2609A_INTS_MSK_AUDIO BIT(0)
#define ATC2609A_INTS_MSK_OV BIT(1)
#define ATC2609A_INTS_MSK_OC BIT(2)
#define ATC2609A_INTS_MSK_OT BIT(3)
#define ATC2609A_INTS_MSK_UV BIT(4)
#define ATC2609A_INTS_MSK_ALARM BIT(5)
#define ATC2609A_INTS_MSK_ONOFF BIT(6)
#define ATC2609A_INTS_MSK_WKUP BIT(7)
#define ATC2609A_INTS_MSK_IR BIT(8)
#define ATC2609A_INTS_MSK_REMCON BIT(9)
#define ATC2609A_INTS_MSK_POWERIN BIT(10)
/* CMU_DEVRST Register Mask Bits */
#define ATC2609A_CMU_DEVRST_AUDIO BIT(0)
#define ATC2609A_CMU_DEVRST_MFP BIT(1)
#define ATC2609A_CMU_DEVRST_INTS BIT(2)
/* PAD_EN Register Mask Bits */
#define ATC2609A_PAD_EN_EXTIRQ BIT(0)
#endif /* __LINUX_MFD_ATC260X_ATC2609A_H */

View File

@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Core MFD defines for ATC260x PMICs
*
* Copyright (C) 2019 Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
* Copyright (C) 2020 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
*/
#ifndef __LINUX_MFD_ATC260X_CORE_H
#define __LINUX_MFD_ATC260X_CORE_H
#include <linux/mfd/atc260x/atc2603c.h>
#include <linux/mfd/atc260x/atc2609a.h>
enum atc260x_type {
ATC2603A = 0,
ATC2603C,
ATC2609A,
};
enum atc260x_ver {
ATC260X_A = 0,
ATC260X_B,
ATC260X_C,
ATC260X_D,
ATC260X_E,
ATC260X_F,
ATC260X_G,
ATC260X_H,
};
struct atc260x {
struct device *dev;
struct regmap *regmap;
const struct regmap_irq_chip *regmap_irq_chip;
struct regmap_irq_chip_data *irq_data;
struct mutex *regmap_mutex; /* mutex for custom regmap locking */
const struct mfd_cell *cells;
int nr_cells;
int irq;
enum atc260x_type ic_type;
enum atc260x_ver ic_ver;
const char *type_name;
unsigned int rev_reg;
const struct atc260x_init_regs *init_regs; /* regs for device init */
};
struct regmap_config;
int atc260x_match_device(struct atc260x *atc260x, struct regmap_config *regmap_cfg);
int atc260x_device_probe(struct atc260x *atc260x);
#endif /* __LINUX_MFD_ATC260X_CORE_H */

View File

@ -50,7 +50,7 @@
#define MFD_DEP_LEVEL_HIGH 1
struct irq_domain;
struct property_entry;
struct software_node;
/* Matches ACPI PNP id, either _HID or _CID, or ACPI _ADR */
struct mfd_cell_acpi_match {
@ -78,8 +78,8 @@ struct mfd_cell {
void *platform_data;
size_t pdata_size;
/* device properties passed to the sub devices drivers */
const struct property_entry *properties;
/* Software node for the device. */
const struct software_node *swnode;
/*
* Device Tree compatible string

View File

@ -1037,6 +1037,9 @@
#define DA9063_NONKEY_PIN_AUTODOWN 0x02
#define DA9063_NONKEY_PIN_AUTOFLPRT 0x03
/* DA9063_REG_CONFIG_J (addr=0x10F) */
#define DA9063_TWOWIRE_TO 0x40
/* DA9063_REG_MON_REG_5 (addr=0x116) */
#define DA9063_MON_A8_IDX_MASK 0x07
#define DA9063_MON_A8_IDX_NONE 0x00

View File

@ -720,7 +720,7 @@ static inline int db8500_prcmu_load_a9wdog(u8 id, u32 val)
static inline bool db8500_prcmu_is_ac_wake_requested(void)
{
return 0;
return false;
}
static inline int db8500_prcmu_set_arm_opp(u8 opp)

View File

@ -9,9 +9,15 @@
#include <linux/regmap.h>
#define M10BMC_LEGACY_SYS_BASE 0x300400
#define M10BMC_LEGACY_BUILD_VER 0x300468
#define M10BMC_SYS_BASE 0x300800
#define M10BMC_MEM_END 0x200000fc
#define M10BMC_SYS_END 0x300fff
#define M10BMC_FLASH_BASE 0x10000000
#define M10BMC_FLASH_END 0x1fffffff
#define M10BMC_MEM_END M10BMC_FLASH_END
#define M10BMC_STAGING_BASE 0x18000000
#define M10BMC_STAGING_SIZE 0x3800000
/* Register offset of system registers */
#define NIOS2_FW_VERSION 0x0
@ -30,6 +36,88 @@
#define M10BMC_VER_PCB_INFO_MSK GENMASK(31, 24)
#define M10BMC_VER_LEGACY_INVALID 0xffffffff
/* Secure update doorbell register, in system register region */
#define M10BMC_DOORBELL 0x400
/* Authorization Result register, in system register region */
#define M10BMC_AUTH_RESULT 0x404
/* Doorbell register fields */
#define DRBL_RSU_REQUEST BIT(0)
#define DRBL_RSU_PROGRESS GENMASK(7, 4)
#define DRBL_HOST_STATUS GENMASK(11, 8)
#define DRBL_RSU_STATUS GENMASK(23, 16)
#define DRBL_PKVL_EEPROM_LOAD_SEC BIT(24)
#define DRBL_PKVL1_POLL_EN BIT(25)
#define DRBL_PKVL2_POLL_EN BIT(26)
#define DRBL_CONFIG_SEL BIT(28)
#define DRBL_REBOOT_REQ BIT(29)
#define DRBL_REBOOT_DISABLED BIT(30)
/* Progress states */
#define RSU_PROG_IDLE 0x0
#define RSU_PROG_PREPARE 0x1
#define RSU_PROG_READY 0x3
#define RSU_PROG_AUTHENTICATING 0x4
#define RSU_PROG_COPYING 0x5
#define RSU_PROG_UPDATE_CANCEL 0x6
#define RSU_PROG_PROGRAM_KEY_HASH 0x7
#define RSU_PROG_RSU_DONE 0x8
#define RSU_PROG_PKVL_PROM_DONE 0x9
/* Device and error states */
#define RSU_STAT_NORMAL 0x0
#define RSU_STAT_TIMEOUT 0x1
#define RSU_STAT_AUTH_FAIL 0x2
#define RSU_STAT_COPY_FAIL 0x3
#define RSU_STAT_FATAL 0x4
#define RSU_STAT_PKVL_REJECT 0x5
#define RSU_STAT_NON_INC 0x6
#define RSU_STAT_ERASE_FAIL 0x7
#define RSU_STAT_WEAROUT 0x8
#define RSU_STAT_NIOS_OK 0x80
#define RSU_STAT_USER_OK 0x81
#define RSU_STAT_FACTORY_OK 0x82
#define RSU_STAT_USER_FAIL 0x83
#define RSU_STAT_FACTORY_FAIL 0x84
#define RSU_STAT_NIOS_FLASH_ERR 0x85
#define RSU_STAT_FPGA_FLASH_ERR 0x86
#define HOST_STATUS_IDLE 0x0
#define HOST_STATUS_WRITE_DONE 0x1
#define HOST_STATUS_ABORT_RSU 0x2
#define rsu_prog(doorbell) FIELD_GET(DRBL_RSU_PROGRESS, doorbell)
#define rsu_stat(doorbell) FIELD_GET(DRBL_RSU_STATUS, doorbell)
/* interval 100ms and timeout 5s */
#define NIOS_HANDSHAKE_INTERVAL_US (100 * 1000)
#define NIOS_HANDSHAKE_TIMEOUT_US (5 * 1000 * 1000)
/* RSU PREP Timeout (2 minutes) to erase flash staging area */
#define RSU_PREP_INTERVAL_MS 100
#define RSU_PREP_TIMEOUT_MS (2 * 60 * 1000)
/* RSU Complete Timeout (40 minutes) for full flash update */
#define RSU_COMPLETE_INTERVAL_MS 1000
#define RSU_COMPLETE_TIMEOUT_MS (40 * 60 * 1000)
/* Addresses for security related data in FLASH */
#define BMC_REH_ADDR 0x17ffc004
#define BMC_PROG_ADDR 0x17ffc000
#define BMC_PROG_MAGIC 0x5746
#define SR_REH_ADDR 0x17ffd004
#define SR_PROG_ADDR 0x17ffd000
#define SR_PROG_MAGIC 0x5253
#define PR_REH_ADDR 0x17ffe004
#define PR_PROG_ADDR 0x17ffe000
#define PR_PROG_MAGIC 0x5250
/* Address of 4KB inverted bit vector containing staging area FLASH count */
#define STAGING_FLASH_COUNT 0x17ffb000
/**
* struct intel_m10bmc - Intel MAX 10 BMC parent driver data structure
* @dev: this device

View File

@ -237,9 +237,6 @@ enum lp87565_device_type {
#define LP87565_GOIO2_OUT BIT(1)
#define LP87565_GOIO1_OUT BIT(0)
/* Number of step-down converters available */
#define LP87565_NUM_BUCK 6
enum LP87565_regulator_id {
/* BUCK's */
LP87565_BUCK_0,

View File

@ -14,13 +14,13 @@
* others and b) it can be enabled simply by using MAX17042 driver.
*/
#ifndef __LINUX_MFD_MAX8998_H
#define __LINUX_MFD_MAX8998_H
#ifndef __LINUX_MFD_MAX8997_H
#define __LINUX_MFD_MAX8997_H
#include <linux/regulator/consumer.h>
/* MAX8997/8966 regulator IDs */
enum max8998_regulators {
enum max8997_regulators {
MAX8997_LDO1 = 0,
MAX8997_LDO2,
MAX8997_LDO3,
@ -207,4 +207,4 @@ struct max8997_platform_data {
struct max8997_led_platform_data *led_pdata;
};
#endif /* __LINUX_MFD_MAX8998_H */
#endif /* __LINUX_MFD_MAX8997_H */

38
include/linux/mfd/ntxec.h Normal file
View File

@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2020 Jonathan Neuschäfer
*
* Register access and version information for the Netronix embedded
* controller.
*/
#ifndef NTXEC_H
#define NTXEC_H
#include <linux/types.h>
struct device;
struct regmap;
struct ntxec {
struct device *dev;
struct regmap *regmap;
};
/*
* Some registers, such as the battery status register (0x41), are in
* big-endian, but others only have eight significant bits, which are in the
* first byte transmitted over I2C (the MSB of the big-endian value).
* This convenience function converts an 8-bit value to 16-bit for use in the
* second kind of register.
*/
static inline __be16 ntxec_reg8(u8 value)
{
return value << 8;
}
/* Known firmware versions */
#define NTXEC_VERSION_KOBO_AURA 0xd726 /* found in Kobo Aura */
#define NTXEC_VERSION_TOLINO_SHINE2 0xf110 /* found in Tolino Shine 2 HD */
#endif

View File

@ -188,6 +188,7 @@
#define RN5T618_CHGOSCSCORESET3 0xd7
#define RN5T618_CHGOSCFREQSET1 0xd8
#define RN5T618_CHGOSCFREQSET2 0xd9
#define RN5T618_GCHGDET 0xda
#define RN5T618_CONTROL 0xe0
#define RN5T618_SOC 0xe1
#define RN5T618_RE_CAP_H 0xe2

View File

@ -0,0 +1,562 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2021 ROHM Semiconductors.
*
* Author: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
*
* Copyright 2014 Embest Technology Co. Ltd. Inc.
*
* Author: yanglsh@embest-tech.com
*/
#ifndef _MFD_BD71815_H
#define _MFD_BD71815_H
#include <linux/regmap.h>
enum {
BD71815_BUCK1 = 0,
BD71815_BUCK2,
BD71815_BUCK3,
BD71815_BUCK4,
BD71815_BUCK5,
/* General Purpose */
BD71815_LDO1,
BD71815_LDO2,
BD71815_LDO3,
/* LDOs for SD Card and SD Card Interface */
BD71815_LDO4,
BD71815_LDO5,
/* LDO for DDR Reference Voltage */
BD71815_LDODVREF,
/* LDO for Low-Power State Retention */
BD71815_LDOLPSR,
BD71815_WLED,
BD71815_REGULATOR_CNT,
};
#define BD71815_SUPPLY_STATE_ENABLED 0x1
enum {
BD71815_REG_DEVICE = 0,
BD71815_REG_PWRCTRL,
BD71815_REG_BUCK1_MODE,
BD71815_REG_BUCK2_MODE,
BD71815_REG_BUCK3_MODE,
BD71815_REG_BUCK4_MODE,
BD71815_REG_BUCK5_MODE,
BD71815_REG_BUCK1_VOLT_H,
BD71815_REG_BUCK1_VOLT_L,
BD71815_REG_BUCK2_VOLT_H,
BD71815_REG_BUCK2_VOLT_L,
BD71815_REG_BUCK3_VOLT,
BD71815_REG_BUCK4_VOLT,
BD71815_REG_BUCK5_VOLT,
BD71815_REG_LED_CTRL,
BD71815_REG_LED_DIMM,
BD71815_REG_LDO_MODE1,
BD71815_REG_LDO_MODE2,
BD71815_REG_LDO_MODE3,
BD71815_REG_LDO_MODE4,
BD71815_REG_LDO1_VOLT,
BD71815_REG_LDO2_VOLT,
BD71815_REG_LDO3_VOLT,
BD71815_REG_LDO4_VOLT,
BD71815_REG_LDO5_VOLT_H,
BD71815_REG_LDO5_VOLT_L,
BD71815_REG_BUCK_PD_DIS,
BD71815_REG_LDO_PD_DIS,
BD71815_REG_GPO,
BD71815_REG_OUT32K,
BD71815_REG_SEC,
BD71815_REG_MIN,
BD71815_REG_HOUR,
BD71815_REG_WEEK,
BD71815_REG_DAY,
BD71815_REG_MONTH,
BD71815_REG_YEAR,
BD71815_REG_ALM0_SEC,
BD71815_REG_ALM1_SEC = 0x2C,
BD71815_REG_ALM0_MASK = 0x33,
BD71815_REG_ALM1_MASK,
BD71815_REG_ALM2,
BD71815_REG_TRIM,
BD71815_REG_CONF,
BD71815_REG_SYS_INIT,
BD71815_REG_CHG_STATE,
BD71815_REG_CHG_LAST_STATE,
BD71815_REG_BAT_STAT,
BD71815_REG_DCIN_STAT,
BD71815_REG_VSYS_STAT,
BD71815_REG_CHG_STAT,
BD71815_REG_CHG_WDT_STAT,
BD71815_REG_BAT_TEMP,
BD71815_REG_IGNORE_0,
BD71815_REG_INHIBIT_0,
BD71815_REG_DCIN_CLPS,
BD71815_REG_VSYS_REG,
BD71815_REG_VSYS_MAX,
BD71815_REG_VSYS_MIN,
BD71815_REG_CHG_SET1,
BD71815_REG_CHG_SET2,
BD71815_REG_CHG_WDT_PRE,
BD71815_REG_CHG_WDT_FST,
BD71815_REG_CHG_IPRE,
BD71815_REG_CHG_IFST,
BD71815_REG_CHG_IFST_TERM,
BD71815_REG_CHG_VPRE,
BD71815_REG_CHG_VBAT_1,
BD71815_REG_CHG_VBAT_2,
BD71815_REG_CHG_VBAT_3,
BD71815_REG_CHG_LED_1,
BD71815_REG_VF_TH,
BD71815_REG_BAT_SET_1,
BD71815_REG_BAT_SET_2,
BD71815_REG_BAT_SET_3,
BD71815_REG_ALM_VBAT_TH_U,
BD71815_REG_ALM_VBAT_TH_L,
BD71815_REG_ALM_DCIN_TH,
BD71815_REG_ALM_VSYS_TH,
BD71815_REG_VM_IBAT_U,
BD71815_REG_VM_IBAT_L,
BD71815_REG_VM_VBAT_U,
BD71815_REG_VM_VBAT_L,
BD71815_REG_VM_BTMP,
BD71815_REG_VM_VTH,
BD71815_REG_VM_DCIN_U,
BD71815_REG_VM_DCIN_L,
BD71815_REG_VM_VSYS,
BD71815_REG_VM_VF,
BD71815_REG_VM_OCI_PRE_U,
BD71815_REG_VM_OCI_PRE_L,
BD71815_REG_VM_OCV_PRE_U,
BD71815_REG_VM_OCV_PRE_L,
BD71815_REG_VM_OCI_PST_U,
BD71815_REG_VM_OCI_PST_L,
BD71815_REG_VM_OCV_PST_U,
BD71815_REG_VM_OCV_PST_L,
BD71815_REG_VM_SA_VBAT_U,
BD71815_REG_VM_SA_VBAT_L,
BD71815_REG_VM_SA_IBAT_U,
BD71815_REG_VM_SA_IBAT_L,
BD71815_REG_CC_CTRL,
BD71815_REG_CC_BATCAP1_TH_U,
BD71815_REG_CC_BATCAP1_TH_L,
BD71815_REG_CC_BATCAP2_TH_U,
BD71815_REG_CC_BATCAP2_TH_L,
BD71815_REG_CC_BATCAP3_TH_U,
BD71815_REG_CC_BATCAP3_TH_L,
BD71815_REG_CC_STAT,
BD71815_REG_CC_CCNTD_3,
BD71815_REG_CC_CCNTD_2,
BD71815_REG_CC_CCNTD_1,
BD71815_REG_CC_CCNTD_0,
BD71815_REG_CC_CURCD_U,
BD71815_REG_CC_CURCD_L,
BD71815_REG_VM_OCUR_THR_1,
BD71815_REG_VM_OCUR_DUR_1,
BD71815_REG_VM_OCUR_THR_2,
BD71815_REG_VM_OCUR_DUR_2,
BD71815_REG_VM_OCUR_THR_3,
BD71815_REG_VM_OCUR_DUR_3,
BD71815_REG_VM_OCUR_MON,
BD71815_REG_VM_BTMP_OV_THR,
BD71815_REG_VM_BTMP_OV_DUR,
BD71815_REG_VM_BTMP_LO_THR,
BD71815_REG_VM_BTMP_LO_DUR,
BD71815_REG_VM_BTMP_MON,
BD71815_REG_INT_EN_01,
BD71815_REG_INT_EN_11 = 0x95,
BD71815_REG_INT_EN_12,
BD71815_REG_INT_STAT,
BD71815_REG_INT_STAT_01,
BD71815_REG_INT_STAT_02,
BD71815_REG_INT_STAT_03,
BD71815_REG_INT_STAT_04,
BD71815_REG_INT_STAT_05,
BD71815_REG_INT_STAT_06,
BD71815_REG_INT_STAT_07,
BD71815_REG_INT_STAT_08,
BD71815_REG_INT_STAT_09,
BD71815_REG_INT_STAT_10,
BD71815_REG_INT_STAT_11,
BD71815_REG_INT_STAT_12,
BD71815_REG_INT_UPDATE,
BD71815_REG_VM_VSYS_U = 0xC0,
BD71815_REG_VM_VSYS_L,
BD71815_REG_VM_SA_VSYS_U,
BD71815_REG_VM_SA_VSYS_L,
BD71815_REG_VM_SA_IBAT_MIN_U = 0xD0,
BD71815_REG_VM_SA_IBAT_MIN_L,
BD71815_REG_VM_SA_IBAT_MAX_U,
BD71815_REG_VM_SA_IBAT_MAX_L,
BD71815_REG_VM_SA_VBAT_MIN_U,
BD71815_REG_VM_SA_VBAT_MIN_L,
BD71815_REG_VM_SA_VBAT_MAX_U,
BD71815_REG_VM_SA_VBAT_MAX_L,
BD71815_REG_VM_SA_VSYS_MIN_U,
BD71815_REG_VM_SA_VSYS_MIN_L,
BD71815_REG_VM_SA_VSYS_MAX_U,
BD71815_REG_VM_SA_VSYS_MAX_L,
BD71815_REG_VM_SA_MINMAX_CLR,
BD71815_REG_REX_CCNTD_3 = 0xE0,
BD71815_REG_REX_CCNTD_2,
BD71815_REG_REX_CCNTD_1,
BD71815_REG_REX_CCNTD_0,
BD71815_REG_REX_SA_VBAT_U,
BD71815_REG_REX_SA_VBAT_L,
BD71815_REG_REX_CTRL_1,
BD71815_REG_REX_CTRL_2,
BD71815_REG_FULL_CCNTD_3,
BD71815_REG_FULL_CCNTD_2,
BD71815_REG_FULL_CCNTD_1,
BD71815_REG_FULL_CCNTD_0,
BD71815_REG_FULL_CTRL,
BD71815_REG_CCNTD_CHG_3 = 0xF0,
BD71815_REG_CCNTD_CHG_2,
BD71815_REG_TEST_MODE = 0xFE,
BD71815_MAX_REGISTER,
};
/* BD71815_REG_BUCK1_MODE bits */
#define BD71815_BUCK_RAMPRATE_MASK 0xC0
#define BD71815_BUCK_RAMPRATE_10P00MV 0x0
#define BD71815_BUCK_RAMPRATE_5P00MV 0x01
#define BD71815_BUCK_RAMPRATE_2P50MV 0x02
#define BD71815_BUCK_RAMPRATE_1P25MV 0x03
#define BD71815_BUCK_PWM_FIXED BIT(4)
#define BD71815_BUCK_SNVS_ON BIT(3)
#define BD71815_BUCK_RUN_ON BIT(2)
#define BD71815_BUCK_LPSR_ON BIT(1)
#define BD71815_BUCK_SUSP_ON BIT(0)
/* BD71815_REG_BUCK1_VOLT_H bits */
#define BD71815_BUCK_DVSSEL BIT(7)
#define BD71815_BUCK_STBY_DVS BIT(6)
#define BD71815_VOLT_MASK 0x3F
#define BD71815_BUCK1_H_DEFAULT 0x14
#define BD71815_BUCK1_L_DEFAULT 0x14
/* BD71815_REG_BUCK2_VOLT_H bits */
#define BD71815_BUCK2_H_DEFAULT 0x14
#define BD71815_BUCK2_L_DEFAULT 0x14
/* WLED output */
/* current register mask */
#define LED_DIMM_MASK 0x3f
/* LED enable bits at LED_CTRL reg */
#define LED_CHGDONE_EN BIT(4)
#define LED_RUN_ON BIT(2)
#define LED_LPSR_ON BIT(1)
#define LED_SUSP_ON BIT(0)
/* BD71815_REG_LDO1_CTRL bits */
#define LDO1_EN BIT(0)
#define LDO2_EN BIT(1)
#define LDO3_EN BIT(2)
#define DVREF_EN BIT(3)
#define VOSNVS_SW_EN BIT(4)
/* LDO_MODE1_register */
#define LDO1_SNVS_ON BIT(7)
#define LDO1_RUN_ON BIT(6)
#define LDO1_LPSR_ON BIT(5)
#define LDO1_SUSP_ON BIT(4)
/* set => register control, unset => GPIO control */
#define LDO4_MODE_MASK BIT(3)
#define LDO4_MODE_I2C BIT(3)
#define LDO4_MODE_GPIO 0
/* set => register control, unset => start when DCIN connected */
#define LDO3_MODE_MASK BIT(2)
#define LDO3_MODE_I2C BIT(2)
#define LDO3_MODE_DCIN 0
/* LDO_MODE2 register */
#define LDO3_SNVS_ON BIT(7)
#define LDO3_RUN_ON BIT(6)
#define LDO3_LPSR_ON BIT(5)
#define LDO3_SUSP_ON BIT(4)
#define LDO2_SNVS_ON BIT(3)
#define LDO2_RUN_ON BIT(2)
#define LDO2_LPSR_ON BIT(1)
#define LDO2_SUSP_ON BIT(0)
/* LDO_MODE3 register */
#define LDO5_SNVS_ON BIT(7)
#define LDO5_RUN_ON BIT(6)
#define LDO5_LPSR_ON BIT(5)
#define LDO5_SUSP_ON BIT(4)
#define LDO4_SNVS_ON BIT(3)
#define LDO4_RUN_ON BIT(2)
#define LDO4_LPSR_ON BIT(1)
#define LDO4_SUSP_ON BIT(0)
/* LDO_MODE4 register */
#define DVREF_SNVS_ON BIT(7)
#define DVREF_RUN_ON BIT(6)
#define DVREF_LPSR_ON BIT(5)
#define DVREF_SUSP_ON BIT(4)
#define LDO_LPSR_SNVS_ON BIT(3)
#define LDO_LPSR_RUN_ON BIT(2)
#define LDO_LPSR_LPSR_ON BIT(1)
#define LDO_LPSR_SUSP_ON BIT(0)
/* BD71815_REG_OUT32K bits */
#define OUT32K_EN BIT(0)
#define OUT32K_MODE BIT(1)
#define OUT32K_MODE_CMOS BIT(1)
#define OUT32K_MODE_OPEN_DRAIN 0
/* BD71815_REG_BAT_STAT bits */
#define BAT_DET BIT(5)
#define BAT_DET_OFFSET 5
#define BAT_DET_DONE BIT(4)
#define VBAT_OV BIT(3)
#define DBAT_DET BIT(0)
/* BD71815_REG_VBUS_STAT bits */
#define VBUS_DET BIT(0)
#define BD71815_REG_RTC_START BD71815_REG_SEC
#define BD71815_REG_RTC_ALM_START BD71815_REG_ALM0_SEC
/* BD71815_REG_ALM0_MASK bits */
#define A0_ONESEC BIT(7)
/* BD71815_REG_INT_EN_00 bits */
#define ALMALE BIT(0)
/* BD71815_REG_INT_STAT_03 bits */
#define DCIN_MON_DET BIT(1)
#define DCIN_MON_RES BIT(0)
#define POWERON_LONG BIT(2)
#define POWERON_MID BIT(3)
#define POWERON_SHORT BIT(4)
#define POWERON_PRESS BIT(5)
/* BD71805_REG_INT_STAT_08 bits */
#define VBAT_MON_DET BIT(1)
#define VBAT_MON_RES BIT(0)
/* BD71805_REG_INT_STAT_11 bits */
#define INT_STAT_11_VF_DET BIT(7)
#define INT_STAT_11_VF_RES BIT(6)
#define INT_STAT_11_VF125_DET BIT(5)
#define INT_STAT_11_VF125_RES BIT(4)
#define INT_STAT_11_OVTMP_DET BIT(3)
#define INT_STAT_11_OVTMP_RES BIT(2)
#define INT_STAT_11_LOTMP_DET BIT(1)
#define INT_STAT_11_LOTMP_RES BIT(0)
#define VBAT_MON_DET BIT(1)
#define VBAT_MON_RES BIT(0)
/* BD71815_REG_PWRCTRL bits */
#define RESTARTEN BIT(0)
/* BD71815_REG_GPO bits */
#define READY_FORCE_LOW BIT(2)
#define BD71815_GPIO_DRIVE_MASK BIT(4)
#define BD71815_GPIO_OPEN_DRAIN 0
#define BD71815_GPIO_CMOS BIT(4)
/* BD71815 interrupt masks */
enum {
BD71815_INT_EN_01_BUCKAST_MASK = 0x0F,
BD71815_INT_EN_02_DCINAST_MASK = 0x3E,
BD71815_INT_EN_03_DCINAST_MASK = 0x3F,
BD71815_INT_EN_04_VSYSAST_MASK = 0xCF,
BD71815_INT_EN_05_CHGAST_MASK = 0xFC,
BD71815_INT_EN_06_BATAST_MASK = 0xF3,
BD71815_INT_EN_07_BMONAST_MASK = 0xFE,
BD71815_INT_EN_08_BMONAST_MASK = 0x03,
BD71815_INT_EN_09_BMONAST_MASK = 0x07,
BD71815_INT_EN_10_BMONAST_MASK = 0x3F,
BD71815_INT_EN_11_TMPAST_MASK = 0xFF,
BD71815_INT_EN_12_ALMAST_MASK = 0x07,
};
/* BD71815 interrupt irqs */
enum {
/* BUCK reg interrupts */
BD71815_INT_BUCK1_OCP,
BD71815_INT_BUCK2_OCP,
BD71815_INT_BUCK3_OCP,
BD71815_INT_BUCK4_OCP,
BD71815_INT_BUCK5_OCP,
BD71815_INT_LED_OVP,
BD71815_INT_LED_OCP,
BD71815_INT_LED_SCP,
/* DCIN1 interrupts */
BD71815_INT_DCIN_RMV,
BD71815_INT_CLPS_OUT,
BD71815_INT_CLPS_IN,
BD71815_INT_DCIN_OVP_RES,
BD71815_INT_DCIN_OVP_DET,
/* DCIN2 interrupts */
BD71815_INT_DCIN_MON_RES,
BD71815_INT_DCIN_MON_DET,
BD71815_INT_WDOG,
/* Vsys INT_STAT_04 */
BD71815_INT_VSYS_UV_RES,
BD71815_INT_VSYS_UV_DET,
BD71815_INT_VSYS_LOW_RES,
BD71815_INT_VSYS_LOW_DET,
BD71815_INT_VSYS_MON_RES,
BD71815_INT_VSYS_MON_DET,
/* Charger INT_STAT_05 */
BD71815_INT_CHG_WDG_TEMP,
BD71815_INT_CHG_WDG_TIME,
BD71815_INT_CHG_RECHARGE_RES,
BD71815_INT_CHG_RECHARGE_DET,
BD71815_INT_CHG_RANGED_TEMP_TRANSITION,
BD71815_INT_CHG_STATE_TRANSITION,
/* Battery INT_STAT_06 */
BD71815_INT_BAT_TEMP_NORMAL,
BD71815_INT_BAT_TEMP_ERANGE,
BD71815_INT_BAT_REMOVED,
BD71815_INT_BAT_DETECTED,
BD71815_INT_THERM_REMOVED,
BD71815_INT_THERM_DETECTED,
/* Battery Mon 1 INT_STAT_07 */
BD71815_INT_BAT_DEAD,
BD71815_INT_BAT_SHORTC_RES,
BD71815_INT_BAT_SHORTC_DET,
BD71815_INT_BAT_LOW_VOLT_RES,
BD71815_INT_BAT_LOW_VOLT_DET,
BD71815_INT_BAT_OVER_VOLT_RES,
BD71815_INT_BAT_OVER_VOLT_DET,
/* Battery Mon 2 INT_STAT_08 */
BD71815_INT_BAT_MON_RES,
BD71815_INT_BAT_MON_DET,
/* Battery Mon 3 (Coulomb counter) INT_STAT_09 */
BD71815_INT_BAT_CC_MON1,
BD71815_INT_BAT_CC_MON2,
BD71815_INT_BAT_CC_MON3,
/* Battery Mon 4 INT_STAT_10 */
BD71815_INT_BAT_OVER_CURR_1_RES,
BD71815_INT_BAT_OVER_CURR_1_DET,
BD71815_INT_BAT_OVER_CURR_2_RES,
BD71815_INT_BAT_OVER_CURR_2_DET,
BD71815_INT_BAT_OVER_CURR_3_RES,
BD71815_INT_BAT_OVER_CURR_3_DET,
/* Temperature INT_STAT_11 */
BD71815_INT_TEMP_BAT_LOW_RES,
BD71815_INT_TEMP_BAT_LOW_DET,
BD71815_INT_TEMP_BAT_HI_RES,
BD71815_INT_TEMP_BAT_HI_DET,
BD71815_INT_TEMP_CHIP_OVER_125_RES,
BD71815_INT_TEMP_CHIP_OVER_125_DET,
BD71815_INT_TEMP_CHIP_OVER_VF_RES,
BD71815_INT_TEMP_CHIP_OVER_VF_DET,
/* RTC Alarm INT_STAT_12 */
BD71815_INT_RTC0,
BD71815_INT_RTC1,
BD71815_INT_RTC2,
};
#define BD71815_INT_BUCK1_OCP_MASK BIT(0)
#define BD71815_INT_BUCK2_OCP_MASK BIT(1)
#define BD71815_INT_BUCK3_OCP_MASK BIT(2)
#define BD71815_INT_BUCK4_OCP_MASK BIT(3)
#define BD71815_INT_BUCK5_OCP_MASK BIT(4)
#define BD71815_INT_LED_OVP_MASK BIT(5)
#define BD71815_INT_LED_OCP_MASK BIT(6)
#define BD71815_INT_LED_SCP_MASK BIT(7)
#define BD71815_INT_DCIN_RMV_MASK BIT(1)
#define BD71815_INT_CLPS_OUT_MASK BIT(2)
#define BD71815_INT_CLPS_IN_MASK BIT(3)
#define BD71815_INT_DCIN_OVP_RES_MASK BIT(4)
#define BD71815_INT_DCIN_OVP_DET_MASK BIT(5)
#define BD71815_INT_DCIN_MON_RES_MASK BIT(0)
#define BD71815_INT_DCIN_MON_DET_MASK BIT(1)
#define BD71815_INT_WDOG_MASK BIT(6)
#define BD71815_INT_VSYS_UV_RES_MASK BIT(0)
#define BD71815_INT_VSYS_UV_DET_MASK BIT(1)
#define BD71815_INT_VSYS_LOW_RES_MASK BIT(2)
#define BD71815_INT_VSYS_LOW_DET_MASK BIT(3)
#define BD71815_INT_VSYS_MON_RES_MASK BIT(6)
#define BD71815_INT_VSYS_MON_DET_MASK BIT(7)
#define BD71815_INT_CHG_WDG_TEMP_MASK BIT(2)
#define BD71815_INT_CHG_WDG_TIME_MASK BIT(3)
#define BD71815_INT_CHG_RECHARGE_RES_MASK BIT(4)
#define BD71815_INT_CHG_RECHARGE_DET_MASK BIT(5)
#define BD71815_INT_CHG_RANGED_TEMP_TRANSITION_MASK BIT(6)
#define BD71815_INT_CHG_STATE_TRANSITION_MASK BIT(7)
#define BD71815_INT_BAT_TEMP_NORMAL_MASK BIT(0)
#define BD71815_INT_BAT_TEMP_ERANGE_MASK BIT(1)
#define BD71815_INT_BAT_REMOVED_MASK BIT(4)
#define BD71815_INT_BAT_DETECTED_MASK BIT(5)
#define BD71815_INT_THERM_REMOVED_MASK BIT(6)
#define BD71815_INT_THERM_DETECTED_MASK BIT(7)
#define BD71815_INT_BAT_DEAD_MASK BIT(1)
#define BD71815_INT_BAT_SHORTC_RES_MASK BIT(2)
#define BD71815_INT_BAT_SHORTC_DET_MASK BIT(3)
#define BD71815_INT_BAT_LOW_VOLT_RES_MASK BIT(4)
#define BD71815_INT_BAT_LOW_VOLT_DET_MASK BIT(5)
#define BD71815_INT_BAT_OVER_VOLT_RES_MASK BIT(6)
#define BD71815_INT_BAT_OVER_VOLT_DET_MASK BIT(7)
#define BD71815_INT_BAT_MON_RES_MASK BIT(0)
#define BD71815_INT_BAT_MON_DET_MASK BIT(1)
#define BD71815_INT_BAT_CC_MON1_MASK BIT(0)
#define BD71815_INT_BAT_CC_MON2_MASK BIT(1)
#define BD71815_INT_BAT_CC_MON3_MASK BIT(2)
#define BD71815_INT_BAT_OVER_CURR_1_RES_MASK BIT(0)
#define BD71815_INT_BAT_OVER_CURR_1_DET_MASK BIT(1)
#define BD71815_INT_BAT_OVER_CURR_2_RES_MASK BIT(2)
#define BD71815_INT_BAT_OVER_CURR_2_DET_MASK BIT(3)
#define BD71815_INT_BAT_OVER_CURR_3_RES_MASK BIT(4)
#define BD71815_INT_BAT_OVER_CURR_3_DET_MASK BIT(5)
#define BD71815_INT_TEMP_BAT_LOW_RES_MASK BIT(0)
#define BD71815_INT_TEMP_BAT_LOW_DET_MASK BIT(1)
#define BD71815_INT_TEMP_BAT_HI_RES_MASK BIT(2)
#define BD71815_INT_TEMP_BAT_HI_DET_MASK BIT(3)
#define BD71815_INT_TEMP_CHIP_OVER_125_RES_MASK BIT(4)
#define BD71815_INT_TEMP_CHIP_OVER_125_DET_MASK BIT(5)
#define BD71815_INT_TEMP_CHIP_OVER_VF_RES_MASK BIT(6)
#define BD71815_INT_TEMP_CHIP_OVER_VF_DET_MASK BIT(7)
#define BD71815_INT_RTC0_MASK BIT(0)
#define BD71815_INT_RTC1_MASK BIT(1)
#define BD71815_INT_RTC2_MASK BIT(2)
/* BD71815_REG_CC_CTRL bits */
#define CCNTRST 0x80
#define CCNTENB 0x40
#define CCCALIB 0x20
/* BD71815_REG_CC_CURCD */
#define CURDIR_Discharging 0x8000
/* BD71815_REG_VM_SA_IBAT */
#define IBAT_SA_DIR_Discharging 0x8000
/* BD71815_REG_REX_CTRL_1 bits */
#define REX_CLR BIT(4)
/* BD71815_REG_REX_CTRL_1 bits */
#define REX_PMU_STATE_MASK BIT(2)
/* BD71815_REG_LED_CTRL bits */
#define CHGDONE_LED_EN BIT(4)
#endif /* __LINUX_MFD_BD71815_H */

View File

@ -151,6 +151,9 @@ enum {
#define BD71828_REG_GPIO_CTRL3 0x49
#define BD71828_REG_IO_STAT 0xed
/* clk */
#define BD71828_REG_OUT32K 0x4b
/* RTC */
#define BD71828_REG_RTC_SEC 0x4c
#define BD71828_REG_RTC_MINUTE 0x4d

View File

@ -310,17 +310,4 @@ enum {
BD718XX_PWRBTN_LONG_PRESS_15S
};
struct bd718xx {
/*
* Please keep this as the first member here as some
* drivers (clk) supporting more than one chip may only know this
* generic struct 'struct rohm_regmap_dev' and assume it is
* the first chunk of parent device's private data.
*/
struct rohm_regmap_dev chip;
int chip_irq;
struct regmap_irq_chip_data *irq_data;
};
#endif /* __LINUX_MFD_BD718XX_H__ */

View File

@ -0,0 +1,140 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2021 ROHM Semiconductors */
#ifndef __LINUX_MFD_BD957X_H__
#define __LINUX_MFD_BD957X_H__
enum {
BD957X_VD50,
BD957X_VD18,
BD957X_VDDDR,
BD957X_VD10,
BD957X_VOUTL1,
BD957X_VOUTS1,
};
/*
* The BD9576 has own IRQ 'blocks' for:
* - I2C/thermal,
* - Over voltage protection
* - Short-circuit protection
* - Over current protection
* - Over voltage detection
* - Under voltage detection
* - Under voltage protection
* - 'system interrupt'.
*
* Each of the blocks have a status register giving more accurate IRQ source
* information - for example which of the regulators have over-voltage.
*
* On top of this, there is "main IRQ" status register where each bit indicates
* which of sub-blocks have active IRQs. Fine. That would fit regmap-irq main
* status handling. Except that:
* - Only some sub-IRQs can be masked.
* - The IRQ informs us about fault-condition, not when fault state changes.
* The IRQ line it is kept asserted until the detected condition is acked
* AND cleared in HW. This is annoying for IRQs like the one informing high
* temperature because if IRQ is not disabled it keeps the CPU in IRQ
* handling loop.
*
* For now we do just use the main-IRQ register as source for our IRQ
* information and bind the regmap-irq to this. We leave fine-grained sub-IRQ
* register handling to handlers in sub-devices. The regulator driver shall
* read which regulators are source for problem - or if the detected error is
* regulator temperature error. The sub-drivers do also handle masking of "sub-
* IRQs" if this is supported/needed.
*
* To overcome the problem with HW keeping IRQ asserted we do call
* disable_irq_nosync() from sub-device handler and add a delayed work to
* re-enable IRQ roughly 1 second later. This should keep our CPU out of
* busy-loop.
*/
#define IRQS_SILENT_MS 1000
enum {
BD9576_INT_THERM,
BD9576_INT_OVP,
BD9576_INT_SCP,
BD9576_INT_OCP,
BD9576_INT_OVD,
BD9576_INT_UVD,
BD9576_INT_UVP,
BD9576_INT_SYS,
};
#define BD957X_REG_SMRB_ASSERT 0x15
#define BD957X_REG_PMIC_INTERNAL_STAT 0x20
#define BD957X_REG_INT_THERM_STAT 0x23
#define BD957X_REG_INT_THERM_MASK 0x24
#define BD957X_REG_INT_OVP_STAT 0x25
#define BD957X_REG_INT_SCP_STAT 0x26
#define BD957X_REG_INT_OCP_STAT 0x27
#define BD957X_REG_INT_OVD_STAT 0x28
#define BD957X_REG_INT_UVD_STAT 0x29
#define BD957X_REG_INT_UVP_STAT 0x2a
#define BD957X_REG_INT_SYS_STAT 0x2b
#define BD957X_REG_INT_SYS_MASK 0x2c
#define BD957X_REG_INT_MAIN_STAT 0x30
#define BD957X_REG_INT_MAIN_MASK 0x31
#define UVD_IRQ_VALID_MASK 0x6F
#define OVD_IRQ_VALID_MASK 0x2F
#define BD957X_MASK_INT_MAIN_THERM BIT(0)
#define BD957X_MASK_INT_MAIN_OVP BIT(1)
#define BD957X_MASK_INT_MAIN_SCP BIT(2)
#define BD957X_MASK_INT_MAIN_OCP BIT(3)
#define BD957X_MASK_INT_MAIN_OVD BIT(4)
#define BD957X_MASK_INT_MAIN_UVD BIT(5)
#define BD957X_MASK_INT_MAIN_UVP BIT(6)
#define BD957X_MASK_INT_MAIN_SYS BIT(7)
#define BD957X_MASK_INT_ALL 0xff
#define BD957X_REG_WDT_CONF 0x16
#define BD957X_REG_POW_TRIGGER1 0x41
#define BD957X_REG_POW_TRIGGER2 0x42
#define BD957X_REG_POW_TRIGGER3 0x43
#define BD957X_REG_POW_TRIGGER4 0x44
#define BD957X_REG_POW_TRIGGERL1 0x45
#define BD957X_REG_POW_TRIGGERS1 0x46
#define BD957X_REGULATOR_EN_MASK 0xff
#define BD957X_REGULATOR_DIS_VAL 0xff
#define BD957X_VSEL_REG_MASK 0xff
#define BD957X_MASK_VOUT1_TUNE 0x87
#define BD957X_MASK_VOUT2_TUNE 0x87
#define BD957X_MASK_VOUT3_TUNE 0x1f
#define BD957X_MASK_VOUT4_TUNE 0x1f
#define BD957X_MASK_VOUTL1_TUNE 0x87
#define BD957X_REG_VOUT1_TUNE 0x50
#define BD957X_REG_VOUT2_TUNE 0x53
#define BD957X_REG_VOUT3_TUNE 0x56
#define BD957X_REG_VOUT4_TUNE 0x59
#define BD957X_REG_VOUTL1_TUNE 0x5c
#define BD9576_REG_VOUT1_OVD 0x51
#define BD9576_REG_VOUT1_UVD 0x52
#define BD9576_REG_VOUT2_OVD 0x54
#define BD9576_REG_VOUT2_UVD 0x55
#define BD9576_REG_VOUT3_OVD 0x57
#define BD9576_REG_VOUT3_UVD 0x58
#define BD9576_REG_VOUT4_OVD 0x5a
#define BD9576_REG_VOUT4_UVD 0x5b
#define BD9576_REG_VOUTL1_OVD 0x5d
#define BD9576_REG_VOUTL1_UVD 0x5e
#define BD9576_MASK_XVD 0x7f
#define BD9576_REG_VOUT1S_OCW 0x5f
#define BD9576_REG_VOUT1S_OCP 0x60
#define BD9576_MASK_VOUT1S_OCW 0x3f
#define BD9576_MASK_VOUT1S_OCP 0x3f
#define BD957X_MAX_REGISTER 0x61
#endif

View File

@ -8,12 +8,15 @@
#include <linux/regulator/driver.h>
enum rohm_chip_type {
ROHM_CHIP_TYPE_BD71837 = 0,
ROHM_CHIP_TYPE_BD71847,
ROHM_CHIP_TYPE_BD70528,
ROHM_CHIP_TYPE_BD71828,
ROHM_CHIP_TYPE_BD9571,
ROHM_CHIP_TYPE_BD9573,
ROHM_CHIP_TYPE_BD9574,
ROHM_CHIP_TYPE_BD9576,
ROHM_CHIP_TYPE_BD70528,
ROHM_CHIP_TYPE_BD71815,
ROHM_CHIP_TYPE_BD71828,
ROHM_CHIP_TYPE_BD71837,
ROHM_CHIP_TYPE_BD71847,
ROHM_CHIP_TYPE_AMOUNT
};
@ -26,7 +29,8 @@ struct rohm_regmap_dev {
#define ROHM_DVS_LEVEL_IDLE BIT(1)
#define ROHM_DVS_LEVEL_SUSPEND BIT(2)
#define ROHM_DVS_LEVEL_LPSR BIT(3)
#define ROHM_DVS_LEVEL_VALID_AMOUNT 4
#define ROHM_DVS_LEVEL_SNVS BIT(4)
#define ROHM_DVS_LEVEL_VALID_AMOUNT 5
#define ROHM_DVS_LEVEL_UNKNOWN 0
/**
@ -65,6 +69,9 @@ struct rohm_dvs_config {
unsigned int lpsr_reg;
unsigned int lpsr_mask;
unsigned int lpsr_on_mask;
unsigned int snvs_reg;
unsigned int snvs_mask;
unsigned int snvs_on_mask;
};
#if IS_ENABLED(CONFIG_REGULATOR_ROHM)

View File

@ -781,8 +781,6 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base);
#define TWL4030_VAUX3_DEV_GRP 0x1F
#define TWL4030_VAUX3_DEDICATED 0x22
static inline int twl4030charger_usb_en(int enable) { return 0; }
/*----------------------------------------------------------------------*/
/* Linux-specific regulator identifiers ... for now, we only support

View File

@ -1,13 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright(c) 2014 Intel Corporation.
*/
#ifndef I2C_DESIGNWARE_H
#define I2C_DESIGNWARE_H
struct dw_i2c_platform_data {
unsigned int i2c_scl_freq;
};
#endif

View File

@ -290,7 +290,7 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
unsigned int mode;
/* Microphone detection can't use idle mode */
pm_runtime_get(info->dev);
pm_runtime_get_sync(info->dev);
if (info->detecting) {
ret = regulator_allow_bypass(info->micvdd, false);
@ -601,7 +601,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
struct arizona *arizona = info->arizona;
int id_gpio = arizona->pdata.hpdet_id_gpio;
unsigned int report = EXTCON_JACK_HEADPHONE;
int ret, reading;
int ret, reading, state;
bool mic = false;
mutex_lock(&info->lock);
@ -614,12 +614,11 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
}
/* If the cable was removed while measuring ignore the result */
ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret);
state = extcon_get_state(info->edev, EXTCON_MECHANICAL);
if (state < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n", state);
goto out;
} else if (!ret) {
} else if (!state) {
dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n");
goto done;
}
@ -667,7 +666,7 @@ done:
gpio_set_value_cansleep(id_gpio, 0);
/* If we have a mic then reenable MICDET */
if (mic || info->mic)
if (state && (mic || info->mic))
arizona_start_mic(info);
if (info->hpdet_active) {
@ -675,6 +674,8 @@ done:
info->hpdet_active = false;
}
/* Do not set hp_det done when the cable has been unplugged */
if (state)
info->hpdet_done = true;
out:
@ -694,7 +695,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
dev_dbg(arizona->dev, "Starting HPDET\n");
/* Make sure we keep the device enabled during the measurement */
pm_runtime_get(info->dev);
pm_runtime_get_sync(info->dev);
info->hpdet_active = true;
@ -1509,7 +1510,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
*/
info->micd_pol_gpio = gpiod_get_optional(arizona->dev,
"wlf,micd-pol",
GPIOD_OUT_LOW);
mode);
if (IS_ERR(info->micd_pol_gpio)) {
ret = PTR_ERR(info->micd_pol_gpio);
dev_err(arizona->dev,
@ -1759,25 +1760,6 @@ static int arizona_extcon_remove(struct platform_device *pdev)
bool change;
int ret;
ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_ENA, 0,
&change);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to disable micd on remove: %d\n",
ret);
} else if (change) {
regulator_disable(info->micvdd);
pm_runtime_put(info->dev);
}
gpiod_put(info->micd_pol_gpio);
pm_runtime_disable(&pdev->dev);
regmap_update_bits(arizona->regmap,
ARIZONA_MICD_CLAMP_CONTROL,
ARIZONA_MICD_CLAMP_MODE_MASK, 0);
if (info->micd_clamp) {
jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL;
@ -1793,10 +1775,31 @@ static int arizona_extcon_remove(struct platform_device *pdev)
arizona_free_irq(arizona, jack_irq_rise, info);
arizona_free_irq(arizona, jack_irq_fall, info);
cancel_delayed_work_sync(&info->hpdet_work);
cancel_delayed_work_sync(&info->micd_detect_work);
cancel_delayed_work_sync(&info->micd_timeout_work);
ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_ENA, 0,
&change);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to disable micd on remove: %d\n",
ret);
} else if (change) {
regulator_disable(info->micvdd);
pm_runtime_put(info->dev);
}
regmap_update_bits(arizona->regmap,
ARIZONA_MICD_CLAMP_CONTROL,
ARIZONA_MICD_CLAMP_MODE_MASK, 0);
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, 0);
arizona_clk32k_disable(arizona);
gpiod_put(info->micd_pol_gpio);
pm_runtime_disable(&pdev->dev);
return 0;
}