mirror of https://gitee.com/openkylin/linux.git
- New Drivers
- Add support for IQS620A/621/622/624/625 Azoteq IQS62X Sensors - New Device Support - Add support for ADC, IRQ, Regulator, RTC and WDT to Ricoh RN5T618 PMIC - Add support for Comet Lake to Intel LPSS - New Functionality - Add support for Charger Detection to Spreadtrum SC27xx PMICs - Add support for Interrupt Polarity to Dialog Semi DA9062/61 PMIC - Add ACPI enumeration support to Diolan DLN2 USB Adaptor - Fix-ups - Device Tree; iqs62x, rn5t618, cros_ec_dev, stm32-lptimer, rohm,bd71837, rohm,bd71847 - I2C registration; rn5t618 - Kconfig; MFD_CPCAP, AB8500_CORE, MFD_WM8994, MFD_WM97xx, MFD_STPMIC1 - Use flexible-array members; omap-usb-tll, qcom-pm8xxx - Remove unnecessary casts; omap-usb-host, omap-usb-tll - Power (suspend/resume/poweroff) enhancements; rk808 - Improve error/sanity checking; dln2 - Use snprintf(); aat2870-core - Bug Fixes - Fix PCI IDs; intel-lpss-pci -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAl6MSwsACgkQUa+KL4f8 d2GklhAAk1e0tYu1w4yS0IE40mReDk5ycOE1O8Q75qWw5Af2dK48qvSHGDwaU4ES WNYZ3Mr/OUrvd+/XU8EXI6NXAi9W75ye2J2KmmDDL6yXnDuHbzBZF8ifEtXQKSOp rElzCJVnXehpjSfwa4cp5f1Msox/rFg1Kpmaas48j8U2JYCmn51GsQCCG6qMIx2i 0ZvXNZuz+eTY3V7l6pRLfTYqwR8E5Y11ryWJaeliyDcVpUlZaykiXuxD7pbqLjJo 8v9ISm67rzf6X5zmi6YhdvpIAzpomdRaaxHfVDkeCKqDgpuWGiq0xXA86vDKwjah qVx6rK/wg4YM5uSEomxIgVj9uvvLSytDqvRlPe7vFKgkR0Xhl5SdkiwadI4P9zkr zrRGNRAUHsZ4ZrlObD0QJWOQYnIg5oCmRouRKcHtQVZYP3rSdRz/1oC6FN8LVqUw 3NfLB/S6uBCJ3uahckrgnjQughnvmsAzlflcNV2t+P8di8Cwyh2A5N9qkT/oI67o Xf7NODSdu0JzzTgsM+EJ2JphSLMYaekWs06qAXyibPrrS2SoThE79igCmNOkyyR7 obJfN2SM5k4LkhMGlYbPfbRT2Hoa+9OJozBe3WgiW3kwTdFVB9aen2U230tHplbB g/NuVS+PrReR5lfzT+O8dodH7lj5/GhmGsapdguFVhXYIE+2CMg= =hJ2o -----END PGP SIGNATURE----- Merge tag 'mfd-next-5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd Pull mfd updates from Lee Jones: "New Drivers: - Add support for IQS620A/621/622/624/625 Azoteq IQS62X Sensors New Device Support: - Add support for ADC, IRQ, Regulator, RTC and WDT to Ricoh RN5T618 PMIC - Add support for Comet Lake to Intel LPSS New Functionality: - Add support for Charger Detection to Spreadtrum SC27xx PMICs - Add support for Interrupt Polarity to Dialog Semi DA9062/61 PMIC - Add ACPI enumeration support to Diolan DLN2 USB Adaptor Fix-ups: - Device Tree; iqs62x, rn5t618, cros_ec_dev, stm32-lptimer, rohm,bd71837, rohm,bd71847 - I2C registration; rn5t618 - Kconfig; MFD_CPCAP, AB8500_CORE, MFD_WM8994, MFD_WM97xx, MFD_STPMIC1 - Use flexible-array members; omap-usb-tll, qcom-pm8xxx - Remove unnecessary casts; omap-usb-host, omap-usb-tll - Power (suspend/resume/poweroff) enhancements; rk808 - Improve error/sanity checking; dln2 - Use snprintf(); aat2870-core Bug Fixes: - Fix PCI IDs in intel-lpss-pci" * tag 'mfd-next-5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (33 commits) mfd: intel-lpss: Fix Intel Elkhart Lake LPSS I2C input clock mfd: aat2870: Use scnprintf() for avoiding potential buffer overflow mfd: dln2: Allow to be enumerated via ACPI mfd: da9062: Add support for interrupt polarity defined in device tree dt-bindings: bd718x7: Yamlify and add BD71850 mfd: dln2: Fix sanity checking for endpoints mfd: intel-lpss: Add Intel Comet Lake PCH-V PCI IDs mfd: sc27xx: Add USB charger type detection support dt-bindings: mfd: Document STM32 low power timer bindings mfd: rk808: Convert RK805 to shutdown/suspend hooks mfd: rk808: Reduce shutdown duplication mfd: rk808: Stop using syscore ops mfd: rk808: Ensure suspend/resume hooks always work mfd: rk808: Always use poweroff when requested mfd: omap: Remove useless cast for driver.name mfd: Kconfig: Fix some misspelling of the word functionality mfd: pm8xxx: Replace zero-length array with flexible-array member mfd: omap-usb-tll: Replace zero-length array with flexible-array member mfd: cpcap: Fix compile if MFD_CORE is not selected mfd: cros_ec: Check DT node for usbpd-notify add ...
This commit is contained in:
commit
8645f09bad
|
@ -0,0 +1,132 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/iqs62x-keys.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Azoteq IQS620A/621/622/624/625 Keys and Switches
|
||||
|
||||
maintainers:
|
||||
- Jeff LaBundy <jeff@labundy.com>
|
||||
|
||||
description: |
|
||||
The Azoteq IQS620A, IQS621, IQS622, IQS624 and IQS625 multi-function sensors
|
||||
feature a variety of self-capacitive, mutual-inductive and Hall-effect sens-
|
||||
ing capabilities that can facilitate a variety of contactless key and switch
|
||||
applications.
|
||||
|
||||
These functions are collectively represented by a "keys" child node from the
|
||||
parent MFD driver. See Documentation/devicetree/bindings/mfd/iqs62x.yaml for
|
||||
further details and examples. Sensor hardware configuration (self-capacitive
|
||||
vs. mutual-inductive, etc.) is selected based on the device's firmware.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- azoteq,iqs620a-keys
|
||||
- azoteq,iqs621-keys
|
||||
- azoteq,iqs622-keys
|
||||
- azoteq,iqs624-keys
|
||||
- azoteq,iqs625-keys
|
||||
|
||||
linux,keycodes:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- minItems: 1
|
||||
maxItems: 16
|
||||
description: |
|
||||
Specifies the numeric keycodes associated with each available touch or
|
||||
proximity event according to the following table. An 'x' indicates the
|
||||
event is supported for a given device. Specify 0 for unused events.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
| # | Event | IQS620A | IQS621 | IQS622 | IQS624 | IQS625 |
|
||||
-------------------------------------------------------------------------
|
||||
| 0 | CH0 Touch | x | x | x | x | x |
|
||||
| | Antenna 1 Touch* | x | | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 1 | CH0 Proximity | x | x | x | x | x |
|
||||
| | Antenna 1 Prox.* | x | | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 2 | CH1 Touch | x | x | x | x | x |
|
||||
| | Ant. 1 Deep Touch* | x | | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 3 | CH1 Proximity | x | x | x | x | x |
|
||||
-------------------------------------------------------------------------
|
||||
| 4 | CH2 Touch | x | | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 5 | CH2 Proximity | x | | | | |
|
||||
| | Antenna 2 Prox.* | x | | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 6 | Metal (+) Touch** | x | x | | | |
|
||||
| | Ant. 2 Deep Touch* | x | | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 7 | Metal (+) Prox.** | x | x | | | |
|
||||
| | Antenna 2 Touch* | x | | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 8 | Metal (-) Touch** | x | x | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 9 | Metal (-) Prox.** | x | x | | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 10 | SAR Active*** | x | | x | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 11 | SAR Quick Rel.*** | x | | x | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 12 | SAR Movement*** | x | | x | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 13 | SAR Filter Halt*** | x | | x | | |
|
||||
-------------------------------------------------------------------------
|
||||
| 14 | Wheel Up | | | | x | |
|
||||
-------------------------------------------------------------------------
|
||||
| 15 | Wheel Down | | | | x | |
|
||||
-------------------------------------------------------------------------
|
||||
* Two-channel SAR. Replaces CH0-2 plus metal touch and proximity events
|
||||
if enabled via firmware.
|
||||
** "+" and "-" refer to the polarity of a channel's delta (LTA - counts),
|
||||
where "LTA" is defined as the channel's long-term average.
|
||||
*** One-channel SAR. Replaces CH0-2 touch and proximity events if enabled
|
||||
via firmware.
|
||||
|
||||
patternProperties:
|
||||
"^hall-switch-(north|south)$":
|
||||
type: object
|
||||
description:
|
||||
Represents north/south-field Hall-effect sensor touch or proximity
|
||||
events. Note that north/south-field orientation is reversed on the
|
||||
IQS620AXzCSR device due to its flip-chip package.
|
||||
|
||||
properties:
|
||||
linux,code:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Numeric switch code associated with the event.
|
||||
|
||||
azoteq,use-prox:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
If present, specifies that Hall-effect sensor reporting should
|
||||
use the device's wide-range proximity threshold instead of its
|
||||
close-range touch threshold (default).
|
||||
|
||||
required:
|
||||
- linux,code
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- azoteq,iqs624-keys
|
||||
- azoteq,iqs625-keys
|
||||
then:
|
||||
patternProperties:
|
||||
"^hall-switch-(north|south)$": false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- linux,keycodes
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
...
|
|
@ -0,0 +1,179 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/iqs62x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Azoteq IQS620A/621/622/624/625 Multi-Function Sensors
|
||||
|
||||
maintainers:
|
||||
- Jeff LaBundy <jeff@labundy.com>
|
||||
|
||||
description: |
|
||||
The Azoteq IQS620A, IQS621, IQS622, IQS624 and IQS625 multi-function sensors
|
||||
integrate multiple sensing technologies in a single package.
|
||||
|
||||
Link to datasheets: https://www.azoteq.com/
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- azoteq,iqs620a
|
||||
- azoteq,iqs621
|
||||
- azoteq,iqs622
|
||||
- azoteq,iqs624
|
||||
- azoteq,iqs625
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
firmware-name:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description:
|
||||
Specifies the name of the calibration and configuration file selected by
|
||||
the driver. If this property is omitted, the name is chosen based on the
|
||||
device name with ".bin" as the extension (e.g. iqs620a.bin for IQS620A).
|
||||
|
||||
keys:
|
||||
$ref: ../input/iqs62x-keys.yaml
|
||||
|
||||
pwm:
|
||||
$ref: ../pwm/iqs620a-pwm.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
/*
|
||||
* Dual capacitive buttons with proximity-activated function, unipolar lid
|
||||
* switch and panel-mounted LED.
|
||||
*/
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
iqs620a@44 {
|
||||
compatible = "azoteq,iqs620a";
|
||||
reg = <0x44>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
keys {
|
||||
compatible = "azoteq,iqs620a-keys";
|
||||
|
||||
linux,keycodes = <KEY_SELECT>,
|
||||
<KEY_MENU>,
|
||||
<KEY_OK>,
|
||||
<KEY_MENU>;
|
||||
|
||||
hall-switch-south {
|
||||
linux,code = <SW_LID>;
|
||||
azoteq,use-prox;
|
||||
};
|
||||
};
|
||||
|
||||
iqs620a_pwm: pwm {
|
||||
compatible = "azoteq,iqs620a-pwm";
|
||||
#pwm-cells = <2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pwmleds {
|
||||
compatible = "pwm-leds";
|
||||
|
||||
panel {
|
||||
pwms = <&iqs620a_pwm 0 1000000>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
/* Single inductive button with bipolar dock/tablet-mode switch. */
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
iqs620a@44 {
|
||||
compatible = "azoteq,iqs620a";
|
||||
reg = <0x44>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
firmware-name = "iqs620a_coil.bin";
|
||||
|
||||
keys {
|
||||
compatible = "azoteq,iqs620a-keys";
|
||||
|
||||
linux,keycodes = <0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<KEY_MUTE>;
|
||||
|
||||
hall-switch-north {
|
||||
linux,code = <SW_DOCK>;
|
||||
};
|
||||
|
||||
hall-switch-south {
|
||||
linux,code = <SW_TABLET_MODE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
/* Dual capacitive buttons with volume knob. */
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
iqs624@44 {
|
||||
compatible = "azoteq,iqs624";
|
||||
reg = <0x44>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
keys {
|
||||
compatible = "azoteq,iqs624-keys";
|
||||
|
||||
linux,keycodes = <BTN_0>,
|
||||
<0>,
|
||||
<BTN_1>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<KEY_VOLUMEUP>,
|
||||
<KEY_VOLUMEDOWN>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -15,6 +15,8 @@ Required properties:
|
|||
- reg: the I2C slave address of the device
|
||||
|
||||
Optional properties:
|
||||
- interrupts: interrupt mapping for IRQ
|
||||
See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
- system-power-controller:
|
||||
See Documentation/devicetree/bindings/power/power-controller.txt
|
||||
|
||||
|
@ -32,6 +34,8 @@ Example:
|
|||
pmic@32 {
|
||||
compatible = "ricoh,rn5t618";
|
||||
reg = <0x32>;
|
||||
interrupt-parent = <&gpio5>;
|
||||
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
|
||||
system-power-controller;
|
||||
|
||||
regulators {
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
* ROHM BD71837 and BD71847 Power Management Integrated Circuit bindings
|
||||
|
||||
BD71837MWV and BD71847MWV are programmable Power Management ICs for powering
|
||||
single-core, dual-core, and quad-core SoCs such as NXP-i.MX 8M. They are
|
||||
optimized for low BOM cost and compact solution footprint. BD71837MWV
|
||||
integrates 8 Buck regulators and 7 LDOs. BD71847MWV contains 6 Buck regulators
|
||||
and 6 LDOs.
|
||||
|
||||
Datasheet for BD71837 is available at:
|
||||
https://www.rohm.com/datasheet/BD71837MWV/bd71837mwv-e
|
||||
Datasheet for BD71847 is available at:
|
||||
https://www.rohm.com/datasheet/BD71847AMWV/bd71847amwv-e
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "rohm,bd71837" for bd71837
|
||||
"rohm,bd71847" for bd71847.
|
||||
- reg : I2C slave address.
|
||||
- interrupt-parent : Phandle to the parent interrupt controller.
|
||||
- interrupts : The interrupt line the device is connected to.
|
||||
- clocks : The parent clock connected to PMIC. If this is missing
|
||||
32768 KHz clock is assumed.
|
||||
- #clock-cells : Should be 0.
|
||||
- regulators: : List of child nodes that specify the regulators.
|
||||
Please see ../regulator/rohm,bd71837-regulator.txt
|
||||
|
||||
Optional properties:
|
||||
- clock-output-names : Should contain name for output clock.
|
||||
- rohm,reset-snvs-powered : Transfer BD718x7 to SNVS state at reset.
|
||||
|
||||
The BD718x7 supports two different HW states as reset target states. States
|
||||
are called as SNVS and READY. At READY state all the PMIC power outputs go
|
||||
down and OTP is reload. At the SNVS state all other logic and external
|
||||
devices apart from the SNVS power domain are shut off. Please refer to NXP
|
||||
i.MX8 documentation for further information regarding SNVS state. When a
|
||||
reset is done via SNVS state the PMIC OTP data is not reload. This causes
|
||||
power outputs that have been under SW control to stay down when reset has
|
||||
switched power state to SNVS. If reset is done via READY state the power
|
||||
outputs will be returned to HW control by OTP loading. Thus the reset
|
||||
target state is set to READY by default. If SNVS state is used the boot
|
||||
crucial regulators must have the regulator-always-on and regulator-boot-on
|
||||
properties set in regulator node.
|
||||
|
||||
- rohm,short-press-ms : Short press duration in milliseconds
|
||||
- rohm,long-press-ms : Long press duration in milliseconds
|
||||
|
||||
Configure the "short press" and "long press" timers for the power button.
|
||||
Values are rounded to what hardware supports (500ms multiple for short and
|
||||
1000ms multiple for long). If these properties are not present the existing
|
||||
configuration (from bootloader or OTP) is not touched.
|
||||
|
||||
Example:
|
||||
|
||||
/* external oscillator node */
|
||||
osc: oscillator {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <1>;
|
||||
clock-frequency = <32768>;
|
||||
clock-output-names = "osc";
|
||||
};
|
||||
|
||||
pmic: pmic@4b {
|
||||
compatible = "rohm,bd71837";
|
||||
reg = <0x4b>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <29 GPIO_ACTIVE_LOW>;
|
||||
interrupt-names = "irq";
|
||||
#clock-cells = <0>;
|
||||
clocks = <&osc 0>;
|
||||
clock-output-names = "bd71837-32k-out";
|
||||
rohm,reset-snvs-powered;
|
||||
|
||||
regulators {
|
||||
buck1: BUCK1 {
|
||||
regulator-name = "buck1";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
};
|
||||
// [...]
|
||||
};
|
||||
};
|
||||
|
||||
/* Clock consumer node */
|
||||
rtc@0 {
|
||||
compatible = "company,my-rtc";
|
||||
clock-names = "my-clock";
|
||||
clocks = <&pmic>;
|
||||
};
|
|
@ -0,0 +1,236 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/rohm,bd71837-pmic.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BD71837 Power Management Integrated Circuit bindings
|
||||
|
||||
maintainers:
|
||||
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
|
||||
|
||||
description: |
|
||||
BD71837MWV is programmable Power Management ICs for powering single-core,
|
||||
dual-core, and quad-core SoCs such as NXP-i.MX 8M. It is optimized for low
|
||||
BOM cost and compact solution footprint. BD71837MWV integrates 8 Buck
|
||||
regulators and 7 LDOs.
|
||||
Datasheet for BD71837 is available at
|
||||
https://www.rohm.com/products/power-management/power-management-ic-for-system/industrial-consumer-applications/nxp-imx/bd71837amwv-product
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: rohm,bd71837
|
||||
|
||||
reg:
|
||||
description:
|
||||
I2C slave address.
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
# The BD718x7 supports two different HW states as reset target states. States
|
||||
# are called as SNVS and READY. At READY state all the PMIC power outputs go
|
||||
# down and OTP is reload. At the SNVS state all other logic and external
|
||||
# devices apart from the SNVS power domain are shut off. Please refer to NXP
|
||||
# i.MX8 documentation for further information regarding SNVS state. When a
|
||||
# reset is done via SNVS state the PMIC OTP data is not reload. This causes
|
||||
# power outputs that have been under SW control to stay down when reset has
|
||||
# switched power state to SNVS. If reset is done via READY state the power
|
||||
# outputs will be returned to HW control by OTP loading. Thus the reset
|
||||
# target state is set to READY by default. If SNVS state is used the boot
|
||||
# crucial regulators must have the regulator-always-on and regulator-boot-on
|
||||
# properties set in regulator node.
|
||||
|
||||
rohm,reset-snvs-powered:
|
||||
description: |
|
||||
Transfer PMIC to SNVS state at reset
|
||||
type: boolean
|
||||
|
||||
# Configure the "short press" and "long press" timers for the power button.
|
||||
# Values are rounded to what hardware supports
|
||||
# Short-press:
|
||||
# Shortest being 10ms, next 500ms and then multiple of 500ms up to 7,5s
|
||||
# Long-press:
|
||||
# Shortest being 10ms, next 1000ms and then multiple of 1000ms up to 15s
|
||||
# If these properties are not present the existing configuration (from
|
||||
# bootloader or OTP) is not touched.
|
||||
|
||||
rohm,short-press-ms:
|
||||
description:
|
||||
Short press duration in milliseconds
|
||||
enum:
|
||||
- 10
|
||||
- 500
|
||||
- 1000
|
||||
- 1500
|
||||
- 2000
|
||||
- 2500
|
||||
- 3000
|
||||
- 3500
|
||||
- 4000
|
||||
- 4500
|
||||
- 5000
|
||||
- 5500
|
||||
- 6000
|
||||
- 6500
|
||||
- 7000
|
||||
|
||||
rohm,long-press-ms:
|
||||
description:
|
||||
Long press duration in milliseconds
|
||||
enum:
|
||||
- 10
|
||||
- 1000
|
||||
- 2000
|
||||
- 3000
|
||||
- 4000
|
||||
- 5000
|
||||
- 6000
|
||||
- 7000
|
||||
- 8000
|
||||
- 9000
|
||||
- 10000
|
||||
- 11000
|
||||
- 12000
|
||||
- 13000
|
||||
- 14000
|
||||
|
||||
regulators:
|
||||
$ref: ../regulator/rohm,bd71837-regulator.yaml
|
||||
description:
|
||||
List of child nodes that specify the regulators.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- "#clock-cells"
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c {
|
||||
pmic: pmic@4b {
|
||||
compatible = "rohm,bd71837";
|
||||
reg = <0x4b>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
|
||||
#clock-cells = <0>;
|
||||
clocks = <&osc 0>;
|
||||
rohm,reset-snvs-powered;
|
||||
rohm,short-press-ms = <10>;
|
||||
rohm,long-press-ms = <2000>;
|
||||
|
||||
regulators {
|
||||
buck1: BUCK1 {
|
||||
regulator-name = "buck1";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
rohm,dvs-run-voltage = <900000>;
|
||||
rohm,dvs-idle-voltage = <850000>;
|
||||
rohm,dvs-suspend-voltage = <800000>;
|
||||
};
|
||||
buck2: BUCK2 {
|
||||
regulator-name = "buck2";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
rohm,dvs-run-voltage = <1000000>;
|
||||
rohm,dvs-idle-voltage = <900000>;
|
||||
};
|
||||
buck3: BUCK3 {
|
||||
regulator-name = "buck3";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
rohm,dvs-run-voltage = <1000000>;
|
||||
};
|
||||
buck4: BUCK4 {
|
||||
regulator-name = "buck4";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
rohm,dvs-run-voltage = <1000000>;
|
||||
};
|
||||
buck5: BUCK5 {
|
||||
regulator-name = "buck5";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1350000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck6: BUCK6 {
|
||||
regulator-name = "buck6";
|
||||
regulator-min-microvolt = <3000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck7: BUCK7 {
|
||||
regulator-name = "buck7";
|
||||
regulator-min-microvolt = <1605000>;
|
||||
regulator-max-microvolt = <1995000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck8: BUCK8 {
|
||||
regulator-name = "buck8";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
};
|
||||
|
||||
ldo1: LDO1 {
|
||||
regulator-name = "ldo1";
|
||||
regulator-min-microvolt = <3000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo2: LDO2 {
|
||||
regulator-name = "ldo2";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <900000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo3: LDO3 {
|
||||
regulator-name = "ldo3";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
ldo4: LDO4 {
|
||||
regulator-name = "ldo4";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
};
|
||||
ldo5: LDO5 {
|
||||
regulator-name = "ldo5";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
ldo6: LDO6 {
|
||||
regulator-name = "ldo6";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
};
|
||||
ldo7_reg: LDO7 {
|
||||
regulator-name = "ldo7";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,222 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mfd/rohm,bd71847-pmic.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BD71847 and BD71850 Power Management Integrated Circuit bindings
|
||||
|
||||
maintainers:
|
||||
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
|
||||
|
||||
description: |
|
||||
BD71847AMWV and BD71850MWV are programmable Power Management ICs for powering
|
||||
single-core, dual-core, and quad-core SoCs such as NXP-i.MX 8M. It is
|
||||
optimized for low BOM cost and compact solution footprint. BD71847MWV and
|
||||
BD71850MWV integrate 6 Buck regulators and 6 LDOs.
|
||||
Datasheets are available at
|
||||
https://www.rohm.com/products/power-management/power-management-ic-for-system/industrial-consumer-applications/nxp-imx/bd71847amwv-product
|
||||
https://www.rohm.com/products/power-management/power-management-ic-for-system/industrial-consumer-applications/nxp-imx/bd71850mwv-product
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- rohm,bd71847
|
||||
- rohm,bd71850
|
||||
|
||||
reg:
|
||||
description:
|
||||
I2C slave address.
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
# The BD71847 abd BD71850 support two different HW states as reset target
|
||||
# states. States are called as SNVS and READY. At READY state all the PMIC
|
||||
# power outputs go down and OTP is reload. At the SNVS state all other logic
|
||||
# and external devices apart from the SNVS power domain are shut off. Please
|
||||
# refer to NXP i.MX8 documentation for further information regarding SNVS
|
||||
# state. When a reset is done via SNVS state the PMIC OTP data is not reload.
|
||||
# This causes power outputs that have been under SW control to stay down when
|
||||
# reset has switched power state to SNVS. If reset is done via READY state the
|
||||
# power outputs will be returned to HW control by OTP loading. Thus the reset
|
||||
# target state is set to READY by default. If SNVS state is used the boot
|
||||
# crucial regulators must have the regulator-always-on and regulator-boot-on
|
||||
# properties set in regulator node.
|
||||
|
||||
rohm,reset-snvs-powered:
|
||||
description:
|
||||
Transfer PMIC to SNVS state at reset.
|
||||
type: boolean
|
||||
|
||||
# Configure the "short press" and "long press" timers for the power button.
|
||||
# Values are rounded to what hardware supports
|
||||
# Short-press:
|
||||
# Shortest being 10ms, next 500ms and then multiple of 500ms up to 7,5s
|
||||
# Long-press:
|
||||
# Shortest being 10ms, next 1000ms and then multiple of 1000ms up to 15s
|
||||
# If these properties are not present the existing # configuration (from
|
||||
# bootloader or OTP) is not touched.
|
||||
|
||||
rohm,short-press-ms:
|
||||
description:
|
||||
Short press duration in milliseconds
|
||||
enum:
|
||||
- 10
|
||||
- 500
|
||||
- 1000
|
||||
- 1500
|
||||
- 2000
|
||||
- 2500
|
||||
- 3000
|
||||
- 3500
|
||||
- 4000
|
||||
- 4500
|
||||
- 5000
|
||||
- 5500
|
||||
- 6000
|
||||
- 6500
|
||||
- 7000
|
||||
- 7500
|
||||
|
||||
rohm,long-press-ms:
|
||||
description:
|
||||
Long press duration in milliseconds
|
||||
enum:
|
||||
- 10
|
||||
- 1000
|
||||
- 2000
|
||||
- 3000
|
||||
- 4000
|
||||
- 5000
|
||||
- 6000
|
||||
- 7000
|
||||
- 8000
|
||||
- 9000
|
||||
- 10000
|
||||
- 11000
|
||||
- 12000
|
||||
- 13000
|
||||
- 14000
|
||||
- 15000
|
||||
|
||||
regulators:
|
||||
$ref: ../regulator/rohm,bd71847-regulator.yaml
|
||||
description:
|
||||
List of child nodes that specify the regulators.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- "#clock-cells"
|
||||
- regulators
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/leds/common.h>
|
||||
|
||||
i2c {
|
||||
pmic: pmic@4b {
|
||||
compatible = "rohm,bd71847";
|
||||
reg = <0x4b>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
|
||||
#clock-cells = <0>;
|
||||
clocks = <&osc 0>;
|
||||
rohm,reset-snvs-powered;
|
||||
rohm,short-press-ms = <10>;
|
||||
rohm,long-press-ms = <2000>;
|
||||
|
||||
regulators {
|
||||
buck1: BUCK1 {
|
||||
regulator-name = "buck1";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
rohm,dvs-run-voltage = <900000>;
|
||||
rohm,dvs-idle-voltage = <850000>;
|
||||
rohm,dvs-suspend-voltage = <800000>;
|
||||
};
|
||||
buck2: BUCK2 {
|
||||
regulator-name = "buck2";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
rohm,dvs-run-voltage = <1000000>;
|
||||
rohm,dvs-idle-voltage = <900000>;
|
||||
};
|
||||
buck3: BUCK3 {
|
||||
regulator-name = "buck3";
|
||||
regulator-min-microvolt = <550000>;
|
||||
regulator-max-microvolt = <1350000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck4: BUCK4 {
|
||||
regulator-name = "buck4";
|
||||
regulator-min-microvolt = <2600000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck5: BUCK5 {
|
||||
regulator-name = "buck5";
|
||||
regulator-min-microvolt = <1605000>;
|
||||
regulator-max-microvolt = <1995000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck8: BUCK6 {
|
||||
regulator-name = "buck6";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
};
|
||||
|
||||
ldo1: LDO1 {
|
||||
regulator-name = "ldo1";
|
||||
regulator-min-microvolt = <1600000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo2: LDO2 {
|
||||
regulator-name = "ldo2";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <900000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo3: LDO3 {
|
||||
regulator-name = "ldo3";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
ldo4: LDO4 {
|
||||
regulator-name = "ldo4";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
};
|
||||
ldo5: LDO5 {
|
||||
regulator-name = "ldo5";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
ldo6: LDO6 {
|
||||
regulator-name = "ldo6";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -39,6 +39,8 @@ properties:
|
|||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
pwm:
|
||||
type: object
|
||||
|
||||
|
@ -81,6 +83,16 @@ patternProperties:
|
|||
required:
|
||||
- compatible
|
||||
|
||||
timer:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: st,stm32-lptimer-timer
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
required:
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
|
@ -115,6 +127,10 @@ examples:
|
|||
counter {
|
||||
compatible = "st,stm32-lptimer-counter";
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "st,stm32-lptimer-timer";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pwm/iqs620a-pwm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Azoteq IQS620A PWM Generator
|
||||
|
||||
maintainers:
|
||||
- Jeff LaBundy <jeff@labundy.com>
|
||||
|
||||
description: |
|
||||
The Azoteq IQS620A multi-function sensor generates a fixed-frequency PWM
|
||||
output represented by a "pwm" child node from the parent MFD driver. See
|
||||
Documentation/devicetree/bindings/mfd/iqs62x.yaml for further details as
|
||||
well as an example.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- azoteq,iqs620a-pwm
|
||||
|
||||
"#pwm-cells":
|
||||
const: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#pwm-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
...
|
|
@ -1,162 +0,0 @@
|
|||
ROHM BD71837 and BD71847 Power Management Integrated Circuit regulator bindings
|
||||
|
||||
Required properties:
|
||||
- regulator-name: should be "buck1", ..., "buck8" and "ldo1", ..., "ldo7" for
|
||||
BD71837. For BD71847 names should be "buck1", ..., "buck6"
|
||||
and "ldo1", ..., "ldo6"
|
||||
|
||||
List of regulators provided by this controller. BD71837 regulators node
|
||||
should be sub node of the BD71837 MFD node. See BD71837 MFD bindings at
|
||||
Documentation/devicetree/bindings/mfd/rohm,bd71837-pmic.txt
|
||||
Regulator nodes should be named to BUCK_<number> and LDO_<number>. The
|
||||
definition for each of these nodes is defined using the standard
|
||||
binding for regulators at
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt.
|
||||
Note that if BD71837 starts at RUN state you probably want to use
|
||||
regulator-boot-on at least for BUCK6 and BUCK7 so that those are not
|
||||
disabled by driver at startup. LDO5 and LDO6 are supplied by those and
|
||||
if they are disabled at startup the voltage monitoring for LDO5/LDO6 will
|
||||
cause PMIC to reset.
|
||||
|
||||
The valid names for BD71837 regulator nodes are:
|
||||
BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, BUCK7, BUCK8
|
||||
LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7
|
||||
|
||||
The valid names for BD71847 regulator nodes are:
|
||||
BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6
|
||||
LDO1, LDO2, LDO3, LDO4, LDO5, LDO6
|
||||
|
||||
Optional properties:
|
||||
- rohm,dvs-run-voltage : PMIC default "RUN" state voltage in uV.
|
||||
See below table for bucks which support this.
|
||||
- rohm,dvs-idle-voltage : PMIC default "IDLE" state voltage in uV.
|
||||
See below table for bucks which support this.
|
||||
- rohm,dvs-suspend-voltage : PMIC default "SUSPEND" state voltage in uV.
|
||||
See below table for bucks which support this.
|
||||
- Any optional property defined in bindings/regulator/regulator.txt
|
||||
|
||||
Supported default DVS states:
|
||||
|
||||
BD71837:
|
||||
buck | dvs-run-voltage | dvs-idle-voltage | dvs-suspend-voltage
|
||||
-----------------------------------------------------------------------------
|
||||
1 | supported | supported | supported
|
||||
----------------------------------------------------------------------------
|
||||
2 | supported | supported | not supported
|
||||
----------------------------------------------------------------------------
|
||||
3 | supported | not supported | not supported
|
||||
----------------------------------------------------------------------------
|
||||
4 | supported | not supported | not supported
|
||||
----------------------------------------------------------------------------
|
||||
rest | not supported | not supported | not supported
|
||||
|
||||
BD71847:
|
||||
buck | dvs-run-voltage | dvs-idle-voltage | dvs-suspend-voltage
|
||||
-----------------------------------------------------------------------------
|
||||
1 | supported | supported | supported
|
||||
----------------------------------------------------------------------------
|
||||
2 | supported | supported | not supported
|
||||
----------------------------------------------------------------------------
|
||||
rest | not supported | not supported | not supported
|
||||
|
||||
Example:
|
||||
regulators {
|
||||
buck1: BUCK1 {
|
||||
regulator-name = "buck1";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
rohm,dvs-run-voltage = <900000>;
|
||||
rohm,dvs-idle-voltage = <850000>;
|
||||
rohm,dvs-suspend-voltage = <800000>;
|
||||
};
|
||||
buck2: BUCK2 {
|
||||
regulator-name = "buck2";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
regulator-ramp-delay = <1250>;
|
||||
rohm,dvs-run-voltage = <1000000>;
|
||||
rohm,dvs-idle-voltage = <900000>;
|
||||
};
|
||||
buck3: BUCK3 {
|
||||
regulator-name = "buck3";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
rohm,dvs-run-voltage = <1000000>;
|
||||
};
|
||||
buck4: BUCK4 {
|
||||
regulator-name = "buck4";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-boot-on;
|
||||
rohm,dvs-run-voltage = <1000000>;
|
||||
};
|
||||
buck5: BUCK5 {
|
||||
regulator-name = "buck5";
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <1350000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck6: BUCK6 {
|
||||
regulator-name = "buck6";
|
||||
regulator-min-microvolt = <3000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck7: BUCK7 {
|
||||
regulator-name = "buck7";
|
||||
regulator-min-microvolt = <1605000>;
|
||||
regulator-max-microvolt = <1995000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
buck8: BUCK8 {
|
||||
regulator-name = "buck8";
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
};
|
||||
|
||||
ldo1: LDO1 {
|
||||
regulator-name = "ldo1";
|
||||
regulator-min-microvolt = <3000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo2: LDO2 {
|
||||
regulator-name = "ldo2";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <900000>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
ldo3: LDO3 {
|
||||
regulator-name = "ldo3";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
ldo4: LDO4 {
|
||||
regulator-name = "ldo4";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
};
|
||||
ldo5: LDO5 {
|
||||
regulator-name = "ldo5";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
ldo6: LDO6 {
|
||||
regulator-name = "ldo6";
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
};
|
||||
ldo7_reg: LDO7 {
|
||||
regulator-name = "ldo7";
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/rohm,bd71837-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BD71837 Power Management Integrated Circuit regulators
|
||||
|
||||
maintainers:
|
||||
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
|
||||
|
||||
description: |
|
||||
List of regulators provided by this controller. BD71837 regulators node
|
||||
should be sub node of the BD71837 MFD node. See BD71837 MFD bindings at
|
||||
Documentation/devicetree/bindings/mfd/rohm,bd71837-pmic.yaml
|
||||
Regulator nodes should be named to BUCK_<number> and LDO_<number>. The
|
||||
definition for each of these nodes is defined using the standard
|
||||
binding for regulators at
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt.
|
||||
Note that if BD71837 starts at RUN state you probably want to use
|
||||
regulator-boot-on at least for BUCK6 and BUCK7 so that those are not
|
||||
disabled by driver at startup. LDO5 and LDO6 are supplied by those and
|
||||
if they are disabled at startup the voltage monitoring for LDO5/LDO6 will
|
||||
cause PMIC to reset.
|
||||
|
||||
#The valid names for BD71837 regulator nodes are:
|
||||
#BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, BUCK7, BUCK8
|
||||
#LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7
|
||||
|
||||
patternProperties:
|
||||
"^LDO[1-7]$":
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: regulator.yaml#
|
||||
description:
|
||||
Properties for single LDO regulator.
|
||||
|
||||
properties:
|
||||
regulator-name:
|
||||
pattern: "^ldo[1-7]$"
|
||||
description:
|
||||
should be "ldo1", ..., "ldo7"
|
||||
|
||||
"^BUCK[1-8]$":
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: regulator.yaml#
|
||||
description:
|
||||
Properties for single BUCK regulator.
|
||||
|
||||
properties:
|
||||
regulator-name:
|
||||
pattern: "^buck[1-8]$"
|
||||
description:
|
||||
should be "buck1", ..., "buck8"
|
||||
|
||||
rohm,dvs-run-voltage:
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0
|
||||
maximum: 1300000
|
||||
description:
|
||||
PMIC default "RUN" state voltage in uV. See below table for
|
||||
bucks which support this. 0 means disabled.
|
||||
|
||||
rohm,dvs-idle-voltage:
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0
|
||||
maximum: 1300000
|
||||
description:
|
||||
PMIC default "IDLE" state voltage in uV. See below table for
|
||||
bucks which support this. 0 means disabled.
|
||||
|
||||
rohm,dvs-suspend-voltage:
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0
|
||||
maximum: 1300000
|
||||
description:
|
||||
PMIC default "SUSPEND" state voltage in uV. See below table for
|
||||
bucks which support this. 0 means disabled.
|
||||
|
||||
# Supported default DVS states:
|
||||
#
|
||||
# BD71837:
|
||||
# buck | dvs-run-voltage | dvs-idle-voltage | dvs-suspend-voltage
|
||||
# ----------------------------------------------------------------
|
||||
# 1 | supported | supported | supported
|
||||
# ----------------------------------------------------------------
|
||||
# 2 | supported | supported | not supported
|
||||
# ----------------------------------------------------------------
|
||||
# 3 | supported | not supported | not supported
|
||||
# ----------------------------------------------------------------
|
||||
# 4 | supported | not supported | not supported
|
||||
# ----------------------------------------------------------------
|
||||
# rest | not supported | not supported | not supported
|
||||
|
||||
|
||||
required:
|
||||
- regulator-name
|
||||
additionalProperties: false
|
||||
additionalProperties: false
|
|
@ -0,0 +1,97 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/rohm,bd71847-regulator.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ROHM BD71847 and BD71850 Power Management Integrated Circuit regulators
|
||||
|
||||
maintainers:
|
||||
- Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
|
||||
|
||||
description: |
|
||||
List of regulators provided by this controller. BD71847 regulators node
|
||||
should be sub node of the BD71847 MFD node. See BD71847 MFD bindings at
|
||||
Documentation/devicetree/bindings/mfd/rohm,bd71847-pmic.yaml
|
||||
Regulator nodes should be named to BUCK_<number> and LDO_<number>. The
|
||||
definition for each of these nodes is defined using the standard
|
||||
binding for regulators at
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt.
|
||||
Note that if BD71847 starts at RUN state you probably want to use
|
||||
regulator-boot-on at least for BUCK5. LDO6 is supplied by it and it must
|
||||
not be disabled by driver at startup. If BUCK5 is disabled at startup the
|
||||
voltage monitoring for LDO5/LDO6 can cause PMIC to reset.
|
||||
|
||||
#The valid names for BD71847 regulator nodes are:
|
||||
#BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6
|
||||
#LDO1, LDO2, LDO3, LDO4, LDO5, LDO6
|
||||
|
||||
patternProperties:
|
||||
"^LDO[1-6]$":
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: regulator.yaml#
|
||||
description:
|
||||
Properties for single LDO regulator.
|
||||
|
||||
properties:
|
||||
regulator-name:
|
||||
pattern: "^ldo[1-6]$"
|
||||
description:
|
||||
should be "ldo1", ..., "ldo6"
|
||||
|
||||
"^BUCK[1-6]$":
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: regulator.yaml#
|
||||
description:
|
||||
Properties for single BUCK regulator.
|
||||
|
||||
properties:
|
||||
regulator-name:
|
||||
pattern: "^buck[1-6]$"
|
||||
description:
|
||||
should be "buck1", ..., "buck6"
|
||||
|
||||
rohm,dvs-run-voltage:
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0
|
||||
maximum: 1300000
|
||||
description:
|
||||
PMIC default "RUN" state voltage in uV. See below table for
|
||||
bucks which support this. 0 means disabled.
|
||||
|
||||
rohm,dvs-idle-voltage:
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0
|
||||
maximum: 1300000
|
||||
description:
|
||||
PMIC default "IDLE" state voltage in uV. See below table for
|
||||
bucks which support this. 0 means disabled.
|
||||
|
||||
rohm,dvs-suspend-voltage:
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0
|
||||
maximum: 1300000
|
||||
description:
|
||||
PMIC default "SUSPEND" state voltage in uV. See below table for
|
||||
bucks which support this. 0 means disabled.
|
||||
|
||||
# Supported default DVS states:
|
||||
#
|
||||
# BD71847:
|
||||
# buck | dvs-run-voltage | dvs-idle-voltage | dvs-suspend-voltage
|
||||
# ----------------------------------------------------------------
|
||||
# 1 | supported | supported | supported
|
||||
# ----------------------------------------------------------------
|
||||
# 2 | supported | supported | not supported
|
||||
# ----------------------------------------------------------------
|
||||
# rest | not supported | not supported | not supported
|
||||
|
||||
required:
|
||||
- regulator-name
|
||||
additionalProperties: false
|
||||
additionalProperties: false
|
|
@ -88,6 +88,7 @@ source "drivers/iio/orientation/Kconfig"
|
|||
if IIO_TRIGGER
|
||||
source "drivers/iio/trigger/Kconfig"
|
||||
endif #IIO_TRIGGER
|
||||
source "drivers/iio/position/Kconfig"
|
||||
source "drivers/iio/potentiometer/Kconfig"
|
||||
source "drivers/iio/potentiostat/Kconfig"
|
||||
source "drivers/iio/pressure/Kconfig"
|
||||
|
|
|
@ -31,6 +31,7 @@ obj-y += light/
|
|||
obj-y += magnetometer/
|
||||
obj-y += multiplexer/
|
||||
obj-y += orientation/
|
||||
obj-y += position/
|
||||
obj-y += potentiometer/
|
||||
obj-y += potentiostat/
|
||||
obj-y += pressure/
|
||||
|
|
|
@ -795,6 +795,16 @@ config RCAR_GYRO_ADC
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called rcar-gyroadc.
|
||||
|
||||
config RN5T618_ADC
|
||||
tristate "ADC for the RN5T618/RC5T619 family of chips"
|
||||
depends on MFD_RN5T618
|
||||
help
|
||||
Say yes here to build support for the integrated ADC inside the
|
||||
RN5T618/619 series PMICs:
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rn5t618-adc.
|
||||
|
||||
config ROCKCHIP_SARADC
|
||||
tristate "Rockchip SARADC driver"
|
||||
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
|
||||
|
|
|
@ -75,6 +75,7 @@ obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
|
|||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
|
||||
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
|
||||
obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
|
||||
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ADC driver for the RICOH RN5T618 power management chip family
|
||||
*
|
||||
* Copyright (C) 2019 Andreas Kemnade
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/rn5t618.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define RN5T618_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(500))
|
||||
#define RN5T618_REFERENCE_VOLT 2500
|
||||
|
||||
/* mask for selecting channels for single conversion */
|
||||
#define RN5T618_ADCCNT3_CHANNEL_MASK 0x7
|
||||
/* average 4-time conversion mode */
|
||||
#define RN5T618_ADCCNT3_AVG BIT(3)
|
||||
/* set for starting a single conversion, gets cleared by hw when done */
|
||||
#define RN5T618_ADCCNT3_GODONE BIT(4)
|
||||
/* automatic conversion, period is in ADCCNT2, selected channels are
|
||||
* in ADCCNT1
|
||||
*/
|
||||
#define RN5T618_ADCCNT3_AUTO BIT(5)
|
||||
#define RN5T618_ADCEND_IRQ BIT(0)
|
||||
|
||||
struct rn5t618_adc_data {
|
||||
struct device *dev;
|
||||
struct rn5t618 *rn5t618;
|
||||
struct completion conv_completion;
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct rn5t618_channel_ratios {
|
||||
u16 numerator;
|
||||
u16 denominator;
|
||||
};
|
||||
|
||||
enum rn5t618_channels {
|
||||
LIMMON = 0,
|
||||
VBAT,
|
||||
VADP,
|
||||
VUSB,
|
||||
VSYS,
|
||||
VTHM,
|
||||
AIN1,
|
||||
AIN0
|
||||
};
|
||||
|
||||
static const struct rn5t618_channel_ratios rn5t618_ratios[8] = {
|
||||
[LIMMON] = {50, 32}, /* measured across 20mOhm, amplified by 32 */
|
||||
[VBAT] = {2, 1},
|
||||
[VADP] = {3, 1},
|
||||
[VUSB] = {3, 1},
|
||||
[VSYS] = {3, 1},
|
||||
[VTHM] = {1, 1},
|
||||
[AIN1] = {1, 1},
|
||||
[AIN0] = {1, 1},
|
||||
};
|
||||
|
||||
static int rn5t618_read_adc_reg(struct rn5t618 *rn5t618, int reg, u16 *val)
|
||||
{
|
||||
u8 data[2];
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(rn5t618->regmap, reg, data, sizeof(data));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = (data[0] << 4) | (data[1] & 0xF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rn5t618_adc_irq(int irq, void *data)
|
||||
{
|
||||
struct rn5t618_adc_data *adc = data;
|
||||
unsigned int r = 0;
|
||||
int ret;
|
||||
|
||||
/* clear low & high threshold irqs */
|
||||
regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC1, 0);
|
||||
regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC2, 0);
|
||||
|
||||
ret = regmap_read(adc->rn5t618->regmap, RN5T618_IR_ADC3, &r);
|
||||
if (ret < 0)
|
||||
dev_err(adc->dev, "failed to read IRQ status: %d\n", ret);
|
||||
|
||||
regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC3, 0);
|
||||
|
||||
if (r & RN5T618_ADCEND_IRQ)
|
||||
complete(&adc->conv_completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rn5t618_adc_read(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct rn5t618_adc_data *adc = iio_priv(iio_dev);
|
||||
u16 raw;
|
||||
int ret;
|
||||
|
||||
if (mask == IIO_CHAN_INFO_SCALE) {
|
||||
*val = RN5T618_REFERENCE_VOLT *
|
||||
rn5t618_ratios[chan->channel].numerator;
|
||||
*val2 = rn5t618_ratios[chan->channel].denominator * 4095;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
/* select channel */
|
||||
ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3,
|
||||
RN5T618_ADCCNT3_CHANNEL_MASK,
|
||||
chan->channel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(adc->rn5t618->regmap, RN5T618_EN_ADCIR3,
|
||||
RN5T618_ADCEND_IRQ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3,
|
||||
RN5T618_ADCCNT3_AVG,
|
||||
mask == IIO_CHAN_INFO_AVERAGE_RAW ?
|
||||
RN5T618_ADCCNT3_AVG : 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
init_completion(&adc->conv_completion);
|
||||
/* single conversion */
|
||||
ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3,
|
||||
RN5T618_ADCCNT3_GODONE,
|
||||
RN5T618_ADCCNT3_GODONE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = wait_for_completion_timeout(&adc->conv_completion,
|
||||
RN5T618_ADC_CONVERSION_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
dev_warn(adc->dev, "timeout waiting for adc result\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
ret = rn5t618_read_adc_reg(adc->rn5t618,
|
||||
RN5T618_ILIMDATAH + 2 * chan->channel,
|
||||
&raw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = raw;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info rn5t618_adc_iio_info = {
|
||||
.read_raw = &rn5t618_adc_read,
|
||||
};
|
||||
|
||||
#define RN5T618_ADC_CHANNEL(_channel, _type, _name) { \
|
||||
.type = _type, \
|
||||
.channel = _channel, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = _name, \
|
||||
.indexed = 1. \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec rn5t618_adc_iio_channels[] = {
|
||||
RN5T618_ADC_CHANNEL(LIMMON, IIO_CURRENT, "LIMMON"),
|
||||
RN5T618_ADC_CHANNEL(VBAT, IIO_VOLTAGE, "VBAT"),
|
||||
RN5T618_ADC_CHANNEL(VADP, IIO_VOLTAGE, "VADP"),
|
||||
RN5T618_ADC_CHANNEL(VUSB, IIO_VOLTAGE, "VUSB"),
|
||||
RN5T618_ADC_CHANNEL(VSYS, IIO_VOLTAGE, "VSYS"),
|
||||
RN5T618_ADC_CHANNEL(VTHM, IIO_VOLTAGE, "VTHM"),
|
||||
RN5T618_ADC_CHANNEL(AIN1, IIO_VOLTAGE, "AIN1"),
|
||||
RN5T618_ADC_CHANNEL(AIN0, IIO_VOLTAGE, "AIN0")
|
||||
};
|
||||
|
||||
static int rn5t618_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *iio_dev;
|
||||
struct rn5t618_adc_data *adc;
|
||||
struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
|
||||
if (!iio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
adc = iio_priv(iio_dev);
|
||||
adc->dev = &pdev->dev;
|
||||
adc->rn5t618 = rn5t618;
|
||||
|
||||
if (rn5t618->irq_data)
|
||||
adc->irq = regmap_irq_get_virq(rn5t618->irq_data,
|
||||
RN5T618_IRQ_ADC);
|
||||
|
||||
if (adc->irq <= 0) {
|
||||
dev_err(&pdev->dev, "get virq failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
init_completion(&adc->conv_completion);
|
||||
|
||||
iio_dev->name = dev_name(&pdev->dev);
|
||||
iio_dev->dev.parent = &pdev->dev;
|
||||
iio_dev->info = &rn5t618_adc_iio_info;
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->channels = rn5t618_adc_iio_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(rn5t618_adc_iio_channels);
|
||||
|
||||
/* stop any auto-conversion */
|
||||
ret = regmap_write(rn5t618->regmap, RN5T618_ADCCNT3, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, iio_dev);
|
||||
|
||||
ret = devm_request_threaded_irq(adc->dev, adc->irq, NULL,
|
||||
rn5t618_adc_irq,
|
||||
IRQF_ONESHOT, dev_name(adc->dev),
|
||||
adc);
|
||||
if (ret < 0) {
|
||||
dev_err(adc->dev, "request irq %d failed: %d\n", adc->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(adc->dev, iio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver rn5t618_adc_driver = {
|
||||
.driver = {
|
||||
.name = "rn5t618-adc",
|
||||
},
|
||||
.probe = rn5t618_adc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(rn5t618_adc_driver);
|
||||
MODULE_ALIAS("platform:rn5t618-adc");
|
||||
MODULE_DESCRIPTION("RICOH RN5T618 ADC driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -194,6 +194,16 @@ config GP2AP020A00F
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called gp2ap020a00f.
|
||||
|
||||
config IQS621_ALS
|
||||
tristate "Azoteq IQS621/622 ambient light sensors"
|
||||
depends on MFD_IQS62X || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you want to build support for the Azoteq IQS621
|
||||
and IQS622 ambient light sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called iqs621-als.
|
||||
|
||||
config SENSORS_ISL29018
|
||||
tristate "Intersil 29018 light and proximity sensor"
|
||||
depends on I2C
|
||||
|
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_GP2AP002) += gp2ap002.o
|
|||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
|
||||
obj-$(CONFIG_IQS621_ALS) += iqs621-als.o
|
||||
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
|
||||
obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
|
||||
obj-$(CONFIG_ISL29125) += isl29125.o
|
||||
|
|
|
@ -0,0 +1,617 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Azoteq IQS621/622 Ambient Light Sensors
|
||||
*
|
||||
* Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/iqs62x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define IQS621_ALS_FLAGS_LIGHT BIT(7)
|
||||
#define IQS621_ALS_FLAGS_RANGE GENMASK(3, 0)
|
||||
|
||||
#define IQS621_ALS_UI_OUT 0x17
|
||||
|
||||
#define IQS621_ALS_THRESH_DARK 0x80
|
||||
#define IQS621_ALS_THRESH_LIGHT 0x81
|
||||
|
||||
#define IQS622_IR_RANGE 0x15
|
||||
#define IQS622_IR_FLAGS 0x16
|
||||
#define IQS622_IR_FLAGS_TOUCH BIT(1)
|
||||
#define IQS622_IR_FLAGS_PROX BIT(0)
|
||||
|
||||
#define IQS622_IR_UI_OUT 0x17
|
||||
|
||||
#define IQS622_IR_THRESH_PROX 0x91
|
||||
#define IQS622_IR_THRESH_TOUCH 0x92
|
||||
|
||||
struct iqs621_als_private {
|
||||
struct iqs62x_core *iqs62x;
|
||||
struct notifier_block notifier;
|
||||
struct mutex lock;
|
||||
bool light_en;
|
||||
bool range_en;
|
||||
bool prox_en;
|
||||
u8 als_flags;
|
||||
u8 ir_flags_mask;
|
||||
u8 ir_flags;
|
||||
u8 thresh_light;
|
||||
u8 thresh_dark;
|
||||
u8 thresh_prox;
|
||||
};
|
||||
|
||||
static int iqs621_als_init(struct iqs621_als_private *iqs621_als)
|
||||
{
|
||||
struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
|
||||
unsigned int event_mask = 0;
|
||||
int ret;
|
||||
|
||||
switch (iqs621_als->ir_flags_mask) {
|
||||
case IQS622_IR_FLAGS_TOUCH:
|
||||
ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_TOUCH,
|
||||
iqs621_als->thresh_prox);
|
||||
break;
|
||||
|
||||
case IQS622_IR_FLAGS_PROX:
|
||||
ret = regmap_write(iqs62x->regmap, IQS622_IR_THRESH_PROX,
|
||||
iqs621_als->thresh_prox);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT,
|
||||
iqs621_als->thresh_light);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(iqs62x->regmap, IQS621_ALS_THRESH_DARK,
|
||||
iqs621_als->thresh_dark);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (iqs621_als->light_en || iqs621_als->range_en)
|
||||
event_mask |= iqs62x->dev_desc->als_mask;
|
||||
|
||||
if (iqs621_als->prox_en)
|
||||
event_mask |= iqs62x->dev_desc->ir_mask;
|
||||
|
||||
return regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
|
||||
event_mask, 0);
|
||||
}
|
||||
|
||||
static int iqs621_als_notifier(struct notifier_block *notifier,
|
||||
unsigned long event_flags, void *context)
|
||||
{
|
||||
struct iqs62x_event_data *event_data = context;
|
||||
struct iqs621_als_private *iqs621_als;
|
||||
struct iio_dev *indio_dev;
|
||||
bool light_new, light_old;
|
||||
bool prox_new, prox_old;
|
||||
u8 range_new, range_old;
|
||||
s64 timestamp;
|
||||
int ret;
|
||||
|
||||
iqs621_als = container_of(notifier, struct iqs621_als_private,
|
||||
notifier);
|
||||
indio_dev = iio_priv_to_dev(iqs621_als);
|
||||
timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
mutex_lock(&iqs621_als->lock);
|
||||
|
||||
if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
|
||||
ret = iqs621_als_init(iqs621_als);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"Failed to re-initialize device: %d\n", ret);
|
||||
ret = NOTIFY_BAD;
|
||||
} else {
|
||||
ret = NOTIFY_OK;
|
||||
}
|
||||
|
||||
goto err_mutex;
|
||||
}
|
||||
|
||||
if (!iqs621_als->light_en && !iqs621_als->range_en &&
|
||||
!iqs621_als->prox_en) {
|
||||
ret = NOTIFY_DONE;
|
||||
goto err_mutex;
|
||||
}
|
||||
|
||||
/* IQS621 only */
|
||||
light_new = event_data->als_flags & IQS621_ALS_FLAGS_LIGHT;
|
||||
light_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_LIGHT;
|
||||
|
||||
if (iqs621_als->light_en && light_new && !light_old)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
timestamp);
|
||||
else if (iqs621_als->light_en && !light_new && light_old)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
timestamp);
|
||||
|
||||
/* IQS621 and IQS622 */
|
||||
range_new = event_data->als_flags & IQS621_ALS_FLAGS_RANGE;
|
||||
range_old = iqs621_als->als_flags & IQS621_ALS_FLAGS_RANGE;
|
||||
|
||||
if (iqs621_als->range_en && (range_new > range_old))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
|
||||
IIO_EV_TYPE_CHANGE,
|
||||
IIO_EV_DIR_RISING),
|
||||
timestamp);
|
||||
else if (iqs621_als->range_en && (range_new < range_old))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
|
||||
IIO_EV_TYPE_CHANGE,
|
||||
IIO_EV_DIR_FALLING),
|
||||
timestamp);
|
||||
|
||||
/* IQS622 only */
|
||||
prox_new = event_data->ir_flags & iqs621_als->ir_flags_mask;
|
||||
prox_old = iqs621_als->ir_flags & iqs621_als->ir_flags_mask;
|
||||
|
||||
if (iqs621_als->prox_en && prox_new && !prox_old)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
timestamp);
|
||||
else if (iqs621_als->prox_en && !prox_new && prox_old)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
timestamp);
|
||||
|
||||
iqs621_als->als_flags = event_data->als_flags;
|
||||
iqs621_als->ir_flags = event_data->ir_flags;
|
||||
ret = NOTIFY_OK;
|
||||
|
||||
err_mutex:
|
||||
mutex_unlock(&iqs621_als->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void iqs621_als_notifier_unregister(void *context)
|
||||
{
|
||||
struct iqs621_als_private *iqs621_als = context;
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(iqs621_als);
|
||||
int ret;
|
||||
|
||||
ret = blocking_notifier_chain_unregister(&iqs621_als->iqs62x->nh,
|
||||
&iqs621_als->notifier);
|
||||
if (ret)
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"Failed to unregister notifier: %d\n", ret);
|
||||
}
|
||||
|
||||
static int iqs621_als_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
|
||||
struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
|
||||
int ret;
|
||||
__le16 val_buf;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_INTENSITY:
|
||||
ret = regmap_read(iqs62x->regmap, chan->address, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val &= IQS621_ALS_FLAGS_RANGE;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_PROXIMITY:
|
||||
case IIO_LIGHT:
|
||||
ret = regmap_raw_read(iqs62x->regmap, chan->address, &val_buf,
|
||||
sizeof(val_buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = le16_to_cpu(val_buf);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int iqs621_als_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iqs621_als->lock);
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = iqs621_als->light_en;
|
||||
break;
|
||||
|
||||
case IIO_INTENSITY:
|
||||
ret = iqs621_als->range_en;
|
||||
break;
|
||||
|
||||
case IIO_PROXIMITY:
|
||||
ret = iqs621_als->prox_en;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&iqs621_als->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iqs621_als_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
|
||||
struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iqs621_als->lock);
|
||||
|
||||
ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->als_flags, &val);
|
||||
if (ret)
|
||||
goto err_mutex;
|
||||
iqs621_als->als_flags = val;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
|
||||
iqs62x->dev_desc->als_mask,
|
||||
iqs621_als->range_en || state ? 0 :
|
||||
0xFF);
|
||||
if (!ret)
|
||||
iqs621_als->light_en = state;
|
||||
break;
|
||||
|
||||
case IIO_INTENSITY:
|
||||
ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
|
||||
iqs62x->dev_desc->als_mask,
|
||||
iqs621_als->light_en || state ? 0 :
|
||||
0xFF);
|
||||
if (!ret)
|
||||
iqs621_als->range_en = state;
|
||||
break;
|
||||
|
||||
case IIO_PROXIMITY:
|
||||
ret = regmap_read(iqs62x->regmap, IQS622_IR_FLAGS, &val);
|
||||
if (ret)
|
||||
goto err_mutex;
|
||||
iqs621_als->ir_flags = val;
|
||||
|
||||
ret = regmap_update_bits(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
|
||||
iqs62x->dev_desc->ir_mask,
|
||||
state ? 0 : 0xFF);
|
||||
if (!ret)
|
||||
iqs621_als->prox_en = state;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
err_mutex:
|
||||
mutex_unlock(&iqs621_als->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iqs621_als_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
|
||||
int ret = IIO_VAL_INT;
|
||||
|
||||
mutex_lock(&iqs621_als->lock);
|
||||
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
*val = iqs621_als->thresh_light * 16;
|
||||
break;
|
||||
|
||||
case IIO_EV_DIR_FALLING:
|
||||
*val = iqs621_als->thresh_dark * 4;
|
||||
break;
|
||||
|
||||
case IIO_EV_DIR_EITHER:
|
||||
if (iqs621_als->ir_flags_mask == IQS622_IR_FLAGS_TOUCH)
|
||||
*val = iqs621_als->thresh_prox * 4;
|
||||
else
|
||||
*val = iqs621_als->thresh_prox;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&iqs621_als->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iqs621_als_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
struct iqs621_als_private *iqs621_als = iio_priv(indio_dev);
|
||||
struct iqs62x_core *iqs62x = iqs621_als->iqs62x;
|
||||
unsigned int thresh_reg, thresh_val;
|
||||
u8 ir_flags_mask, *thresh_cache;
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&iqs621_als->lock);
|
||||
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
thresh_reg = IQS621_ALS_THRESH_LIGHT;
|
||||
thresh_val = val / 16;
|
||||
|
||||
thresh_cache = &iqs621_als->thresh_light;
|
||||
ir_flags_mask = 0;
|
||||
break;
|
||||
|
||||
case IIO_EV_DIR_FALLING:
|
||||
thresh_reg = IQS621_ALS_THRESH_DARK;
|
||||
thresh_val = val / 4;
|
||||
|
||||
thresh_cache = &iqs621_als->thresh_dark;
|
||||
ir_flags_mask = 0;
|
||||
break;
|
||||
|
||||
case IIO_EV_DIR_EITHER:
|
||||
/*
|
||||
* The IQS622 supports two detection thresholds, both measured
|
||||
* in the same arbitrary units reported by read_raw: proximity
|
||||
* (0 through 255 in steps of 1), and touch (0 through 1020 in
|
||||
* steps of 4).
|
||||
*
|
||||
* Based on the single detection threshold chosen by the user,
|
||||
* select the hardware threshold that gives the best trade-off
|
||||
* between range and resolution.
|
||||
*
|
||||
* By default, the close-range (but coarse) touch threshold is
|
||||
* chosen during probe.
|
||||
*/
|
||||
switch (val) {
|
||||
case 0 ... 255:
|
||||
thresh_reg = IQS622_IR_THRESH_PROX;
|
||||
thresh_val = val;
|
||||
|
||||
ir_flags_mask = IQS622_IR_FLAGS_PROX;
|
||||
break;
|
||||
|
||||
case 256 ... 1020:
|
||||
thresh_reg = IQS622_IR_THRESH_TOUCH;
|
||||
thresh_val = val / 4;
|
||||
|
||||
ir_flags_mask = IQS622_IR_FLAGS_TOUCH;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto err_mutex;
|
||||
}
|
||||
|
||||
thresh_cache = &iqs621_als->thresh_prox;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto err_mutex;
|
||||
}
|
||||
|
||||
if (thresh_val > 0xFF)
|
||||
goto err_mutex;
|
||||
|
||||
ret = regmap_write(iqs62x->regmap, thresh_reg, thresh_val);
|
||||
if (ret)
|
||||
goto err_mutex;
|
||||
|
||||
*thresh_cache = thresh_val;
|
||||
iqs621_als->ir_flags_mask = ir_flags_mask;
|
||||
|
||||
err_mutex:
|
||||
mutex_unlock(&iqs621_als->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info iqs621_als_info = {
|
||||
.read_raw = &iqs621_als_read_raw,
|
||||
.read_event_config = iqs621_als_read_event_config,
|
||||
.write_event_config = iqs621_als_write_event_config,
|
||||
.read_event_value = iqs621_als_read_event_value,
|
||||
.write_event_value = iqs621_als_write_event_value,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec iqs621_als_range_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_CHANGE,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec iqs621_als_light_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
},
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec iqs621_als_channels[] = {
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.address = IQS621_ALS_FLAGS,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.event_spec = iqs621_als_range_events,
|
||||
.num_event_specs = ARRAY_SIZE(iqs621_als_range_events),
|
||||
},
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.address = IQS621_ALS_UI_OUT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.event_spec = iqs621_als_light_events,
|
||||
.num_event_specs = ARRAY_SIZE(iqs621_als_light_events),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec iqs622_als_prox_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
|
||||
BIT(IIO_EV_INFO_VALUE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec iqs622_als_channels[] = {
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||
.address = IQS622_ALS_FLAGS,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.event_spec = iqs621_als_range_events,
|
||||
.num_event_specs = ARRAY_SIZE(iqs621_als_range_events),
|
||||
.modified = true,
|
||||
},
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.channel2 = IIO_MOD_LIGHT_IR,
|
||||
.address = IQS622_IR_RANGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.modified = true,
|
||||
},
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.address = IQS622_IR_UI_OUT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.event_spec = iqs622_als_prox_events,
|
||||
.num_event_specs = ARRAY_SIZE(iqs622_als_prox_events),
|
||||
},
|
||||
};
|
||||
|
||||
static int iqs621_als_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct iqs621_als_private *iqs621_als;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs621_als));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
iqs621_als = iio_priv(indio_dev);
|
||||
iqs621_als->iqs62x = iqs62x;
|
||||
|
||||
if (iqs62x->dev_desc->prod_num == IQS622_PROD_NUM) {
|
||||
ret = regmap_read(iqs62x->regmap, IQS622_IR_THRESH_TOUCH,
|
||||
&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
iqs621_als->thresh_prox = val;
|
||||
iqs621_als->ir_flags_mask = IQS622_IR_FLAGS_TOUCH;
|
||||
|
||||
indio_dev->channels = iqs622_als_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(iqs622_als_channels);
|
||||
} else {
|
||||
ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_LIGHT,
|
||||
&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
iqs621_als->thresh_light = val;
|
||||
|
||||
ret = regmap_read(iqs62x->regmap, IQS621_ALS_THRESH_DARK,
|
||||
&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
iqs621_als->thresh_dark = val;
|
||||
|
||||
indio_dev->channels = iqs621_als_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(iqs621_als_channels);
|
||||
}
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = iqs62x->dev_desc->dev_name;
|
||||
indio_dev->info = &iqs621_als_info;
|
||||
|
||||
mutex_init(&iqs621_als->lock);
|
||||
|
||||
iqs621_als->notifier.notifier_call = iqs621_als_notifier;
|
||||
ret = blocking_notifier_chain_register(&iqs621_als->iqs62x->nh,
|
||||
&iqs621_als->notifier);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&pdev->dev,
|
||||
iqs621_als_notifier_unregister,
|
||||
iqs621_als);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver iqs621_als_platform_driver = {
|
||||
.driver = {
|
||||
.name = "iqs621-als",
|
||||
},
|
||||
.probe = iqs621_als_probe,
|
||||
};
|
||||
module_platform_driver(iqs621_als_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
|
||||
MODULE_DESCRIPTION("Azoteq IQS621/622 Ambient Light Sensors");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:iqs621-als");
|
|
@ -0,0 +1,19 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Linear and angular position sensors
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Linear and angular position sensors"
|
||||
|
||||
config IQS624_POS
|
||||
tristate "Azoteq IQS624/625 angular position sensors"
|
||||
depends on MFD_IQS62X || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you want to build support for the Azoteq IQS624
|
||||
and IQS625 angular position sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called iqs624-pos.
|
||||
|
||||
endmenu
|
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Makefile for IIO linear and angular position sensors
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
obj-$(CONFIG_IQS624_POS) += iqs624-pos.o
|
|
@ -0,0 +1,284 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Azoteq IQS624/625 Angular Position Sensors
|
||||
*
|
||||
* Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/iqs62x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define IQS624_POS_DEG_OUT 0x16
|
||||
|
||||
#define IQS624_POS_SCALE1 (314159 / 180)
|
||||
#define IQS624_POS_SCALE2 100000
|
||||
|
||||
struct iqs624_pos_private {
|
||||
struct iqs62x_core *iqs62x;
|
||||
struct notifier_block notifier;
|
||||
struct mutex lock;
|
||||
bool angle_en;
|
||||
u16 angle;
|
||||
};
|
||||
|
||||
static int iqs624_pos_angle_en(struct iqs62x_core *iqs62x, bool angle_en)
|
||||
{
|
||||
unsigned int event_mask = IQS624_HALL_UI_WHL_EVENT;
|
||||
|
||||
/*
|
||||
* The IQS625 reports angular position in the form of coarse intervals,
|
||||
* so only interval change events are unmasked. Conversely, the IQS624
|
||||
* reports angular position down to one degree of resolution, so wheel
|
||||
* movement events are unmasked instead.
|
||||
*/
|
||||
if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
|
||||
event_mask = IQS624_HALL_UI_INT_EVENT;
|
||||
|
||||
return regmap_update_bits(iqs62x->regmap, IQS624_HALL_UI, event_mask,
|
||||
angle_en ? 0 : 0xFF);
|
||||
}
|
||||
|
||||
static int iqs624_pos_notifier(struct notifier_block *notifier,
|
||||
unsigned long event_flags, void *context)
|
||||
{
|
||||
struct iqs62x_event_data *event_data = context;
|
||||
struct iqs624_pos_private *iqs624_pos;
|
||||
struct iqs62x_core *iqs62x;
|
||||
struct iio_dev *indio_dev;
|
||||
u16 angle = event_data->ui_data;
|
||||
s64 timestamp;
|
||||
int ret;
|
||||
|
||||
iqs624_pos = container_of(notifier, struct iqs624_pos_private,
|
||||
notifier);
|
||||
indio_dev = iio_priv_to_dev(iqs624_pos);
|
||||
timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
iqs62x = iqs624_pos->iqs62x;
|
||||
if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
|
||||
angle = event_data->interval;
|
||||
|
||||
mutex_lock(&iqs624_pos->lock);
|
||||
|
||||
if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
|
||||
ret = iqs624_pos_angle_en(iqs62x, iqs624_pos->angle_en);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"Failed to re-initialize device: %d\n", ret);
|
||||
ret = NOTIFY_BAD;
|
||||
} else {
|
||||
ret = NOTIFY_OK;
|
||||
}
|
||||
} else if (iqs624_pos->angle_en && (angle != iqs624_pos->angle)) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_ANGL, 0,
|
||||
IIO_EV_TYPE_CHANGE,
|
||||
IIO_EV_DIR_NONE),
|
||||
timestamp);
|
||||
|
||||
iqs624_pos->angle = angle;
|
||||
ret = NOTIFY_OK;
|
||||
} else {
|
||||
ret = NOTIFY_DONE;
|
||||
}
|
||||
|
||||
mutex_unlock(&iqs624_pos->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void iqs624_pos_notifier_unregister(void *context)
|
||||
{
|
||||
struct iqs624_pos_private *iqs624_pos = context;
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(iqs624_pos);
|
||||
int ret;
|
||||
|
||||
ret = blocking_notifier_chain_unregister(&iqs624_pos->iqs62x->nh,
|
||||
&iqs624_pos->notifier);
|
||||
if (ret)
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"Failed to unregister notifier: %d\n", ret);
|
||||
}
|
||||
|
||||
static int iqs624_pos_angle_get(struct iqs62x_core *iqs62x, unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
__le16 val_buf;
|
||||
|
||||
if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM)
|
||||
return regmap_read(iqs62x->regmap, iqs62x->dev_desc->interval,
|
||||
val);
|
||||
|
||||
ret = regmap_raw_read(iqs62x->regmap, IQS624_POS_DEG_OUT, &val_buf,
|
||||
sizeof(val_buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = le16_to_cpu(val_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iqs624_pos_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
|
||||
struct iqs62x_core *iqs62x = iqs624_pos->iqs62x;
|
||||
unsigned int scale = 1;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iqs624_pos_angle_get(iqs62x, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (iqs62x->dev_desc->prod_num == IQS625_PROD_NUM) {
|
||||
ret = regmap_read(iqs62x->regmap, IQS624_INTERVAL_DIV,
|
||||
&scale);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = scale * IQS624_POS_SCALE1;
|
||||
*val2 = IQS624_POS_SCALE2;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int iqs624_pos_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iqs624_pos->lock);
|
||||
ret = iqs624_pos->angle_en;
|
||||
mutex_unlock(&iqs624_pos->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iqs624_pos_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
struct iqs624_pos_private *iqs624_pos = iio_priv(indio_dev);
|
||||
struct iqs62x_core *iqs62x = iqs624_pos->iqs62x;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iqs624_pos->lock);
|
||||
|
||||
ret = iqs624_pos_angle_get(iqs62x, &val);
|
||||
if (ret)
|
||||
goto err_mutex;
|
||||
|
||||
ret = iqs624_pos_angle_en(iqs62x, state);
|
||||
if (ret)
|
||||
goto err_mutex;
|
||||
|
||||
iqs624_pos->angle = val;
|
||||
iqs624_pos->angle_en = state;
|
||||
|
||||
err_mutex:
|
||||
mutex_unlock(&iqs624_pos->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info iqs624_pos_info = {
|
||||
.read_raw = &iqs624_pos_read_raw,
|
||||
.read_event_config = iqs624_pos_read_event_config,
|
||||
.write_event_config = iqs624_pos_write_event_config,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec iqs624_pos_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_CHANGE,
|
||||
.dir = IIO_EV_DIR_NONE,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec iqs624_pos_channels[] = {
|
||||
{
|
||||
.type = IIO_ANGL,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.event_spec = iqs624_pos_events,
|
||||
.num_event_specs = ARRAY_SIZE(iqs624_pos_events),
|
||||
},
|
||||
};
|
||||
|
||||
static int iqs624_pos_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct iqs624_pos_private *iqs624_pos;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*iqs624_pos));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
iqs624_pos = iio_priv(indio_dev);
|
||||
iqs624_pos->iqs62x = iqs62x;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->channels = iqs624_pos_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(iqs624_pos_channels);
|
||||
indio_dev->name = iqs62x->dev_desc->dev_name;
|
||||
indio_dev->info = &iqs624_pos_info;
|
||||
|
||||
mutex_init(&iqs624_pos->lock);
|
||||
|
||||
iqs624_pos->notifier.notifier_call = iqs624_pos_notifier;
|
||||
ret = blocking_notifier_chain_register(&iqs624_pos->iqs62x->nh,
|
||||
&iqs624_pos->notifier);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&pdev->dev,
|
||||
iqs624_pos_notifier_unregister,
|
||||
iqs624_pos);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver iqs624_pos_platform_driver = {
|
||||
.driver = {
|
||||
.name = "iqs624-pos",
|
||||
},
|
||||
.probe = iqs624_pos_probe,
|
||||
};
|
||||
module_platform_driver(iqs624_pos_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
|
||||
MODULE_DESCRIPTION("Azoteq IQS624/625 Angular Position Sensors");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:iqs624-pos");
|
|
@ -4,6 +4,16 @@
|
|||
#
|
||||
menu "Temperature sensors"
|
||||
|
||||
config IQS620AT_TEMP
|
||||
tristate "Azoteq IQS620AT temperature sensor"
|
||||
depends on MFD_IQS62X || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you want to build support for the Azoteq IQS620AT
|
||||
temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called iqs620at-temp.
|
||||
|
||||
config LTC2983
|
||||
tristate "Analog Devices Multi-Sensor Digital Temperature Measurement System"
|
||||
depends on SPI
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Makefile for industrial I/O temperature drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IQS620AT_TEMP) += iqs620at-temp.o
|
||||
obj-$(CONFIG_LTC2983) += ltc2983.o
|
||||
obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o
|
||||
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Azoteq IQS620AT Temperature Sensor
|
||||
*
|
||||
* Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/iqs62x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define IQS620_TEMP_UI_OUT 0x1A
|
||||
|
||||
#define IQS620_TEMP_SCALE 1000
|
||||
#define IQS620_TEMP_OFFSET (-100)
|
||||
|
||||
static int iqs620_temp_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct iqs62x_core *iqs62x = iio_device_get_drvdata(indio_dev);
|
||||
int ret;
|
||||
__le16 val_buf;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = regmap_raw_read(iqs62x->regmap, IQS620_TEMP_UI_OUT,
|
||||
&val_buf, sizeof(val_buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = le16_to_cpu(val_buf);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = IQS620_TEMP_SCALE;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = IQS620_TEMP_OFFSET;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info iqs620_temp_info = {
|
||||
.read_raw = &iqs620_temp_read_raw,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec iqs620_temp_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
},
|
||||
};
|
||||
|
||||
static int iqs620_temp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, 0);
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_set_drvdata(indio_dev, iqs62x);
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->channels = iqs620_temp_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(iqs620_temp_channels);
|
||||
indio_dev->name = iqs62x->dev_desc->dev_name;
|
||||
indio_dev->info = &iqs620_temp_info;
|
||||
|
||||
return devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver iqs620_temp_platform_driver = {
|
||||
.driver = {
|
||||
.name = "iqs620at-temp",
|
||||
},
|
||||
.probe = iqs620_temp_probe,
|
||||
};
|
||||
module_platform_driver(iqs620_temp_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
|
||||
MODULE_DESCRIPTION("Azoteq IQS620AT Temperature Sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:iqs620at-temp");
|
|
@ -663,6 +663,16 @@ config KEYBOARD_IPAQ_MICRO
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ipaq-micro-keys.
|
||||
|
||||
config KEYBOARD_IQS62X
|
||||
tristate "Azoteq IQS620A/621/622/624/625 keys and switches"
|
||||
depends on MFD_IQS62X
|
||||
help
|
||||
Say Y here to enable key and switch support for the Azoteq IQS620A,
|
||||
IQS621, IQS622, IQS624 and IQS625 multi-function sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called iqs62x-keys.
|
||||
|
||||
config KEYBOARD_OMAP
|
||||
tristate "TI OMAP keypad support"
|
||||
depends on ARCH_OMAP1
|
||||
|
|
|
@ -28,6 +28,7 @@ obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o
|
|||
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_IPAQ_MICRO) += ipaq-micro-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_IQS62X) += iqs62x-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_IMX_SC_KEY) += imx_sc_key.o
|
||||
obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Azoteq IQS620A/621/622/624/625 Keys and Switches
|
||||
*
|
||||
* Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/iqs62x.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
enum {
|
||||
IQS62X_SW_HALL_N,
|
||||
IQS62X_SW_HALL_S,
|
||||
};
|
||||
|
||||
static const char * const iqs62x_switch_names[] = {
|
||||
[IQS62X_SW_HALL_N] = "hall-switch-north",
|
||||
[IQS62X_SW_HALL_S] = "hall-switch-south",
|
||||
};
|
||||
|
||||
struct iqs62x_switch_desc {
|
||||
enum iqs62x_event_flag flag;
|
||||
unsigned int code;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct iqs62x_keys_private {
|
||||
struct iqs62x_core *iqs62x;
|
||||
struct input_dev *input;
|
||||
struct notifier_block notifier;
|
||||
struct iqs62x_switch_desc switches[ARRAY_SIZE(iqs62x_switch_names)];
|
||||
unsigned int keycode[IQS62X_NUM_KEYS];
|
||||
unsigned int keycodemax;
|
||||
u8 interval;
|
||||
};
|
||||
|
||||
static int iqs62x_keys_parse_prop(struct platform_device *pdev,
|
||||
struct iqs62x_keys_private *iqs62x_keys)
|
||||
{
|
||||
struct fwnode_handle *child;
|
||||
unsigned int val;
|
||||
int ret, i;
|
||||
|
||||
ret = device_property_count_u32(&pdev->dev, "linux,keycodes");
|
||||
if (ret > IQS62X_NUM_KEYS) {
|
||||
dev_err(&pdev->dev, "Too many keycodes present\n");
|
||||
return -EINVAL;
|
||||
} else if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to count keycodes: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
iqs62x_keys->keycodemax = ret;
|
||||
|
||||
ret = device_property_read_u32_array(&pdev->dev, "linux,keycodes",
|
||||
iqs62x_keys->keycode,
|
||||
iqs62x_keys->keycodemax);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to read keycodes: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++) {
|
||||
child = device_get_named_child_node(&pdev->dev,
|
||||
iqs62x_switch_names[i]);
|
||||
if (!child)
|
||||
continue;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "linux,code", &val);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to read switch code: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
iqs62x_keys->switches[i].code = val;
|
||||
iqs62x_keys->switches[i].enabled = true;
|
||||
|
||||
if (fwnode_property_present(child, "azoteq,use-prox"))
|
||||
iqs62x_keys->switches[i].flag = (i == IQS62X_SW_HALL_N ?
|
||||
IQS62X_EVENT_HALL_N_P :
|
||||
IQS62X_EVENT_HALL_S_P);
|
||||
else
|
||||
iqs62x_keys->switches[i].flag = (i == IQS62X_SW_HALL_N ?
|
||||
IQS62X_EVENT_HALL_N_T :
|
||||
IQS62X_EVENT_HALL_S_T);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iqs62x_keys_init(struct iqs62x_keys_private *iqs62x_keys)
|
||||
{
|
||||
struct iqs62x_core *iqs62x = iqs62x_keys->iqs62x;
|
||||
enum iqs62x_event_flag flag;
|
||||
unsigned int event_reg, val;
|
||||
unsigned int event_mask = 0;
|
||||
int ret, i;
|
||||
|
||||
switch (iqs62x->dev_desc->prod_num) {
|
||||
case IQS620_PROD_NUM:
|
||||
case IQS621_PROD_NUM:
|
||||
case IQS622_PROD_NUM:
|
||||
event_reg = IQS620_GLBL_EVENT_MASK;
|
||||
|
||||
/*
|
||||
* Discreet button, hysteresis and SAR UI flags represent keys
|
||||
* and are unmasked if mapped to a valid keycode.
|
||||
*/
|
||||
for (i = 0; i < iqs62x_keys->keycodemax; i++) {
|
||||
if (iqs62x_keys->keycode[i] == KEY_RESERVED)
|
||||
continue;
|
||||
|
||||
if (iqs62x_events[i].reg == IQS62X_EVENT_PROX)
|
||||
event_mask |= iqs62x->dev_desc->prox_mask;
|
||||
else if (iqs62x_events[i].reg == IQS62X_EVENT_HYST)
|
||||
event_mask |= (iqs62x->dev_desc->hyst_mask |
|
||||
iqs62x->dev_desc->sar_mask);
|
||||
}
|
||||
|
||||
ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->hall_flags,
|
||||
&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Hall UI flags represent switches and are unmasked if their
|
||||
* corresponding child nodes are present.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++) {
|
||||
if (!(iqs62x_keys->switches[i].enabled))
|
||||
continue;
|
||||
|
||||
flag = iqs62x_keys->switches[i].flag;
|
||||
|
||||
if (iqs62x_events[flag].reg != IQS62X_EVENT_HALL)
|
||||
continue;
|
||||
|
||||
event_mask |= iqs62x->dev_desc->hall_mask;
|
||||
|
||||
input_report_switch(iqs62x_keys->input,
|
||||
iqs62x_keys->switches[i].code,
|
||||
(val & iqs62x_events[flag].mask) ==
|
||||
iqs62x_events[flag].val);
|
||||
}
|
||||
|
||||
input_sync(iqs62x_keys->input);
|
||||
break;
|
||||
|
||||
case IQS624_PROD_NUM:
|
||||
event_reg = IQS624_HALL_UI;
|
||||
|
||||
/*
|
||||
* Interval change events represent keys and are unmasked if
|
||||
* either wheel movement flag is mapped to a valid keycode.
|
||||
*/
|
||||
if (iqs62x_keys->keycode[IQS62X_EVENT_WHEEL_UP] != KEY_RESERVED)
|
||||
event_mask |= IQS624_HALL_UI_INT_EVENT;
|
||||
|
||||
if (iqs62x_keys->keycode[IQS62X_EVENT_WHEEL_DN] != KEY_RESERVED)
|
||||
event_mask |= IQS624_HALL_UI_INT_EVENT;
|
||||
|
||||
ret = regmap_read(iqs62x->regmap, iqs62x->dev_desc->interval,
|
||||
&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iqs62x_keys->interval = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return regmap_update_bits(iqs62x->regmap, event_reg, event_mask, 0);
|
||||
}
|
||||
|
||||
static int iqs62x_keys_notifier(struct notifier_block *notifier,
|
||||
unsigned long event_flags, void *context)
|
||||
{
|
||||
struct iqs62x_event_data *event_data = context;
|
||||
struct iqs62x_keys_private *iqs62x_keys;
|
||||
int ret, i;
|
||||
|
||||
iqs62x_keys = container_of(notifier, struct iqs62x_keys_private,
|
||||
notifier);
|
||||
|
||||
if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
|
||||
ret = iqs62x_keys_init(iqs62x_keys);
|
||||
if (ret) {
|
||||
dev_err(iqs62x_keys->input->dev.parent,
|
||||
"Failed to re-initialize device: %d\n", ret);
|
||||
return NOTIFY_BAD;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
for (i = 0; i < iqs62x_keys->keycodemax; i++) {
|
||||
if (iqs62x_events[i].reg == IQS62X_EVENT_WHEEL &&
|
||||
event_data->interval == iqs62x_keys->interval)
|
||||
continue;
|
||||
|
||||
input_report_key(iqs62x_keys->input, iqs62x_keys->keycode[i],
|
||||
event_flags & BIT(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++)
|
||||
if (iqs62x_keys->switches[i].enabled)
|
||||
input_report_switch(iqs62x_keys->input,
|
||||
iqs62x_keys->switches[i].code,
|
||||
event_flags &
|
||||
BIT(iqs62x_keys->switches[i].flag));
|
||||
|
||||
input_sync(iqs62x_keys->input);
|
||||
|
||||
if (event_data->interval == iqs62x_keys->interval)
|
||||
return NOTIFY_OK;
|
||||
|
||||
/*
|
||||
* Each frame contains at most one wheel event (up or down), in which
|
||||
* case a complementary release cycle is emulated.
|
||||
*/
|
||||
if (event_flags & BIT(IQS62X_EVENT_WHEEL_UP)) {
|
||||
input_report_key(iqs62x_keys->input,
|
||||
iqs62x_keys->keycode[IQS62X_EVENT_WHEEL_UP],
|
||||
0);
|
||||
input_sync(iqs62x_keys->input);
|
||||
} else if (event_flags & BIT(IQS62X_EVENT_WHEEL_DN)) {
|
||||
input_report_key(iqs62x_keys->input,
|
||||
iqs62x_keys->keycode[IQS62X_EVENT_WHEEL_DN],
|
||||
0);
|
||||
input_sync(iqs62x_keys->input);
|
||||
}
|
||||
|
||||
iqs62x_keys->interval = event_data->interval;
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int iqs62x_keys_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct iqs62x_keys_private *iqs62x_keys;
|
||||
struct input_dev *input;
|
||||
int ret, i;
|
||||
|
||||
iqs62x_keys = devm_kzalloc(&pdev->dev, sizeof(*iqs62x_keys),
|
||||
GFP_KERNEL);
|
||||
if (!iqs62x_keys)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, iqs62x_keys);
|
||||
|
||||
ret = iqs62x_keys_parse_prop(pdev, iqs62x_keys);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->keycodemax = iqs62x_keys->keycodemax;
|
||||
input->keycode = iqs62x_keys->keycode;
|
||||
input->keycodesize = sizeof(*iqs62x_keys->keycode);
|
||||
|
||||
input->name = iqs62x->dev_desc->dev_name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
|
||||
for (i = 0; i < iqs62x_keys->keycodemax; i++)
|
||||
if (iqs62x_keys->keycode[i] != KEY_RESERVED)
|
||||
input_set_capability(input, EV_KEY,
|
||||
iqs62x_keys->keycode[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(iqs62x_keys->switches); i++)
|
||||
if (iqs62x_keys->switches[i].enabled)
|
||||
input_set_capability(input, EV_SW,
|
||||
iqs62x_keys->switches[i].code);
|
||||
|
||||
iqs62x_keys->iqs62x = iqs62x;
|
||||
iqs62x_keys->input = input;
|
||||
|
||||
ret = iqs62x_keys_init(iqs62x_keys);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to initialize device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = input_register_device(iqs62x_keys->input);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
iqs62x_keys->notifier.notifier_call = iqs62x_keys_notifier;
|
||||
ret = blocking_notifier_chain_register(&iqs62x_keys->iqs62x->nh,
|
||||
&iqs62x_keys->notifier);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iqs62x_keys_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iqs62x_keys_private *iqs62x_keys = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = blocking_notifier_chain_unregister(&iqs62x_keys->iqs62x->nh,
|
||||
&iqs62x_keys->notifier);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to unregister notifier: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver iqs62x_keys_platform_driver = {
|
||||
.driver = {
|
||||
.name = "iqs62x-keys",
|
||||
},
|
||||
.probe = iqs62x_keys_probe,
|
||||
.remove = iqs62x_keys_remove,
|
||||
};
|
||||
module_platform_driver(iqs62x_keys_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
|
||||
MODULE_DESCRIPTION("Azoteq IQS620A/621/622/624/625 Keys and Switches");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:iqs62x-keys");
|
|
@ -642,6 +642,19 @@ config MFD_IPAQ_MICRO
|
|||
AT90LS8535 microcontroller flashed with a special iPAQ
|
||||
firmware using the custom protocol implemented in this driver.
|
||||
|
||||
config MFD_IQS62X
|
||||
tristate "Azoteq IQS620A/621/622/624/625 core support"
|
||||
depends on I2C
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here if you want to build core support for the Azoteq IQS620A,
|
||||
IQS621, IQS622, IQS624 and IQS625 multi-function sensors. Additional
|
||||
options must be selected to enable device-specific functions.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called iqs62x.
|
||||
|
||||
config MFD_JANZ_CMODIO
|
||||
tristate "Janz CMOD-IO PCI MODULbus Carrier Board"
|
||||
select MFD_CORE
|
||||
|
@ -893,6 +906,7 @@ config MFD_CPCAP
|
|||
tristate "Support for Motorola CPCAP"
|
||||
depends on SPI
|
||||
depends on OF || COMPILE_TEST
|
||||
select MFD_CORE
|
||||
select REGMAP_SPI
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
|
@ -1058,6 +1072,7 @@ config MFD_RN5T618
|
|||
depends on OF
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
Say yes here to add support for the Ricoh RN5T567,
|
||||
RN5T618, RC5T619 PMIC.
|
||||
|
@ -1201,7 +1216,7 @@ config AB8500_CORE
|
|||
chip. This connects to U8500 either on the SSP/SPI bus (deprecated
|
||||
since hardware version v1.0) or the I2C bus via PRCMU. It also adds
|
||||
the irq_chip parts for handling the Mixed Signal chip events.
|
||||
This chip embeds various other multimedia funtionalities as well.
|
||||
This chip embeds various other multimedia functionalities as well.
|
||||
|
||||
config AB8500_DEBUG
|
||||
bool "Enable debug info via debugfs"
|
||||
|
@ -1851,7 +1866,7 @@ config MFD_WM8994
|
|||
has on board GPIO and regulator functionality which is
|
||||
supported via the relevant subsystems. This driver provides
|
||||
core support for the WM8994, in order to use the actual
|
||||
functionaltiy of the device other drivers must be enabled.
|
||||
functionality of the device other drivers must be enabled.
|
||||
|
||||
config MFD_WM97xx
|
||||
tristate "Wolfson Microelectronics WM97xx"
|
||||
|
@ -1864,7 +1879,7 @@ config MFD_WM97xx
|
|||
designed for smartphone applications. As well as audio functionality
|
||||
it has on board GPIO and a touchscreen functionality which is
|
||||
supported via the relevant subsystems. This driver provides core
|
||||
support for the WM97xx, in order to use the actual functionaltiy of
|
||||
support for the WM97xx, in order to use the actual functionality of
|
||||
the device other drivers must be enabled.
|
||||
|
||||
config MFD_STW481X
|
||||
|
@ -1957,7 +1972,7 @@ config MFD_STPMIC1
|
|||
Support for ST Microelectronics STPMIC1 PMIC. STPMIC1 has power on
|
||||
key, watchdog and regulator functionalities which are supported via
|
||||
the relevant subsystems. This driver provides core support for the
|
||||
STPMIC1. In order to use the actual functionaltiy of the device other
|
||||
STPMIC1. In order to use the actual functionality of the device other
|
||||
drivers must be enabled.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
|
|
|
@ -226,6 +226,7 @@ obj-$(CONFIG_MFD_AS3711) += as3711.o
|
|||
obj-$(CONFIG_MFD_AS3722) += as3722.o
|
||||
obj-$(CONFIG_MFD_STW481X) += stw481x.o
|
||||
obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
|
||||
obj-$(CONFIG_MFD_IQS62X) += iqs62x.o
|
||||
obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o
|
||||
obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o
|
||||
obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
|
||||
|
|
|
@ -221,7 +221,7 @@ static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf)
|
|||
|
||||
count += sprintf(buf, "aat2870 registers\n");
|
||||
for (addr = 0; addr < AAT2870_REG_NUM; addr++) {
|
||||
count += sprintf(buf + count, "0x%02x: ", addr);
|
||||
count += snprintf(buf + count, PAGE_SIZE - count, "0x%02x: ", addr);
|
||||
if (count >= PAGE_SIZE - 1)
|
||||
break;
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ static int ec_device_probe(struct platform_device *pdev)
|
|||
* explicitly added on platforms that don't have the PD notifier ACPI
|
||||
* device entry defined.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_OF)) {
|
||||
if (IS_ENABLED(CONFIG_OF) && ec->ec_dev->dev->of_node) {
|
||||
if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) {
|
||||
retval = mfd_add_hotplug_devices(ec->dev,
|
||||
cros_usbpd_notify_cells,
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#define DA9062_REG_EVENT_B_OFFSET 1
|
||||
#define DA9062_REG_EVENT_C_OFFSET 2
|
||||
|
||||
#define DA9062_IRQ_LOW 0
|
||||
#define DA9062_IRQ_HIGH 1
|
||||
|
||||
static struct regmap_irq da9061_irqs[] = {
|
||||
/* EVENT A */
|
||||
[DA9061_IRQ_ONKEY] = {
|
||||
|
@ -369,6 +372,33 @@ static int da9062_get_device_type(struct da9062 *chip)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static u32 da9062_configure_irq_type(struct da9062 *chip, int irq, u32 *trigger)
|
||||
{
|
||||
u32 irq_type = 0;
|
||||
struct irq_data *irq_data = irq_get_irq_data(irq);
|
||||
|
||||
if (!irq_data) {
|
||||
dev_err(chip->dev, "Invalid IRQ: %d\n", irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
*trigger = irqd_get_trigger_type(irq_data);
|
||||
|
||||
switch (*trigger) {
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
irq_type = DA9062_IRQ_HIGH;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
irq_type = DA9062_IRQ_LOW;
|
||||
break;
|
||||
default:
|
||||
dev_warn(chip->dev, "Unsupported IRQ type: %d\n", *trigger);
|
||||
return -EINVAL;
|
||||
}
|
||||
return regmap_update_bits(chip->regmap, DA9062AA_CONFIG_A,
|
||||
DA9062AA_IRQ_TYPE_MASK,
|
||||
irq_type << DA9062AA_IRQ_TYPE_SHIFT);
|
||||
}
|
||||
|
||||
static const struct regmap_range da9061_aa_readable_ranges[] = {
|
||||
regmap_reg_range(DA9062AA_PAGE_CON, DA9062AA_STATUS_B),
|
||||
regmap_reg_range(DA9062AA_STATUS_D, DA9062AA_EVENT_C),
|
||||
|
@ -388,6 +418,7 @@ static const struct regmap_range da9061_aa_readable_ranges[] = {
|
|||
regmap_reg_range(DA9062AA_VBUCK1_A, DA9062AA_VBUCK4_A),
|
||||
regmap_reg_range(DA9062AA_VBUCK3_A, DA9062AA_VBUCK3_A),
|
||||
regmap_reg_range(DA9062AA_VLDO1_A, DA9062AA_VLDO4_A),
|
||||
regmap_reg_range(DA9062AA_CONFIG_A, DA9062AA_CONFIG_A),
|
||||
regmap_reg_range(DA9062AA_VBUCK1_B, DA9062AA_VBUCK4_B),
|
||||
regmap_reg_range(DA9062AA_VBUCK3_B, DA9062AA_VBUCK3_B),
|
||||
regmap_reg_range(DA9062AA_VLDO1_B, DA9062AA_VLDO4_B),
|
||||
|
@ -417,6 +448,7 @@ static const struct regmap_range da9061_aa_writeable_ranges[] = {
|
|||
regmap_reg_range(DA9062AA_VBUCK1_A, DA9062AA_VBUCK4_A),
|
||||
regmap_reg_range(DA9062AA_VBUCK3_A, DA9062AA_VBUCK3_A),
|
||||
regmap_reg_range(DA9062AA_VLDO1_A, DA9062AA_VLDO4_A),
|
||||
regmap_reg_range(DA9062AA_CONFIG_A, DA9062AA_CONFIG_A),
|
||||
regmap_reg_range(DA9062AA_VBUCK1_B, DA9062AA_VBUCK4_B),
|
||||
regmap_reg_range(DA9062AA_VBUCK3_B, DA9062AA_VBUCK3_B),
|
||||
regmap_reg_range(DA9062AA_VLDO1_B, DA9062AA_VLDO4_B),
|
||||
|
@ -596,6 +628,7 @@ static int da9062_i2c_probe(struct i2c_client *i2c,
|
|||
const struct regmap_irq_chip *irq_chip;
|
||||
const struct regmap_config *config;
|
||||
int cell_num;
|
||||
u32 trigger_type = 0;
|
||||
int ret;
|
||||
|
||||
chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
|
||||
|
@ -654,10 +687,15 @@ static int da9062_i2c_probe(struct i2c_client *i2c,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = da9062_configure_irq_type(chip, i2c->irq, &trigger_type);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to configure IRQ type\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(chip->regmap, i2c->irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
|
||||
-1, irq_chip,
|
||||
&chip->regmap_irq);
|
||||
trigger_type | IRQF_SHARED | IRQF_ONESHOT,
|
||||
-1, irq_chip, &chip->regmap_irq);
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "Failed to request IRQ %d: %d\n",
|
||||
i2c->irq, ret);
|
||||
|
|
|
@ -90,6 +90,11 @@ struct dln2_mod_rx_slots {
|
|||
spinlock_t lock;
|
||||
};
|
||||
|
||||
enum dln2_endpoint {
|
||||
DLN2_EP_OUT = 0,
|
||||
DLN2_EP_IN = 1,
|
||||
};
|
||||
|
||||
struct dln2_dev {
|
||||
struct usb_device *usb_dev;
|
||||
struct usb_interface *interface;
|
||||
|
@ -640,35 +645,56 @@ static int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
DLN2_ACPI_MATCH_GPIO = 0,
|
||||
DLN2_ACPI_MATCH_I2C = 1,
|
||||
DLN2_ACPI_MATCH_SPI = 2,
|
||||
};
|
||||
|
||||
static struct dln2_platform_data dln2_pdata_gpio = {
|
||||
.handle = DLN2_HANDLE_GPIO,
|
||||
};
|
||||
|
||||
static struct mfd_cell_acpi_match dln2_acpi_match_gpio = {
|
||||
.adr = DLN2_ACPI_MATCH_GPIO,
|
||||
};
|
||||
|
||||
/* Only one I2C port seems to be supported on current hardware */
|
||||
static struct dln2_platform_data dln2_pdata_i2c = {
|
||||
.handle = DLN2_HANDLE_I2C,
|
||||
.port = 0,
|
||||
};
|
||||
|
||||
static struct mfd_cell_acpi_match dln2_acpi_match_i2c = {
|
||||
.adr = DLN2_ACPI_MATCH_I2C,
|
||||
};
|
||||
|
||||
/* Only one SPI port supported */
|
||||
static struct dln2_platform_data dln2_pdata_spi = {
|
||||
.handle = DLN2_HANDLE_SPI,
|
||||
.port = 0,
|
||||
};
|
||||
|
||||
static struct mfd_cell_acpi_match dln2_acpi_match_spi = {
|
||||
.adr = DLN2_ACPI_MATCH_SPI,
|
||||
};
|
||||
|
||||
static const struct mfd_cell dln2_devs[] = {
|
||||
{
|
||||
.name = "dln2-gpio",
|
||||
.acpi_match = &dln2_acpi_match_gpio,
|
||||
.platform_data = &dln2_pdata_gpio,
|
||||
.pdata_size = sizeof(struct dln2_platform_data),
|
||||
},
|
||||
{
|
||||
.name = "dln2-i2c",
|
||||
.acpi_match = &dln2_acpi_match_i2c,
|
||||
.platform_data = &dln2_pdata_i2c,
|
||||
.pdata_size = sizeof(struct dln2_platform_data),
|
||||
},
|
||||
{
|
||||
.name = "dln2-spi",
|
||||
.acpi_match = &dln2_acpi_match_spi,
|
||||
.platform_data = &dln2_pdata_spi,
|
||||
.pdata_size = sizeof(struct dln2_platform_data),
|
||||
},
|
||||
|
@ -733,10 +759,10 @@ static int dln2_probe(struct usb_interface *interface,
|
|||
hostif->desc.bNumEndpoints < 2)
|
||||
return -ENODEV;
|
||||
|
||||
epin = &hostif->endpoint[0].desc;
|
||||
epout = &hostif->endpoint[1].desc;
|
||||
epout = &hostif->endpoint[DLN2_EP_OUT].desc;
|
||||
if (!usb_endpoint_is_bulk_out(epout))
|
||||
return -ENODEV;
|
||||
epin = &hostif->endpoint[DLN2_EP_IN].desc;
|
||||
if (!usb_endpoint_is_bulk_in(epin))
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
@ -139,6 +139,11 @@ static const struct intel_lpss_platform_info cnl_i2c_info = {
|
|||
.properties = spt_i2c_properties,
|
||||
};
|
||||
|
||||
static const struct intel_lpss_platform_info ehl_i2c_info = {
|
||||
.clk_rate = 100000000,
|
||||
.properties = bxt_i2c_properties,
|
||||
};
|
||||
|
||||
static const struct pci_device_id intel_lpss_pci_ids[] = {
|
||||
/* CML-LP */
|
||||
{ PCI_VDEVICE(INTEL, 0x02a8), (kernel_ulong_t)&spt_uart_info },
|
||||
|
@ -231,15 +236,15 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
|
|||
{ PCI_VDEVICE(INTEL, 0x4b2a), (kernel_ulong_t)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b2b), (kernel_ulong_t)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b37), (kernel_ulong_t)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b44), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b45), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b4b), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b4c), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b44), (kernel_ulong_t)&ehl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b45), (kernel_ulong_t)&ehl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b4b), (kernel_ulong_t)&ehl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b4c), (kernel_ulong_t)&ehl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b4d), (kernel_ulong_t)&bxt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b78), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b79), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b7a), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b7b), (kernel_ulong_t)&bxt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b78), (kernel_ulong_t)&ehl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b79), (kernel_ulong_t)&ehl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b7a), (kernel_ulong_t)&ehl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b7b), (kernel_ulong_t)&ehl_i2c_info },
|
||||
/* JSL */
|
||||
{ PCI_VDEVICE(INTEL, 0x4da8), (kernel_ulong_t)&spt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x4da9), (kernel_ulong_t)&spt_uart_info },
|
||||
|
@ -347,6 +352,16 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
|
|||
{ PCI_VDEVICE(INTEL, 0xa36a), (kernel_ulong_t)&cnl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa36b), (kernel_ulong_t)&cnl_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa37b), (kernel_ulong_t)&spt_info },
|
||||
/* CML-V */
|
||||
{ PCI_VDEVICE(INTEL, 0xa3a7), (kernel_ulong_t)&spt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa3a8), (kernel_ulong_t)&spt_uart_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa3a9), (kernel_ulong_t)&spt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa3aa), (kernel_ulong_t)&spt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa3e0), (kernel_ulong_t)&spt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa3e1), (kernel_ulong_t)&spt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa3e2), (kernel_ulong_t)&spt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa3e3), (kernel_ulong_t)&spt_i2c_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa3e6), (kernel_ulong_t)&spt_uart_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, intel_lpss_pci_ids);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -840,7 +840,7 @@ MODULE_DEVICE_TABLE(of, usbhs_omap_dt_ids);
|
|||
|
||||
static struct platform_driver usbhs_omap_driver = {
|
||||
.driver = {
|
||||
.name = (char *)usbhs_driver_name,
|
||||
.name = usbhs_driver_name,
|
||||
.pm = &usbhsomap_dev_pm_ops,
|
||||
.of_match_table = usbhs_omap_dt_ids,
|
||||
},
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
struct usbtll_omap {
|
||||
void __iomem *base;
|
||||
int nch; /* num. of channels */
|
||||
struct clk *ch_clk[0]; /* must be the last member */
|
||||
struct clk *ch_clk[]; /* must be the last member */
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -304,7 +304,7 @@ MODULE_DEVICE_TABLE(of, usbtll_omap_dt_ids);
|
|||
|
||||
static struct platform_driver usbtll_omap_driver = {
|
||||
.driver = {
|
||||
.name = (char *)usbtll_driver_name,
|
||||
.name = usbtll_driver_name,
|
||||
.of_match_table = usbtll_omap_dt_ids,
|
||||
},
|
||||
.probe = usbtll_omap_probe,
|
||||
|
|
|
@ -76,7 +76,7 @@ struct pm_irq_chip {
|
|||
unsigned int num_masters;
|
||||
const struct pm_irq_data *pm_irq_data;
|
||||
/* MUST BE AT THE END OF THIS STRUCT */
|
||||
u8 config[0];
|
||||
u8 config[];
|
||||
};
|
||||
|
||||
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
struct rk808_reg_data {
|
||||
int addr;
|
||||
|
@ -186,7 +185,6 @@ static const struct rk808_reg_data rk805_pre_init_reg[] = {
|
|||
{RK805_BUCK4_CONFIG_REG, RK805_BUCK3_4_ILMAX_MASK,
|
||||
RK805_BUCK4_ILMAX_3500MA},
|
||||
{RK805_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_400MA},
|
||||
{RK805_GPIO_IO_POL_REG, SLP_SD_MSK, SLEEP_FUN},
|
||||
{RK805_THERMAL_REG, TEMP_HOTDIE_MSK, TEMP115C},
|
||||
};
|
||||
|
||||
|
@ -449,88 +447,60 @@ static const struct regmap_irq_chip rk818_irq_chip = {
|
|||
|
||||
static struct i2c_client *rk808_i2c_client;
|
||||
|
||||
static void rk805_device_shutdown(void)
|
||||
static void rk808_pm_power_off(void)
|
||||
{
|
||||
int ret;
|
||||
unsigned int reg, bit;
|
||||
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
|
||||
|
||||
if (!rk808)
|
||||
switch (rk808->variant) {
|
||||
case RK805_ID:
|
||||
reg = RK805_DEV_CTRL_REG;
|
||||
bit = DEV_OFF;
|
||||
break;
|
||||
case RK808_ID:
|
||||
reg = RK808_DEVCTRL_REG,
|
||||
bit = DEV_OFF_RST;
|
||||
break;
|
||||
case RK818_ID:
|
||||
reg = RK818_DEVCTRL_REG;
|
||||
bit = DEV_OFF;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
RK805_DEV_CTRL_REG,
|
||||
DEV_OFF, DEV_OFF);
|
||||
}
|
||||
ret = regmap_update_bits(rk808->regmap, reg, bit, bit);
|
||||
if (ret)
|
||||
dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
|
||||
}
|
||||
|
||||
static void rk805_device_shutdown_prepare(void)
|
||||
static void rk8xx_shutdown(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
|
||||
|
||||
if (!rk808)
|
||||
return;
|
||||
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
RK805_GPIO_IO_POL_REG,
|
||||
SLP_SD_MSK, SHUTDOWN_FUN);
|
||||
if (ret)
|
||||
dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
|
||||
}
|
||||
|
||||
static void rk808_device_shutdown(void)
|
||||
{
|
||||
int ret;
|
||||
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
|
||||
|
||||
if (!rk808)
|
||||
return;
|
||||
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
RK808_DEVCTRL_REG,
|
||||
DEV_OFF_RST, DEV_OFF_RST);
|
||||
if (ret)
|
||||
dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
|
||||
}
|
||||
|
||||
static void rk818_device_shutdown(void)
|
||||
{
|
||||
int ret;
|
||||
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
|
||||
|
||||
if (!rk808)
|
||||
return;
|
||||
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
RK818_DEVCTRL_REG,
|
||||
DEV_OFF, DEV_OFF);
|
||||
if (ret)
|
||||
dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
|
||||
}
|
||||
|
||||
static void rk8xx_syscore_shutdown(void)
|
||||
{
|
||||
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
|
||||
struct rk808 *rk808 = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
if (system_state == SYSTEM_POWER_OFF &&
|
||||
(rk808->variant == RK809_ID || rk808->variant == RK817_ID)) {
|
||||
switch (rk808->variant) {
|
||||
case RK805_ID:
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
RK805_GPIO_IO_POL_REG,
|
||||
SLP_SD_MSK,
|
||||
SHUTDOWN_FUN);
|
||||
break;
|
||||
case RK809_ID:
|
||||
case RK817_ID:
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
RK817_SYS_CFG(3),
|
||||
RK817_SLPPIN_FUNC_MSK,
|
||||
SLPPIN_DN_FUN);
|
||||
if (ret) {
|
||||
dev_warn(&rk808_i2c_client->dev,
|
||||
"Cannot switch to power down function\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (ret)
|
||||
dev_warn(&client->dev,
|
||||
"Cannot switch to power down function\n");
|
||||
}
|
||||
|
||||
static struct syscore_ops rk808_syscore_ops = {
|
||||
.shutdown = rk8xx_syscore_shutdown,
|
||||
};
|
||||
|
||||
static const struct of_device_id rk808_of_match[] = {
|
||||
{ .compatible = "rockchip,rk805" },
|
||||
{ .compatible = "rockchip,rk808" },
|
||||
|
@ -550,7 +520,7 @@ static int rk808_probe(struct i2c_client *client,
|
|||
const struct mfd_cell *cells;
|
||||
int nr_pre_init_regs;
|
||||
int nr_cells;
|
||||
int pm_off = 0, msb, lsb;
|
||||
int msb, lsb;
|
||||
unsigned char pmic_id_msb, pmic_id_lsb;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -594,8 +564,6 @@ static int rk808_probe(struct i2c_client *client,
|
|||
nr_pre_init_regs = ARRAY_SIZE(rk805_pre_init_reg);
|
||||
cells = rk805s;
|
||||
nr_cells = ARRAY_SIZE(rk805s);
|
||||
rk808->pm_pwroff_fn = rk805_device_shutdown;
|
||||
rk808->pm_pwroff_prep_fn = rk805_device_shutdown_prepare;
|
||||
break;
|
||||
case RK808_ID:
|
||||
rk808->regmap_cfg = &rk808_regmap_config;
|
||||
|
@ -604,7 +572,6 @@ static int rk808_probe(struct i2c_client *client,
|
|||
nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
|
||||
cells = rk808s;
|
||||
nr_cells = ARRAY_SIZE(rk808s);
|
||||
rk808->pm_pwroff_fn = rk808_device_shutdown;
|
||||
break;
|
||||
case RK818_ID:
|
||||
rk808->regmap_cfg = &rk818_regmap_config;
|
||||
|
@ -613,7 +580,6 @@ static int rk808_probe(struct i2c_client *client,
|
|||
nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
|
||||
cells = rk818s;
|
||||
nr_cells = ARRAY_SIZE(rk818s);
|
||||
rk808->pm_pwroff_fn = rk818_device_shutdown;
|
||||
break;
|
||||
case RK809_ID:
|
||||
case RK817_ID:
|
||||
|
@ -623,7 +589,6 @@ static int rk808_probe(struct i2c_client *client,
|
|||
nr_pre_init_regs = ARRAY_SIZE(rk817_pre_init_reg);
|
||||
cells = rk817s;
|
||||
nr_cells = ARRAY_SIZE(rk817s);
|
||||
register_syscore_ops(&rk808_syscore_ops);
|
||||
break;
|
||||
default:
|
||||
dev_err(&client->dev, "Unsupported RK8XX ID %lu\n",
|
||||
|
@ -674,17 +639,9 @@ static int rk808_probe(struct i2c_client *client,
|
|||
goto err_irq;
|
||||
}
|
||||
|
||||
pm_off = of_property_read_bool(np,
|
||||
"rockchip,system-power-controller");
|
||||
if (pm_off && !pm_power_off) {
|
||||
if (of_property_read_bool(np, "rockchip,system-power-controller")) {
|
||||
rk808_i2c_client = client;
|
||||
pm_power_off = rk808->pm_pwroff_fn;
|
||||
}
|
||||
|
||||
if (pm_off && !pm_power_off_prepare) {
|
||||
if (!rk808_i2c_client)
|
||||
rk808_i2c_client = client;
|
||||
pm_power_off_prepare = rk808->pm_pwroff_prep_fn;
|
||||
pm_power_off = rk808_pm_power_off;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -704,25 +661,24 @@ static int rk808_remove(struct i2c_client *client)
|
|||
* pm_power_off may points to a function from another module.
|
||||
* Check if the pointer is set by us and only then overwrite it.
|
||||
*/
|
||||
if (rk808->pm_pwroff_fn && pm_power_off == rk808->pm_pwroff_fn)
|
||||
if (pm_power_off == rk808_pm_power_off)
|
||||
pm_power_off = NULL;
|
||||
|
||||
/**
|
||||
* As above, check if the pointer is set by us before overwrite.
|
||||
*/
|
||||
if (rk808->pm_pwroff_prep_fn &&
|
||||
pm_power_off_prepare == rk808->pm_pwroff_prep_fn)
|
||||
pm_power_off_prepare = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rk8xx_suspend(struct device *dev)
|
||||
{
|
||||
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
|
||||
struct rk808 *rk808 = i2c_get_clientdata(to_i2c_client(dev));
|
||||
int ret = 0;
|
||||
|
||||
switch (rk808->variant) {
|
||||
case RK805_ID:
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
RK805_GPIO_IO_POL_REG,
|
||||
SLP_SD_MSK,
|
||||
SLEEP_FUN);
|
||||
break;
|
||||
case RK809_ID:
|
||||
case RK817_ID:
|
||||
ret = regmap_update_bits(rk808->regmap,
|
||||
|
@ -739,7 +695,7 @@ static int __maybe_unused rk8xx_suspend(struct device *dev)
|
|||
|
||||
static int __maybe_unused rk8xx_resume(struct device *dev)
|
||||
{
|
||||
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
|
||||
struct rk808 *rk808 = i2c_get_clientdata(to_i2c_client(dev));
|
||||
int ret = 0;
|
||||
|
||||
switch (rk808->variant) {
|
||||
|
@ -766,6 +722,7 @@ static struct i2c_driver rk808_i2c_driver = {
|
|||
},
|
||||
.probe = rk808_probe,
|
||||
.remove = rk808_remove,
|
||||
.shutdown = rk8xx_shutdown,
|
||||
};
|
||||
|
||||
module_i2c_driver(rk808_i2c_driver);
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/rn5t618.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
|
@ -20,6 +23,13 @@ static const struct mfd_cell rn5t618_cells[] = {
|
|||
{ .name = "rn5t618-wdt" },
|
||||
};
|
||||
|
||||
static const struct mfd_cell rc5t619_cells[] = {
|
||||
{ .name = "rn5t618-adc" },
|
||||
{ .name = "rn5t618-regulator" },
|
||||
{ .name = "rc5t619-rtc" },
|
||||
{ .name = "rn5t618-wdt" },
|
||||
};
|
||||
|
||||
static bool rn5t618_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
|
@ -32,6 +42,8 @@ static bool rn5t618_volatile_reg(struct device *dev, unsigned int reg)
|
|||
case RN5T618_IR_GPF:
|
||||
case RN5T618_MON_IOIN:
|
||||
case RN5T618_INTMON:
|
||||
case RN5T618_RTC_CTRL1 ... RN5T618_RTC_CTRL2:
|
||||
case RN5T618_RTC_SECONDS ... RN5T618_RTC_YEAR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -46,9 +58,56 @@ static const struct regmap_config rn5t618_regmap_config = {
|
|||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct regmap_irq rc5t619_irqs[] = {
|
||||
REGMAP_IRQ_REG(RN5T618_IRQ_SYS, 0, BIT(0)),
|
||||
REGMAP_IRQ_REG(RN5T618_IRQ_DCDC, 0, BIT(1)),
|
||||
REGMAP_IRQ_REG(RN5T618_IRQ_RTC, 0, BIT(2)),
|
||||
REGMAP_IRQ_REG(RN5T618_IRQ_ADC, 0, BIT(3)),
|
||||
REGMAP_IRQ_REG(RN5T618_IRQ_GPIO, 0, BIT(4)),
|
||||
REGMAP_IRQ_REG(RN5T618_IRQ_CHG, 0, BIT(6)),
|
||||
};
|
||||
|
||||
static const struct regmap_irq_chip rc5t619_irq_chip = {
|
||||
.name = "rc5t619",
|
||||
.irqs = rc5t619_irqs,
|
||||
.num_irqs = ARRAY_SIZE(rc5t619_irqs),
|
||||
.num_regs = 1,
|
||||
.status_base = RN5T618_INTMON,
|
||||
.mask_base = RN5T618_INTEN,
|
||||
.mask_invert = true,
|
||||
};
|
||||
|
||||
static struct rn5t618 *rn5t618_pm_power_off;
|
||||
static struct notifier_block rn5t618_restart_handler;
|
||||
|
||||
static int rn5t618_irq_init(struct rn5t618 *rn5t618)
|
||||
{
|
||||
const struct regmap_irq_chip *irq_chip = NULL;
|
||||
int ret;
|
||||
|
||||
if (!rn5t618->irq)
|
||||
return 0;
|
||||
|
||||
switch (rn5t618->variant) {
|
||||
case RC5T619:
|
||||
irq_chip = &rc5t619_irq_chip;
|
||||
break;
|
||||
default:
|
||||
dev_err(rn5t618->dev, "Currently no IRQ support for variant %d\n",
|
||||
(int)rn5t618->variant);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = devm_regmap_add_irq_chip(rn5t618->dev, rn5t618->regmap,
|
||||
rn5t618->irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
0, irq_chip, &rn5t618->irq_data);
|
||||
if (ret)
|
||||
dev_err(rn5t618->dev, "Failed to register IRQ chip\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rn5t618_trigger_poweroff_sequence(bool repower)
|
||||
{
|
||||
/* disable automatic repower-on */
|
||||
|
@ -87,8 +146,7 @@ static const struct of_device_id rn5t618_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, rn5t618_of_match);
|
||||
|
||||
static int rn5t618_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
static int rn5t618_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
struct rn5t618 *priv;
|
||||
|
@ -106,6 +164,8 @@ static int rn5t618_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
i2c_set_clientdata(i2c, priv);
|
||||
priv->variant = (long)of_id->data;
|
||||
priv->irq = i2c->irq;
|
||||
priv->dev = &i2c->dev;
|
||||
|
||||
priv->regmap = devm_regmap_init_i2c(i2c, &rn5t618_regmap_config);
|
||||
if (IS_ERR(priv->regmap)) {
|
||||
|
@ -114,8 +174,16 @@ static int rn5t618_i2c_probe(struct i2c_client *i2c,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_mfd_add_devices(&i2c->dev, -1, rn5t618_cells,
|
||||
ARRAY_SIZE(rn5t618_cells), NULL, 0, NULL);
|
||||
if (priv->variant == RC5T619)
|
||||
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE,
|
||||
rc5t619_cells,
|
||||
ARRAY_SIZE(rc5t619_cells),
|
||||
NULL, 0, NULL);
|
||||
else
|
||||
ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE,
|
||||
rn5t618_cells,
|
||||
ARRAY_SIZE(rn5t618_cells),
|
||||
NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "failed to add sub-devices: %d\n", ret);
|
||||
return ret;
|
||||
|
@ -138,7 +206,7 @@ static int rn5t618_i2c_probe(struct i2c_client *i2c,
|
|||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return rn5t618_irq_init(priv);
|
||||
}
|
||||
|
||||
static int rn5t618_i2c_remove(struct i2c_client *i2c)
|
||||
|
@ -155,19 +223,38 @@ static int rn5t618_i2c_remove(struct i2c_client *i2c)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id rn5t618_i2c_id[] = {
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rn5t618_i2c_id);
|
||||
static int __maybe_unused rn5t618_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct rn5t618 *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv->irq)
|
||||
disable_irq(priv->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rn5t618_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct rn5t618 *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv->irq)
|
||||
enable_irq(priv->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rn5t618_i2c_dev_pm_ops,
|
||||
rn5t618_i2c_suspend,
|
||||
rn5t618_i2c_resume);
|
||||
|
||||
static struct i2c_driver rn5t618_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "rn5t618",
|
||||
.of_match_table = of_match_ptr(rn5t618_of_match),
|
||||
.pm = &rn5t618_i2c_dev_pm_ops,
|
||||
},
|
||||
.probe = rn5t618_i2c_probe,
|
||||
.probe_new = rn5t618_i2c_probe,
|
||||
.remove = rn5t618_i2c_remove,
|
||||
.id_table = rn5t618_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(rn5t618_i2c_driver);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <uapi/linux/usb/charger.h>
|
||||
|
||||
#define SPRD_PMIC_INT_MASK_STATUS 0x0
|
||||
#define SPRD_PMIC_INT_RAW_STATUS 0x4
|
||||
|
@ -17,6 +18,16 @@
|
|||
|
||||
#define SPRD_SC2731_IRQ_BASE 0x140
|
||||
#define SPRD_SC2731_IRQ_NUMS 16
|
||||
#define SPRD_SC2731_CHG_DET 0xedc
|
||||
|
||||
/* PMIC charger detection definition */
|
||||
#define SPRD_PMIC_CHG_DET_DELAY_US 200000
|
||||
#define SPRD_PMIC_CHG_DET_TIMEOUT 2000000
|
||||
#define SPRD_PMIC_CHG_DET_DONE BIT(11)
|
||||
#define SPRD_PMIC_SDP_TYPE BIT(7)
|
||||
#define SPRD_PMIC_DCP_TYPE BIT(6)
|
||||
#define SPRD_PMIC_CDP_TYPE BIT(5)
|
||||
#define SPRD_PMIC_CHG_TYPE_MASK GENMASK(7, 5)
|
||||
|
||||
struct sprd_pmic {
|
||||
struct regmap *regmap;
|
||||
|
@ -24,12 +35,14 @@ struct sprd_pmic {
|
|||
struct regmap_irq *irqs;
|
||||
struct regmap_irq_chip irq_chip;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
const struct sprd_pmic_data *pdata;
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct sprd_pmic_data {
|
||||
u32 irq_base;
|
||||
u32 num_irqs;
|
||||
u32 charger_det;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -40,8 +53,46 @@ struct sprd_pmic_data {
|
|||
static const struct sprd_pmic_data sc2731_data = {
|
||||
.irq_base = SPRD_SC2731_IRQ_BASE,
|
||||
.num_irqs = SPRD_SC2731_IRQ_NUMS,
|
||||
.charger_det = SPRD_SC2731_CHG_DET,
|
||||
};
|
||||
|
||||
enum usb_charger_type sprd_pmic_detect_charger_type(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct sprd_pmic *ddata = spi_get_drvdata(spi);
|
||||
const struct sprd_pmic_data *pdata = ddata->pdata;
|
||||
enum usb_charger_type type;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(ddata->regmap, pdata->charger_det, val,
|
||||
(val & SPRD_PMIC_CHG_DET_DONE),
|
||||
SPRD_PMIC_CHG_DET_DELAY_US,
|
||||
SPRD_PMIC_CHG_DET_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "failed to detect charger type\n");
|
||||
return UNKNOWN_TYPE;
|
||||
}
|
||||
|
||||
switch (val & SPRD_PMIC_CHG_TYPE_MASK) {
|
||||
case SPRD_PMIC_CDP_TYPE:
|
||||
type = CDP_TYPE;
|
||||
break;
|
||||
case SPRD_PMIC_DCP_TYPE:
|
||||
type = DCP_TYPE;
|
||||
break;
|
||||
case SPRD_PMIC_SDP_TYPE:
|
||||
type = SDP_TYPE;
|
||||
break;
|
||||
default:
|
||||
type = UNKNOWN_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sprd_pmic_detect_charger_type);
|
||||
|
||||
static const struct mfd_cell sprd_pmic_devs[] = {
|
||||
{
|
||||
.name = "sc27xx-wdt",
|
||||
|
@ -181,6 +232,7 @@ static int sprd_pmic_probe(struct spi_device *spi)
|
|||
spi_set_drvdata(spi, ddata);
|
||||
ddata->dev = &spi->dev;
|
||||
ddata->irq = spi->irq;
|
||||
ddata->pdata = pdata;
|
||||
|
||||
ddata->irq_chip.name = dev_name(&spi->dev);
|
||||
ddata->irq_chip.status_base =
|
||||
|
|
|
@ -590,6 +590,16 @@ config RTC_DRV_RC5T583
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rc5t583.
|
||||
|
||||
config RTC_DRV_RC5T619
|
||||
tristate "RICOH RC5T619 RTC driver"
|
||||
depends on MFD_RN5T618
|
||||
help
|
||||
If you say yes here you get support for the RTC on the
|
||||
RICOH RC5T619 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rc5t619.
|
||||
|
||||
config RTC_DRV_S35390A
|
||||
tristate "Seiko Instruments S-35390A"
|
||||
select BITREVERSE
|
||||
|
|
|
@ -133,6 +133,7 @@ obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
|
|||
obj-$(CONFIG_RTC_DRV_R7301) += rtc-r7301.o
|
||||
obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
|
||||
obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o
|
||||
obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-rc5t619.o
|
||||
obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
|
||||
obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
|
||||
obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* drivers/rtc/rtc-rc5t619.c
|
||||
*
|
||||
* Real time clock driver for RICOH RC5T619 power management chip.
|
||||
*
|
||||
* Copyright (C) 2019 Andreas Kemnade
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/rn5t618.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
struct rc5t619_rtc {
|
||||
int irq;
|
||||
struct rtc_device *rtc;
|
||||
struct rn5t618 *rn5t618;
|
||||
};
|
||||
|
||||
#define CTRL1_ALARM_ENABLED 0x40
|
||||
#define CTRL1_24HR 0x20
|
||||
#define CTRL1_PERIODIC_MASK 0xf
|
||||
|
||||
#define CTRL2_PON 0x10
|
||||
#define CTRL2_ALARM_STATUS 0x80
|
||||
#define CTRL2_CTFG 0x4
|
||||
#define CTRL2_CTC 0x1
|
||||
|
||||
#define MONTH_CENTFLAG 0x80
|
||||
#define HOUR_PMFLAG 0x20
|
||||
#define MDAY_DAL_EXT 0x80
|
||||
|
||||
static uint8_t rtc5t619_12hour_bcd2bin(uint8_t hour)
|
||||
{
|
||||
if (hour & HOUR_PMFLAG) {
|
||||
hour = bcd2bin(hour & ~HOUR_PMFLAG);
|
||||
return hour == 12 ? 12 : 12 + hour;
|
||||
}
|
||||
|
||||
hour = bcd2bin(hour);
|
||||
return hour == 12 ? 0 : hour;
|
||||
}
|
||||
|
||||
static uint8_t rtc5t619_12hour_bin2bcd(uint8_t hour)
|
||||
{
|
||||
if (!hour)
|
||||
return 0x12;
|
||||
|
||||
if (hour < 12)
|
||||
return bin2bcd(hour);
|
||||
|
||||
if (hour == 12)
|
||||
return 0x12 | HOUR_PMFLAG;
|
||||
|
||||
return bin2bcd(hour - 12) | HOUR_PMFLAG;
|
||||
}
|
||||
|
||||
static int rc5t619_rtc_periodic_disable(struct device *dev)
|
||||
{
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
/* disable function */
|
||||
err = regmap_update_bits(rtc->rn5t618->regmap,
|
||||
RN5T618_RTC_CTRL1, CTRL1_PERIODIC_MASK, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* clear alarm flag and CTFG */
|
||||
err = regmap_update_bits(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2,
|
||||
CTRL2_ALARM_STATUS | CTRL2_CTFG | CTRL2_CTC,
|
||||
0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* things to be done once after power on */
|
||||
static int rc5t619_rtc_pon_setup(struct device *dev)
|
||||
{
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
int err;
|
||||
unsigned int reg_data;
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, ®_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* clear VDET PON */
|
||||
reg_data &= ~(CTRL2_PON | CTRL2_CTC | 0x4a); /* 0101-1011 */
|
||||
reg_data |= 0x20; /* 0010-0000 */
|
||||
err = regmap_write(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, reg_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* clearing RTC Adjust register */
|
||||
err = regmap_write(rtc->rn5t618->regmap, RN5T618_RTC_ADJUST, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return regmap_update_bits(rtc->rn5t618->regmap,
|
||||
RN5T618_RTC_CTRL1,
|
||||
CTRL1_24HR, CTRL1_24HR);
|
||||
}
|
||||
|
||||
static int rc5t619_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
u8 buff[7];
|
||||
int err;
|
||||
int cent_flag;
|
||||
unsigned int ctrl1;
|
||||
unsigned int ctrl2;
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (ctrl2 & CTRL2_PON)
|
||||
return -EINVAL;
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = regmap_bulk_read(rtc->rn5t618->regmap, RN5T618_RTC_SECONDS,
|
||||
buff, sizeof(buff));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (buff[5] & MONTH_CENTFLAG)
|
||||
cent_flag = 1;
|
||||
else
|
||||
cent_flag = 0;
|
||||
|
||||
tm->tm_sec = bcd2bin(buff[0]);
|
||||
tm->tm_min = bcd2bin(buff[1]);
|
||||
|
||||
if (ctrl1 & CTRL1_24HR)
|
||||
tm->tm_hour = bcd2bin(buff[2]);
|
||||
else
|
||||
tm->tm_hour = rtc5t619_12hour_bcd2bin(buff[2]);
|
||||
|
||||
tm->tm_wday = bcd2bin(buff[3]);
|
||||
tm->tm_mday = bcd2bin(buff[4]);
|
||||
tm->tm_mon = bcd2bin(buff[5] & 0x1f) - 1; /* back to system 0-11 */
|
||||
tm->tm_year = bcd2bin(buff[6]) + 100 * cent_flag;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rc5t619_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
u8 buff[7];
|
||||
int err;
|
||||
int cent_flag;
|
||||
unsigned int ctrl1;
|
||||
unsigned int ctrl2;
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (ctrl2 & CTRL2_PON)
|
||||
rc5t619_rtc_pon_setup(dev);
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (tm->tm_year >= 100)
|
||||
cent_flag = 1;
|
||||
else
|
||||
cent_flag = 0;
|
||||
|
||||
buff[0] = bin2bcd(tm->tm_sec);
|
||||
buff[1] = bin2bcd(tm->tm_min);
|
||||
|
||||
if (ctrl1 & CTRL1_24HR)
|
||||
buff[2] = bin2bcd(tm->tm_hour);
|
||||
else
|
||||
buff[2] = rtc5t619_12hour_bin2bcd(tm->tm_hour);
|
||||
|
||||
buff[3] = bin2bcd(tm->tm_wday);
|
||||
buff[4] = bin2bcd(tm->tm_mday);
|
||||
buff[5] = bin2bcd(tm->tm_mon + 1); /* system set 0-11 */
|
||||
buff[6] = bin2bcd(tm->tm_year - cent_flag * 100);
|
||||
|
||||
if (cent_flag)
|
||||
buff[5] |= MONTH_CENTFLAG;
|
||||
|
||||
err = regmap_bulk_write(rtc->rn5t618->regmap, RN5T618_RTC_SECONDS,
|
||||
buff, sizeof(buff));
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to program new time: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 0-disable, 1-enable */
|
||||
static int rc5t619_rtc_alarm_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
|
||||
return regmap_update_bits(rtc->rn5t618->regmap,
|
||||
RN5T618_RTC_CTRL1,
|
||||
CTRL1_ALARM_ENABLED,
|
||||
enabled ? CTRL1_ALARM_ENABLED : 0);
|
||||
}
|
||||
|
||||
static int rc5t619_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
u8 buff[6];
|
||||
unsigned int buff_cent;
|
||||
int err;
|
||||
int cent_flag;
|
||||
unsigned int ctrl1;
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_MONTH, &buff_cent);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to read time: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (buff_cent & MONTH_CENTFLAG)
|
||||
cent_flag = 1;
|
||||
else
|
||||
cent_flag = 0;
|
||||
|
||||
err = regmap_bulk_read(rtc->rn5t618->regmap, RN5T618_RTC_ALARM_Y_SEC,
|
||||
buff, sizeof(buff));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
buff[3] = buff[3] & 0x3f;
|
||||
|
||||
alrm->time.tm_sec = bcd2bin(buff[0]);
|
||||
alrm->time.tm_min = bcd2bin(buff[1]);
|
||||
|
||||
if (ctrl1 & CTRL1_24HR)
|
||||
alrm->time.tm_hour = bcd2bin(buff[2]);
|
||||
else
|
||||
alrm->time.tm_hour = rtc5t619_12hour_bcd2bin(buff[2]);
|
||||
|
||||
alrm->time.tm_mday = bcd2bin(buff[3]);
|
||||
alrm->time.tm_mon = bcd2bin(buff[4]) - 1;
|
||||
alrm->time.tm_year = bcd2bin(buff[5]) + 100 * cent_flag;
|
||||
alrm->enabled = !!(ctrl1 & CTRL1_ALARM_ENABLED);
|
||||
dev_dbg(dev, "read alarm: %ptR\n", &alrm->time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rc5t619_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
u8 buff[6];
|
||||
int err;
|
||||
int cent_flag;
|
||||
unsigned int ctrl1;
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL1, &ctrl1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = rc5t619_rtc_alarm_enable(dev, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (rtc->irq == -1)
|
||||
return -EINVAL;
|
||||
|
||||
if (alrm->enabled == 0)
|
||||
return 0;
|
||||
|
||||
if (alrm->time.tm_year >= 100)
|
||||
cent_flag = 1;
|
||||
else
|
||||
cent_flag = 0;
|
||||
|
||||
alrm->time.tm_mon += 1;
|
||||
buff[0] = bin2bcd(alrm->time.tm_sec);
|
||||
buff[1] = bin2bcd(alrm->time.tm_min);
|
||||
|
||||
if (ctrl1 & CTRL1_24HR)
|
||||
buff[2] = bin2bcd(alrm->time.tm_hour);
|
||||
else
|
||||
buff[2] = rtc5t619_12hour_bin2bcd(alrm->time.tm_hour);
|
||||
|
||||
buff[3] = bin2bcd(alrm->time.tm_mday);
|
||||
buff[4] = bin2bcd(alrm->time.tm_mon);
|
||||
buff[5] = bin2bcd(alrm->time.tm_year - 100 * cent_flag);
|
||||
buff[3] |= MDAY_DAL_EXT;
|
||||
|
||||
err = regmap_bulk_write(rtc->rn5t618->regmap, RN5T618_RTC_ALARM_Y_SEC,
|
||||
buff, sizeof(buff));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return rc5t619_rtc_alarm_enable(dev, alrm->enabled);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops rc5t619_rtc_ops = {
|
||||
.read_time = rc5t619_rtc_read_time,
|
||||
.set_time = rc5t619_rtc_set_time,
|
||||
.set_alarm = rc5t619_rtc_set_alarm,
|
||||
.read_alarm = rc5t619_rtc_read_alarm,
|
||||
.alarm_irq_enable = rc5t619_rtc_alarm_enable,
|
||||
};
|
||||
|
||||
static int rc5t619_rtc_alarm_flag_clr(struct device *dev)
|
||||
{
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
|
||||
/* clear alarm-D status bits.*/
|
||||
return regmap_update_bits(rtc->rn5t618->regmap,
|
||||
RN5T618_RTC_CTRL2,
|
||||
CTRL2_ALARM_STATUS | CTRL2_CTC, 0);
|
||||
}
|
||||
|
||||
static irqreturn_t rc5t619_rtc_irq(int irq, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
struct rc5t619_rtc *rtc = dev_get_drvdata(dev);
|
||||
|
||||
rc5t619_rtc_alarm_flag_clr(dev);
|
||||
|
||||
rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rc5t619_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct rc5t619_rtc *rtc;
|
||||
unsigned int ctrl2;
|
||||
int err;
|
||||
|
||||
rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
|
||||
if (IS_ERR(rtc)) {
|
||||
err = PTR_ERR(rtc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rtc->rn5t618 = rn5t618;
|
||||
|
||||
dev_set_drvdata(dev, rtc);
|
||||
rtc->irq = -1;
|
||||
|
||||
if (rn5t618->irq_data)
|
||||
rtc->irq = regmap_irq_get_virq(rn5t618->irq_data,
|
||||
RN5T618_IRQ_RTC);
|
||||
|
||||
if (rtc->irq < 0)
|
||||
rtc->irq = -1;
|
||||
|
||||
err = regmap_read(rtc->rn5t618->regmap, RN5T618_RTC_CTRL2, &ctrl2);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* disable rtc periodic function */
|
||||
err = rc5t619_rtc_periodic_disable(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ctrl2 & CTRL2_PON) {
|
||||
err = rc5t619_rtc_alarm_flag_clr(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(rtc->rtc)) {
|
||||
err = PTR_ERR(rtc->rtc);
|
||||
dev_err(dev, "RTC device register: err %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
rtc->rtc->ops = &rc5t619_rtc_ops;
|
||||
rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_1900;
|
||||
rtc->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
/* set interrupt and enable it */
|
||||
if (rtc->irq != -1) {
|
||||
err = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
|
||||
rc5t619_rtc_irq,
|
||||
IRQF_ONESHOT,
|
||||
"rtc-rc5t619",
|
||||
&pdev->dev);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq);
|
||||
rtc->irq = -1;
|
||||
|
||||
err = rc5t619_rtc_alarm_enable(&pdev->dev, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
} else {
|
||||
/* enable wake */
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
enable_irq_wake(rtc->irq);
|
||||
}
|
||||
} else {
|
||||
/* system don't want to using alarm interrupt, so close it */
|
||||
err = rc5t619_rtc_alarm_enable(&pdev->dev, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dev_warn(&pdev->dev, "rc5t619 interrupt is disabled\n");
|
||||
}
|
||||
|
||||
return rtc_register_device(rtc->rtc);
|
||||
}
|
||||
|
||||
static struct platform_driver rc5t619_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "rc5t619-rtc",
|
||||
},
|
||||
.probe = rc5t619_rtc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(rc5t619_rtc_driver);
|
||||
MODULE_ALIAS("platform:rc5t619-rtc");
|
||||
MODULE_DESCRIPTION("RICOH RC5T619 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,139 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Azoteq IQS620A/621/622/624/625 Multi-Function Sensors
|
||||
*
|
||||
* Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_IQS62X_H
|
||||
#define __LINUX_MFD_IQS62X_H
|
||||
|
||||
#define IQS620_PROD_NUM 0x41
|
||||
#define IQS621_PROD_NUM 0x46
|
||||
#define IQS622_PROD_NUM 0x42
|
||||
#define IQS624_PROD_NUM 0x43
|
||||
#define IQS625_PROD_NUM 0x4E
|
||||
|
||||
#define IQS621_ALS_FLAGS 0x16
|
||||
#define IQS622_ALS_FLAGS 0x14
|
||||
|
||||
#define IQS624_HALL_UI 0x70
|
||||
#define IQS624_HALL_UI_WHL_EVENT BIT(4)
|
||||
#define IQS624_HALL_UI_INT_EVENT BIT(3)
|
||||
#define IQS624_HALL_UI_AUTO_CAL BIT(2)
|
||||
|
||||
#define IQS624_INTERVAL_DIV 0x7D
|
||||
|
||||
#define IQS620_GLBL_EVENT_MASK 0xD7
|
||||
#define IQS620_GLBL_EVENT_MASK_PMU BIT(6)
|
||||
|
||||
#define IQS62X_NUM_KEYS 16
|
||||
#define IQS62X_NUM_EVENTS (IQS62X_NUM_KEYS + 5)
|
||||
|
||||
#define IQS62X_EVENT_SIZE 10
|
||||
|
||||
enum iqs62x_ui_sel {
|
||||
IQS62X_UI_PROX,
|
||||
IQS62X_UI_SAR1,
|
||||
};
|
||||
|
||||
enum iqs62x_event_reg {
|
||||
IQS62X_EVENT_NONE,
|
||||
IQS62X_EVENT_SYS,
|
||||
IQS62X_EVENT_PROX,
|
||||
IQS62X_EVENT_HYST,
|
||||
IQS62X_EVENT_HALL,
|
||||
IQS62X_EVENT_ALS,
|
||||
IQS62X_EVENT_IR,
|
||||
IQS62X_EVENT_WHEEL,
|
||||
IQS62X_EVENT_INTER,
|
||||
IQS62X_EVENT_UI_LO,
|
||||
IQS62X_EVENT_UI_HI,
|
||||
};
|
||||
|
||||
enum iqs62x_event_flag {
|
||||
/* keys */
|
||||
IQS62X_EVENT_PROX_CH0_T,
|
||||
IQS62X_EVENT_PROX_CH0_P,
|
||||
IQS62X_EVENT_PROX_CH1_T,
|
||||
IQS62X_EVENT_PROX_CH1_P,
|
||||
IQS62X_EVENT_PROX_CH2_T,
|
||||
IQS62X_EVENT_PROX_CH2_P,
|
||||
IQS62X_EVENT_HYST_POS_T,
|
||||
IQS62X_EVENT_HYST_POS_P,
|
||||
IQS62X_EVENT_HYST_NEG_T,
|
||||
IQS62X_EVENT_HYST_NEG_P,
|
||||
IQS62X_EVENT_SAR1_ACT,
|
||||
IQS62X_EVENT_SAR1_QRD,
|
||||
IQS62X_EVENT_SAR1_MOVE,
|
||||
IQS62X_EVENT_SAR1_HALT,
|
||||
IQS62X_EVENT_WHEEL_UP,
|
||||
IQS62X_EVENT_WHEEL_DN,
|
||||
|
||||
/* switches */
|
||||
IQS62X_EVENT_HALL_N_T,
|
||||
IQS62X_EVENT_HALL_N_P,
|
||||
IQS62X_EVENT_HALL_S_T,
|
||||
IQS62X_EVENT_HALL_S_P,
|
||||
|
||||
/* everything else */
|
||||
IQS62X_EVENT_SYS_RESET,
|
||||
};
|
||||
|
||||
struct iqs62x_event_data {
|
||||
u16 ui_data;
|
||||
u8 als_flags;
|
||||
u8 ir_flags;
|
||||
u8 interval;
|
||||
};
|
||||
|
||||
struct iqs62x_event_desc {
|
||||
enum iqs62x_event_reg reg;
|
||||
u8 mask;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
struct iqs62x_dev_desc {
|
||||
const char *dev_name;
|
||||
const struct mfd_cell *sub_devs;
|
||||
int num_sub_devs;
|
||||
|
||||
u8 prod_num;
|
||||
u8 sw_num;
|
||||
const u8 *cal_regs;
|
||||
int num_cal_regs;
|
||||
|
||||
u8 prox_mask;
|
||||
u8 sar_mask;
|
||||
u8 hall_mask;
|
||||
u8 hyst_mask;
|
||||
u8 temp_mask;
|
||||
u8 als_mask;
|
||||
u8 ir_mask;
|
||||
|
||||
u8 prox_settings;
|
||||
u8 als_flags;
|
||||
u8 hall_flags;
|
||||
u8 hyst_shift;
|
||||
|
||||
u8 interval;
|
||||
u8 interval_div;
|
||||
|
||||
u8 clk_div;
|
||||
const char *fw_name;
|
||||
const enum iqs62x_event_reg (*event_regs)[IQS62X_EVENT_SIZE];
|
||||
};
|
||||
|
||||
struct iqs62x_core {
|
||||
const struct iqs62x_dev_desc *dev_desc;
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
struct blocking_notifier_head nh;
|
||||
struct list_head fw_blk_head;
|
||||
struct completion fw_done;
|
||||
enum iqs62x_ui_sel ui_sel;
|
||||
};
|
||||
|
||||
extern const struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS];
|
||||
|
||||
#endif /* __LINUX_MFD_IQS62X_H */
|
|
@ -620,7 +620,5 @@ struct rk808 {
|
|||
long variant;
|
||||
const struct regmap_config *regmap_cfg;
|
||||
const struct regmap_irq_chip *regmap_irq_chip;
|
||||
void (*pm_pwroff_fn)(void);
|
||||
void (*pm_pwroff_prep_fn)(void);
|
||||
};
|
||||
#endif /* __LINUX_REGULATOR_RK808_H */
|
||||
|
|
|
@ -139,6 +139,17 @@
|
|||
#define RN5T618_INTPOL 0x9c
|
||||
#define RN5T618_INTEN 0x9d
|
||||
#define RN5T618_INTMON 0x9e
|
||||
|
||||
#define RN5T618_RTC_SECONDS 0xA0
|
||||
#define RN5T618_RTC_MDAY 0xA4
|
||||
#define RN5T618_RTC_MONTH 0xA5
|
||||
#define RN5T618_RTC_YEAR 0xA6
|
||||
#define RN5T618_RTC_ADJUST 0xA7
|
||||
#define RN5T618_RTC_ALARM_Y_SEC 0xA8
|
||||
#define RN5T618_RTC_DAL_MONTH 0xAC
|
||||
#define RN5T618_RTC_CTRL1 0xAE
|
||||
#define RN5T618_RTC_CTRL2 0xAF
|
||||
|
||||
#define RN5T618_PREVINDAC 0xb0
|
||||
#define RN5T618_BATDAC 0xb1
|
||||
#define RN5T618_CHGCTL1 0xb3
|
||||
|
@ -242,9 +253,24 @@ enum {
|
|||
RC5T619,
|
||||
};
|
||||
|
||||
/* RN5T618 IRQ definitions */
|
||||
enum {
|
||||
RN5T618_IRQ_SYS = 0,
|
||||
RN5T618_IRQ_DCDC,
|
||||
RN5T618_IRQ_RTC,
|
||||
RN5T618_IRQ_ADC,
|
||||
RN5T618_IRQ_GPIO,
|
||||
RN5T618_IRQ_CHG,
|
||||
RN5T618_NR_IRQS,
|
||||
};
|
||||
|
||||
struct rn5t618 {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
long variant;
|
||||
|
||||
int irq;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_RN5T618_H */
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __LINUX_MFD_SC27XX_PMIC_H
|
||||
#define __LINUX_MFD_SC27XX_PMIC_H
|
||||
|
||||
extern enum usb_charger_type sprd_pmic_detect_charger_type(struct device *dev);
|
||||
|
||||
#endif /* __LINUX_MFD_SC27XX_PMIC_H */
|
Loading…
Reference in New Issue