Commit 8645f09b authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents b574beb6 d2923aa4
# 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
...
# 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 {
......
* 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>;
};
# 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>;
};
};
};
};
# 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";
};
};
...
# 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
...
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>;
};
};
# 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
# 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
......
// 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
......
// 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");
# 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
#
# 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
// 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
......
// 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
......
// 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);
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Azoteq IQS620A/621/622/624/625 Multi-Function Sensors
*
* Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
*
* These devices rely on application-specific register settings and calibration
* data developed in and exported from a suite of GUIs offered by the vendor. A
* separate tool converts the GUIs' ASCII-based output into a standard firmware
* file parsed by the driver.
*
* Link to datasheets and GUIs: https://www.azoteq.com/
*
* Link to conversion tool: https://github.com/jlabundy/iqs62x-h2bin.git
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mfd/core.h>
#include <linux/mfd/iqs62x.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#define IQS62X_PROD_NUM 0x00
#define IQS62X_SYS_FLAGS 0x10
#define IQS62X_SYS_FLAGS_IN_ATI BIT(2)
#define IQS620_HALL_FLAGS 0x16
#define IQS621_HALL_FLAGS 0x19
#define IQS622_HALL_FLAGS IQS621_HALL_FLAGS
#define IQS624_INTERVAL_NUM 0x18
#define IQS625_INTERVAL_NUM 0x12
#define IQS622_PROX_SETTINGS_4 0x48
#define IQS620_PROX_SETTINGS_4 0x50
#define IQS620_PROX_SETTINGS_4_SAR_EN BIT(7)
#define IQS621_ALS_CAL_DIV_LUX 0x82
#define IQS621_ALS_CAL_DIV_IR 0x83
#define IQS620_TEMP_CAL_MULT 0xC2
#define IQS620_TEMP_CAL_DIV 0xC3
#define IQS620_TEMP_CAL_OFFS 0xC4
#define IQS62X_SYS_SETTINGS 0xD0
#define IQS62X_SYS_SETTINGS_SOFT_RESET BIT(7)
#define IQS62X_SYS_SETTINGS_ACK_RESET BIT(6)
#define IQS62X_SYS_SETTINGS_EVENT_MODE BIT(5)
#define IQS62X_SYS_SETTINGS_CLK_DIV BIT(4)
#define IQS62X_SYS_SETTINGS_REDO_ATI BIT(1)
#define IQS62X_PWR_SETTINGS 0xD2
#define IQS62X_PWR_SETTINGS_DIS_AUTO BIT(5)
#define IQS62X_PWR_SETTINGS_PWR_MODE_MASK (BIT(4) | BIT(3))
#define IQS62X_PWR_SETTINGS_PWR_MODE_HALT (BIT(4) | BIT(3))
#define IQS62X_PWR_SETTINGS_PWR_MODE_NORM 0
#define IQS62X_OTP_CMD 0xF0
#define IQS62X_OTP_CMD_FG3 0x13
#define IQS62X_OTP_DATA 0xF1
#define IQS62X_MAX_REG 0xFF
#define IQS62X_HALL_CAL_MASK GENMASK(3, 0)
#define IQS62X_FW_REC_TYPE_INFO 0
#define IQS62X_FW_REC_TYPE_PROD 1
#define IQS62X_FW_REC_TYPE_HALL 2
#define IQS62X_FW_REC_TYPE_MASK 3
#define IQS62X_FW_REC_TYPE_DATA 4
#define IQS62X_ATI_POLL_SLEEP_US 10000
#define IQS62X_ATI_POLL_TIMEOUT_US 500000
#define IQS62X_ATI_STABLE_DELAY_MS 150
struct iqs62x_fw_rec {
u8 type;
u8 addr;
u8 len;
u8 data;
} __packed;
struct iqs62x_fw_blk {
struct list_head list;
u8 addr;
u8 mask;
u8 len;
u8 data[];
};
struct iqs62x_info {
u8 prod_num;
u8 sw_num;
u8 hw_num;
} __packed;
static int iqs62x_dev_init(struct iqs62x_core *iqs62x)
{
struct iqs62x_fw_blk *fw_blk;
unsigned int val;
int ret;
u8 clk_div = 1;
list_for_each_entry(fw_blk, &iqs62x->fw_blk_head, list) {
if (fw_blk->mask)
ret = regmap_update_bits(iqs62x->regmap, fw_blk->addr,
fw_blk->mask, *fw_blk->data);
else
ret = regmap_raw_write(iqs62x->regmap, fw_blk->addr,
fw_blk->data, fw_blk->len);
if (ret)
return ret;
}
switch (iqs62x->dev_desc->prod_num) {
case IQS620_PROD_NUM:
case IQS622_PROD_NUM:
ret = regmap_read(iqs62x->regmap,
iqs62x->dev_desc->prox_settings, &val);
if (ret)
return ret;
if (val & IQS620_PROX_SETTINGS_4_SAR_EN)
iqs62x->ui_sel = IQS62X_UI_SAR1;
/* fall through */
case IQS621_PROD_NUM:
ret = regmap_write(iqs62x->regmap, IQS620_GLBL_EVENT_MASK,
IQS620_GLBL_EVENT_MASK_PMU |
iqs62x->dev_desc->prox_mask |
iqs62x->dev_desc->sar_mask |
iqs62x->dev_desc->hall_mask |
iqs62x->dev_desc->hyst_mask |
iqs62x->dev_desc->temp_mask |
iqs62x->dev_desc->als_mask |
iqs62x->dev_desc->ir_mask);
if (ret)
return ret;
break;
default:
ret = regmap_write(iqs62x->regmap, IQS624_HALL_UI,
IQS624_HALL_UI_WHL_EVENT |
IQS624_HALL_UI_INT_EVENT |
IQS624_HALL_UI_AUTO_CAL);
if (ret)
return ret;
/*
* The IQS625 default interval divider is below the minimum
* permissible value, and the datasheet mandates that it is
* corrected during initialization (unless an updated value
* has already been provided by firmware).
*
* To protect against an unacceptably low user-entered value
* stored in the firmware, the same check is extended to the
* IQS624 as well.
*/
ret = regmap_read(iqs62x->regmap, IQS624_INTERVAL_DIV, &val);
if (ret)
return ret;
if (val >= iqs62x->dev_desc->interval_div)
break;
ret = regmap_write(iqs62x->regmap, IQS624_INTERVAL_DIV,
iqs62x->dev_desc->interval_div);
if (ret)
return ret;
}
ret = regmap_read(iqs62x->regmap, IQS62X_SYS_SETTINGS, &val);
if (ret)
return ret;
if (val & IQS62X_SYS_SETTINGS_CLK_DIV)
clk_div = iqs62x->dev_desc->clk_div;
ret = regmap_write(iqs62x->regmap, IQS62X_SYS_SETTINGS, val |
IQS62X_SYS_SETTINGS_ACK_RESET |
IQS62X_SYS_SETTINGS_EVENT_MODE |
IQS62X_SYS_SETTINGS_REDO_ATI);
if (ret)
return ret;
ret = regmap_read_poll_timeout(iqs62x->regmap, IQS62X_SYS_FLAGS, val,
!(val & IQS62X_SYS_FLAGS_IN_ATI),
IQS62X_ATI_POLL_SLEEP_US,
IQS62X_ATI_POLL_TIMEOUT_US * clk_div);
if (ret)
return ret;
msleep(IQS62X_ATI_STABLE_DELAY_MS * clk_div);
return 0;
}
static int iqs62x_firmware_parse(struct iqs62x_core *iqs62x,
const struct firmware *fw)
{
struct i2c_client *client = iqs62x->client;
struct iqs62x_fw_rec *fw_rec;
struct iqs62x_fw_blk *fw_blk;
unsigned int val;
size_t pos = 0;
int ret = 0;
u8 mask, len, *data;
u8 hall_cal_index = 0;
while (pos < fw->size) {
if (pos + sizeof(*fw_rec) > fw->size) {
ret = -EINVAL;
break;
}
fw_rec = (struct iqs62x_fw_rec *)(fw->data + pos);
pos += sizeof(*fw_rec);
if (pos + fw_rec->len - 1 > fw->size) {
ret = -EINVAL;
break;
}
pos += fw_rec->len - 1;
switch (fw_rec->type) {
case IQS62X_FW_REC_TYPE_INFO:
continue;
case IQS62X_FW_REC_TYPE_PROD:
if (fw_rec->data == iqs62x->dev_desc->prod_num)
continue;
dev_err(&client->dev,
"Incompatible product number: 0x%02X\n",
fw_rec->data);
ret = -EINVAL;
break;
case IQS62X_FW_REC_TYPE_HALL:
if (!hall_cal_index) {
ret = regmap_write(iqs62x->regmap,
IQS62X_OTP_CMD,
IQS62X_OTP_CMD_FG3);
if (ret)
break;
ret = regmap_read(iqs62x->regmap,
IQS62X_OTP_DATA, &val);
if (ret)
break;
hall_cal_index = val & IQS62X_HALL_CAL_MASK;
if (!hall_cal_index) {
dev_err(&client->dev,
"Uncalibrated device\n");
ret = -ENODATA;
break;
}
}
if (hall_cal_index > fw_rec->len) {
ret = -EINVAL;
break;
}
mask = 0;
data = &fw_rec->data + hall_cal_index - 1;
len = sizeof(*data);
break;
case IQS62X_FW_REC_TYPE_MASK:
if (fw_rec->len < (sizeof(mask) + sizeof(*data))) {
ret = -EINVAL;
break;
}
mask = fw_rec->data;
data = &fw_rec->data + sizeof(mask);
len = sizeof(*data);
break;
case IQS62X_FW_REC_TYPE_DATA:
mask = 0;
data = &fw_rec->data;
len = fw_rec->len;
break;
default:
dev_err(&client->dev,
"Unrecognized record type: 0x%02X\n",
fw_rec->type);
ret = -EINVAL;
}
if (ret)
break;
fw_blk = devm_kzalloc(&client->dev,
struct_size(fw_blk, data, len),
GFP_KERNEL);
if (!fw_blk) {
ret = -ENOMEM;
break;
}
fw_blk->addr = fw_rec->addr;
fw_blk->mask = mask;
fw_blk->len = len;
memcpy(fw_blk->data, data, len);
list_add(&fw_blk->list, &iqs62x->fw_blk_head);
}
release_firmware(fw);
return ret;
}
const struct iqs62x_event_desc iqs62x_events[IQS62X_NUM_EVENTS] = {
[IQS62X_EVENT_PROX_CH0_T] = {
.reg = IQS62X_EVENT_PROX,
.mask = BIT(4),
.val = BIT(4),
},
[IQS62X_EVENT_PROX_CH0_P] = {
.reg = IQS62X_EVENT_PROX,
.mask = BIT(0),
.val = BIT(0),
},
[IQS62X_EVENT_PROX_CH1_T] = {
.reg = IQS62X_EVENT_PROX,
.mask = BIT(5),
.val = BIT(5),
},
[IQS62X_EVENT_PROX_CH1_P] = {
.reg = IQS62X_EVENT_PROX,
.mask = BIT(1),
.val = BIT(1),
},
[IQS62X_EVENT_PROX_CH2_T] = {
.reg = IQS62X_EVENT_PROX,
.mask = BIT(6),
.val = BIT(6),
},
[IQS62X_EVENT_PROX_CH2_P] = {
.reg = IQS62X_EVENT_PROX,
.mask = BIT(2),
.val = BIT(2),
},
[IQS62X_EVENT_HYST_POS_T] = {
.reg = IQS62X_EVENT_HYST,
.mask = BIT(6) | BIT(7),
.val = BIT(6),
},
[IQS62X_EVENT_HYST_POS_P] = {
.reg = IQS62X_EVENT_HYST,
.mask = BIT(5) | BIT(7),
.val = BIT(5),
},
[IQS62X_EVENT_HYST_NEG_T] = {
.reg = IQS62X_EVENT_HYST,
.mask = BIT(6) | BIT(7),
.val = BIT(6) | BIT(7),
},
[IQS62X_EVENT_HYST_NEG_P] = {
.reg = IQS62X_EVENT_HYST,
.mask = BIT(5) | BIT(7),
.val = BIT(5) | BIT(7),
},
[IQS62X_EVENT_SAR1_ACT] = {
.reg = IQS62X_EVENT_HYST,
.mask = BIT(4),
.val = BIT(4),
},
[IQS62X_EVENT_SAR1_QRD] = {
.reg = IQS62X_EVENT_HYST,
.mask = BIT(2),
.val = BIT(2),
},
[IQS62X_EVENT_SAR1_MOVE] = {
.reg = IQS62X_EVENT_HYST,
.mask = BIT(1),
.val = BIT(1),
},
[IQS62X_EVENT_SAR1_HALT] = {
.reg = IQS62X_EVENT_HYST,
.mask = BIT(0),
.val = BIT(0),
},
[IQS62X_EVENT_WHEEL_UP] = {
.reg = IQS62X_EVENT_WHEEL,
.mask = BIT(7) | BIT(6),
.val = BIT(7),
},
[IQS62X_EVENT_WHEEL_DN] = {
.reg = IQS62X_EVENT_WHEEL,
.mask = BIT(7) | BIT(6),
.val = BIT(7) | BIT(6),
},
[IQS62X_EVENT_HALL_N_T] = {
.reg = IQS62X_EVENT_HALL,
.mask = BIT(2) | BIT(0),
.val = BIT(2),
},
[IQS62X_EVENT_HALL_N_P] = {
.reg = IQS62X_EVENT_HALL,
.mask = BIT(1) | BIT(0),
.val = BIT(1),
},
[IQS62X_EVENT_HALL_S_T] = {
.reg = IQS62X_EVENT_HALL,
.mask = BIT(2) | BIT(0),
.val = BIT(2) | BIT(0),
},
[IQS62X_EVENT_HALL_S_P] = {
.reg = IQS62X_EVENT_HALL,
.mask = BIT(1) | BIT(0),
.val = BIT(1) | BIT(0),
},
[IQS62X_EVENT_SYS_RESET] = {
.reg = IQS62X_EVENT_SYS,
.mask = BIT(7),
.val = BIT(7),
},
};
EXPORT_SYMBOL_GPL(iqs62x_events);
static irqreturn_t iqs62x_irq(int irq, void *context)
{
struct iqs62x_core *iqs62x = context;
struct i2c_client *client = iqs62x->client;
struct iqs62x_event_data event_data;
struct iqs62x_event_desc event_desc;
enum iqs62x_event_reg event_reg;
unsigned long event_flags = 0;
int ret, i, j;
u8 event_map[IQS62X_EVENT_SIZE];
/*
* The device asserts the RDY output to signal the beginning of a
* communication window, which is closed by an I2C stop condition.
* As such, all interrupt status is captured in a single read and
* broadcast to any interested sub-device drivers.
*/
ret = regmap_raw_read(iqs62x->regmap, IQS62X_SYS_FLAGS, event_map,
sizeof(event_map));
if (ret) {
dev_err(&client->dev, "Failed to read device status: %d\n",
ret);
return IRQ_NONE;
}
for (i = 0; i < sizeof(event_map); i++) {
event_reg = iqs62x->dev_desc->event_regs[iqs62x->ui_sel][i];
switch (event_reg) {
case IQS62X_EVENT_UI_LO:
event_data.ui_data = get_unaligned_le16(&event_map[i]);
/* fall through */
case IQS62X_EVENT_UI_HI:
case IQS62X_EVENT_NONE:
continue;
case IQS62X_EVENT_ALS:
event_data.als_flags = event_map[i];
continue;
case IQS62X_EVENT_IR:
event_data.ir_flags = event_map[i];
continue;
case IQS62X_EVENT_INTER:
event_data.interval = event_map[i];
continue;
case IQS62X_EVENT_HYST:
event_map[i] <<= iqs62x->dev_desc->hyst_shift;
/* fall through */
case IQS62X_EVENT_WHEEL:
case IQS62X_EVENT_HALL:
case IQS62X_EVENT_PROX:
case IQS62X_EVENT_SYS:
break;
}
for (j = 0; j < IQS62X_NUM_EVENTS; j++) {
event_desc = iqs62x_events[j];
if (event_desc.reg != event_reg)
continue;
if ((event_map[i] & event_desc.mask) == event_desc.val)
event_flags |= BIT(j);
}
}
/*
* The device resets itself in response to the I2C master stalling
* communication past a fixed timeout. In this case, all registers
* are restored and any interested sub-device drivers are notified.
*/
if (event_flags & BIT(IQS62X_EVENT_SYS_RESET)) {
dev_err(&client->dev, "Unexpected device reset\n");
ret = iqs62x_dev_init(iqs62x);
if (ret) {
dev_err(&client->dev,
"Failed to re-initialize device: %d\n", ret);
return IRQ_NONE;
}
}
ret = blocking_notifier_call_chain(&iqs62x->nh, event_flags,
&event_data);
if (ret & NOTIFY_STOP_MASK)
return IRQ_NONE;
/*
* Once the communication window is closed, a small delay is added to
* ensure the device's RDY output has been deasserted by the time the
* interrupt handler returns.
*/
usleep_range(50, 100);
return IRQ_HANDLED;
}
static void iqs62x_firmware_load(const struct firmware *fw, void *context)
{
struct iqs62x_core *iqs62x = context;
struct i2c_client *client = iqs62x->client;
int ret;
if (fw) {
ret = iqs62x_firmware_parse(iqs62x, fw);
if (ret) {
dev_err(&client->dev, "Failed to parse firmware: %d\n",
ret);
goto err_out;
}
}
ret = iqs62x_dev_init(iqs62x);
if (ret) {
dev_err(&client->dev, "Failed to initialize device: %d\n", ret);
goto err_out;
}
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, iqs62x_irq, IRQF_ONESHOT,
client->name, iqs62x);
if (ret) {
dev_err(&client->dev, "Failed to request IRQ: %d\n", ret);
goto err_out;
}
ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
iqs62x->dev_desc->sub_devs,
iqs62x->dev_desc->num_sub_devs,
NULL, 0, NULL);
if (ret)
dev_err(&client->dev, "Failed to add sub-devices: %d\n", ret);
err_out:
complete_all(&iqs62x->fw_done);
}
static const struct mfd_cell iqs620at_sub_devs[] = {
{
.name = "iqs62x-keys",
.of_compatible = "azoteq,iqs620a-keys",
},
{
.name = "iqs620a-pwm",
.of_compatible = "azoteq,iqs620a-pwm",
},
{ .name = "iqs620at-temp", },
};
static const struct mfd_cell iqs620a_sub_devs[] = {
{
.name = "iqs62x-keys",
.of_compatible = "azoteq,iqs620a-keys",
},
{
.name = "iqs620a-pwm",
.of_compatible = "azoteq,iqs620a-pwm",
},
};
static const struct mfd_cell iqs621_sub_devs[] = {
{
.name = "iqs62x-keys",
.of_compatible = "azoteq,iqs621-keys",
},
{ .name = "iqs621-als", },
};
static const struct mfd_cell iqs622_sub_devs[] = {
{
.name = "iqs62x-keys",
.of_compatible = "azoteq,iqs622-keys",
},
{ .name = "iqs621-als", },
};
static const struct mfd_cell iqs624_sub_devs[] = {
{
.name = "iqs62x-keys",
.of_compatible = "azoteq,iqs624-keys",
},
{ .name = "iqs624-pos", },
};
static const struct mfd_cell iqs625_sub_devs[] = {
{
.name = "iqs62x-keys",
.of_compatible = "azoteq,iqs625-keys",
},
{ .name = "iqs624-pos", },
};
static const u8 iqs620at_cal_regs[] = {
IQS620_TEMP_CAL_MULT,
IQS620_TEMP_CAL_DIV,
IQS620_TEMP_CAL_OFFS,
};
static const u8 iqs621_cal_regs[] = {
IQS621_ALS_CAL_DIV_LUX,
IQS621_ALS_CAL_DIV_IR,
};
static const enum iqs62x_event_reg iqs620a_event_regs[][IQS62X_EVENT_SIZE] = {
[IQS62X_UI_PROX] = {
IQS62X_EVENT_SYS, /* 0x10 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_PROX, /* 0x12 */
IQS62X_EVENT_HYST, /* 0x13 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_HALL, /* 0x16 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
},
[IQS62X_UI_SAR1] = {
IQS62X_EVENT_SYS, /* 0x10 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_HYST, /* 0x13 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_HALL, /* 0x16 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
},
};
static const enum iqs62x_event_reg iqs621_event_regs[][IQS62X_EVENT_SIZE] = {
[IQS62X_UI_PROX] = {
IQS62X_EVENT_SYS, /* 0x10 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_PROX, /* 0x12 */
IQS62X_EVENT_HYST, /* 0x13 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_ALS, /* 0x16 */
IQS62X_EVENT_UI_LO, /* 0x17 */
IQS62X_EVENT_UI_HI, /* 0x18 */
IQS62X_EVENT_HALL, /* 0x19 */
},
};
static const enum iqs62x_event_reg iqs622_event_regs[][IQS62X_EVENT_SIZE] = {
[IQS62X_UI_PROX] = {
IQS62X_EVENT_SYS, /* 0x10 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_PROX, /* 0x12 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_ALS, /* 0x14 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_IR, /* 0x16 */
IQS62X_EVENT_UI_LO, /* 0x17 */
IQS62X_EVENT_UI_HI, /* 0x18 */
IQS62X_EVENT_HALL, /* 0x19 */
},
[IQS62X_UI_SAR1] = {
IQS62X_EVENT_SYS, /* 0x10 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_HYST, /* 0x13 */
IQS62X_EVENT_ALS, /* 0x14 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_IR, /* 0x16 */
IQS62X_EVENT_UI_LO, /* 0x17 */
IQS62X_EVENT_UI_HI, /* 0x18 */
IQS62X_EVENT_HALL, /* 0x19 */
},
};
static const enum iqs62x_event_reg iqs624_event_regs[][IQS62X_EVENT_SIZE] = {
[IQS62X_UI_PROX] = {
IQS62X_EVENT_SYS, /* 0x10 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_PROX, /* 0x12 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_WHEEL, /* 0x14 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_UI_LO, /* 0x16 */
IQS62X_EVENT_UI_HI, /* 0x17 */
IQS62X_EVENT_INTER, /* 0x18 */
IQS62X_EVENT_NONE,
},
};
static const enum iqs62x_event_reg iqs625_event_regs[][IQS62X_EVENT_SIZE] = {
[IQS62X_UI_PROX] = {
IQS62X_EVENT_SYS, /* 0x10 */
IQS62X_EVENT_PROX, /* 0x11 */
IQS62X_EVENT_INTER, /* 0x12 */
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
IQS62X_EVENT_NONE,
},
};
static const struct iqs62x_dev_desc iqs62x_devs[] = {
{
.dev_name = "iqs620at",
.sub_devs = iqs620at_sub_devs,
.num_sub_devs = ARRAY_SIZE(iqs620at_sub_devs),
.prod_num = IQS620_PROD_NUM,
.sw_num = 0x08,
.cal_regs = iqs620at_cal_regs,
.num_cal_regs = ARRAY_SIZE(iqs620at_cal_regs),
.prox_mask = BIT(0),
.sar_mask = BIT(1) | BIT(7),
.hall_mask = BIT(2),
.hyst_mask = BIT(3),
.temp_mask = BIT(4),
.prox_settings = IQS620_PROX_SETTINGS_4,
.hall_flags = IQS620_HALL_FLAGS,
.clk_div = 4,
.fw_name = "iqs620a.bin",
.event_regs = &iqs620a_event_regs[IQS62X_UI_PROX],
},
{
.dev_name = "iqs620a",
.sub_devs = iqs620a_sub_devs,
.num_sub_devs = ARRAY_SIZE(iqs620a_sub_devs),
.prod_num = IQS620_PROD_NUM,
.sw_num = 0x08,
.prox_mask = BIT(0),
.sar_mask = BIT(1) | BIT(7),
.hall_mask = BIT(2),
.hyst_mask = BIT(3),
.temp_mask = BIT(4),
.prox_settings = IQS620_PROX_SETTINGS_4,
.hall_flags = IQS620_HALL_FLAGS,
.clk_div = 4,
.fw_name = "iqs620a.bin",
.event_regs = &iqs620a_event_regs[IQS62X_UI_PROX],
},
{
.dev_name = "iqs621",
.sub_devs = iqs621_sub_devs,
.num_sub_devs = ARRAY_SIZE(iqs621_sub_devs),
.prod_num = IQS621_PROD_NUM,
.sw_num = 0x09,
.cal_regs = iqs621_cal_regs,
.num_cal_regs = ARRAY_SIZE(iqs621_cal_regs),
.prox_mask = BIT(0),
.hall_mask = BIT(1),
.als_mask = BIT(2),
.hyst_mask = BIT(3),
.temp_mask = BIT(4),
.als_flags = IQS621_ALS_FLAGS,
.hall_flags = IQS621_HALL_FLAGS,
.hyst_shift = 5,
.clk_div = 2,
.fw_name = "iqs621.bin",
.event_regs = &iqs621_event_regs[IQS62X_UI_PROX],
},
{
.dev_name = "iqs622",
.sub_devs = iqs622_sub_devs,
.num_sub_devs = ARRAY_SIZE(iqs622_sub_devs),
.prod_num = IQS622_PROD_NUM,
.sw_num = 0x06,
.prox_mask = BIT(0),
.sar_mask = BIT(1),
.hall_mask = BIT(2),
.als_mask = BIT(3),
.ir_mask = BIT(4),
.prox_settings = IQS622_PROX_SETTINGS_4,
.als_flags = IQS622_ALS_FLAGS,
.hall_flags = IQS622_HALL_FLAGS,
.clk_div = 2,
.fw_name = "iqs622.bin",
.event_regs = &iqs622_event_regs[IQS62X_UI_PROX],
},
{
.dev_name = "iqs624",
.sub_devs = iqs624_sub_devs,
.num_sub_devs = ARRAY_SIZE(iqs624_sub_devs),
.prod_num = IQS624_PROD_NUM,
.sw_num = 0x0B,
.interval = IQS624_INTERVAL_NUM,
.interval_div = 3,
.clk_div = 2,
.fw_name = "iqs624.bin",
.event_regs = &iqs624_event_regs[IQS62X_UI_PROX],
},
{
.dev_name = "iqs625",
.sub_devs = iqs625_sub_devs,
.num_sub_devs = ARRAY_SIZE(iqs625_sub_devs),
.prod_num = IQS625_PROD_NUM,
.sw_num = 0x0B,
.interval = IQS625_INTERVAL_NUM,
.interval_div = 10,
.clk_div = 2,
.fw_name = "iqs625.bin",
.event_regs = &iqs625_event_regs[IQS62X_UI_PROX],
},
};
static const struct regmap_config iqs62x_map_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = IQS62X_MAX_REG,
};
static int iqs62x_probe(struct i2c_client *client)
{
struct iqs62x_core *iqs62x;
struct iqs62x_info info;
unsigned int val;
int ret, i, j;
u8 sw_num = 0;
const char *fw_name = NULL;
iqs62x = devm_kzalloc(&client->dev, sizeof(*iqs62x), GFP_KERNEL);
if (!iqs62x)
return -ENOMEM;
i2c_set_clientdata(client, iqs62x);
iqs62x->client = client;
BLOCKING_INIT_NOTIFIER_HEAD(&iqs62x->nh);
INIT_LIST_HEAD(&iqs62x->fw_blk_head);
init_completion(&iqs62x->fw_done);
iqs62x->regmap = devm_regmap_init_i2c(client, &iqs62x_map_config);
if (IS_ERR(iqs62x->regmap)) {
ret = PTR_ERR(iqs62x->regmap);
dev_err(&client->dev, "Failed to initialize register map: %d\n",
ret);
return ret;
}
ret = regmap_raw_read(iqs62x->regmap, IQS62X_PROD_NUM, &info,
sizeof(info));
if (ret)
return ret;
/*
* The following sequence validates the device's product and software
* numbers. It then determines if the device is factory-calibrated by
* checking for nonzero values in the device's designated calibration
* registers (if applicable). Depending on the device, the absence of
* calibration data indicates a reduced feature set or invalid device.
*
* For devices given in both calibrated and uncalibrated versions, the
* calibrated version (e.g. IQS620AT) appears first in the iqs62x_devs
* array. The uncalibrated version (e.g. IQS620A) appears next and has
* the same product and software numbers, but no calibration registers
* are specified.
*/
for (i = 0; i < ARRAY_SIZE(iqs62x_devs); i++) {
if (info.prod_num != iqs62x_devs[i].prod_num)
continue;
iqs62x->dev_desc = &iqs62x_devs[i];
if (info.sw_num < iqs62x->dev_desc->sw_num)
continue;
sw_num = info.sw_num;
/*
* Read each of the device's designated calibration registers,
* if any, and exit from the inner loop early if any are equal
* to zero (indicating the device is uncalibrated). This could
* be acceptable depending on the device (e.g. IQS620A instead
* of IQS620AT).
*/
for (j = 0; j < iqs62x->dev_desc->num_cal_regs; j++) {
ret = regmap_read(iqs62x->regmap,
iqs62x->dev_desc->cal_regs[j], &val);
if (ret)
return ret;
if (!val)
break;
}
/*
* If the number of nonzero values read from the device equals
* the number of designated calibration registers (which could
* be zero), exit from the outer loop early to signal that the
* device's product and software numbers match a known device,
* and the device is calibrated (if applicable).
*/
if (j == iqs62x->dev_desc->num_cal_regs)
break;
}
if (!iqs62x->dev_desc) {
dev_err(&client->dev, "Unrecognized product number: 0x%02X\n",
info.prod_num);
return -EINVAL;
}
if (!sw_num) {
dev_err(&client->dev, "Unrecognized software number: 0x%02X\n",
info.sw_num);
return -EINVAL;
}
if (i == ARRAY_SIZE(iqs62x_devs)) {
dev_err(&client->dev, "Uncalibrated device\n");
return -ENODATA;
}
device_property_read_string(&client->dev, "firmware-name", &fw_name);
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
fw_name ? : iqs62x->dev_desc->fw_name,
&client->dev, GFP_KERNEL, iqs62x,
iqs62x_firmware_load);
if (ret)
dev_err(&client->dev, "Failed to request firmware: %d\n", ret);
return ret;
}
static int iqs62x_remove(struct i2c_client *client)
{
struct iqs62x_core *iqs62x = i2c_get_clientdata(client);
wait_for_completion(&iqs62x->fw_done);
return 0;
}
static int __maybe_unused iqs62x_suspend(struct device *dev)
{
struct iqs62x_core *iqs62x = dev_get_drvdata(dev);
int ret;
wait_for_completion(&iqs62x->fw_done);
/*
* As per the datasheet, automatic mode switching must be disabled
* before the device is placed in or taken out of halt mode.
*/
ret = regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS,
IQS62X_PWR_SETTINGS_DIS_AUTO, 0xFF);
if (ret)
return ret;
return regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS,
IQS62X_PWR_SETTINGS_PWR_MODE_MASK,
IQS62X_PWR_SETTINGS_PWR_MODE_HALT);
}
static int __maybe_unused iqs62x_resume(struct device *dev)
{
struct iqs62x_core *iqs62x = dev_get_drvdata(dev);
int ret;
ret = regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS,
IQS62X_PWR_SETTINGS_PWR_MODE_MASK,
IQS62X_PWR_SETTINGS_PWR_MODE_NORM);
if (ret)
return ret;
return regmap_update_bits(iqs62x->regmap, IQS62X_PWR_SETTINGS,
IQS62X_PWR_SETTINGS_DIS_AUTO, 0);
}
static SIMPLE_DEV_PM_OPS(iqs62x_pm, iqs62x_suspend, iqs62x_resume);
static const struct of_device_id iqs62x_of_match[] = {
{ .compatible = "azoteq,iqs620a" },
{ .compatible = "azoteq,iqs621" },
{ .compatible = "azoteq,iqs622" },
{ .compatible = "azoteq,iqs624" },
{ .compatible = "azoteq,iqs625" },
{ }
};
MODULE_DEVICE_TABLE(of, iqs62x_of_match);
static struct i2c_driver iqs62x_i2c_driver = {
.driver = {
.name = "iqs62x",
.of_match_table = iqs62x_of_match,
.pm = &iqs62x_pm,
},
.probe_new = iqs62x_probe,
.remove = iqs62x_remove,
};
module_i2c_driver(iqs62x_i2c_driver);
MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
MODULE_DESCRIPTION("Azoteq IQS620A/621/622/624/625 Multi-Function Sensors");
MODULE_LICENSE("GPL");
......@@ -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)
{
struct rk808 *rk808 = i2c_get_clientdata(client);
int ret;
struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
if (!rk808)
return;
switch (rk808->variant) {
case RK805_ID:
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);
int ret;
if (system_state == SYSTEM_POWER_OFF &&
(rk808->variant == RK809_ID || rk808->variant == RK817_ID)) {
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) {
rk808_i2c_client = client;
pm_power_off = rk808->pm_pwroff_fn;
}
if (pm_off && !pm_power_off_prepare) {
if (!rk808_i2c_client)
if (of_property_read_bool(np, "rockchip,system-power-controller")) {
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
......
// 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, &reg_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");
/* 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 */
/* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment