- New Drivers

- Add support for ROHM BD96801 Power Management IC
    - Add support for Cirrus Logic CS40L50 Haptic Driver with Waveform Memory
    - Add support for Marvell 88PM886 Power Management IC
 
  - New Device Support
    - Add support for Keyboard Backlight to ChromeOS Embedded Controller
    - Add support for LEDs to ChromeOS Embedded Controller
    - Add support for Charge Control to ChromeOS Embedded Controller
    - Add support for the HW Monitoring Service to ChromeOS Embedded Controller
    - Add support for AUXADCs to MediaTek MT635{7,8,9} Power Management ICs
 
  - New Functionality
    - Allow Syscon consumers to supply their own Regmaps on registration
 
  - Fix-ups
    - Constify/staticise applicable data structures
    - Remove superfluous/duplicated/unused sections
    - Device Tree binding adaptions/conversions/creation
    - Trivial; spelling, whitespace, coding-style adaptions
    - Utilise centrally provided helpers and macros to aid simplicity/duplication
    - Drop i2c_device_id::driver_data where the value is unused
    - Replace ACPI/DT firmware helpers with agnostic variants
    - Move over to GPIOD (descriptor-based) APIs
    - Annotate a bunch of __counted_by() cases
    - Straighten out some includes
 
  - Bug Fixes
    - Ensure potentially asserted recent lines are deasserted during initialisation
    - Avoid "<module>.ko is added to multiple modules" warnings
    - Supply a bunch of MODULE_DESCRIPTIONs to silence modpost warnings
    - Fix Wvoid-pointer-to-enum-cast warnings
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAmaWf6sACgkQUa+KL4f8
 d2HhAw//UMujhKk/IfzGck3RoaKH3H22oVpd98BpzJCZBKSpl9pGsumHCicBMVAK
 gp8SuwKNCAX+Fa/TubHz0xH6FWxLFXezh5DvO1t1DrPNokG+u4QPTfgMJ1IfBMHO
 w7aL74rtJEyWBeod4+qNVoq6KNDaWjiWQlxGQ+9IoSNmxSTL6pkYMqo935RnqhRr
 nm2TfSOIshk4tiO9tVA1ecCgjVwsG51803hypmd1AH6qBb7JsY6k1HWukLGaqUiV
 +57oQzCTPIRYJhYdca06xi4ZmPg2kmoYKlxqW5ExyM7Mxs9aZZzwwZ7929LKXC6o
 ebAPDc3auoww7B5mHbbVuBj0gDZKtfXpBRKSHLNtmhi0xmjnwZxQIumkpVGQALkI
 0TQffgYVU4O7IXsAZG9w5igyMzEo9SZJMyrfFaQ0iB3rx5bXuh4b6btfewAkyI1H
 +o3Yjymf4CR1trY9qnWCGWM/COQLIiGRhsk/RqGjy0xtpQo1Skx+AIkc6QD2zl6Y
 ohC0JzEWTQe7c1DOM3SLpNoCb/GbFpVi0RrXRVfRltPHpVb/r54Zlbo+PrCaC8FB
 EkU+86XbxGMh7hLtz5yhmnNCWKHQ6jbaFESwtZLo4d42CKvZaobL4xVCL56OntsH
 ikmTNG+X0mUAZiCwGgK5OhEVCAtCcjRtz1U93wgDBaz7Y39z+yM=
 =DSjk
 -----END PGP SIGNATURE-----

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

Pull MFD updates from Lee Jones:
 "New Drivers:
   - ROHM BD96801 Power Management IC
   - Cirrus Logic CS40L50 Haptic Driver with Waveform Memory
   - Marvell 88PM886 Power Management IC

  New Device Support:
   - Keyboard Backlight to ChromeOS Embedded Controller
   - LEDs to ChromeOS Embedded Controller
   - Charge Control to ChromeOS Embedded Controller
   - HW Monitoring Service to ChromeOS Embedded Controller
   - AUXADCs to MediaTek MT635{7,8,9} Power Management ICs

  New Functionality:
   - Allow Syscon consumers to supply their own Regmaps on registration

  Fix-ups:
   - Constify/staticise applicable data structures
   - Remove superfluous/duplicated/unused sections
   - Device Tree binding adaptions/conversions/creation
   - Trivial; spelling, whitespace, coding-style adaptions
   - Utilise centrally provided helpers and macros to aid
     simplicity/duplication
   - Drop i2c_device_id::driver_data where the value is unused
   - Replace ACPI/DT firmware helpers with agnostic variants
   - Move over to GPIOD (descriptor-based) APIs
   - Annotate a bunch of __counted_by() cases
   - Straighten out some includes

  Bug Fixes:
   - Ensure potentially asserted recent lines are deasserted during
     initialisation
   - Avoid "<module>.ko is added to multiple modules" warnings
   - Supply a bunch of MODULE_DESCRIPTIONs to silence modpost warnings
   - Fix Wvoid-pointer-to-enum-cast warnings"

* tag 'mfd-next-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (87 commits)
  mfd: timberdale: Attach device properties to TSC2007 board info
  mfd: tmio: Move header to platform_data
  mfd: tmio: Sanitize comments
  mfd: tmio: Update include files
  mmc: tmio/sdhi: Fix includes
  mfd: tmio: Remove obsolete io accessors
  mfd: tmio: Remove obsolete platform_data
  watchdog: bd96801_wdt: Add missing include for FIELD_*()
  dt-bindings: mfd: syscon: Add APM poweroff mailbox
  dt-bindings: mfd: syscon: Split and enforce documenting MFD children
  dt-bindings: mfd: rk817: Merge support for RK809
  dt-bindings: mfd: rk817: Fixup clocks and reference dai-common
  dt-bindings: mfd: syscon: Add TI's opp table compatible
  mfd: omap-usb-tll: Use struct_size to allocate tll
  dt-bindings: mfd: Explain lack of child dependency in simple-mfd
  dt-bindings: mfd: Dual licensing for st,stpmic1 bindings
  mfd: omap-usb-tll: Annotate struct usbtll_omap with __counted_by
  mfd: tps6594-core: Remove unneeded semicolon in tps6594_check_crc_mode()
  mfd: lm3533: Move to new GPIO descriptor-based APIs
  mfd: tps65912: Use devm helper functions to simplify probe
  ...
This commit is contained in:
Linus Torvalds 2024-07-17 17:42:20 -07:00
commit 1200af3ac1
143 changed files with 6730 additions and 1194 deletions

View File

@ -1,20 +0,0 @@
Amlogic Meson8 and Meson8b "analog top" registers:
--------------------------------------------------
The analog top registers contain information about the so-called
"metal revision" (which encodes the "minor version") of the SoC.
Required properties:
- reg: the register range of the analog top registers
- compatible: depending on the SoC this should be one of:
- "amlogic,meson8-analog-top"
- "amlogic,meson8b-analog-top"
along with "syscon"
Example:
analog_top: analog-top@81a8 {
compatible = "amlogic,meson8-analog-top", "syscon";
reg = <0x81a8 0x14>;
};

View File

@ -1,17 +0,0 @@
Amlogic Meson6/Meson8/Meson8b assist registers:
-----------------------------------------------
The assist registers contain basic information about the SoC,
for example the encoded SoC part number.
Required properties:
- reg: the register range of the assist registers
- compatible: should be "amlogic,meson-mx-assist" along with "syscon"
Example:
assist: assist@7c00 {
compatible = "amlogic,meson-mx-assist", "syscon";
reg = <0x7c00 0x200>;
};

View File

@ -1,17 +0,0 @@
Amlogic Meson6/Meson8/Meson8b bootrom:
--------------------------------------
The bootrom register area can be used to access SoC specific
information, such as the "misc version".
Required properties:
- reg: the register range of the bootrom registers
- compatible: should be "amlogic,meson-mx-bootrom" along with "syscon"
Example:
bootrom: bootrom@d9040000 {
compatible = "amlogic,meson-mx-bootrom", "syscon";
reg = <0xd9040000 0x10000>;
};

View File

@ -1,18 +0,0 @@
Amlogic Meson8 and Meson8b power-management-unit:
-------------------------------------------------
The pmu is used to turn off and on different power domains of the SoCs
This includes the power to the CPU cores.
Required node properties:
- compatible value : depending on the SoC this should be one of:
"amlogic,meson8-pmu"
"amlogic,meson8b-pmu"
- reg : physical base address and the size of the registers window
Example:
pmu@c81000e4 {
compatible = "amlogic,meson8b-pmu", "syscon";
reg = <0xc81000e0 0x18>;
};

View File

@ -41,35 +41,6 @@ Examples:
reg = <0xffffe800 0x200>;
};
RAMC PHY Controller required properties:
- compatible: Should be "microchip,sama7g5-ddr3phy", "syscon"
- reg: Should contain registers location and length
Example:
ddr3phy: ddr3phy@e3804000 {
compatible = "microchip,sama7g5-ddr3phy", "syscon";
reg = <0xe3804000 0x1000>;
};
Special Function Registers (SFR)
Special Function Registers (SFR) manage specific aspects of the integrated
memory, bridge implementations, processor and other functionality not controlled
elsewhere.
required properties:
- compatible: Should be "atmel,<chip>-sfr", "syscon" or
"atmel,<chip>-sfrbu", "syscon"
<chip> can be "sama5d3", "sama5d4" or "sama5d2".
It also can be "microchip,sam9x60-sfr", "syscon".
- reg: Should contain registers location and length
sfr@f0038000 {
compatible = "atmel,sama5d3-sfr", "syscon";
reg = <0xf0038000 0x60>;
};
Security Module (SECUMOD)
The Security Module macrocell provides all necessary secure functions to avoid

View File

@ -7,22 +7,6 @@ ARTPEC-6 ARM SoC
Required root node properties:
- compatible = "axis,artpec6";
ARTPEC-6 System Controller
--------------------------
The ARTPEC-6 has a system controller with mixed functions controlling DMA, PCIe
and resets.
Required properties:
- compatible: "axis,artpec6-syscon", "syscon"
- reg: Address and length of the register bank.
Example:
syscon {
compatible = "axis,artpec6-syscon", "syscon";
reg = <0xf8000000 0x48>;
};
ARTPEC-6 Development board:
---------------------------
Required root node properties:

View File

@ -27,16 +27,6 @@ Properties:
- reg : Offset and length of the register set for the device
* Alpine System-Fabric Service Registers
The System-Fabric Service Registers allow various operation on CPU and
system fabric, like powering CPUs off.
Properties:
- compatible : Should contain "al,alpine-sysfabric-service" and "syscon".
- reg : Offset and length of the register set for the device
Example:
cpus {

View File

@ -1,14 +0,0 @@
Freescale Vybrid Miscellaneous System Control - CPU Configuration
The MSCM IP contains multiple sub modules, this binding describes the first
block of registers which contains CPU configuration information.
Required properties:
- compatible: "fsl,vf610-mscm-cpucfg", "syscon"
- reg: the register range of the MSCM CPU configuration registers
Example:
mscm_cpucfg: cpucfg@40001000 {
compatible = "fsl,vf610-mscm-cpucfg", "syscon";
reg = <0x40001000 0x800>;
}

View File

@ -5,18 +5,3 @@ Boards with a Marvell Dove SoC shall have the following properties:
Required root node property:
- compatible: must contain "marvell,dove";
* Global Configuration registers
Global Configuration registers of Dove SoC are shared by a syscon node.
Required properties:
- compatible: must contain "marvell,dove-global-config" and "syscon".
- reg: base address and size of the Global Configuration registers.
Example:
gconf: global-config@e802c {
compatible = "marvell,dove-global-config", "syscon";
reg = <0xe802c 0x14>;
};

View File

@ -1,9 +0,0 @@
SPEAr Misc configuration
===========================
SPEAr SOCs have some miscellaneous registers which are used to configure
few properties of different peripheral controllers.
misc node required properties:
- compatible Should be "st,spear1340-misc", "syscon".
- reg: Address range of misc space up to 8K

View File

@ -1,20 +0,0 @@
* Device tree bindings for Texas Instruments keystone pll controller
The main pll controller used to drive theC66x CorePacs, the switch fabric,
and a majority of the peripheral clocks (all but the ARM CorePacs, DDR3 and
the NETCP modules) requires a PLL Controller to manage the various clock
divisions, gating, and synchronization.
Required properties:
- compatible: "ti,keystone-pllctrl", "syscon"
- reg: contains offset/length value for pll controller
registers space.
Example:
pllctrl: pll-controller@02310000 {
compatible = "ti,keystone-pllctrl", "syscon";
reg = <0x02310000 0x200>;
};

View File

@ -0,0 +1,68 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/cirrus,cs40l50.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cirrus Logic CS40L50 Advanced Haptic Driver
maintainers:
- James Ogletree <jogletre@opensource.cirrus.com>
description:
CS40L50 is a haptic driver with waveform memory,
integrated DSP, and closed-loop algorithms.
properties:
compatible:
enum:
- cirrus,cs40l50
reg:
maxItems: 1
interrupts:
maxItems: 1
reset-gpios:
maxItems: 1
vdd-a-supply:
description: Power supply for internal analog circuits.
vdd-p-supply:
description: Power supply for always-on circuits.
vdd-io-supply:
description: Power supply for digital input/output.
vdd-b-supply:
description: Power supply for the boost converter.
required:
- compatible
- reg
- interrupts
- reset-gpios
- vdd-io-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
haptic-driver@34 {
compatible = "cirrus,cs40l50";
reg = <0x34>;
interrupt-parent = <&gpio>;
interrupts = <113 IRQ_TYPE_LEVEL_LOW>;
reset-gpios = <&gpio 112 GPIO_ACTIVE_LOW>;
vdd-io-supply = <&vreg>;
};
};

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/marvell,88pm886-a1.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Marvell 88PM886 PMIC core
maintainers:
- Karel Balej <balejk@matfyz.cz>
description:
Marvell 88PM886 is a PMIC providing several functions such as onkey,
regulators or battery and charger.
properties:
compatible:
const: marvell,88pm886-a1
reg:
maxItems: 1
interrupts:
maxItems: 1
wakeup-source: true
regulators:
type: object
additionalProperties: false
patternProperties:
"^(ldo(1[0-6]|[1-9])|buck[1-5])$":
type: object
$ref: /schemas/regulator/regulator.yaml#
description: LDO or buck regulator.
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@30 {
compatible = "marvell,88pm886-a1";
reg = <0x30>;
interrupts = <0 4 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&gic>;
wakeup-source;
regulators {
ldo2: ldo2 {
regulator-min-microvolt = <3100000>;
regulator-max-microvolt = <3300000>;
};
ldo15: ldo15 {
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
buck2: buck2 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
};
};
};
};
...

View File

@ -22,8 +22,10 @@ properties:
- mediatek,mt8173-scpsys
- mediatek,mt8183-scpsys
- mediatek,mt8186-scpsys
- mediatek,mt8188-scpsys
- mediatek,mt8192-scpsys
- mediatek,mt8195-scpsys
- mediatek,mt8365-scpsys
- const: syscon
- const: simple-mfd

View File

@ -17,13 +17,14 @@ A typical MFD can be:
Optional properties:
- compatible : "simple-mfd" - this signifies that the operating system should
consider all subnodes of the MFD device as separate devices akin to how
"simple-bus" indicates when to see subnodes as children for a simple
memory-mapped bus. For more complex devices, when the nexus driver has to
probe registers to figure out what child devices exist etc, this should not
be used. In the latter case the child devices will be determined by the
operating system.
- compatible : "simple-mfd" - this signifies that the operating system
should consider all subnodes of the MFD device as separate and independent
devices, so not needing any resources to be provided by the parent device.
Similarly to how "simple-bus" indicates when to see subnodes as children for
a simple memory-mapped bus.
For more complex devices, when the nexus driver has to probe registers to
figure out what child devices exist etc, this should not be used. In the
latter case the child devices will be determined by the operating system.
- ranges: Describes the address mapping relationship to the parent. Should set
the child's base address to 0, the physical address within parent's address

View File

@ -19,110 +19,136 @@ properties:
const: qcom,pm8008
reg:
description:
I2C slave address.
maxItems: 1
interrupts:
maxItems: 1
description: Parent interrupt.
reset-gpios:
maxItems: 1
vdd-l1-l2-supply: true
vdd-l3-l4-supply: true
vdd-l5-supply: true
vdd-l6-supply: true
vdd-l7-supply: true
gpio-controller: true
"#gpio-cells":
const: 2
gpio-ranges:
maxItems: 1
interrupt-controller: true
"#interrupt-cells":
const: 2
description: |
The first cell is the IRQ number, the second cell is the IRQ trigger
flag. All interrupts are listed in include/dt-bindings/mfd/qcom-pm8008.h.
interrupt-controller: true
"#address-cells":
const: 1
"#size-cells":
"#thermal-sensor-cells":
const: 0
patternProperties:
"^gpio@[0-9a-f]+$":
pinctrl:
type: object
description: |
The GPIO peripheral. This node may be specified twice, one for each GPIO.
properties:
compatible:
items:
- const: qcom,pm8008-gpio
- const: qcom,spmi-gpio
reg:
description: Peripheral address of one of the two GPIO peripherals.
maxItems: 1
gpio-controller: true
gpio-ranges:
maxItems: 1
interrupt-controller: true
"#interrupt-cells":
const: 2
"#gpio-cells":
const: 2
required:
- compatible
- reg
- gpio-controller
- interrupt-controller
- "#gpio-cells"
- gpio-ranges
- "#interrupt-cells"
additionalProperties: false
patternProperties:
"-state$":
type: object
allOf:
- $ref: /schemas/pinctrl/pinmux-node.yaml
- $ref: /schemas/pinctrl/pincfg-node.yaml
properties:
pins:
items:
pattern: "^gpio[12]$"
function:
items:
- enum:
- normal
required:
- pins
- function
additionalProperties: false
regulators:
type: object
additionalProperties: false
patternProperties:
"^ldo[1-7]$":
type: object
$ref: /schemas/regulator/regulator.yaml#
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
- "#address-cells"
- "#size-cells"
- vdd-l1-l2-supply
- vdd-l3-l4-supply
- vdd-l5-supply
- vdd-l6-supply
- vdd-l7-supply
- gpio-controller
- "#gpio-cells"
- gpio-ranges
- interrupt-controller
- "#interrupt-cells"
- "#thermal-sensor-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/mfd/qcom-pm8008.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@8 {
pm8008: pmic@8 {
compatible = "qcom,pm8008";
reg = <0x8>;
#address-cells = <1>;
#size-cells = <0>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&tlmm>;
interrupts = <32 IRQ_TYPE_EDGE_RISING>;
pm8008_gpios: gpio@c000 {
compatible = "qcom,pm8008-gpio", "qcom,spmi-gpio";
reg = <0xc000>;
gpio-controller;
gpio-ranges = <&pm8008_gpios 0 0 2>;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reset-gpios = <&tlmm 42 GPIO_ACTIVE_LOW>;
vdd-l1-l2-supply = <&vreg_s8b_1p2>;
vdd-l3-l4-supply = <&vreg_s1b_1p8>;
vdd-l5-supply = <&vreg_bob>;
vdd-l6-supply = <&vreg_bob>;
vdd-l7-supply = <&vreg_bob>;
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pm8008 0 0 2>;
interrupt-controller;
#interrupt-cells = <2>;
#thermal-sensor-cells = <0>;
pinctrl {
gpio-keys-state {
pins = "gpio1";
function = "normal";
};
};
regulators {
ldo1 {
regulator-name = "vreg_l1";
regulator-min-microvolt = <950000>;
regulator-max-microvolt = <1300000>;
};
};
};
};

View File

@ -75,6 +75,7 @@ properties:
- qcom,pma8084
- qcom,pmc8180
- qcom,pmc8180c
- qcom,pmc8380
- qcom,pmd9635
- qcom,pmi632
- qcom,pmi8950
@ -95,6 +96,7 @@ properties:
- qcom,pmx65
- qcom,pmx75
- qcom,smb2351
- qcom,smb2360
- const: qcom,spmi-pmic
reg:

View File

@ -1,288 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/rockchip,rk809.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RK809 Power Management Integrated Circuit
maintainers:
- Chris Zhong <zyw@rock-chips.com>
- Zhang Qing <zhangqing@rock-chips.com>
description: |
Rockchip RK809 series PMIC. This device consists of an i2c controlled MFD
that includes regulators, an RTC, and power button.
properties:
compatible:
enum:
- rockchip,rk809
reg:
maxItems: 1
interrupts:
maxItems: 1
'#clock-cells':
description: |
See <dt-bindings/clock/rockchip,rk808.h> for clock IDs.
minimum: 0
maximum: 1
clock-output-names:
description:
From common clock binding to override the default output clock name.
rockchip,system-power-controller:
type: boolean
deprecated: true
description:
Telling whether or not this PMIC is controlling the system power.
system-power-controller: true
wakeup-source:
type: boolean
description:
Device can be used as a wakeup source.
vcc1-supply:
description:
The input supply for DCDC_REG1.
vcc2-supply:
description:
The input supply for DCDC_REG2.
vcc3-supply:
description:
The input supply for DCDC_REG3.
vcc4-supply:
description:
The input supply for DCDC_REG4.
vcc5-supply:
description:
The input supply for LDO_REG1, LDO_REG2, and LDO_REG3.
vcc6-supply:
description:
The input supply for LDO_REG4, LDO_REG5, and LDO_REG6.
vcc7-supply:
description:
The input supply for LDO_REG7, LDO_REG8, and LDO_REG9.
vcc8-supply:
description:
The input supply for SWITCH_REG1.
vcc9-supply:
description:
The input supply for DCDC_REG5 and SWITCH_REG2.
regulators:
type: object
patternProperties:
"^(LDO_REG[1-9]|DCDC_REG[1-5]|SWITCH_REG[1-2])$":
type: object
$ref: /schemas/regulator/regulator.yaml#
unevaluatedProperties: false
unevaluatedProperties: false
allOf:
- if:
properties:
'#clock-cells':
const: 0
then:
properties:
clock-output-names:
maxItems: 1
else:
properties:
clock-output-names:
maxItems: 2
required:
- compatible
- reg
- interrupts
- "#clock-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
rk808: pmic@1b {
compatible = "rockchip,rk808";
reg = <0x1b>;
#clock-cells = <1>;
clock-output-names = "xin32k", "rk808-clkout2";
interrupt-parent = <&gpio3>;
interrupts = <10 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pmic_int_l_pin>;
rockchip,system-power-controller;
wakeup-source;
vcc1-supply = <&vcc_sysin>;
vcc2-supply = <&vcc_sysin>;
vcc3-supply = <&vcc_sysin>;
vcc4-supply = <&vcc_sysin>;
vcc6-supply = <&vcc_sysin>;
vcc7-supply = <&vcc_sysin>;
vcc8-supply = <&vcc3v3_sys>;
vcc9-supply = <&vcc_sysin>;
vcc10-supply = <&vcc_sysin>;
vcc11-supply = <&vcc_sysin>;
vcc12-supply = <&vcc3v3_sys>;
regulators {
vdd_center: DCDC_REG1 {
regulator-name = "vdd_center";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1350000>;
regulator-ramp-delay = <6001>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vdd_cpu_l: DCDC_REG2 {
regulator-name = "vdd_cpu_l";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1350000>;
regulator-ramp-delay = <6001>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_ddr: DCDC_REG3 {
regulator-name = "vcc_ddr";
regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-on-in-suspend;
};
};
vcc_1v8: vcc_wl: DCDC_REG4 {
regulator-name = "vcc_1v8";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <1800000>;
};
};
vcc1v8_pmupll: LDO_REG3 {
regulator-name = "vcc1v8_pmupll";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <1800000>;
};
};
vcc_sdio: LDO_REG4 {
regulator-name = "vcc_sdio";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3000000>;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <3000000>;
};
};
vcca3v0_codec: LDO_REG5 {
regulator-name = "vcca3v0_codec";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_1v5: LDO_REG6 {
regulator-name = "vcc_1v5";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1500000>;
regulator-max-microvolt = <1500000>;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <1500000>;
};
};
vcca1v8_codec: LDO_REG7 {
regulator-name = "vcca1v8_codec";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_3v0: LDO_REG8 {
regulator-name = "vcc_3v0";
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <3000000>;
};
};
vcc3v3_s3: SWITCH_REG1 {
regulator-name = "vcc3v3_s3";
regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc3v3_s0: SWITCH_REG2 {
regulator-name = "vcc3v3_s0";
regulator-always-on;
regulator-boot-on;
regulator-state-mem {
regulator-off-in-suspend;
};
};
};
};
};

View File

@ -4,20 +4,21 @@
$id: http://devicetree.org/schemas/mfd/rockchip,rk817.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RK817 Power Management Integrated Circuit
title: RK809/RK817 Power Management Integrated Circuit
maintainers:
- Chris Zhong <zyw@rock-chips.com>
- Zhang Qing <zhangqing@rock-chips.com>
description: |
Rockchip RK817 series PMIC. This device consists of an i2c controlled MFD
that includes regulators, an RTC, a power button, an audio codec, and a
battery charger manager.
Rockchip RK809/RK817 series PMIC. This device consists of an i2c controlled
MFD that includes regulators, an RTC, a power button and an audio codec.
The RK817 variant also provides a battery charger manager.
properties:
compatible:
enum:
- rockchip,rk809
- rockchip,rk817
reg:
@ -32,6 +33,13 @@ properties:
minimum: 0
maximum: 1
clocks:
maxItems: 1
clock-names:
items:
- const: mclk
clock-output-names:
description:
From common clock binding to override the default output clock name.
@ -42,6 +50,9 @@ properties:
description:
Telling whether or not this PMIC is controlling the system power.
'#sound-dai-cells':
const: 0
system-power-controller: true
wakeup-source:
@ -79,41 +90,22 @@ properties:
vcc8-supply:
description:
The input supply for BOOST.
The input supply for BOOST on RK817, or for SWITCH_REG2 on RK809.
vcc9-supply:
description:
The input supply for OTG_SWITCH.
The input supply for OTG_SWITCH on RK817,
or for DCDC_REG5 and SWITCH_REG1 on RK809.
regulators:
type: object
patternProperties:
"^(LDO_REG[1-9]|DCDC_REG[1-4]|BOOST|OTG_SWITCH)$":
type: object
"^(LDO_REG[1-9]|DCDC_REG[1-5]|BOOST|OTG_SWITCH|SWITCH_REG[1-2])$":
$ref: /schemas/regulator/regulator.yaml
unevaluatedProperties: false
$ref: /schemas/regulator/regulator.yaml#
unevaluatedProperties: false
clocks:
description:
The input clock for the audio codec.
clock-names:
description:
The clock name for the codec clock.
items:
- const: mclk
'#sound-dai-cells':
description:
Needed for the interpretation of sound dais.
const: 0
additionalProperties: false
codec:
description: |
The child node for the codec to hold additional properties. If no
additional properties are required for the codec, this node can be
omitted.
type: object
additionalProperties: false
properties:
@ -123,9 +115,6 @@ properties:
Describes if the microphone uses differential mode.
charger:
description: |
The child node for the charger to hold additional properties. If a
battery is not in use, this node can be omitted.
type: object
$ref: /schemas/power/supply/power-supply.yaml
@ -168,6 +157,7 @@ properties:
additionalProperties: false
allOf:
- $ref: /schemas/sound/dai-common.yaml#
- if:
properties:
'#clock-cells':
@ -183,6 +173,22 @@ allOf:
clock-output-names:
maxItems: 2
- if:
properties:
compatible:
contains:
const: rockchip,rk817
then:
properties:
regulators:
patternProperties:
"^(DCDC_REG5|SWITCH_REG[1-2])$": false
else:
properties:
regulators:
patternProperties:
"^(BOOST|OTG_SWITCH)$": false
required:
- compatible
- reg

View File

@ -0,0 +1,173 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/rohm,bd96801-pmic.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BD96801 Scalable Power Management Integrated Circuit
maintainers:
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
description:
BD96801 is an automotive grade single-chip power management IC.
It integrates 4 buck converters and 3 LDOs with safety features like
over-/under voltage and over current detection and a watchdog.
properties:
compatible:
const: rohm,bd96801
reg:
maxItems: 1
interrupts:
description:
The PMIC provides intb and errb IRQ lines. The errb IRQ line is used
for fatal IRQs which will cause the PMIC to shut down power outputs.
In many systems this will shut down the SoC contolling the PMIC and
connecting/handling the errb can be omitted. However, there are cases
where the SoC is not powered by the PMIC or has a short time backup
energy to handle shutdown of critical hardware. In that case it may be
useful to connect the errb and handle errb events.
minItems: 1
maxItems: 2
interrupt-names:
minItems: 1
items:
- enum: [intb, errb]
- const: errb
rohm,hw-timeout-ms:
description:
Watchdog timeout value(s). First walue is timeout limit. Second value is
optional value for 'too early' watchdog ping if window timeout mode is
to be used.
minItems: 1
maxItems: 2
rohm,wdg-action:
description:
Whether the watchdog failure must turn off the regulator power outputs or
just toggle the INTB line.
enum:
- prstb
- intb-only
timeout-sec:
maxItems: 2
regulators:
$ref: /schemas/regulator/rohm,bd96801-regulator.yaml
description:
List of child nodes that specify the regulators.
required:
- compatible
- reg
- interrupts
- interrupt-names
- regulators
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@60 {
reg = <0x60>;
compatible = "rohm,bd96801";
interrupt-parent = <&gpio1>;
interrupts = <29 IRQ_TYPE_LEVEL_LOW>, <6 IRQ_TYPE_LEVEL_LOW>;
interrupt-names = "intb", "errb";
regulators {
buck1 {
regulator-name = "buck1";
regulator-ramp-delay = <1250>;
/* 0.5V min INITIAL - 150 mV tune */
regulator-min-microvolt = <350000>;
/* 3.3V + 150mV tune */
regulator-max-microvolt = <3450000>;
/* These can be set only when PMIC is in STBY */
rohm,initial-voltage-microvolt = <500000>;
regulator-ov-error-microvolt = <230000>;
regulator-uv-error-microvolt = <230000>;
regulator-temp-protection-kelvin = <1>;
regulator-temp-warn-kelvin = <0>;
};
buck2 {
regulator-name = "buck2";
regulator-min-microvolt = <350000>;
regulator-max-microvolt = <3450000>;
rohm,initial-voltage-microvolt = <3000000>;
regulator-ov-error-microvolt = <18000>;
regulator-uv-error-microvolt = <18000>;
regulator-temp-protection-kelvin = <1>;
regulator-temp-warn-kelvin = <1>;
};
buck3 {
regulator-name = "buck3";
regulator-min-microvolt = <350000>;
regulator-max-microvolt = <3450000>;
rohm,initial-voltage-microvolt = <600000>;
regulator-ov-warn-microvolt = <18000>;
regulator-uv-warn-microvolt = <18000>;
regulator-temp-protection-kelvin = <1>;
regulator-temp-error-kelvin = <0>;
};
buck4 {
regulator-name = "buck4";
regulator-min-microvolt = <350000>;
regulator-max-microvolt = <3450000>;
rohm,initial-voltage-microvolt = <600000>;
regulator-ov-warn-microvolt = <18000>;
regulator-uv-warn-microvolt = <18000>;
regulator-temp-protection-kelvin = <1>;
regulator-temp-error-kelvin = <0>;
};
ldo5 {
regulator-name = "ldo5";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <3300000>;
rohm,initial-voltage-microvolt = <500000>;
regulator-ov-error-microvolt = <36000>;
regulator-uv-error-microvolt = <34000>;
regulator-temp-protection-kelvin = <1>;
regulator-temp-warn-kelvin = <0>;
};
ldo6 {
regulator-name = "ldo6";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <3300000>;
rohm,initial-voltage-microvolt = <300000>;
regulator-ov-error-microvolt = <36000>;
regulator-uv-error-microvolt = <34000>;
regulator-temp-protection-kelvin = <1>;
regulator-temp-warn-kelvin = <0>;
};
ldo7 {
regulator-name = "ldo7";
regulator-min-microvolt = <300000>;
regulator-max-microvolt = <3300000>;
rohm,initial-voltage-microvolt = <500000>;
regulator-ov-error-microvolt = <36000>;
regulator-uv-error-microvolt = <34000>;
regulator-temp-protection-kelvin = <1>;
regulator-temp-warn-kelvin = <0>;
};
};
};
};

View File

@ -0,0 +1,71 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/syscon-common.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: System Controller Registers R/W Common Properties
description:
System controller node represents a register region containing a set
of miscellaneous registers. The registers are not cohesive enough to
represent as any specific type of device. The typical use-case is
for some other node's driver, or platform-specific code, to acquire
a reference to the syscon node (e.g. by phandle, node path, or
search using a specific compatible value), interrogate the node (or
associated OS driver) to determine the location of the registers,
and access the registers directly.
maintainers:
- Lee Jones <lee@kernel.org>
select:
properties:
compatible:
contains:
const: syscon
required:
- compatible
properties:
compatible:
contains:
const: syscon
minItems: 2
maxItems: 5 # Should be enough
reg:
maxItems: 1
reg-io-width:
description:
The size (in bytes) of the IO accesses that should be performed
on the device.
enum: [1, 2, 4, 8]
required:
- compatible
- reg
allOf:
- if:
properties:
compatible:
contains:
const: simple-mfd
then:
properties:
compatible:
minItems: 3
maxItems: 5
additionalProperties: true
examples:
- |
syscon: syscon@1c00000 {
compatible = "allwinner,sun8i-h3-system-controller", "syscon";
reg = <0x01c00000 0x1000>;
};
...

View File

@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/mfd/syscon.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: System Controller Registers R/W
title: System Controller Devices
description: |
System controller node represents a register region containing a set
@ -19,121 +19,213 @@ description: |
maintainers:
- Lee Jones <lee@kernel.org>
# Need a select with all compatibles listed for compatibility with older
# dtschema (<2024.02), so this will not be selected for other schemas having
# syscon fallback.
select:
properties:
compatible:
contains:
enum:
- syscon
- al,alpine-sysfabric-servic
- allwinner,sun8i-a83t-system-controller
- allwinner,sun8i-h3-system-controller
- allwinner,sun8i-v3s-system-controller
- allwinner,sun50i-a64-system-controller
- altr,l3regs
- altr,sdr-ctl
- amd,pensando-elba-syscon
- amlogic,meson-mx-assist
- amlogic,meson-mx-bootrom
- amlogic,meson8-analog-top
- amlogic,meson8b-analog-top
- amlogic,meson8-pmu
- amlogic,meson8b-pmu
- apm,merlin-poweroff-mailbox
- apm,mustang-poweroff-mailbox
- apm,xgene-csw
- apm,xgene-efuse
- apm,xgene-mcb
- apm,xgene-rb
- apm,xgene-scu
- atmel,sama5d2-sfrbu
- atmel,sama5d3-nfc-io
- atmel,sama5d3-sfrbu
- atmel,sama5d4-sfrbu
- axis,artpec6-syscon
- brcm,cru-clkset
- brcm,sr-cdru
- brcm,sr-mhb
- cirrus,ep7209-syscon1
- cirrus,ep7209-syscon2
- cirrus,ep7209-syscon3
- cnxt,cx92755-uc
- freecom,fsg-cs2-system-controller
- fsl,imx93-aonmix-ns-syscfg
- fsl,imx93-wakeupmix-syscfg
- fsl,ls1088a-reset
- fsl,vf610-anatop
- fsl,vf610-mscm-cpucfg
- hisilicon,dsa-subctrl
- hisilicon,hi6220-sramctrl
- hisilicon,hip04-ppe
- hisilicon,pcie-sas-subctrl
- hisilicon,peri-subctrl
- hpe,gxp-sysreg
- loongson,ls1b-syscon
- loongson,ls1c-syscon
- lsi,axxia-syscon
- marvell,armada-3700-cpu-misc
- marvell,armada-3700-nb-pm
- marvell,armada-3700-avs
- marvell,armada-3700-usb2-host-misc
- marvell,dove-global-config
- mediatek,mt2701-pctl-a-syscfg
- mediatek,mt2712-pctl-a-syscfg
- mediatek,mt6397-pctl-pmic-syscfg
- mediatek,mt8135-pctl-a-syscfg
- mediatek,mt8135-pctl-b-syscfg
- mediatek,mt8173-pctl-a-syscfg
- mediatek,mt8365-syscfg
- microchip,lan966x-cpu-syscon
- microchip,sam9x60-sfr
- microchip,sama7g5-ddr3phy
- mscc,ocelot-cpu-syscon
- mstar,msc313-pmsleep
- nuvoton,ma35d1-sys
- nuvoton,wpcm450-shm
- rockchip,px30-qos
- rockchip,rk3036-qos
- rockchip,rk3066-qos
- rockchip,rk3128-qos
- rockchip,rk3228-qos
- rockchip,rk3288-qos
- rockchip,rk3368-qos
- rockchip,rk3399-qos
- rockchip,rk3568-qos
- rockchip,rk3588-qos
- rockchip,rv1126-qos
- st,spear1340-misc
- stericsson,nomadik-pmu
- starfive,jh7100-sysmain
- ti,am62-opp-efuse-table
- ti,am62-usb-phy-ctrl
- ti,am625-dss-oldi-io-ctrl
- ti,am62p-cpsw-mac-efuse
- ti,am654-dss-oldi-io-ctrl
- ti,j784s4-pcie-ctrl
- ti,keystone-pllctrl
required:
- compatible
properties:
compatible:
anyOf:
- items:
- enum:
- allwinner,sun8i-a83t-system-controller
- allwinner,sun8i-h3-system-controller
- allwinner,sun8i-v3s-system-controller
- allwinner,sun50i-a64-system-controller
- altr,sdr-ctl
- amd,pensando-elba-syscon
- apm,xgene-csw
- apm,xgene-efuse
- apm,xgene-mcb
- apm,xgene-rb
- apm,xgene-scu
- brcm,cru-clkset
- brcm,sr-cdru
- brcm,sr-mhb
- freecom,fsg-cs2-system-controller
- fsl,imx93-aonmix-ns-syscfg
- fsl,imx93-wakeupmix-syscfg
- fsl,ls1088a-reset
- hisilicon,dsa-subctrl
- hisilicon,hi6220-sramctrl
- hisilicon,pcie-sas-subctrl
- hisilicon,peri-subctrl
- hpe,gxp-sysreg
- intel,lgm-syscon
- loongson,ls1b-syscon
- loongson,ls1c-syscon
- marvell,armada-3700-cpu-misc
- marvell,armada-3700-nb-pm
- marvell,armada-3700-avs
- marvell,armada-3700-usb2-host-misc
- mediatek,mt2712-pctl-a-syscfg
- mediatek,mt6397-pctl-pmic-syscfg
- mediatek,mt8135-pctl-a-syscfg
- mediatek,mt8135-pctl-b-syscfg
- mediatek,mt8173-pctl-a-syscfg
- mediatek,mt8365-syscfg
- microchip,lan966x-cpu-syscon
- microchip,sparx5-cpu-syscon
- mstar,msc313-pmsleep
- nuvoton,ma35d1-sys
- nuvoton,wpcm450-shm
- rockchip,px30-qos
- rockchip,rk3036-qos
- rockchip,rk3066-qos
- rockchip,rk3128-qos
- rockchip,rk3228-qos
- rockchip,rk3288-qos
- rockchip,rk3368-qos
- rockchip,rk3399-qos
- rockchip,rk3568-qos
- rockchip,rk3588-qos
- rockchip,rv1126-qos
- starfive,jh7100-sysmain
- ti,am62-usb-phy-ctrl
- ti,am62p-cpsw-mac-efuse
- ti,am654-dss-oldi-io-ctrl
- ti,am654-serdes-ctrl
- ti,j784s4-pcie-ctrl
- const: syscon
- contains:
const: syscon
minItems: 2
maxItems: 5 # Should be enough
items:
- enum:
- al,alpine-sysfabric-service
- allwinner,sun8i-a83t-system-controller
- allwinner,sun8i-h3-system-controller
- allwinner,sun8i-v3s-system-controller
- allwinner,sun50i-a64-system-controller
- altr,l3regs
- altr,sdr-ctl
- amd,pensando-elba-syscon
- amlogic,meson-mx-assist
- amlogic,meson-mx-bootrom
- amlogic,meson8-analog-top
- amlogic,meson8b-analog-top
- amlogic,meson8-pmu
- amlogic,meson8b-pmu
- apm,merlin-poweroff-mailbox
- apm,mustang-poweroff-mailbox
- apm,xgene-csw
- apm,xgene-efuse
- apm,xgene-mcb
- apm,xgene-rb
- apm,xgene-scu
- atmel,sama5d2-sfrbu
- atmel,sama5d3-nfc-io
- atmel,sama5d3-sfrbu
- atmel,sama5d4-sfrbu
- axis,artpec6-syscon
- brcm,cru-clkset
- brcm,sr-cdru
- brcm,sr-mhb
- cirrus,ep7209-syscon1
- cirrus,ep7209-syscon2
- cirrus,ep7209-syscon3
- cnxt,cx92755-uc
- freecom,fsg-cs2-system-controller
- fsl,imx93-aonmix-ns-syscfg
- fsl,imx93-wakeupmix-syscfg
- fsl,ls1088a-reset
- fsl,vf610-anatop
- fsl,vf610-mscm-cpucfg
- hisilicon,dsa-subctrl
- hisilicon,hi6220-sramctrl
- hisilicon,hip04-ppe
- hisilicon,pcie-sas-subctrl
- hisilicon,peri-subctrl
- hpe,gxp-sysreg
- loongson,ls1b-syscon
- loongson,ls1c-syscon
- lsi,axxia-syscon
- marvell,armada-3700-cpu-misc
- marvell,armada-3700-nb-pm
- marvell,armada-3700-avs
- marvell,armada-3700-usb2-host-misc
- marvell,dove-global-config
- mediatek,mt2701-pctl-a-syscfg
- mediatek,mt2712-pctl-a-syscfg
- mediatek,mt6397-pctl-pmic-syscfg
- mediatek,mt8135-pctl-a-syscfg
- mediatek,mt8135-pctl-b-syscfg
- mediatek,mt8173-pctl-a-syscfg
- mediatek,mt8365-syscfg
- microchip,lan966x-cpu-syscon
- microchip,sam9x60-sfr
- microchip,sama7g5-ddr3phy
- mscc,ocelot-cpu-syscon
- mstar,msc313-pmsleep
- nuvoton,ma35d1-sys
- nuvoton,wpcm450-shm
- rockchip,px30-qos
- rockchip,rk3036-qos
- rockchip,rk3066-qos
- rockchip,rk3128-qos
- rockchip,rk3228-qos
- rockchip,rk3288-qos
- rockchip,rk3368-qos
- rockchip,rk3399-qos
- rockchip,rk3568-qos
- rockchip,rk3588-qos
- rockchip,rv1126-qos
- st,spear1340-misc
- stericsson,nomadik-pmu
- starfive,jh7100-sysmain
- ti,am62-opp-efuse-table
- ti,am62-usb-phy-ctrl
- ti,am625-dss-oldi-io-ctrl
- ti,am62p-cpsw-mac-efuse
- ti,am654-dss-oldi-io-ctrl
- ti,j784s4-pcie-ctrl
- ti,keystone-pllctrl
- const: syscon
reg:
maxItems: 1
reg-io-width:
description: |
The size (in bytes) of the IO accesses that should be performed
on the device.
enum: [1, 2, 4, 8]
resets:
maxItems: 1
hwlocks:
maxItems: 1
description:
Reference to a phandle of a hardware spinlock provider node.
required:
- compatible
- reg
allOf:
- if:
properties:
compatible:
contains:
const: simple-mfd
then:
properties:
compatible:
minItems: 3
maxItems: 5
- $ref: syscon-common.yaml#
additionalProperties: true
unevaluatedProperties: false
examples:
- |

View File

@ -25,23 +25,6 @@ Example:
reg = <0x71070000 0x1c>;
};
o CPU system control:
The SoC has a few registers (ICPU_CFG:CPU_SYSTEM_CTRL) handling configuration of
the CPU: 8 general purpose registers, reset control, CPU en/disabling, CPU
endianness, CPU bus control, CPU status.
Required properties:
- compatible: Should be "mscc,ocelot-cpu-syscon", "syscon"
- reg : Should contain registers location and length
Example:
syscon@70000000 {
compatible = "mscc,ocelot-cpu-syscon", "syscon";
reg = <0x70000000 0x2c>;
};
o HSIO regs:
The SoC has a few registers (HSIO) handling miscellaneous functionalities:

View File

@ -60,15 +60,6 @@ Required properties:
- reg: should contain 2 register ranges. The first one is pointing to the PMECC
block, and the second one to the PMECC_ERRLOC block.
* SAMA5 NFC I/O bindings:
SAMA5 SoCs embed an advanced NAND controller logic to automate READ/WRITE page
operations. This interface to this logic is placed in a separate I/O range and
should thus have its own DT node.
- compatible: should be "atmel,sama5d3-nfc-io", "syscon".
- reg: should contain the I/O range used to interact with the NFC logic.
Example:
nfc_io: nfc-io@70000000 {

View File

@ -19,16 +19,6 @@ Optional properties:
[1] Documentation/devicetree/bindings/net/ethernet.txt
* Ethernet ppe node:
Control rx & tx fifos of all ethernet controllers.
Have 2048 recv channels shared by all ethernet controllers, only if no overlap.
Each controller's recv channel start from channel * number (RX_DESC_NUM).
Required properties:
- compatible: "hisilicon,hip04-ppe", "syscon".
- reg: address and length of the register set for the device.
* MDIO bus node:
Required properties:

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/rohm,bd96801-regulator.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BD96801 Power Management Integrated Circuit regulators
maintainers:
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
description:
This module is part of the ROHM BD96801 MFD device. For more details
see Documentation/devicetree/bindings/mfd/rohm,bd96801-pmic.yaml.
The regulator controller is represented as a sub-node of the PMIC node
on the device tree.
Regulator nodes should be named to buck_<number> and ldo_<number>.
The valid names for BD96801 regulator nodes are
buck1, buck2, buck3, buck4, ldo5, ldo6, ldo7
patternProperties:
"^ldo[5-7]$":
type: object
description:
Properties for single LDO regulator.
$ref: regulator.yaml#
properties:
rohm,initial-voltage-microvolt:
description:
Initial voltage for regulator. Voltage can be tuned +/-150 mV from
this value. NOTE, This can be modified via I2C only when PMIC is in
STBY state.
minimum: 300000
maximum: 3300000
unevaluatedProperties: false
"^buck[1-4]$":
type: object
description:
Properties for single BUCK regulator.
$ref: regulator.yaml#
properties:
rohm,initial-voltage-microvolt:
description:
Initial voltage for regulator. Voltage can be tuned +/-150 mV from
this value. NOTE, This can be modified via I2C only when PMIC is in
STBY state.
minimum: 500000
maximum: 3300000
rohm,keep-on-stby:
description:
Keep the regulator powered when PMIC transitions to STBY state.
type: boolean
unevaluatedProperties: false
additionalProperties: false

View File

@ -0,0 +1,57 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/intel/intel,lgm-syscon.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Intel Lightning Mountain(LGM) Syscon
maintainers:
- Chuanhua Lei <lchuanhua@maxlinear.com>
- Rahul Tanwar <rtanwar@maxlinear.com>
properties:
compatible:
items:
- const: intel,lgm-syscon
- const: syscon
reg:
maxItems: 1
ranges: true
"#address-cells":
const: 1
"#size-cells":
const: 1
patternProperties:
"^emmc-phy@[0-9a-f]+$":
$ref: /schemas/phy/intel,lgm-emmc-phy.yaml#
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
additionalProperties: false
examples:
- |
chiptop@e0200000 {
compatible = "intel,lgm-syscon", "syscon";
reg = <0xe0200000 0x100>;
ranges = <0x0 0xe0200000 0x100>;
#address-cells = <1>;
#size-cells = <1>;
emmc-phy@a8 {
compatible = "intel,lgm-emmc-phy";
reg = <0x00a8 0x10>;
clocks = <&emmc>;
#phy-cells = <0>;
};
};

View File

@ -0,0 +1,49 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/microchip/microchip,sparx5-cpu-syscon.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip Sparx5 CPU Syscon
maintainers:
- Lars Povlsen <lars.povlsen@microchip.com>
properties:
compatible:
items:
- const: microchip,sparx5-cpu-syscon
- const: syscon
- const: simple-mfd
reg:
maxItems: 1
mux-controller:
$ref: /schemas/mux/reg-mux.yaml#
required:
- compatible
- reg
- mux-controller
additionalProperties: false
examples:
- |
soc {
#address-cells = <2>;
#size-cells = <1>;
syscon@600000000 {
compatible = "microchip,sparx5-cpu-syscon", "syscon",
"simple-mfd";
reg = <0x6 0x00000000 0xd0>;
mux: mux-controller {
compatible = "mmio-mux";
#mux-control-cells = <1>;
mux-reg-masks = <0x88 0xf0>;
};
};
};

View File

@ -0,0 +1,55 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/sprd/sprd,sc9863a-glbregs.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: SC9863A Syscon
maintainers:
- Orson Zhai <orsonzhai@gmail.com>
- Baolin Wang <baolin.wang7@gmail.com>
- Chunyan Zhang <zhang.lyra@gmail.com>
properties:
compatible:
items:
- const: sprd,sc9863a-glbregs
- const: syscon
- const: simple-mfd
reg:
maxItems: 1
ranges: true
"#address-cells":
const: 1
"#size-cells":
const: 1
patternProperties:
"@[0-9a-f]+$":
$ref: /schemas/clock/sprd,sc9863a-clk.yaml
description: Clock controllers
additionalProperties: false
examples:
- |
syscon@20e00000 {
compatible = "sprd,sc9863a-glbregs", "syscon", "simple-mfd";
reg = <0x20e00000 0x4000>;
ranges = <0 0x20e00000 0x4000>;
#address-cells = <1>;
#size-cells = <1>;
apahb_gate: apahb-gate@0 {
compatible = "sprd,sc9863a-apahb-gate";
reg = <0x0 0x1020>;
#clock-cells = <1>;
};
};
...

View File

@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/ti/ti,am654-serdes-ctrl.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments AM654 Serdes Control Syscon
maintainers:
- Nishanth Menon <nm@ti.com>
properties:
compatible:
items:
- const: ti,am654-serdes-ctrl
- const: syscon
reg:
maxItems: 1
mux-controller:
$ref: /schemas/mux/reg-mux.yaml#
required:
- compatible
- reg
- mux-controller
additionalProperties: false
examples:
- |
clock@4080 {
compatible = "ti,am654-serdes-ctrl", "syscon";
reg = <0x4080 0x4>;
mux-controller {
compatible = "mmio-mux";
#mux-control-cells = <1>;
mux-reg-masks = <0x0 0x3>; /* lane select */
};
};

View File

@ -5213,6 +5213,11 @@ S: Maintained
F: Documentation/hwmon/cros_ec_hwmon.rst
F: drivers/hwmon/cros_ec_hwmon.c
CHROMEOS EC LED DRIVER
M: Thomas Weißschuh <thomas@weissschuh.net>
S: Maintained
F: drivers/leds/leds-cros_ec.c
CHROMEOS EC SUBDRIVERS
M: Benson Leung <bleung@chromium.org>
R: Guenter Roeck <groeck@chromium.org>
@ -5284,6 +5289,18 @@ F: sound/pci/hda/hda_component*
F: sound/pci/hda/hda_cs_dsp_ctl.*
F: sound/soc/codecs/cs*
CIRRUS LOGIC HAPTIC DRIVERS
M: James Ogletree <jogletre@opensource.cirrus.com>
M: Fred Treven <fred.treven@cirrus.com>
M: Ben Bright <ben.bright@cirrus.com>
L: patches@opensource.cirrus.com
S: Supported
F: Documentation/devicetree/bindings/input/cirrus,cs40l50.yaml
F: drivers/input/misc/cs40l*
F: drivers/mfd/cs40l*
F: include/linux/mfd/cs40l*
F: sound/soc/codecs/cs40l*
CIRRUS LOGIC DSP FIRMWARE DRIVER
M: Simon Trimmer <simont@opensource.cirrus.com>
M: Charles Keepax <ckeepax@opensource.cirrus.com>
@ -13387,6 +13404,15 @@ F: drivers/net/dsa/mv88e6xxx/
F: include/linux/dsa/mv88e6xxx.h
F: include/linux/platform_data/mv88e6xxx.h
MARVELL 88PM886 PMIC DRIVER
M: Karel Balej <balejk@matfyz.cz>
S: Maintained
F: Documentation/devicetree/bindings/mfd/marvell,88pm886-a1.yaml
F: drivers/input/misc/88pm886-onkey.c
F: drivers/mfd/88pm886.c
F: drivers/regulators/88pm886-regulator.c
F: include/linux/mfd/88pm886.h
MARVELL ARMADA 3700 PHY DRIVERS
M: Miquel Raynal <miquel.raynal@bootlin.com>
S: Maintained
@ -19607,17 +19633,21 @@ F: drivers/gpio/gpio-bd71828.c
F: drivers/mfd/rohm-bd71828.c
F: drivers/mfd/rohm-bd718x7.c
F: drivers/mfd/rohm-bd9576.c
F: drivers/mfd/rohm-bd96801.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/bd96801-regulator.c
F: drivers/regulator/rohm-regulator.c
F: drivers/rtc/rtc-bd70528.c
F: drivers/watchdog/bd9576_wdt.c
F: drivers/watchdog/bd96801_wdt.c
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-bd96801.h
F: include/linux/mfd/rohm-generic.h
F: include/linux/mfd/rohm-shared.h
@ -22822,7 +22852,7 @@ L: linux-renesas-soc@vger.kernel.org
S: Supported
F: drivers/mmc/host/renesas_sdhi*
F: drivers/mmc/host/tmio_mmc*
F: include/linux/mfd/tmio.h
F: include/linux/platform_data/tmio.h
TMP513 HARDWARE MONITOR DRIVER
M: Eric Tremblay <etremblay@distech-controls.com>

View File

@ -14,9 +14,9 @@
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/io.h>
#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
#include <linux/platform_data/sh_mmcif.h>
#include <linux/platform_data/tmio.h>
#include <linux/sh_eth.h>
#include <linux/sh_intc.h>
#include <linux/usb/renesas_usbhs.h>

View File

@ -24,10 +24,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/memblock.h>
#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/sh_flctl.h>
#include <linux/platform_data/tmio.h>
#include <linux/platform_device.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>

View File

@ -17,13 +17,13 @@
#include <linux/input/sh_keysc.h>
#include <linux/interrupt.h>
#include <linux/memblock.h>
#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
#include <linux/platform_data/sh_mmcif.h>
#include <linux/mtd/physmap.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/platform_data/gpio_backlight.h>
#include <linux/platform_data/tmio.h>
#include <linux/platform_data/tsc2007.h>
#include <linux/platform_device.h>
#include <linux/regulator/fixed.h>

View File

@ -22,10 +22,10 @@
#include <linux/input/sh_keysc.h>
#include <linux/interrupt.h>
#include <linux/memblock.h>
#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
#include <linux/mtd/physmap.h>
#include <linux/platform_data/lv5207lp.h>
#include <linux/platform_data/tmio.h>
#include <linux/platform_device.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>

View File

@ -7,6 +7,7 @@
#include <linux/clkdev.h>
#include <linux/dma-map-ops.h>
#include <linux/init.h>
#include <linux/platform_data/tmio.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/input.h>
@ -14,7 +15,6 @@
#include <linux/memblock.h>
#include <linux/mmc/host.h>
#include <linux/mtd/physmap.h>
#include <linux/mfd/tmio.h>
#include <linux/mtd/platnand.h>
#include <linux/i2c.h>
#include <linux/regulator/fixed.h>

View File

@ -21,9 +21,9 @@
#include <linux/input/sh_keysc.h>
#include <linux/interrupt.h>
#include <linux/memblock.h>
#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
#include <linux/mtd/physmap.h>
#include <linux/platform_data/tmio.h>
#include <linux/platform_device.h>
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>

View File

@ -275,6 +275,12 @@
#define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff
#define HALO_MPU_VIO_ERR_SRC_SHIFT 0
/*
* Write Sequence
*/
#define WSEQ_OP_MAX_WORDS 3
#define WSEQ_END_OF_SCRIPT 0xFFFFFF
struct cs_dsp_ops {
bool (*validate_version)(struct cs_dsp *dsp, unsigned int version);
unsigned int (*parse_sizes)(struct cs_dsp *dsp,
@ -3495,6 +3501,278 @@ int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits)
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_read, FW_CS_DSP);
struct cs_dsp_wseq_op {
struct list_head list;
u32 address;
u32 data;
u16 offset;
u8 operation;
};
static void cs_dsp_wseq_clear(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq)
{
struct cs_dsp_wseq_op *op, *op_tmp;
list_for_each_entry_safe(op, op_tmp, &wseq->ops, list) {
list_del(&op->list);
devm_kfree(dsp->dev, op);
}
}
static int cs_dsp_populate_wseq(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq)
{
struct cs_dsp_wseq_op *op = NULL;
struct cs_dsp_chunk chunk;
u8 *words;
int ret;
if (!wseq->ctl) {
cs_dsp_err(dsp, "No control for write sequence\n");
return -EINVAL;
}
words = kzalloc(wseq->ctl->len, GFP_KERNEL);
if (!words)
return -ENOMEM;
ret = cs_dsp_coeff_read_ctrl(wseq->ctl, 0, words, wseq->ctl->len);
if (ret) {
cs_dsp_err(dsp, "Failed to read %s: %d\n", wseq->ctl->subname, ret);
goto err_free;
}
INIT_LIST_HEAD(&wseq->ops);
chunk = cs_dsp_chunk(words, wseq->ctl->len);
while (!cs_dsp_chunk_end(&chunk)) {
op = devm_kzalloc(dsp->dev, sizeof(*op), GFP_KERNEL);
if (!op) {
ret = -ENOMEM;
goto err_free;
}
op->offset = cs_dsp_chunk_bytes(&chunk);
op->operation = cs_dsp_chunk_read(&chunk, 8);
switch (op->operation) {
case CS_DSP_WSEQ_END:
op->data = WSEQ_END_OF_SCRIPT;
break;
case CS_DSP_WSEQ_UNLOCK:
op->data = cs_dsp_chunk_read(&chunk, 16);
break;
case CS_DSP_WSEQ_ADDR8:
op->address = cs_dsp_chunk_read(&chunk, 8);
op->data = cs_dsp_chunk_read(&chunk, 32);
break;
case CS_DSP_WSEQ_H16:
case CS_DSP_WSEQ_L16:
op->address = cs_dsp_chunk_read(&chunk, 24);
op->data = cs_dsp_chunk_read(&chunk, 16);
break;
case CS_DSP_WSEQ_FULL:
op->address = cs_dsp_chunk_read(&chunk, 32);
op->data = cs_dsp_chunk_read(&chunk, 32);
break;
default:
ret = -EINVAL;
cs_dsp_err(dsp, "Unsupported op: %X\n", op->operation);
devm_kfree(dsp->dev, op);
goto err_free;
}
list_add_tail(&op->list, &wseq->ops);
if (op->operation == CS_DSP_WSEQ_END)
break;
}
if (op && op->operation != CS_DSP_WSEQ_END) {
cs_dsp_err(dsp, "%s missing end terminator\n", wseq->ctl->subname);
ret = -ENOENT;
}
err_free:
kfree(words);
return ret;
}
/**
* cs_dsp_wseq_init() - Initialize write sequences contained within the loaded DSP firmware
* @dsp: Pointer to DSP structure
* @wseqs: List of write sequences to initialize
* @num_wseqs: Number of write sequences to initialize
*
* Return: Zero for success, a negative number on error.
*/
int cs_dsp_wseq_init(struct cs_dsp *dsp, struct cs_dsp_wseq *wseqs, unsigned int num_wseqs)
{
int i, ret;
lockdep_assert_held(&dsp->pwr_lock);
for (i = 0; i < num_wseqs; i++) {
ret = cs_dsp_populate_wseq(dsp, &wseqs[i]);
if (ret) {
cs_dsp_wseq_clear(dsp, &wseqs[i]);
return ret;
}
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_init, FW_CS_DSP);
static struct cs_dsp_wseq_op *cs_dsp_wseq_find_op(u32 addr, u8 op_code,
struct list_head *wseq_ops)
{
struct cs_dsp_wseq_op *op;
list_for_each_entry(op, wseq_ops, list) {
if (op->operation == op_code && op->address == addr)
return op;
}
return NULL;
}
/**
* cs_dsp_wseq_write() - Add or update an entry in a write sequence
* @dsp: Pointer to a DSP structure
* @wseq: Write sequence to write to
* @addr: Address of the register to be written to
* @data: Data to be written
* @op_code: The type of operation of the new entry
* @update: If true, searches for the first entry in the write sequence with
* the same address and op_code, and replaces it. If false, creates a new entry
* at the tail
*
* This function formats register address and value pairs into the format
* required for write sequence entries, and either updates or adds the
* new entry into the write sequence.
*
* If update is set to true and no matching entry is found, it will add a new entry.
*
* Return: Zero for success, a negative number on error.
*/
int cs_dsp_wseq_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq,
u32 addr, u32 data, u8 op_code, bool update)
{
struct cs_dsp_wseq_op *op_end, *op_new = NULL;
u32 words[WSEQ_OP_MAX_WORDS];
struct cs_dsp_chunk chunk;
int new_op_size, ret;
if (update)
op_new = cs_dsp_wseq_find_op(addr, op_code, &wseq->ops);
/* If entry to update is not found, treat it as a new operation */
if (!op_new) {
op_end = cs_dsp_wseq_find_op(0, CS_DSP_WSEQ_END, &wseq->ops);
if (!op_end) {
cs_dsp_err(dsp, "Missing terminator for %s\n", wseq->ctl->subname);
return -EINVAL;
}
op_new = devm_kzalloc(dsp->dev, sizeof(*op_new), GFP_KERNEL);
if (!op_new)
return -ENOMEM;
op_new->operation = op_code;
op_new->address = addr;
op_new->offset = op_end->offset;
update = false;
}
op_new->data = data;
chunk = cs_dsp_chunk(words, sizeof(words));
cs_dsp_chunk_write(&chunk, 8, op_new->operation);
switch (op_code) {
case CS_DSP_WSEQ_FULL:
cs_dsp_chunk_write(&chunk, 32, op_new->address);
cs_dsp_chunk_write(&chunk, 32, op_new->data);
break;
case CS_DSP_WSEQ_L16:
case CS_DSP_WSEQ_H16:
cs_dsp_chunk_write(&chunk, 24, op_new->address);
cs_dsp_chunk_write(&chunk, 16, op_new->data);
break;
default:
ret = -EINVAL;
cs_dsp_err(dsp, "Operation %X not supported\n", op_code);
goto op_new_free;
}
new_op_size = cs_dsp_chunk_bytes(&chunk);
if (!update) {
if (wseq->ctl->len - op_end->offset < new_op_size) {
cs_dsp_err(dsp, "Not enough memory in %s for entry\n", wseq->ctl->subname);
ret = -E2BIG;
goto op_new_free;
}
op_end->offset += new_op_size;
ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_end->offset / sizeof(u32),
&op_end->data, sizeof(u32));
if (ret)
goto op_new_free;
list_add_tail(&op_new->list, &op_end->list);
}
ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_new->offset / sizeof(u32),
words, new_op_size);
if (ret)
goto op_new_free;
return 0;
op_new_free:
devm_kfree(dsp->dev, op_new);
return ret;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_write, FW_CS_DSP);
/**
* cs_dsp_wseq_multi_write() - Add or update multiple entries in a write sequence
* @dsp: Pointer to a DSP structure
* @wseq: Write sequence to write to
* @reg_seq: List of address-data pairs
* @num_regs: Number of address-data pairs
* @op_code: The types of operations of the new entries
* @update: If true, searches for the first entry in the write sequence with
* the same address and op_code, and replaces it. If false, creates a new entry
* at the tail
*
* This function calls cs_dsp_wseq_write() for multiple address-data pairs.
*
* Return: Zero for success, a negative number on error.
*/
int cs_dsp_wseq_multi_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq,
const struct reg_sequence *reg_seq, int num_regs,
u8 op_code, bool update)
{
int i, ret;
for (i = 0; i < num_regs; i++) {
ret = cs_dsp_wseq_write(dsp, wseq, reg_seq[i].reg,
reg_seq[i].def, op_code, update);
if (ret)
return ret;
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_multi_write, FW_CS_DSP);
MODULE_DESCRIPTION("Cirrus Logic DSP Support");
MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/88pm886.h>
struct pm886_onkey {
struct input_dev *idev;
struct pm886_chip *chip;
};
static irqreturn_t pm886_onkey_irq_handler(int irq, void *data)
{
struct pm886_onkey *onkey = data;
struct regmap *regmap = onkey->chip->regmap;
struct input_dev *idev = onkey->idev;
struct device *parent = idev->dev.parent;
unsigned int val;
int err;
err = regmap_read(regmap, PM886_REG_STATUS1, &val);
if (err) {
dev_err(parent, "Failed to read status: %d\n", err);
return IRQ_NONE;
}
val &= PM886_ONKEY_STS1;
input_report_key(idev, KEY_POWER, val);
input_sync(idev);
return IRQ_HANDLED;
}
static int pm886_onkey_probe(struct platform_device *pdev)
{
struct pm886_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct pm886_onkey *onkey;
struct input_dev *idev;
int irq, err;
onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL);
if (!onkey)
return -ENOMEM;
onkey->chip = chip;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return dev_err_probe(dev, irq, "Failed to get IRQ\n");
idev = devm_input_allocate_device(dev);
if (!idev) {
dev_err(dev, "Failed to allocate input device\n");
return -ENOMEM;
}
onkey->idev = idev;
idev->name = "88pm886-onkey";
idev->phys = "88pm886-onkey/input0";
idev->id.bustype = BUS_I2C;
input_set_capability(idev, EV_KEY, KEY_POWER);
err = devm_request_threaded_irq(dev, irq, NULL, pm886_onkey_irq_handler,
IRQF_ONESHOT | IRQF_NO_SUSPEND, "onkey",
onkey);
if (err)
return dev_err_probe(dev, err, "Failed to request IRQ\n");
err = input_register_device(idev);
if (err)
return dev_err_probe(dev, err, "Failed to register input device\n");
return 0;
}
static const struct platform_device_id pm886_onkey_id_table[] = {
{ "88pm886-onkey", },
{ }
};
MODULE_DEVICE_TABLE(platform, pm886_onkey_id_table);
static struct platform_driver pm886_onkey_driver = {
.driver = {
.name = "88pm886-onkey",
},
.probe = pm886_onkey_probe,
.id_table = pm886_onkey_id_table,
};
module_platform_driver(pm886_onkey_driver);
MODULE_DESCRIPTION("Marvell 88PM886 onkey driver");
MODULE_AUTHOR("Karel Balej <balejk@matfyz.cz>");
MODULE_LICENSE("GPL");

View File

@ -33,6 +33,13 @@ config INPUT_88PM80X_ONKEY
To compile this driver as a module, choose M here: the module
will be called 88pm80x_onkey.
config INPUT_88PM886_ONKEY
tristate "Marvell 88PM886 onkey support"
depends on MFD_88PM886_PMIC
help
Support the onkey of Marvell 88PM886 PMIC as an input device
reporting power button status.
config INPUT_AB8500_PONKEY
tristate "AB8500 Pon (PowerOn) Key"
depends on AB8500_CORE
@ -140,6 +147,16 @@ config INPUT_BMA150
To compile this driver as a module, choose M here: the
module will be called bma150.
config INPUT_CS40L50_VIBRA
tristate "CS40L50 Haptic Driver support"
depends on MFD_CS40L50_CORE
help
Say Y here to enable support for Cirrus Logic's CS40L50
haptic driver.
To compile this driver as a module, choose M here: the
module will be called cs40l50-vibra.
config INPUT_E3X0_BUTTON
tristate "NI Ettus Research USRP E3xx Button support."
default n

View File

@ -7,6 +7,7 @@
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_88PM80X_ONKEY) += 88pm80x_onkey.o
obj-$(CONFIG_INPUT_88PM886_ONKEY) += 88pm886-onkey.o
obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
@ -28,6 +29,7 @@ obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_CPCAP_PWRBUTTON) += cpcap-pwrbutton.o
obj-$(CONFIG_INPUT_CS40L50_VIBRA) += cs40l50-vibra.o
obj-$(CONFIG_INPUT_DA7280_HAPTICS) += da7280.o
obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o

View File

@ -0,0 +1,555 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CS40L50 Advanced Haptic Driver with waveform memory,
* integrated DSP, and closed-loop algorithms
*
* Copyright 2024 Cirrus Logic, Inc.
*
* Author: James Ogletree <james.ogletree@cirrus.com>
*/
#include <linux/bitfield.h>
#include <linux/input.h>
#include <linux/mfd/cs40l50.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
/* Wavetables */
#define CS40L50_RAM_INDEX_START 0x1000000
#define CS40L50_RAM_INDEX_END 0x100007F
#define CS40L50_RTH_INDEX_START 0x1400000
#define CS40L50_RTH_INDEX_END 0x1400001
#define CS40L50_ROM_INDEX_START 0x1800000
#define CS40L50_ROM_INDEX_END 0x180001A
#define CS40L50_TYPE_PCM 8
#define CS40L50_TYPE_PWLE 12
#define CS40L50_PCM_ID 0x0
#define CS40L50_OWT_CUSTOM_DATA_SIZE 2
#define CS40L50_CUSTOM_DATA_MASK 0xFFFFU
/* DSP */
#define CS40L50_GPIO_BASE 0x2804140
#define CS40L50_OWT_BASE 0x2805C34
#define CS40L50_OWT_SIZE 0x2805C38
#define CS40L50_OWT_NEXT 0x2805C3C
#define CS40L50_EFFECTS_MAX 1
/* GPIO */
#define CS40L50_GPIO_NUM_MASK GENMASK(14, 12)
#define CS40L50_GPIO_EDGE_MASK BIT(15)
#define CS40L50_GPIO_MAPPING_NONE 0
#define CS40L50_GPIO_DISABLE 0x1FF
enum cs40l50_bank_type {
CS40L50_WVFRM_BANK_RAM,
CS40L50_WVFRM_BANK_ROM,
CS40L50_WVFRM_BANK_OWT,
CS40L50_WVFRM_BANK_NUM,
};
/* Describes an area in DSP memory populated by effects */
struct cs40l50_bank {
enum cs40l50_bank_type type;
u32 base_index;
u32 max_index;
};
struct cs40l50_effect {
enum cs40l50_bank_type type;
struct list_head list;
u32 gpio_reg;
u32 index;
int id;
};
/* Describes haptic interface of loaded DSP firmware */
struct cs40l50_vibra_dsp {
struct cs40l50_bank *banks;
u32 gpio_base_reg;
u32 owt_offset_reg;
u32 owt_size_reg;
u32 owt_base_reg;
u32 push_owt_cmd;
u32 delete_owt_cmd;
u32 stop_cmd;
int (*write)(struct device *dev, struct regmap *regmap, u32 val);
};
/* Describes configuration and state of haptic operations */
struct cs40l50_vibra {
struct device *dev;
struct regmap *regmap;
struct input_dev *input;
struct workqueue_struct *vib_wq;
struct list_head effect_head;
struct cs40l50_vibra_dsp dsp;
};
struct cs40l50_work {
struct cs40l50_vibra *vib;
struct ff_effect *effect;
struct work_struct work;
s16 *custom_data;
int custom_len;
int count;
int error;
};
static struct cs40l50_bank cs40l50_banks[] = {
{
.type = CS40L50_WVFRM_BANK_RAM,
.base_index = CS40L50_RAM_INDEX_START,
.max_index = CS40L50_RAM_INDEX_END,
},
{
.type = CS40L50_WVFRM_BANK_ROM,
.base_index = CS40L50_ROM_INDEX_START,
.max_index = CS40L50_ROM_INDEX_END,
},
{
.type = CS40L50_WVFRM_BANK_OWT,
.base_index = CS40L50_RTH_INDEX_START,
.max_index = CS40L50_RTH_INDEX_END,
},
};
static struct cs40l50_vibra_dsp cs40l50_dsp = {
.banks = cs40l50_banks,
.gpio_base_reg = CS40L50_GPIO_BASE,
.owt_base_reg = CS40L50_OWT_BASE,
.owt_offset_reg = CS40L50_OWT_NEXT,
.owt_size_reg = CS40L50_OWT_SIZE,
.push_owt_cmd = CS40L50_OWT_PUSH,
.delete_owt_cmd = CS40L50_OWT_DELETE,
.stop_cmd = CS40L50_STOP_PLAYBACK,
.write = cs40l50_dsp_write,
};
static struct cs40l50_effect *cs40l50_find_effect(int id, struct list_head *effect_head)
{
struct cs40l50_effect *effect;
list_for_each_entry(effect, effect_head, list)
if (effect->id == id)
return effect;
return NULL;
}
static int cs40l50_effect_bank_set(struct cs40l50_work *work_data,
struct cs40l50_effect *effect)
{
s16 bank_type = work_data->custom_data[0] & CS40L50_CUSTOM_DATA_MASK;
if (bank_type >= CS40L50_WVFRM_BANK_NUM) {
dev_err(work_data->vib->dev, "Invalid bank (%d)\n", bank_type);
return -EINVAL;
}
if (work_data->custom_len > CS40L50_OWT_CUSTOM_DATA_SIZE)
effect->type = CS40L50_WVFRM_BANK_OWT;
else
effect->type = bank_type;
return 0;
}
static int cs40l50_effect_index_set(struct cs40l50_work *work_data,
struct cs40l50_effect *effect)
{
struct cs40l50_vibra *vib = work_data->vib;
struct cs40l50_effect *owt_effect;
u32 base_index, max_index;
base_index = vib->dsp.banks[effect->type].base_index;
max_index = vib->dsp.banks[effect->type].max_index;
effect->index = base_index;
switch (effect->type) {
case CS40L50_WVFRM_BANK_OWT:
list_for_each_entry(owt_effect, &vib->effect_head, list)
if (owt_effect->type == CS40L50_WVFRM_BANK_OWT)
effect->index++;
break;
case CS40L50_WVFRM_BANK_ROM:
case CS40L50_WVFRM_BANK_RAM:
effect->index += work_data->custom_data[1] & CS40L50_CUSTOM_DATA_MASK;
break;
default:
dev_err(vib->dev, "Bank type %d not supported\n", effect->type);
return -EINVAL;
}
if (effect->index > max_index || effect->index < base_index) {
dev_err(vib->dev, "Index out of bounds: %u\n", effect->index);
return -ENOSPC;
}
return 0;
}
static int cs40l50_effect_gpio_mapping_set(struct cs40l50_work *work_data,
struct cs40l50_effect *effect)
{
u16 gpio_edge, gpio_num, button = work_data->effect->trigger.button;
struct cs40l50_vibra *vib = work_data->vib;
if (button) {
gpio_num = FIELD_GET(CS40L50_GPIO_NUM_MASK, button);
gpio_edge = FIELD_GET(CS40L50_GPIO_EDGE_MASK, button);
effect->gpio_reg = vib->dsp.gpio_base_reg + (gpio_num * 8) - gpio_edge;
return regmap_write(vib->regmap, effect->gpio_reg, button);
}
effect->gpio_reg = CS40L50_GPIO_MAPPING_NONE;
return 0;
}
struct cs40l50_owt_header {
u32 type;
u32 data_words;
u32 offset;
} __packed;
static int cs40l50_upload_owt(struct cs40l50_work *work_data)
{
u8 *new_owt_effect_data __free(kfree) = NULL;
struct cs40l50_vibra *vib = work_data->vib;
size_t len = work_data->custom_len * 2;
struct cs40l50_owt_header header;
u32 offset, size;
int error;
error = regmap_read(vib->regmap, vib->dsp.owt_size_reg, &size);
if (error)
return error;
if ((size * sizeof(u32)) < sizeof(header) + len) {
dev_err(vib->dev, "No space in open wavetable for effect\n");
return -ENOSPC;
}
header.type = work_data->custom_data[0] == CS40L50_PCM_ID ? CS40L50_TYPE_PCM :
CS40L50_TYPE_PWLE;
header.offset = sizeof(header) / sizeof(u32);
header.data_words = len / sizeof(u32);
new_owt_effect_data = kmalloc(sizeof(header) + len, GFP_KERNEL);
memcpy(new_owt_effect_data, &header, sizeof(header));
memcpy(new_owt_effect_data + sizeof(header), work_data->custom_data, len);
error = regmap_read(vib->regmap, vib->dsp.owt_offset_reg, &offset);
if (error)
return error;
error = regmap_bulk_write(vib->regmap, vib->dsp.owt_base_reg +
(offset * sizeof(u32)), new_owt_effect_data,
sizeof(header) + len);
if (error)
return error;
error = vib->dsp.write(vib->dev, vib->regmap, vib->dsp.push_owt_cmd);
if (error)
return error;
return 0;
}
static void cs40l50_add_worker(struct work_struct *work)
{
struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
struct cs40l50_vibra *vib = work_data->vib;
struct cs40l50_effect *effect;
bool is_new = false;
int error;
error = pm_runtime_resume_and_get(vib->dev);
if (error)
goto err_exit;
/* Update effect if already uploaded, otherwise create new effect */
effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
if (!effect) {
effect = kzalloc(sizeof(*effect), GFP_KERNEL);
if (!effect) {
error = -ENOMEM;
goto err_pm;
}
effect->id = work_data->effect->id;
is_new = true;
}
error = cs40l50_effect_bank_set(work_data, effect);
if (error)
goto err_free;
error = cs40l50_effect_index_set(work_data, effect);
if (error)
goto err_free;
error = cs40l50_effect_gpio_mapping_set(work_data, effect);
if (error)
goto err_free;
if (effect->type == CS40L50_WVFRM_BANK_OWT)
error = cs40l50_upload_owt(work_data);
err_free:
if (is_new) {
if (error)
kfree(effect);
else
list_add(&effect->list, &vib->effect_head);
}
err_pm:
pm_runtime_mark_last_busy(vib->dev);
pm_runtime_put_autosuspend(vib->dev);
err_exit:
work_data->error = error;
}
static int cs40l50_add(struct input_dev *dev, struct ff_effect *effect,
struct ff_effect *old)
{
struct ff_periodic_effect *periodic = &effect->u.periodic;
struct cs40l50_vibra *vib = input_get_drvdata(dev);
struct cs40l50_work work_data;
if (effect->type != FF_PERIODIC || periodic->waveform != FF_CUSTOM) {
dev_err(vib->dev, "Type (%#X) or waveform (%#X) unsupported\n",
effect->type, periodic->waveform);
return -EINVAL;
}
work_data.custom_data = memdup_array_user(effect->u.periodic.custom_data,
effect->u.periodic.custom_len,
sizeof(s16));
if (IS_ERR(work_data.custom_data))
return PTR_ERR(work_data.custom_data);
work_data.custom_len = effect->u.periodic.custom_len;
work_data.vib = vib;
work_data.effect = effect;
INIT_WORK(&work_data.work, cs40l50_add_worker);
/* Push to the workqueue to serialize with playbacks */
queue_work(vib->vib_wq, &work_data.work);
flush_work(&work_data.work);
kfree(work_data.custom_data);
return work_data.error;
}
static void cs40l50_start_worker(struct work_struct *work)
{
struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
struct cs40l50_vibra *vib = work_data->vib;
struct cs40l50_effect *start_effect;
if (pm_runtime_resume_and_get(vib->dev) < 0)
goto err_free;
start_effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
if (start_effect) {
while (--work_data->count >= 0) {
vib->dsp.write(vib->dev, vib->regmap, start_effect->index);
usleep_range(work_data->effect->replay.length,
work_data->effect->replay.length + 100);
}
} else {
dev_err(vib->dev, "Effect to play not found\n");
}
pm_runtime_mark_last_busy(vib->dev);
pm_runtime_put_autosuspend(vib->dev);
err_free:
kfree(work_data);
}
static void cs40l50_stop_worker(struct work_struct *work)
{
struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
struct cs40l50_vibra *vib = work_data->vib;
if (pm_runtime_resume_and_get(vib->dev) < 0)
return;
vib->dsp.write(vib->dev, vib->regmap, vib->dsp.stop_cmd);
pm_runtime_mark_last_busy(vib->dev);
pm_runtime_put_autosuspend(vib->dev);
kfree(work_data);
}
static int cs40l50_playback(struct input_dev *dev, int effect_id, int val)
{
struct cs40l50_vibra *vib = input_get_drvdata(dev);
struct cs40l50_work *work_data;
work_data = kzalloc(sizeof(*work_data), GFP_ATOMIC);
if (!work_data)
return -ENOMEM;
work_data->vib = vib;
if (val > 0) {
work_data->effect = &dev->ff->effects[effect_id];
work_data->count = val;
INIT_WORK(&work_data->work, cs40l50_start_worker);
} else {
/* Stop the amplifier as device drives only one effect */
INIT_WORK(&work_data->work, cs40l50_stop_worker);
}
queue_work(vib->vib_wq, &work_data->work);
return 0;
}
static void cs40l50_erase_worker(struct work_struct *work)
{
struct cs40l50_work *work_data = container_of(work, struct cs40l50_work, work);
struct cs40l50_effect *erase_effect, *owt_effect;
struct cs40l50_vibra *vib = work_data->vib;
int error;
error = pm_runtime_resume_and_get(vib->dev);
if (error)
goto err_exit;
erase_effect = cs40l50_find_effect(work_data->effect->id, &vib->effect_head);
if (!erase_effect) {
dev_err(vib->dev, "Effect to erase not found\n");
error = -EINVAL;
goto err_pm;
}
if (erase_effect->gpio_reg != CS40L50_GPIO_MAPPING_NONE) {
error = regmap_write(vib->regmap, erase_effect->gpio_reg,
CS40L50_GPIO_DISABLE);
if (error)
goto err_pm;
}
if (erase_effect->type == CS40L50_WVFRM_BANK_OWT) {
error = vib->dsp.write(vib->dev, vib->regmap,
vib->dsp.delete_owt_cmd |
(erase_effect->index & 0xFF));
if (error)
goto err_pm;
list_for_each_entry(owt_effect, &vib->effect_head, list)
if (owt_effect->type == CS40L50_WVFRM_BANK_OWT &&
owt_effect->index > erase_effect->index)
owt_effect->index--;
}
list_del(&erase_effect->list);
kfree(erase_effect);
err_pm:
pm_runtime_mark_last_busy(vib->dev);
pm_runtime_put_autosuspend(vib->dev);
err_exit:
work_data->error = error;
}
static int cs40l50_erase(struct input_dev *dev, int effect_id)
{
struct cs40l50_vibra *vib = input_get_drvdata(dev);
struct cs40l50_work work_data;
work_data.vib = vib;
work_data.effect = &dev->ff->effects[effect_id];
INIT_WORK(&work_data.work, cs40l50_erase_worker);
/* Push to workqueue to serialize with playbacks */
queue_work(vib->vib_wq, &work_data.work);
flush_work(&work_data.work);
return work_data.error;
}
static void cs40l50_remove_wq(void *data)
{
flush_workqueue(data);
destroy_workqueue(data);
}
static int cs40l50_vibra_probe(struct platform_device *pdev)
{
struct cs40l50 *cs40l50 = dev_get_drvdata(pdev->dev.parent);
struct cs40l50_vibra *vib;
int error;
vib = devm_kzalloc(pdev->dev.parent, sizeof(*vib), GFP_KERNEL);
if (!vib)
return -ENOMEM;
vib->dev = cs40l50->dev;
vib->regmap = cs40l50->regmap;
vib->dsp = cs40l50_dsp;
vib->input = devm_input_allocate_device(vib->dev);
if (!vib->input)
return -ENOMEM;
vib->input->id.product = cs40l50->devid;
vib->input->id.version = cs40l50->revid;
vib->input->name = "cs40l50_vibra";
input_set_drvdata(vib->input, vib);
input_set_capability(vib->input, EV_FF, FF_PERIODIC);
input_set_capability(vib->input, EV_FF, FF_CUSTOM);
error = input_ff_create(vib->input, CS40L50_EFFECTS_MAX);
if (error) {
dev_err(vib->dev, "Failed to create input device\n");
return error;
}
vib->input->ff->upload = cs40l50_add;
vib->input->ff->playback = cs40l50_playback;
vib->input->ff->erase = cs40l50_erase;
INIT_LIST_HEAD(&vib->effect_head);
vib->vib_wq = alloc_ordered_workqueue("vib_wq", WQ_HIGHPRI);
if (!vib->vib_wq)
return -ENOMEM;
error = devm_add_action_or_reset(vib->dev, cs40l50_remove_wq, vib->vib_wq);
if (error)
return error;
error = input_register_device(vib->input);
if (error)
return error;
return 0;
}
static const struct platform_device_id cs40l50_vibra_id_match[] = {
{ "cs40l50-vibra", },
{}
};
MODULE_DEVICE_TABLE(platform, cs40l50_vibra_id_match);
static struct platform_driver cs40l50_vibra_driver = {
.probe = cs40l50_vibra_probe,
.id_table = cs40l50_vibra_id_match,
.driver = {
.name = "cs40l50-vibra",
},
};
module_platform_driver(cs40l50_vibra_driver);
MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver");
MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
MODULE_LICENSE("GPL");

View File

@ -179,6 +179,21 @@ config LEDS_CR0014114
To compile this driver as a module, choose M here: the module
will be called leds-cr0014114.
config LEDS_CROS_EC
tristate "LED Support for ChromeOS EC"
depends on MFD_CROS_EC_DEV
depends on LEDS_CLASS_MULTICOLOR
select LEDS_TRIGGERS
default MFD_CROS_EC_DEV
help
This option enables support for LEDs managed by ChromeOS ECs.
All LEDs exposed by the EC are supported in multicolor mode.
A hardware trigger to switch back to the automatic behaviour is
provided.
To compile this driver as a module, choose M here: the module
will be called leds-cros_ec.
config LEDS_EL15203000
tristate "LED Support for Crane EL15203000"
depends on LEDS_CLASS

View File

@ -26,6 +26,7 @@ obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
obj-$(CONFIG_LEDS_CPCAP) += leds-cpcap.o
obj-$(CONFIG_LEDS_CROS_EC) += leds-cros_ec.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o

View File

@ -101,7 +101,7 @@ static ssize_t multi_index_show(struct device *dev,
for (i = 0; i < mcled_cdev->num_colors; i++) {
index = mcled_cdev->subled_info[i].color_index;
len += sprintf(buf + len, "%s", led_colors[index]);
len += sprintf(buf + len, "%s", led_get_color_name(index));
if (i < mcled_cdev->num_colors - 1)
len += sprintf(buf + len, " ");
}

View File

@ -503,6 +503,11 @@ int led_classdev_register_ext(struct device *parent,
ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
if (ret < 0)
return ret;
else if (ret && led_cdev->flags & LED_REJECT_NAME_CONFLICT)
return -EEXIST;
else if (ret)
dev_warn(parent, "Led %s renamed to %s due to name collision\n",
proposed_name, final_name);
if (led_cdev->color >= LED_COLOR_ID_MAX)
dev_warn(parent, "LED %s color identifier out of range\n", final_name);
@ -518,10 +523,6 @@ int led_classdev_register_ext(struct device *parent,
if (init_data && init_data->fwnode)
device_set_node(led_cdev->dev, init_data->fwnode);
if (ret)
dev_warn(parent, "Led %s renamed to %s due to name collision",
proposed_name, dev_name(led_cdev->dev));
if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
ret = led_add_brightness_hw_changed(led_cdev);
if (ret) {

View File

@ -25,7 +25,7 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
LIST_HEAD(leds_list);
EXPORT_SYMBOL_GPL(leds_list);
const char * const led_colors[LED_COLOR_ID_MAX] = {
static const char * const led_colors[LED_COLOR_ID_MAX] = {
[LED_COLOR_ID_WHITE] = "white",
[LED_COLOR_ID_RED] = "red",
[LED_COLOR_ID_GREEN] = "green",
@ -42,7 +42,6 @@ const char * const led_colors[LED_COLOR_ID_MAX] = {
[LED_COLOR_ID_CYAN] = "cyan",
[LED_COLOR_ID_LIME] = "lime",
};
EXPORT_SYMBOL_GPL(led_colors);
static int __led_set_brightness(struct led_classdev *led_cdev, unsigned int value)
{
@ -534,6 +533,15 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data,
}
EXPORT_SYMBOL_GPL(led_compose_name);
const char *led_get_color_name(u8 color_id)
{
if (color_id >= ARRAY_SIZE(led_colors))
return NULL;
return led_colors[color_id];
}
EXPORT_SYMBOL_GPL(led_get_color_name);
enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode)
{
const char *state = NULL;

277
drivers/leds/leds-cros_ec.c Normal file
View File

@ -0,0 +1,277 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ChromeOS EC LED Driver
*
* Copyright (C) 2024 Thomas Weißschuh <linux@weissschuh.net>
*/
#include <linux/device.h>
#include <linux/leds.h>
#include <linux/led-class-multicolor.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
static const char * const cros_ec_led_functions[] = {
[EC_LED_ID_BATTERY_LED] = LED_FUNCTION_CHARGING,
[EC_LED_ID_POWER_LED] = LED_FUNCTION_POWER,
[EC_LED_ID_ADAPTER_LED] = "adapter",
[EC_LED_ID_LEFT_LED] = "left",
[EC_LED_ID_RIGHT_LED] = "right",
[EC_LED_ID_RECOVERY_HW_REINIT_LED] = "recovery-hw-reinit",
[EC_LED_ID_SYSRQ_DEBUG_LED] = "sysrq-debug",
};
static_assert(ARRAY_SIZE(cros_ec_led_functions) == EC_LED_ID_COUNT);
static const int cros_ec_led_to_linux_id[] = {
[EC_LED_COLOR_RED] = LED_COLOR_ID_RED,
[EC_LED_COLOR_GREEN] = LED_COLOR_ID_GREEN,
[EC_LED_COLOR_BLUE] = LED_COLOR_ID_BLUE,
[EC_LED_COLOR_YELLOW] = LED_COLOR_ID_YELLOW,
[EC_LED_COLOR_WHITE] = LED_COLOR_ID_WHITE,
[EC_LED_COLOR_AMBER] = LED_COLOR_ID_AMBER,
};
static_assert(ARRAY_SIZE(cros_ec_led_to_linux_id) == EC_LED_COLOR_COUNT);
static const int cros_ec_linux_to_ec_id[] = {
[LED_COLOR_ID_RED] = EC_LED_COLOR_RED,
[LED_COLOR_ID_GREEN] = EC_LED_COLOR_GREEN,
[LED_COLOR_ID_BLUE] = EC_LED_COLOR_BLUE,
[LED_COLOR_ID_YELLOW] = EC_LED_COLOR_YELLOW,
[LED_COLOR_ID_WHITE] = EC_LED_COLOR_WHITE,
[LED_COLOR_ID_AMBER] = EC_LED_COLOR_AMBER,
};
struct cros_ec_led_priv {
struct led_classdev_mc led_mc_cdev;
struct cros_ec_device *cros_ec;
enum ec_led_id led_id;
};
static inline struct cros_ec_led_priv *cros_ec_led_cdev_to_priv(struct led_classdev *led_cdev)
{
return container_of(lcdev_to_mccdev(led_cdev), struct cros_ec_led_priv, led_mc_cdev);
}
union cros_ec_led_cmd_data {
struct ec_params_led_control req;
struct ec_response_led_control resp;
} __packed;
static int cros_ec_led_send_cmd(struct cros_ec_device *cros_ec,
union cros_ec_led_cmd_data *arg)
{
int ret;
struct {
struct cros_ec_command msg;
union cros_ec_led_cmd_data data;
} __packed buf = {
.msg = {
.version = 1,
.command = EC_CMD_LED_CONTROL,
.insize = sizeof(arg->resp),
.outsize = sizeof(arg->req),
},
.data.req = arg->req
};
ret = cros_ec_cmd_xfer_status(cros_ec, &buf.msg);
if (ret < 0)
return ret;
arg->resp = buf.data.resp;
return 0;
}
static int cros_ec_led_trigger_activate(struct led_classdev *led_cdev)
{
struct cros_ec_led_priv *priv = cros_ec_led_cdev_to_priv(led_cdev);
union cros_ec_led_cmd_data arg = {};
arg.req.led_id = priv->led_id;
arg.req.flags = EC_LED_FLAGS_AUTO;
return cros_ec_led_send_cmd(priv->cros_ec, &arg);
}
static struct led_hw_trigger_type cros_ec_led_trigger_type;
static struct led_trigger cros_ec_led_trigger = {
.name = "chromeos-auto",
.trigger_type = &cros_ec_led_trigger_type,
.activate = cros_ec_led_trigger_activate,
};
static int cros_ec_led_brightness_set_blocking(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct cros_ec_led_priv *priv = cros_ec_led_cdev_to_priv(led_cdev);
union cros_ec_led_cmd_data arg = {};
enum ec_led_colors led_color;
struct mc_subled *subled;
size_t i;
led_mc_calc_color_components(&priv->led_mc_cdev, brightness);
arg.req.led_id = priv->led_id;
for (i = 0; i < priv->led_mc_cdev.num_colors; i++) {
subled = &priv->led_mc_cdev.subled_info[i];
led_color = cros_ec_linux_to_ec_id[subled->color_index];
arg.req.brightness[led_color] = subled->brightness;
}
return cros_ec_led_send_cmd(priv->cros_ec, &arg);
}
static int cros_ec_led_count_subleds(struct device *dev,
struct ec_response_led_control *resp,
unsigned int *max_brightness)
{
unsigned int range, common_range = 0;
int num_subleds = 0;
size_t i;
for (i = 0; i < EC_LED_COLOR_COUNT; i++) {
range = resp->brightness_range[i];
if (!range)
continue;
num_subleds++;
if (!common_range)
common_range = range;
if (common_range != range) {
/* The multicolor LED API expects a uniform max_brightness */
dev_err(dev, "Inconsistent LED brightness values\n");
return -EINVAL;
}
}
if (!num_subleds)
return -EINVAL;
*max_brightness = common_range;
return num_subleds;
}
static const char *cros_ec_led_get_color_name(struct led_classdev_mc *led_mc_cdev)
{
int color;
if (led_mc_cdev->num_colors == 1)
color = led_mc_cdev->subled_info[0].color_index;
else
color = LED_COLOR_ID_MULTI;
return led_get_color_name(color);
}
static int cros_ec_led_probe_one(struct device *dev, struct cros_ec_device *cros_ec,
enum ec_led_id id)
{
union cros_ec_led_cmd_data arg = {};
struct cros_ec_led_priv *priv;
struct led_classdev *led_cdev;
struct mc_subled *subleds;
int i, ret, num_subleds;
size_t subled;
arg.req.led_id = id;
arg.req.flags = EC_LED_FLAGS_QUERY;
ret = cros_ec_led_send_cmd(cros_ec, &arg);
if (ret == -EINVAL)
return 0; /* Unknown LED, skip */
if (ret == -EOPNOTSUPP)
return -ENODEV;
if (ret < 0)
return ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
num_subleds = cros_ec_led_count_subleds(dev, &arg.resp,
&priv->led_mc_cdev.led_cdev.max_brightness);
if (num_subleds < 0)
return num_subleds;
priv->cros_ec = cros_ec;
priv->led_id = id;
subleds = devm_kcalloc(dev, num_subleds, sizeof(*subleds), GFP_KERNEL);
if (!subleds)
return -ENOMEM;
subled = 0;
for (i = 0; i < EC_LED_COLOR_COUNT; i++) {
if (!arg.resp.brightness_range[i])
continue;
subleds[subled].color_index = cros_ec_led_to_linux_id[i];
if (subled == 0)
subleds[subled].intensity = 100;
subled++;
}
priv->led_mc_cdev.subled_info = subleds;
priv->led_mc_cdev.num_colors = num_subleds;
led_cdev = &priv->led_mc_cdev.led_cdev;
led_cdev->brightness_set_blocking = cros_ec_led_brightness_set_blocking;
led_cdev->trigger_type = &cros_ec_led_trigger_type;
led_cdev->default_trigger = cros_ec_led_trigger.name;
led_cdev->hw_control_trigger = cros_ec_led_trigger.name;
led_cdev->name = devm_kasprintf(dev, GFP_KERNEL, "chromeos:%s:%s",
cros_ec_led_get_color_name(&priv->led_mc_cdev),
cros_ec_led_functions[id]);
if (!led_cdev->name)
return -ENOMEM;
return devm_led_classdev_multicolor_register(dev, &priv->led_mc_cdev);
}
static int cros_ec_led_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
int i, ret = 0;
ret = devm_led_trigger_register(dev, &cros_ec_led_trigger);
if (ret)
return ret;
for (i = 0; i < EC_LED_ID_COUNT; i++) {
ret = cros_ec_led_probe_one(dev, cros_ec, i);
if (ret)
break;
}
return ret;
}
static const struct platform_device_id cros_ec_led_id[] = {
{ "cros-ec-led", 0 },
{}
};
static struct platform_driver cros_ec_led_driver = {
.driver.name = "cros-ec-led",
.probe = cros_ec_led_probe,
.id_table = cros_ec_led_id,
};
module_platform_driver(cros_ec_led_driver);
MODULE_DEVICE_TABLE(platform, cros_ec_led_id);
MODULE_DESCRIPTION("ChromeOS EC LED Driver");
MODULE_AUTHOR("Thomas Weißschuh <linux@weissschuh.net");
MODULE_LICENSE("GPL");

View File

@ -30,6 +30,5 @@ ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
extern struct rw_semaphore leds_list_lock;
extern struct list_head leds_list;
extern const char * const led_colors[LED_COLOR_ID_MAX];
#endif /* __LEDS_H_INCLUDED */

View File

@ -116,7 +116,7 @@ enum {
#define PM800_CHIP_GEN_ID_NUM 0x3
static const struct i2c_device_id pm80x_id_table[] = {
{"88PM800", 0},
{ "88PM800" },
{} /* NULL terminated */
};
MODULE_DEVICE_TABLE(i2c, pm80x_id_table);

View File

@ -30,7 +30,7 @@
#include <linux/delay.h>
static const struct i2c_device_id pm80x_id_table[] = {
{"88PM805", 0},
{ "88PM805" },
{} /* NULL terminated */
};
MODULE_DEVICE_TABLE(i2c, pm80x_id_table);

View File

@ -1233,7 +1233,7 @@ static int pm860x_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume);
static const struct i2c_device_id pm860x_id_table[] = {
{ "88PM860x", 0 },
{ "88PM860x" },
{}
};
MODULE_DEVICE_TABLE(i2c, pm860x_id_table);

148
drivers/mfd/88pm886.c Normal file
View File

@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/mfd/88pm886.h>
static const struct regmap_config pm886_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = PM886_REG_RTC_SPARE6,
};
static struct regmap_irq pm886_regmap_irqs[] = {
REGMAP_IRQ_REG(PM886_IRQ_ONKEY, 0, PM886_INT_ENA1_ONKEY),
};
static struct regmap_irq_chip pm886_regmap_irq_chip = {
.name = "88pm886",
.irqs = pm886_regmap_irqs,
.num_irqs = ARRAY_SIZE(pm886_regmap_irqs),
.num_regs = 4,
.status_base = PM886_REG_INT_STATUS1,
.ack_base = PM886_REG_INT_STATUS1,
.unmask_base = PM886_REG_INT_ENA_1,
};
static struct resource pm886_onkey_resources[] = {
DEFINE_RES_IRQ_NAMED(PM886_IRQ_ONKEY, "88pm886-onkey"),
};
static struct mfd_cell pm886_devs[] = {
MFD_CELL_RES("88pm886-onkey", pm886_onkey_resources),
MFD_CELL_NAME("88pm886-regulator"),
};
static int pm886_power_off_handler(struct sys_off_data *sys_off_data)
{
struct pm886_chip *chip = sys_off_data->cb_data;
struct regmap *regmap = chip->regmap;
struct device *dev = &chip->client->dev;
int err;
err = regmap_update_bits(regmap, PM886_REG_MISC_CONFIG1, PM886_SW_PDOWN, PM886_SW_PDOWN);
if (err) {
dev_err(dev, "Failed to power off the device: %d\n", err);
return NOTIFY_BAD;
}
return NOTIFY_DONE;
}
static int pm886_setup_irq(struct pm886_chip *chip,
struct regmap_irq_chip_data **irq_data)
{
struct regmap *regmap = chip->regmap;
struct device *dev = &chip->client->dev;
int err;
/* Set interrupt clearing mode to clear on write. */
err = regmap_update_bits(regmap, PM886_REG_MISC_CONFIG2,
PM886_INT_INV | PM886_INT_CLEAR | PM886_INT_MASK_MODE,
PM886_INT_WC);
if (err) {
dev_err(dev, "Failed to set interrupt clearing mode: %d\n", err);
return err;
}
err = devm_regmap_add_irq_chip(dev, regmap, chip->client->irq,
IRQF_ONESHOT, 0, &pm886_regmap_irq_chip,
irq_data);
if (err) {
dev_err(dev, "Failed to request IRQ: %d\n", err);
return err;
}
return 0;
}
static int pm886_probe(struct i2c_client *client)
{
struct regmap_irq_chip_data *irq_data;
struct device *dev = &client->dev;
struct pm886_chip *chip;
struct regmap *regmap;
unsigned int chip_id;
int err;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->client = client;
chip->chip_id = (uintptr_t)device_get_match_data(dev);
i2c_set_clientdata(client, chip);
regmap = devm_regmap_init_i2c(client, &pm886_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialize regmap\n");
chip->regmap = regmap;
err = regmap_read(regmap, PM886_REG_ID, &chip_id);
if (err)
return dev_err_probe(dev, err, "Failed to read chip ID\n");
if (chip->chip_id != chip_id)
return dev_err_probe(dev, -EINVAL, "Unsupported chip: 0x%x\n", chip_id);
err = pm886_setup_irq(chip, &irq_data);
if (err)
return err;
err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, pm886_devs, ARRAY_SIZE(pm886_devs),
NULL, 0, regmap_irq_get_domain(irq_data));
if (err)
return dev_err_probe(dev, err, "Failed to add devices\n");
err = devm_register_power_off_handler(dev, pm886_power_off_handler, chip);
if (err)
return dev_err_probe(dev, err, "Failed to register power off handler\n");
device_init_wakeup(dev, device_property_read_bool(dev, "wakeup-source"));
return 0;
}
static const struct of_device_id pm886_of_match[] = {
{ .compatible = "marvell,88pm886-a1", .data = (void *)PM886_A1_CHIP_ID },
{ }
};
MODULE_DEVICE_TABLE(of, pm886_of_match);
static struct i2c_driver pm886_i2c_driver = {
.driver = {
.name = "88pm886",
.of_match_table = pm886_of_match,
},
.probe = pm886_probe,
};
module_i2c_driver(pm886_i2c_driver);
MODULE_DESCRIPTION("Marvell 88PM886 PMIC driver");
MODULE_AUTHOR("Karel Balej <balejk@matfyz.cz>");
MODULE_LICENSE("GPL");

View File

@ -794,6 +794,18 @@ config MFD_88PM860X
select individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.
config MFD_88PM886_PMIC
bool "Marvell 88PM886 PMIC"
depends on I2C=y
depends on OF
select REGMAP_I2C
select REGMAP_IRQ
select MFD_CORE
help
This enables support for Marvell 88PM886 Power Management IC.
This includes the I2C driver and the core APIs _only_, you have to
select individual components like onkey under the corresponding menus.
config MFD_MAX14577
tristate "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
depends on I2C
@ -2089,6 +2101,19 @@ config MFD_ROHM_BD957XMUF
BD9573MUF Power Management ICs. BD9576 and BD9573 are primarily
designed to be used to power R-Car series processors.
config MFD_ROHM_BD96801
tristate "ROHM BD96801 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 BD96801 Power
Management IC. The ROHM BD96801 is a highly scalable Power Management
IC for industrial and automotive use. The BD96801 can be used as a
master PMIC in a chained PMIC solution with suitable companion PMICs.
config MFD_STM32_LPTIMER
tristate "Support for STM32 Low-Power Timer"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
@ -2208,6 +2233,7 @@ config MFD_ACER_A500_EC
config MFD_QCOM_PM8008
tristate "QCOM PM8008 Power Management IC"
depends on I2C && OF
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
@ -2243,6 +2269,36 @@ config MCP_UCB1200_TS
endmenu
config MFD_CS40L50_CORE
tristate
select MFD_CORE
select FW_CS_DSP
select REGMAP_IRQ
config MFD_CS40L50_I2C
tristate "Cirrus Logic CS40L50 (I2C)"
select REGMAP_I2C
select MFD_CS40L50_CORE
depends on I2C
help
Select this to support the Cirrus Logic CS40L50 Haptic
Driver over I2C.
This driver can be built as a module. If built as a module it will be
called "cs40l50-i2c".
config MFD_CS40L50_SPI
tristate "Cirrus Logic CS40L50 (SPI)"
select REGMAP_SPI
select MFD_CS40L50_CORE
depends on SPI
help
Select this to support the Cirrus Logic CS40L50 Haptic
Driver over SPI.
This driver can be built as a module. If built as a module it will be
called "cs40l50-spi".
config MFD_VEXPRESS_SYSREG
tristate "Versatile Express System Registers"
depends on VEXPRESS_CONFIG && GPIOLIB

View File

@ -7,6 +7,7 @@
obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
obj-$(CONFIG_MFD_88PM886_PMIC) += 88pm886.o
obj-$(CONFIG_MFD_ACT8945A) += act8945a.o
obj-$(CONFIG_MFD_SM501) += sm501.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
@ -88,6 +89,10 @@ obj-$(CONFIG_MFD_MADERA) += madera.o
obj-$(CONFIG_MFD_MADERA_I2C) += madera-i2c.o
obj-$(CONFIG_MFD_MADERA_SPI) += madera-spi.o
obj-$(CONFIG_MFD_CS40L50_CORE) += cs40l50-core.o
obj-$(CONFIG_MFD_CS40L50_I2C) += cs40l50-i2c.o
obj-$(CONFIG_MFD_CS40L50_SPI) += cs40l50-spi.o
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_TPS6507X) += tps6507x.o
@ -264,6 +269,7 @@ obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.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_ROHM_BD96801) += rohm-bd96801.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
@ -280,7 +286,5 @@ obj-$(CONFIG_MFD_INTEL_M10_BMC_PMCI) += intel-m10-bmc-pmci.o
obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o
obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o
rsmu-i2c-objs := rsmu_core.o rsmu_i2c.o
rsmu-spi-objs := rsmu_core.o rsmu_spi.o
obj-$(CONFIG_MFD_RSMU_I2C) += rsmu-i2c.o
obj-$(CONFIG_MFD_RSMU_SPI) += rsmu-spi.o
obj-$(CONFIG_MFD_RSMU_I2C) += rsmu_i2c.o rsmu_core.o
obj-$(CONFIG_MFD_RSMU_SPI) += rsmu_spi.o rsmu_core.o

View File

@ -439,7 +439,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(aat2870_pm_ops, aat2870_i2c_suspend,
aat2870_i2c_resume);
static const struct i2c_device_id aat2870_i2c_id_table[] = {
{ "aat2870", 0 },
{ "aat2870" },
{ }
};

View File

@ -54,7 +54,7 @@ static int act8945a_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id act8945a_i2c_id[] = {
{ "act8945a", 0 },
{ "act8945a" },
{}
};
MODULE_DEVICE_TABLE(i2c, act8945a_i2c_id);

View File

@ -1429,4 +1429,5 @@ int arizona_dev_exit(struct arizona *arizona)
}
EXPORT_SYMBOL_GPL(arizona_dev_exit);
MODULE_DESCRIPTION("Wolfson Arizona core driver");
MODULE_LICENSE("GPL v2");

View File

@ -190,19 +190,12 @@ static int arizona_spi_acpi_probe(struct arizona *arizona)
static int arizona_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
const void *match_data;
struct arizona *arizona;
const struct regmap_config *regmap_config = NULL;
unsigned long type = 0;
int ret;
match_data = device_get_match_data(&spi->dev);
if (match_data)
type = (unsigned long)match_data;
else if (id)
type = id->driver_data;
type = (unsigned long)spi_get_device_match_data(spi);
switch (type) {
case WM5102:
if (IS_ENABLED(CONFIG_MFD_WM5102))

View File

@ -430,8 +430,8 @@ static const struct of_device_id as3722_of_match[] = {
MODULE_DEVICE_TABLE(of, as3722_of_match);
static const struct i2c_device_id as3722_i2c_id[] = {
{ "as3722", 0 },
{},
{ "as3722" },
{}
};
MODULE_DEVICE_TABLE(i2c, as3722_i2c_id);

View File

@ -75,18 +75,18 @@ MODULE_DEVICE_TABLE(of, axp20x_i2c_of_match);
#endif
static const struct i2c_device_id axp20x_i2c_id[] = {
{ "axp152", 0 },
{ "axp192", 0 },
{ "axp202", 0 },
{ "axp209", 0 },
{ "axp221", 0 },
{ "axp223", 0 },
{ "axp313a", 0 },
{ "axp717", 0 },
{ "axp803", 0 },
{ "axp806", 0 },
{ "axp15060", 0 },
{ },
{ "axp152" },
{ "axp192" },
{ "axp202" },
{ "axp209" },
{ "axp221" },
{ "axp223" },
{ "axp313a" },
{ "axp717" },
{ "axp803" },
{ "axp806" },
{ "axp15060" },
{ }
};
MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);

View File

@ -268,7 +268,7 @@ static const struct of_device_id bd9571mwv_of_match_table[] = {
MODULE_DEVICE_TABLE(of, bd9571mwv_of_match_table);
static const struct i2c_device_id bd9571mwv_id_table[] = {
{ "bd9571mwv", 0 },
{ "bd9571mwv" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, bd9571mwv_id_table);

View File

@ -87,6 +87,7 @@ static const struct mfd_cell cros_ec_sensorhub_cells[] = {
};
static const struct mfd_cell cros_usbpd_charger_cells[] = {
{ .name = "cros-charge-control", },
{ .name = "cros-usbpd-charger", },
{ .name = "cros-usbpd-logger", },
};
@ -99,6 +100,14 @@ static const struct mfd_cell cros_ec_wdt_cells[] = {
{ .name = "cros-ec-wdt", }
};
static const struct mfd_cell cros_ec_led_cells[] = {
{ .name = "cros-ec-led", },
};
static const struct mfd_cell cros_ec_keyboard_leds_cells[] = {
{ .name = "cros-keyboard-leds", },
};
static const struct cros_feature_to_cells cros_subdevices[] = {
{
.id = EC_FEATURE_CEC,
@ -125,11 +134,22 @@ static const struct cros_feature_to_cells cros_subdevices[] = {
.mfd_cells = cros_ec_wdt_cells,
.num_cells = ARRAY_SIZE(cros_ec_wdt_cells),
},
{
.id = EC_FEATURE_LED,
.mfd_cells = cros_ec_led_cells,
.num_cells = ARRAY_SIZE(cros_ec_led_cells),
},
{
.id = EC_FEATURE_PWM_KEYB,
.mfd_cells = cros_ec_keyboard_leds_cells,
.num_cells = ARRAY_SIZE(cros_ec_keyboard_leds_cells),
},
};
static const struct mfd_cell cros_ec_platform_cells[] = {
{ .name = "cros-ec-chardev", },
{ .name = "cros-ec-debugfs", },
{ .name = "cros-ec-hwmon", },
{ .name = "cros-ec-sysfs", },
};

570
drivers/mfd/cs40l50-core.c Normal file
View File

@ -0,0 +1,570 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CS40L50 Advanced Haptic Driver with waveform memory,
* integrated DSP, and closed-loop algorithms
*
* Copyright 2024 Cirrus Logic, Inc.
*
* Author: James Ogletree <james.ogletree@cirrus.com>
*/
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/mfd/core.h>
#include <linux/mfd/cs40l50.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
static const struct mfd_cell cs40l50_devs[] = {
{ .name = "cs40l50-codec", },
{ .name = "cs40l50-vibra", },
};
const struct regmap_config cs40l50_regmap = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_BIG,
};
EXPORT_SYMBOL_GPL(cs40l50_regmap);
static const char * const cs40l50_supplies[] = {
"vdd-io",
};
static const struct regmap_irq cs40l50_reg_irqs[] = {
REGMAP_IRQ_REG(CS40L50_DSP_QUEUE_IRQ, CS40L50_IRQ1_INT_2_OFFSET,
CS40L50_DSP_QUEUE_MASK),
REGMAP_IRQ_REG(CS40L50_AMP_SHORT_IRQ, CS40L50_IRQ1_INT_1_OFFSET,
CS40L50_AMP_SHORT_MASK),
REGMAP_IRQ_REG(CS40L50_TEMP_ERR_IRQ, CS40L50_IRQ1_INT_8_OFFSET,
CS40L50_TEMP_ERR_MASK),
REGMAP_IRQ_REG(CS40L50_BST_UVP_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
CS40L50_BST_UVP_MASK),
REGMAP_IRQ_REG(CS40L50_BST_SHORT_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
CS40L50_BST_SHORT_MASK),
REGMAP_IRQ_REG(CS40L50_BST_ILIMIT_IRQ, CS40L50_IRQ1_INT_9_OFFSET,
CS40L50_BST_ILIMIT_MASK),
REGMAP_IRQ_REG(CS40L50_UVLO_VDDBATT_IRQ, CS40L50_IRQ1_INT_10_OFFSET,
CS40L50_UVLO_VDDBATT_MASK),
REGMAP_IRQ_REG(CS40L50_GLOBAL_ERROR_IRQ, CS40L50_IRQ1_INT_18_OFFSET,
CS40L50_GLOBAL_ERROR_MASK),
};
static struct regmap_irq_chip cs40l50_irq_chip = {
.name = "cs40l50",
.status_base = CS40L50_IRQ1_INT_1,
.mask_base = CS40L50_IRQ1_MASK_1,
.ack_base = CS40L50_IRQ1_INT_1,
.num_regs = 22,
.irqs = cs40l50_reg_irqs,
.num_irqs = ARRAY_SIZE(cs40l50_reg_irqs),
.runtime_pm = true,
};
int cs40l50_dsp_write(struct device *dev, struct regmap *regmap, u32 val)
{
int i, ret;
u32 ack;
/* Device NAKs if hibernating, so optionally retry */
for (i = 0; i < CS40L50_DSP_TIMEOUT_COUNT; i++) {
ret = regmap_write(regmap, CS40L50_DSP_QUEUE, val);
if (!ret)
break;
usleep_range(CS40L50_DSP_POLL_US, CS40L50_DSP_POLL_US + 100);
}
/* If the write never took place, no need to check for the ACK */
if (i == CS40L50_DSP_TIMEOUT_COUNT) {
dev_err(dev, "Timed out writing %#X to DSP: %d\n", val, ret);
return ret;
}
ret = regmap_read_poll_timeout(regmap, CS40L50_DSP_QUEUE, ack, !ack,
CS40L50_DSP_POLL_US,
CS40L50_DSP_POLL_US * CS40L50_DSP_TIMEOUT_COUNT);
if (ret)
dev_err(dev, "DSP failed to ACK %#X: %d\n", val, ret);
return ret;
}
EXPORT_SYMBOL_GPL(cs40l50_dsp_write);
static const struct cs_dsp_region cs40l50_dsp_regions[] = {
{ .type = WMFW_HALO_PM_PACKED, .base = CS40L50_PMEM_0 },
{ .type = WMFW_HALO_XM_PACKED, .base = CS40L50_XMEM_PACKED_0 },
{ .type = WMFW_HALO_YM_PACKED, .base = CS40L50_YMEM_PACKED_0 },
{ .type = WMFW_ADSP2_XM, .base = CS40L50_XMEM_UNPACKED24_0 },
{ .type = WMFW_ADSP2_YM, .base = CS40L50_YMEM_UNPACKED24_0 },
};
static const struct reg_sequence cs40l50_internal_vamp_config[] = {
{ CS40L50_BST_LPMODE_SEL, CS40L50_DCM_LOW_POWER },
{ CS40L50_BLOCK_ENABLES2, CS40L50_OVERTEMP_WARN },
};
static const struct reg_sequence cs40l50_irq_mask_override[] = {
{ CS40L50_IRQ1_MASK_2, CS40L50_IRQ_MASK_2_OVERRIDE },
{ CS40L50_IRQ1_MASK_20, CS40L50_IRQ_MASK_20_OVERRIDE },
};
static int cs40l50_wseq_init(struct cs40l50 *cs40l50)
{
struct cs_dsp *dsp = &cs40l50->dsp;
cs40l50->wseqs[CS40L50_STANDBY].ctl = cs_dsp_get_ctl(dsp, "STANDBY_SEQUENCE",
WMFW_ADSP2_XM,
CS40L50_PM_ALGO);
if (!cs40l50->wseqs[CS40L50_STANDBY].ctl) {
dev_err(cs40l50->dev, "Control not found for standby sequence\n");
return -ENOENT;
}
cs40l50->wseqs[CS40L50_ACTIVE].ctl = cs_dsp_get_ctl(dsp, "ACTIVE_SEQUENCE",
WMFW_ADSP2_XM,
CS40L50_PM_ALGO);
if (!cs40l50->wseqs[CS40L50_ACTIVE].ctl) {
dev_err(cs40l50->dev, "Control not found for active sequence\n");
return -ENOENT;
}
cs40l50->wseqs[CS40L50_PWR_ON].ctl = cs_dsp_get_ctl(dsp, "PM_PWR_ON_SEQ",
WMFW_ADSP2_XM,
CS40L50_PM_ALGO);
if (!cs40l50->wseqs[CS40L50_PWR_ON].ctl) {
dev_err(cs40l50->dev, "Control not found for power-on sequence\n");
return -ENOENT;
}
return cs_dsp_wseq_init(&cs40l50->dsp, cs40l50->wseqs, ARRAY_SIZE(cs40l50->wseqs));
}
static int cs40l50_dsp_config(struct cs40l50 *cs40l50)
{
int ret;
/* Configure internal V_AMP supply */
ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_internal_vamp_config,
ARRAY_SIZE(cs40l50_internal_vamp_config));
if (ret)
return ret;
ret = cs_dsp_wseq_multi_write(&cs40l50->dsp, &cs40l50->wseqs[CS40L50_PWR_ON],
cs40l50_internal_vamp_config, CS_DSP_WSEQ_FULL,
ARRAY_SIZE(cs40l50_internal_vamp_config), false);
if (ret)
return ret;
/* Override firmware defaults for IRQ masks */
ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_irq_mask_override,
ARRAY_SIZE(cs40l50_irq_mask_override));
if (ret)
return ret;
return cs_dsp_wseq_multi_write(&cs40l50->dsp, &cs40l50->wseqs[CS40L50_PWR_ON],
cs40l50_irq_mask_override, CS_DSP_WSEQ_FULL,
ARRAY_SIZE(cs40l50_irq_mask_override), false);
}
static int cs40l50_dsp_post_run(struct cs_dsp *dsp)
{
struct cs40l50 *cs40l50 = container_of(dsp, struct cs40l50, dsp);
int ret;
ret = cs40l50_wseq_init(cs40l50);
if (ret)
return ret;
ret = cs40l50_dsp_config(cs40l50);
if (ret) {
dev_err(cs40l50->dev, "Failed to configure DSP: %d\n", ret);
return ret;
}
ret = devm_mfd_add_devices(cs40l50->dev, PLATFORM_DEVID_NONE, cs40l50_devs,
ARRAY_SIZE(cs40l50_devs), NULL, 0, NULL);
if (ret)
dev_err(cs40l50->dev, "Failed to add child devices: %d\n", ret);
return ret;
}
static const struct cs_dsp_client_ops client_ops = {
.post_run = cs40l50_dsp_post_run,
};
static void cs40l50_dsp_remove(void *data)
{
cs_dsp_remove(data);
}
static int cs40l50_dsp_init(struct cs40l50 *cs40l50)
{
int ret;
cs40l50->dsp.num = 1;
cs40l50->dsp.type = WMFW_HALO;
cs40l50->dsp.dev = cs40l50->dev;
cs40l50->dsp.regmap = cs40l50->regmap;
cs40l50->dsp.base = CS40L50_CORE_BASE;
cs40l50->dsp.base_sysinfo = CS40L50_SYS_INFO_ID;
cs40l50->dsp.mem = cs40l50_dsp_regions;
cs40l50->dsp.num_mems = ARRAY_SIZE(cs40l50_dsp_regions);
cs40l50->dsp.no_core_startstop = true;
cs40l50->dsp.client_ops = &client_ops;
ret = cs_dsp_halo_init(&cs40l50->dsp);
if (ret)
return ret;
return devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_remove,
&cs40l50->dsp);
}
static int cs40l50_reset_dsp(struct cs40l50 *cs40l50)
{
int ret;
mutex_lock(&cs40l50->lock);
if (cs40l50->dsp.running)
cs_dsp_stop(&cs40l50->dsp);
if (cs40l50->dsp.booted)
cs_dsp_power_down(&cs40l50->dsp);
ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_SHUTDOWN);
if (ret)
goto err_mutex;
ret = cs_dsp_power_up(&cs40l50->dsp, cs40l50->fw, "cs40l50.wmfw",
cs40l50->bin, "cs40l50.bin", "cs40l50");
if (ret)
goto err_mutex;
ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_SYSTEM_RESET);
if (ret)
goto err_mutex;
ret = cs40l50_dsp_write(cs40l50->dev, cs40l50->regmap, CS40L50_PREVENT_HIBER);
if (ret)
goto err_mutex;
ret = cs_dsp_run(&cs40l50->dsp);
err_mutex:
mutex_unlock(&cs40l50->lock);
return ret;
}
static void cs40l50_dsp_power_down(void *data)
{
cs_dsp_power_down(data);
}
static void cs40l50_dsp_stop(void *data)
{
cs_dsp_stop(data);
}
static void cs40l50_dsp_bringup(const struct firmware *bin, void *context)
{
struct cs40l50 *cs40l50 = context;
u32 nwaves;
int ret;
/* Wavetable is optional; bringup DSP regardless */
cs40l50->bin = bin;
ret = cs40l50_reset_dsp(cs40l50);
if (ret) {
dev_err(cs40l50->dev, "Failed to reset DSP: %d\n", ret);
goto err_fw;
}
ret = regmap_read(cs40l50->regmap, CS40L50_NUM_WAVES, &nwaves);
if (ret)
goto err_fw;
dev_info(cs40l50->dev, "%u RAM effects loaded\n", nwaves);
/* Add teardown actions for first-time bringup */
ret = devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_power_down,
&cs40l50->dsp);
if (ret) {
dev_err(cs40l50->dev, "Failed to add power down action: %d\n", ret);
goto err_fw;
}
ret = devm_add_action_or_reset(cs40l50->dev, cs40l50_dsp_stop, &cs40l50->dsp);
if (ret)
dev_err(cs40l50->dev, "Failed to add stop action: %d\n", ret);
err_fw:
release_firmware(cs40l50->bin);
release_firmware(cs40l50->fw);
}
static void cs40l50_request_firmware(const struct firmware *fw, void *context)
{
struct cs40l50 *cs40l50 = context;
int ret;
if (!fw) {
dev_err(cs40l50->dev, "No firmware file found\n");
return;
}
cs40l50->fw = fw;
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, CS40L50_WT,
cs40l50->dev, GFP_KERNEL, cs40l50,
cs40l50_dsp_bringup);
if (ret) {
dev_err(cs40l50->dev, "Failed to request %s: %d\n", CS40L50_WT, ret);
release_firmware(cs40l50->fw);
}
}
struct cs40l50_irq {
const char *name;
int virq;
};
static struct cs40l50_irq cs40l50_irqs[] = {
{ "DSP", },
{ "Global", },
{ "Boost UVLO", },
{ "Boost current limit", },
{ "Boost short", },
{ "Boost undervolt", },
{ "Overtemp", },
{ "Amp short", },
};
static const struct reg_sequence cs40l50_err_rls[] = {
{ CS40L50_ERR_RLS, CS40L50_GLOBAL_ERR_RLS_SET },
{ CS40L50_ERR_RLS, CS40L50_GLOBAL_ERR_RLS_CLEAR },
};
static irqreturn_t cs40l50_hw_err(int irq, void *data)
{
struct cs40l50 *cs40l50 = data;
int ret = 0, i;
mutex_lock(&cs40l50->lock);
/* Log hardware interrupt and execute error release sequence */
for (i = 1; i < ARRAY_SIZE(cs40l50_irqs); i++) {
if (cs40l50_irqs[i].virq == irq) {
dev_err(cs40l50->dev, "%s error\n", cs40l50_irqs[i].name);
ret = regmap_multi_reg_write(cs40l50->regmap, cs40l50_err_rls,
ARRAY_SIZE(cs40l50_err_rls));
break;
}
}
mutex_unlock(&cs40l50->lock);
return IRQ_RETVAL(!ret);
}
static irqreturn_t cs40l50_dsp_queue(int irq, void *data)
{
struct cs40l50 *cs40l50 = data;
u32 rd_ptr, val, wt_ptr;
int ret = 0;
mutex_lock(&cs40l50->lock);
/* Read from DSP queue, log, and update read pointer */
while (!ret) {
ret = regmap_read(cs40l50->regmap, CS40L50_DSP_QUEUE_WT, &wt_ptr);
if (ret)
break;
ret = regmap_read(cs40l50->regmap, CS40L50_DSP_QUEUE_RD, &rd_ptr);
if (ret)
break;
/* Check if queue is empty */
if (wt_ptr == rd_ptr)
break;
ret = regmap_read(cs40l50->regmap, rd_ptr, &val);
if (ret)
break;
dev_dbg(cs40l50->dev, "DSP payload: %#X", val);
rd_ptr += sizeof(u32);
if (rd_ptr > CS40L50_DSP_QUEUE_END)
rd_ptr = CS40L50_DSP_QUEUE_BASE;
ret = regmap_write(cs40l50->regmap, CS40L50_DSP_QUEUE_RD, rd_ptr);
}
mutex_unlock(&cs40l50->lock);
return IRQ_RETVAL(!ret);
}
static int cs40l50_irq_init(struct cs40l50 *cs40l50)
{
int ret, i, virq;
ret = devm_regmap_add_irq_chip(cs40l50->dev, cs40l50->regmap, cs40l50->irq,
IRQF_ONESHOT | IRQF_SHARED, 0,
&cs40l50_irq_chip, &cs40l50->irq_data);
if (ret) {
dev_err(cs40l50->dev, "Failed adding IRQ chip\n");
return ret;
}
for (i = 0; i < ARRAY_SIZE(cs40l50_irqs); i++) {
virq = regmap_irq_get_virq(cs40l50->irq_data, i);
if (virq < 0) {
dev_err(cs40l50->dev, "Failed getting virq for %s\n",
cs40l50_irqs[i].name);
return virq;
}
cs40l50_irqs[i].virq = virq;
/* Handle DSP and hardware interrupts separately */
ret = devm_request_threaded_irq(cs40l50->dev, virq, NULL,
i ? cs40l50_hw_err : cs40l50_dsp_queue,
IRQF_ONESHOT | IRQF_SHARED,
cs40l50_irqs[i].name, cs40l50);
if (ret) {
return dev_err_probe(cs40l50->dev, ret,
"Failed requesting %s IRQ\n",
cs40l50_irqs[i].name);
}
}
return 0;
}
static int cs40l50_get_model(struct cs40l50 *cs40l50)
{
int ret;
ret = regmap_read(cs40l50->regmap, CS40L50_DEVID, &cs40l50->devid);
if (ret)
return ret;
if (cs40l50->devid != CS40L50_DEVID_A)
return -EINVAL;
ret = regmap_read(cs40l50->regmap, CS40L50_REVID, &cs40l50->revid);
if (ret)
return ret;
if (cs40l50->revid < CS40L50_REVID_B0)
return -EINVAL;
dev_dbg(cs40l50->dev, "Cirrus Logic CS40L50 rev. %02X\n", cs40l50->revid);
return 0;
}
static int cs40l50_pm_runtime_setup(struct device *dev)
{
int ret;
pm_runtime_set_autosuspend_delay(dev, CS40L50_AUTOSUSPEND_MS);
pm_runtime_use_autosuspend(dev);
pm_runtime_get_noresume(dev);
ret = pm_runtime_set_active(dev);
if (ret)
return ret;
return devm_pm_runtime_enable(dev);
}
int cs40l50_probe(struct cs40l50 *cs40l50)
{
struct device *dev = cs40l50->dev;
int ret;
mutex_init(&cs40l50->lock);
cs40l50->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(cs40l50->reset_gpio))
return dev_err_probe(dev, PTR_ERR(cs40l50->reset_gpio),
"Failed getting reset GPIO\n");
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(cs40l50_supplies),
cs40l50_supplies);
if (ret)
return dev_err_probe(dev, ret, "Failed getting supplies\n");
/* Ensure minimum reset pulse width */
usleep_range(CS40L50_RESET_PULSE_US, CS40L50_RESET_PULSE_US + 100);
gpiod_set_value_cansleep(cs40l50->reset_gpio, 0);
/* Wait for control port to be ready */
usleep_range(CS40L50_CP_READY_US, CS40L50_CP_READY_US + 100);
ret = cs40l50_get_model(cs40l50);
if (ret)
return dev_err_probe(dev, ret, "Failed to get part number\n");
ret = cs40l50_dsp_init(cs40l50);
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize DSP\n");
ret = cs40l50_pm_runtime_setup(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize runtime PM\n");
ret = cs40l50_irq_init(cs40l50);
if (ret)
return ret;
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, CS40L50_FW,
dev, GFP_KERNEL, cs40l50, cs40l50_request_firmware);
if (ret)
return dev_err_probe(dev, ret, "Failed to request %s\n", CS40L50_FW);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
}
EXPORT_SYMBOL_GPL(cs40l50_probe);
int cs40l50_remove(struct cs40l50 *cs40l50)
{
gpiod_set_value_cansleep(cs40l50->reset_gpio, 1);
return 0;
}
EXPORT_SYMBOL_GPL(cs40l50_remove);
static int cs40l50_runtime_suspend(struct device *dev)
{
struct cs40l50 *cs40l50 = dev_get_drvdata(dev);
return regmap_write(cs40l50->regmap, CS40L50_DSP_QUEUE, CS40L50_ALLOW_HIBER);
}
static int cs40l50_runtime_resume(struct device *dev)
{
struct cs40l50 *cs40l50 = dev_get_drvdata(dev);
return cs40l50_dsp_write(dev, cs40l50->regmap, CS40L50_PREVENT_HIBER);
}
EXPORT_GPL_DEV_PM_OPS(cs40l50_pm_ops) = {
RUNTIME_PM_OPS(cs40l50_runtime_suspend, cs40l50_runtime_resume, NULL)
};
MODULE_DESCRIPTION("CS40L50 Advanced Haptic Driver");
MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(FW_CS_DSP);

68
drivers/mfd/cs40l50-i2c.c Normal file
View File

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CS40L50 Advanced Haptic Driver with waveform memory,
* integrated DSP, and closed-loop algorithms
*
* Copyright 2024 Cirrus Logic, Inc.
*
* Author: James Ogletree <james.ogletree@cirrus.com>
*/
#include <linux/i2c.h>
#include <linux/mfd/cs40l50.h>
static int cs40l50_i2c_probe(struct i2c_client *i2c)
{
struct cs40l50 *cs40l50;
cs40l50 = devm_kzalloc(&i2c->dev, sizeof(*cs40l50), GFP_KERNEL);
if (!cs40l50)
return -ENOMEM;
i2c_set_clientdata(i2c, cs40l50);
cs40l50->dev = &i2c->dev;
cs40l50->irq = i2c->irq;
cs40l50->regmap = devm_regmap_init_i2c(i2c, &cs40l50_regmap);
if (IS_ERR(cs40l50->regmap))
return dev_err_probe(cs40l50->dev, PTR_ERR(cs40l50->regmap),
"Failed to initialize register map\n");
return cs40l50_probe(cs40l50);
}
static void cs40l50_i2c_remove(struct i2c_client *i2c)
{
struct cs40l50 *cs40l50 = i2c_get_clientdata(i2c);
cs40l50_remove(cs40l50);
}
static const struct i2c_device_id cs40l50_id_i2c[] = {
{ "cs40l50" },
{}
};
MODULE_DEVICE_TABLE(i2c, cs40l50_id_i2c);
static const struct of_device_id cs40l50_of_match[] = {
{ .compatible = "cirrus,cs40l50" },
{}
};
MODULE_DEVICE_TABLE(of, cs40l50_of_match);
static struct i2c_driver cs40l50_i2c_driver = {
.driver = {
.name = "cs40l50",
.of_match_table = cs40l50_of_match,
.pm = pm_ptr(&cs40l50_pm_ops),
},
.id_table = cs40l50_id_i2c,
.probe = cs40l50_i2c_probe,
.remove = cs40l50_i2c_remove,
};
module_i2c_driver(cs40l50_i2c_driver);
MODULE_DESCRIPTION("CS40L50 I2C Driver");
MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
MODULE_LICENSE("GPL");

68
drivers/mfd/cs40l50-spi.c Normal file
View File

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
/*
* CS40L50 Advanced Haptic Driver with waveform memory,
* integrated DSP, and closed-loop algorithms
*
* Copyright 2024 Cirrus Logic, Inc.
*
* Author: James Ogletree <james.ogletree@cirrus.com>
*/
#include <linux/mfd/cs40l50.h>
#include <linux/spi/spi.h>
static int cs40l50_spi_probe(struct spi_device *spi)
{
struct cs40l50 *cs40l50;
cs40l50 = devm_kzalloc(&spi->dev, sizeof(*cs40l50), GFP_KERNEL);
if (!cs40l50)
return -ENOMEM;
spi_set_drvdata(spi, cs40l50);
cs40l50->dev = &spi->dev;
cs40l50->irq = spi->irq;
cs40l50->regmap = devm_regmap_init_spi(spi, &cs40l50_regmap);
if (IS_ERR(cs40l50->regmap))
return dev_err_probe(cs40l50->dev, PTR_ERR(cs40l50->regmap),
"Failed to initialize register map\n");
return cs40l50_probe(cs40l50);
}
static void cs40l50_spi_remove(struct spi_device *spi)
{
struct cs40l50 *cs40l50 = spi_get_drvdata(spi);
cs40l50_remove(cs40l50);
}
static const struct spi_device_id cs40l50_id_spi[] = {
{ "cs40l50" },
{}
};
MODULE_DEVICE_TABLE(spi, cs40l50_id_spi);
static const struct of_device_id cs40l50_of_match[] = {
{ .compatible = "cirrus,cs40l50" },
{}
};
MODULE_DEVICE_TABLE(of, cs40l50_of_match);
static struct spi_driver cs40l50_spi_driver = {
.driver = {
.name = "cs40l50",
.of_match_table = cs40l50_of_match,
.pm = pm_ptr(&cs40l50_pm_ops),
},
.id_table = cs40l50_id_spi,
.probe = cs40l50_spi_probe,
.remove = cs40l50_spi_remove,
};
module_spi_driver(cs40l50_spi_driver);
MODULE_DESCRIPTION("CS40L50 SPI Driver");
MODULE_AUTHOR("James Ogletree, Cirrus Logic Inc. <james.ogletree@cirrus.com>");
MODULE_LICENSE("GPL");

View File

@ -54,7 +54,7 @@ static void da9055_i2c_remove(struct i2c_client *i2c)
* and CODEC, which must be different to operate together.
*/
static const struct i2c_device_id da9055_i2c_id[] = {
{"da9055-pmic", 0},
{ "da9055-pmic" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);

View File

@ -103,7 +103,7 @@ static const struct software_node spt_spi_node = {
.properties = spt_spi_properties,
};
static const struct intel_lpss_platform_info spt_info = {
static const struct intel_lpss_platform_info spt_spi_info = {
.clk_rate = 120000000,
.swnode = &spt_spi_node,
};
@ -148,7 +148,7 @@ static const struct software_node bxt_spi_node = {
.properties = bxt_spi_properties,
};
static const struct intel_lpss_platform_info bxt_info = {
static const struct intel_lpss_platform_info bxt_spi_info = {
.clk_rate = 100000000,
.swnode = &bxt_spi_node,
};
@ -216,7 +216,7 @@ static const struct software_node cnl_spi_node = {
.properties = cnl_spi_properties,
};
static const struct intel_lpss_platform_info cnl_info = {
static const struct intel_lpss_platform_info cnl_spi_info = {
.clk_rate = 120000000,
.swnode = &cnl_spi_node,
};
@ -240,7 +240,7 @@ static const struct software_node tgl_spi_node = {
.properties = tgl_spi_properties,
};
static const struct intel_lpss_platform_info tgl_info = {
static const struct intel_lpss_platform_info tgl_spi_info = {
.clk_rate = 100000000,
.swnode = &tgl_spi_node,
};
@ -249,8 +249,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CML-LP */
{ PCI_VDEVICE(INTEL, 0x02a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x02a9), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x02ab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x02c5), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02c6), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02c7), (kernel_ulong_t)&spt_uart_info },
@ -258,18 +258,18 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x02e9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02ea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02eb), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&cnl_spi_info },
/* CML-H */
{ PCI_VDEVICE(INTEL, 0x06a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x06a9), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x06aa), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x06aa), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x06c7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x06e8), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06e9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06ea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06eb), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x06fb), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x06fb), (kernel_ulong_t)&cnl_spi_info },
/* BXT A-Step */
{ PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x0aae), (kernel_ulong_t)&bxt_i2c_info },
@ -282,9 +282,9 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0abc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x0abe), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x0ac0), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x0ac2), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x0ac4), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x0ac6), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x0ac2), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x0ac4), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x0ac6), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x0aee), (kernel_ulong_t)&bxt_uart_info },
/* BXT B-Step */
{ PCI_VDEVICE(INTEL, 0x1aac), (kernel_ulong_t)&bxt_i2c_info },
@ -298,9 +298,9 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x1abc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x1abe), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x1ac0), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x1ac2), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x1ac2), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x1ac4), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info },
/* EBG */
{ PCI_VDEVICE(INTEL, 0x1bad), (kernel_ulong_t)&bxt_uart_info },
@ -317,15 +317,15 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x31bc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x31be), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x31c0), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x31c2), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x31c4), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x31c6), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x31ee), (kernel_ulong_t)&bxt_uart_info },
/* ICL-LP */
{ PCI_VDEVICE(INTEL, 0x34a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x34a9), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x34aa), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x34aa), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x34ab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x34c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34c7), (kernel_ulong_t)&spt_uart_info },
@ -333,15 +333,15 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x34e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34eb), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x34fb), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x34fb), (kernel_ulong_t)&cnl_spi_info },
/* ICL-N */
{ PCI_VDEVICE(INTEL, 0x38a8), (kernel_ulong_t)&spt_uart_info },
/* TGL-H */
{ PCI_VDEVICE(INTEL, 0x43a7), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x43a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x43a9), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x43aa), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x43ab), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x43aa), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x43ab), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x43ad), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43ae), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43d8), (kernel_ulong_t)&bxt_i2c_info },
@ -350,14 +350,14 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x43e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43eb), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x43fb), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x43fd), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x43fb), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x43fd), (kernel_ulong_t)&tgl_spi_info },
/* EHL */
{ PCI_VDEVICE(INTEL, 0x4b28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4b29), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4b2a), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x4b2b), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x4b37), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x4b2a), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x4b2b), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x4b37), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x4b44), (kernel_ulong_t)&ehl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4b45), (kernel_ulong_t)&ehl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4b4b), (kernel_ulong_t)&ehl_i2c_info },
@ -370,8 +370,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* JSL */
{ PCI_VDEVICE(INTEL, 0x4da8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4da9), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x4daa), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x4daa), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x4dab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x4dc5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dc6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dc7), (kernel_ulong_t)&spt_uart_info },
@ -379,12 +379,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x4de9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4deb), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x4dfb), (kernel_ulong_t)&cnl_spi_info },
/* ADL-P */
{ PCI_VDEVICE(INTEL, 0x51a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x51a9), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x51aa), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x51aa), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x51ab), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x51c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51c7), (kernel_ulong_t)&bxt_uart_info },
@ -394,12 +394,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x51e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51eb), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x51fb), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x51fb), (kernel_ulong_t)&tgl_spi_info },
/* ADL-M */
{ PCI_VDEVICE(INTEL, 0x54a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x54a9), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x54aa), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x54ab), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x54aa), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x54ab), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x54c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54c7), (kernel_ulong_t)&bxt_uart_info },
@ -407,7 +407,7 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x54e9), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54ea), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54eb), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x54fb), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x54fb), (kernel_ulong_t)&tgl_spi_info },
/* APL */
{ PCI_VDEVICE(INTEL, 0x5aac), (kernel_ulong_t)&apl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x5aae), (kernel_ulong_t)&apl_i2c_info },
@ -420,46 +420,46 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x5abc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x5abe), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x5ac0), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x5ac2), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x5ac2), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_info },
/* RPL-S */
{ PCI_VDEVICE(INTEL, 0x7a28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7a29), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7a2a), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7a2b), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7a2a), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7a2b), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7a4c), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4d), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4e), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a4f), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a5c), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7a79), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7a7b), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7a79), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7a7b), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7a7c), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a7d), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7a7e), (kernel_ulong_t)&bxt_uart_info },
/* ADL-S */
{ PCI_VDEVICE(INTEL, 0x7aa8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7aa9), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7aaa), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7aaa), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7aab), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7acc), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7acd), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7ace), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7acf), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7adc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7af9), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7af9), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7afb), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7afc), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7afd), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7afe), (kernel_ulong_t)&bxt_uart_info },
/* MTL-P */
{ PCI_VDEVICE(INTEL, 0x7e25), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7e26), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7e27), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7e30), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7e46), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7e27), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7e30), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7e46), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7e50), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7e51), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7e52), (kernel_ulong_t)&bxt_uart_info },
@ -470,22 +470,22 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* MTP-S */
{ PCI_VDEVICE(INTEL, 0x7f28), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7f29), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7f2a), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7f2b), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7f2a), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7f2b), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7f4c), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f4d), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f4e), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f4f), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f5c), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7f5d), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x7f5e), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7f5f), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0x7f5e), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7f5f), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0x7f7a), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x7f7b), (kernel_ulong_t)&bxt_i2c_info },
/* LKF */
{ PCI_VDEVICE(INTEL, 0x98a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x98a9), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0x98aa), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x98aa), (kernel_ulong_t)&bxt_spi_info },
{ PCI_VDEVICE(INTEL, 0x98c5), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x98c6), (kernel_ulong_t)&bxt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x98c7), (kernel_ulong_t)&bxt_uart_info },
@ -496,8 +496,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* SPT-LP */
{ PCI_VDEVICE(INTEL, 0x9d27), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9d29), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0x9d2a), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0x9d29), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0x9d2a), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0x9d60), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9d61), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9d62), (kernel_ulong_t)&spt_i2c_info },
@ -508,8 +508,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CNL-LP */
{ PCI_VDEVICE(INTEL, 0x9da8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9da9), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9daa), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x9dab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x9daa), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x9dab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0x9dc5), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dc6), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dc7), (kernel_ulong_t)&spt_uart_info },
@ -517,12 +517,12 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x9de9), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dea), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9deb), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9dfb), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0x9dfb), (kernel_ulong_t)&cnl_spi_info },
/* TGL-LP */
{ PCI_VDEVICE(INTEL, 0xa0a8), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0a9), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0aa), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0ab), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0aa), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa0ab), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa0c5), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0c6), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0c7), (kernel_ulong_t)&bxt_uart_info },
@ -532,20 +532,20 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0xa0db), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0dc), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0dd), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa0de), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0df), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0de), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa0df), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa0e8), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0e9), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0ea), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0eb), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa0fb), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0fd), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0fe), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa0fb), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa0fd), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa0fe), (kernel_ulong_t)&cnl_spi_info },
/* SPT-H */
{ PCI_VDEVICE(INTEL, 0xa127), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa128), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa129), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa12a), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa129), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa12a), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa162), (kernel_ulong_t)&spt_i2c_info },
@ -553,8 +553,8 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* KBL-H */
{ PCI_VDEVICE(INTEL, 0xa2a7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa2a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa2a9), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa2aa), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa2a9), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa2aa), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa2e0), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa2e1), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa2e2), (kernel_ulong_t)&spt_i2c_info },
@ -563,19 +563,19 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* CNL-H */
{ PCI_VDEVICE(INTEL, 0xa328), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa329), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa32a), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa32b), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa32a), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa32b), (kernel_ulong_t)&cnl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa347), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa368), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa369), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa36a), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa36b), (kernel_ulong_t)&cnl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa37b), (kernel_ulong_t)&cnl_info },
{ PCI_VDEVICE(INTEL, 0xa37b), (kernel_ulong_t)&cnl_spi_info },
/* CML-V */
{ PCI_VDEVICE(INTEL, 0xa3a7), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa3a8), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa3a9), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa3aa), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa3a9), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa3aa), (kernel_ulong_t)&spt_spi_info },
{ PCI_VDEVICE(INTEL, 0xa3e0), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa3e1), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa3e2), (kernel_ulong_t)&spt_i2c_info },
@ -584,9 +584,9 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
/* LNL-M */
{ PCI_VDEVICE(INTEL, 0xa825), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa826), (kernel_ulong_t)&bxt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa827), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0xa830), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0xa846), (kernel_ulong_t)&tgl_info },
{ PCI_VDEVICE(INTEL, 0xa827), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa830), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa846), (kernel_ulong_t)&tgl_spi_info },
{ PCI_VDEVICE(INTEL, 0xa850), (kernel_ulong_t)&ehl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa851), (kernel_ulong_t)&ehl_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa852), (kernel_ulong_t)&bxt_uart_info },

View File

@ -581,5 +581,6 @@ static struct platform_driver bxtwc_driver = {
module_platform_driver(bxtwc_driver);
MODULE_DESCRIPTION("Intel Broxton Whiskey Cove PMIC MFD core driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Qipeng Zha <qipeng.zha@intel.com>");

View File

@ -137,7 +137,9 @@ static const struct regmap_irq_chip crystal_cove_irq_chip = {
/* PWM consumed by the Intel GFX */
static struct pwm_lookup crc_pwm_lookup[] = {
PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_pmic_backlight", 0, PWM_POLARITY_NORMAL),
PWM_LOOKUP_WITH_MODULE("crystal_cove_pwm", 0, "0000:00:02.0",
"pwm_pmic_backlight", 0, PWM_POLARITY_NORMAL,
"pwm-crc"),
};
struct crystal_cove_config {

View File

@ -11,7 +11,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/regmap.h>
@ -225,14 +225,12 @@ static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led)
static void lm3533_enable(struct lm3533 *lm3533)
{
if (gpio_is_valid(lm3533->gpio_hwen))
gpio_set_value(lm3533->gpio_hwen, 1);
gpiod_set_value(lm3533->hwen, 1);
}
static void lm3533_disable(struct lm3533 *lm3533)
{
if (gpio_is_valid(lm3533->gpio_hwen))
gpio_set_value(lm3533->gpio_hwen, 0);
gpiod_set_value(lm3533->hwen, 0);
}
enum lm3533_attribute_type {
@ -483,18 +481,10 @@ static int lm3533_device_init(struct lm3533 *lm3533)
return -EINVAL;
}
lm3533->gpio_hwen = pdata->gpio_hwen;
if (gpio_is_valid(lm3533->gpio_hwen)) {
ret = devm_gpio_request_one(lm3533->dev, lm3533->gpio_hwen,
GPIOF_OUT_INIT_LOW, "lm3533-hwen");
if (ret < 0) {
dev_err(lm3533->dev,
"failed to request HWEN GPIO %d\n",
lm3533->gpio_hwen);
return ret;
}
}
lm3533->hwen = devm_gpiod_get(lm3533->dev, NULL, GPIOD_OUT_LOW);
if (IS_ERR(lm3533->hwen))
return dev_err_probe(lm3533->dev, PTR_ERR(lm3533->hwen), "failed to request HWEN GPIO\n");
gpiod_set_consumer_name(lm3533->hwen, "lm3533-hwen");
lm3533_enable(lm3533);
@ -614,8 +604,8 @@ static void lm3533_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id lm3533_i2c_ids[] = {
{ "lm3533", 0 },
{ },
{ "lm3533" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids);

View File

@ -126,7 +126,7 @@ static int lp3943_probe(struct i2c_client *cl)
}
static const struct i2c_device_id lp3943_ids[] = {
{ "lp3943", 0 },
{ "lp3943" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lp3943_ids);

View File

@ -68,8 +68,8 @@ static const struct of_device_id of_lp873x_match_table[] = {
MODULE_DEVICE_TABLE(of, of_lp873x_match_table);
static const struct i2c_device_id lp873x_id_table[] = {
{ "lp873x", 0 },
{ },
{ "lp873x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lp873x_id_table);

View File

@ -106,8 +106,8 @@ static void lp87565_shutdown(struct i2c_client *client)
}
static const struct i2c_device_id lp87565_id_table[] = {
{ "lp87565-q1", 0 },
{ },
{ "lp87565-q1" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lp87565_id_table);

View File

@ -216,7 +216,7 @@ static void lp8788_remove(struct i2c_client *cl)
}
static const struct i2c_device_id lp8788_ids[] = {
{"lp8788", 0},
{ "lp8788" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lp8788_ids);

View File

@ -18,21 +18,14 @@
static int madera_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct madera *madera;
const struct regmap_config *regmap_16bit_config = NULL;
const struct regmap_config *regmap_32bit_config = NULL;
const void *of_data;
unsigned long type;
const char *name;
int ret;
of_data = of_device_get_match_data(&spi->dev);
if (of_data)
type = (unsigned long)of_data;
else
type = id->driver_data;
type = (unsigned long)spi_get_device_match_data(spi);
switch (type) {
case CS47L15:
if (IS_ENABLED(CONFIG_MFD_CS47L15)) {

View File

@ -397,7 +397,7 @@ static int max14577_i2c_probe(struct i2c_client *i2c)
return ret;
}
max14577->dev_type = (enum maxim_device_type)i2c_get_match_data(i2c);
max14577->dev_type = (kernel_ulong_t)i2c_get_match_data(i2c);
max14577_print_dev_type(max14577);

View File

@ -300,7 +300,7 @@ MODULE_DEVICE_TABLE(of, max8907_of_match);
#endif
static const struct i2c_device_id max8907_i2c_id[] = {
{"max8907", 0},
{ "max8907" },
{}
};
MODULE_DEVICE_TABLE(i2c, max8907_i2c_id);

View File

@ -127,8 +127,8 @@ EXPORT_SYMBOL(max8925_set_bits);
static const struct i2c_device_id max8925_id_table[] = {
{ "max8925", 0 },
{ },
{ "max8925" },
{ }
};
static int max8925_dt_init(struct device_node *np, struct device *dev,

View File

@ -29,7 +29,6 @@
#include <linux/bcd.h>
#include <linux/slab.h>
#include <linux/mfd/menelaus.h>
#include <linux/gpio.h>
#include <asm/mach/irq.h>
@ -1231,7 +1230,7 @@ static void menelaus_remove(struct i2c_client *client)
}
static const struct i2c_device_id menelaus_id[] = {
{ "menelaus", 0 },
{ "menelaus" },
{ }
};
MODULE_DEVICE_TABLE(i2c, menelaus_id);

View File

@ -87,7 +87,7 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
}
}
ACPI_COMPANION_SET(&pdev->dev, adev ?: parent);
device_set_node(&pdev->dev, acpi_fwnode_handle(adev ?: parent));
}
#else
static inline void mfd_acpi_add_device(const struct mfd_cell *cell,
@ -131,8 +131,7 @@ allocate_of_node:
of_entry->np = np;
list_add_tail(&of_entry->list, &mfd_of_node_list);
pdev->dev.of_node = np;
pdev->dev.fwnode = &np->fwnode;
device_set_node(&pdev->dev, of_fwnode_handle(np));
#endif
return 0;
}
@ -437,5 +436,6 @@ int devm_mfd_add_devices(struct device *dev, int id,
}
EXPORT_SYMBOL(devm_mfd_add_devices);
MODULE_DESCRIPTION("Core MFD support");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");

View File

@ -135,6 +135,9 @@ static const struct mfd_cell mt6323_devs[] = {
static const struct mfd_cell mt6357_devs[] = {
{
.name = "mt6359-auxadc",
.of_compatible = "mediatek,mt6357-auxadc"
}, {
.name = "mt6357-regulator",
}, {
.name = "mt6357-rtc",
@ -175,6 +178,9 @@ static const struct mfd_cell mt6331_mt6332_devs[] = {
static const struct mfd_cell mt6358_devs[] = {
{
.name = "mt6359-auxadc",
.of_compatible = "mediatek,mt6358-auxadc"
}, {
.name = "mt6358-regulator",
.of_compatible = "mediatek,mt6358-regulator"
}, {
@ -194,6 +200,10 @@ static const struct mfd_cell mt6358_devs[] = {
};
static const struct mfd_cell mt6359_devs[] = {
{
.name = "mt6359-auxadc",
.of_compatible = "mediatek,mt6359-auxadc"
},
{ .name = "mt6359-regulator", },
{
.name = "mt6359-rtc",

View File

@ -137,7 +137,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
if (!lradc)
return -ENOMEM;
lradc->soc = (enum mxs_lradc_id)device_get_match_data(&pdev->dev);
lradc->soc = (kernel_ulong_t)device_get_match_data(&pdev->dev);
lradc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(lradc->clk)) {

View File

@ -13,7 +13,6 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/platform_data/usb-omap.h>
#include <linux/pm_runtime.h>

View File

@ -98,8 +98,8 @@
struct usbtll_omap {
void __iomem *base;
int nch; /* num. of channels */
struct clk *ch_clk[]; /* must be the last member */
int nch;
struct clk *ch_clk[] __counted_by(nch);
};
/*-------------------------------------------------------------------------*/
@ -230,8 +230,7 @@ static int usbtll_omap_probe(struct platform_device *pdev)
break;
}
tll = devm_kzalloc(dev, sizeof(*tll) + sizeof(tll->ch_clk[nch]),
GFP_KERNEL);
tll = devm_kzalloc(dev, struct_size(tll, ch_clk, nch), GFP_KERNEL);
if (!tll) {
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);

View File

@ -88,4 +88,5 @@ int pcf50633_gpio_power_supply_set(struct pcf50633 *pcf,
}
EXPORT_SYMBOL_GPL(pcf50633_gpio_power_supply_set);
MODULE_DESCRIPTION("NXP PCF50633 GPIO Driver");
MODULE_LICENSE("GPL");

View File

@ -4,10 +4,13 @@
*/
#include <linux/bitops.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@ -15,8 +18,6 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <dt-bindings/mfd/qcom-pm8008.h>
#define I2C_INTR_STATUS_BASE 0x0550
#define INT_RT_STS_OFFSET 0x10
#define INT_SET_TYPE_OFFSET 0x11
@ -37,34 +38,54 @@ enum {
#define PM8008_PERIPH_0_BASE 0x900
#define PM8008_PERIPH_1_BASE 0x2400
#define PM8008_PERIPH_2_BASE 0xC000
#define PM8008_PERIPH_3_BASE 0xC100
#define PM8008_PERIPH_2_BASE 0xc000
#define PM8008_PERIPH_3_BASE 0xc100
#define PM8008_TEMP_ALARM_ADDR PM8008_PERIPH_1_BASE
#define PM8008_GPIO1_ADDR PM8008_PERIPH_2_BASE
#define PM8008_GPIO2_ADDR PM8008_PERIPH_3_BASE
/* PM8008 IRQ numbers */
#define PM8008_IRQ_MISC_UVLO 0
#define PM8008_IRQ_MISC_OVLO 1
#define PM8008_IRQ_MISC_OTST2 2
#define PM8008_IRQ_MISC_OTST3 3
#define PM8008_IRQ_MISC_LDO_OCP 4
#define PM8008_IRQ_TEMP_ALARM 5
#define PM8008_IRQ_GPIO1 6
#define PM8008_IRQ_GPIO2 7
enum {
SET_TYPE_INDEX,
POLARITY_HI_INDEX,
POLARITY_LO_INDEX,
};
static unsigned int pm8008_config_regs[] = {
static const unsigned int pm8008_config_regs[] = {
INT_SET_TYPE_OFFSET,
INT_POL_HIGH_OFFSET,
INT_POL_LOW_OFFSET,
};
static struct regmap_irq pm8008_irqs[] = {
REGMAP_IRQ_REG(PM8008_IRQ_MISC_UVLO, PM8008_MISC, BIT(0)),
REGMAP_IRQ_REG(PM8008_IRQ_MISC_OVLO, PM8008_MISC, BIT(1)),
REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST2, PM8008_MISC, BIT(2)),
REGMAP_IRQ_REG(PM8008_IRQ_MISC_OTST3, PM8008_MISC, BIT(3)),
REGMAP_IRQ_REG(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC, BIT(4)),
REGMAP_IRQ_REG(PM8008_IRQ_TEMP_ALARM, PM8008_TEMP_ALARM, BIT(0)),
REGMAP_IRQ_REG(PM8008_IRQ_GPIO1, PM8008_GPIO1, BIT(0)),
REGMAP_IRQ_REG(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0)),
#define _IRQ(_irq, _off, _mask, _types) \
[_irq] = { \
.reg_offset = (_off), \
.mask = (_mask), \
.type = { \
.type_reg_offset = (_off), \
.types_supported = (_types), \
}, \
}
static const struct regmap_irq pm8008_irqs[] = {
_IRQ(PM8008_IRQ_MISC_UVLO, PM8008_MISC, BIT(0), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_MISC_OVLO, PM8008_MISC, BIT(1), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_MISC_OTST2, PM8008_MISC, BIT(2), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_MISC_OTST3, PM8008_MISC, BIT(3), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_MISC_LDO_OCP, PM8008_MISC, BIT(4), IRQ_TYPE_EDGE_RISING),
_IRQ(PM8008_IRQ_TEMP_ALARM, PM8008_TEMP_ALARM,BIT(0), IRQ_TYPE_SENSE_MASK),
_IRQ(PM8008_IRQ_GPIO1, PM8008_GPIO1, BIT(0), IRQ_TYPE_SENSE_MASK),
_IRQ(PM8008_IRQ_GPIO2, PM8008_GPIO2, BIT(0), IRQ_TYPE_SENSE_MASK),
};
static const unsigned int pm8008_periph_base[] = {
@ -118,8 +139,8 @@ static int pm8008_set_type_config(unsigned int **buf, unsigned int type,
return 0;
}
static struct regmap_irq_chip pm8008_irq_chip = {
.name = "pm8008_irq",
static const struct regmap_irq_chip pm8008_irq_chip = {
.name = "pm8008",
.main_status = I2C_INTR_STATUS_BASE,
.num_main_regs = 1,
.irqs = pm8008_irqs,
@ -137,62 +158,106 @@ static struct regmap_irq_chip pm8008_irq_chip = {
.get_irq_reg = pm8008_get_irq_reg,
};
static struct regmap_config qcom_mfd_regmap_cfg = {
static const struct regmap_config qcom_mfd_regmap_cfg = {
.name = "primary",
.reg_bits = 16,
.val_bits = 8,
.max_register = 0xFFFF,
.max_register = 0xffff,
};
static int pm8008_probe_irq_peripherals(struct device *dev,
struct regmap *regmap,
int client_irq)
static const struct regmap_config pm8008_regmap_cfg_2 = {
.name = "secondary",
.reg_bits = 16,
.val_bits = 8,
.max_register = 0xffff,
};
static const struct resource pm8008_temp_res[] = {
DEFINE_RES_MEM(PM8008_TEMP_ALARM_ADDR, 0x100),
DEFINE_RES_IRQ(PM8008_IRQ_TEMP_ALARM),
};
static const struct mfd_cell pm8008_cells[] = {
MFD_CELL_NAME("pm8008-regulator"),
MFD_CELL_RES("qpnp-temp-alarm", pm8008_temp_res),
MFD_CELL_NAME("pm8008-gpio"),
};
static void devm_irq_domain_fwnode_release(void *data)
{
int rc, i;
struct regmap_irq_type *type;
struct regmap_irq_chip_data *irq_data;
struct fwnode_handle *fwnode = data;
for (i = 0; i < ARRAY_SIZE(pm8008_irqs); i++) {
type = &pm8008_irqs[i].type;
type->type_reg_offset = pm8008_irqs[i].reg_offset;
if (type->type_reg_offset == PM8008_MISC)
type->types_supported = IRQ_TYPE_EDGE_RISING;
else
type->types_supported = (IRQ_TYPE_EDGE_BOTH |
IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW);
}
rc = devm_regmap_add_irq_chip(dev, regmap, client_irq,
IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
if (rc) {
dev_err(dev, "Failed to add IRQ chip: %d\n", rc);
return rc;
}
return 0;
irq_domain_free_fwnode(fwnode);
}
static int pm8008_probe(struct i2c_client *client)
{
int rc;
struct device *dev;
struct regmap *regmap;
struct regmap_irq_chip_data *irq_data;
struct device *dev = &client->dev;
struct regmap *regmap, *regmap2;
struct fwnode_handle *fwnode;
struct i2c_client *dummy;
struct gpio_desc *reset;
char *name;
int ret;
dev = &client->dev;
dummy = devm_i2c_new_dummy_device(dev, client->adapter, client->addr + 1);
if (IS_ERR(dummy)) {
ret = PTR_ERR(dummy);
dev_err(dev, "failed to claim second address: %d\n", ret);
return ret;
}
regmap2 = devm_regmap_init_i2c(dummy, &qcom_mfd_regmap_cfg);
if (IS_ERR(regmap2))
return PTR_ERR(regmap2);
ret = regmap_attach_dev(dev, regmap2, &pm8008_regmap_cfg_2);
if (ret)
return ret;
/* Default regmap must be attached last. */
regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
i2c_set_clientdata(client, regmap);
reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset))
return PTR_ERR(reset);
if (of_property_read_bool(dev->of_node, "interrupt-controller")) {
rc = pm8008_probe_irq_peripherals(dev, regmap, client->irq);
if (rc)
dev_err(dev, "Failed to probe irq periphs: %d\n", rc);
/*
* The PMIC does not appear to require a post-reset delay, but wait
* for a millisecond for now anyway.
*/
usleep_range(1000, 2000);
name = devm_kasprintf(dev, GFP_KERNEL, "%pOF-internal", dev->of_node);
if (!name)
return -ENOMEM;
name = strreplace(name, '/', ':');
fwnode = irq_domain_alloc_named_fwnode(name);
if (!fwnode)
return -ENOMEM;
ret = devm_add_action_or_reset(dev, devm_irq_domain_fwnode_release, fwnode);
if (ret)
return ret;
ret = devm_regmap_add_irq_chip_fwnode(dev, fwnode, regmap, client->irq,
IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
if (ret) {
dev_err(dev, "failed to add IRQ chip: %d\n", ret);
return ret;
}
return devm_of_platform_populate(dev);
/* Needed by GPIO driver. */
dev_set_drvdata(dev, regmap_irq_get_domain(irq_data));
return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, pm8008_cells,
ARRAY_SIZE(pm8008_cells), NULL, 0,
regmap_irq_get_domain(irq_data));
}
static const struct of_device_id pm8008_match[] = {
@ -210,4 +275,5 @@ static struct i2c_driver pm8008_mfd_driver = {
};
module_i2c_driver(pm8008_mfd_driver);
MODULE_DESCRIPTION("QCOM PM8008 Power Management IC driver");
MODULE_LICENSE("GPL v2");

View File

@ -300,8 +300,8 @@ static void retu_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id retu_id[] = {
{ "retu", 0 },
{ "tahvo", 0 },
{ "retu" },
{ "tahvo" },
{ }
};
MODULE_DEVICE_TABLE(i2c, retu_id);

273
drivers/mfd/rohm-bd96801.c Normal file
View File

@ -0,0 +1,273 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2024 ROHM Semiconductors
*
* ROHM BD96801 PMIC driver
*
* This version of the "BD86801 scalable PMIC"'s driver supports only very
* basic set of the PMIC features. Most notably, there is no support for
* the ERRB interrupt and the configurations which should be done when the
* PMIC is in STBY mode.
*
* Supporting the ERRB interrupt would require dropping the regmap-IRQ
* usage or working around (or accepting a presense of) a naming conflict
* in debugFS IRQs.
*
* Being able to reliably do the configurations like changing the
* regulator safety limits (like limits for the over/under -voltages, over
* current, thermal protection) would require the configuring driver to be
* synchronized with entity causing the PMIC state transitions. Eg, one
* should be able to ensure the PMIC is in STBY state when the
* configurations are applied to the hardware. How and when the PMIC state
* transitions are to be done is likely to be very system specific, as will
* be the need to configure these safety limits. Hence it's not simple to
* come up with a generic solution.
*
* Users who require the ERRB handling and STBY state configurations can
* have a look at the original RFC:
* https://lore.kernel.org/all/cover.1712920132.git.mazziesaccount@gmail.com/
* which implements a workaround to debugFS naming conflict and some of
* the safety limit configurations - but leaves the state change handling
* and synchronization to be implemented.
*
* It would be great to hear (and receive a patch!) if you implement the
* STBY configuration support or a proper fix to the debugFS naming
* conflict in your downstream driver ;)
*/
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/mfd/rohm-bd96801.h>
#include <linux/mfd/rohm-generic.h>
static const struct resource regulator_intb_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD96801_TW_STAT, "bd96801-core-thermal"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPH_STAT, "bd96801-buck1-overcurr-h"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPL_STAT, "bd96801-buck1-overcurr-l"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OCPN_STAT, "bd96801-buck1-overcurr-n"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_OVD_STAT, "bd96801-buck1-overvolt"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_UVD_STAT, "bd96801-buck1-undervolt"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK1_TW_CH_STAT, "bd96801-buck1-thermal"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPH_STAT, "bd96801-buck2-overcurr-h"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPL_STAT, "bd96801-buck2-overcurr-l"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OCPN_STAT, "bd96801-buck2-overcurr-n"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_OVD_STAT, "bd96801-buck2-overvolt"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_UVD_STAT, "bd96801-buck2-undervolt"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK2_TW_CH_STAT, "bd96801-buck2-thermal"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPH_STAT, "bd96801-buck3-overcurr-h"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPL_STAT, "bd96801-buck3-overcurr-l"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OCPN_STAT, "bd96801-buck3-overcurr-n"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_OVD_STAT, "bd96801-buck3-overvolt"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_UVD_STAT, "bd96801-buck3-undervolt"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK3_TW_CH_STAT, "bd96801-buck3-thermal"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPH_STAT, "bd96801-buck4-overcurr-h"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPL_STAT, "bd96801-buck4-overcurr-l"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OCPN_STAT, "bd96801-buck4-overcurr-n"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_OVD_STAT, "bd96801-buck4-overvolt"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_UVD_STAT, "bd96801-buck4-undervolt"),
DEFINE_RES_IRQ_NAMED(BD96801_BUCK4_TW_CH_STAT, "bd96801-buck4-thermal"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OCPH_STAT, "bd96801-ldo5-overcurr"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_OVD_STAT, "bd96801-ldo5-overvolt"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO5_UVD_STAT, "bd96801-ldo5-undervolt"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OCPH_STAT, "bd96801-ldo6-overcurr"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_OVD_STAT, "bd96801-ldo6-overvolt"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO6_UVD_STAT, "bd96801-ldo6-undervolt"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OCPH_STAT, "bd96801-ldo7-overcurr"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_OVD_STAT, "bd96801-ldo7-overvolt"),
DEFINE_RES_IRQ_NAMED(BD96801_LDO7_UVD_STAT, "bd96801-ldo7-undervolt"),
};
static const struct resource wdg_intb_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD96801_WDT_ERR_STAT, "bd96801-wdg"),
};
static struct mfd_cell bd96801_cells[] = {
{
.name = "bd96801-wdt",
.resources = wdg_intb_irqs,
.num_resources = ARRAY_SIZE(wdg_intb_irqs),
}, {
.name = "bd96801-regulator",
.resources = regulator_intb_irqs,
.num_resources = ARRAY_SIZE(regulator_intb_irqs),
},
};
static const struct regmap_range bd96801_volatile_ranges[] = {
/* Status registers */
regmap_reg_range(BD96801_REG_WD_FEED, BD96801_REG_WD_FAILCOUNT),
regmap_reg_range(BD96801_REG_WD_ASK, BD96801_REG_WD_ASK),
regmap_reg_range(BD96801_REG_WD_STATUS, BD96801_REG_WD_STATUS),
regmap_reg_range(BD96801_REG_PMIC_STATE, BD96801_REG_INT_LDO7_INTB),
/* Registers which do not update value unless PMIC is in STBY */
regmap_reg_range(BD96801_REG_SSCG_CTRL, BD96801_REG_SHD_INTB),
regmap_reg_range(BD96801_REG_BUCK_OVP, BD96801_REG_BOOT_OVERTIME),
/*
* LDO control registers have single bit (LDO MODE) which does not
* change when we write it unless PMIC is in STBY. It's safer to not
* cache it.
*/
regmap_reg_range(BD96801_LDO5_VOL_LVL_REG, BD96801_LDO7_VOL_LVL_REG),
};
static const struct regmap_access_table volatile_regs = {
.yes_ranges = bd96801_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(bd96801_volatile_ranges),
};
static const struct regmap_irq bd96801_intb_irqs[] = {
/* STATUS SYSTEM INTB */
REGMAP_IRQ_REG(BD96801_TW_STAT, 0, BD96801_TW_STAT_MASK),
REGMAP_IRQ_REG(BD96801_WDT_ERR_STAT, 0, BD96801_WDT_ERR_STAT_MASK),
REGMAP_IRQ_REG(BD96801_I2C_ERR_STAT, 0, BD96801_I2C_ERR_STAT_MASK),
REGMAP_IRQ_REG(BD96801_CHIP_IF_ERR_STAT, 0, BD96801_CHIP_IF_ERR_STAT_MASK),
/* STATUS BUCK1 INTB */
REGMAP_IRQ_REG(BD96801_BUCK1_OCPH_STAT, 1, BD96801_BUCK_OCPH_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK1_OCPL_STAT, 1, BD96801_BUCK_OCPL_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK1_OCPN_STAT, 1, BD96801_BUCK_OCPN_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK1_OVD_STAT, 1, BD96801_BUCK_OVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK1_UVD_STAT, 1, BD96801_BUCK_UVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK1_TW_CH_STAT, 1, BD96801_BUCK_TW_CH_STAT_MASK),
/* BUCK 2 INTB */
REGMAP_IRQ_REG(BD96801_BUCK2_OCPH_STAT, 2, BD96801_BUCK_OCPH_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK2_OCPL_STAT, 2, BD96801_BUCK_OCPL_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK2_OCPN_STAT, 2, BD96801_BUCK_OCPN_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK2_OVD_STAT, 2, BD96801_BUCK_OVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK2_UVD_STAT, 2, BD96801_BUCK_UVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK2_TW_CH_STAT, 2, BD96801_BUCK_TW_CH_STAT_MASK),
/* BUCK 3 INTB */
REGMAP_IRQ_REG(BD96801_BUCK3_OCPH_STAT, 3, BD96801_BUCK_OCPH_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK3_OCPL_STAT, 3, BD96801_BUCK_OCPL_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK3_OCPN_STAT, 3, BD96801_BUCK_OCPN_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK3_OVD_STAT, 3, BD96801_BUCK_OVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK3_UVD_STAT, 3, BD96801_BUCK_UVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK3_TW_CH_STAT, 3, BD96801_BUCK_TW_CH_STAT_MASK),
/* BUCK 4 INTB */
REGMAP_IRQ_REG(BD96801_BUCK4_OCPH_STAT, 4, BD96801_BUCK_OCPH_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK4_OCPL_STAT, 4, BD96801_BUCK_OCPL_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK4_OCPN_STAT, 4, BD96801_BUCK_OCPN_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK4_OVD_STAT, 4, BD96801_BUCK_OVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK4_UVD_STAT, 4, BD96801_BUCK_UVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_BUCK4_TW_CH_STAT, 4, BD96801_BUCK_TW_CH_STAT_MASK),
/* LDO5 INTB */
REGMAP_IRQ_REG(BD96801_LDO5_OCPH_STAT, 5, BD96801_LDO_OCPH_STAT_MASK),
REGMAP_IRQ_REG(BD96801_LDO5_OVD_STAT, 5, BD96801_LDO_OVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_LDO5_UVD_STAT, 5, BD96801_LDO_UVD_STAT_MASK),
/* LDO6 INTB */
REGMAP_IRQ_REG(BD96801_LDO6_OCPH_STAT, 6, BD96801_LDO_OCPH_STAT_MASK),
REGMAP_IRQ_REG(BD96801_LDO6_OVD_STAT, 6, BD96801_LDO_OVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_LDO6_UVD_STAT, 6, BD96801_LDO_UVD_STAT_MASK),
/* LDO7 INTB */
REGMAP_IRQ_REG(BD96801_LDO7_OCPH_STAT, 7, BD96801_LDO_OCPH_STAT_MASK),
REGMAP_IRQ_REG(BD96801_LDO7_OVD_STAT, 7, BD96801_LDO_OVD_STAT_MASK),
REGMAP_IRQ_REG(BD96801_LDO7_UVD_STAT, 7, BD96801_LDO_UVD_STAT_MASK),
};
static struct regmap_irq_chip bd96801_irq_chip_intb = {
.name = "bd96801-irq-intb",
.main_status = BD96801_REG_INT_MAIN,
.num_main_regs = 1,
.irqs = &bd96801_intb_irqs[0],
.num_irqs = ARRAY_SIZE(bd96801_intb_irqs),
.status_base = BD96801_REG_INT_SYS_INTB,
.mask_base = BD96801_REG_MASK_SYS_INTB,
.ack_base = BD96801_REG_INT_SYS_INTB,
.init_ack_masked = true,
.num_regs = 8,
.irq_reg_stride = 1,
};
static const struct regmap_config bd96801_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &volatile_regs,
.cache_type = REGCACHE_RBTREE,
};
static int bd96801_i2c_probe(struct i2c_client *i2c)
{
struct regmap_irq_chip_data *intb_irq_data;
const struct fwnode_handle *fwnode;
struct irq_domain *intb_domain;
struct regmap *regmap;
int ret, intb_irq;
fwnode = dev_fwnode(&i2c->dev);
if (!fwnode)
return dev_err_probe(&i2c->dev, -EINVAL, "Failed to find fwnode\n");
intb_irq = fwnode_irq_get_byname(fwnode, "intb");
if (intb_irq < 0)
return dev_err_probe(&i2c->dev, intb_irq, "INTB IRQ not configured\n");
regmap = devm_regmap_init_i2c(i2c, &bd96801_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
"Regmap initialization failed\n");
ret = regmap_write(regmap, BD96801_LOCK_REG, BD96801_UNLOCK);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Failed to unlock PMIC\n");
ret = devm_regmap_add_irq_chip(&i2c->dev, regmap, intb_irq,
IRQF_ONESHOT, 0, &bd96801_irq_chip_intb,
&intb_irq_data);
if (ret)
return dev_err_probe(&i2c->dev, ret, "Failed to add INTB IRQ chip\n");
intb_domain = regmap_irq_get_domain(intb_irq_data);
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO,
bd96801_cells,
ARRAY_SIZE(bd96801_cells), NULL, 0,
intb_domain);
if (ret)
dev_err(&i2c->dev, "Failed to create subdevices\n");
return ret;
}
static const struct of_device_id bd96801_of_match[] = {
{ .compatible = "rohm,bd96801", },
{ }
};
MODULE_DEVICE_TABLE(of, bd96801_of_match);
static struct i2c_driver bd96801_i2c_driver = {
.driver = {
.name = "rohm-bd96801",
.of_match_table = bd96801_of_match,
},
.probe = bd96801_i2c_probe,
};
static int __init bd96801_i2c_init(void)
{
return i2c_add_driver(&bd96801_i2c_driver);
}
/* Initialise early so consumer devices can complete system boot */
subsys_initcall(bd96801_i2c_init);
static void __exit bd96801_i2c_exit(void)
{
i2c_del_driver(&bd96801_i2c_driver);
}
module_exit(bd96801_i2c_exit);
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
MODULE_DESCRIPTION("ROHM BD96801 Power Management IC driver");
MODULE_LICENSE("GPL");

View File

@ -78,11 +78,13 @@ int rsmu_core_init(struct rsmu_ddata *rsmu)
return ret;
}
EXPORT_SYMBOL_GPL(rsmu_core_init);
void rsmu_core_exit(struct rsmu_ddata *rsmu)
{
mutex_destroy(&rsmu->lock);
}
EXPORT_SYMBOL_GPL(rsmu_core_exit);
MODULE_DESCRIPTION("Renesas SMU core driver");
MODULE_LICENSE("GPL");

View File

@ -115,4 +115,5 @@ static struct i2c_driver rt4831_driver = {
module_i2c_driver(rt4831_driver);
MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
MODULE_DESCRIPTION("Richtek RT4831 core driver");
MODULE_LICENSE("GPL v2");

View File

@ -319,6 +319,7 @@ static struct platform_driver ssbi_driver = {
};
module_platform_driver(ssbi_driver);
MODULE_DESCRIPTION("Qualcomm Single-wire Serial Bus Interface (SSBI) driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
MODULE_ALIAS("platform:ssbi");

View File

@ -222,8 +222,8 @@ static int stw481x_probe(struct i2c_client *client)
* the structure of the I2C core.
*/
static const struct i2c_device_id stw481x_id[] = {
{ "stw481x", 0 },
{ },
{ "stw481x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, stw481x_id);

View File

@ -192,6 +192,54 @@ static struct regmap *device_node_get_regmap(struct device_node *np,
return syscon->regmap;
}
/**
* of_syscon_register_regmap() - Register regmap for specified device node
* @np: Device tree node
* @regmap: Pointer to regmap object
*
* Register an externally created regmap object with syscon for the specified
* device tree node. This regmap will then be returned to client drivers using
* the syscon_regmap_lookup_by_phandle() API.
*
* Return: 0 on success, negative error code on failure.
*/
int of_syscon_register_regmap(struct device_node *np, struct regmap *regmap)
{
struct syscon *entry, *syscon = NULL;
int ret;
if (!np || !regmap)
return -EINVAL;
syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
if (!syscon)
return -ENOMEM;
/* check if syscon entry already exists */
spin_lock(&syscon_list_slock);
list_for_each_entry(entry, &syscon_list, list)
if (entry->np == np) {
ret = -EEXIST;
goto err_unlock;
}
syscon->regmap = regmap;
syscon->np = np;
/* register the regmap in syscon list */
list_add_tail(&syscon->list, &syscon_list);
spin_unlock(&syscon_list_slock);
return 0;
err_unlock:
spin_unlock(&syscon_list_slock);
kfree(syscon);
return ret;
}
EXPORT_SYMBOL_GPL(of_syscon_register_regmap);
struct regmap *device_node_to_regmap(struct device_node *np)
{
return device_node_get_regmap(np, false);

View File

@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/mfd/core.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/timb_gpio.h>
@ -25,7 +26,6 @@
#include <linux/spi/max7301.h>
#include <linux/spi/mc33880.h>
#include <linux/platform_data/tsc2007.h>
#include <linux/platform_data/media/timb_radio.h>
#include <linux/platform_data/media/timb_video.h>
@ -49,16 +49,21 @@ struct timberdale_device {
/*--------------------------------------------------------------------------*/
static struct tsc2007_platform_data timberdale_tsc2007_platform_data = {
.model = 2003,
.x_plate_ohms = 100
static const struct property_entry timberdale_tsc2007_properties[] = {
PROPERTY_ENTRY_U32("ti,x-plate-ohms", 100),
{ }
};
static const struct software_node timberdale_tsc2007_node = {
.name = "tsc2007",
.properties = timberdale_tsc2007_properties,
};
static struct i2c_board_info timberdale_i2c_board_info[] = {
{
I2C_BOARD_INFO("tsc2007", 0x48),
.platform_data = &timberdale_tsc2007_platform_data,
.irq = IRQ_TIMBERDALE_TSC_INT
.irq = IRQ_TIMBERDALE_TSC_INT,
.swnode = &timberdale_tsc2007_node,
},
};
@ -853,4 +858,5 @@ module_pci_driver(timberdale_pci_driver);
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_VERSION(DRV_VERSION);
MODULE_DESCRIPTION("Timberdale FPGA MFD driver");
MODULE_LICENSE("GPL v2");

View File

@ -191,8 +191,8 @@ static void tps6105x_remove(struct i2c_client *client)
}
static const struct i2c_device_id tps6105x_id[] = {
{ "tps61050", 0 },
{ "tps61052", 0 },
{ "tps61050" },
{ "tps61052" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tps6105x_id);

View File

@ -103,7 +103,7 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id tps6507x_i2c_id[] = {
{ "tps6507x", 0 },
{ "tps6507x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);

View File

@ -127,7 +127,7 @@ static void tps65086_remove(struct i2c_client *client)
}
static const struct i2c_device_id tps65086_id_table[] = {
{ "tps65086", 0 },
{ "tps65086" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, tps65086_id_table);

View File

@ -225,8 +225,8 @@ err_irq_exit:
static const struct i2c_device_id tps65090_id_table[] = {
{ "tps65090", 0 },
{ },
{ "tps65090" },
{ }
};
static struct i2c_driver tps65090_driver = {

View File

@ -642,8 +642,8 @@ static SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_i2c_suspend,
tps6586x_i2c_resume);
static const struct i2c_device_id tps6586x_id_table[] = {
{ "tps6586x", 0 },
{ },
{ "tps6586x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tps6586x_id_table);

Some files were not shown because too many files have changed in this diff Show More