Commit 1a31c123 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull MFD updates from Lee Jones:
 "New Drivers:
   - Add support for initialising shared (between children) Regmaps
   - Add support for Kontron SL28CPLD
   - Add support for ENE KB3930 Embedded Controller
   - Add support for Intel FPGA PAC MAX 10 BMC

  New Device Support:
   - Add support for Power to Ricoh RN5T618
   - Add support for UART to Intel Lakefield
   - Add support for LP87524_Q1 to Texas Instruments LP87565

  New Functionality:
   - Device Tree; ene-kb3930, sl28cpld, syscon, lp87565, lp87524-q1
   - Use new helper dev_err_probe(); madera-core, stmfx, wcd934x
   - Use new GPIOD API; dm355evm_msp
   - Add wake-up capability; sprd-sc27xx-spi
   - Add ACPI support; kempld-core

  Fix-ups:
   - Trivial (spelling/whitespace); Kconfig, ab8500
   - Fix for unused variables; khadas-mcu, kempld-core
   - Remove unused header file(s); mt6360-core
   - Use correct IRQ flags in docs; act8945a, gateworks-gsc, rohm,bd70528-pmic
   - Add COMPILE_TEST support; asic3, tmio_core
   - Add dependency on I2C; SL28CPLD

  Bug Fixes:
   - Fix memory leak(s); sm501
   - Do not free regmap_config's 'name' until exit; syscon"

* tag 'mfd-next-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (34 commits)
  mfd: kempld-core: Fix unused variable 'kempld_acpi_table' when !ACPI
  mfd: sl28cpld: Depend on I2C
  mfd: asic3: Build if COMPILE_TEST=y
  dt-bindings: mfd: Correct interrupt flags in examples
  mfd: Add ACPI support to Kontron PLD driver
  mfd: intel-m10-bmc: Add Intel MAX 10 BMC chip support for Intel FPGA PAC
  mfd: lp87565: Add LP87524-Q1 variant
  dt-bindings: mfd: Add LP87524-Q1
  dt-bindings: mfd: lp87565: Convert to yaml
  mfd: mt6360: Remove unused include <linux/version.h>
  mfd: sm501: Fix leaks in probe()
  mfd: syscon: Don't free allocated name for regmap_config
  dt-bindings: mfd: syscon: Document Exynos3 and Exynos5433 compatibles
  dt-bindings: mfd: syscon: Merge Samsung Exynos Sysreg bindings
  dt-bindings: mfd: ab8500: Remove weird Unicode characters
  mfd: sprd: Add wakeup capability for PMIC IRQ
  mfd: intel-lpss: Add device IDs for UART ports for Lakefield
  mfd: dm355evm_msp: Convert LEDs to GPIO descriptor table
  mfd: wcd934x: Simplify with dev_err_probe()
  mfd: stmfx: Simplify with dev_err_probe()
  ...
parents f888bdf9 6d81dc3c
What: /sys/bus/spi/devices/.../bmc_version
Date: June 2020
KernelVersion: 5.10
Contact: Xu Yilun <yilun.xu@intel.com>
Description: Read only. Returns the hardware build version of Intel
MAX10 BMC chip.
Format: "0x%x".
What: /sys/bus/spi/devices/.../bmcfw_version
Date: June 2020
KernelVersion: 5.10
Contact: Xu Yilun <yilun.xu@intel.com>
Description: Read only. Returns the firmware version of Intel MAX10
BMC chip.
Format: "0x%x".
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/arm/samsung/sysreg.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung S5P/Exynos SoC series System Registers (SYSREG)
maintainers:
- Krzysztof Kozlowski <krzk@kernel.org>
# Custom select to avoid matching all nodes with 'syscon'
select:
properties:
compatible:
contains:
enum:
- samsung,exynos4-sysreg
- samsung,exynos5-sysreg
required:
- compatible
properties:
compatible:
allOf:
- items:
- enum:
- samsung,exynos4-sysreg
- samsung,exynos5-sysreg
- const: syscon
reg:
maxItems: 1
examples:
- |
syscon@10010000 {
compatible = "samsung,exynos4-sysreg", "syscon";
reg = <0x10010000 0x400>;
};
syscon@10050000 {
compatible = "samsung,exynos5-sysreg", "syscon";
reg = <0x10050000 0x5000>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/gpio/kontron,sl28cpld-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: GPIO driver for the sl28cpld board management controller
maintainers:
- Michael Walle <michael@walle.cc>
description: |
This module is part of the sl28cpld multi-function device. For more
details see ../mfd/kontron,sl28cpld.yaml.
There are three flavors of the GPIO controller, one full featured
input/output with interrupt support (kontron,sl28cpld-gpio), one
output-only (kontron,sl28-gpo) and one input-only (kontron,sl28-gpi).
Each controller supports 8 GPIO lines.
properties:
compatible:
enum:
- kontron,sl28cpld-gpio
- kontron,sl28cpld-gpi
- kontron,sl28cpld-gpo
reg:
maxItems: 1
interrupts:
maxItems: 1
"#interrupt-cells":
const: 2
interrupt-controller: true
"#gpio-cells":
const: 2
gpio-controller: true
gpio-line-names:
minItems: 1
maxItems: 8
required:
- compatible
- "#gpio-cells"
- gpio-controller
additionalProperties: false
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/kontron,sl28cpld-hwmon.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Hardware monitoring driver for the sl28cpld board management controller
maintainers:
- Michael Walle <michael@walle.cc>
description: |
This module is part of the sl28cpld multi-function device. For more
details see ../mfd/kontron,sl28cpld.yaml.
properties:
compatible:
enum:
- kontron,sl28cpld-fan
reg:
maxItems: 1
required:
- compatible
additionalProperties: false
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interrupt-controller/kontron,sl28cpld-intc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Interrupt controller driver for the sl28cpld board management controller
maintainers:
- Michael Walle <michael@walle.cc>
description: |
This module is part of the sl28cpld multi-function device. For more
details see ../mfd/kontron,sl28cpld.yaml.
The following interrupts are available. All types and levels are fixed
and handled by the board management controller.
==== ============= ==================================
IRQ line/device description
==== ============= ==================================
0 RTC_INT# Interrupt line from on-board RTC
1 SMB_ALERT# Event on SMB_ALERT# line (P1)
2 ESPI_ALERT0# Event on ESPI_ALERT0# line (S43)
3 ESPI_ALERT1# Event on ESPI_ALERT1# line (S44)
4 PWR_BTN# Event on PWR_BTN# line (P128)
5 SLEEP# Event on SLEEP# line (S149)
6 watchdog Interrupt of the internal watchdog
7 n/a not used
==== ============= ==================================
properties:
compatible:
enum:
- kontron,sl28cpld-intc
reg:
maxItems: 1
interrupts:
maxItems: 1
"#interrupt-cells":
const: 2
interrupt-controller: true
required:
- compatible
- interrupts
- "#interrupt-cells"
- interrupt-controller
additionalProperties: false
...@@ -31,8 +31,8 @@ ab8500-btemp : : vtvout : Battery Temperature ...@@ -31,8 +31,8 @@ ab8500-btemp : : vtvout : Battery Temperature
: BAT_CTRL_INDB : : Battery Removal Indicator : BAT_CTRL_INDB : : Battery Removal Indicator
: BTEMP_LOW : : Btemp < BtempLow, if battery temperature is lower than -10°C : BTEMP_LOW : : Btemp < BtempLow, if battery temperature is lower than -10°C
: BTEMP_LOW_MEDIUM : : BtempLow < Btemp < BtempMedium,if battery temperature is between -10 and 0°C : BTEMP_LOW_MEDIUM : : BtempLow < Btemp < BtempMedium,if battery temperature is between -10 and 0°C
: BTEMP_MEDIUM_HIGH : : BtempMedium < Btemp < BtempHigh,if battery temperature is between 0°C andMaxTemp : BTEMP_MEDIUM_HIGH : : BtempMedium < Btemp < BtempHigh,if battery temperature is between 0°C and MaxTemp
: BTEMP_HIGH : : Btemp > BtempHigh, if battery temperature is higher than MaxTemp : BTEMP_HIGH : : Btemp > BtempHigh, if battery temperature is higher than MaxTemp
ab8500-charger : : vddadc : Charger interface ab8500-charger : : vddadc : Charger interface
: MAIN_CH_UNPLUG_DET : : main charger unplug detection management (not in 8505) : MAIN_CH_UNPLUG_DET : : main charger unplug detection management (not in 8505)
: MAIN_CHARGE_PLUG_DET : : main charger plug detection management (not in 8505) : MAIN_CHARGE_PLUG_DET : : main charger plug detection management (not in 8505)
......
...@@ -71,7 +71,7 @@ Example: ...@@ -71,7 +71,7 @@ Example:
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_charger_chglev &pinctrl_charger_lbo &pinctrl_charger_irq>; pinctrl-0 = <&pinctrl_charger_chglev &pinctrl_charger_lbo &pinctrl_charger_irq>;
interrupt-parent = <&pioA>; interrupt-parent = <&pioA>;
interrupts = <45 GPIO_ACTIVE_LOW>; interrupts = <45 IRQ_TYPE_LEVEL_LOW>;
active-semi,chglev-gpios = <&pioA 12 GPIO_ACTIVE_HIGH>; active-semi,chglev-gpios = <&pioA 12 GPIO_ACTIVE_HIGH>;
active-semi,lbo-gpios = <&pioA 72 GPIO_ACTIVE_LOW>; active-semi,lbo-gpios = <&pioA 72 GPIO_ACTIVE_LOW>;
......
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/ene-kb3930.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ENE KB3930 Embedded Controller bindings
description: |
This binding describes the ENE KB3930 Embedded Controller attached to an
I2C bus.
maintainers:
- Lubomir Rintel <lkundrak@v3.sk>
properties:
compatible:
items:
- enum:
- dell,wyse-ariel-ec # Dell Wyse Ariel board (3020)
- const: ene,kb3930
reg:
maxItems: 1
off-gpios:
description: GPIO used with the shutdown protocol on Ariel
maxItems: 2
system-power-controller: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
embedded-controller@58 {
compatible = "dell,wyse-ariel-ec", "ene,kb3930";
reg = <0x58>;
system-power-controller;
off-gpios = <&gpio 126 GPIO_ACTIVE_HIGH>,
<&gpio 127 GPIO_ACTIVE_HIGH>;
};
};
...
...@@ -149,6 +149,7 @@ additionalProperties: false ...@@ -149,6 +149,7 @@ additionalProperties: false
examples: examples:
- | - |
#include <dt-bindings/gpio/gpio.h> #include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c { i2c {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
...@@ -157,7 +158,7 @@ examples: ...@@ -157,7 +158,7 @@ examples:
compatible = "gw,gsc"; compatible = "gw,gsc";
reg = <0x20>; reg = <0x20>;
interrupt-parent = <&gpio1>; interrupt-parent = <&gpio1>;
interrupts = <4 GPIO_ACTIVE_LOW>; interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
interrupt-controller; interrupt-controller;
#interrupt-cells = <1>; #interrupt-cells = <1>;
#address-cells = <1>; #address-cells = <1>;
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/kontron,sl28cpld.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Kontron's sl28cpld board management controller
maintainers:
- Michael Walle <michael@walle.cc>
description: |
The board management controller may contain different IP blocks like
watchdog, fan monitoring, PWM controller, interrupt controller and a
GPIO controller.
properties:
compatible:
const: kontron,sl28cpld
reg:
description:
I2C device address.
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
"#interrupt-cells":
const: 2
interrupts:
maxItems: 1
interrupt-controller: true
patternProperties:
"^gpio(@[0-9a-f]+)?$":
$ref: ../gpio/kontron,sl28cpld-gpio.yaml
"^hwmon(@[0-9a-f]+)?$":
$ref: ../hwmon/kontron,sl28cpld-hwmon.yaml
"^interrupt-controller(@[0-9a-f]+)?$":
$ref: ../interrupt-controller/kontron,sl28cpld-intc.yaml
"^pwm(@[0-9a-f]+)?$":
$ref: ../pwm/kontron,sl28cpld-pwm.yaml
"^watchdog(@[0-9a-f]+)?$":
$ref: ../watchdog/kontron,sl28cpld-wdt.yaml
required:
- "#address-cells"
- "#size-cells"
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
sl28cpld@4a {
compatible = "kontron,sl28cpld";
reg = <0x4a>;
#address-cells = <1>;
#size-cells = <0>;
watchdog@4 {
compatible = "kontron,sl28cpld-wdt";
reg = <0x4>;
kontron,assert-wdt-timeout-pin;
};
hwmon@b {
compatible = "kontron,sl28cpld-fan";
reg = <0xb>;
};
pwm@c {
compatible = "kontron,sl28cpld-pwm";
reg = <0xc>;
#pwm-cells = <2>;
};
pwm@e {
compatible = "kontron,sl28cpld-pwm";
reg = <0xe>;
#pwm-cells = <2>;
};
gpio@10 {
compatible = "kontron,sl28cpld-gpio";
reg = <0x10>;
interrupts-extended = <&gpio2 6
IRQ_TYPE_EDGE_FALLING>;
gpio-controller;
#gpio-cells = <2>;
gpio-line-names = "a", "b", "c";
interrupt-controller;
#interrupt-cells = <2>;
};
gpio@15 {
compatible = "kontron,sl28cpld-gpio";
reg = <0x15>;
interrupts-extended = <&gpio2 6
IRQ_TYPE_EDGE_FALLING>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
gpio@1a {
compatible = "kontron,sl28cpld-gpo";
reg = <0x1a>;
gpio-controller;
#gpio-cells = <2>;
};
gpio@1b {
compatible = "kontron,sl28cpld-gpi";
reg = <0x1b>;
gpio-controller;
#gpio-cells = <2>;
};
interrupt-controller@1c {
compatible = "kontron,sl28cpld-intc";
reg = <0x1c>;
interrupts-extended = <&gpio2 6
IRQ_TYPE_EDGE_FALLING>;
interrupt-controller;
#interrupt-cells = <2>;
};
};
};
TI LP87565 PMIC MFD driver
Required properties:
- compatible: "ti,lp87565", "ti,lp87565-q1"
- reg: I2C slave address.
- gpio-controller: Marks the device node as a GPIO Controller.
- #gpio-cells: Should be two. The first cell is the pin number and
the second cell is used to specify flags.
See ../gpio/gpio.txt for more information.
- xxx-in-supply: Phandle to parent supply node of each regulator
populated under regulators node. xxx should match
the supply_name populated in driver.
Example:
lp87565_pmic: pmic@60 {
compatible = "ti,lp87565-q1";
reg = <0x60>;
gpio-controller;
#gpio-cells = <2>;
buck10-in-supply = <&vsys_3v3>;
buck23-in-supply = <&vsys_3v3>;
regulators: regulators {
buck10_reg: buck10 {
/* VDD_MPU */
regulator-name = "buck10";
regulator-min-microvolt = <850000>;
regulator-max-microvolt = <1250000>;
regulator-always-on;
regulator-boot-on;
};
buck23_reg: buck23 {
/* VDD_GPU */
regulator-name = "buck23";
regulator-min-microvolt = <850000>;
regulator-max-microvolt = <1250000>;
regulator-boot-on;
regulator-always-on;
};
};
};
TI LP87561 PMIC:
This is a single output 4-phase regulator configuration
Required properties:
- compatible: "ti,lp87561-q1"
- reg: I2C slave address.
- gpio-controller: Marks the device node as a GPIO Controller.
- #gpio-cells: Should be two. The first cell is the pin number and
the second cell is used to specify flags.
See ../gpio/gpio.txt for more information.
- xxx-in-supply: Phandle to parent supply node of each regulator
populated under regulators node. xxx should match
the supply_name populated in driver.
Example:
lp87561_pmic: pmic@62 {
compatible = "ti,lp87561-q1";
reg = <0x62>;
gpio-controller;
#gpio-cells = <2>;
buck3210-in-supply = <&vsys_3v3>;
regulators: regulators {
buck3210_reg: buck3210 {
/* VDD_CORE */
regulator-name = "buck3210";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <800000>;
regulator-always-on;
regulator-boot-on;
};
};
};
...@@ -39,7 +39,7 @@ pmic: pmic@4b { ...@@ -39,7 +39,7 @@ pmic: pmic@4b {
compatible = "rohm,bd70528"; compatible = "rohm,bd70528";
reg = <0x4b>; reg = <0x4b>;
interrupt-parent = <&gpio1>; interrupt-parent = <&gpio1>;
interrupts = <29 GPIO_ACTIVE_LOW>; interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
clocks = <&osc 0>; clocks = <&osc 0>;
#clock-cells = <0>; #clock-cells = <0>;
clock-output-names = "bd70528-32k-out"; clock-output-names = "bd70528-32k-out";
......
...@@ -44,6 +44,10 @@ properties: ...@@ -44,6 +44,10 @@ properties:
- hisilicon,peri-subctrl - hisilicon,peri-subctrl
- microchip,sparx5-cpu-syscon - microchip,sparx5-cpu-syscon
- mstar,msc313-pmsleep - mstar,msc313-pmsleep
- samsung,exynos3-sysreg
- samsung,exynos4-sysreg
- samsung,exynos5-sysreg
- samsung,exynos5433-sysreg
- const: syscon - const: syscon
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/ti,lp87524-q1.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI LP87524-Q1 four 1-phase output buck converter
maintainers:
- Keerthy <j-keerthy@ti.com>
properties:
compatible:
const: ti,lp87524-q1
reg:
description: I2C slave address
const: 0x60
gpio-controller: true
'#gpio-cells':
description:
The first cell is the pin number.
The second cell is is used to specify flags.
See ../gpio/gpio.txt for more information.
const: 2
regulators:
type: object
patternProperties:
"^buck[0123]$":
type: object
$ref: /schemas/regulator/regulator.yaml#
required:
- buck0
- buck1
- buck2
- buck3
additionalProperties: false
patternProperties:
"^buck[0123]-in-supply$":
description: Voltage regulator supply for each BUCK converter
required:
- compatible
- reg
- gpio-controller
- '#gpio-cells'
- buck0-in-supply
- buck1-in-supply
- buck2-in-supply
- buck3-in-supply
- regulators
additionalProperties: false
examples:
- |
i2c@0 {
reg = <0x0 0x100>;
#address-cells = <1>;
#size-cells = <0>;
pmic@60 {
compatible = "ti,lp87524-q1";
reg = <0x60>;
gpio-controller;
#gpio-cells = <2>;
buck0-in-supply = <&vdd_5v0>;
buck1-in-supply = <&vdd_5v0>;
buck2-in-supply = <&vdd_5v0>;
buck3-in-supply = <&vdd_5v0>;
regulators {
buck0_reg: buck0 {
regulator-name = "buck0";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
buck1_reg: buck1 {
regulator-name = "buck1";
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
regulator-always-on;
};
buck2_reg: buck2 {
regulator-name = "buck2";
regulator-min-microvolt = <950000>;
regulator-max-microvolt = <950000>;
regulator-always-on;
};
buck3_reg: buck3 {
regulator-name = "buck3";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
};
};
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/ti,lp87561-q1.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI LP87561-Q1 single 4-phase output buck converter
maintainers:
- Keerthy <j-keerthy@ti.com>
properties:
compatible:
const: ti,lp87561-q1
reg:
description: I2C slave address
const: 0x60
gpio-controller: true
'#gpio-cells':
description:
The first cell is the pin number.
The second cell is is used to specify flags.
See ../gpio/gpio.txt for more information.
const: 2
buck3210-in-supply:
description:
Voltage regulator supply for all the four BUCK converters.
regulators:
type: object
properties:
buck3210:
type: object
$ref: /schemas/regulator/regulator.yaml#
required:
- buck3210
additionalProperties: false
required:
- compatible
- reg
- gpio-controller
- '#gpio-cells'
- buck3210-in-supply
additionalProperties: false
examples:
- |
i2c@0 {
reg = <0x0 0x100>;
#address-cells = <1>;
#size-cells = <0>;
pmic@60 {
compatible = "ti,lp87561-q1";
reg = <0x60>;
gpio-controller;
#gpio-cells = <2>;
buck3210-in-supply = <&vsys_3v3>;
regulators {
buck3210_reg: buck3210 {
/* VDD_CORE */
regulator-name = "buck3210";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <800000>;
regulator-always-on;
regulator-boot-on;
};
};
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/ti,lp87565-q1.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI LP87565-Q1 / LP87565 dual 2-phase output buck converter
maintainers:
- Keerthy <j-keerthy@ti.com>
properties:
compatible:
oneOf:
- const: ti,lp87565
- const: ti,lp87565-q1
reg:
description: I2C slave address
const: 0x60
gpio-controller: true
'#gpio-cells':
description:
The first cell is the pin number.
The second cell is is used to specify flags.
See ../gpio/gpio.txt for more information.
const: 2
buck10-in-supply:
description:
Voltage regulator supply for BUCK0 and BUCK1 converters.
buck23-in-supply:
description:
Voltage regulator supply for BUCK2 and BUCK3 converters.
regulators:
type: object
patternProperties:
"^buck(10|23)$":
type: object
$ref: /schemas/regulator/regulator.yaml#
required:
- buck10
- buck23
additionalProperties: false
required:
- compatible
- reg
- gpio-controller
- '#gpio-cells'
- buck10-in-supply
- buck23-in-supply
additionalProperties: false
examples:
- |
i2c@0 {
reg = <0x0 0x100>;
#address-cells = <1>;
#size-cells = <0>;
pmic@60 {
compatible = "ti,lp87565-q1";
reg = <0x60>;
gpio-controller;
#gpio-cells = <2>;
buck10-in-supply = <&vsys_3v3>;
buck23-in-supply = <&vsys_3v3>;
regulators {
buck10_reg: buck10 {
/* VDD_MPU */
regulator-name = "buck10";
regulator-min-microvolt = <850000>;
regulator-max-microvolt = <1250000>;
regulator-always-on;
regulator-boot-on;
};
buck23_reg: buck23 {
/* VDD_GPU */
regulator-name = "buck23";
regulator-min-microvolt = <850000>;
regulator-max-microvolt = <1250000>;
regulator-boot-on;
regulator-always-on;
};
};
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pwm/kontron,sl28cpld-pwm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: PWM driver for the sl28cpld board management controller
maintainers:
- Michael Walle <michael@walle.cc>
description: |
This module is part of the sl28cpld multi-function device. For more
details see ../mfd/kontron,sl28cpld.yaml.
The controller supports one PWM channel and supports only four distinct
frequencies (250Hz, 500Hz, 1kHz, 2kHz).
allOf:
- $ref: pwm.yaml#
properties:
compatible:
const: kontron,sl28cpld-pwm
reg:
maxItems: 1
"#pwm-cells":
const: 2
required:
- compatible
additionalProperties: false
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/kontron,sl28cpld-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Watchdog driver for the sl28cpld board management controller
maintainers:
- Michael Walle <michael@walle.cc>
description: |
This module is part of the sl28cpld multi-function device. For more
details see ../mfd/kontron,sl28cpld.yaml.
allOf:
- $ref: watchdog.yaml#
properties:
compatible:
const: kontron,sl28cpld-wdt
reg:
maxItems: 1
kontron,assert-wdt-timeout-pin:
description: The SMARC standard defines a WDT_TIME_OUT# pin. If this
property is set, this output will be pulsed when the watchdog bites
and the system resets.
type: boolean
required:
- compatible
additionalProperties: false
...@@ -156,6 +156,7 @@ Hardware Monitoring Kernel Drivers ...@@ -156,6 +156,7 @@ Hardware Monitoring Kernel Drivers
sht3x sht3x
shtc1 shtc1
sis5595 sis5595
sl28cpld
smm665 smm665
smsc47b397 smsc47b397
smsc47m192 smsc47m192
......
.. SPDX-License-Identifier: GPL-2.0-only
Kernel driver sl28cpld
======================
Supported chips:
* Kontron sl28cpld
Prefix: 'sl28cpld'
Datasheet: not available
Authors: Michael Walle <michael@walle.cc>
Description
-----------
The sl28cpld is a board management controller which also exposes a hardware
monitoring controller. At the moment this controller supports a single fan
supervisor. In the future there might be other flavours and additional
hardware monitoring might be supported.
The fan supervisor has a 7 bit counter register and a counter period of 1
second. If the 7 bit counter overflows, the supervisor will automatically
switch to x8 mode to support a wider input range at the loss of
granularity.
Sysfs entries
-------------
The following attributes are supported.
======================= ========================================================
fan1_input Fan RPM. Assuming 2 pulses per revolution.
======================= ========================================================
...@@ -1248,6 +1248,18 @@ config GPIO_RC5T583 ...@@ -1248,6 +1248,18 @@ config GPIO_RC5T583
This driver provides the support for driving/reading the gpio pins This driver provides the support for driving/reading the gpio pins
of RC5T583 device through standard gpio library. of RC5T583 device through standard gpio library.
config GPIO_SL28CPLD
tristate "Kontron sl28cpld GPIO support"
depends on MFD_SL28CPLD || COMPILE_TEST
select GPIO_REGMAP
select GPIOLIB_IRQCHIP
select REGMAP_IRQ
help
This enables support for the GPIOs found on the Kontron sl28 CPLD.
This driver can also be built as a module. If so, the module will be
called gpio-sl28cpld.
config GPIO_STMPE config GPIO_STMPE
bool "STMPE GPIOs" bool "STMPE GPIOs"
depends on MFD_STMPE depends on MFD_STMPE
......
...@@ -131,6 +131,7 @@ obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o ...@@ -131,6 +131,7 @@ obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* sl28cpld GPIO driver
*
* Copyright 2020 Michael Walle <michael@walle.cc>
*/
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/regmap.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
/* GPIO flavor */
#define GPIO_REG_DIR 0x00
#define GPIO_REG_OUT 0x01
#define GPIO_REG_IN 0x02
#define GPIO_REG_IE 0x03
#define GPIO_REG_IP 0x04
/* input-only flavor */
#define GPI_REG_IN 0x00
/* output-only flavor */
#define GPO_REG_OUT 0x00
enum sl28cpld_gpio_type {
SL28CPLD_GPIO = 1,
SL28CPLD_GPI,
SL28CPLD_GPO,
};
static const struct regmap_irq sl28cpld_gpio_irqs[] = {
REGMAP_IRQ_REG_LINE(0, 8),
REGMAP_IRQ_REG_LINE(1, 8),
REGMAP_IRQ_REG_LINE(2, 8),
REGMAP_IRQ_REG_LINE(3, 8),
REGMAP_IRQ_REG_LINE(4, 8),
REGMAP_IRQ_REG_LINE(5, 8),
REGMAP_IRQ_REG_LINE(6, 8),
REGMAP_IRQ_REG_LINE(7, 8),
};
static int sl28cpld_gpio_irq_init(struct platform_device *pdev,
unsigned int base,
struct gpio_regmap_config *config)
{
struct regmap_irq_chip_data *irq_data;
struct regmap_irq_chip *irq_chip;
struct device *dev = &pdev->dev;
int irq, ret;
if (!device_property_read_bool(dev, "interrupt-controller"))
return 0;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL);
if (!irq_chip)
return -ENOMEM;
irq_chip->name = "sl28cpld-gpio-irq",
irq_chip->irqs = sl28cpld_gpio_irqs;
irq_chip->num_irqs = ARRAY_SIZE(sl28cpld_gpio_irqs);
irq_chip->num_regs = 1;
irq_chip->status_base = base + GPIO_REG_IP;
irq_chip->mask_base = base + GPIO_REG_IE;
irq_chip->mask_invert = true,
irq_chip->ack_base = base + GPIO_REG_IP;
ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
config->regmap, irq,
IRQF_SHARED | IRQF_ONESHOT,
0, irq_chip, &irq_data);
if (ret)
return ret;
config->irq_domain = regmap_irq_get_domain(irq_data);
return 0;
}
static int sl28cpld_gpio_probe(struct platform_device *pdev)
{
struct gpio_regmap_config config = {0};
enum sl28cpld_gpio_type type;
struct regmap *regmap;
u32 base;
int ret;
if (!pdev->dev.parent)
return -ENODEV;
type = (uintptr_t)device_get_match_data(&pdev->dev);
if (!type)
return -ENODEV;
ret = device_property_read_u32(&pdev->dev, "reg", &base);
if (ret)
return -EINVAL;
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!regmap)
return -ENODEV;
config.regmap = regmap;
config.parent = &pdev->dev;
config.ngpio = 8;
switch (type) {
case SL28CPLD_GPIO:
config.reg_dat_base = base + GPIO_REG_IN;
config.reg_set_base = base + GPIO_REG_OUT;
/* reg_dir_out_base might be zero */
config.reg_dir_out_base = GPIO_REGMAP_ADDR(base + GPIO_REG_DIR);
/* This type supports interrupts */
ret = sl28cpld_gpio_irq_init(pdev, base, &config);
if (ret)
return ret;
break;
case SL28CPLD_GPO:
config.reg_set_base = base + GPO_REG_OUT;
break;
case SL28CPLD_GPI:
config.reg_dat_base = base + GPI_REG_IN;
break;
default:
dev_err(&pdev->dev, "unknown type %d\n", type);
return -ENODEV;
}
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
}
static const struct of_device_id sl28cpld_gpio_of_match[] = {
{ .compatible = "kontron,sl28cpld-gpio", .data = (void *)SL28CPLD_GPIO },
{ .compatible = "kontron,sl28cpld-gpi", .data = (void *)SL28CPLD_GPI },
{ .compatible = "kontron,sl28cpld-gpo", .data = (void *)SL28CPLD_GPO },
{}
};
MODULE_DEVICE_TABLE(of, sl28cpld_gpio_of_match);
static struct platform_driver sl28cpld_gpio_driver = {
.probe = sl28cpld_gpio_probe,
.driver = {
.name = "sl28cpld-gpio",
.of_match_table = sl28cpld_gpio_of_match,
},
};
module_platform_driver(sl28cpld_gpio_driver);
MODULE_DESCRIPTION("sl28cpld GPIO Driver");
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
MODULE_LICENSE("GPL");
...@@ -1489,6 +1489,16 @@ config SENSORS_RASPBERRYPI_HWMON ...@@ -1489,6 +1489,16 @@ config SENSORS_RASPBERRYPI_HWMON
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called raspberrypi-hwmon. will be called raspberrypi-hwmon.
config SENSORS_SL28CPLD
tristate "Kontron sl28cpld hardware monitoring driver"
depends on MFD_SL28CPLD || COMPILE_TEST
help
If you say yes here you get support for the fan supervisor of the
sl28cpld board management controller.
This driver can also be built as a module. If so, the module
will be called sl28cpld-hwmon.
config SENSORS_SHT15 config SENSORS_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat." tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
depends on GPIOLIB || COMPILE_TEST depends on GPIOLIB || COMPILE_TEST
......
...@@ -161,6 +161,7 @@ obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o ...@@ -161,6 +161,7 @@ obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o
obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o
obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o
obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o
obj-$(CONFIG_SENSORS_SHT21) += sht21.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o
obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* sl28cpld hardware monitoring driver
*
* Copyright 2020 Kontron Europe GmbH
*/
#include <linux/bitfield.h>
#include <linux/hwmon.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#define FAN_INPUT 0x00
#define FAN_SCALE_X8 BIT(7)
#define FAN_VALUE_MASK GENMASK(6, 0)
struct sl28cpld_hwmon {
struct regmap *regmap;
u32 offset;
};
static umode_t sl28cpld_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
return 0444;
}
static int sl28cpld_hwmon_read(struct device *dev,
enum hwmon_sensor_types type, u32 attr,
int channel, long *input)
{
struct sl28cpld_hwmon *hwmon = dev_get_drvdata(dev);
unsigned int value;
int ret;
switch (attr) {
case hwmon_fan_input:
ret = regmap_read(hwmon->regmap, hwmon->offset + FAN_INPUT,
&value);
if (ret)
return ret;
/*
* The register has a 7 bit value and 1 bit which indicates the
* scale. If the MSB is set, then the lower 7 bit has to be
* multiplied by 8, to get the correct reading.
*/
if (value & FAN_SCALE_X8)
value = FIELD_GET(FAN_VALUE_MASK, value) << 3;
/*
* The counter period is 1000ms and the sysfs specification
* says we should asssume 2 pulses per revolution.
*/
value *= 60 / 2;
break;
default:
return -EOPNOTSUPP;
}
*input = value;
return 0;
}
static const u32 sl28cpld_hwmon_fan_config[] = {
HWMON_F_INPUT,
0
};
static const struct hwmon_channel_info sl28cpld_hwmon_fan = {
.type = hwmon_fan,
.config = sl28cpld_hwmon_fan_config,
};
static const struct hwmon_channel_info *sl28cpld_hwmon_info[] = {
&sl28cpld_hwmon_fan,
NULL
};
static const struct hwmon_ops sl28cpld_hwmon_ops = {
.is_visible = sl28cpld_hwmon_is_visible,
.read = sl28cpld_hwmon_read,
};
static const struct hwmon_chip_info sl28cpld_hwmon_chip_info = {
.ops = &sl28cpld_hwmon_ops,
.info = sl28cpld_hwmon_info,
};
static int sl28cpld_hwmon_probe(struct platform_device *pdev)
{
struct sl28cpld_hwmon *hwmon;
struct device *hwmon_dev;
int ret;
if (!pdev->dev.parent)
return -ENODEV;
hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
if (!hwmon)
return -ENOMEM;
hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!hwmon->regmap)
return -ENODEV;
ret = device_property_read_u32(&pdev->dev, "reg", &hwmon->offset);
if (ret)
return -EINVAL;
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
"sl28cpld_hwmon", hwmon,
&sl28cpld_hwmon_chip_info, NULL);
if (IS_ERR(hwmon_dev))
dev_err(&pdev->dev, "failed to register as hwmon device");
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct of_device_id sl28cpld_hwmon_of_match[] = {
{ .compatible = "kontron,sl28cpld-fan" },
{}
};
MODULE_DEVICE_TABLE(of, sl28cpld_hwmon_of_match);
static struct platform_driver sl28cpld_hwmon_driver = {
.probe = sl28cpld_hwmon_probe,
.driver = {
.name = "sl28cpld-fan",
.of_match_table = sl28cpld_hwmon_of_match,
},
};
module_platform_driver(sl28cpld_hwmon_driver);
MODULE_DESCRIPTION("sl28cpld Hardware Monitoring Driver");
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
MODULE_LICENSE("GPL");
...@@ -246,6 +246,14 @@ config RENESAS_RZA1_IRQC ...@@ -246,6 +246,14 @@ config RENESAS_RZA1_IRQC
Enable support for the Renesas RZ/A1 Interrupt Controller, to use up Enable support for the Renesas RZ/A1 Interrupt Controller, to use up
to 8 external interrupts with configurable sense select. to 8 external interrupts with configurable sense select.
config SL28CPLD_INTC
bool "Kontron sl28cpld IRQ controller"
depends on MFD_SL28CPLD=y || COMPILE_TEST
select REGMAP_IRQ
help
Interrupt controller driver for the board management controller
found on the Kontron sl28 CPLD.
config ST_IRQCHIP config ST_IRQCHIP
bool bool
select REGMAP select REGMAP
......
...@@ -114,3 +114,4 @@ obj-$(CONFIG_LOONGSON_HTVEC) += irq-loongson-htvec.o ...@@ -114,3 +114,4 @@ obj-$(CONFIG_LOONGSON_HTVEC) += irq-loongson-htvec.o
obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-loongson-pch-pic.o obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-loongson-pch-pic.o
obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o
obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o
obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* sl28cpld interrupt controller driver
*
* Copyright 2020 Kontron Europe GmbH
*/
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#define INTC_IE 0x00
#define INTC_IP 0x01
static const struct regmap_irq sl28cpld_irqs[] = {
REGMAP_IRQ_REG_LINE(0, 8),
REGMAP_IRQ_REG_LINE(1, 8),
REGMAP_IRQ_REG_LINE(2, 8),
REGMAP_IRQ_REG_LINE(3, 8),
REGMAP_IRQ_REG_LINE(4, 8),
REGMAP_IRQ_REG_LINE(5, 8),
REGMAP_IRQ_REG_LINE(6, 8),
REGMAP_IRQ_REG_LINE(7, 8),
};
struct sl28cpld_intc {
struct regmap *regmap;
struct regmap_irq_chip chip;
struct regmap_irq_chip_data *irq_data;
};
static int sl28cpld_intc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sl28cpld_intc *irqchip;
int irq;
u32 base;
int ret;
if (!dev->parent)
return -ENODEV;
irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL);
if (!irqchip)
return -ENOMEM;
irqchip->regmap = dev_get_regmap(dev->parent, NULL);
if (!irqchip->regmap)
return -ENODEV;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = device_property_read_u32(&pdev->dev, "reg", &base);
if (ret)
return -EINVAL;
irqchip->chip.name = "sl28cpld-intc";
irqchip->chip.irqs = sl28cpld_irqs;
irqchip->chip.num_irqs = ARRAY_SIZE(sl28cpld_irqs);
irqchip->chip.num_regs = 1;
irqchip->chip.status_base = base + INTC_IP;
irqchip->chip.mask_base = base + INTC_IE;
irqchip->chip.mask_invert = true,
irqchip->chip.ack_base = base + INTC_IP;
return devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
irqchip->regmap, irq,
IRQF_SHARED | IRQF_ONESHOT, 0,
&irqchip->chip,
&irqchip->irq_data);
}
static const struct of_device_id sl28cpld_intc_of_match[] = {
{ .compatible = "kontron,sl28cpld-intc" },
{}
};
MODULE_DEVICE_TABLE(of, sl28cpld_intc_of_match);
static struct platform_driver sl28cpld_intc_driver = {
.probe = sl28cpld_intc_probe,
.driver = {
.name = "sl28cpld-intc",
.of_match_table = sl28cpld_intc_of_match,
}
};
module_platform_driver(sl28cpld_intc_driver);
MODULE_DESCRIPTION("sl28cpld Interrupt Controller Driver");
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
MODULE_LICENSE("GPL");
...@@ -290,7 +290,8 @@ config MFD_CS47L92 ...@@ -290,7 +290,8 @@ config MFD_CS47L92
config MFD_ASIC3 config MFD_ASIC3
bool "Compaq ASIC3" bool "Compaq ASIC3"
depends on GPIOLIB && ARM depends on GPIOLIB
depends on ARM || COMPILE_TEST
select MFD_CORE select MFD_CORE
help help
This driver supports the ASIC3 multifunction chip found on many This driver supports the ASIC3 multifunction chip found on many
...@@ -398,6 +399,17 @@ config MFD_DLN2 ...@@ -398,6 +399,17 @@ config MFD_DLN2
etc. must be enabled in order to use the functionality of etc. must be enabled in order to use the functionality of
the device. the device.
config MFD_ENE_KB3930
tristate "ENE KB3930 Embedded Controller support"
depends on I2C
depends on MACH_MMP3_DT || COMPILE_TEST
select MFD_CORE
help
This adds support for the power-off functionality and access to
the registers that control LEDS and USB port power on ENE KB3930
Embedded Controller. To use the LED functionality LEDS_ARIEL must
be enabled.
config MFD_EXYNOS_LPASS config MFD_EXYNOS_LPASS
tristate "Samsung Exynos SoC Low Power Audio Subsystem" tristate "Samsung Exynos SoC Low Power Audio Subsystem"
depends on ARCH_EXYNOS || COMPILE_TEST depends on ARCH_EXYNOS || COMPILE_TEST
...@@ -493,7 +505,7 @@ config MFD_HI6421_PMIC ...@@ -493,7 +505,7 @@ config MFD_HI6421_PMIC
Add support for HiSilicon Hi6421 PMIC. Hi6421 includes multi- Add support for HiSilicon Hi6421 PMIC. Hi6421 includes multi-
functions, such as regulators, RTC, codec, Coulomb counter, etc. functions, such as regulators, RTC, codec, Coulomb counter, etc.
This driver includes core APIs _only_. You have to select This driver includes core APIs _only_. You have to select
individul components like voltage regulators under corresponding individual components like voltage regulators under corresponding
menus in order to enable them. menus in order to enable them.
We communicate with the Hi6421 via memory-mapped I/O. We communicate with the Hi6421 via memory-mapped I/O.
...@@ -1162,6 +1174,29 @@ config MFD_SI476X_CORE ...@@ -1162,6 +1174,29 @@ config MFD_SI476X_CORE
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called si476x-core. module will be called si476x-core.
config MFD_SIMPLE_MFD_I2C
tristate
depends on I2C
select REGMAP_I2C
help
This driver creates a single register map with the intention for it
to be shared by all sub-devices.
Once the register map has been successfully initialised, any
sub-devices represented by child nodes in Device Tree will be
subsequently registered.
config MFD_SL28CPLD
tristate "Kontron sl28cpld Board Management Controller"
depends on I2C
select MFD_SIMPLE_MFD_I2C
help
Say yes here to enable support for the Kontron sl28cpld board
management controller.
It can be found on the following boards:
* SMARC-sAL28
config MFD_SM501 config MFD_SM501
tristate "Silicon Motion SM501" tristate "Silicon Motion SM501"
depends on HAS_DMA depends on HAS_DMA
...@@ -2118,5 +2153,18 @@ config SGI_MFD_IOC3 ...@@ -2118,5 +2153,18 @@ config SGI_MFD_IOC3
If you have an SGI Origin, Octane, or a PCI IOC3 card, If you have an SGI Origin, Octane, or a PCI IOC3 card,
then say Y. Otherwise say N. then say Y. Otherwise say N.
config MFD_INTEL_M10_BMC
tristate "Intel MAX 10 Board Management Controller"
depends on SPI_MASTER
select REGMAP_SPI_AVMM
select MFD_CORE
help
Support for the Intel MAX 10 board management controller using the
SPI interface.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the functionality
of the device.
endmenu endmenu
endif endif
...@@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o ...@@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o
obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o
...@@ -264,3 +265,5 @@ obj-$(CONFIG_MFD_STMFX) += stmfx.o ...@@ -264,3 +265,5 @@ obj-$(CONFIG_MFD_STMFX) += stmfx.o
obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.o
obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/mfd/dm355evm_msp.h> #include <linux/mfd/dm355evm_msp.h>
...@@ -116,6 +117,54 @@ static const u8 msp_gpios[] = { ...@@ -116,6 +117,54 @@ static const u8 msp_gpios[] = {
MSP_GPIO(4, SDMMC), MSP_GPIO(3, SDMMC), /* mmc1 WP, nCD */ MSP_GPIO(4, SDMMC), MSP_GPIO(3, SDMMC), /* mmc1 WP, nCD */
}; };
static struct gpio_led evm_leds[] = {
{ .name = "dm355evm::ds14",
.default_trigger = "heartbeat", },
{ .name = "dm355evm::ds15",
.default_trigger = "mmc0", },
{ .name = "dm355evm::ds16",
/* could also be a CE-ATA drive */
.default_trigger = "mmc1", },
{ .name = "dm355evm::ds17",
.default_trigger = "nand-disk", },
{ .name = "dm355evm::ds18", },
{ .name = "dm355evm::ds19", },
{ .name = "dm355evm::ds20", },
{ .name = "dm355evm::ds21", },
};
static struct gpio_led_platform_data evm_led_data = {
.num_leds = ARRAY_SIZE(evm_leds),
.leds = evm_leds,
};
static struct gpiod_lookup_table evm_leds_gpio_table = {
.dev_id = "leds-gpio",
.table = {
/*
* These GPIOs are on the dm355evm_msp
* GPIO chip at index 0..7
*/
GPIO_LOOKUP_IDX("dm355evm_msp", 0, NULL,
0, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("dm355evm_msp", 1, NULL,
1, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("dm355evm_msp", 2, NULL,
2, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("dm355evm_msp", 3, NULL,
3, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("dm355evm_msp", 4, NULL,
4, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("dm355evm_msp", 5, NULL,
5, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("dm355evm_msp", 6, NULL,
6, GPIO_ACTIVE_LOW),
GPIO_LOOKUP_IDX("dm355evm_msp", 7, NULL,
7, GPIO_ACTIVE_LOW),
{ },
},
};
#define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3) #define MSP_GPIO_REG(offset) (msp_gpios[(offset)] >> 3)
#define MSP_GPIO_MASK(offset) BIT(msp_gpios[(offset)] & 0x07) #define MSP_GPIO_MASK(offset) BIT(msp_gpios[(offset)] & 0x07)
...@@ -260,32 +309,7 @@ static int add_children(struct i2c_client *client) ...@@ -260,32 +309,7 @@ static int add_children(struct i2c_client *client)
/* LED output */ /* LED output */
if (msp_has_leds()) { if (msp_has_leds()) {
#define GPIO_LED(l) .name = l, .active_low = true gpiod_add_lookup_table(&evm_leds_gpio_table);
static struct gpio_led evm_leds[] = {
{ GPIO_LED("dm355evm::ds14"),
.default_trigger = "heartbeat", },
{ GPIO_LED("dm355evm::ds15"),
.default_trigger = "mmc0", },
{ GPIO_LED("dm355evm::ds16"),
/* could also be a CE-ATA drive */
.default_trigger = "mmc1", },
{ GPIO_LED("dm355evm::ds17"),
.default_trigger = "nand-disk", },
{ GPIO_LED("dm355evm::ds18"), },
{ GPIO_LED("dm355evm::ds19"), },
{ GPIO_LED("dm355evm::ds20"), },
{ GPIO_LED("dm355evm::ds21"), },
};
#undef GPIO_LED
struct gpio_led_platform_data evm_led_data = {
.num_leds = ARRAY_SIZE(evm_leds),
.leds = evm_leds,
};
for (i = 0; i < ARRAY_SIZE(evm_leds); i++)
evm_leds[i].gpio = i + dm355evm_msp_gpio.base;
/* NOTE: these are the only fully programmable LEDs /* NOTE: these are the only fully programmable LEDs
* on the board, since GPIO-61/ds22 (and many signals * on the board, since GPIO-61/ds22 (and many signals
* going to DC7) must be used for AEMIF address lines * going to DC7) must be used for AEMIF address lines
......
// SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later
/*
* ENE KB3930 Embedded Controller Driver
*
* Copyright (C) 2020 Lubomir Rintel
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
/* I2C registers that are multiplexing access to the EC RAM. */
enum {
EC_DATA_IN = 0x00,
EC_RAM_OUT = 0x80,
EC_RAM_IN = 0x81,
};
/* EC RAM registers. */
enum {
EC_MODEL = 0x30,
EC_VERSION_MAJ = 0x31,
EC_VERSION_MIN = 0x32,
};
struct kb3930 {
struct i2c_client *client;
struct regmap *ram_regmap;
struct gpio_descs *off_gpios;
};
struct kb3930 *kb3930_power_off;
#define EC_GPIO_WAVE 0
#define EC_GPIO_OFF_MODE 1
#define EC_OFF_MODE_REBOOT 0
#define EC_OFF_MODE_POWER 1
static void kb3930_off(struct kb3930 *ddata, int off_mode)
{
gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_OFF_MODE],
off_mode);
/*
* This creates a 10 Hz wave on EC_GPIO_WAVE that signals a
* shutdown request to the EC. Once the EC detects it, it will
* proceed to turn the power off or reset the board depending on
* the value of EC_GPIO_OFF_MODE.
*/
while (1) {
mdelay(50);
gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_WAVE], 0);
mdelay(50);
gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_WAVE], 1);
}
}
static int kb3930_restart(struct notifier_block *this,
unsigned long mode, void *cmd)
{
kb3930_off(kb3930_power_off, EC_OFF_MODE_REBOOT);
return NOTIFY_DONE;
}
static void kb3930_pm_power_off(void)
{
kb3930_off(kb3930_power_off, EC_OFF_MODE_POWER);
}
static struct notifier_block kb3930_restart_nb = {
.notifier_call = kb3930_restart,
};
static const struct mfd_cell ariel_ec_cells[] = {
{ .name = "dell-wyse-ariel-led", },
{ .name = "dell-wyse-ariel-power", },
};
static int kb3930_ec_ram_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct kb3930 *ddata = context;
return i2c_smbus_write_word_data(ddata->client, EC_RAM_OUT,
(val << 8) | reg);
}
static int kb3930_ec_ram_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
struct kb3930 *ddata = context;
int ret;
ret = i2c_smbus_write_word_data(ddata->client, EC_RAM_IN, reg);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(ddata->client, EC_DATA_IN);
if (ret < 0)
return ret;
*val = ret >> 8;
return 0;
}
static const struct regmap_config kb3930_ram_regmap_config = {
.name = "ec_ram",
.reg_bits = 8,
.val_bits = 8,
.reg_stride = 1,
.max_register = 0xff,
.reg_write = kb3930_ec_ram_reg_write,
.reg_read = kb3930_ec_ram_reg_read,
.fast_io = false,
};
static int kb3930_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device_node *np = dev->of_node;
struct kb3930 *ddata;
unsigned int model;
int ret;
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
kb3930_power_off = ddata;
ddata->client = client;
i2c_set_clientdata(client, ddata);
ddata->ram_regmap = devm_regmap_init(dev, NULL, ddata,
&kb3930_ram_regmap_config);
if (IS_ERR(ddata->ram_regmap))
return PTR_ERR(ddata->ram_regmap);
ret = regmap_read(ddata->ram_regmap, EC_MODEL, &model);
if (ret < 0)
return ret;
/* Currently we only support the cells present on Dell Ariel model. */
if (model != 'J') {
dev_err(dev, "unknown board model: %02x\n", model);
return -ENODEV;
}
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
ariel_ec_cells,
ARRAY_SIZE(ariel_ec_cells),
NULL, 0, NULL);
if (ret)
return ret;
if (of_property_read_bool(np, "system-power-controller")) {
ddata->off_gpios =
devm_gpiod_get_array_optional(dev, "off", GPIOD_IN);
if (IS_ERR(ddata->off_gpios))
return PTR_ERR(ddata->off_gpios);
if (ddata->off_gpios->ndescs < 2) {
dev_err(dev, "invalid off-gpios property\n");
return -EINVAL;
}
}
if (ddata->off_gpios) {
register_restart_handler(&kb3930_restart_nb);
if (!pm_power_off)
pm_power_off = kb3930_pm_power_off;
}
return 0;
}
static int kb3930_remove(struct i2c_client *client)
{
struct kb3930 *ddata = i2c_get_clientdata(client);
if (ddata->off_gpios) {
if (pm_power_off == kb3930_pm_power_off)
pm_power_off = NULL;
unregister_restart_handler(&kb3930_restart_nb);
}
kb3930_power_off = NULL;
return 0;
}
static const struct of_device_id kb3930_dt_ids[] = {
{ .compatible = "ene,kb3930" },
{ }
};
MODULE_DEVICE_TABLE(of, kb3930_dt_ids);
static struct i2c_driver kb3930_driver = {
.probe_new = kb3930_probe,
.remove = kb3930_remove,
.driver = {
.name = "ene-kb3930",
.of_match_table = of_match_ptr(kb3930_dt_ids),
},
};
module_i2c_driver(kb3930_driver);
MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
MODULE_DESCRIPTION("ENE KB3930 Embedded Controller Driver");
MODULE_LICENSE("Dual BSD/GPL");
...@@ -293,6 +293,10 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { ...@@ -293,6 +293,10 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x5ac4), (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, 0x5ac6), (kernel_ulong_t)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x5aee), (kernel_ulong_t)&bxt_uart_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, 0x98c7), (kernel_ulong_t)&bxt_uart_info },
/* SPT-LP */ /* SPT-LP */
{ PCI_VDEVICE(INTEL, 0x9d27), (kernel_ulong_t)&spt_uart_info }, { PCI_VDEVICE(INTEL, 0x9d27), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info }, { PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info },
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel MAX 10 Board Management Controller chip
*
* Copyright (C) 2018-2020 Intel Corporation. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/init.h>
#include <linux/mfd/core.h>
#include <linux/mfd/intel-m10-bmc.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
enum m10bmc_type {
M10_N3000,
};
static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
{ .name = "n3000bmc-hwmon" },
{ .name = "n3000bmc-retimer" },
{ .name = "n3000bmc-secure" },
};
static struct regmap_config intel_m10bmc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = M10BMC_MEM_END,
};
static ssize_t bmc_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *ddata = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = m10bmc_sys_read(ddata, M10BMC_BUILD_VER, &val);
if (ret)
return ret;
return sprintf(buf, "0x%x\n", val);
}
static DEVICE_ATTR_RO(bmc_version);
static ssize_t bmcfw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *ddata = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = m10bmc_sys_read(ddata, NIOS2_FW_VERSION, &val);
if (ret)
return ret;
return sprintf(buf, "0x%x\n", val);
}
static DEVICE_ATTR_RO(bmcfw_version);
static struct attribute *m10bmc_attrs[] = {
&dev_attr_bmc_version.attr,
&dev_attr_bmcfw_version.attr,
NULL,
};
ATTRIBUTE_GROUPS(m10bmc);
static int check_m10bmc_version(struct intel_m10bmc *ddata)
{
unsigned int v;
int ret;
/*
* This check is to filter out the very old legacy BMC versions,
* M10BMC_LEGACY_SYS_BASE is the offset to this old block of mmio
* registers. In the old BMC chips, the BMC version info is stored
* in this old version register (M10BMC_LEGACY_SYS_BASE +
* M10BMC_BUILD_VER), so its read out value would have not been
* LEGACY_INVALID (0xffffffff). But in new BMC chips that the
* driver supports, the value of this register should be
* LEGACY_INVALID.
*/
ret = m10bmc_raw_read(ddata,
M10BMC_LEGACY_SYS_BASE + M10BMC_BUILD_VER, &v);
if (ret)
return -ENODEV;
if (v != M10BMC_VER_LEGACY_INVALID) {
dev_err(ddata->dev, "bad version M10BMC detected\n");
return -ENODEV;
}
return 0;
}
static int intel_m10_bmc_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct device *dev = &spi->dev;
struct mfd_cell *cells;
struct intel_m10bmc *ddata;
int ret, n_cell;
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
ddata->dev = dev;
ddata->regmap =
devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config);
if (IS_ERR(ddata->regmap)) {
ret = PTR_ERR(ddata->regmap);
dev_err(dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
spi_set_drvdata(spi, ddata);
ret = check_m10bmc_version(ddata);
if (ret) {
dev_err(dev, "Failed to identify m10bmc hardware\n");
return ret;
}
switch (id->driver_data) {
case M10_N3000:
cells = m10bmc_pacn3000_subdevs;
n_cell = ARRAY_SIZE(m10bmc_pacn3000_subdevs);
break;
default:
return -ENODEV;
}
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, n_cell,
NULL, 0, NULL);
if (ret)
dev_err(dev, "Failed to register sub-devices: %d\n", ret);
return ret;
}
static const struct spi_device_id m10bmc_spi_id[] = {
{ "m10-n3000", M10_N3000 },
{ }
};
MODULE_DEVICE_TABLE(spi, m10bmc_spi_id);
static struct spi_driver intel_m10bmc_spi_driver = {
.driver = {
.name = "intel-m10-bmc",
.dev_groups = m10bmc_groups,
},
.probe = intel_m10_bmc_spi_probe,
.id_table = m10bmc_spi_id,
};
module_spi_driver(intel_m10bmc_spi_driver);
MODULE_DESCRIPTION("Intel MAX 10 BMC Device Driver");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:intel-m10-bmc");
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/acpi.h>
#define MAX_ID_LEN 4 #define MAX_ID_LEN 4
static char force_device_id[MAX_ID_LEN + 1] = ""; static char force_device_id[MAX_ID_LEN + 1] = "";
...@@ -124,6 +125,7 @@ static const struct kempld_platform_data kempld_platform_data_generic = { ...@@ -124,6 +125,7 @@ static const struct kempld_platform_data kempld_platform_data_generic = {
}; };
static struct platform_device *kempld_pdev; static struct platform_device *kempld_pdev;
static bool kempld_acpi_mode;
static int kempld_create_platform_device(const struct dmi_system_id *id) static int kempld_create_platform_device(const struct dmi_system_id *id)
{ {
...@@ -426,13 +428,93 @@ static int kempld_detect_device(struct kempld_device_data *pld) ...@@ -426,13 +428,93 @@ static int kempld_detect_device(struct kempld_device_data *pld)
return ret; return ret;
} }
#ifdef CONFIG_ACPI
static int kempld_get_acpi_data(struct platform_device *pdev)
{
struct list_head resource_list;
struct resource *resources;
struct resource_entry *rentry;
struct device *dev = &pdev->dev;
struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
const struct kempld_platform_data *pdata;
int ret;
int count;
pdata = acpi_device_get_match_data(dev);
ret = platform_device_add_data(pdev, pdata,
sizeof(struct kempld_platform_data));
if (ret)
return ret;
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(acpi_dev, &resource_list, NULL, NULL);
if (ret < 0)
goto out;
count = ret;
if (count == 0) {
ret = platform_device_add_resources(pdev, pdata->ioresource, 1);
goto out;
}
resources = devm_kcalloc(&acpi_dev->dev, count, sizeof(*resources),
GFP_KERNEL);
if (!resources) {
ret = -ENOMEM;
goto out;
}
count = 0;
list_for_each_entry(rentry, &resource_list, node) {
memcpy(&resources[count], rentry->res,
sizeof(*resources));
count++;
}
ret = platform_device_add_resources(pdev, resources, count);
out:
acpi_dev_free_resource_list(&resource_list);
return ret;
}
#else
static int kempld_get_acpi_data(struct platform_device *pdev)
{
return -ENODEV;
}
#endif /* CONFIG_ACPI */
static int kempld_probe(struct platform_device *pdev) static int kempld_probe(struct platform_device *pdev)
{ {
const struct kempld_platform_data *pdata = const struct kempld_platform_data *pdata;
dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct kempld_device_data *pld; struct kempld_device_data *pld;
struct resource *ioport; struct resource *ioport;
int ret;
if (kempld_pdev == NULL) {
/*
* No kempld_pdev device has been registered in kempld_init,
* so we seem to be probing an ACPI platform device.
*/
ret = kempld_get_acpi_data(pdev);
if (ret)
return ret;
kempld_acpi_mode = true;
} else if (kempld_pdev != pdev) {
/*
* The platform device we are probing is not the one we
* registered in kempld_init using the DMI table, so this one
* comes from ACPI.
* As we can only probe one - abort here and use the DMI
* based one instead.
*/
dev_notice(dev, "platform device exists - not using ACPI\n");
return -ENODEV;
}
pdata = dev_get_platdata(dev);
pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL); pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL);
if (!pld) if (!pld)
...@@ -471,9 +553,19 @@ static int kempld_remove(struct platform_device *pdev) ...@@ -471,9 +553,19 @@ static int kempld_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_ACPI
static const struct acpi_device_id kempld_acpi_table[] = {
{ "KEM0001", (kernel_ulong_t)&kempld_platform_data_generic },
{}
};
MODULE_DEVICE_TABLE(acpi, kempld_acpi_table);
#endif
static struct platform_driver kempld_driver = { static struct platform_driver kempld_driver = {
.driver = { .driver = {
.name = "kempld", .name = "kempld",
.acpi_match_table = ACPI_PTR(kempld_acpi_table),
.probe_type = PROBE_FORCE_SYNCHRONOUS,
}, },
.probe = kempld_probe, .probe = kempld_probe,
.remove = kempld_remove, .remove = kempld_remove,
...@@ -792,6 +884,7 @@ MODULE_DEVICE_TABLE(dmi, kempld_dmi_table); ...@@ -792,6 +884,7 @@ MODULE_DEVICE_TABLE(dmi, kempld_dmi_table);
static int __init kempld_init(void) static int __init kempld_init(void)
{ {
const struct dmi_system_id *id; const struct dmi_system_id *id;
int ret;
if (force_device_id[0]) { if (force_device_id[0]) {
for (id = kempld_dmi_table; for (id = kempld_dmi_table;
...@@ -801,12 +894,24 @@ static int __init kempld_init(void) ...@@ -801,12 +894,24 @@ static int __init kempld_init(void)
break; break;
if (id->matches[0].slot == DMI_NONE) if (id->matches[0].slot == DMI_NONE)
return -ENODEV; return -ENODEV;
} else {
if (!dmi_check_system(kempld_dmi_table))
return -ENODEV;
} }
return platform_driver_register(&kempld_driver); ret = platform_driver_register(&kempld_driver);
if (ret)
return ret;
/*
* With synchronous probing the device should already be probed now.
* If no device id is forced and also no ACPI definition for the
* device was found, scan DMI table as fallback.
*
* If drivers_autoprobing is disabled and the device is found here,
* only that device can be bound manually later.
*/
if (!kempld_pdev && !kempld_acpi_mode)
dmi_check_system(kempld_dmi_table);
return 0;
} }
static void __exit kempld_exit(void) static void __exit kempld_exit(void)
......
...@@ -122,11 +122,13 @@ static int khadas_mcu_probe(struct i2c_client *client, ...@@ -122,11 +122,13 @@ static int khadas_mcu_probe(struct i2c_client *client,
return 0; return 0;
} }
#ifdef CONFIG_OF
static const struct of_device_id khadas_mcu_of_match[] = { static const struct of_device_id khadas_mcu_of_match[] = {
{ .compatible = "khadas,mcu", }, { .compatible = "khadas,mcu", },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, khadas_mcu_of_match); MODULE_DEVICE_TABLE(of, khadas_mcu_of_match);
#endif
static struct i2c_driver khadas_mcu_driver = { static struct i2c_driver khadas_mcu_driver = {
.driver = { .driver = {
......
...@@ -26,6 +26,10 @@ static const struct mfd_cell lp87565_cells[] = { ...@@ -26,6 +26,10 @@ static const struct mfd_cell lp87565_cells[] = {
static const struct of_device_id of_lp87565_match_table[] = { static const struct of_device_id of_lp87565_match_table[] = {
{ .compatible = "ti,lp87565", }, { .compatible = "ti,lp87565", },
{
.compatible = "ti,lp87524-q1",
.data = (void *)LP87565_DEVICE_TYPE_LP87524_Q1,
},
{ {
.compatible = "ti,lp87565-q1", .compatible = "ti,lp87565-q1",
.data = (void *)LP87565_DEVICE_TYPE_LP87565_Q1, .data = (void *)LP87565_DEVICE_TYPE_LP87565_Q1,
......
...@@ -369,19 +369,14 @@ EXPORT_SYMBOL_GPL(madera_of_match); ...@@ -369,19 +369,14 @@ EXPORT_SYMBOL_GPL(madera_of_match);
static int madera_get_reset_gpio(struct madera *madera) static int madera_get_reset_gpio(struct madera *madera)
{ {
struct gpio_desc *reset; struct gpio_desc *reset;
int ret;
if (madera->pdata.reset) if (madera->pdata.reset)
return 0; return 0;
reset = devm_gpiod_get_optional(madera->dev, "reset", GPIOD_OUT_LOW); reset = devm_gpiod_get_optional(madera->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset)) { if (IS_ERR(reset))
ret = PTR_ERR(reset); return dev_err_probe(madera->dev, PTR_ERR(reset),
if (ret != -EPROBE_DEFER) "Failed to request /RESET");
dev_err(madera->dev, "Failed to request /RESET: %d\n",
ret);
return ret;
}
/* /*
* A hard reset is needed for full reset of the chip. We allow running * A hard reset is needed for full reset of the chip. We allow running
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/version.h>
#include <linux/mfd/mt6360.h> #include <linux/mfd/mt6360.h>
......
...@@ -25,6 +25,7 @@ static const struct mfd_cell rn5t618_cells[] = { ...@@ -25,6 +25,7 @@ static const struct mfd_cell rn5t618_cells[] = {
static const struct mfd_cell rc5t619_cells[] = { static const struct mfd_cell rc5t619_cells[] = {
{ .name = "rn5t618-adc" }, { .name = "rn5t618-adc" },
{ .name = "rn5t618-power" },
{ .name = "rn5t618-regulator" }, { .name = "rn5t618-regulator" },
{ .name = "rc5t619-rtc" }, { .name = "rc5t619-rtc" },
{ .name = "rn5t618-wdt" }, { .name = "rn5t618-wdt" },
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Simple MFD - I2C
*
* This driver creates a single register map with the intention for it to be
* shared by all sub-devices. Children can use their parent's device structure
* (dev.parent) in order to reference it.
*
* Once the register map has been successfully initialised, any sub-devices
* represented by child nodes in Device Tree will be subsequently registered.
*/
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
static const struct regmap_config simple_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int simple_mfd_i2c_probe(struct i2c_client *i2c)
{
const struct regmap_config *config;
struct regmap *regmap;
config = device_get_match_data(&i2c->dev);
if (!config)
config = &simple_regmap_config;
regmap = devm_regmap_init_i2c(i2c, config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return devm_of_platform_populate(&i2c->dev);
}
static const struct of_device_id simple_mfd_i2c_of_match[] = {
{ .compatible = "kontron,sl28cpld" },
{}
};
MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match);
static struct i2c_driver simple_mfd_i2c_driver = {
.probe_new = simple_mfd_i2c_probe,
.driver = {
.name = "simple-mfd-i2c",
.of_match_table = simple_mfd_i2c_of_match,
},
};
module_i2c_driver(simple_mfd_i2c_driver);
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
MODULE_DESCRIPTION("Simple MFD - I2C driver");
MODULE_LICENSE("GPL v2");
...@@ -1415,8 +1415,14 @@ static int sm501_plat_probe(struct platform_device *dev) ...@@ -1415,8 +1415,14 @@ static int sm501_plat_probe(struct platform_device *dev)
goto err_claim; goto err_claim;
} }
return sm501_init_dev(sm); ret = sm501_init_dev(sm);
if (ret)
goto err_unmap;
return 0;
err_unmap:
iounmap(sm->regs);
err_claim: err_claim:
release_mem_region(sm->io_res->start, 0x100); release_mem_region(sm->io_res->start, 0x100);
err_res: err_res:
......
...@@ -189,7 +189,7 @@ static int sprd_pmic_probe(struct spi_device *spi) ...@@ -189,7 +189,7 @@ static int sprd_pmic_probe(struct spi_device *spi)
ddata->irqs[i].mask = BIT(i); ddata->irqs[i].mask = BIT(i);
ret = devm_regmap_add_irq_chip(&spi->dev, ddata->regmap, ddata->irq, ret = devm_regmap_add_irq_chip(&spi->dev, ddata->regmap, ddata->irq,
IRQF_ONESHOT | IRQF_NO_SUSPEND, 0, IRQF_ONESHOT, 0,
&ddata->irq_chip, &ddata->irq_data); &ddata->irq_chip, &ddata->irq_data);
if (ret) { if (ret) {
dev_err(&spi->dev, "Failed to add PMIC irq chip %d\n", ret); dev_err(&spi->dev, "Failed to add PMIC irq chip %d\n", ret);
...@@ -202,9 +202,34 @@ static int sprd_pmic_probe(struct spi_device *spi) ...@@ -202,9 +202,34 @@ static int sprd_pmic_probe(struct spi_device *spi)
return ret; return ret;
} }
device_init_wakeup(&spi->dev, true);
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int sprd_pmic_suspend(struct device *dev)
{
struct sprd_pmic *ddata = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(ddata->irq);
return 0;
}
static int sprd_pmic_resume(struct device *dev)
{
struct sprd_pmic *ddata = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(ddata->irq);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(sprd_pmic_pm_ops, sprd_pmic_suspend, sprd_pmic_resume);
static const struct of_device_id sprd_pmic_match[] = { static const struct of_device_id sprd_pmic_match[] = {
{ .compatible = "sprd,sc2731", .data = &sc2731_data }, { .compatible = "sprd,sc2731", .data = &sc2731_data },
{}, {},
...@@ -215,6 +240,7 @@ static struct spi_driver sprd_pmic_driver = { ...@@ -215,6 +240,7 @@ static struct spi_driver sprd_pmic_driver = {
.driver = { .driver = {
.name = "sc27xx-pmic", .name = "sc27xx-pmic",
.of_match_table = sprd_pmic_match, .of_match_table = sprd_pmic_match,
.pm = &sprd_pmic_pm_ops,
}, },
.probe = sprd_pmic_probe, .probe = sprd_pmic_probe,
}; };
......
...@@ -331,11 +331,9 @@ static int stmfx_chip_init(struct i2c_client *client) ...@@ -331,11 +331,9 @@ static int stmfx_chip_init(struct i2c_client *client)
ret = PTR_ERR_OR_ZERO(stmfx->vdd); ret = PTR_ERR_OR_ZERO(stmfx->vdd);
if (ret == -ENODEV) { if (ret == -ENODEV) {
stmfx->vdd = NULL; stmfx->vdd = NULL;
} else if (ret == -EPROBE_DEFER) { } else {
return ret; return dev_err_probe(&client->dev, ret,
} else if (ret) { "Failed to get VDD regulator\n");
dev_err(&client->dev, "Failed to get VDD regulator: %d\n", ret);
return ret;
} }
if (stmfx->vdd) { if (stmfx->vdd) {
......
...@@ -108,7 +108,6 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) ...@@ -108,7 +108,6 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
syscon_config.max_register = resource_size(&res) - reg_io_width; syscon_config.max_register = resource_size(&res) - reg_io_width;
regmap = regmap_init_mmio(NULL, base, &syscon_config); regmap = regmap_init_mmio(NULL, base, &syscon_config);
kfree(syscon_config.name);
if (IS_ERR(regmap)) { if (IS_ERR(regmap)) {
pr_err("regmap init failed\n"); pr_err("regmap init failed\n");
ret = PTR_ERR(regmap); ret = PTR_ERR(regmap);
...@@ -145,6 +144,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) ...@@ -145,6 +144,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
regmap_exit(regmap); regmap_exit(regmap);
err_regmap: err_regmap:
iounmap(base); iounmap(base);
kfree(syscon_config.name);
err_map: err_map:
kfree(syscon); kfree(syscon);
return ERR_PTR(ret); return ERR_PTR(ret);
......
...@@ -219,12 +219,9 @@ static int wcd934x_slim_probe(struct slim_device *sdev) ...@@ -219,12 +219,9 @@ static int wcd934x_slim_probe(struct slim_device *sdev)
return -ENOMEM; return -ENOMEM;
ddata->irq = of_irq_get(np, 0); ddata->irq = of_irq_get(np, 0);
if (ddata->irq < 0) { if (ddata->irq < 0)
if (ddata->irq != -EPROBE_DEFER) return dev_err_probe(ddata->dev, ddata->irq,
dev_err(ddata->dev, "Failed to get IRQ: err = %d\n", "Failed to get IRQ\n");
ddata->irq);
return ddata->irq;
}
reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
if (reset_gpio < 0) { if (reset_gpio < 0) {
......
...@@ -428,6 +428,16 @@ config PWM_SIFIVE ...@@ -428,6 +428,16 @@ config PWM_SIFIVE
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-sifive. will be called pwm-sifive.
config PWM_SL28CPLD
tristate "Kontron sl28cpld PWM support"
depends on MFD_SL28CPLD || COMPILE_TEST
help
Generic PWM framework driver for board management controller
found on the Kontron sl28 CPLD.
To compile this driver as a module, choose M here: the module
will be called pwm-sl28cpld.
config PWM_SPEAR config PWM_SPEAR
tristate "STMicroelectronics SPEAr PWM support" tristate "STMicroelectronics SPEAr PWM support"
depends on PLAT_SPEAR || COMPILE_TEST depends on PLAT_SPEAR || COMPILE_TEST
......
...@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o ...@@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o
obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
obj-$(CONFIG_PWM_SPRD) += pwm-sprd.o obj-$(CONFIG_PWM_SPRD) += pwm-sprd.o
obj-$(CONFIG_PWM_STI) += pwm-sti.o obj-$(CONFIG_PWM_STI) += pwm-sti.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* sl28cpld PWM driver
*
* Copyright (c) 2020 Michael Walle <michael@walle.cc>
*
* There is no public datasheet available for this PWM core. But it is easy
* enough to be briefly explained. It consists of one 8-bit counter. The PWM
* supports four distinct frequencies by selecting when to reset the counter.
* With the prescaler setting you can select which bit of the counter is used
* to reset it. This implies that the higher the frequency the less remaining
* bits are available for the actual counter.
*
* Let cnt[7:0] be the counter, clocked at 32kHz:
* +-----------+--------+--------------+-----------+---------------+
* | prescaler | reset | counter bits | frequency | period length |
* +-----------+--------+--------------+-----------+---------------+
* | 0 | cnt[7] | cnt[6:0] | 250 Hz | 4000000 ns |
* | 1 | cnt[6] | cnt[5:0] | 500 Hz | 2000000 ns |
* | 2 | cnt[5] | cnt[4:0] | 1 kHz | 1000000 ns |
* | 3 | cnt[4] | cnt[3:0] | 2 kHz | 500000 ns |
* +-----------+--------+--------------+-----------+---------------+
*
* Limitations:
* - The hardware cannot generate a 100% duty cycle if the prescaler is 0.
* - The hardware cannot atomically set the prescaler and the counter value,
* which might lead to glitches and inconsistent states if a write fails.
* - The counter is not reset if you switch the prescaler which leads
* to glitches, too.
* - The duty cycle will switch immediately and not after a complete cycle.
* - Depending on the actual implementation, disabling the PWM might have
* side effects. For example, if the output pin is shared with a GPIO pin
* it will automatically switch back to GPIO mode.
*/
#include <linux/bitfield.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
/*
* PWM timer block registers.
*/
#define SL28CPLD_PWM_CTRL 0x00
#define SL28CPLD_PWM_CTRL_ENABLE BIT(7)
#define SL28CPLD_PWM_CTRL_PRESCALER_MASK GENMASK(1, 0)
#define SL28CPLD_PWM_CYCLE 0x01
#define SL28CPLD_PWM_CYCLE_MAX GENMASK(6, 0)
#define SL28CPLD_PWM_CLK 32000 /* 32 kHz */
#define SL28CPLD_PWM_MAX_DUTY_CYCLE(prescaler) (1 << (7 - (prescaler)))
#define SL28CPLD_PWM_PERIOD(prescaler) \
(NSEC_PER_SEC / SL28CPLD_PWM_CLK * SL28CPLD_PWM_MAX_DUTY_CYCLE(prescaler))
/*
* We calculate the duty cycle like this:
* duty_cycle_ns = pwm_cycle_reg * max_period_ns / max_duty_cycle
*
* With
* max_period_ns = 1 << (7 - prescaler) / SL28CPLD_PWM_CLK * NSEC_PER_SEC
* max_duty_cycle = 1 << (7 - prescaler)
* this then simplifies to:
* duty_cycle_ns = pwm_cycle_reg / SL28CPLD_PWM_CLK * NSEC_PER_SEC
* = NSEC_PER_SEC / SL28CPLD_PWM_CLK * pwm_cycle_reg
*
* NSEC_PER_SEC is a multiple of SL28CPLD_PWM_CLK, therefore we're not losing
* precision by doing the divison first.
*/
#define SL28CPLD_PWM_TO_DUTY_CYCLE(reg) \
(NSEC_PER_SEC / SL28CPLD_PWM_CLK * (reg))
#define SL28CPLD_PWM_FROM_DUTY_CYCLE(duty_cycle) \
(DIV_ROUND_DOWN_ULL((duty_cycle), NSEC_PER_SEC / SL28CPLD_PWM_CLK))
#define sl28cpld_pwm_read(priv, reg, val) \
regmap_read((priv)->regmap, (priv)->offset + (reg), (val))
#define sl28cpld_pwm_write(priv, reg, val) \
regmap_write((priv)->regmap, (priv)->offset + (reg), (val))
struct sl28cpld_pwm {
struct pwm_chip pwm_chip;
struct regmap *regmap;
u32 offset;
};
static void sl28cpld_pwm_get_state(struct pwm_chip *chip,
struct pwm_device *pwm,
struct pwm_state *state)
{
struct sl28cpld_pwm *priv = dev_get_drvdata(chip->dev);
unsigned int reg;
int prescaler;
sl28cpld_pwm_read(priv, SL28CPLD_PWM_CTRL, &reg);
state->enabled = reg & SL28CPLD_PWM_CTRL_ENABLE;
prescaler = FIELD_GET(SL28CPLD_PWM_CTRL_PRESCALER_MASK, reg);
state->period = SL28CPLD_PWM_PERIOD(prescaler);
sl28cpld_pwm_read(priv, SL28CPLD_PWM_CYCLE, &reg);
state->duty_cycle = SL28CPLD_PWM_TO_DUTY_CYCLE(reg);
state->polarity = PWM_POLARITY_NORMAL;
/*
* Sanitize values for the PWM core. Depending on the prescaler it
* might happen that we calculate a duty_cycle greater than the actual
* period. This might happen if someone (e.g. the bootloader) sets an
* invalid combination of values. The behavior of the hardware is
* undefined in this case. But we need to report sane values back to
* the PWM core.
*/
state->duty_cycle = min(state->duty_cycle, state->period);
}
static int sl28cpld_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct sl28cpld_pwm *priv = dev_get_drvdata(chip->dev);
unsigned int cycle, prescaler;
bool write_duty_cycle_first;
int ret;
u8 ctrl;
/* Polarity inversion is not supported */
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
/*
* Calculate the prescaler. Pick the biggest period that isn't
* bigger than the requested period.
*/
prescaler = DIV_ROUND_UP_ULL(SL28CPLD_PWM_PERIOD(0), state->period);
prescaler = order_base_2(prescaler);
if (prescaler > field_max(SL28CPLD_PWM_CTRL_PRESCALER_MASK))
return -ERANGE;
ctrl = FIELD_PREP(SL28CPLD_PWM_CTRL_PRESCALER_MASK, prescaler);
if (state->enabled)
ctrl |= SL28CPLD_PWM_CTRL_ENABLE;
cycle = SL28CPLD_PWM_FROM_DUTY_CYCLE(state->duty_cycle);
cycle = min_t(unsigned int, cycle, SL28CPLD_PWM_MAX_DUTY_CYCLE(prescaler));
/*
* Work around the hardware limitation. See also above. Trap 100% duty
* cycle if the prescaler is 0. Set prescaler to 1 instead. We don't
* care about the frequency because its "all-one" in either case.
*
* We don't need to check the actual prescaler setting, because only
* if the prescaler is 0 we can have this particular value.
*/
if (cycle == SL28CPLD_PWM_MAX_DUTY_CYCLE(0)) {
ctrl &= ~SL28CPLD_PWM_CTRL_PRESCALER_MASK;
ctrl |= FIELD_PREP(SL28CPLD_PWM_CTRL_PRESCALER_MASK, 1);
cycle = SL28CPLD_PWM_MAX_DUTY_CYCLE(1);
}
/*
* To avoid glitches when we switch the prescaler, we have to make sure
* we have a valid duty cycle for the new mode.
*
* Take the current prescaler (or the current period length) into
* account to decide whether we have to write the duty cycle or the new
* prescaler first. If the period length is decreasing we have to
* write the duty cycle first.
*/
write_duty_cycle_first = pwm->state.period > state->period;
if (write_duty_cycle_first) {
ret = sl28cpld_pwm_write(priv, SL28CPLD_PWM_CYCLE, cycle);
if (ret)
return ret;
}
ret = sl28cpld_pwm_write(priv, SL28CPLD_PWM_CTRL, ctrl);
if (ret)
return ret;
if (!write_duty_cycle_first) {
ret = sl28cpld_pwm_write(priv, SL28CPLD_PWM_CYCLE, cycle);
if (ret)
return ret;
}
return 0;
}
static const struct pwm_ops sl28cpld_pwm_ops = {
.apply = sl28cpld_pwm_apply,
.get_state = sl28cpld_pwm_get_state,
.owner = THIS_MODULE,
};
static int sl28cpld_pwm_probe(struct platform_device *pdev)
{
struct sl28cpld_pwm *priv;
struct pwm_chip *chip;
int ret;
if (!pdev->dev.parent) {
dev_err(&pdev->dev, "no parent device\n");
return -ENODEV;
}
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!priv->regmap) {
dev_err(&pdev->dev, "could not get parent regmap\n");
return -ENODEV;
}
ret = device_property_read_u32(&pdev->dev, "reg", &priv->offset);
if (ret) {
dev_err(&pdev->dev, "no 'reg' property found (%pe)\n",
ERR_PTR(ret));
return -EINVAL;
}
/* Initialize the pwm_chip structure */
chip = &priv->pwm_chip;
chip->dev = &pdev->dev;
chip->ops = &sl28cpld_pwm_ops;
chip->base = -1;
chip->npwm = 1;
ret = pwmchip_add(&priv->pwm_chip);
if (ret) {
dev_err(&pdev->dev, "failed to add PWM chip (%pe)",
ERR_PTR(ret));
return ret;
}
platform_set_drvdata(pdev, priv);
return 0;
}
static int sl28cpld_pwm_remove(struct platform_device *pdev)
{
struct sl28cpld_pwm *priv = platform_get_drvdata(pdev);
return pwmchip_remove(&priv->pwm_chip);
}
static const struct of_device_id sl28cpld_pwm_of_match[] = {
{ .compatible = "kontron,sl28cpld-pwm" },
{}
};
MODULE_DEVICE_TABLE(of, sl28cpld_pwm_of_match);
static struct platform_driver sl28cpld_pwm_driver = {
.probe = sl28cpld_pwm_probe,
.remove = sl28cpld_pwm_remove,
.driver = {
.name = "sl28cpld-pwm",
.of_match_table = sl28cpld_pwm_of_match,
},
};
module_platform_driver(sl28cpld_pwm_driver);
MODULE_DESCRIPTION("sl28cpld PWM Driver");
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
MODULE_LICENSE("GPL");
...@@ -340,6 +340,17 @@ config MLX_WDT ...@@ -340,6 +340,17 @@ config MLX_WDT
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called mlx-wdt. module will be called mlx-wdt.
config SL28CPLD_WATCHDOG
tristate "Kontron sl28cpld Watchdog"
depends on MFD_SL28CPLD || COMPILE_TEST
select WATCHDOG_CORE
help
Say Y here to include support for the watchdog timer
on the Kontron sl28 CPLD.
To compile this driver as a module, choose M here: the
module will be called sl28cpld_wdt.
# ALPHA Architecture # ALPHA Architecture
# ARM Architecture # ARM Architecture
......
...@@ -225,3 +225,4 @@ obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o ...@@ -225,3 +225,4 @@ obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o
obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
obj-$(CONFIG_SL28CPLD_WATCHDOG) += sl28cpld_wdt.o
// SPDX-License-Identifier: GPL-2.0-only
/*
* sl28cpld watchdog driver
*
* Copyright 2020 Kontron Europe GmbH
*/
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/watchdog.h>
/*
* Watchdog timer block registers.
*/
#define WDT_CTRL 0x00
#define WDT_CTRL_EN BIT(0)
#define WDT_CTRL_LOCK BIT(2)
#define WDT_CTRL_ASSERT_SYS_RESET BIT(6)
#define WDT_CTRL_ASSERT_WDT_TIMEOUT BIT(7)
#define WDT_TIMEOUT 0x01
#define WDT_KICK 0x02
#define WDT_KICK_VALUE 0x6b
#define WDT_COUNT 0x03
#define WDT_DEFAULT_TIMEOUT 10
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static int timeout;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
struct sl28cpld_wdt {
struct watchdog_device wdd;
struct regmap *regmap;
u32 offset;
bool assert_wdt_timeout;
};
static int sl28cpld_wdt_ping(struct watchdog_device *wdd)
{
struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
return regmap_write(wdt->regmap, wdt->offset + WDT_KICK,
WDT_KICK_VALUE);
}
static int sl28cpld_wdt_start(struct watchdog_device *wdd)
{
struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
unsigned int val;
val = WDT_CTRL_EN | WDT_CTRL_ASSERT_SYS_RESET;
if (wdt->assert_wdt_timeout)
val |= WDT_CTRL_ASSERT_WDT_TIMEOUT;
if (nowayout)
val |= WDT_CTRL_LOCK;
return regmap_update_bits(wdt->regmap, wdt->offset + WDT_CTRL,
val, val);
}
static int sl28cpld_wdt_stop(struct watchdog_device *wdd)
{
struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
return regmap_update_bits(wdt->regmap, wdt->offset + WDT_CTRL,
WDT_CTRL_EN, 0);
}
static unsigned int sl28cpld_wdt_get_timeleft(struct watchdog_device *wdd)
{
struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
unsigned int val;
int ret;
ret = regmap_read(wdt->regmap, wdt->offset + WDT_COUNT, &val);
if (ret)
return 0;
return val;
}
static int sl28cpld_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct sl28cpld_wdt *wdt = watchdog_get_drvdata(wdd);
int ret;
ret = regmap_write(wdt->regmap, wdt->offset + WDT_TIMEOUT, timeout);
if (ret)
return ret;
wdd->timeout = timeout;
return 0;
}
static const struct watchdog_info sl28cpld_wdt_info = {
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.identity = "sl28cpld watchdog",
};
static struct watchdog_ops sl28cpld_wdt_ops = {
.owner = THIS_MODULE,
.start = sl28cpld_wdt_start,
.stop = sl28cpld_wdt_stop,
.ping = sl28cpld_wdt_ping,
.set_timeout = sl28cpld_wdt_set_timeout,
.get_timeleft = sl28cpld_wdt_get_timeleft,
};
static int sl28cpld_wdt_probe(struct platform_device *pdev)
{
struct watchdog_device *wdd;
struct sl28cpld_wdt *wdt;
unsigned int status;
unsigned int val;
int ret;
if (!pdev->dev.parent)
return -ENODEV;
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
wdt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!wdt->regmap)
return -ENODEV;
ret = device_property_read_u32(&pdev->dev, "reg", &wdt->offset);
if (ret)
return -EINVAL;
wdt->assert_wdt_timeout = device_property_read_bool(&pdev->dev,
"kontron,assert-wdt-timeout-pin");
/* initialize struct watchdog_device */
wdd = &wdt->wdd;
wdd->parent = &pdev->dev;
wdd->info = &sl28cpld_wdt_info;
wdd->ops = &sl28cpld_wdt_ops;
wdd->min_timeout = 1;
wdd->max_timeout = 255;
watchdog_set_drvdata(wdd, wdt);
watchdog_stop_on_reboot(wdd);
/*
* Read the status early, in case of an error, we haven't modified the
* hardware.
*/
ret = regmap_read(wdt->regmap, wdt->offset + WDT_CTRL, &status);
if (ret)
return ret;
/*
* Initial timeout value, may be overwritten by device tree or module
* parmeter in watchdog_init_timeout().
*
* Reading a zero here means that either the hardware has a default
* value of zero (which is very unlikely and definitely a hardware
* bug) or the bootloader set it to zero. In any case, we handle
* this case gracefully and set out own timeout.
*/
ret = regmap_read(wdt->regmap, wdt->offset + WDT_TIMEOUT, &val);
if (ret)
return ret;
if (val)
wdd->timeout = val;
else
wdd->timeout = WDT_DEFAULT_TIMEOUT;
watchdog_init_timeout(wdd, timeout, &pdev->dev);
sl28cpld_wdt_set_timeout(wdd, wdd->timeout);
/* if the watchdog is locked, we set nowayout */
if (status & WDT_CTRL_LOCK)
nowayout = true;
watchdog_set_nowayout(wdd, nowayout);
/*
* If watchdog is already running, keep it enabled, but make
* sure its mode is set correctly.
*/
if (status & WDT_CTRL_EN) {
sl28cpld_wdt_start(wdd);
set_bit(WDOG_HW_RUNNING, &wdd->status);
}
ret = devm_watchdog_register_device(&pdev->dev, wdd);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register watchdog device\n");
return ret;
}
dev_info(&pdev->dev, "initial timeout %d sec%s\n",
wdd->timeout, nowayout ? ", nowayout" : "");
return 0;
}
static const struct of_device_id sl28cpld_wdt_of_match[] = {
{ .compatible = "kontron,sl28cpld-wdt" },
{}
};
MODULE_DEVICE_TABLE(of, sl28cpld_wdt_of_match);
static struct platform_driver sl28cpld_wdt_driver = {
.probe = sl28cpld_wdt_probe,
.driver = {
.name = "sl28cpld-wdt",
.of_match_table = sl28cpld_wdt_of_match,
},
};
module_platform_driver(sl28cpld_wdt_driver);
MODULE_DESCRIPTION("sl28cpld Watchdog Driver");
MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
MODULE_LICENSE("GPL");
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel MAX 10 Board Management Controller chip.
*
* Copyright (C) 2018-2020 Intel Corporation, Inc.
*/
#ifndef __MFD_INTEL_M10_BMC_H
#define __MFD_INTEL_M10_BMC_H
#include <linux/regmap.h>
#define M10BMC_LEGACY_SYS_BASE 0x300400
#define M10BMC_SYS_BASE 0x300800
#define M10BMC_MEM_END 0x200000fc
/* Register offset of system registers */
#define NIOS2_FW_VERSION 0x0
#define M10BMC_TEST_REG 0x3c
#define M10BMC_BUILD_VER 0x68
#define M10BMC_VER_MAJOR_MSK GENMASK(23, 16)
#define M10BMC_VER_PCB_INFO_MSK GENMASK(31, 24)
#define M10BMC_VER_LEGACY_INVALID 0xffffffff
/**
* struct intel_m10bmc - Intel MAX 10 BMC parent driver data structure
* @dev: this device
* @regmap: the regmap used to access registers by m10bmc itself
*/
struct intel_m10bmc {
struct device *dev;
struct regmap *regmap;
};
/*
* register access helper functions.
*
* m10bmc_raw_read - read m10bmc register per addr
* m10bmc_sys_read - read m10bmc system register per offset
*/
static inline int
m10bmc_raw_read(struct intel_m10bmc *m10bmc, unsigned int addr,
unsigned int *val)
{
int ret;
ret = regmap_read(m10bmc->regmap, addr, val);
if (ret)
dev_err(m10bmc->dev, "fail to read raw reg %x: %d\n",
addr, ret);
return ret;
}
/*
* The base of the system registers could be configured by HW developers, and
* in HW SPEC, the base is not added to the addresses of the system registers.
*
* This macro helps to simplify the accessing of the system registers. And if
* the base is reconfigured in HW, SW developers could simply change the
* M10BMC_SYS_BASE accordingly.
*/
#define m10bmc_sys_read(m10bmc, offset, val) \
m10bmc_raw_read(m10bmc, M10BMC_SYS_BASE + (offset), val)
#endif /* __MFD_INTEL_M10_BMC_H */
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
enum lp87565_device_type { enum lp87565_device_type {
LP87565_DEVICE_TYPE_UNKNOWN = 0, LP87565_DEVICE_TYPE_UNKNOWN = 0,
LP87565_DEVICE_TYPE_LP87524_Q1,
LP87565_DEVICE_TYPE_LP87561_Q1, LP87565_DEVICE_TYPE_LP87561_Q1,
LP87565_DEVICE_TYPE_LP87565_Q1, LP87565_DEVICE_TYPE_LP87565_Q1,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment