- 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAl+GrSsACgkQUa+KL4f8 d2FQ/w//TRSwPbXdUl+rYaDVQ9YwEk5Zi2U8EYBP8OGsKLjXs9GFkiuyQ5GwsoAd oD5N4Fh/QGxmdWafKXB96e/D+n4uFOOIWGCmNsE5NmvnWbjk1Jss4K4qV4IbUEW/ KWxUCKPhaNcs7Vj9nfN5jItCPBaG6bQJ3qgFwZHSMhe/Su4izUYlLCfhhVAFkq1k oF2TgJCllqPl7ZY+VxDPD5ZWIBhV+NZoFVRFQ7Kvw1elmE+///nrfuii261G+iBm kHi3PtcRMDW0+Uo8B9I9xtLVUrfBMtm9ThOrn8JYLOaJ7RzWwwF6WayAKsNbDuDB CiYKHDZWS72IxfW3KfHk1AvbsUMJQX1kmDJ6lAhXHa3E55nGbX+U2FwgMnczJUuM 7BzfD9pXp3nnfTQ6ZJtr1vGPLz3Nk7hcRlg9r+6TObIjzB+KNW/hyCYur7sX1Mbb wn3EZo5WhKnlcqrGhh3H0/kJDbHTIFDfNt80+bjLfIeKCQj1x6cByPWC7N6Ln4GC BDky88fb/Y9Q5bnvyEQDR16+QMpG3ko5lIUwt9Gtbhtt3/zjxzElxRlsq6u9scrp vZnnjqxSQ/9cvnwOsGMG+E8uVjZ+5lQu6Qi+0w20M4MX28hXLPQbpjAK/3uqH7Kz NLwiZe4dzRtVbAQ83ozGArTgvH9YZ5VOw/ieqk0aRf6mIExrENk= =1wSH -----END PGP SIGNATURE----- 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() ...
This commit is contained in:
Коммит
1a31c12371
|
@ -0,0 +1,15 @@
|
|||
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".
|
|
@ -1,45 +0,0 @@
|
|||
# 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>;
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
# 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
|
|
@ -0,0 +1,27 @@
|
|||
# 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
|
|
@ -0,0 +1,54 @@
|
|||
# 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
|
|||
: BAT_CTRL_INDB : : Battery Removal Indicator
|
||||
: 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_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_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
|
||||
ab8500-charger : : vddadc : Charger interface
|
||||
: MAIN_CH_UNPLUG_DET : : main charger unplug detection management (not in 8505)
|
||||
: MAIN_CHARGE_PLUG_DET : : main charger plug detection management (not in 8505)
|
||||
|
|
|
@ -71,7 +71,7 @@ Example:
|
|||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_charger_chglev &pinctrl_charger_lbo &pinctrl_charger_irq>;
|
||||
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,lbo-gpios = <&pioA 72 GPIO_ACTIVE_LOW>;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# 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
|
|||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
@ -157,7 +158,7 @@ examples:
|
|||
compatible = "gw,gsc";
|
||||
reg = <0x20>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <4 GPIO_ACTIVE_LOW>;
|
||||
interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
# 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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -1,79 +0,0 @@
|
|||
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 {
|
|||
compatible = "rohm,bd70528";
|
||||
reg = <0x4b>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <29 GPIO_ACTIVE_LOW>;
|
||||
interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
|
||||
clocks = <&osc 0>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "bd70528-32k-out";
|
||||
|
|
|
@ -44,6 +44,10 @@ properties:
|
|||
- hisilicon,peri-subctrl
|
||||
- microchip,sparx5-cpu-syscon
|
||||
- mstar,msc313-pmsleep
|
||||
- samsung,exynos3-sysreg
|
||||
- samsung,exynos4-sysreg
|
||||
- samsung,exynos5-sysreg
|
||||
- samsung,exynos5433-sysreg
|
||||
|
||||
- const: syscon
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
# 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,83 @@
|
|||
# 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,101 @@
|
|||
# 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,35 @@
|
|||
# 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
|
|
@ -0,0 +1,35 @@
|
|||
# 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
|
|||
sht3x
|
||||
shtc1
|
||||
sis5595
|
||||
sl28cpld
|
||||
smm665
|
||||
smsc47b397
|
||||
smsc47m192
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
.. 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
|
|||
This driver provides the support for driving/reading the gpio pins
|
||||
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
|
||||
bool "STMPE GPIOs"
|
||||
depends on MFD_STMPE
|
||||
|
|
|
@ -131,6 +131,7 @@ obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
|
|||
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
|
||||
obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.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_SPEAR_SPICS) += gpio-spear-spics.o
|
||||
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
// 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
|
|||
This driver can also be built as a module. If so, the module
|
||||
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
|
||||
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
|
|
|
@ -161,6 +161,7 @@ obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o
|
|||
obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
|
||||
obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
|
||||
obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o
|
||||
obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_SHT15) += sht15.o
|
||||
obj-$(CONFIG_SENSORS_SHT21) += sht21.o
|
||||
obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
// 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
|
|||
Enable support for the Renesas RZ/A1 Interrupt Controller, to use up
|
||||
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
|
||||
bool
|
||||
select REGMAP
|
||||
|
|
|
@ -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_MSI) += irq-loongson-pch-msi.o
|
||||
obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o
|
||||
obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
// 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
|
|||
|
||||
config MFD_ASIC3
|
||||
bool "Compaq ASIC3"
|
||||
depends on GPIOLIB && ARM
|
||||
depends on GPIOLIB
|
||||
depends on ARM || COMPILE_TEST
|
||||
select MFD_CORE
|
||||
help
|
||||
This driver supports the ASIC3 multifunction chip found on many
|
||||
|
@ -398,6 +399,17 @@ config MFD_DLN2
|
|||
etc. must be enabled in order to use the functionality of
|
||||
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
|
||||
tristate "Samsung Exynos SoC Low Power Audio Subsystem"
|
||||
depends on ARCH_EXYNOS || COMPILE_TEST
|
||||
|
@ -493,7 +505,7 @@ config MFD_HI6421_PMIC
|
|||
Add support for HiSilicon Hi6421 PMIC. Hi6421 includes multi-
|
||||
functions, such as regulators, RTC, codec, Coulomb counter, etc.
|
||||
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.
|
||||
We communicate with the Hi6421 via memory-mapped I/O.
|
||||
|
||||
|
@ -1162,6 +1174,29 @@ config MFD_SI476X_CORE
|
|||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
tristate "Silicon Motion SM501"
|
||||
depends on HAS_DMA
|
||||
|
@ -2118,5 +2153,18 @@ config SGI_MFD_IOC3
|
|||
If you have an SGI Origin, Octane, or a PCI IOC3 card,
|
||||
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
|
||||
endif
|
||||
|
|
|
@ -14,6 +14,7 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
|
|||
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
|
||||
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.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_GATEWORKS_GSC) += gateworks-gsc.o
|
||||
|
||||
|
@ -264,3 +265,5 @@ obj-$(CONFIG_MFD_STMFX) += stmfx.o
|
|||
obj-$(CONFIG_MFD_KHADAS_MCU) += khadas-mcu.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 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/dm355evm_msp.h>
|
||||
|
@ -116,6 +117,54 @@ static const u8 msp_gpios[] = {
|
|||
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_MASK(offset) BIT(msp_gpios[(offset)] & 0x07)
|
||||
|
||||
|
@ -260,32 +309,7 @@ static int add_children(struct i2c_client *client)
|
|||
|
||||
/* LED output */
|
||||
if (msp_has_leds()) {
|
||||
#define GPIO_LED(l) .name = l, .active_low = true
|
||||
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;
|
||||
|
||||
gpiod_add_lookup_table(&evm_leds_gpio_table);
|
||||
/* NOTE: these are the only fully programmable LEDs
|
||||
* on the board, since GPIO-61/ds22 (and many signals
|
||||
* going to DC7) must be used for AEMIF address lines
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
// 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[] = {
|
|||
{ PCI_VDEVICE(INTEL, 0x5ac4), (kernel_ulong_t)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x5ac6), (kernel_ulong_t)&bxt_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 */
|
||||
{ PCI_VDEVICE(INTEL, 0x9d27), (kernel_ulong_t)&spt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info },
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
// 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 @@
|
|||
#include <linux/dmi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define MAX_ID_LEN 4
|
||||
static char force_device_id[MAX_ID_LEN + 1] = "";
|
||||
|
@ -124,6 +125,7 @@ static const struct kempld_platform_data kempld_platform_data_generic = {
|
|||
};
|
||||
|
||||
static struct platform_device *kempld_pdev;
|
||||
static bool kempld_acpi_mode;
|
||||
|
||||
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)
|
|||
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)
|
||||
{
|
||||
const struct kempld_platform_data *pdata =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
const struct kempld_platform_data *pdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct kempld_device_data *pld;
|
||||
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);
|
||||
if (!pld)
|
||||
|
@ -471,9 +553,19 @@ static int kempld_remove(struct platform_device *pdev)
|
|||
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 = {
|
||||
.driver = {
|
||||
.name = "kempld",
|
||||
.acpi_match_table = ACPI_PTR(kempld_acpi_table),
|
||||
.probe_type = PROBE_FORCE_SYNCHRONOUS,
|
||||
},
|
||||
.probe = kempld_probe,
|
||||
.remove = kempld_remove,
|
||||
|
@ -792,6 +884,7 @@ MODULE_DEVICE_TABLE(dmi, kempld_dmi_table);
|
|||
static int __init kempld_init(void)
|
||||
{
|
||||
const struct dmi_system_id *id;
|
||||
int ret;
|
||||
|
||||
if (force_device_id[0]) {
|
||||
for (id = kempld_dmi_table;
|
||||
|
@ -801,12 +894,24 @@ static int __init kempld_init(void)
|
|||
break;
|
||||
if (id->matches[0].slot == DMI_NONE)
|
||||
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)
|
||||
|
|
|
@ -122,11 +122,13 @@ static int khadas_mcu_probe(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id khadas_mcu_of_match[] = {
|
||||
{ .compatible = "khadas,mcu", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, khadas_mcu_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver khadas_mcu_driver = {
|
||||
.driver = {
|
||||
|
|
|
@ -26,6 +26,10 @@ static const struct mfd_cell lp87565_cells[] = {
|
|||
|
||||
static const struct of_device_id of_lp87565_match_table[] = {
|
||||
{ .compatible = "ti,lp87565", },
|
||||
{
|
||||
.compatible = "ti,lp87524-q1",
|
||||
.data = (void *)LP87565_DEVICE_TYPE_LP87524_Q1,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,lp87565-q1",
|
||||
.data = (void *)LP87565_DEVICE_TYPE_LP87565_Q1,
|
||||
|
|
|
@ -369,19 +369,14 @@ EXPORT_SYMBOL_GPL(madera_of_match);
|
|||
static int madera_get_reset_gpio(struct madera *madera)
|
||||
{
|
||||
struct gpio_desc *reset;
|
||||
int ret;
|
||||
|
||||
if (madera->pdata.reset)
|
||||
return 0;
|
||||
|
||||
reset = devm_gpiod_get_optional(madera->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(reset)) {
|
||||
ret = PTR_ERR(reset);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(madera->dev, "Failed to request /RESET: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(reset))
|
||||
return dev_err_probe(madera->dev, PTR_ERR(reset),
|
||||
"Failed to request /RESET");
|
||||
|
||||
/*
|
||||
* A hard reset is needed for full reset of the chip. We allow running
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <linux/mfd/mt6360.h>
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ static const struct mfd_cell rn5t618_cells[] = {
|
|||
|
||||
static const struct mfd_cell rc5t619_cells[] = {
|
||||
{ .name = "rn5t618-adc" },
|
||||
{ .name = "rn5t618-power" },
|
||||
{ .name = "rn5t618-regulator" },
|
||||
{ .name = "rc5t619-rtc" },
|
||||
{ .name = "rn5t618-wdt" },
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// 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)
|
|||
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:
|
||||
release_mem_region(sm->io_res->start, 0x100);
|
||||
err_res:
|
||||
|
|
|
@ -189,7 +189,7 @@ static int sprd_pmic_probe(struct spi_device *spi)
|
|||
ddata->irqs[i].mask = BIT(i);
|
||||
|
||||
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);
|
||||
if (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)
|
|||
return ret;
|
||||
}
|
||||
|
||||
device_init_wakeup(&spi->dev, true);
|
||||
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[] = {
|
||||
{ .compatible = "sprd,sc2731", .data = &sc2731_data },
|
||||
{},
|
||||
|
@ -215,6 +240,7 @@ static struct spi_driver sprd_pmic_driver = {
|
|||
.driver = {
|
||||
.name = "sc27xx-pmic",
|
||||
.of_match_table = sprd_pmic_match,
|
||||
.pm = &sprd_pmic_pm_ops,
|
||||
},
|
||||
.probe = sprd_pmic_probe,
|
||||
};
|
||||
|
|
|
@ -331,11 +331,9 @@ static int stmfx_chip_init(struct i2c_client *client)
|
|||
ret = PTR_ERR_OR_ZERO(stmfx->vdd);
|
||||
if (ret == -ENODEV) {
|
||||
stmfx->vdd = NULL;
|
||||
} else if (ret == -EPROBE_DEFER) {
|
||||
return ret;
|
||||
} else if (ret) {
|
||||
dev_err(&client->dev, "Failed to get VDD regulator: %d\n", ret);
|
||||
return ret;
|
||||
} else {
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"Failed to get VDD regulator\n");
|
||||
}
|
||||
|
||||
if (stmfx->vdd) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
regmap = regmap_init_mmio(NULL, base, &syscon_config);
|
||||
kfree(syscon_config.name);
|
||||
if (IS_ERR(regmap)) {
|
||||
pr_err("regmap init failed\n");
|
||||
ret = PTR_ERR(regmap);
|
||||
|
@ -145,6 +144,7 @@ err_clk:
|
|||
regmap_exit(regmap);
|
||||
err_regmap:
|
||||
iounmap(base);
|
||||
kfree(syscon_config.name);
|
||||
err_map:
|
||||
kfree(syscon);
|
||||
return ERR_PTR(ret);
|
||||
|
|
|
@ -219,12 +219,9 @@ static int wcd934x_slim_probe(struct slim_device *sdev)
|
|||
return -ENOMEM;
|
||||
|
||||
ddata->irq = of_irq_get(np, 0);
|
||||
if (ddata->irq < 0) {
|
||||
if (ddata->irq != -EPROBE_DEFER)
|
||||
dev_err(ddata->dev, "Failed to get IRQ: err = %d\n",
|
||||
ddata->irq);
|
||||
return ddata->irq;
|
||||
}
|
||||
if (ddata->irq < 0)
|
||||
return dev_err_probe(ddata->dev, ddata->irq,
|
||||
"Failed to get IRQ\n");
|
||||
|
||||
reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
|
||||
if (reset_gpio < 0) {
|
||||
|
|
|
@ -428,6 +428,16 @@ config PWM_SIFIVE
|
|||
To compile this driver as a module, choose M here: the module
|
||||
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
|
||||
tristate "STMicroelectronics SPEAr PWM support"
|
||||
depends on PLAT_SPEAR || COMPILE_TEST
|
||||
|
|
|
@ -40,6 +40,7 @@ obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
|
|||
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
|
||||
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.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_SPRD) += pwm-sprd.o
|
||||
obj-$(CONFIG_PWM_STI) += pwm-sti.o
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
// 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, ®);
|
||||
|
||||
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, ®);
|
||||
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
|
|||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
|
||||
# ARM Architecture
|
||||
|
|
|
@ -225,3 +225,4 @@ obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
|
|||
obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o
|
||||
obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
|
||||
obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
|
||||
obj-$(CONFIG_SL28CPLD_WATCHDOG) += sl28cpld_wdt.o
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
// 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");
|
|
@ -0,0 +1,65 @@
|
|||
/* 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 @@
|
|||
|
||||
enum lp87565_device_type {
|
||||
LP87565_DEVICE_TYPE_UNKNOWN = 0,
|
||||
LP87565_DEVICE_TYPE_LP87524_Q1,
|
||||
LP87565_DEVICE_TYPE_LP87561_Q1,
|
||||
LP87565_DEVICE_TYPE_LP87565_Q1,
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче