From: ChiaEn Wu chiaen_wu@richtek.com
Sorry, for the patch dependency, we resend this patch series and we're sorry for any inconvenience that we may have caused. This patch series add Mediatek MT6370 PMIC support. The MT6370 is a highly-integrated smart power management IC, which includes a single cell Li-Ion/Li-Polymer switching battery charger, a USB Type-C & Power Delivery (PD) controller, dual Flash LED current sources, a RGB LED driver, a backlight WLED driver, a display bias driver and a general LDO for portable devices.
Alice Chen (3): dt-bindings: leds: Add Mediatek MT6370 flashlight binding leds: mt6370: Add Mediatek MT6370 Indicator support leds: flashlight: mt6370: Add Mediatek MT6370 flashlight support
ChiYuan Huang (7): dt-bindings: usb: Add Mediatek MT6370 TCPC binding dt-bindings: leds: mt6370: Add Mediatek mt6370 indicator dt-bindings: backlight: Add Mediatek MT6370 backlight binding dt-bindings: mfd: Add Mediatek MT6370 binding mfd: mt6370: Add Mediatek MT6370 support usb: typec: tcpci_mt6370: Add Mediatek MT6370 tcpci driver regulator: mt6370: Add mt6370 DisplayBias and VibLDO support
ChiaEn Wu (4): dt-bindings: power: supply: Add Mediatek MT6370 Charger binding iio: adc: mt6370: Add Mediatek MT6370 support power: supply: mt6370: Add Mediatek MT6370 charger driver video: backlight: mt6370: Add Mediatek MT6370 support
.../backlight/mediatek,mt6370-backlight.yaml | 110 ++ .../leds/mediatek,mt6370-flashlight.yaml | 48 + .../leds/mediatek,mt6370-indicator.yaml | 57 + .../bindings/mfd/mediatek,mt6370.yaml | 282 ++++ .../power/supply/mediatek,mt6370-charger.yaml | 60 + .../bindings/usb/mediatek,mt6370-tcpc.yaml | 35 + drivers/iio/adc/Kconfig | 9 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/mt6370-adc.c | 257 ++++ drivers/leds/Kconfig | 11 + drivers/leds/Makefile | 1 + drivers/leds/flash/Kconfig | 9 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-mt6370-flash.c | 665 ++++++++++ drivers/leds/leds-mt6370.c | 994 +++++++++++++++ drivers/mfd/Kconfig | 13 + drivers/mfd/Makefile | 1 + drivers/mfd/mt6370.c | 273 ++++ drivers/power/supply/Kconfig | 11 + drivers/power/supply/Makefile | 1 + drivers/power/supply/mt6370-charger.c | 1132 +++++++++++++++++ drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/mt6370-regulator.c | 389 ++++++ drivers/usb/typec/tcpm/Kconfig | 8 + drivers/usb/typec/tcpm/Makefile | 1 + drivers/usb/typec/tcpm/tcpci_mt6370.c | 212 +++ drivers/video/backlight/Kconfig | 8 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/mt6370-backlight.c | 338 +++++ .../dt-bindings/iio/adc/mediatek,mt6370_adc.h | 18 + include/dt-bindings/mfd/mediatek,mt6370.h | 83 ++ 32 files changed, 5038 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/backlight/mediatek,mt6370-backlight.yaml create mode 100644 Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml create mode 100644 Documentation/devicetree/bindings/leds/mediatek,mt6370-indicator.yaml create mode 100644 Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml create mode 100644 Documentation/devicetree/bindings/power/supply/mediatek,mt6370-charger.yaml create mode 100644 Documentation/devicetree/bindings/usb/mediatek,mt6370-tcpc.yaml create mode 100644 drivers/iio/adc/mt6370-adc.c create mode 100644 drivers/leds/flash/leds-mt6370-flash.c create mode 100644 drivers/leds/leds-mt6370.c create mode 100644 drivers/mfd/mt6370.c create mode 100644 drivers/power/supply/mt6370-charger.c create mode 100644 drivers/regulator/mt6370-regulator.c create mode 100644 drivers/usb/typec/tcpm/tcpci_mt6370.c create mode 100644 drivers/video/backlight/mt6370-backlight.c create mode 100644 include/dt-bindings/iio/adc/mediatek,mt6370_adc.h create mode 100644 include/dt-bindings/mfd/mediatek,mt6370.h
From: ChiYuan Huang cy_huang@richtek.com
Add Mediatek MT6370 TCPC binding documentation.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com --- .../bindings/usb/mediatek,mt6370-tcpc.yaml | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/mediatek,mt6370-tcpc.yaml
diff --git a/Documentation/devicetree/bindings/usb/mediatek,mt6370-tcpc.yaml b/Documentation/devicetree/bindings/usb/mediatek,mt6370-tcpc.yaml new file mode 100644 index 000000000000..49316633f92f --- /dev/null +++ b/Documentation/devicetree/bindings/usb/mediatek,mt6370-tcpc.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/usb/mediatek,mt6370-tcpc.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Mediatek MT6370 Type-C Port Switch and Power Delivery controller DT bindings + +maintainers: + - ChiYuan Huang cy_huang@richtek.com + +description: | + Mediatek MT6370 is a multi-functional device. It integrates charger, ADC, flash, RGB indicators, + regulators (DSV/VIBLDO), and TypeC Port Switch with Power Delivery controller. + This document only describes MT6370 Type-C Port Switch and Power Delivery controller. + +properties: + compatible: + enum: + - mediatek,mt6370-tcpc + + interrupts: + maxItems: 1 + + connector: + type: object + $ref: /schemas/connector/usb-connector.yaml# + description: + Properties for usb c connector. + +additionalProperties: false + +required: + - compatible + - interrupts
On 31/05/2022 13:18, ChiaEn Wu wrote:
From: ChiYuan Huang cy_huang@richtek.com
Add Mediatek MT6370 TCPC binding documentation.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com
Please apply my previous comments.
Best regards, Krzysztof
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 Charger binding documentation.
Signed-off-by: ChiaEn Wu chiaen_wu@richtek.com --- .../power/supply/mediatek,mt6370-charger.yaml | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/mediatek,mt6370-charger.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/mediatek,mt6370-charger.yaml b/Documentation/devicetree/bindings/power/supply/mediatek,mt6370-charger.yaml new file mode 100644 index 000000000000..9d5c4487ca9c --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/mediatek,mt6370-charger.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/mediatek,mt6370-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek MT6370 Battery Charger + +maintainers: + - ChiaEn Wu chiaen_wu@richtek.com + +description: | + This module is part of the MT6370 MFD device. + Provides Battery Charger, Boost for OTG devices and BC1.2 detection. + +properties: + compatible: + const: mediatek,mt6370-charger + + interrupts: + description: | + Specify what irqs are needed to be handled by MT6370 Charger driver. IRQ + "MT6370_IRQ_CHG_MIVR", "MT6370_IRQ_ATTACH" and "MT6370_IRQ_OVPCTRL_UVP_D" + are required. + items: + - description: BC1.2 done irq for mt6370 charger + - description: usb plug in irq for mt6370 charger + - description: mivr irq for mt6370 charger + + interrupt-names: + items: + - const: attach_i + - const: uvp_d_evt + - const: mivr + + io-channels: + description: | + Use ADC channel to read vbus, ibus, ibat, etc., info. Ibus ADC channel + is required. + + usb-otg-vbus: + type: object + description: OTG boost regulator. + $ref: /schemas/regulator/regulator.yaml# + + properties: + enable-gpio: + maxItems: 1 + description: | + Specify a valid 'enable' gpio for the regulator and it's optional + +required: + - compatible + - interrupts + - interrupt-names + - io-channels + +additionalProperties: false + +...
On 31/05/2022 13:18, ChiaEn Wu wrote:
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 Charger binding documentation.
Please apply my previous comments.
Best regards, Krzysztof
From: ChiYuan Huang cy_huang@richtek.com
Add Mediatek mt6370 indicator documentation.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com --- .../leds/mediatek,mt6370-indicator.yaml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/mediatek,mt6370-indicator.yaml
diff --git a/Documentation/devicetree/bindings/leds/mediatek,mt6370-indicator.yaml b/Documentation/devicetree/bindings/leds/mediatek,mt6370-indicator.yaml new file mode 100644 index 000000000000..823be3add097 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/mediatek,mt6370-indicator.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/mediatek,mt6370-indicator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LED driver for MT6370 PMIC from MediaTek Integrated. + +maintainers: + - Alice Chen alice_chen@richtek.com + +description: | + This module is part of the MT6370 MFD device. + see Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml + Add MT6370 LED driver include 4-channel RGB LED support Register/PWM/Breath Mode + +properties: + compatible: + const: mediatek,mt6370-indicator + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^(multi-)?led@[0-3]$": + description: | + Properties for a single LED. + $ref: common.yaml# + type: object + + properties: + reg: + description: | + Index of the LED. + enum: + - 0 # LED output ISINK1 + - 1 # LED output ISINK2 + - 2 # LED output ISINK3 + - 3 # LED output ISINK4 + + mediatek,soft-start: + description: | + soft start step control, support /0.5ms/1ms/1.5ms/2ms. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + + unevaluatedProperties: false + +required: + - compatible + - "#address-cells" + - "#size-cells" + +additionalProperties: false
On Tue, 31 May 2022 19:18:49 +0800, ChiaEn Wu wrote:
From: ChiYuan Huang cy_huang@richtek.com
Add Mediatek mt6370 indicator documentation.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com
.../leds/mediatek,mt6370-indicator.yaml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/mediatek,mt6370-indicator.yaml
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors:
doc reference errors (make refcheckdocs): Warning: Documentation/devicetree/bindings/leds/mediatek,mt6370-indicator.yaml references a file that doesn't exist: Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml Documentation/devicetree/bindings/leds/mediatek,mt6370-indicator.yaml: Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml
See https://patchwork.ozlabs.org/patch/
This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1.
If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date:
pip3 install dtschema --upgrade
Please check and re-submit.
On 31/05/2022 13:18, ChiaEn Wu wrote:
From: ChiYuan Huang cy_huang@richtek.com
Add Mediatek mt6370 indicator documentation.
Please apply my previous comments.
Best regards, Krzysztof
From: Alice Chen alice_chen@richtek.com
Add Mediatek MT6370 flashlight binding documentation
Signed-off-by: Alice Chen alice_chen@richtek.com --- .../leds/mediatek,mt6370-flashlight.yaml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml
diff --git a/Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml b/Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml new file mode 100644 index 000000000000..b1b11bd3d410 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/leds-mt6370-flashlight.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Flash LED driver for MT6370 PMIC from MediaTek Integrated. + +maintainers: + - Alice Chen alice_chen@richtek.com + +description: | + This module is part of the MT6370 MFD device. + see Documentation/devicetree/bindings/mfd/mt6370.yaml + Add MT6370 flash LED driver include 2-channel flash LED support Torch/Strobe Mode. + +properties: + compatible: + const: mediatek,mt6370-flashlight + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^led@[0-1]$": + type: object + $ref: common.yaml# + description: + Properties for a single flash LED. + + properties: + reg: + description: Index of the flash LED. + enum: + - 0 #Address of LED1 + - 1 #Address of LED2 + + unevaluatedProperties: false + +required: + - compatible + - "#address-cells" + - "#size-cells" + +additionalProperties: false
On Tue, 31 May 2022 19:18:50 +0800, ChiaEn Wu wrote:
From: Alice Chen alice_chen@richtek.com
Add Mediatek MT6370 flashlight binding documentation
Signed-off-by: Alice Chen alice_chen@richtek.com
.../leds/mediatek,mt6370-flashlight.yaml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors: ./Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml: $id: relative path/filename doesn't match actual path or filename expected: http://devicetree.org/schemas/leds/mediatek,mt6370-flashlight.yaml#
doc reference errors (make refcheckdocs): Warning: Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml references a file that doesn't exist: Documentation/devicetree/bindings/mfd/mt6370.yaml Documentation/devicetree/bindings/leds/mediatek,mt6370-flashlight.yaml: Documentation/devicetree/bindings/mfd/mt6370.yaml
See https://patchwork.ozlabs.org/patch/
This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1.
If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date:
pip3 install dtschema --upgrade
Please check and re-submit.
On 31/05/2022 13:18, ChiaEn Wu wrote:
From: Alice Chen alice_chen@richtek.com
Add Mediatek MT6370 flashlight binding documentation
Please apply my previous comments.
Best regards, Krzysztof
From: ChiYuan Huang cy_huang@richtek.com
Add mt6370 backlight binding documentation.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com --- .../backlight/mediatek,mt6370-backlight.yaml | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/backlight/mediatek,mt6370-backlight.yaml
diff --git a/Documentation/devicetree/bindings/leds/backlight/mediatek,mt6370-backlight.yaml b/Documentation/devicetree/bindings/leds/backlight/mediatek,mt6370-backlight.yaml new file mode 100644 index 000000000000..81d72ed44be4 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/backlight/mediatek,mt6370-backlight.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/backlight/mediatek,mt6370-backlight.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek MT6370 Backlight + +maintainers: + - ChiaEn Wu chiaen_wu@richtek.com + +description: | + MT6370 is a highly-integrated smart power management IC, which includes a + single cell Li-Ion/Li-Polymer switching battery charger, a USB Type-C & + Power Delivery (PD) controller, dual flash LED current sources, a RGB LED + driver, a backlight WLED driver, a display bias driver and a general LDO for + portable devices. + + For the LCD backlight, it can provide 4 channel WLED driving capability. + Each channel driving current is up to 30mA + +allOf: + - $ref: common.yaml# + +properties: + compatible: + const: mediatek,mt6370-backlight + + default-brightness: + minimum: 0 + maximum: 2048 + + max-brightness: + minimum: 0 + maximum: 2048 + + enable-gpios: + description: External backlight 'enable' pin + maxItems: 1 + + mediatek,bled-pwm-enable: + description: | + Enable external PWM input for backlight dimming + type: boolean + + mediatek,bled-pwm-hys-enable: + description: | + Enable the backlight input-hysteresis for PWM mode + type: boolean + + mediatek,bled-pwm-hys-sel: + $ref: /schemas/types.yaml#/definitions/uint8 + enum: [0, 1, 2, 3] + description: | + Backlight PWM hysteresis input level selection. + value mapping: + - 0: 1bit + - 1: 2bit + - 2: 4bit + - 3: 6bit + + mediatek,bled-ovp-shutdown: + description: | + Enable the backlight shutdown when OVP level triggered + type: boolean + + mediatek,bled-ovp-level-sel: + $ref: /schemas/types.yaml#/definitions/uint8 + enum: [0, 1, 2, 3] + description: | + Backlight OVP level selection. + value mapping: + - 0: 17V + - 1: 21V + - 2: 25V + - 3: 29V + + mediatek,bled-ocp-shutdown: + description: | + Enable the backlight shutdown when OCP level triggerred. + type: boolean + + mediatek,bled-ocp-level-sel: + $ref: /schemas/types.yaml#/definitions/uint8 + enum: [0, 1, 2, 3] + description: | + Backlight OC level selection. + value mapping: + - 0: 900mA + - 1: 1200mA + - 2: 1500mA + - 3: 1800mA + + mediatek,bled-channel-use: + $ref: /schemas/types.yaml#/definitions/uint8 + description: | + Backlight LED channel to be used. + Each bit mapping to: + - 0: CH4 + - 1: CH3 + - 2: CH2 + - 3: CH1 + minimum: 1 + maximum: 15 + +required: + - compatible + - mediatek,bled-channel-use + +additionalProperties: false
On 31/05/2022 13:18, ChiaEn Wu wrote:
From: ChiYuan Huang cy_huang@richtek.com
Add mt6370 backlight binding documentation.
Please apply my previous comments.
Best regards, Krzysztof
From: ChiYuan Huang cy_huang@richtek.com
Add Mediatek MT6370 binding documentation.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com --- .../bindings/mfd/mediatek,mt6370.yaml | 282 ++++++++++++++++++ .../dt-bindings/iio/adc/mediatek,mt6370_adc.h | 18 ++ include/dt-bindings/mfd/mediatek,mt6370.h | 83 ++++++ 3 files changed, 383 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml create mode 100644 include/dt-bindings/iio/adc/mediatek,mt6370_adc.h create mode 100644 include/dt-bindings/mfd/mediatek,mt6370.h
diff --git a/Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml b/Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml new file mode 100644 index 000000000000..96a12dce0108 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml @@ -0,0 +1,282 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/mediatek,mt6370.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek MT6370 SubPMIC + +maintainers: + - ChiYuan Huang cy_huang@richtek.com + +description: | + MT6370 is a highly-integrated smart power management IC, which includes a + single cell Li-Ion/Li-Polymer switching battery charger, a USB Type-C & + Power Delivery (PD) controller, dual flash LED current sources, a RGB LED + driver, a backlight WLED driver, a display bias driver and a general LDO for + portable devices. + +properties: + compatible: + const: mediatek,mt6370 + + reg: + maxItems: 1 + + wakeup-source: true + + interrupts: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 1 + + adc: + type: object + description: | + List the compatible configurations of MT6370 ADC. + + properties: + compatible: + const: mediatek,mt6370-adc + + "#io-channel-cells": + const: 1 + + required: + - compatible + - '#io-channel-cells' + + backlight: + type: object + $ref: /schemas/leds/backlight/mediatek,mt6370-backlight.yaml# + + charger: + type: object + $ref: /schemas/power/supply/mediatek,mt6370-charger.yaml# + + tcpc: + type: object + $ref: /schemas/usb/mediatek,mt6370-tcpc.yaml# + + indicator: + type: object + $ref: /schemas/leds/mediatek,mt6370-indicator.yaml# + + flashlight: + type: object + $ref: /schemas/leds/mediatek,mt6370-flashlight.yaml# + + regulators: + type: object + description: | + List all supported regulators + + patternProperties: + "^(dsvbst|vibldo)$": + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + "^(dsvpos|dsvneg)$": + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + properties: + enable-gpio: + maxItems: 1 + description: | + Specify a valid 'enable' gpio for the regulator and it's optional + +required: + - compatible + - reg + - interrupts + - interrupt-controller + - '#interrupt-cells' + - regulators + - adc + - backlight + - indicator + - tcpc + - charger + - flashlight + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/leds/common.h> + #include <dt-bindings/mfd/mediatek,mt6370.h> + #include <dt-bindings/iio/adc/mediatek,mt6370_adc.h> + #include <dt-bindings/usb/pd.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + mt6370@34 { + compatible = "mediatek,mt6370"; + reg = <0x34>; + wakeup-source; + interrupts-extended = <&gpio26 3 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <1>; + + mt6370_adc: adc { + compatible = "mediatek,mt6370-adc"; + #io-channel-cells = <1>; + }; + + backlight { + compatible = "mediatek,mt6370-backlight"; + mediatek,bled-channel-use = /bits/ 8 <15>; + }; + + charger { + compatible = "mediatek,mt6370-charger"; + interrupts = <MT6370_IRQ_ATTACH>, <MT6370_IRQ_OVPCTRL_UVP_D>, + <MT6370_IRQ_CHG_MIVR>; + interrupt-names = "attach_i", "uvp_d_evt", "mivr"; + io-channels = <&mt6370_adc MT6370_CHAN_IBUS>; + + mt6370_otg_vbus: usb-otg-vbus { + regulator-compatible = "mt6370,otg-vbus"; + regulator-name = "usb-otg-vbus"; + regulator-min-microvolt = <4350000>; + regulator-max-microvolt = <5800000>; + regulator-min-microamp = <500000>; + regulator-max-microamp = <3000000>; + }; + }; + + indicator { + compatible = "mediatek,mt6370-indicator"; + #address-cells = <1>; + #size-cells = <0>; + + multi-led@0 { + reg = <0>; + function = LED_FUNCTION_INDICATOR; + color = <LED_COLOR_ID_RGB>; + led-max-microamp = <24000>; + #address-cells = <1>; + #size-cells = <0>; + mediatek,soft-start = <3>; + led@0 { + reg = <0>; + color = <LED_COLOR_ID_RED>; + }; + led@1 { + reg = <1>; + color = <LED_COLOR_ID_GREEN>; + }; + led@2 { + reg = <2>; + color = <LED_COLOR_ID_BLUE>; + }; + }; + led@3 { + reg = <3>; + function = LED_FUNCTION_INDICATOR; + color = <LED_COLOR_ID_WHITE>; + led-max-microamp = <6000>; + }; + }; + + flashlight { + compatible = "mediatek,mt6370-flashlight"; + #address-cells = <1>; + #size-cells = <0>; + led@0 { + reg = <0>; + led-sources = <0>; + function = LED_FUNCTION_FLASH; + color = <LED_COLOR_ID_WHITE>; + function-enumerator = <1>; + led-max-microamp = <200000>; + flash-max-microamp = <500000>; + flash-max-timeout-us = <1248000>; + }; + led@1 { + reg = <1>; + led-sources = <1>; + function = LED_FUNCTION_FLASH; + color = <LED_COLOR_ID_WHITE>; + function-enumerator = <2>; + led-max-microamp = <200000>; + flash-max-microamp = <500000>; + flash-max-timeout-us = <1248000>; + }; + }; + + tcpc { + compatible = "mediatek,mt6370-tcpc"; + interrupts-extended = <&gpio26 4 IRQ_TYPE_LEVEL_LOW>; + + connector { + compatible = "usb-c-connector"; + label = "USB-C"; + vbus-supply = <&mt6370_otg_vbus>; + data-role = "dual"; + power-role = "dual"; + try-power-role = "sink"; + source-pdos = <PDO_FIXED(5000, 1000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>; + sink-pdos = <PDO_FIXED(5000, 2000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>; + op-sink-microwatt = <10000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + endpoint { + remote-endpoint = <&usb_hs>; + }; + }; + port@1 { + reg = <1>; + endpoint { + remote-endpoint = <&usb_ss>; + }; + }; + port@2 { + reg = <2>; + endpoint { + remote-endpoint = <&dp_aux>; + }; + }; + }; + }; + }; + + regulators { + dsvbst { + regulator-name = "mt6370-dsv-vbst"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6200000>; + }; + dsvpos { + regulator-name = "mt6370-dsv-vpos"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + regulator-boot-on; + }; + dsvneg { + regulator-name = "mt6370-dsv-vneg"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + regulator-boot-on; + }; + vibldo { + regulator-name = "mt6370-vib-ldo"; + regulator-min-microvolt = <1600000>; + regulator-max-microvolt = <4000000>; + }; + }; + }; + }; diff --git a/include/dt-bindings/iio/adc/mediatek,mt6370_adc.h b/include/dt-bindings/iio/adc/mediatek,mt6370_adc.h new file mode 100644 index 000000000000..18ce2fef8f9e --- /dev/null +++ b/include/dt-bindings/iio/adc/mediatek,mt6370_adc.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __DT_BINDINGS_MEDIATEK_MT6370_ADC_H__ +#define __DT_BINDINGS_MEDIATEK_MT6370_ADC_H__ + +/* ADC Channel Index */ +#define MT6370_CHAN_VBUSDIV5 0 +#define MT6370_CHAN_VBUSDIV2 1 +#define MT6370_CHAN_VSYS 2 +#define MT6370_CHAN_VBAT 3 +#define MT6370_CHAN_TS_BAT 4 +#define MT6370_CHAN_IBUS 5 +#define MT6370_CHAN_IBAT 6 +#define MT6370_CHAN_CHG_VDDP 7 +#define MT6370_CHAN_TEMP_JC 8 +#define MT6370_CHAN_MAX 9 + +#endif diff --git a/include/dt-bindings/mfd/mediatek,mt6370.h b/include/dt-bindings/mfd/mediatek,mt6370.h new file mode 100644 index 000000000000..df641e5d651f --- /dev/null +++ b/include/dt-bindings/mfd/mediatek,mt6370.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __DT_BINDINGS_MEDIATEK_MT6370_H__ +#define __DT_BINDINGS_MEDIATEK_MT6370_H__ + +/* IRQ definitions */ +#define MT6370_IRQ_DIRCHGON 0 +#define MT6370_IRQ_CHG_TREG 4 +#define MT6370_IRQ_CHG_AICR 5 +#define MT6370_IRQ_CHG_MIVR 6 +#define MT6370_IRQ_PWR_RDY 7 +#define MT6370_IRQ_FL_CHG_VINOVP 11 +#define MT6370_IRQ_CHG_VSYSUV 12 +#define MT6370_IRQ_CHG_VSYSOV 13 +#define MT6370_IRQ_CHG_VBATOV 14 +#define MT6370_IRQ_CHG_VINOVPCHG 15 +#define MT6370_IRQ_TS_BAT_COLD 20 +#define MT6370_IRQ_TS_BAT_COOL 21 +#define MT6370_IRQ_TS_BAT_WARM 22 +#define MT6370_IRQ_TS_BAT_HOT 23 +#define MT6370_IRQ_TS_STATC 24 +#define MT6370_IRQ_CHG_FAULT 25 +#define MT6370_IRQ_CHG_STATC 26 +#define MT6370_IRQ_CHG_TMR 27 +#define MT6370_IRQ_CHG_BATABS 28 +#define MT6370_IRQ_CHG_ADPBAD 29 +#define MT6370_IRQ_CHG_RVP 30 +#define MT6370_IRQ_TSHUTDOWN 31 +#define MT6370_IRQ_CHG_IINMEAS 32 +#define MT6370_IRQ_CHG_ICCMEAS 33 +#define MT6370_IRQ_CHGDET_DONE 34 +#define MT6370_IRQ_WDTMR 35 +#define MT6370_IRQ_SSFINISH 36 +#define MT6370_IRQ_CHG_RECHG 37 +#define MT6370_IRQ_CHG_TERM 38 +#define MT6370_IRQ_CHG_IEOC 39 +#define MT6370_IRQ_ADC_DONE 40 +#define MT6370_IRQ_PUMPX_DONE 41 +#define MT6370_IRQ_BST_BATUV 45 +#define MT6370_IRQ_BST_MIDOV 46 +#define MT6370_IRQ_BST_OLP 47 +#define MT6370_IRQ_ATTACH 48 +#define MT6370_IRQ_DETACH 49 +#define MT6370_IRQ_HVDCP_STPDONE 51 +#define MT6370_IRQ_HVDCP_VBUSDET_DONE 52 +#define MT6370_IRQ_HVDCP_DET 53 +#define MT6370_IRQ_CHGDET 54 +#define MT6370_IRQ_DCDT 55 +#define MT6370_IRQ_DIRCHG_VGOK 59 +#define MT6370_IRQ_DIRCHG_WDTMR 60 +#define MT6370_IRQ_DIRCHG_UC 61 +#define MT6370_IRQ_DIRCHG_OC 62 +#define MT6370_IRQ_DIRCHG_OV 63 +#define MT6370_IRQ_OVPCTRL_SWON 67 +#define MT6370_IRQ_OVPCTRL_UVP_D 68 +#define MT6370_IRQ_OVPCTRL_UVP 69 +#define MT6370_IRQ_OVPCTRL_OVP_D 70 +#define MT6370_IRQ_OVPCTRL_OVP 71 +#define MT6370_IRQ_FLED_STRBPIN 72 +#define MT6370_IRQ_FLED_TORPIN 73 +#define MT6370_IRQ_FLED_TX 74 +#define MT6370_IRQ_FLED_LVF 75 +#define MT6370_IRQ_FLED2_SHORT 78 +#define MT6370_IRQ_FLED1_SHORT 79 +#define MT6370_IRQ_FLED2_STRB 80 +#define MT6370_IRQ_FLED1_STRB 81 +#define mT6370_IRQ_FLED2_STRB_TO 82 +#define MT6370_IRQ_FLED1_STRB_TO 83 +#define MT6370_IRQ_FLED2_TOR 84 +#define MT6370_IRQ_FLED1_TOR 85 +#define MT6370_IRQ_OTP 93 +#define MT6370_IRQ_VDDA_OVP 94 +#define MT6370_IRQ_VDDA_UV 95 +#define MT6370_IRQ_LDO_OC 103 +#define MT6370_IRQ_BLED_OCP 118 +#define MT6370_IRQ_BLED_OVP 119 +#define MT6370_IRQ_DSV_VNEG_OCP 123 +#define MT6370_IRQ_DSV_VPOS_OCP 124 +#define MT6370_IRQ_DSV_BST_OCP 125 +#define MT6370_IRQ_DSV_VNEG_SCP 126 +#define MT6370_IRQ_DSV_VPOS_SCP 127 + +#endif /* __DT_BINDINGS_MEDIATEK_MT6370_H__ */
On Tue, 31 May 2022 19:18:52 +0800, ChiaEn Wu wrote:
From: ChiYuan Huang cy_huang@richtek.com
Add Mediatek MT6370 binding documentation.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com
.../bindings/mfd/mediatek,mt6370.yaml | 282 ++++++++++++++++++ .../dt-bindings/iio/adc/mediatek,mt6370_adc.h | 18 ++ include/dt-bindings/mfd/mediatek,mt6370.h | 83 ++++++ 3 files changed, 383 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml create mode 100644 include/dt-bindings/iio/adc/mediatek,mt6370_adc.h create mode 100644 include/dt-bindings/mfd/mediatek,mt6370.h
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors: ./Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml: Unable to find schema file matching $id: http://devicetree.org/schemas/power/supply/mediatek,mt6370-charger.yaml /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mfd/mediatek,mt6370.example.dtb: mt6370@34: charger: False schema does not allow {'compatible': ['mediatek,mt6370-charger'], 'interrupts': [[48], [68], [6]], 'interrupt-names': ['attach_i', 'uvp_d_evt', 'mivr'], 'io-channels': [[1, 5]], 'usb-otg-vbus': {'regulator-compatible': ['mt6370,otg-vbus'], 'regulator-name': ['usb-otg-vbus'], 'regulator-min-microvolt': [[4350000]], 'regulator-max-microvolt': [[5800000]], 'regulator-min-microamp': [[500000]], 'regulator-max-microamp': [[3000000]], 'phandle': [[2]]}} From schema: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mfd/mediatek,mt6370.example.dtb: mt6370@34: tcpc: False schema does not allow {'compatible': ['mediatek,mt6370-tcpc'], 'interrupts-extended': [[4294967295, 4, 8]], 'connector': {'compatible': ['usb-c-connector'], 'label': ['USB-C'], 'vbus-supply': [[2]], 'data-role': ['dual'], 'power-role': ['dual'], 'try-power-role': ['sink'], 'source-pdos': [[570527844]], 'sink-pdos': [[570527944]], 'op-sink-microwatt': [[10000000]], 'ports': {'#address-cells': [[1]], '#size-cells': [[0]], 'port@0': {'reg': [[0]], 'endpoint': {'remote-endpoint': [[4294967295]]}}, 'port@1': {'reg': [[1]], 'endpoint': {'remote-endpoint': [[4294967295]]}}, 'port@2': {'reg': [[2]], 'endpoint': {'remote-endpoint': [[4294967295]]}}}}} From schema: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mfd/mediatek,mt6370.example.dtb: mt6370@34: indicator: False schema does not allow {'compatible': ['mediatek,mt6370-indicator'], '#address-cells': [[1]], '#size-cells': [[0]], 'multi-led@0': {'reg': [[0]], 'function': ['indicator'], 'color': [[9]], 'led-max-microamp': [[24000]], '#address-cells': [[1]], '#size-cells': [[0]], 'mediatek,soft-start': [[3]], 'led@0': {'reg': [[0]], 'color': [[1]]}, 'led@1': {'reg': [[1]], 'color': [[2]]}, 'led@2': {'reg': [[2]], 'color': [[3]]}}, 'led@3': {'reg': [[3]], 'function': ['indicator'], 'color': [[0]], 'led-max-microamp': [[6000]]}} From schema: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mfd/mediatek,mt6370.example.dtb: mt6370@34: flashlight: False schema does not allow {'compatible': ['mediatek,mt6370-flashlight'], '#address-cells': [[1]], '#size-cells': [[0]], 'led@0': {'reg': [[0]], 'led-sources': [[0]], 'function': ['flash'], 'color': [[0]], 'function-enumerator': [[1]], 'led-max-microamp': [[200000]], 'flash-max-microamp': [[500000]], 'flash-max-timeout-us': [[1248000]]}, 'led@1': {'reg': [[1]], 'led-sources': [[1]], 'function': ['flash'], 'color': [[0]], 'function-enumerator': [[2]], 'led-max-microamp': [[200000]], 'flash-max-microamp': [[500000]], 'flash-max-timeout-us': [[1248000]]}} From schema: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/mfd/mediatek,mt6370.yaml Documentation/devicetree/bindings/mfd/mediatek,mt6370.example.dtb:0:0: /example-0/i2c/mt6370@34/charger: failed to match any schema with compatible: ['mediatek,mt6370-charger'] Documentation/devicetree/bindings/mfd/mediatek,mt6370.example.dtb:0:0: /example-0/i2c/mt6370@34/indicator: failed to match any schema with compatible: ['mediatek,mt6370-indicator'] Documentation/devicetree/bindings/mfd/mediatek,mt6370.example.dtb:0:0: /example-0/i2c/mt6370@34/flashlight: failed to match any schema with compatible: ['mediatek,mt6370-flashlight'] Documentation/devicetree/bindings/mfd/mediatek,mt6370.example.dtb:0:0: /example-0/i2c/mt6370@34/tcpc: failed to match any schema with compatible: ['mediatek,mt6370-tcpc']
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/patch/
This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1.
If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date:
pip3 install dtschema --upgrade
Please check and re-submit.
From: ChiYuan Huang cy_huang@richtek.com
Add Mediatek MT6370 MFD support.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com --- drivers/mfd/Kconfig | 13 +++ drivers/mfd/Makefile | 1 + drivers/mfd/mt6370.c | 273 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 drivers/mfd/mt6370.c
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3b59456f5545..d9a7524a3e0e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -937,6 +937,19 @@ config MFD_MT6360 PMIC part includes 2-channel BUCKs and 2-channel LDOs LDO part includes 4-channel LDOs
+config MFD_MT6370 + tristate "Mediatek MT6370 SubPMIC" + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + depends on I2C + help + Say Y here to enable MT6370 SubPMIC functional support. + It integrate single cell battery charger with adc monitoring, RGB + LEDs, dual channel flashlight, WLED backlight driver, display bias + voltage supply, one general purpose LDO, and cc logic + controller with USBPD commmunication capable. + config MFD_MT6397 tristate "MediaTek MT6397 PMIC Support" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 858cacf659d6..62b27125420e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -242,6 +242,7 @@ obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC) += intel_soc_pmic_bxtwc.o obj-$(CONFIG_INTEL_SOC_PMIC_CHTWC) += intel_soc_pmic_chtwc.o obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o obj-$(CONFIG_MFD_MT6360) += mt6360-core.o +obj-$(CONFIG_MFD_MT6370) += mt6370.o mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o obj-$(CONFIG_MFD_MT6397) += mt6397.o obj-$(CONFIG_INTEL_SOC_PMIC_MRFLD) += intel_soc_pmic_mrfld.o diff --git a/drivers/mfd/mt6370.c b/drivers/mfd/mt6370.c new file mode 100644 index 000000000000..a5a07be7ccc2 --- /dev/null +++ b/drivers/mfd/mt6370.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <dt-bindings/mfd/mediatek,mt6370.h> +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/regmap.h> + +enum { + MT6370_USBC_I2C = 0, + MT6370_PMU_I2C, + MT6370_MAX_I2C +}; + +#define MT6370_REG_DEV_INFO 0x100 +#define MT6370_REG_CHG_IRQ1 0x1C0 +#define MT6370_REG_CHG_MASK1 0x1E0 + +#define MT6370_VENID_MASK GENMASK(7, 4) + +#define MT6370_NUM_IRQREGS 16 +#define MT6370_USBC_I2CADDR 0x4E +#define MT6370_REG_ADDRLEN 2 +#define MT6370_REG_MAXADDR 0x1FF + +struct mt6370_info { + struct i2c_client *i2c[MT6370_MAX_I2C]; + struct device *dev; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; +}; + +static const struct regmap_irq mt6370_irqs[] = { + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHGON, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TREG, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_AICR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_MIVR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_PWR_RDY, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FL_CHG_VINOVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VSYSUV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VSYSOV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VBATOV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_VINOVPCHG, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_COLD, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_COOL, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_WARM, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_BAT_HOT, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TS_STATC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_FAULT, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_STATC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TMR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_BATABS, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_ADPBAD, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_RVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_TSHUTDOWN, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_IINMEAS, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_ICCMEAS, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHGDET_DONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_WDTMR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_SSFINISH, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_RECHG, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_TERM, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHG_IEOC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_ADC_DONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_PUMPX_DONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_BATUV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_MIDOV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BST_OLP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_ATTACH, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DETACH, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_STPDONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_VBUSDET_DONE, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_HVDCP_DET, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_CHGDET, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DCDT, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_VGOK, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_WDTMR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_UC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_OC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DIRCHG_OV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_SWON, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_UVP_D, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_UVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_OVP_D, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OVPCTRL_OVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_STRBPIN, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_TORPIN, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_TX, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED_LVF, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_SHORT, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_SHORT, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_STRB, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_STRB, 8), + REGMAP_IRQ_REG_LINE(mT6370_IRQ_FLED2_STRB_TO, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_STRB_TO, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED2_TOR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_FLED1_TOR, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_OTP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_VDDA_OVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_VDDA_UV, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_LDO_OC, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BLED_OCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_BLED_OVP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VNEG_OCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VPOS_OCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_BST_OCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VNEG_SCP, 8), + REGMAP_IRQ_REG_LINE(MT6370_IRQ_DSV_VPOS_SCP, 8) +}; + +static const struct regmap_irq_chip mt6370_irq_chip = { + .name = "mt6370-irqs", + .status_base = MT6370_REG_CHG_IRQ1, + .mask_base = MT6370_REG_CHG_MASK1, + .num_regs = MT6370_NUM_IRQREGS, + .irqs = mt6370_irqs, + .num_irqs = ARRAY_SIZE(mt6370_irqs), +}; + +static const struct resource mt6370_regulator_irqs[] = { + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VPOS_SCP, "db_vpos_scp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VNEG_SCP, "db_vneg_scp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_BST_OCP, "db_vbst_ocp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VPOS_OCP, "db_vpos_ocp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_DSV_VNEG_OCP, "db_vneg_ocp"), + DEFINE_RES_IRQ_NAMED(MT6370_IRQ_LDO_OC, "ldo_oc") +}; + +static const struct mfd_cell mt6370_devices[] = { + MFD_CELL_OF("adc", NULL, NULL, 0, 0, "mediatek,mt6370-adc"), + MFD_CELL_OF("charger", NULL, NULL, 0, 0, "mediatek,mt6370-charger"), + MFD_CELL_OF("backlight", NULL, NULL, 0, 0, "mediatek,mt6370-backlight"), + MFD_CELL_OF("flashlight", NULL, NULL, 0, 0, "mediatek,mt6370-flashlight"), + MFD_CELL_OF("indicator", NULL, NULL, 0, 0, "mediatek,mt6370-indicator"), + MFD_CELL_OF("tcpc", NULL, NULL, 0, 0, "mediatek,mt6370-tcpc"), + MFD_CELL_RES("regulator", mt6370_regulator_irqs) +}; + +static int mt6370_check_vendor_info(struct mt6370_info *info) +{ + unsigned int devinfo; + int ret; + + ret = regmap_read(info->regmap, MT6370_REG_DEV_INFO, &devinfo); + if (ret) + return ret; + + switch (FIELD_GET(MT6370_VENID_MASK, devinfo)) { + case 0x8: /* RT5081 */ + case 0xA: /* RT5081A */ + case 0xE: /* MT6370 */ + case 0xF: /* MT6371 */ + case 0x9: /* MT6372P */ + case 0xB: /* MT6372CP */ + break; + default: + dev_err(info->dev, "Not invalid value 0x%02x\n", devinfo); + return -ENODEV; + } + + return 0; +} + +static int mt6370_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct mt6370_info *info = context; + u8 bank_idx = *(u8 *)reg_buf, bank_addr = *(u8 *)(reg_buf + 1); + int ret; + + ret = i2c_smbus_read_i2c_block_data(info->i2c[bank_idx], bank_addr, + val_size, val_buf); + if (ret != val_size) + return ret; + + return 0; +} + +static int mt6370_regmap_write(void *context, const void *data, size_t count) +{ + struct mt6370_info *info = context; + u8 bank_idx = *(u8 *)data, bank_addr = *(u8 *)(data + 1); + int len = count - MT6370_REG_ADDRLEN; + + return i2c_smbus_write_i2c_block_data(info->i2c[bank_idx], bank_addr, + len, data + MT6370_REG_ADDRLEN); +} + +static const struct regmap_bus mt6370_regmap_bus = { + .read = mt6370_regmap_read, + .write = mt6370_regmap_write, +}; + +static const struct regmap_config mt6370_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .max_register = MT6370_REG_MAXADDR, +}; + +static int mt6370_probe(struct i2c_client *i2c) +{ + struct mt6370_info *info; + struct i2c_client *usbc_i2c; + int ret; + + info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &i2c->dev; + + usbc_i2c = devm_i2c_new_dummy_device(&i2c->dev, i2c->adapter, + MT6370_USBC_I2CADDR); + if (IS_ERR(usbc_i2c)) { + ret = PTR_ERR(usbc_i2c); + dev_err(&i2c->dev, "Failed to register usbc i2c client %d\n", ret); + return ret; + } + + /* Assign I2C client for PMU and TypeC */ + info->i2c[MT6370_PMU_I2C] = i2c; + info->i2c[MT6370_USBC_I2C] = usbc_i2c; + + info->regmap = devm_regmap_init(&i2c->dev, &mt6370_regmap_bus, info, + &mt6370_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(&i2c->dev, "Failed to register regmap (%d)\n", ret); + return ret; + } + + ret = mt6370_check_vendor_info(info); + if (ret) { + dev_err(&i2c->dev, "Failed to check vendor info (%d)\n", ret); + return ret; + } + + ret = devm_regmap_add_irq_chip(&i2c->dev, info->regmap, i2c->irq, + IRQF_ONESHOT, -1, &mt6370_irq_chip, + &info->irq_data); + if (ret) { + dev_err(&i2c->dev, "Failed to add irq chip (%d)\n", ret); + return ret; + } + + return devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_AUTO, + mt6370_devices, ARRAY_SIZE(mt6370_devices), + NULL, 0, + regmap_irq_get_domain(info->irq_data)); +} + +static const struct of_device_id mt6370_match_table[] = { + { .compatible = "mediatek,mt6370", }, + {} +}; +MODULE_DEVICE_TABLE(of, mt6370_match_table); + +static struct i2c_driver mt6370_driver = { + .driver = { + .name = "mt6370", + .of_match_table = mt6370_match_table, + }, + .probe_new = mt6370_probe, +}; +module_i2c_driver(mt6370_driver); + +MODULE_AUTHOR("ChiYuan Huang cy_huang@richtek.com"); +MODULE_DESCRIPTION("MT6370 I2C MFD Driver"); +MODULE_LICENSE("GPL v2");
From: ChiYuan Huang cy_huang@richtek.com
Add chip level mt6370 tcpci driver.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com --- drivers/usb/typec/tcpm/Kconfig | 8 + drivers/usb/typec/tcpm/Makefile | 1 + drivers/usb/typec/tcpm/tcpci_mt6370.c | 212 ++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 drivers/usb/typec/tcpm/tcpci_mt6370.c
diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig index 557f392fe24d..f4b7363bc7e9 100644 --- a/drivers/usb/typec/tcpm/Kconfig +++ b/drivers/usb/typec/tcpm/Kconfig @@ -35,6 +35,14 @@ config TYPEC_MT6360 USB Type-C. It works with Type-C Port Controller Manager to provide USB PD and USB Type-C functionalities.
+config TYPEC_TCPCI_MT6370 + tristate "Mediatek MT6370 Type-C driver" + depends on MFD_MT6370 + help + Mediatek MT6370 is a multi-functional IC that includes + USB Type-C. It works with Type-C Port Controller Manager + to provide USB PD and USB Type-C functionalities. + config TYPEC_TCPCI_MAXIM tristate "Maxim TCPCI based Type-C chip driver" help diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile index 7d499f3569fd..906d9dced8e7 100644 --- a/drivers/usb/typec/tcpm/Makefile +++ b/drivers/usb/typec/tcpm/Makefile @@ -6,4 +6,5 @@ typec_wcove-y := wcove.o obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o obj-$(CONFIG_TYPEC_MT6360) += tcpci_mt6360.o +obj-$(CONFIG_TYPEC_TCPCI_MT6370) += tcpci_mt6370.o obj-$(CONFIG_TYPEC_TCPCI_MAXIM) += tcpci_maxim.o diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c new file mode 100644 index 000000000000..ce7f5e03fd10 --- /dev/null +++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/bits.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_wakeup.h> +#include <linux/pm_wakeirq.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/usb/tcpm.h> +#include "tcpci.h" + +#define MT6370_REG_SYSCTRL8 0x9B + +#define MT6370_AUTOIDLE_MASK BIT(3) + +#define MT6370_VENDOR_ID 0x29CF +#define MT6370_TCPC_DID_A 0x2170 + +struct mt6370_priv { + struct device *dev; + struct regulator *vbus; + struct tcpci *tcpci; + struct tcpci_data tcpci_data; + int irq; +}; + +static const struct reg_sequence mt6370_reg_init[] = { + REG_SEQ(0xA0, 0x1, 1000), + REG_SEQ(0x81, 0x38, 0), + REG_SEQ(0x82, 0x82, 0), + REG_SEQ(0xBA, 0xFC, 0), + REG_SEQ(0xBB, 0x50, 0), + REG_SEQ(0x9E, 0x8F, 0), + REG_SEQ(0xA1, 0x5, 0), + REG_SEQ(0xA2, 0x4, 0), + REG_SEQ(0xA3, 0x4A, 0), + REG_SEQ(0xA4, 0x01, 0), + REG_SEQ(0x95, 0x01, 0), + REG_SEQ(0x80, 0x71, 0), + REG_SEQ(0x9B, 0x3A, 1000) +}; + +static int mt6370_tcpc_init(struct tcpci *tcpci, struct tcpci_data *data) +{ + u16 did; + int ret; + + ret = regmap_register_patch(data->regmap, mt6370_reg_init, + ARRAY_SIZE(mt6370_reg_init)); + if (ret) + return ret; + + ret = regmap_raw_read(data->regmap, TCPC_BCD_DEV, &did, sizeof(u16)); + if (ret) + return ret; + + if (did == MT6370_TCPC_DID_A) { + ret = regmap_write(data->regmap, TCPC_FAULT_CTRL, 0x80); + if (ret) + return ret; + } + + return 0; +} + +static int mt6370_tcpc_set_vconn(struct tcpci *tcpci, struct tcpci_data *data, + bool enable) +{ + return regmap_update_bits(data->regmap, MT6370_REG_SYSCTRL8, + MT6370_AUTOIDLE_MASK, + !enable ? MT6370_AUTOIDLE_MASK : 0); +} + +static int mt6370_tcpc_set_vbus(struct tcpci *tcpci, struct tcpci_data *data, + bool source, bool sink) +{ + struct mt6370_priv *priv = container_of(data, struct mt6370_priv, + tcpci_data); + int ret; + + ret = regulator_is_enabled(priv->vbus); + if (ret < 0) + return ret; + + if (ret && !source) + ret = regulator_disable(priv->vbus); + else if (!ret && source) + ret = regulator_enable(priv->vbus); + else + ret = 0; + + return ret; +} + +static irqreturn_t mt6370_irq_handler(int irq, void *dev_id) +{ + struct mt6370_priv *priv = dev_id; + + return tcpci_irq(priv->tcpci); +} + +static int mt6370_check_vendor_info(struct mt6370_priv *priv) +{ + struct regmap *regmap = priv->tcpci_data.regmap; + u16 vid; + int ret; + + ret = regmap_raw_read(regmap, TCPC_VENDOR_ID, &vid, sizeof(u16)); + if (ret) + return ret; + + if (vid != MT6370_VENDOR_ID) { + dev_err(priv->dev, "Vendor ID not correct 0x%02x\n", vid); + return -ENODEV; + } + + return 0; +} + +static int mt6370_tcpc_probe(struct platform_device *pdev) +{ + struct mt6370_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + priv->tcpci_data.regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->tcpci_data.regmap) { + dev_err(&pdev->dev, "Failed to init regmap\n"); + return -ENODEV; + } + + ret = mt6370_check_vendor_info(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to check vendor info (%d)\n", ret); + return ret; + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + dev_err(&pdev->dev, "Failed to get TCPC irq (%d)\n", priv->irq); + return priv->irq; + } + + /* Assign TCPCI feature and ops */ + priv->tcpci_data.auto_discharge_disconnect = 1; + priv->tcpci_data.init = mt6370_tcpc_init; + priv->tcpci_data.set_vconn = mt6370_tcpc_set_vconn; + + priv->vbus = devm_regulator_get_optional(&pdev->dev, "vbus"); + if (!IS_ERR(priv->vbus)) + priv->tcpci_data.set_vbus = mt6370_tcpc_set_vbus; + + priv->tcpci = tcpci_register_port(&pdev->dev, &priv->tcpci_data); + if (IS_ERR(priv->tcpci)) { + dev_err(&pdev->dev, "Failed to register tcpci port\n"); + return PTR_ERR(priv->tcpci); + } + + ret = devm_request_threaded_irq(&pdev->dev, priv->irq, NULL, + mt6370_irq_handler, IRQF_ONESHOT, + dev_name(&pdev->dev), priv); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate irq (%d)\n", ret); + tcpci_unregister_port(priv->tcpci); + return ret; + } + + device_init_wakeup(&pdev->dev, true); + dev_pm_set_wake_irq(&pdev->dev, priv->irq); + return 0; +} + +static int mt6370_tcpc_remove(struct platform_device *pdev) +{ + struct mt6370_priv *priv = platform_get_drvdata(pdev); + + disable_irq(priv->irq); + tcpci_unregister_port(priv->tcpci); + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); + return 0; +} + +static const struct of_device_id mt6370_tcpc_devid_table[] = { + { .compatible = "mediatek,mt6370-tcpc", }, + {} +}; +MODULE_DEVICE_TABLE(of, mt6370_tcpc_devid_table); + +static struct platform_driver mt6370_tcpc_driver = { + .driver = { + .name = "mt6370-tcpc", + .of_match_table = mt6370_tcpc_devid_table, + }, + .probe = mt6370_tcpc_probe, + .remove = mt6370_tcpc_remove, +}; +module_platform_driver(mt6370_tcpc_driver); + +MODULE_AUTHOR("ChiYuan Huang cy_huang@richtek.com"); +MODULE_DESCRIPTION("MT6370 USB Type-C Port Controller Interface Driver"); +MODULE_LICENSE("GPL v2");
From: ChiYuan Huang cy_huang@richtek.com
Add mt6370 DisplayBias and VibLDO support.
Signed-off-by: ChiYuan Huang cy_huang@richtek.com --- drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/mt6370-regulator.c | 389 +++++++++++++++++++++++++++ 3 files changed, 398 insertions(+) create mode 100644 drivers/regulator/mt6370-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index cbe0f96ca342..dcb6866dab53 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -804,6 +804,14 @@ config REGULATOR_MT6360 2-channel buck with Thermal Shutdown and Overload Protection 6-channel High PSRR and Low Dropout LDO.
+config REGULATOR_MT6370 + tristate "MT6370 SubPMIC Regulator" + depends on MFD_MT6370 + help + Say Y here to enable MT6370 regulator support. + This driver support the control for DisplayBias voltages and one + general purpose LDO which commonly used to drive the vibrator. + config REGULATOR_MT6380 tristate "MediaTek MT6380 PMIC" depends on MTK_PMIC_WRAP diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 8d3ee8b6d41d..f1cbff21843e 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o obj-$(CONFIG_REGULATOR_MT6359) += mt6359-regulator.o obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o +obj-$(CONFIG_REGULATOR_MT6370) += mt6370-regulator.o obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c new file mode 100644 index 000000000000..949b2c7b5556 --- /dev/null +++ b/drivers/regulator/mt6370-regulator.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/bits.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +enum { + MT6370_IDX_DSVBOOST = 0, + MT6370_IDX_DSVPOS, + MT6370_IDX_DSVNEG, + MT6370_IDX_VIBLDO, + MT6370_MAX_IDX +}; + +#define MT6370_REG_LDO_CFG 0x180 +#define MT6370_REG_LDO_VOUT 0x181 +#define MT6370_REG_DB_CTRL1 0x1B0 +#define MT6370_REG_DB_CTRL2 0x1B1 +#define MT6370_REG_DB_VBST 0x1B2 +#define MT6370_REG_DB_VPOS 0x1B3 +#define MT6370_REG_DB_VNEG 0x1B4 +#define MT6370_REG_LDO_STAT 0x1DC +#define MT6370_REG_DB_STAT 0x1DF + +#define MT6370_LDOOMS_MASK BIT(7) +#define MT6370_LDOEN_MASK BIT(7) +#define MT6370_LDOVOUT_MASK GENMASK(3, 0) +#define MT6370_DBPERD_MASK (BIT(7) | BIT(4)) +#define MT6370_DBEXTEN_MASK BIT(0) +#define MT6370_DBVPOSEN_MASK BIT(6) +#define MT6370_DBVPOSDISG_MASK BIT(5) +#define MT6370_DBVNEGEN_MASK BIT(3) +#define MT6370_DBVNEGDISG_MASK BIT(2) +#define MT6370_DBALLON_MASK (MT6370_DBVPOSEN_MASK | MT6370_DBVNEGEN_MASK) +#define MT6370_DBSLEW_MASK GENMASK(7, 6) +#define MT6370_DBVOUT_MASK GENMASK(5, 0) +#define MT6370_LDOOC_EVT_MASK BIT(7) +#define MT6370_POSSCP_EVT_MASK BIT(7) +#define MT6370_NEGSCP_EVT_MASK BIT(6) +#define MT6370_BSTOCP_EVT_MASK BIT(5) +#define MT6370_POSOCP_EVT_MASK BIT(4) +#define MT6370_NEGOCP_EVT_MASK BIT(3) + +#define MT6370_LDO_MINUV 1600000 +#define MT6370_LDO_STPUV 200000 +#define MT6370_LDO_N_VOLT 13 +#define MT6370_DBVBOOST_MINUV 4000000 +#define MT6370_DBVBOOST_STPUV 50000 +#define MT6370_DBVBOOST_N_VOLT 45 +#define MT6370_DBVOUT_MINUV 4000000 +#define MT6370_DBVOUT_STPUV 50000 +#define MT6370_DBVOUT_N_VOLT 41 + +struct mt6370_priv { + struct device *dev; + struct regmap *regmap; + struct regulator_dev *rdev[MT6370_MAX_IDX]; + bool use_external_ctrl; +}; + +static const unsigned int mt6370_vpos_ramp_tbl[] = { 8540, 5840, 4830, 3000 }; +static const unsigned int mt6370_vneg_ramp_tbl[] = { 10090, 6310, 5050, 3150 }; + +static int mt6370_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + unsigned int stat_reg, stat, rpt_flags = 0; + int rid = rdev_get_id(rdev), ret; + + if (rid == MT6370_IDX_VIBLDO) + stat_reg = MT6370_REG_LDO_STAT; + else + stat_reg = MT6370_REG_DB_STAT; + + ret = regmap_read(regmap, stat_reg, &stat); + if (ret) + return ret; + + switch (rid) { + case MT6370_IDX_DSVBOOST: + if (stat & MT6370_BSTOCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; + break; + case MT6370_IDX_DSVPOS: + if (stat & MT6370_POSSCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; + + if (stat & MT6370_POSOCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; + break; + case MT6370_IDX_DSVNEG: + if (stat & MT6370_NEGSCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; + + if (stat & MT6370_NEGOCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; + break; + default: + if (stat & MT6370_LDOOC_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; + break; + } + + *flags = rpt_flags; + return 0; +} + +static const struct regulator_ops mt6370_dbvboost_ops = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, + .get_error_flags = mt6370_get_error_flags, +}; + +static const struct regulator_ops mt6370_dbvout_ops = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_ramp_delay = regulator_set_ramp_delay_regmap, + .get_error_flags = mt6370_get_error_flags, +}; + +static const struct regulator_ops mt6370_ldo_ops = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = mt6370_get_error_flags, +}; + +static int mt6370_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct mt6370_priv *priv = config->driver_data; + struct gpio_desc *enable_gpio; + int ret; + + enable_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, + GPIOD_OUT_HIGH | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, + desc->name); + if (IS_ERR(enable_gpio)) { + config->ena_gpiod = NULL; + return 0; + } + + /* + * RG control by default + * Only if all are using external pin, change all by external control + */ + if (priv->use_external_ctrl) { + ret = regmap_update_bits(priv->regmap, MT6370_REG_DB_CTRL1, + MT6370_DBEXTEN_MASK, + MT6370_DBEXTEN_MASK); + if (ret) + return ret; + } + + config->ena_gpiod = enable_gpio; + priv->use_external_ctrl = true; + return 0; +} + +static const struct regulator_desc mt6370_regulator_descs[] = { + { + .name = "mt6370-dsv-vbst", + .of_match = of_match_ptr("dsvbst"), + .regulators_node = of_match_ptr("regulators"), + .id = MT6370_IDX_DSVBOOST, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &mt6370_dbvboost_ops, + .min_uV = MT6370_DBVBOOST_MINUV, + .uV_step = MT6370_DBVBOOST_STPUV, + .n_voltages = MT6370_DBVBOOST_N_VOLT, + .vsel_reg = MT6370_REG_DB_VBST, + .vsel_mask = MT6370_DBVOUT_MASK, + .bypass_reg = MT6370_REG_DB_CTRL1, + .bypass_mask = MT6370_DBPERD_MASK, + .bypass_val_on = MT6370_DBPERD_MASK, + }, + { + .name = "mt6370-dsv-vpos", + .of_match = of_match_ptr("dsvpos"), + .regulators_node = of_match_ptr("regulators"), + .id = MT6370_IDX_DSVPOS, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .of_parse_cb = mt6370_of_parse_cb, + .ops = &mt6370_dbvout_ops, + .min_uV = MT6370_DBVOUT_MINUV, + .uV_step = MT6370_DBVOUT_STPUV, + .n_voltages = MT6370_DBVOUT_N_VOLT, + .vsel_reg = MT6370_REG_DB_VPOS, + .vsel_mask = MT6370_DBVOUT_MASK, + .enable_reg = MT6370_REG_DB_CTRL2, + .enable_mask = MT6370_DBVPOSEN_MASK, + .ramp_reg = MT6370_REG_DB_VPOS, + .ramp_mask = MT6370_DBSLEW_MASK, + .ramp_delay_table = mt6370_vpos_ramp_tbl, + .n_ramp_values = ARRAY_SIZE(mt6370_vpos_ramp_tbl), + .active_discharge_reg = MT6370_REG_DB_CTRL2, + .active_discharge_mask = MT6370_DBVPOSDISG_MASK, + .active_discharge_on = MT6370_DBVPOSDISG_MASK, + }, + { + .name = "mt6370-dsv-vneg", + .of_match = of_match_ptr("dsvneg"), + .regulators_node = of_match_ptr("regulators"), + .id = MT6370_IDX_DSVNEG, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .of_parse_cb = mt6370_of_parse_cb, + .ops = &mt6370_dbvout_ops, + .min_uV = MT6370_DBVOUT_MINUV, + .uV_step = MT6370_DBVOUT_STPUV, + .n_voltages = MT6370_DBVOUT_N_VOLT, + .vsel_reg = MT6370_REG_DB_VNEG, + .vsel_mask = MT6370_DBVOUT_MASK, + .enable_reg = MT6370_REG_DB_CTRL2, + .enable_mask = MT6370_DBVNEGEN_MASK, + .ramp_reg = MT6370_REG_DB_VNEG, + .ramp_mask = MT6370_DBSLEW_MASK, + .ramp_delay_table = mt6370_vneg_ramp_tbl, + .n_ramp_values = ARRAY_SIZE(mt6370_vneg_ramp_tbl), + .active_discharge_reg = MT6370_REG_DB_CTRL2, + .active_discharge_mask = MT6370_DBVNEGDISG_MASK, + .active_discharge_on = MT6370_DBVNEGDISG_MASK, + }, + { + .name = "mt6370-vib-ldo", + .of_match = of_match_ptr("vibldo"), + .regulators_node = of_match_ptr("regulators"), + .id = MT6370_IDX_VIBLDO, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &mt6370_ldo_ops, + .min_uV = MT6370_LDO_MINUV, + .uV_step = MT6370_LDO_STPUV, + .n_voltages = MT6370_LDO_N_VOLT, + .vsel_reg = MT6370_REG_LDO_VOUT, + .vsel_mask = MT6370_LDOVOUT_MASK, + .enable_reg = MT6370_REG_LDO_VOUT, + .enable_mask = MT6370_LDOEN_MASK, + .active_discharge_reg = MT6370_REG_LDO_CFG, + .active_discharge_mask = MT6370_LDOOMS_MASK, + .active_discharge_on = MT6370_LDOOMS_MASK, + } +}; + +static irqreturn_t mt6370_scp_handler(int irq, void *data) +{ + struct regulator_dev *rdev = data; + + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, + NULL); + return IRQ_HANDLED; +} + +static irqreturn_t mt6370_ocp_handler(int irq, void *data) +{ + struct regulator_dev *rdev = data; + + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); + return IRQ_HANDLED; +} + +static int mt6370_regulator_irq_register(struct mt6370_priv *priv) +{ + struct platform_device *pdev = to_platform_device(priv->dev); + static const struct { + const char *name; + int rid; + irq_handler_t handler; + } mt6370_irqs[] = { + { "db_vpos_scp", MT6370_IDX_DSVPOS, mt6370_scp_handler }, + { "db_vneg_scp", MT6370_IDX_DSVNEG, mt6370_scp_handler }, + { "db_vbst_ocp", MT6370_IDX_DSVBOOST, mt6370_ocp_handler }, + { "db_vpos_ocp", MT6370_IDX_DSVPOS, mt6370_ocp_handler }, + { "db_vneg_ocp", MT6370_IDX_DSVNEG, mt6370_ocp_handler }, + { "ldo_oc", MT6370_IDX_VIBLDO, mt6370_ocp_handler } + }; + struct regulator_dev *rdev; + int i, irq, ret; + + for (i = 0; i < ARRAY_SIZE(mt6370_irqs); i++) { + irq = platform_get_irq_byname(pdev, mt6370_irqs[i].name); + + rdev = priv->rdev[mt6370_irqs[i].rid]; + + ret = devm_request_threaded_irq(priv->dev, irq, NULL, + mt6370_irqs[i].handler, 0, + mt6370_irqs[i].name, rdev); + if (ret) { + dev_err(priv->dev, + "Failed to register (%d) interrupt\n", i); + return ret; + } + } + + return 0; +} + +static int mt6370_regualtor_register(struct mt6370_priv *priv) +{ + struct regulator_dev *rdev; + struct regulator_config cfg = {}; + struct device *parent = priv->dev->parent; + int i; + + cfg.dev = parent; + cfg.driver_data = priv; + + for (i = 0; i < MT6370_MAX_IDX; i++) { + rdev = devm_regulator_register(priv->dev, + mt6370_regulator_descs + i, + &cfg); + if (IS_ERR(rdev)) { + dev_err(priv->dev, + "Failed to register (%d) regulator\n", i); + return PTR_ERR(rdev); + } + + priv->rdev[i] = rdev; + } + + return 0; +} + +static int mt6370_regulator_probe(struct platform_device *pdev) +{ + struct mt6370_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) { + dev_err(&pdev->dev, "Failed to init regmap\n"); + return -ENODEV; + } + + ret = mt6370_regualtor_register(priv); + if (ret) + return ret; + + return mt6370_regulator_irq_register(priv); +} + +static const struct platform_device_id mt6370_devid_table[] = { + { "mt6370-regulator", 0}, + {} +}; +MODULE_DEVICE_TABLE(platform, mt6370_devid_table); + +static struct platform_driver mt6370_regulator_driver = { + .driver = { + .name = "mt6370-regulator", + }, + .id_table = mt6370_devid_table, + .probe = mt6370_regulator_probe, +}; +module_platform_driver(mt6370_regulator_driver); + +MODULE_AUTHOR("ChiYuan Huang cy_huang@richtek.com"); +MODULE_DESCRIPTION("Mediatek MT6370 Regulator Driver"); +MODULE_LICENSE("GPL v2");
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 ADC support.
Signed-off-by: ChiaEn Wu chiaen_wu@richtek.com --- drivers/iio/adc/Kconfig | 9 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/mt6370-adc.c | 257 +++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 drivers/iio/adc/mt6370-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 71ab0a06aa82..d7932dd9b773 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -737,6 +737,15 @@ config MEDIATEK_MT6360_ADC is used in smartphones and tablets and supports a 11 channel general purpose ADC.
+config MEDIATEK_MT6370_ADC + tristate "Mediatek MT6370 ADC driver" + depends on MFD_MT6370 + help + Say Y here to enable MT6370 ADC support. + Integrated for System Monitoring includes + is used in smartphones and tablets and supports a 9 channel + general purpose ADC. + config MEDIATEK_MT6577_AUXADC tristate "MediaTek AUXADC driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 39d806f6d457..0ce285c7e2d0 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MCP3911) += mcp3911.o obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o +obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o diff --git a/drivers/iio/adc/mt6370-adc.c b/drivers/iio/adc/mt6370-adc.c new file mode 100644 index 000000000000..3320ebca17ad --- /dev/null +++ b/drivers/iio/adc/mt6370-adc.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <dt-bindings/iio/adc/mediatek,mt6370_adc.h> +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/iio/iio.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MT6370_REG_CHG_CTRL3 0x113 /* AICR */ +#define MT6370_REG_CHG_CTRL7 0x117 /* ICHG */ +#define MT6370_REG_CHG_ADC 0x121 +#define MT6370_REG_ADC_DATA_H 0x14C + +#define MT6370_ADC_START_MASK BIT(0) +#define MT6370_ADC_IN_SEL_MASK GENMASK(7, 4) +#define MT6370_AICR_ICHG_MASK GENMASK(7, 2) + +#define MT6370_ADC_CHAN_SHIFT 4 + +#define MT6370_AICR_400MA 0x6 +#define MT6370_ICHG_500MA 0x4 +#define MT6370_ICHG_900MA 0x8 + +#define ADC_CONV_TIME_US 35000 +#define ADC_CONV_POLLING_TIME 1000 + +struct mt6370_adc_data { + struct device *dev; + struct regmap *regmap; + struct mutex lock; +}; + +static int mt6370_adc_read_channel(struct mt6370_adc_data *priv, int chan, + unsigned long addr, int *val) +{ + __be16 be_val; + unsigned int reg_val; + int ret; + + mutex_lock(&priv->lock); + + reg_val = MT6370_ADC_START_MASK | (addr << MT6370_ADC_CHAN_SHIFT); + ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, reg_val); + if (ret) + goto adc_unlock; + + msleep(ADC_CONV_TIME_US / 1000); + + ret = regmap_read_poll_timeout(priv->regmap, + MT6370_REG_CHG_ADC, reg_val, + !(reg_val & MT6370_ADC_START_MASK), + ADC_CONV_POLLING_TIME, + ADC_CONV_TIME_US * 3); + if (ret) { + if (ret == -ETIMEDOUT) + dev_err(priv->dev, "Failed to wait adc conversion\n"); + goto adc_unlock; + } + + ret = regmap_raw_read(priv->regmap, MT6370_REG_ADC_DATA_H, + &be_val, sizeof(be_val)); + if (ret) + goto adc_unlock; + + *val = be16_to_cpu(be_val); + ret = IIO_VAL_INT; + +adc_unlock: + mutex_unlock(&priv->lock); + + return ret; +} + +static int mt6370_adc_read_scale(struct mt6370_adc_data *priv, + int chan, int *val1, int *val2) +{ + unsigned int reg_val; + int ret; + + switch (chan) { + case MT6370_CHAN_VBAT: + case MT6370_CHAN_VSYS: + case MT6370_CHAN_CHG_VDDP: + *val1 = 5000; + return IIO_VAL_INT; + case MT6370_CHAN_IBUS: + ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL3, ®_val); + if (ret) + return ret; + + reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val); + if (reg_val < MT6370_AICR_400MA) + *val1 = 33500; + else + *val1 = 50000; + + return IIO_VAL_INT; + case MT6370_CHAN_IBAT: + ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL7, ®_val); + if (ret) + return ret; + + reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val); + if (reg_val < MT6370_ICHG_500MA) + *val1 = 23750; + else if (reg_val >= MT6370_ICHG_500MA && + reg_val < MT6370_ICHG_900MA) + *val1 = 26800; + else + *val1 = 50000; + + return IIO_VAL_INT; + case MT6370_CHAN_VBUSDIV5: + *val1 = 25000; + return IIO_VAL_INT; + case MT6370_CHAN_VBUSDIV2: + *val1 = 50000; + return IIO_VAL_INT; + case MT6370_CHAN_TS_BAT: + *val1 = 25; + *val2 = 10000; + return IIO_VAL_FRACTIONAL; + case MT6370_CHAN_TEMP_JC: + *val1 = 2; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int mt6370_adc_read_offset(struct mt6370_adc_data *priv, + int chan, int *val) +{ + *val = (chan == MT6370_CHAN_TEMP_JC) ? -20 : 0; + return IIO_VAL_INT; +} + +static int mt6370_adc_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mt6370_adc_data *priv = iio_priv(iio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return mt6370_adc_read_channel(priv, chan->channel, + chan->address, val); + case IIO_CHAN_INFO_SCALE: + return mt6370_adc_read_scale(priv, chan->channel, val, val2); + case IIO_CHAN_INFO_OFFSET: + return mt6370_adc_read_offset(priv, chan->channel, val); + } + + return -EINVAL; +} + +static const char * const mt6370_channel_labels[MT6370_CHAN_MAX] = { + "vbusdiv5", "vbusdiv2", "vsys", "vbat", + "ts_bat", "ibus", "ibat", "chg_vddp", + "temp_jc", +}; + +static int mt6370_adc_read_label(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, char *label) +{ + return snprintf(label, PAGE_SIZE, "%s\n", + mt6370_channel_labels[chan->channel]); +} + +static const struct iio_info mt6370_adc_iio_info = { + .read_raw = mt6370_adc_read_raw, + .read_label = mt6370_adc_read_label, +}; + +#define MT6370_ADC_CHAN(_idx, _type, _addr) { \ + .type = _type, \ + .channel = MT6370_CHAN_##_idx, \ + .address = _addr, \ + .scan_index = MT6370_CHAN_##_idx, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +static const struct iio_chan_spec mt6370_adc_channels[] = { + MT6370_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE, 1), + MT6370_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE, 2), + MT6370_ADC_CHAN(VSYS, IIO_VOLTAGE, 3), + MT6370_ADC_CHAN(VBAT, IIO_VOLTAGE, 4), + MT6370_ADC_CHAN(TS_BAT, IIO_VOLTAGE, 6), + MT6370_ADC_CHAN(IBUS, IIO_CURRENT, 8), + MT6370_ADC_CHAN(IBAT, IIO_CURRENT, 9), + MT6370_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE, 11), + MT6370_ADC_CHAN(TEMP_JC, IIO_TEMP, 12), +}; + +static int mt6370_adc_probe(struct platform_device *pdev) +{ + int ret; + struct mt6370_adc_data *priv; + struct regmap *regmap; + struct iio_dev *indio_dev; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + dev_err(&pdev->dev, "Failed to get regmap\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->dev = &pdev->dev; + priv->regmap = regmap; + mutex_init(&priv->lock); + + ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, 0); + if (ret) { + dev_err(&pdev->dev, "Failed to reset adc\n"); + return ret; + } + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &mt6370_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mt6370_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(mt6370_adc_channels); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static const struct of_device_id mt6370_adc_of_id[] = { + { .compatible = "mediatek,mt6370-adc", }, + {} +}; +MODULE_DEVICE_TABLE(of, mt6370_adc_of_id); + +static struct platform_driver mt6370_adc_driver = { + .driver = { + .name = "mt6370-adc", + .of_match_table = mt6370_adc_of_id, + }, + .probe = mt6370_adc_probe, +}; +module_platform_driver(mt6370_adc_driver); + +MODULE_AUTHOR("ChiaEn Wu chiaen_wu@richtek.com"); +MODULE_DESCRIPTION("MT6370 ADC Drvier"); +MODULE_LICENSE("GPL v2");
On Tue, 31 May 2022 19:18:56 +0800 ChiaEn Wu peterwu.pub@gmail.com wrote:
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 ADC support.
Signed-off-by: ChiaEn Wu chiaen_wu@richtek.com
Hi ChiaEn,
A few comments inline.
Thanks,
Jonathan
drivers/iio/adc/Kconfig | 9 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/mt6370-adc.c | 257 +++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 drivers/iio/adc/mt6370-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 71ab0a06aa82..d7932dd9b773 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -737,6 +737,15 @@ config MEDIATEK_MT6360_ADC is used in smartphones and tablets and supports a 11 channel general purpose ADC.
+config MEDIATEK_MT6370_ADC
- tristate "Mediatek MT6370 ADC driver"
- depends on MFD_MT6370
- help
Say Y here to enable MT6370 ADC support.
Integrated for System Monitoring includes
The wrapping of this text needs cleaning up.
is used in smartphones and tablets and supports a 9 channel
general purpose ADC.
config MEDIATEK_MT6577_AUXADC tristate "MediaTek AUXADC driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 39d806f6d457..0ce285c7e2d0 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MCP3911) += mcp3911.o obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o +obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o diff --git a/drivers/iio/adc/mt6370-adc.c b/drivers/iio/adc/mt6370-adc.c new file mode 100644 index 000000000000..3320ebca17ad --- /dev/null +++ b/drivers/iio/adc/mt6370-adc.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0
+#include <dt-bindings/iio/adc/mediatek,mt6370_adc.h> +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/iio/iio.h> +#include <linux/irq.h>
Not seeing any interrupt support in here.
+#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h>
+#define MT6370_REG_CHG_CTRL3 0x113 /* AICR */ +#define MT6370_REG_CHG_CTRL7 0x117 /* ICHG */ +#define MT6370_REG_CHG_ADC 0x121 +#define MT6370_REG_ADC_DATA_H 0x14C
+#define MT6370_ADC_START_MASK BIT(0) +#define MT6370_ADC_IN_SEL_MASK GENMASK(7, 4) +#define MT6370_AICR_ICHG_MASK GENMASK(7, 2)
+#define MT6370_ADC_CHAN_SHIFT 4
Prefer using a mask and then FIELD_PREP
+#define MT6370_AICR_400MA 0x6 +#define MT6370_ICHG_500MA 0x4 +#define MT6370_ICHG_900MA 0x8
+#define ADC_CONV_TIME_US 35000 +#define ADC_CONV_POLLING_TIME 1000
+struct mt6370_adc_data {
- struct device *dev;
- struct regmap *regmap;
- struct mutex lock;
All locks need documentation. What is the scope of the lock? Looks like it protects device state when doing setup, wait for read, read cycles.
+};
+static int mt6370_adc_read_channel(struct mt6370_adc_data *priv, int chan,
unsigned long addr, int *val)
+{
- __be16 be_val;
- unsigned int reg_val;
- int ret;
- mutex_lock(&priv->lock);
- reg_val = MT6370_ADC_START_MASK | (addr << MT6370_ADC_CHAN_SHIFT);
FIELD_PREP for that shift?
- ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, reg_val);
- if (ret)
goto adc_unlock;
- msleep(ADC_CONV_TIME_US / 1000);
- ret = regmap_read_poll_timeout(priv->regmap,
MT6370_REG_CHG_ADC, reg_val,
!(reg_val & MT6370_ADC_START_MASK),
ADC_CONV_POLLING_TIME,
ADC_CONV_TIME_US * 3);
- if (ret) {
if (ret == -ETIMEDOUT)
dev_err(priv->dev, "Failed to wait adc conversion\n");
goto adc_unlock;
- }
- ret = regmap_raw_read(priv->regmap, MT6370_REG_ADC_DATA_H,
&be_val, sizeof(be_val));
- if (ret)
goto adc_unlock;
- *val = be16_to_cpu(be_val);
- ret = IIO_VAL_INT;
+adc_unlock:
- mutex_unlock(&priv->lock);
- return ret;
+}
+static int mt6370_adc_read_scale(struct mt6370_adc_data *priv,
int chan, int *val1, int *val2)
+{
- unsigned int reg_val;
- int ret;
- switch (chan) {
- case MT6370_CHAN_VBAT:
- case MT6370_CHAN_VSYS:
- case MT6370_CHAN_CHG_VDDP:
*val1 = 5000;
This seems very large. Voltages are in millivolts as per Documentation/ABI/testing/sysfs-bus-iio and this means each step is 5 volts.
So value in mv is currently 5 * _raw
return IIO_VAL_INT;
- case MT6370_CHAN_IBUS:
ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL3, ®_val);
if (ret)
return ret;
reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val);
if (reg_val < MT6370_AICR_400MA)
*val1 = 33500;
else
*val1 = 50000;
return IIO_VAL_INT;
- case MT6370_CHAN_IBAT:
ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL7, ®_val);
if (ret)
return ret;
reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val);
if (reg_val < MT6370_ICHG_500MA)
*val1 = 23750;
else if (reg_val >= MT6370_ICHG_500MA &&
reg_val < MT6370_ICHG_900MA)
*val1 = 26800;
else
*val1 = 50000;
return IIO_VAL_INT;
- case MT6370_CHAN_VBUSDIV5:
*val1 = 25000;
return IIO_VAL_INT;
- case MT6370_CHAN_VBUSDIV2:
*val1 = 50000;
return IIO_VAL_INT;
- case MT6370_CHAN_TS_BAT:
*val1 = 25;
*val2 = 10000;
return IIO_VAL_FRACTIONAL;
- case MT6370_CHAN_TEMP_JC:
*val1 = 2;
return IIO_VAL_INT;
- }
- return -EINVAL;
+}
+static int mt6370_adc_read_offset(struct mt6370_adc_data *priv,
int chan, int *val)
+{
- *val = (chan == MT6370_CHAN_TEMP_JC) ? -20 : 0;
Offset default is 0, so for channels where it doesn't apply don't provide the offset attribute at all.
- return IIO_VAL_INT;
+}
+static int mt6370_adc_read_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
+{
- struct mt6370_adc_data *priv = iio_priv(iio_dev);
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
return mt6370_adc_read_channel(priv, chan->channel,
chan->address, val);
- case IIO_CHAN_INFO_SCALE:
return mt6370_adc_read_scale(priv, chan->channel, val, val2);
- case IIO_CHAN_INFO_OFFSET:
return mt6370_adc_read_offset(priv, chan->channel, val);
- }
- return -EINVAL;
+}
+static const char * const mt6370_channel_labels[MT6370_CHAN_MAX] = {
Perhaps define an enum with which to index this and the chan spec and hence ensure they end up matching. [vbusdiv5] = "vbusdiv5", etc
- "vbusdiv5", "vbusdiv2", "vsys", "vbat",
- "ts_bat", "ibus", "ibat", "chg_vddp",
- "temp_jc",
+};
+static int mt6370_adc_read_label(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan, char *label)
+{
- return snprintf(label, PAGE_SIZE, "%s\n",
mt6370_channel_labels[chan->channel]);
+}
+static const struct iio_info mt6370_adc_iio_info = {
- .read_raw = mt6370_adc_read_raw,
- .read_label = mt6370_adc_read_label,
+};
+#define MT6370_ADC_CHAN(_idx, _type, _addr) { \
- .type = _type, \
- .channel = MT6370_CHAN_##_idx, \
- .address = _addr, \
- .scan_index = MT6370_CHAN_##_idx, \
- .indexed = 1, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
See above. Only temp_jc channel should hav an offset.
+}
+static const struct iio_chan_spec mt6370_adc_channels[] = {
- MT6370_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE, 1),
- MT6370_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE, 2),
- MT6370_ADC_CHAN(VSYS, IIO_VOLTAGE, 3),
- MT6370_ADC_CHAN(VBAT, IIO_VOLTAGE, 4),
- MT6370_ADC_CHAN(TS_BAT, IIO_VOLTAGE, 6),
- MT6370_ADC_CHAN(IBUS, IIO_CURRENT, 8),
- MT6370_ADC_CHAN(IBAT, IIO_CURRENT, 9),
- MT6370_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE, 11),
- MT6370_ADC_CHAN(TEMP_JC, IIO_TEMP, 12),
+};
+static int mt6370_adc_probe(struct platform_device *pdev) +{
- int ret;
- struct mt6370_adc_data *priv;
- struct regmap *regmap;
- struct iio_dev *indio_dev;
- regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!regmap) {
dev_err(&pdev->dev, "Failed to get regmap\n");
return -ENODEV;
- }
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
- if (!indio_dev)
return -ENOMEM;
- priv = iio_priv(indio_dev);
- priv->dev = &pdev->dev;
- priv->regmap = regmap;
- mutex_init(&priv->lock);
- ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, 0);
- if (ret) {
dev_err(&pdev->dev, "Failed to reset adc\n");
return ret;
- }
- indio_dev->name = dev_name(&pdev->dev);
What does this end up as? It's used for userspace ABI and should correspond to the part number, "mt6370-adc" probably appropriate in this case (I think it'll end up as simply "adc.x" currently?) Normally we just hard code this in the driver for whatever devices the driver supports.
- indio_dev->info = &mt6370_adc_iio_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = mt6370_adc_channels;
- indio_dev->num_channels = ARRAY_SIZE(mt6370_adc_channels);
- return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+static const struct of_device_id mt6370_adc_of_id[] = {
- { .compatible = "mediatek,mt6370-adc", },
- {}
+}; +MODULE_DEVICE_TABLE(of, mt6370_adc_of_id);
+static struct platform_driver mt6370_adc_driver = {
- .driver = {
.name = "mt6370-adc",
.of_match_table = mt6370_adc_of_id,
- },
- .probe = mt6370_adc_probe,
+}; +module_platform_driver(mt6370_adc_driver);
+MODULE_AUTHOR("ChiaEn Wu chiaen_wu@richtek.com"); +MODULE_DESCRIPTION("MT6370 ADC Drvier"); +MODULE_LICENSE("GPL v2");
Hi Jonathan,
Sorry for sending the same email again. I miss to reply all in the last email..
Thanks for your valuable suggestions!
Jonathan Cameron Jonathan.Cameron@huawei.com 於 2022年5月31日 週二 下午9:21寫道:
On Tue, 31 May 2022 19:18:56 +0800 ChiaEn Wu peterwu.pub@gmail.com wrote:
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 ADC support.
Signed-off-by: ChiaEn Wu chiaen_wu@richtek.com
Hi ChiaEn,
A few comments inline.
Thanks,
Jonathan
drivers/iio/adc/Kconfig | 9 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/mt6370-adc.c | 257 +++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 drivers/iio/adc/mt6370-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 71ab0a06aa82..d7932dd9b773 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -737,6 +737,15 @@ config MEDIATEK_MT6360_ADC is used in smartphones and tablets and supports a 11 channel general purpose ADC.
+config MEDIATEK_MT6370_ADC
tristate "Mediatek MT6370 ADC driver"
depends on MFD_MT6370
help
Say Y here to enable MT6370 ADC support.
Integrated for System Monitoring includes
The wrapping of this text needs cleaning up.
is used in smartphones and tablets and supports a 9 channel
general purpose ADC.
config MEDIATEK_MT6577_AUXADC tristate "MediaTek AUXADC driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 39d806f6d457..0ce285c7e2d0 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MCP3911) += mcp3911.o obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o +obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o diff --git a/drivers/iio/adc/mt6370-adc.c b/drivers/iio/adc/mt6370-adc.c new file mode 100644 index 000000000000..3320ebca17ad --- /dev/null +++ b/drivers/iio/adc/mt6370-adc.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0
+#include <dt-bindings/iio/adc/mediatek,mt6370_adc.h> +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/iio/iio.h> +#include <linux/irq.h>
Not seeing any interrupt support in here.
Sorry for that I forgot remove this line, I'll refine it in the next patch.
+#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h>
+#define MT6370_REG_CHG_CTRL3 0x113 /* AICR */ +#define MT6370_REG_CHG_CTRL7 0x117 /* ICHG */ +#define MT6370_REG_CHG_ADC 0x121 +#define MT6370_REG_ADC_DATA_H 0x14C
+#define MT6370_ADC_START_MASK BIT(0) +#define MT6370_ADC_IN_SEL_MASK GENMASK(7, 4) +#define MT6370_AICR_ICHG_MASK GENMASK(7, 2)
+#define MT6370_ADC_CHAN_SHIFT 4
Prefer using a mask and then FIELD_PREP
OK, I got it!
+#define MT6370_AICR_400MA 0x6 +#define MT6370_ICHG_500MA 0x4 +#define MT6370_ICHG_900MA 0x8
+#define ADC_CONV_TIME_US 35000 +#define ADC_CONV_POLLING_TIME 1000
+struct mt6370_adc_data {
struct device *dev;
struct regmap *regmap;
struct mutex lock;
All locks need documentation. What is the scope of the lock? Looks like it protects device state when doing setup, wait for read, read cycles.
This mutex lock is for preventing the different adc channel from being read at the same time. So, if I just change its name to adc_chan_lock or adc_lock and add the comment for this mutex lock, does this change meet your requirement?
+};
+static int mt6370_adc_read_channel(struct mt6370_adc_data *priv, int chan,
unsigned long addr, int *val)
+{
__be16 be_val;
unsigned int reg_val;
int ret;
mutex_lock(&priv->lock);
reg_val = MT6370_ADC_START_MASK | (addr << MT6370_ADC_CHAN_SHIFT);
FIELD_PREP for that shift?
I got it! I'll refine it in the next patch.
ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, reg_val);
if (ret)
goto adc_unlock;
msleep(ADC_CONV_TIME_US / 1000);
ret = regmap_read_poll_timeout(priv->regmap,
MT6370_REG_CHG_ADC, reg_val,
!(reg_val & MT6370_ADC_START_MASK),
ADC_CONV_POLLING_TIME,
ADC_CONV_TIME_US * 3);
if (ret) {
if (ret == -ETIMEDOUT)
dev_err(priv->dev, "Failed to wait adc conversion\n");
goto adc_unlock;
}
ret = regmap_raw_read(priv->regmap, MT6370_REG_ADC_DATA_H,
&be_val, sizeof(be_val));
if (ret)
goto adc_unlock;
*val = be16_to_cpu(be_val);
ret = IIO_VAL_INT;
+adc_unlock:
mutex_unlock(&priv->lock);
return ret;
+}
+static int mt6370_adc_read_scale(struct mt6370_adc_data *priv,
int chan, int *val1, int *val2)
+{
unsigned int reg_val;
int ret;
switch (chan) {
case MT6370_CHAN_VBAT:
case MT6370_CHAN_VSYS:
case MT6370_CHAN_CHG_VDDP:
*val1 = 5000;
This seems very large. Voltages are in millivolts as per Documentation/ABI/testing/sysfs-bus-iio and this means each step is 5 volts.
So value in mv is currently 5 * _raw
OK, I got it. Also, I will add the ABI file in the next version. Thanks!
return IIO_VAL_INT;
case MT6370_CHAN_IBUS:
ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL3, ®_val);
if (ret)
return ret;
reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val);
if (reg_val < MT6370_AICR_400MA)
*val1 = 33500;
else
*val1 = 50000;
return IIO_VAL_INT;
case MT6370_CHAN_IBAT:
ret = regmap_read(priv->regmap, MT6370_REG_CHG_CTRL7, ®_val);
if (ret)
return ret;
reg_val = FIELD_GET(MT6370_AICR_ICHG_MASK, reg_val);
if (reg_val < MT6370_ICHG_500MA)
*val1 = 23750;
else if (reg_val >= MT6370_ICHG_500MA &&
reg_val < MT6370_ICHG_900MA)
*val1 = 26800;
else
*val1 = 50000;
return IIO_VAL_INT;
case MT6370_CHAN_VBUSDIV5:
*val1 = 25000;
return IIO_VAL_INT;
case MT6370_CHAN_VBUSDIV2:
*val1 = 50000;
return IIO_VAL_INT;
case MT6370_CHAN_TS_BAT:
*val1 = 25;
*val2 = 10000;
return IIO_VAL_FRACTIONAL;
case MT6370_CHAN_TEMP_JC:
*val1 = 2;
return IIO_VAL_INT;
}
return -EINVAL;
+}
+static int mt6370_adc_read_offset(struct mt6370_adc_data *priv,
int chan, int *val)
+{
*val = (chan == MT6370_CHAN_TEMP_JC) ? -20 : 0;
Offset default is 0, so for channels where it doesn't apply don't provide the offset attribute at all.
OK!
return IIO_VAL_INT;
+}
+static int mt6370_adc_read_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
+{
struct mt6370_adc_data *priv = iio_priv(iio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return mt6370_adc_read_channel(priv, chan->channel,
chan->address, val);
case IIO_CHAN_INFO_SCALE:
return mt6370_adc_read_scale(priv, chan->channel, val, val2);
case IIO_CHAN_INFO_OFFSET:
return mt6370_adc_read_offset(priv, chan->channel, val);
}
return -EINVAL;
+}
+static const char * const mt6370_channel_labels[MT6370_CHAN_MAX] = {
Perhaps define an enum with which to index this and the chan spec and hence ensure they end up matching. [vbusdiv5] = "vbusdiv5", etc
Do you mean that I can refine this const char array to the following array??
static const char * const mt6370_channel_labels[MT6370_CHAN_MAX] = { [MT6370_CHAN_VBUSDIV5] = "vbusdiv5", [MT6370_CHAN_VBUSDIV2] = "vbusdiv2", ... ... [MT6370_CHAN_TEMP_JC] = "temp_jc", };
"vbusdiv5", "vbusdiv2", "vsys", "vbat",
"ts_bat", "ibus", "ibat", "chg_vddp",
"temp_jc",
+};
+static int mt6370_adc_read_label(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan, char *label)
+{
return snprintf(label, PAGE_SIZE, "%s\n",
mt6370_channel_labels[chan->channel]);
+}
+static const struct iio_info mt6370_adc_iio_info = {
.read_raw = mt6370_adc_read_raw,
.read_label = mt6370_adc_read_label,
+};
+#define MT6370_ADC_CHAN(_idx, _type, _addr) { \
.type = _type, \
.channel = MT6370_CHAN_##_idx, \
.address = _addr, \
.scan_index = MT6370_CHAN_##_idx, \
.indexed = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
See above. Only temp_jc channel should hav an offset.
OK!
+}
+static const struct iio_chan_spec mt6370_adc_channels[] = {
MT6370_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE, 1),
MT6370_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE, 2),
MT6370_ADC_CHAN(VSYS, IIO_VOLTAGE, 3),
MT6370_ADC_CHAN(VBAT, IIO_VOLTAGE, 4),
MT6370_ADC_CHAN(TS_BAT, IIO_VOLTAGE, 6),
MT6370_ADC_CHAN(IBUS, IIO_CURRENT, 8),
MT6370_ADC_CHAN(IBAT, IIO_CURRENT, 9),
MT6370_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE, 11),
MT6370_ADC_CHAN(TEMP_JC, IIO_TEMP, 12),
+};
+static int mt6370_adc_probe(struct platform_device *pdev) +{
int ret;
struct mt6370_adc_data *priv;
struct regmap *regmap;
struct iio_dev *indio_dev;
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!regmap) {
dev_err(&pdev->dev, "Failed to get regmap\n");
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
priv = iio_priv(indio_dev);
priv->dev = &pdev->dev;
priv->regmap = regmap;
mutex_init(&priv->lock);
ret = regmap_write(priv->regmap, MT6370_REG_CHG_ADC, 0);
if (ret) {
dev_err(&pdev->dev, "Failed to reset adc\n");
return ret;
}
indio_dev->name = dev_name(&pdev->dev);
What does this end up as? It's used for userspace ABI and should correspond to the part number, "mt6370-adc" probably appropriate in this case (I think it'll end up as simply "adc.x" currently?) Normally we just hard code this in the driver for whatever devices the driver supports.
I got it, I will change this name to "mt6370-adc" in the next version! Thanks!
indio_dev->info = &mt6370_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mt6370_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(mt6370_adc_channels);
return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+static const struct of_device_id mt6370_adc_of_id[] = {
{ .compatible = "mediatek,mt6370-adc", },
{}
+}; +MODULE_DEVICE_TABLE(of, mt6370_adc_of_id);
+static struct platform_driver mt6370_adc_driver = {
.driver = {
.name = "mt6370-adc",
.of_match_table = mt6370_adc_of_id,
},
.probe = mt6370_adc_probe,
+}; +module_platform_driver(mt6370_adc_driver);
+MODULE_AUTHOR("ChiaEn Wu chiaen_wu@richtek.com"); +MODULE_DESCRIPTION("MT6370 ADC Drvier"); +MODULE_LICENSE("GPL v2");
Best regards, ChiaEn Wu
+#define MT6370_AICR_400MA 0x6 +#define MT6370_ICHG_500MA 0x4 +#define MT6370_ICHG_900MA 0x8
+#define ADC_CONV_TIME_US 35000 +#define ADC_CONV_POLLING_TIME 1000
+struct mt6370_adc_data {
struct device *dev;
struct regmap *regmap;
struct mutex lock;
All locks need documentation. What is the scope of the lock? Looks like it protects device state when doing setup, wait for read, read cycles.
This mutex lock is for preventing the different adc channel from being read at the same time. So, if I just change its name to adc_chan_lock or adc_lock and add the comment for this mutex lock, does this change meet your requirement
Yes
+};
+static int mt6370_adc_read_scale(struct mt6370_adc_data *priv,
int chan, int *val1, int *val2)
+{
unsigned int reg_val;
int ret;
switch (chan) {
case MT6370_CHAN_VBAT:
case MT6370_CHAN_VSYS:
case MT6370_CHAN_CHG_VDDP:
*val1 = 5000;
This seems very large. Voltages are in millivolts as per Documentation/ABI/testing/sysfs-bus-iio and this means each step is 5 volts.
So value in mv is currently 5 * _raw
OK, I got it. Also, I will add the ABI file in the next version. Thanks!
Only add ABI documentation for anything non-standard.
The documentation scripts really don't like repeats!
+static const char * const mt6370_channel_labels[MT6370_CHAN_MAX] = {
Perhaps define an enum with which to index this and the chan spec and hence ensure they end up matching. [vbusdiv5] = "vbusdiv5", etc
Do you mean that I can refine this const char array to the following array??
static const char * const mt6370_channel_labels[MT6370_CHAN_MAX] = { [MT6370_CHAN_VBUSDIV5] = "vbusdiv5", [MT6370_CHAN_VBUSDIV2] = "vbusdiv2", ... ... [MT6370_CHAN_TEMP_JC] = "temp_jc", };
Yes
thanks,
Jonathan
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 charger driver
Signed-off-by: ChiaEn Wu chiaen_wu@richtek.com --- drivers/power/supply/Kconfig | 11 + drivers/power/supply/Makefile | 1 + drivers/power/supply/mt6370-charger.c | 1132 +++++++++++++++++++++++++ 3 files changed, 1144 insertions(+) create mode 100644 drivers/power/supply/mt6370-charger.c
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 1aa8323ad9f6..54d8c3ab01cc 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -619,6 +619,17 @@ config CHARGER_MT6360 Average Input Current Regulation, Battery Temperature Sensing, Over-Temperature Protection, DPDM Detection for BC1.2.
+config CHARGER_MT6370 + tristate "Mediatek MT6370 Charger Driver" + depends on MFD_MT6370 + depends on REGULATOR + select LINEAR_RANGES + help + Say Y here to enable MT6370 Charger Part. + The device supports High-Accuracy Voltage/Current Regulation, + Average Input Current Regulation, Battery Temperature Sensing, + Over-Temperature Protection, DPDM Detection for BC1.2. + config CHARGER_QCOM_SMBB tristate "Qualcomm Switch-Mode Battery Charger and Boost" depends on MFD_SPMI_PMIC || COMPILE_TEST diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 7f02f36aea55..8c9527643c86 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_MP2629) += mp2629_charger.o obj-$(CONFIG_CHARGER_MT6360) += mt6360_charger.o +obj-$(CONFIG_CHARGER_MT6370) += mt6370-charger.o obj-$(CONFIG_CHARGER_QCOM_SMBB) += qcom_smbb.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c new file mode 100644 index 000000000000..1cfa4deeb28e --- /dev/null +++ b/drivers/power/supply/mt6370-charger.c @@ -0,0 +1,1132 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <dt-bindings/iio/adc/mediatek,mt6370_adc.h> +#include <linux/atomic.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/consumer.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/workqueue.h> + +#define MT6370_REG_CHG_CTRL1 0x111 +#define MT6370_REG_CHG_CTRL2 0x112 +#define MT6370_REG_CHG_CTRL3 0x113 +#define MT6370_REG_CHG_CTRL4 0x114 +#define MT6370_REG_CHG_CTRL5 0x115 +#define MT6370_REG_CHG_CTRL6 0x116 +#define MT6370_REG_CHG_CTRL7 0x117 +#define MT6370_REG_CHG_CTRL8 0x118 +#define MT6370_REG_CHG_CTRL9 0x119 +#define MT6370_REG_CHG_CTRL10 0x11A +#define MT6370_REG_DEVICE_TYPE 0x122 +#define MT6370_REG_USB_STATUS1 0x127 +#define MT6370_REG_CHG_STAT 0x14A +#define MT6370_REG_FLED_EN 0x17E +#define MT6370_REG_CHG_STAT1 0X1D0 +#define MT6370_REG_OVPCTRL_STAT 0x1D8 + +#define MT6370_VOBST_MASK GENMASK(7, 2) +#define MT6370_OTG_PIN_EN_MASK BIT(1) +#define MT6370_OPA_MODE_MASK BIT(0) +#define MT6370_OTG_OC_MASK GENMASK(2, 0) + +#define MT6370_MIVR_IBUS_TH 100000 /* 100 mA */ + +enum mt6370_chg_reg_field { + /* MT6370_REG_CHG_CTRL2 */ + F_IINLMTSEL, F_CFO_EN, F_CHG_EN, + /* MT6370_REG_CHG_CTRL3 */ + F_IAICR, F_AICR_EN, F_ILIM_EN, + /* MT6370_REG_CHG_CTRL4 */ + F_VOREG, + /* MT6370_REG_CHG_CTRL5 */ + F_VOBST, + /* MT6370_REG_CHG_CTRL6 */ + F_VMIVR, + /* MT6370_REG_CHG_CTRL7 */ + F_ICHG, + /* MT6370_REG_CHG_CTRL8 */ + F_IPREC, + /* MT6370_REG_CHG_CTRL9 */ + F_IEOC, + /* MT6370_REG_DEVICE_TYPE */ + F_USBCHGEN, + /* MT6370_REG_USB_STATUS1 */ + F_USB_STAT, F_CHGDET, + /* MT6370_REG_CHG_STAT */ + F_CHG_STAT, F_BOOST_STAT, F_VBAT_LVL, + /* MT6370_REG_FLED_EN */ + F_FL_STROBE, + /* MT6370_REG_CHG_STAT1 */ + F_CHG_MIVR_STAT, + /* MT6370_REG_OVPCTRL_STAT */ + F_UVP_D_STAT, + F_MAX, +}; + +struct mt6370_priv { + struct device *dev; + struct iio_channel *iio_adcs; + struct mutex attach_lock; + struct power_supply *psy; + struct power_supply_desc *psy_desc; + struct regmap *regmap; + struct regmap_field *rmap_fields[F_MAX]; + struct regulator_dev *rdev; + struct workqueue_struct *wq; + struct work_struct bc12_work; + struct delayed_work mivr_dwork; + atomic_t attach; + int psy_usb_type; + bool pwr_rdy; +}; + +enum mt6370_usb_status { + MT6370_USB_STAT_NO_VBUS = 0, + MT6370_USB_STAT_VBUS_FLOW_IS_UNDER_GOING, + MT6370_USB_STAT_SDP, + MT6370_USB_STAT_SDP_NSTD, + MT6370_USB_STAT_DCP, + MT6370_USB_STAT_CDP, + MT6370_USB_STAT_MAX, +}; + +struct mt6370_chg_range { + u32 min; + u32 max; + u16 step; + u8 offset; + const u32 *table; + u16 num_table; + bool round_up; +}; + +struct mt6370_chg_field { + const char *name; + const struct mt6370_chg_range *range; + struct reg_field field; +}; + +static const u32 mt6370_chg_otg_oc_ma[] = { + 500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000, +}; + +#define MT6370_CHG_RANGE(_min, _max, _step, _offset, _ru) \ +{ \ + .min = _min, \ + .max = _max, \ + .step = _step, \ + .offset = _offset, \ + .round_up = _ru, \ +} + +#define MT6370_CHG_RANGE_T(_table, _ru) \ +{ \ + .table = _table, \ + .num_table = ARRAY_SIZE(_table), \ + .round_up = _ru, \ +} + +static const struct mt6370_chg_range mt6370_chg_ranges[F_MAX] = { + [F_IAICR] = MT6370_CHG_RANGE(100, 3250, 50, 0, false), + [F_VOREG] = MT6370_CHG_RANGE(3900, 4710, 10, 0, false), + [F_VOBST] = MT6370_CHG_RANGE(4425, 5825, 25, 0, false), + [F_VMIVR] = MT6370_CHG_RANGE(3900, 13400, 100, 0, true), + [F_ICHG] = MT6370_CHG_RANGE(900, 5000, 100, 8, false), + [F_IPREC] = MT6370_CHG_RANGE(100, 850, 50, 0, false), + [F_IEOC] = MT6370_CHG_RANGE(100, 850, 50, 0, false), +}; + +#define MT6370_CHG_FIELD_RANGE(_fd, _reg, _lsb, _msb, _range) \ +[_fd] = { \ + .name = #_fd, \ + .range = _range ? &mt6370_chg_ranges[_fd] : NULL, \ + .field = REG_FIELD(_reg, _lsb, _msb), \ +} + +#define MT6370_CHG_FIELD(_fd, _reg, _lsb, _msb) \ + MT6370_CHG_FIELD_RANGE(_fd, _reg, _lsb, _msb, (_msb > _lsb)) + +static const struct mt6370_chg_field mt6370_chg_fields[F_MAX] = { + MT6370_CHG_FIELD_RANGE(F_IINLMTSEL, MT6370_REG_CHG_CTRL2, 2, 3, false), + MT6370_CHG_FIELD(F_CFO_EN, MT6370_REG_CHG_CTRL2, 1, 1), + MT6370_CHG_FIELD(F_CHG_EN, MT6370_REG_CHG_CTRL2, 0, 0), + MT6370_CHG_FIELD(F_IAICR, MT6370_REG_CHG_CTRL3, 2, 7), + MT6370_CHG_FIELD(F_AICR_EN, MT6370_REG_CHG_CTRL3, 1, 1), + MT6370_CHG_FIELD(F_ILIM_EN, MT6370_REG_CHG_CTRL3, 0, 0), + MT6370_CHG_FIELD(F_VOREG, MT6370_REG_CHG_CTRL4, 1, 7), + MT6370_CHG_FIELD(F_VOBST, MT6370_REG_CHG_CTRL5, 2, 7), + MT6370_CHG_FIELD(F_VMIVR, MT6370_REG_CHG_CTRL6, 1, 7), + MT6370_CHG_FIELD(F_ICHG, MT6370_REG_CHG_CTRL7, 2, 7), + MT6370_CHG_FIELD(F_IPREC, MT6370_REG_CHG_CTRL8, 0, 3), + MT6370_CHG_FIELD(F_IEOC, MT6370_REG_CHG_CTRL9, 4, 7), + MT6370_CHG_FIELD(F_USBCHGEN, MT6370_REG_DEVICE_TYPE, 7, 7), + MT6370_CHG_FIELD_RANGE(F_USB_STAT, MT6370_REG_USB_STATUS1, 4, 6, false), + MT6370_CHG_FIELD(F_CHGDET, MT6370_REG_USB_STATUS1, 3, 3), + MT6370_CHG_FIELD_RANGE(F_CHG_STAT, MT6370_REG_CHG_STAT, 6, 7, false), + MT6370_CHG_FIELD(F_BOOST_STAT, MT6370_REG_CHG_STAT, 3, 3), + MT6370_CHG_FIELD(F_VBAT_LVL, MT6370_REG_CHG_STAT, 5, 5), + MT6370_CHG_FIELD(F_FL_STROBE, MT6370_REG_FLED_EN, 2, 2), + MT6370_CHG_FIELD(F_CHG_MIVR_STAT, MT6370_REG_CHG_STAT1, 6, 6), + MT6370_CHG_FIELD(F_UVP_D_STAT, MT6370_REG_OVPCTRL_STAT, 4, 4), +}; + +static inline u32 mt6370_chg_val_to_reg(const struct mt6370_chg_range *range, + u32 val) +{ + int i; + u8 reg; + + if (!range) + return val; + + if (range->table) { + if (val <= range->table[0]) + return 0; + + for (i = 1; i < range->num_table - 1; i++) { + if (val == range->table[i]) + return i; + if (val > range->table[i] && + val < range->table[i + 1]) + return range->round_up ? i + 1 : i; + } + + return range->num_table - 1; + } + + if (val <= range->min) + reg = 0; + else if (val >= range->max) + reg = (range->max - range->min) / range->step; + else if (range->round_up) + reg = DIV_ROUND_UP(val - range->min, range->step); + else + reg = (val - range->min) / range->step; + + return reg + range->offset; +} + +static inline u32 mt6370_chg_reg_to_val(const struct mt6370_chg_range *range, + u8 reg) +{ + if (!range) + return reg; + + return range->table ? range->table[reg] : + range->min + range->step * (reg - range->offset); +} + +static inline int mt6370_chg_field_get(struct mt6370_priv *priv, + enum mt6370_chg_reg_field fd, u32 *val) +{ + int ret; + u32 reg_val; + + ret = regmap_field_read(priv->rmap_fields[fd], ®_val); + if (ret < 0) + return ret; + + *val = mt6370_chg_reg_to_val(mt6370_chg_fields[fd].range, reg_val); + + return 0; +} + +static inline int mt6370_chg_field_set(struct mt6370_priv *priv, + enum mt6370_chg_reg_field fd, u32 val) +{ + val = mt6370_chg_val_to_reg(mt6370_chg_fields[fd].range, val); + + return regmap_field_write(priv->rmap_fields[fd], val); +} + +enum { + MT6370_CHG_STAT_READY = 0, + MT6370_CHG_STAT_CHARGE_IN_PROGRESS, + MT6370_CHG_STAT_DONE, + MT6370_CHG_STAT_FAULT, + MT6370_CHG_STAT_MAX, +}; + +enum { + MT6370_ATTACH_STAT_DETACH = 0, + MT6370_ATTACH_STAT_ATTACH_BC12_NOT_DONE, + MT6370_ATTACH_STAT_ATTACH_BC12_DONE, + MT6370_ATTACH_STAT_ATTACH_MAX, +}; + +static int mt6370_chg_otg_of_parse_cb(struct device_node *of, + const struct regulator_desc *rdesc, + struct regulator_config *rcfg) +{ + struct mt6370_priv *priv = rcfg->driver_data; + unsigned int val; + int ret = 0; + + rcfg->ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(of), + "enable", 0, + GPIOD_OUT_LOW | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, + rdesc->name); + if (IS_ERR(rcfg->ena_gpiod)) { + dev_err(priv->dev, "Failed to requeset OTG EN Pin\n"); + rcfg->ena_gpiod = NULL; + } else { + val = MT6370_OPA_MODE_MASK | MT6370_OTG_PIN_EN_MASK; + ret = regmap_update_bits(priv->regmap, MT6370_REG_CHG_CTRL1, + val, val); + if (ret) + dev_err(priv->dev, "Failed to set otg bits\n"); + } + + return ret; +} + +static void mt6370_chg_bc12_work_func(struct work_struct *work) +{ + struct mt6370_priv *priv = container_of(work, struct mt6370_priv, + bc12_work); + int ret, attach; + bool rpt_psy = false; + u32 usb_stat; + + mutex_lock(&priv->attach_lock); + attach = atomic_read(&priv->attach); + + switch (attach) { + case MT6370_ATTACH_STAT_DETACH: + usb_stat = 0; + break; + case MT6370_ATTACH_STAT_ATTACH_BC12_DONE: + ret = mt6370_chg_field_get(priv, F_USB_STAT, &usb_stat); + if (ret < 0) { + dev_err(priv->dev, "Failed to get USB status\n"); + goto bc12_work_func_out; + } + break; + case MT6370_ATTACH_STAT_ATTACH_BC12_NOT_DONE: + ret = mt6370_chg_field_set(priv, F_USBCHGEN, attach); + if (ret < 0) + dev_err(priv->dev, "Failed to enable USB CHG EN\n"); + goto bc12_work_func_out; + default: + dev_err(priv->dev, "Invalid attach state\n"); + goto bc12_work_func_out; + } + + rpt_psy = true; + + switch (usb_stat) { + case MT6370_USB_STAT_SDP: + priv->psy_desc->type = POWER_SUPPLY_TYPE_USB; + priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; + break; + case MT6370_USB_STAT_SDP_NSTD: + priv->psy_desc->type = POWER_SUPPLY_TYPE_USB; + priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + case MT6370_USB_STAT_DCP: + priv->psy_desc->type = POWER_SUPPLY_TYPE_USB_DCP; + priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + case MT6370_USB_STAT_CDP: + priv->psy_desc->type = POWER_SUPPLY_TYPE_USB_CDP; + priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP; + break; + case MT6370_USB_STAT_NO_VBUS: + case MT6370_USB_STAT_VBUS_FLOW_IS_UNDER_GOING: + default: + priv->psy_desc->type = POWER_SUPPLY_TYPE_USB; + priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + } + +bc12_work_func_out: + mutex_unlock(&priv->attach_lock); + + if (rpt_psy) + power_supply_changed(priv->psy); +} + +static inline void mt6370_chg_enable_irq(struct mt6370_priv *priv, + const char *irq_name, bool en) +{ + int irq_num; + struct platform_device *pdev = to_platform_device(priv->dev); + + irq_num = platform_get_irq_byname(pdev, irq_name); + + if (irq_num < 0) { + dev_err(priv->dev, "Failed to get platform resource\n"); + } else { + if (en) + enable_irq(irq_num); + else + disable_irq_nosync(irq_num); + } +} + +static int mt6370_chg_toggle_cfo(struct mt6370_priv *priv) +{ + int ret; + u32 fl_strobe; + + /* check if flash led in strobe mode */ + ret = mt6370_chg_field_get(priv, F_FL_STROBE, &fl_strobe); + if (ret < 0) { + dev_err(priv->dev, "Failed to get FL_STROBE_EN\n"); + goto toggle_cfo_exit; + } + + if (fl_strobe) { + dev_err(priv->dev, "Flash led is still in strobe mode\n"); + goto toggle_cfo_exit; + } + + /* cfo off */ + ret = mt6370_chg_field_set(priv, F_CFO_EN, 0); + if (ret < 0) { + dev_err(priv->dev, "Failed to disable CFO_EN\n"); + goto toggle_cfo_exit; + } + + /* cfo on */ + ret = mt6370_chg_field_set(priv, F_CFO_EN, 1); + if (ret < 0) { + dev_err(priv->dev, "Failed to enable CFO_EN\n"); + goto toggle_cfo_exit; + } + +toggle_cfo_exit: + return ret; +} + +static int mt6370_chg_read_adc_chan(struct mt6370_priv *priv, unsigned int chan, + int *val) +{ + int ret; + + if (chan >= MT6370_CHAN_MAX) + return -EINVAL; + + ret = iio_read_channel_processed(&priv->iio_adcs[chan], val); + if (ret) + dev_err(priv->dev, "Failed to read adc\n"); + + return ret; +} + +static void mt6370_chg_mivr_dwork_func(struct work_struct *work) +{ + struct mt6370_priv *priv = container_of(work, struct mt6370_priv, + mivr_dwork.work); + int ret; + u32 mivr_stat; + unsigned int ibus; + + ret = mt6370_chg_field_get(priv, F_CHG_MIVR_STAT, &mivr_stat); + if (ret < 0) { + dev_err(priv->dev, "Failed to get mivr state\n"); + goto mivr_handler_out; + } + + if (!mivr_stat) + goto mivr_handler_out; + + ret = mt6370_chg_read_adc_chan(priv, MT6370_CHAN_IBUS, &ibus); + if (ret) { + dev_err(priv->dev, "Failed to get ibus\n"); + goto mivr_handler_out; + } + + if (ibus < MT6370_MIVR_IBUS_TH) { + ret = mt6370_chg_toggle_cfo(priv); + if (ret) + dev_err(priv->dev, "Failed to toggle cfo\n"); + } + +mivr_handler_out: + mt6370_chg_enable_irq(priv, "mivr", true); + pm_relax(priv->dev); +} + +static void mt6370_chg_pwr_rdy_check(struct mt6370_priv *priv) +{ + int ret; + u32 pwr_rdy, otg_en; + union power_supply_propval val; + + /* Check in otg mode or not */ + ret = mt6370_chg_field_get(priv, F_BOOST_STAT, &otg_en); + if (ret < 0) { + dev_err(priv->dev, "Failed to get otg state\n"); + return; + } + + if (otg_en) + return; + + ret = mt6370_chg_field_get(priv, F_UVP_D_STAT, &pwr_rdy); + if (ret < 0) { + dev_err(priv->dev, "Failed to get pwr_rdy state reg\n"); + return; + } + + val.intval = !pwr_rdy; + + ret = power_supply_set_property(priv->psy, POWER_SUPPLY_PROP_ONLINE, + &val); + if (ret) + dev_err(priv->dev, "Failed to start attach/detach flow\n"); +} + +static int mt6370_chg_get_online(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + mutex_lock(&priv->attach_lock); + val->intval = !!atomic_read(&priv->attach); + mutex_unlock(&priv->attach_lock); + + return 0; +} + +static int mt6370_chg_get_status(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + int ret; + u32 chg_stat; + + ret = mt6370_chg_get_online(priv, val); + if (!val->intval) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } + + ret = mt6370_chg_field_get(priv, F_CHG_STAT, &chg_stat); + if (ret < 0) + return ret; + + switch (chg_stat) { + case MT6370_CHG_STAT_READY: + case MT6370_CHG_STAT_FAULT: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case MT6370_CHG_STAT_CHARGE_IN_PROGRESS: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case MT6370_CHG_STAT_DONE: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + + return ret; +} + +static int mt6370_chg_get_charge_type(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + int type, ret; + u32 chg_stat, vbat_lvl; + + ret = mt6370_chg_field_get(priv, F_CHG_STAT, &chg_stat); + if (ret < 0) + return ret; + + ret = mt6370_chg_field_get(priv, F_VBAT_LVL, &vbat_lvl); + if (ret < 0) + return ret; + + switch (chg_stat) { + case MT6370_CHG_STAT_CHARGE_IN_PROGRESS: + if (vbat_lvl) + type = POWER_SUPPLY_CHARGE_TYPE_FAST; + else + type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case MT6370_CHG_STAT_READY: + case MT6370_CHG_STAT_DONE: + case MT6370_CHG_STAT_FAULT: + default: + type = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + } + + val->intval = type; + + return 0; +} + +static int mt6370_chg_get_ichg(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + return mt6370_chg_field_get(priv, F_ICHG, &val->intval); +} + +static int mt6370_chg_get_max_ichg(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + return mt6370_chg_ranges[F_ICHG].max; +} + +static int mt6370_chg_get_cv(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + return mt6370_chg_field_get(priv, F_VOREG, &val->intval); +} + +static int mt6370_chg_get_max_cv(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + return mt6370_chg_ranges[F_VOREG].max; +} + +static int mt6370_chg_get_aicr(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + return mt6370_chg_field_get(priv, F_IAICR, &val->intval); +} + +static int mt6370_chg_get_mivr(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + return mt6370_chg_field_get(priv, F_VMIVR, &val->intval); +} + +static int mt6370_chg_get_iprechg(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + return mt6370_chg_field_get(priv, F_IPREC, &val->intval); +} + +static int mt6370_chg_get_ieoc(struct mt6370_priv *priv, + union power_supply_propval *val) +{ + return mt6370_chg_field_get(priv, F_IEOC, &val->intval); +} + +static int mt6370_chg_set_online(struct mt6370_priv *priv, + const union power_supply_propval *val) +{ + int attach; + u32 pwr_rdy = !!val->intval; + + mutex_lock(&priv->attach_lock); + attach = atomic_read(&priv->attach); + if (pwr_rdy == !!attach) { + dev_err(priv->dev, "pwr_rdy is same(%d)\n", pwr_rdy); + mutex_unlock(&priv->attach_lock); + return 0; + } + + atomic_set(&priv->attach, pwr_rdy); + mutex_unlock(&priv->attach_lock); + + if (!queue_work(priv->wq, &priv->bc12_work)) + dev_err(priv->dev, "bc12 work has already queued\n"); + + return 0; + +} + +static int mt6370_chg_set_ichg(struct mt6370_priv *priv, + const union power_supply_propval *val) +{ + return mt6370_chg_field_set(priv, F_ICHG, val->intval); +} + +static int mt6370_chg_set_cv(struct mt6370_priv *priv, + const union power_supply_propval *val) +{ + return mt6370_chg_field_set(priv, F_VOREG, val->intval); +} + +static int mt6370_chg_set_aicr(struct mt6370_priv *priv, + const union power_supply_propval *val) +{ + return mt6370_chg_field_set(priv, F_IAICR, val->intval); +} + +static int mt6370_chg_set_mivr(struct mt6370_priv *priv, + const union power_supply_propval *val) +{ + return mt6370_chg_field_set(priv, F_VMIVR, val->intval); +} + +static int mt6370_chg_set_iprechg(struct mt6370_priv *priv, + const union power_supply_propval *val) +{ + return mt6370_chg_field_set(priv, F_IPREC, val->intval); +} + +static int mt6370_chg_set_ieoc(struct mt6370_priv *priv, + const union power_supply_propval *val) +{ + return mt6370_chg_field_set(priv, F_IEOC, val->intval); +} + +static int mt6370_chg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mt6370_priv *priv = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = mt6370_chg_get_online(priv, val); + break; + case POWER_SUPPLY_PROP_STATUS: + ret = mt6370_chg_get_status(priv, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = mt6370_chg_get_charge_type(priv, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = mt6370_chg_get_ichg(priv, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = mt6370_chg_get_max_ichg(priv, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = mt6370_chg_get_cv(priv, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = mt6370_chg_get_max_cv(priv, val); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = mt6370_chg_get_aicr(priv, val); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = mt6370_chg_get_mivr(priv, val); + break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = mt6370_chg_get_iprechg(priv, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = mt6370_chg_get_ieoc(priv, val); + break; + case POWER_SUPPLY_PROP_TYPE: + val->intval = priv->psy_desc->type; + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = priv->psy_usb_type; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mt6370_chg_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct mt6370_priv *priv = power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = mt6370_chg_set_online(priv, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = mt6370_chg_set_ichg(priv, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = mt6370_chg_set_cv(priv, val); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = mt6370_chg_set_aicr(priv, val); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = mt6370_chg_set_mivr(priv, val); + break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = mt6370_chg_set_iprechg(priv, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + ret = mt6370_chg_set_ieoc(priv, val); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int mt6370_chg_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return 1; + default: + return 0; + } +} + +static enum power_supply_property mt6370_chg_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_USB_TYPE, +}; + +static enum power_supply_usb_type mt6370_chg_usb_types[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_CDP, + POWER_SUPPLY_USB_TYPE_DCP, +}; + +static struct power_supply_desc mt6370_chg_psy_desc = { + .type = POWER_SUPPLY_TYPE_USB, + .properties = mt6370_chg_properties, + .num_properties = ARRAY_SIZE(mt6370_chg_properties), + .get_property = mt6370_chg_get_property, + .set_property = mt6370_chg_set_property, + .property_is_writeable = mt6370_chg_property_is_writeable, + .usb_types = mt6370_chg_usb_types, + .num_usb_types = ARRAY_SIZE(mt6370_chg_usb_types), +}; + +static const struct regulator_ops mt6370_chg_otg_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_current_limit = regulator_set_current_limit_regmap, + .get_current_limit = regulator_get_current_limit_regmap, +}; + +static const struct regulator_desc mt6370_chg_otg_rdesc = { + .of_match = "mt6370,otg-vbus", + .of_parse_cb = mt6370_chg_otg_of_parse_cb, + .name = "mt6370,otg-vbus", + .ops = &mt6370_chg_otg_ops, + .owner = THIS_MODULE, + .type = REGULATOR_VOLTAGE, + .min_uV = 4425000, + .uV_step = 25000, + .n_voltages = 57, + .vsel_reg = MT6370_REG_CHG_CTRL5, + .vsel_mask = MT6370_VOBST_MASK, + .enable_reg = MT6370_REG_CHG_CTRL1, + .enable_mask = MT6370_OPA_MODE_MASK, + .curr_table = mt6370_chg_otg_oc_ma, + .n_current_limits = ARRAY_SIZE(mt6370_chg_otg_oc_ma), + .csel_reg = MT6370_REG_CHG_CTRL10, + .csel_mask = MT6370_OTG_OC_MASK, +}; + +static int mt6370_chg_init_rmap_fields(struct mt6370_priv *priv) +{ + int i; + const struct mt6370_chg_field *fds = mt6370_chg_fields; + + for (i = 0; i < F_MAX; i++) { + priv->rmap_fields[i] = devm_regmap_field_alloc(priv->dev, + priv->regmap, + fds[i].field); + if (IS_ERR(priv->rmap_fields[i])) { + dev_err(priv->dev, + "Failed to allocate regmap field [%s]\n", + fds[i].name); + return PTR_ERR(priv->rmap_fields[i]); + } + } + + return 0; +} + +static int mt6370_chg_init_setting(struct mt6370_priv *priv) +{ + int ret; + + priv->wq = create_singlethread_workqueue(dev_name(priv->dev)); + if (IS_ERR(priv->wq)) { + dev_err(priv->dev, "Failed to create workqueue\n"); + return PTR_ERR(priv->wq); + } + + INIT_WORK(&priv->bc12_work, mt6370_chg_bc12_work_func); + INIT_DELAYED_WORK(&priv->mivr_dwork, mt6370_chg_mivr_dwork_func); + mutex_init(&priv->attach_lock); + atomic_set(&priv->attach, 0); + + /* Disable usb_chg_en */ + ret = mt6370_chg_field_set(priv, F_USBCHGEN, 0); + if (ret < 0) { + dev_err(priv->dev, "Failed to disable usb_chg_en\n"); + return ret; + } + + /* ICHG/IEOC Workaroud, ICHG can not be set less than 900mA */ + ret = mt6370_chg_field_set(priv, F_ICHG, 900); + if (ret < 0) { + dev_err(priv->dev, "Failed to set ICHG to 900mA"); + return ret; + } + + /* Change input current limit selection to using IAICR results */ + ret = mt6370_chg_field_set(priv, F_IINLMTSEL, 3); + if (ret < 0) { + dev_err(priv->dev, "Failed to set IINLMTSEL\n"); + return ret; + } + + return 0; +} + +#define MT6370_CHG_DT_PROP_DECL(_name, _type, _field) \ +{ \ + .name = "mediatek,chg-" #_name, \ + .type = MT6370_PARSE_TYPE_##_type, \ + .fd = _field, \ +} + +static int mt6370_chg_init_otg_regulator(struct mt6370_priv *priv) +{ + struct regulator_config rcfg = { + .dev = priv->dev, + .regmap = priv->regmap, + .driver_data = priv, + }; + + priv->rdev = devm_regulator_register(priv->dev, &mt6370_chg_otg_rdesc, + &rcfg); + + return IS_ERR(priv->rdev) ? PTR_ERR(priv->rdev) : 0; +} + +static int mt6370_chg_init_psy(struct mt6370_priv *priv) +{ + struct power_supply_config cfg = { + .drv_data = priv, + .of_node = priv->dev->of_node, + }; + + priv->psy_desc = &mt6370_chg_psy_desc; + priv->psy_desc->name = dev_name(priv->dev); + priv->psy = devm_power_supply_register(priv->dev, priv->psy_desc, &cfg); + + return IS_ERR(priv->psy) ? PTR_ERR(priv->psy) : 0; +} + +static irqreturn_t mt6370_attach_i_handler(int irq, void *data) +{ + struct mt6370_priv *priv = data; + u32 otg_en; + int ret; + + /* Check in otg mode or not */ + ret = mt6370_chg_field_get(priv, F_BOOST_STAT, &otg_en); + if (ret < 0) { + dev_err(priv->dev, "failed to get otg state\n"); + return IRQ_HANDLED; + } + + if (otg_en) + return IRQ_HANDLED; + + mutex_lock(&priv->attach_lock); + atomic_set(&priv->attach, MT6370_ATTACH_STAT_ATTACH_BC12_DONE); + mutex_unlock(&priv->attach_lock); + + if (!queue_work(priv->wq, &priv->bc12_work)) + dev_err(priv->dev, "bc12 work has already queued\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t mt6370_uvp_d_evt_handler(int irq, void *data) +{ + struct mt6370_priv *priv = data; + + mt6370_chg_pwr_rdy_check(priv); + + return IRQ_HANDLED; +} + +static irqreturn_t mt6370_mivr_handler(int irq, void *data) +{ + struct mt6370_priv *priv = data; + + pm_stay_awake(priv->dev); + mt6370_chg_enable_irq(priv, "mivr", false); + schedule_delayed_work(&priv->mivr_dwork, msecs_to_jiffies(200)); + + return IRQ_HANDLED; +} + +#define MT6370_CHG_IRQ(_name) \ +{ \ + .name = #_name, \ + .handler = mt6370_##_name##_handler, \ +} + +static int mt6370_chg_init_irq(struct mt6370_priv *priv) +{ + int i, ret; + const struct { + char *name; + irq_handler_t handler; + } mt6370_chg_irqs[] = { + MT6370_CHG_IRQ(attach_i), + MT6370_CHG_IRQ(uvp_d_evt), + MT6370_CHG_IRQ(mivr), + }; + + for (i = 0; i < ARRAY_SIZE(mt6370_chg_irqs); i++) { + ret = platform_get_irq_byname(to_platform_device(priv->dev), + mt6370_chg_irqs[i].name); + if (ret < 0) { + dev_err(priv->dev, "Failed to get irq %s\n", + mt6370_chg_irqs[i].name); + return ret; + } + + ret = devm_request_threaded_irq(priv->dev, ret, NULL, + mt6370_chg_irqs[i].handler, + IRQF_TRIGGER_FALLING, + dev_name(priv->dev), + priv); + + if (ret < 0) { + dev_err(priv->dev, "Failed to request irq %s\n", + mt6370_chg_irqs[i].name); + return ret; + } + } + + return 0; +} + +static int mt6370_chg_probe(struct platform_device *pdev) +{ + int ret; + struct mt6370_priv *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) { + dev_err(&pdev->dev, "Failed to get regmap\n"); + return -ENODEV; + } + + ret = mt6370_chg_init_rmap_fields(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to init regmap fields\n"); + return ret; + } + + platform_set_drvdata(pdev, priv); + + priv->iio_adcs = devm_iio_channel_get_all(priv->dev); + if (IS_ERR(priv->iio_adcs)) { + dev_err(&pdev->dev, "Failed to get iio adc\n"); + return PTR_ERR(priv->iio_adcs); + } + + ret = mt6370_chg_init_otg_regulator(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to init otg regulator\n"); + return ret; + } + + ret = mt6370_chg_init_psy(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to init psy\n"); + return ret; + } + + ret = mt6370_chg_init_setting(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to init mt6370 private data\n"); + goto probe_out; + } + + ret = mt6370_chg_init_irq(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to init irq\n"); + goto probe_out; + } + + mt6370_chg_pwr_rdy_check(priv); + + return 0; + +probe_out: + destroy_workqueue(priv->wq); + mutex_destroy(&priv->attach_lock); + + return ret; +} + +static int mt6370_chg_remove(struct platform_device *pdev) +{ + struct mt6370_priv *priv = platform_get_drvdata(pdev); + + if (priv) { + mt6370_chg_enable_irq(priv, "mivr", false); + cancel_delayed_work_sync(&priv->mivr_dwork); + destroy_workqueue(priv->wq); + mutex_destroy(&priv->attach_lock); + } + + return 0; +} + +static const struct of_device_id mt6370_chg_of_match[] = { + { .compatible = "mediatek,mt6370-charger", }, + {} +}; +MODULE_DEVICE_TABLE(of, mt6370_chg_of_match); + +static struct platform_driver mt6370_chg_driver = { + .probe = mt6370_chg_probe, + .remove = mt6370_chg_remove, + .driver = { + .name = "mt6370-charger", + .of_match_table = of_match_ptr(mt6370_chg_of_match), + }, +}; +module_platform_driver(mt6370_chg_driver); + +MODULE_AUTHOR("ChiaEn Wu chiaen_wu@richtek.com"); +MODULE_DESCRIPTION("Mediatek MT6370 Charger Driver"); +MODULE_LICENSE("GPL v2");
From: Alice Chen alice_chen@richtek.com
Add Mediatek MT6370 Indicator support
Signed-off-by: Alice Chen alice_chen@richtek.com --- drivers/leds/Kconfig | 11 + drivers/leds/Makefile | 1 + drivers/leds/leds-mt6370.c | 994 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1006 insertions(+) create mode 100644 drivers/leds/leds-mt6370.c
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 6090e647daee..0cb2294b3431 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -244,6 +244,17 @@ config LEDS_MT6323 This option enables support for on-chip LED drivers found on Mediatek MT6323 PMIC.
+config LEDS_MT6370_RGB + tristate "LED Support for Mediatek MT6370 PMIC" + depends on LEDS_CLASS + depends on MFD_MT6370 + select LINEAR_RANGE + help + Support 4 channels and reg/pwm/breath mode. + Isink4 can also use as a CHG_VIN power good Indicator. + Say Y here to enable support for + MT6370_RGB_LED device. + config LEDS_S3C24XX tristate "LED Support for Samsung S3C24XX GPIO LEDs" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e58ecb36360f..b641fd83f49a 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o obj-$(CONFIG_LEDS_MLXREG) += leds-mlxreg.o obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o +obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o diff --git a/drivers/leds/leds-mt6370.c b/drivers/leds/leds-mt6370.c new file mode 100644 index 000000000000..54f654d4f4fc --- /dev/null +++ b/drivers/leds/leds-mt6370.c @@ -0,0 +1,994 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/bitops.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/led-class-multicolor.h> +#include <linux/linear_range.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/property.h> +#include <linux/regmap.h> + +enum { + MT6370_LED_ISNK1 = 0, + MT6370_LED_ISNK2, + MT6370_LED_ISNK3, + MT6370_LED_ISNK4, + MT6370_MAX_LEDS +}; + +enum mt6370_led_mode { + MT6370_LED_PWM_MODE = 0, + MT6370_LED_BREATH_MODE, + MT6370_LED_REG_MODE, + MT6370_LED_MAX_MODE +}; + +enum mt6370_led_field { + F_RGB_EN = 0, + F_CHGIND_EN, + F_LED1_CURR, + F_LED2_CURR, + F_LED3_CURR, + F_LED4_CURR, + F_LED1_MODE, + F_LED2_MODE, + F_LED3_MODE, + F_LED4_MODE, + F_LED1_DUTY, + F_LED2_DUTY, + F_LED3_DUTY, + F_LED4_DUTY, + F_LED1_FREQ, + F_LED2_FREQ, + F_LED3_FREQ, + F_LED4_FREQ, + F_MAX_FIELDS +}; + +enum mt6370_led_ranges { + R_LED123_CURR = 0, + R_LED4_CURR, + R_LED_TRFON, + R_LED_TOFF, + R_MAX_RANGES, +}; + +enum mt6370_pattern { + P_LED_TR1 = 0, + P_LED_TR2, + P_LED_TF1, + P_LED_TF2, + P_LED_TON, + P_LED_TOFF, + P_MAX_PATTERNS +}; + +#define MT6370_REG_DEV_INFO 0x100 +#define MT6370_REG_RGB1_DIM 0x182 +#define MT6370_REG_RGB2_DIM 0x183 +#define MT6370_REG_RGB3_DIM 0x184 +#define MT6370_REG_RGB_EN 0x185 +#define MT6370_REG_RGB1_ISNK 0x186 +#define MT6370_REG_RGB2_ISNK 0x187 +#define MT6370_REG_RGB3_ISNK 0x188 +#define MT6370_REG_RGB1_TR 0x189 +#define MT6370_REG_RGB_CHRIND_DIM 0x192 +#define MT6370_REG_RGB_CHRIND_CTRL 0x193 +#define MT6370_REG_RGB_CHRIND_TR 0x194 + +#define MT6372_REG_RGB_EN 0x182 +#define MT6372_REG_RGB1_ISNK 0x183 +#define MT6372_REG_RGB2_ISNK 0x184 +#define MT6372_REG_RGB3_ISNK 0x185 +#define MT6372_REG_RGB4_ISNK 0x186 +#define MT6372_REG_RGB1_DIM 0x187 +#define MT6372_REG_RGB2_DIM 0x188 +#define MT6372_REG_RGB3_DIM 0x189 +#define MT6372_REG_RGB4_DIM 0x18A +#define MT6372_REG_RGB12_FREQ 0x18B +#define MT6372_REG_RGB34_FREQ 0x18C +#define MT6372_REG_RGB1_TR 0x18D + +#define MT6370_VENID_MASK GENMASK(7, 4) +#define MT6370_CHEN_BIT(id) BIT(MT6370_LED_ISNK4 - id) +#define MT6370_VIRTUAL_MULTICOLOR 5 +#define MC_CHANNEL_NUM 3 +#define MT6370_PWM_DUTY 31 +#define MT6372_PMW_DUTY 255 + +#define STATE_OFF 0 +#define STATE_KEEP 1 +#define STATE_ON 2 + +struct mt6370_led { + union { + struct led_classdev isink; + struct led_classdev_mc mc; + }; + struct mt6370_priv *priv; + u32 default_state; + u32 index; +}; + +struct mt6370_priv { + struct mutex lock; + struct device *dev; + struct regmap *regmap; + struct regmap_field *fields[F_MAX_FIELDS]; + const struct reg_field *reg_fields; + const struct linear_range *ranges; + struct reg_cfg *reg_cfgs; + unsigned int leds_count; + unsigned int leds_active; + bool is_mt6372; + struct mt6370_led leds[]; +}; + +static const struct reg_field common_reg_fields[F_MAX_FIELDS] = { + [F_RGB_EN] = REG_FIELD(MT6370_REG_RGB_EN, 4, 7), + [F_CHGIND_EN] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 7, 7), + [F_LED1_CURR] = REG_FIELD(MT6370_REG_RGB1_ISNK, 0, 2), + [F_LED2_CURR] = REG_FIELD(MT6370_REG_RGB2_ISNK, 0, 2), + [F_LED3_CURR] = REG_FIELD(MT6370_REG_RGB3_ISNK, 0, 2), + [F_LED4_CURR] = REG_FIELD(MT6370_REG_RGB_CHRIND_CTRL, 0, 1), + [F_LED1_MODE] = REG_FIELD(MT6370_REG_RGB1_DIM, 5, 6), + [F_LED2_MODE] = REG_FIELD(MT6370_REG_RGB2_DIM, 5, 6), + [F_LED3_MODE] = REG_FIELD(MT6370_REG_RGB3_DIM, 5, 6), + [F_LED4_MODE] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 5, 6), + [F_LED1_DUTY] = REG_FIELD(MT6370_REG_RGB1_DIM, 0, 4), + [F_LED2_DUTY] = REG_FIELD(MT6370_REG_RGB2_DIM, 0, 4), + [F_LED3_DUTY] = REG_FIELD(MT6370_REG_RGB3_DIM, 0, 4), + [F_LED4_DUTY] = REG_FIELD(MT6370_REG_RGB_CHRIND_DIM, 0, 4), + [F_LED1_FREQ] = REG_FIELD(MT6370_REG_RGB1_ISNK, 3, 5), + [F_LED2_FREQ] = REG_FIELD(MT6370_REG_RGB2_ISNK, 3, 5), + [F_LED3_FREQ] = REG_FIELD(MT6370_REG_RGB3_ISNK, 3, 5), + [F_LED4_FREQ] = REG_FIELD(MT6370_REG_RGB_CHRIND_CTRL, 2, 4) +}; + +static const struct reg_field mt6372_reg_fields[F_MAX_FIELDS] = { + [F_RGB_EN] = REG_FIELD(MT6372_REG_RGB_EN, 4, 7), + [F_CHGIND_EN] = REG_FIELD(MT6372_REG_RGB_EN, 3, 3), + [F_LED1_CURR] = REG_FIELD(MT6372_REG_RGB1_ISNK, 0, 3), + [F_LED2_CURR] = REG_FIELD(MT6372_REG_RGB2_ISNK, 0, 3), + [F_LED3_CURR] = REG_FIELD(MT6372_REG_RGB3_ISNK, 0, 3), + [F_LED4_CURR] = REG_FIELD(MT6372_REG_RGB4_ISNK, 0, 3), + [F_LED1_MODE] = REG_FIELD(MT6372_REG_RGB1_ISNK, 6, 7), + [F_LED2_MODE] = REG_FIELD(MT6372_REG_RGB2_ISNK, 6, 7), + [F_LED3_MODE] = REG_FIELD(MT6372_REG_RGB3_ISNK, 6, 7), + [F_LED4_MODE] = REG_FIELD(MT6372_REG_RGB4_ISNK, 6, 7), + [F_LED1_DUTY] = REG_FIELD(MT6372_REG_RGB1_DIM, 0, 7), + [F_LED2_DUTY] = REG_FIELD(MT6372_REG_RGB2_DIM, 0, 7), + [F_LED3_DUTY] = REG_FIELD(MT6372_REG_RGB3_DIM, 0, 7), + [F_LED4_DUTY] = REG_FIELD(MT6372_REG_RGB4_DIM, 0, 7), + [F_LED1_FREQ] = REG_FIELD(MT6372_REG_RGB12_FREQ, 5, 7), + [F_LED2_FREQ] = REG_FIELD(MT6372_REG_RGB12_FREQ, 2, 4), + [F_LED3_FREQ] = REG_FIELD(MT6372_REG_RGB34_FREQ, 5, 7), + [F_LED4_FREQ] = REG_FIELD(MT6372_REG_RGB34_FREQ, 2, 4) +}; + +/* Current unit: microamp, time unit: millisecond */ +static const struct linear_range common_led_ranges[R_MAX_RANGES] = { + [R_LED123_CURR] = { 4000, 1, 6, 4000 }, + [R_LED4_CURR] = { 2000, 1, 3, 2000 }, + [R_LED_TRFON] = { 125, 0, 15, 200 }, + [R_LED_TOFF] = { 250, 0, 15, 400 } +}; + +static const struct linear_range mt6372_led_ranges[R_MAX_RANGES] = { + [R_LED123_CURR] = { 2000, 1, 14, 2000 }, + [R_LED4_CURR] = { 2000, 1, 14, 2000 }, + [R_LED_TRFON] = { 125, 0, 15, 250 }, + [R_LED_TOFF] = { 250, 0, 15, 500 } +}; + +static enum mt6370_led_field mt6370_get_led_current_field(unsigned int led_no) +{ + switch (led_no) { + case MT6370_LED_ISNK1: + return F_LED1_CURR; + case MT6370_LED_ISNK2: + return F_LED2_CURR; + case MT6370_LED_ISNK3: + return F_LED3_CURR; + default: + return F_LED4_CURR; + } +} + +static int mt6370_set_led_brightness(struct mt6370_priv *priv, + unsigned int led_no, unsigned int level) +{ + enum mt6370_led_field sel_field; + + sel_field = mt6370_get_led_current_field(led_no); + + return regmap_field_write(priv->fields[sel_field], level); +} + +static int mt6370_get_led_brightness(struct mt6370_priv *priv, + unsigned int led_no, unsigned int *level) +{ + enum mt6370_led_field sel_field; + + sel_field = mt6370_get_led_current_field(led_no); + + return regmap_field_read(priv->fields[sel_field], level); +} + +static int mt6370_set_led_duty(struct mt6370_priv *priv, unsigned int led_no, + unsigned int ton, unsigned int toff) +{ + enum mt6370_led_field sel_field; + unsigned int divisor, ratio; + + divisor = priv->is_mt6372 ? MT6372_PMW_DUTY : MT6370_PWM_DUTY; + ratio = ton * divisor / (ton + toff); + + switch (led_no) { + case MT6370_LED_ISNK1: + sel_field = F_LED1_DUTY; + break; + case MT6370_LED_ISNK2: + sel_field = F_LED2_DUTY; + break; + case MT6370_LED_ISNK3: + sel_field = F_LED3_DUTY; + break; + default: + sel_field = F_LED4_DUTY; + } + + return regmap_field_write(priv->fields[sel_field], ratio); +} + +static const unsigned int common_tfreqs[] = { + 10000, 5000, 2000, 1000, 500, 200, 5, 1 +}; + +static const unsigned int mt6372_tfreqs[] = { + 8000, 4000, 2000, 1000, 500, 250, 8, 4 +}; + +static int mt6370_set_led_freq(struct mt6370_priv *priv, unsigned int led_no, + unsigned int ton, unsigned int toff) +{ + enum mt6370_led_field sel_field; + const unsigned int *tfreq; + unsigned int tfreq_len, tsum; + int i; + + if (priv->is_mt6372) { + tfreq = mt6372_tfreqs; + tfreq_len = ARRAY_SIZE(mt6372_tfreqs); + } else { + tfreq = common_tfreqs; + tfreq_len = ARRAY_SIZE(common_tfreqs); + } + + tsum = ton + toff; + + if (tsum > tfreq[0] || tsum < tfreq[tfreq_len - 1]) + return -EOPNOTSUPP; + + for (i = 0; i < tfreq_len; i++) { + if (tsum >= tfreq[i]) + break; + } + + switch (led_no) { + case MT6370_LED_ISNK1: + sel_field = F_LED1_FREQ; + break; + case MT6370_LED_ISNK2: + sel_field = F_LED2_FREQ; + break; + case MT6370_LED_ISNK3: + sel_field = F_LED3_FREQ; + break; + default: + sel_field = F_LED4_FREQ; + } + + return regmap_field_write(priv->fields[sel_field], i); +} + +static void mt6370_get_breath_reg_base(struct mt6370_priv *priv, + unsigned int led_no, unsigned int *base) +{ + if (priv->is_mt6372) { + *base = MT6372_REG_RGB1_TR + led_no * 3; + return; + } + + switch (led_no) { + case MT6370_LED_ISNK1: + case MT6370_LED_ISNK2: + case MT6370_LED_ISNK3: + *base = MT6370_REG_RGB1_TR + led_no * 3; + break; + default: + *base = MT6370_REG_RGB_CHRIND_TR; + } +} + +static int mt6370_gen_breath_pattern(struct mt6370_priv *priv, + struct led_pattern *pattern, u32 len, + u8 *pattern_val, u32 val_len) +{ + enum mt6370_led_ranges sel_range; + struct led_pattern *curr; + unsigned int sel; + u8 val[P_MAX_PATTERNS / 2] = {0}; + int i; + + if (len < P_MAX_PATTERNS && val_len < P_MAX_PATTERNS / 2) + return -EINVAL; + + /* + * Pattern list + * tr1: byte 0, b'[7: 4] + * tr2: byte 0, b'[3: 0] + * tf1: byte 1, b'[7: 4] + * tf2: byte 1, b'[3: 0] + * ton: byte 2, b'[7: 4] + * toff: byte 2, b'[3: 0] + */ + for (i = 0; i < P_MAX_PATTERNS; i++) { + curr = pattern + i; + + sel_range = i == P_LED_TOFF ? R_LED_TOFF : R_LED_TRFON; + + linear_range_get_selector_within(priv->ranges + sel_range, + curr->delta_t, &sel); + + val[i / 2] |= sel << (4 * ((i + 1) % 2)); + } + + memcpy(pattern_val, val, 3); + return 0; +} + +static int mt6370_set_led_mode(struct mt6370_priv *priv, unsigned int led_no, + enum mt6370_led_mode mode) +{ + enum mt6370_led_field sel_field; + + switch (led_no) { + case MT6370_LED_ISNK1: + sel_field = F_LED1_MODE; + break; + case MT6370_LED_ISNK2: + sel_field = F_LED2_MODE; + break; + case MT6370_LED_ISNK3: + sel_field = F_LED3_MODE; + break; + default: + sel_field = F_LED4_MODE; + } + + return regmap_field_write(priv->fields[sel_field], mode); +} + +static int mt6370_mc_brightness_set(struct led_classdev *lcdev, + enum led_brightness level) +{ + struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev); + struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc); + struct mt6370_priv *priv = led->priv; + struct mc_subled *subled; + unsigned int enable, disable; + int i, ret; + + mutex_lock(&priv->lock); + + led_mc_calc_color_components(mccdev, level); + + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); + if (ret) + goto out; + + disable = enable; + + for (i = 0; i < mccdev->num_colors; i++) { + u32 brightness; + + subled = mccdev->subled_info + i; + brightness = min(subled->brightness, lcdev->max_brightness); + disable &= ~MT6370_CHEN_BIT(subled->channel); + + if (level == LED_OFF) { + enable &= ~MT6370_CHEN_BIT(subled->channel); + + ret = mt6370_set_led_mode(priv, subled->channel, + MT6370_LED_REG_MODE); + if (ret) + goto out; + + continue; + } + + if (brightness == LED_OFF) { + enable &= ~MT6370_CHEN_BIT(subled->channel); + continue; + } + + enable |= MT6370_CHEN_BIT(subled->channel); + + ret = mt6370_set_led_brightness(priv, subled->channel, + brightness); + if (ret) + goto out; + } + + ret = regmap_field_write(priv->fields[F_RGB_EN], disable); + if (ret) + goto out; + + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); + +out: + mutex_unlock(&priv->lock); + + return ret; +} + +static int mt6370_mc_blink_set(struct led_classdev *lcdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev); + struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc); + struct mt6370_priv *priv = led->priv; + struct mc_subled *subled; + unsigned int enable, disable; + int i, ret; + + mutex_lock(&priv->lock); + + if (!*delay_on && !*delay_off) + *delay_on = *delay_off = 500; + + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); + if (ret) + goto out; + + disable = enable; + + for (i = 0; i < mccdev->num_colors; i++) { + subled = mccdev->subled_info + i; + + disable &= ~MT6370_CHEN_BIT(subled->channel); + + ret = mt6370_set_led_duty(priv, subled->channel, *delay_on, + *delay_off); + if (ret) + goto out; + + ret = mt6370_set_led_freq(priv, subled->channel, *delay_on, + *delay_off); + if (ret) + goto out; + + ret = mt6370_set_led_mode(priv, subled->channel, + MT6370_LED_PWM_MODE); + if (ret) + goto out; + } + + /* Toggle to make pattern timing the same */ + ret = regmap_field_write(priv->fields[F_RGB_EN], disable); + if (ret) + goto out; + + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); + +out: + mutex_unlock(&priv->lock); + + return ret; +} + +static int mt6370_mc_pattern_set(struct led_classdev *lcdev, + struct led_pattern *pattern, u32 len, int repeat) +{ + struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev); + struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc); + struct mt6370_priv *priv = led->priv; + struct mc_subled *subled; + unsigned int reg_base, enable, disable; + u8 params[P_MAX_PATTERNS / 2]; + int i, ret; + + mutex_lock(&priv->lock); + + ret = mt6370_gen_breath_pattern(priv, pattern, len, params, + sizeof(params)); + if (ret) + goto out; + + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); + if (ret) + goto out; + + disable = enable; + + for (i = 0; i < mccdev->num_colors; i++) { + subled = mccdev->subled_info + i; + + mt6370_get_breath_reg_base(priv, subled->channel, ®_base); + disable &= ~MT6370_CHEN_BIT(subled->channel); + + ret = regmap_raw_write(priv->regmap, reg_base, params, + sizeof(params)); + if (ret) + goto out; + + ret = mt6370_set_led_mode(priv, subled->channel, + MT6370_LED_BREATH_MODE); + if (ret) + goto out; + } + + /* Toggle to make pattern timing be the same */ + ret = regmap_field_write(priv->fields[F_RGB_EN], disable); + if (ret) + goto out; + + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); + +out: + mutex_unlock(&priv->lock); + + return ret; +} + +static inline int mt6370_mc_pattern_clear(struct led_classdev *lcdev) +{ + struct led_classdev_mc *mccdev = lcdev_to_mccdev(lcdev); + struct mt6370_led *led = container_of(mccdev, struct mt6370_led, mc); + struct mt6370_priv *priv = led->priv; + struct mc_subled *subled; + int i, ret = 0; + + mutex_lock(&led->priv->lock); + + for (i = 0; i < mccdev->num_colors; i++) { + subled = mccdev->subled_info + i; + + ret = mt6370_set_led_mode(priv, subled->channel, + MT6370_LED_REG_MODE); + if (ret) + break; + } + + mutex_unlock(&led->priv->lock); + + return ret; +} + +static int mt6370_isnk_brightness_set(struct led_classdev *lcdev, + enum led_brightness level) +{ + struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink); + struct mt6370_priv *priv = led->priv; + unsigned int enable; + int ret; + + mutex_lock(&priv->lock); + + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); + if (ret) + goto out; + + if (level == LED_OFF) { + enable &= ~MT6370_CHEN_BIT(led->index); + + ret = mt6370_set_led_mode(priv, led->index, + MT6370_LED_REG_MODE); + if (ret) + goto out; + + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); + } else { + enable |= MT6370_CHEN_BIT(led->index); + + ret = mt6370_set_led_brightness(priv, led->index, level); + if (ret) + goto out; + + ret = regmap_field_write(priv->fields[F_RGB_EN], enable); + } + +out: + mutex_unlock(&priv->lock); + + return ret; +} + +static int mt6370_isnk_blink_set(struct led_classdev *lcdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink); + struct mt6370_priv *priv = led->priv; + int ret; + + mutex_lock(&priv->lock); + + if (!*delay_on && !*delay_off) + *delay_on = *delay_off = 500; + + ret = mt6370_set_led_duty(priv, led->index, *delay_on, *delay_off); + if (ret) + goto out; + + ret = mt6370_set_led_freq(priv, led->index, *delay_on, *delay_off); + if (ret) + goto out; + + ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_PWM_MODE); + +out: + mutex_unlock(&priv->lock); + + return ret; +} + +static int mt6370_isnk_pattern_set(struct led_classdev *lcdev, + struct led_pattern *pattern, u32 len, + int repeat) +{ + struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink); + struct mt6370_priv *priv = led->priv; + unsigned int reg_base; + u8 params[P_MAX_PATTERNS / 2]; + int ret; + + mutex_lock(&priv->lock); + + ret = mt6370_gen_breath_pattern(priv, pattern, len, params, + sizeof(params)); + if (ret) + goto out; + + mt6370_get_breath_reg_base(priv, led->index, ®_base); + + ret = regmap_raw_write(priv->regmap, reg_base, params, sizeof(params)); + if (ret) + goto out; + + ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_BREATH_MODE); + +out: + mutex_unlock(&priv->lock); + + return ret; +} + +static inline int mt6370_isnk_pattern_clear(struct led_classdev *lcdev) +{ + struct mt6370_led *led = container_of(lcdev, struct mt6370_led, isink); + struct mt6370_priv *priv = led->priv; + int ret; + + mutex_lock(&led->priv->lock); + ret = mt6370_set_led_mode(priv, led->index, MT6370_LED_REG_MODE); + mutex_unlock(&led->priv->lock); + + return ret; +} + +static int mt6370_init_led_properties(struct mt6370_led *led, + struct led_init_data *init_data) +{ + struct mt6370_priv *priv = led->priv; + struct led_classdev *lcdev; + struct fwnode_handle *child; + enum mt6370_led_ranges sel_range; + u32 max_uA, max_level; + const char * const states[] = { "off", "keep", "on" }; + const char *stat_str; + int ret; + + if (led->index == MT6370_VIRTUAL_MULTICOLOR) { + struct mc_subled *sub_led; + u32 num_color = 0; + + sub_led = devm_kzalloc(priv->dev, + sizeof(*sub_led) * MC_CHANNEL_NUM, + GFP_KERNEL); + if (!sub_led) + return -ENOMEM; + + fwnode_for_each_child_node(init_data->fwnode, child) { + u32 reg, color; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg > MT6370_LED_ISNK3 || + priv->leds_active & BIT(reg)) + return -EINVAL; + + ret = fwnode_property_read_u32(child, "color", &color); + if (ret) { + dev_err(priv->dev, + "led %d, no color specified\n", + led->index); + return ret; + } + + priv->leds_active |= BIT(reg); + sub_led[num_color].color_index = color; + sub_led[num_color].channel = reg; + num_color++; + } + + if (num_color < 2) { + dev_err(priv->dev, + "Multicolor must include 2 or more led channel\n"); + return -EINVAL; + } + + led->mc.num_colors = num_color; + led->mc.subled_info = sub_led; + + lcdev = &led->mc.led_cdev; + lcdev->brightness_set_blocking = mt6370_mc_brightness_set; + lcdev->blink_set = mt6370_mc_blink_set; + lcdev->pattern_set = mt6370_mc_pattern_set; + lcdev->pattern_clear = mt6370_mc_pattern_clear; + } else { + lcdev = &led->isink; + lcdev->brightness_set_blocking = mt6370_isnk_brightness_set; + lcdev->blink_set = mt6370_isnk_blink_set; + lcdev->pattern_set = mt6370_isnk_pattern_set; + lcdev->pattern_clear = mt6370_isnk_pattern_clear; + } + + ret = fwnode_property_read_u32(init_data->fwnode, "led-max-microamp", + &max_uA); + if (ret) { + dev_warn(priv->dev, + "Not specified led-max-microamp, config to the minimum\n"); + max_uA = 0; + } + + if (led->index == MT6370_LED_ISNK4) + sel_range = R_LED4_CURR; + else + sel_range = R_LED123_CURR; + + linear_range_get_selector_within(priv->ranges + sel_range, max_uA, + &max_level); + + lcdev->max_brightness = max_level; + + fwnode_property_read_string(init_data->fwnode, "linux,default-trigger", + &lcdev->default_trigger); + + if (!fwnode_property_read_string(init_data->fwnode, "default-state", + &stat_str)) { + ret = match_string(states, ARRAY_SIZE(states), stat_str); + if (ret < 0) + ret = STATE_OFF; + + led->default_state = ret; + } + + return 0; +} + +static int mt6370_isnk_init_default_state(struct mt6370_led *led) +{ + struct mt6370_priv *priv = led->priv; + unsigned int enable, level; + int ret; + + ret = mt6370_get_led_brightness(priv, led->index, &level); + if (ret) + return ret; + + ret = regmap_field_read(priv->fields[F_RGB_EN], &enable); + if (ret) + return ret; + + if (!(enable & MT6370_CHEN_BIT(led->index))) + level = LED_OFF; + + switch (led->default_state) { + case STATE_ON: + led->isink.brightness = led->isink.max_brightness; + break; + case STATE_KEEP: + led->isink.brightness = min(level, led->isink.max_brightness); + break; + default: + led->isink.brightness = LED_OFF; + } + + return mt6370_isnk_brightness_set(&led->isink, led->isink.brightness); +} + +static int mt6370_led_register(struct device *parent, struct mt6370_led *led, + struct led_init_data *init_data) +{ + struct mt6370_priv *priv = led->priv; + int ret; + + if (led->index == MT6370_VIRTUAL_MULTICOLOR) { + ret = mt6370_mc_brightness_set(&led->mc.led_cdev, LED_OFF); + if (ret) { + dev_err(parent, "Couldn't set multicolor brightness\n"); + return ret; + } + + ret = devm_led_classdev_multicolor_register_ext(parent, + &led->mc, + init_data); + if (ret) { + dev_err(parent, "Couldn't register multicolor\n"); + return ret; + } + } else { + if (led->index == MT6370_LED_ISNK4) { + ret = regmap_field_write(priv->fields[F_CHGIND_EN], 1); + if (ret) { + dev_err(parent, "Failed to set CHRIND to SW\n"); + return ret; + } + } + + ret = mt6370_isnk_init_default_state(led); + if (ret) { + dev_err(parent, "Failed to init %d isnk state\n", + led->index); + return ret; + } + + ret = devm_led_classdev_register_ext(parent, &led->isink, + init_data); + if (ret) { + dev_err(parent, "Couldn't register isink %d\n", + led->index); + return ret; + } + } + + return 0; +} + +static int mt6370_check_vendor_info(struct mt6370_priv *priv) +{ + unsigned int devinfo, vid; + int ret; + + ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &devinfo); + if (ret) + return ret; + + vid = FIELD_GET(MT6370_VENID_MASK, devinfo); + if (vid == 0x9 || vid == 0xb) { + priv->reg_fields = mt6372_reg_fields; + priv->ranges = mt6372_led_ranges; + priv->is_mt6372 = true; + } else { + priv->reg_fields = common_reg_fields; + priv->ranges = common_led_ranges; + } + + return 0; +} + +static int mt6370_leds_probe(struct platform_device *pdev) +{ + struct mt6370_priv *priv; + struct fwnode_handle *child; + size_t count; + int i = 0, ret; + + count = device_get_child_node_count(&pdev->dev); + if (!count || count > MT6370_MAX_LEDS) { + dev_err(&pdev->dev, + "No child node or node count over max led number %zu\n", + count); + return -EINVAL; + } + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, leds, count), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->leds_count = count; + priv->dev = &pdev->dev; + mutex_init(&priv->lock); + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) { + dev_err(&pdev->dev, "Failed to get parent regmap\n"); + return -ENODEV; + } + + ret = mt6370_check_vendor_info(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to check vendor info (%d)\n", ret); + return ret; + } + + ret = devm_regmap_field_bulk_alloc(&pdev->dev, priv->regmap, + priv->fields, priv->reg_fields, + F_MAX_FIELDS); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate regmap field\n"); + return ret; + } + + device_for_each_child_node(&pdev->dev, child) { + struct mt6370_led *led = priv->leds + i++; + struct led_init_data init_data = { .fwnode = child, }; + u32 reg, color; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(&pdev->dev, "Failed to parse reg property\n"); + return ret; + } + + if (reg >= MT6370_MAX_LEDS) { + dev_err(&pdev->dev, "Error reg property number\n"); + return -EINVAL; + } + + ret = fwnode_property_read_u32(child, "color", &color); + if (ret) { + dev_err(&pdev->dev, "Failed to parse color property\n"); + return ret; + } + + if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI) + reg = MT6370_VIRTUAL_MULTICOLOR; + + if (priv->leds_active & BIT(reg)) { + dev_err(&pdev->dev, "Duplicat reg property\n"); + return -EINVAL; + } + + priv->leds_active |= BIT(reg); + + led->index = reg; + led->priv = priv; + + ret = mt6370_init_led_properties(led, &init_data); + if (ret) + return ret; + + ret = mt6370_led_register(&pdev->dev, led, &init_data); + if (ret) + return ret; + } + + return 0; +} + +static const struct of_device_id mt6370_rgbled_device_table[] = { + { .compatible = "mediatek,mt6370-indicator", }, + {} +}; +MODULE_DEVICE_TABLE(of, mt6370_rgbled_device_table); + +static struct platform_driver mt6370_rgbled_driver = { + .driver = { + .name = "mt6370-indicator", + .of_match_table = mt6370_rgbled_device_table, + }, + .probe = mt6370_leds_probe, +}; +module_platform_driver(mt6370_rgbled_driver); + +MODULE_AUTHOR("Alice Chen alice_chen@richtek.com"); +MODULE_DESCRIPTION("Mediatek MT6370 RGB Led Driver"); +MODULE_LICENSE("GPL v2");
Hi ChiaEn,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on pavel-leds/for-next] [also build test ERROR on lee-mfd/for-mfd-next lee-backlight/for-backlight-next v5.19-rc1 next-20220606] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
url: https://github.com/intel-lab-lkp/linux/commits/ChiaEn-Wu/Add-Mediatek-MT6370... base: git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git for-next config: nios2-allyesconfig (https://download.01.org/0day-ci/archive/20220606/202206062207.q0S8wxpj-lkp@i...) compiler: nios2-linux-gcc (GCC) 11.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/4de9e5ff11aeade155aa744bcaf9ef... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review ChiaEn-Wu/Add-Mediatek-MT6370-PMIC-support/20220531-211432 git checkout 4de9e5ff11aeade155aa744bcaf9efca82b3d4ee # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=nios2 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable Reported-by: kernel test robot lkp@intel.com
All errors (new ones prefixed by >>):
drivers/leds/leds-mt6370.c: In function 'mt6370_check_vendor_info':
drivers/leds/leds-mt6370.c:873:15: error: implicit declaration of function 'FIELD_GET'; did you mean 'FOLL_GET'? [-Werror=implicit-function-declaration]
873 | vid = FIELD_GET(MT6370_VENID_MASK, devinfo); | ^~~~~~~~~ | FOLL_GET cc1: some warnings being treated as errors
vim +873 drivers/leds/leds-mt6370.c
863 864 static int mt6370_check_vendor_info(struct mt6370_priv *priv) 865 { 866 unsigned int devinfo, vid; 867 int ret; 868 869 ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &devinfo); 870 if (ret) 871 return ret; 872
873 vid = FIELD_GET(MT6370_VENID_MASK, devinfo);
874 if (vid == 0x9 || vid == 0xb) { 875 priv->reg_fields = mt6372_reg_fields; 876 priv->ranges = mt6372_led_ranges; 877 priv->is_mt6372 = true; 878 } else { 879 priv->reg_fields = common_reg_fields; 880 priv->ranges = common_led_ranges; 881 } 882 883 return 0; 884 } 885
From: Alice Chen alice_chen@richtek.com
Add Mediatek MT6370 flashlight support
Signed-off-by: Alice Chen alice_chen@richtek.com --- drivers/leds/flash/Kconfig | 9 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-mt6370-flash.c | 665 +++++++++++++++++++++++++ 3 files changed, 675 insertions(+) create mode 100644 drivers/leds/flash/leds-mt6370-flash.c
diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index d3eb689b193c..d38e263aaf09 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -90,4 +90,13 @@ config LEDS_SGM3140 This option enables support for the SGM3140 500mA Buck/Boost Charge Pump LED Driver.
+config LEDS_MT6370_FLASHLIGHT + tristate "Flash LED Support for Mediatek MT6370 PMIC" + depends on LEDS_CLASS + depends on MFD_MT6370 + help + Support 2 channels and torch/strobe mode. + Say Y here to enable support for + MT6370_FLASH_LED device. + endif # LEDS_CLASS_FLASH diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 0acbddc0b91b..4c4c1710f506 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o +obj-$(CONFIG_LEDS_MT6370_FLASHLIGHT) += leds-mt6370-flash.o diff --git a/drivers/leds/flash/leds-mt6370-flash.c b/drivers/leds/flash/leds-mt6370-flash.c new file mode 100644 index 000000000000..8f6796aae3e1 --- /dev/null +++ b/drivers/leds/flash/leds-mt6370-flash.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/led-class-flash.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <media/v4l2-flash-led-class.h> +enum { + MT6370_LED_FLASH1, + MT6370_LED_FLASH2, + MT6370_MAX_LEDS +}; + +/* Virtual definition for multicolor */ + +#define MT6370_REG_FLEDEN 0x17E +#define MT6370_REG_STRBTO 0x173 +#define MT6370_REG_CHGSTAT2 0x1D1 +#define MT6370_REG_FLEDSTAT1 0x1D9 +#define MT6370_REG_FLEDISTRB(_id) (0x174 + 4 * _id) +#define MT6370_REG_FLEDITOR(_id) (0x175 + 4 * _id) +#define MT6370_ITORCH_MASK GENMASK(4, 0) +#define MT6370_ISTROBE_MASK GENMASK(6, 0) +#define MT6370_STRBTO_MASK GENMASK(6, 0) +#define MT6370_TORCHEN_MASK BIT(3) +#define MT6370_STROBEN_MASK BIT(2) +#define MT6370_FLCSEN_MASK(_id) BIT(MT6370_LED_FLASH2 - _id) +#define MT6370_FLCSEN_MASK_ALL (BIT(0) | BIT(1)) +#define MT6370_FLEDCHGVINOVP_MASK BIT(3) +#define MT6370_FLED1STRBTO_MASK BIT(11) +#define MT6370_FLED2STRBTO_MASK BIT(10) +#define MT6370_FLED1STRB_MASK BIT(9) +#define MT6370_FLED2STRB_MASK BIT(8) +#define MT6370_FLED1SHORT_MASK BIT(7) +#define MT6370_FLED2SHORT_MASK BIT(6) +#define MT6370_FLEDLVF_MASK BIT(3) + +#define MT6370_LED_JOINT 2 +#define MT6370_RANGE_FLED_REG 4 +#define MT6370_ITORCH_MINUA 25000 +#define MT6370_ITORCH_STEPUA 12500 +#define MT6370_ITORCH_MAXUA 400000 +#define MT6370_ITORCH_DOUBLE_MAXUA 800000 +#define MT6370_ISTRB_MINUA 50000 +#define MT6370_ISTRB_STEPUA 12500 +#define MT6370_ISTRB_MAXUA 1500000 +#define MT6370_ISTRB_DOUBLE_MAXUA 3000000 +#define MT6370_STRBTO_MINUS 64000 +#define MT6370_STRBTO_STEPUS 32000 +#define MT6370_STRBTO_MAXUS 2432000 + +#define STATE_OFF 0 +#define STATE_KEEP 1 +#define STATE_ON 2 + +struct mt6370_led { + struct led_classdev_flash flash; + struct v4l2_flash *v4l2_flash; + struct mt6370_priv *priv; + u32 led_no; + u32 default_state; +}; + +struct mt6370_priv { + struct device *dev; + struct regmap *regmap; + struct mutex lock; + unsigned int fled_strobe_used; + unsigned int fled_torch_used; + unsigned int leds_active; + unsigned int leds_count; + struct mt6370_led leds[]; +}; + +static int mt6370_torch_brightness_set(struct led_classdev *lcdev, + enum led_brightness level) +{ + struct mt6370_led *led = + container_of(lcdev, struct mt6370_led, flash.led_cdev); + struct mt6370_priv *priv = led->priv; + u32 led_enable_mask = (led->led_no == MT6370_LED_JOINT) ? + MT6370_FLCSEN_MASK_ALL : + MT6370_FLCSEN_MASK(led->led_no); + u32 enable_mask = MT6370_TORCHEN_MASK | led_enable_mask; + u32 val = level ? led_enable_mask : 0; + u32 prev = priv->fled_torch_used, curr; + int ret, i; + + mutex_lock(&priv->lock); + + /* + * Only one set of flash control logic, + * use the flag to avoid strobe is currently used. + */ + if (priv->fled_strobe_used) { + dev_warn(lcdev->dev, "Please disable strobe first [%d]\n", + priv->fled_strobe_used); + ret = -EBUSY; + goto unlock; + } + + if (level) + curr = prev | BIT(led->led_no); + else + curr = prev & ~BIT(led->led_no); + + if (curr) + val |= MT6370_TORCHEN_MASK; + + + if (level) { + level -= 1; + if (led->led_no == MT6370_LED_JOINT) { + int flevel[MT6370_MAX_LEDS]; + + flevel[0] = level / 2; + flevel[1] = level - flevel[0]; + for (i = 0; i < MT6370_MAX_LEDS; i++) { + ret = regmap_update_bits(priv->regmap, + MT6370_REG_FLEDITOR(i), + MT6370_ITORCH_MASK, flevel[i]); + if (ret) + goto unlock; + } + } else { + ret = regmap_update_bits(priv->regmap, + MT6370_REG_FLEDITOR(led->led_no), + MT6370_ITORCH_MASK, level); + if (ret) + goto unlock; + } + } + + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, + enable_mask, val); + if (ret) + goto unlock; + + priv->fled_torch_used = curr; + +unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static int mt6370_flash_brightness_set(struct led_classdev_flash *fl_cdev, + u32 brightness) +{ + /* + * Due to the current spike when turning on flash, + * let brightness to be kept by framework. + * This empty function is used to + * prevent led_classdev_flash register ops check failure. + */ + return 0; +} + +static int _mt6370_flash_brightness_set(struct led_classdev_flash *fl_cdev, + u32 brightness) +{ + struct mt6370_led *led = container_of(fl_cdev, struct mt6370_led, + flash); + struct mt6370_priv *priv = led->priv; + struct led_flash_setting *s = &fl_cdev->brightness; + u32 val = (brightness - s->min) / s->step; + int ret, i; + + if (led->led_no == MT6370_LED_JOINT) { + int flevel[MT6370_MAX_LEDS]; + + flevel[0] = val / 2; + flevel[1] = val - flevel[0]; + for (i = 0; i < MT6370_MAX_LEDS; i++) { + ret = regmap_update_bits(priv->regmap, + MT6370_REG_FLEDISTRB(i), + MT6370_ISTROBE_MASK, flevel[i]); + if (ret) + return ret; + } + } else { + ret = regmap_update_bits(priv->regmap, + MT6370_REG_FLEDISTRB(led->led_no), + MT6370_ISTROBE_MASK, val); + if (ret) + return ret; + } + return ret; +} + +static int mt6370_strobe_set(struct led_classdev_flash *fl_cdev, bool state) +{ + struct mt6370_led *led = container_of(fl_cdev, struct mt6370_led, + flash); + struct mt6370_priv *priv = led->priv; + struct led_classdev *lcdev = &fl_cdev->led_cdev; + struct led_flash_setting *s = &fl_cdev->brightness; + u32 led_enable_mask = (led->led_no == MT6370_LED_JOINT) ? + MT6370_FLCSEN_MASK_ALL : + MT6370_FLCSEN_MASK(led->led_no); + u32 enable_mask = MT6370_STROBEN_MASK | led_enable_mask; + u32 val = state ? led_enable_mask : 0; + u32 prev = priv->fled_strobe_used, curr; + int ret; + + mutex_lock(&priv->lock); + + /* + * Only one set of flash control logic, + * use the flag to avoid torch is currently used + */ + if (priv->fled_torch_used) { + dev_warn(lcdev->dev, "Please disable torch first [0x%x]\n", + priv->fled_torch_used); + ret = -EBUSY; + goto unlock; + } + + if (state) + curr = prev | BIT(led->led_no); + else + curr = prev & ~BIT(led->led_no); + + if (curr) + val |= MT6370_STROBEN_MASK; + + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, enable_mask, + val); + if (ret) { + dev_err(lcdev->dev, "[%d] control current source %d fail\n", + led->led_no, state); + goto unlock; + } + + /* + * If the flash need to be on, + * config the flash current ramping up to the setting value + * Else, always recover back to the minimum one + */ + ret = _mt6370_flash_brightness_set(fl_cdev, state ? s->val : s->min); + if (ret) + goto unlock; + + /* + * For the flash turn on/off, HW rampping up/down time is 5ms/500us, + * respectively + */ + if (!prev && curr) + usleep_range(5000, 6000); + else if (prev && !curr) + udelay(500); + + priv->fled_strobe_used = curr; + +unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static int mt6370_strobe_get(struct led_classdev_flash *fl_cdev, bool *state) +{ + struct mt6370_led *led = container_of(fl_cdev, struct mt6370_led, + flash); + struct mt6370_priv *priv = led->priv; + + mutex_lock(&priv->lock); + *state = !!(priv->fled_strobe_used & BIT(led->led_no)); + mutex_unlock(&priv->lock); + + return 0; +} + +static int mt6370_timeout_set(struct led_classdev_flash *fl_cdev, u32 timeout) +{ + struct mt6370_led *led = container_of(fl_cdev, struct mt6370_led, + flash); + struct mt6370_priv *priv = led->priv; + struct led_flash_setting *s = &fl_cdev->timeout; + u32 val = (timeout - s->min) / s->step; + int ret; + + mutex_lock(&priv->lock); + ret = regmap_update_bits(priv->regmap, MT6370_REG_STRBTO, + MT6370_STRBTO_MASK, val); + mutex_unlock(&priv->lock); + + return ret; +} + +static int mt6370_fault_get(struct led_classdev_flash *fl_cdev, u32 *fault) +{ + struct mt6370_led *led = container_of(fl_cdev, struct mt6370_led, + flash); + struct mt6370_priv *priv = led->priv; + u16 fled_stat; + unsigned int chg_stat, strobe_timeout_mask, fled_short_mask; + u32 rfault = 0; + int ret; + + mutex_lock(&priv->lock); + ret = regmap_read(priv->regmap, MT6370_REG_CHGSTAT2, &chg_stat); + if (ret) + goto unlock; + + ret = regmap_raw_read(priv->regmap, MT6370_REG_FLEDSTAT1, &fled_stat, + sizeof(fled_stat)); + if (ret) + goto unlock; + + switch (led->led_no) { + case MT6370_LED_FLASH1: + strobe_timeout_mask = MT6370_FLED1STRBTO_MASK; + fled_short_mask = MT6370_FLED1SHORT_MASK; + break; + + case MT6370_LED_FLASH2: + strobe_timeout_mask = MT6370_FLED2STRBTO_MASK; + fled_short_mask = MT6370_FLED2SHORT_MASK; + break; + + case MT6370_LED_JOINT: + strobe_timeout_mask = MT6370_FLED1STRBTO_MASK | + MT6370_FLED2STRBTO_MASK; + fled_short_mask = MT6370_FLED1SHORT_MASK | + MT6370_FLED2SHORT_MASK; + } + + if (chg_stat & MT6370_FLEDCHGVINOVP_MASK) + rfault |= LED_FAULT_INPUT_VOLTAGE; + + if (fled_stat & strobe_timeout_mask) + rfault |= LED_FAULT_TIMEOUT; + + if (fled_stat & fled_short_mask) + rfault |= LED_FAULT_SHORT_CIRCUIT; + + if (fled_stat & MT6370_FLEDLVF_MASK) + rfault |= LED_FAULT_UNDER_VOLTAGE; + + *fault = rfault; +unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static const struct led_flash_ops mt6370_flash_ops = { + .flash_brightness_set = mt6370_flash_brightness_set, + .strobe_set = mt6370_strobe_set, + .strobe_get = mt6370_strobe_get, + .timeout_set = mt6370_timeout_set, + .fault_get = mt6370_fault_get, +}; + +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) +static int mt6370_flash_external_strobe_set(struct v4l2_flash *v4l2_flash, + bool enable) +{ + struct led_classdev_flash *flash = v4l2_flash->fled_cdev; + struct mt6370_led *led = container_of(flash, struct mt6370_led, flash); + struct mt6370_priv *priv = led->priv; + u32 mask = (led->led_no == MT6370_LED_JOINT) ? MT6370_FLCSEN_MASK_ALL : + MT6370_FLCSEN_MASK(led->led_no); + u32 val = enable ? mask : 0; + int ret; + + mutex_lock(&priv->lock); + ret = regmap_update_bits(priv->regmap, MT6370_REG_FLEDEN, mask, val); + if (ret) + goto unlock; + + if (enable) + priv->fled_strobe_used |= BIT(led->led_no); + else + priv->fled_strobe_used &= ~BIT(led->led_no); + +unlock: + mutex_unlock(&priv->lock); + return ret; +} + +static const struct v4l2_flash_ops v4l2_flash_ops = { + .external_strobe_set = mt6370_flash_external_strobe_set, +}; + +static void mt6370_init_v4l2_flash_config(struct mt6370_led *led, + struct v4l2_flash_config *config) +{ + struct led_classdev *lcdev; + struct led_flash_setting *s = &config->intensity; + + lcdev = &led->flash.led_cdev; + + s->min = MT6370_ITORCH_MINUA; + s->step = MT6370_ITORCH_STEPUA; + s->val = s->max = s->min + (lcdev->max_brightness - 1) * s->step; + + config->has_external_strobe = 1; + strscpy(config->dev_name, lcdev->dev->kobj.name, + sizeof(config->dev_name)); + + config->flash_faults = LED_FAULT_SHORT_CIRCUIT | LED_FAULT_TIMEOUT | + LED_FAULT_INPUT_VOLTAGE | + LED_FAULT_UNDER_VOLTAGE; +} +#else +static const struct v4l2_flash_ops v4l2_flash_ops; +static void mt6370_init_v4l2_flash_config(struct mt6370_led *led, + struct v4l2_flash_config *config) +{ +} +#endif + +static int mt6370_led_register(struct device *parent, struct mt6370_led *led, + struct led_init_data *init_data) +{ + struct v4l2_flash_config v4l2_config = {0}; + int ret; + + ret = devm_led_classdev_flash_register_ext(parent, &led->flash, + init_data); + if (ret) { + dev_err(parent, "Couldn't register flash %d\n", led->led_no); + return ret; + } + + mt6370_init_v4l2_flash_config(led, &v4l2_config); + led->v4l2_flash = v4l2_flash_init(parent, init_data->fwnode, + &led->flash, &v4l2_flash_ops, + &v4l2_config); + if (IS_ERR(led->v4l2_flash)) { + dev_err(parent, "Failed to register %d v4l2 sd\n", led->led_no); + return PTR_ERR(led->v4l2_flash); + } + + return 0; +} + +static u32 clamp_align(u32 val, u32 min, u32 max, u32 step) +{ + u32 retval; + + retval = clamp_val(val, min, max); + if (step > 1) + retval = rounddown(retval - min, step) + min; + + return retval; +} + +static int mt6370_init_flash_properties(struct mt6370_led *led, + struct led_init_data *init_data) +{ + struct led_classdev_flash *flash = &led->flash; + struct led_classdev *lcdev = &flash->led_cdev; + struct mt6370_priv *priv = led->priv; + struct led_flash_setting *s; + u32 sources[MT6370_MAX_LEDS]; + u32 max_uA, val; + int i, ret, num; + + num = fwnode_property_count_u32(init_data->fwnode, "led-sources"); + if (num < 1 || num > MT6370_MAX_LEDS) { + dev_err(priv->dev, + "Not specified or wrong number of led-sources\n"); + return -EINVAL; + } + + ret = fwnode_property_read_u32_array(init_data->fwnode, + "led-sources", sources, num); + if (ret) + return ret; + + for (i = 0; i < num; i++) { + if (sources[i] >= MT6370_MAX_LEDS) + return -EINVAL; + if (priv->leds_active & BIT(sources[i])) + return -EINVAL; + priv->leds_active |= BIT(sources[i]); + + } + led->led_no = (num == MT6370_MAX_LEDS) ? MT6370_LED_JOINT : + sources[0]; + + max_uA = (num == 2) ? MT6370_ITORCH_DOUBLE_MAXUA : MT6370_ITORCH_MAXUA; + ret = fwnode_property_read_u32(init_data->fwnode, "led-max-microamp", + &val); + if (ret) { + dev_warn(priv->dev, + "Not specified led-max-microamp, config to the minimum\n"); + val = MT6370_ITORCH_MINUA; + } else + val = clamp_align(val, MT6370_ITORCH_MINUA, max_uA, + MT6370_ITORCH_STEPUA); + + lcdev->max_brightness = (val - MT6370_ITORCH_MINUA) / + MT6370_ITORCH_STEPUA + 1; + lcdev->brightness_set_blocking = mt6370_torch_brightness_set; + lcdev->flags |= LED_DEV_CAP_FLASH; + + max_uA = (num == 2) ? MT6370_ISTRB_DOUBLE_MAXUA : MT6370_ISTRB_MAXUA; + ret = fwnode_property_read_u32(init_data->fwnode, "flash-max-microamp", + &val); + if (ret) { + dev_warn(priv->dev, + "Not specified flash-max-microamp, config to the minimum\n"); + val = MT6370_ISTRB_MINUA; + } else + val = clamp_align(val, MT6370_ISTRB_MINUA, max_uA, + MT6370_ISTRB_STEPUA); + + s = &flash->brightness; + s->min = MT6370_ISTRB_MINUA; + s->step = MT6370_ISTRB_STEPUA; + s->val = s->max = val; + + /* + * Always configure as min level when off to + * prevent flash current spike + */ + ret = _mt6370_flash_brightness_set(flash, s->min); + if (ret) + return ret; + + ret = fwnode_property_read_u32(init_data->fwnode, + "flash-max-timeout-us", &val); + if (ret) { + dev_warn(priv->dev, + "Not specified flash-max-timeout-us, config to the minimum\n"); + val = MT6370_STRBTO_MINUS; + } else + val = clamp_align(val, MT6370_STRBTO_MINUS, MT6370_STRBTO_MAXUS, + MT6370_STRBTO_STEPUS); + + + s = &flash->timeout; + s->min = MT6370_STRBTO_MINUS; + s->step = MT6370_STRBTO_STEPUS; + s->val = s->max = val; + + flash->ops = &mt6370_flash_ops; + + return 0; +} + +static int mt6370_init_common_properties(struct mt6370_led *led, + struct led_init_data *init_data) +{ + const char * const states[] = { "off", "keep", "on" }; + const char *str; + int ret; + + if (!fwnode_property_read_string(init_data->fwnode, + "default-state", &str)) { + ret = match_string(states, ARRAY_SIZE(states), str); + if (ret < 0) + ret = STATE_OFF; + + led->default_state = ret; + } + + return 0; +} + +static void mt6370_v4l2_flash_release(struct mt6370_priv *priv) +{ + int i; + + for (i = 0; i < priv->leds_count; i++) { + struct mt6370_led *led = priv->leds + i; + + if (led->v4l2_flash) + v4l2_flash_release(led->v4l2_flash); + } +} + +static int mt6370_led_probe(struct platform_device *pdev) +{ + struct mt6370_priv *priv; + struct fwnode_handle *child; + size_t count; + int i = 0, ret; + + count = device_get_child_node_count(&pdev->dev); + if (!count || count > MT6370_MAX_LEDS) { + dev_err(&pdev->dev, + "No child node or node count over max led number %lu\n", count); + return -EINVAL; + } + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, count), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->leds_count = count; + priv->dev = &pdev->dev; + mutex_init(&priv->lock); + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) { + dev_err(&pdev->dev, "Failed to get parent regmap\n"); + return -ENODEV; + } + + device_for_each_child_node(&pdev->dev, child) { + struct mt6370_led *led = priv->leds + i; + struct led_init_data init_data = { .fwnode = child, }; + + led->priv = priv; + ret = mt6370_init_common_properties(led, &init_data); + if (ret) + goto out_flash_release; + + ret = mt6370_init_flash_properties(led, &init_data); + + if (ret) + goto out_flash_release; + + ret = mt6370_led_register(&pdev->dev, led, &init_data); + if (ret) + goto out_flash_release; + + i++; + } + + platform_set_drvdata(pdev, priv); + return 0; + +out_flash_release: + mt6370_v4l2_flash_release(priv); + return ret; +} + +static int mt6370_led_remove(struct platform_device *pdev) +{ + struct mt6370_priv *priv = platform_get_drvdata(pdev); + + mt6370_v4l2_flash_release(priv); + return 0; +} + +static const struct of_device_id __maybe_unused mt6370_led_of_id[] = { + { .compatible = "mediatek,mt6370-flashlight", }, + {} +}; +MODULE_DEVICE_TABLE(of, mt6370_led_of_id); + +static struct platform_driver mt6370_led_driver = { + .driver = { + .name = "mt6370-flashlight", + .of_match_table = mt6370_led_of_id, + }, + .probe = mt6370_led_probe, + .remove = mt6370_led_remove, +}; +module_platform_driver(mt6370_led_driver); + +MODULE_AUTHOR("Alice Chen alice_chen@richtek.com"); +MODULE_DESCRIPTION("MT6370 FLASH LED Driver"); +MODULE_LICENSE("GPL v2");
Hi ChiaEn,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on pavel-leds/for-next] [also build test WARNING on lee-mfd/for-mfd-next lee-backlight/for-backlight-next v5.18 next-20220602] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
url: https://github.com/intel-lab-lkp/linux/commits/ChiaEn-Wu/Add-Mediatek-MT6370... base: git://git.kernel.org/pub/scm/linux/kernel/git/pavel/linux-leds.git for-next config: nios2-allyesconfig (https://download.01.org/0day-ci/archive/20220602/202206021739.LZjU7zjg-lkp@i...) compiler: nios2-linux-gcc (GCC) 11.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/805a8af17c769562ec4b85e9b7d266... git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review ChiaEn-Wu/Add-Mediatek-MT6370-PMIC-support/20220531-211432 git checkout 805a8af17c769562ec4b85e9b7d2669d004fe3a6 # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=nios2 SHELL=/bin/bash drivers/gpu/drm/v3d/ drivers/leds/ sound/core/
If you fix the issue, kindly add following tag where applicable Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
In file included from include/linux/device.h:15, from include/linux/leds.h:12, from include/linux/led-class-flash.h:11, from drivers/leds/flash/leds-mt6370-flash.c:8: drivers/leds/flash/leds-mt6370-flash.c: In function 'mt6370_led_probe':
drivers/leds/flash/leds-mt6370-flash.c:591:17: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'size_t' {aka 'unsigned int'} [-Wformat=]
591 | "No child node or node count over max led number %lu\n", count); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/leds/flash/leds-mt6370-flash.c:590:17: note: in expansion of macro 'dev_err' 590 | dev_err(&pdev->dev, | ^~~~~~~ drivers/leds/flash/leds-mt6370-flash.c:591:68: note: format string is defined here 591 | "No child node or node count over max led number %lu\n", count); | ~~^ | | | long unsigned int | %u
vim +591 drivers/leds/flash/leds-mt6370-flash.c
580 581 static int mt6370_led_probe(struct platform_device *pdev) 582 { 583 struct mt6370_priv *priv; 584 struct fwnode_handle *child; 585 size_t count; 586 int i = 0, ret; 587 588 count = device_get_child_node_count(&pdev->dev); 589 if (!count || count > MT6370_MAX_LEDS) { 590 dev_err(&pdev->dev,
591 "No child node or node count over max led number %lu\n", count);
592 return -EINVAL; 593 } 594 595 priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, count), 596 GFP_KERNEL); 597 if (!priv) 598 return -ENOMEM; 599 600 priv->leds_count = count; 601 priv->dev = &pdev->dev; 602 mutex_init(&priv->lock); 603 604 priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); 605 if (!priv->regmap) { 606 dev_err(&pdev->dev, "Failed to get parent regmap\n"); 607 return -ENODEV; 608 } 609 610 device_for_each_child_node(&pdev->dev, child) { 611 struct mt6370_led *led = priv->leds + i; 612 struct led_init_data init_data = { .fwnode = child, }; 613 614 led->priv = priv; 615 ret = mt6370_init_common_properties(led, &init_data); 616 if (ret) 617 goto out_flash_release; 618 619 ret = mt6370_init_flash_properties(led, &init_data); 620 621 if (ret) 622 goto out_flash_release; 623 624 ret = mt6370_led_register(&pdev->dev, led, &init_data); 625 if (ret) 626 goto out_flash_release; 627 628 i++; 629 } 630 631 platform_set_drvdata(pdev, priv); 632 return 0; 633 634 out_flash_release: 635 mt6370_v4l2_flash_release(priv); 636 return ret; 637 } 638
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 Backlight support.
Signed-off-by: ChiaEn Wu chiaen_wu@richtek.com --- drivers/video/backlight/Kconfig | 8 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/mt6370-backlight.c | 338 +++++++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 drivers/video/backlight/mt6370-backlight.c
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index a003e02e13ce..d9868fbe7488 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -268,6 +268,14 @@ config BACKLIGHT_MAX8925 If you have a LCD backlight connected to the WLED output of MAX8925 WLED output, say Y here to enable this driver.
+config BACKLIGHT_MT6370 + tristate "Mediatek MT6370 Backlight Driver" + depends on MFD_MT6370 + help + Say Y here to enable MT6370 Backlight support. + It's commonly used to drive the display WLED. There're 4 channels + inisde, and each channel can provide up to 30mA current. + config BACKLIGHT_APPLE tristate "Apple Backlight Driver" depends on X86 && ACPI diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index cae2c83422ae..e815f3f1deff 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o +obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o diff --git a/drivers/video/backlight/mt6370-backlight.c b/drivers/video/backlight/mt6370-backlight.c new file mode 100644 index 000000000000..f8a8d33203ed --- /dev/null +++ b/drivers/video/backlight/mt6370-backlight.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/backlight.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MT6370_REG_DEV_INFO 0x100 +#define MT6370_REG_BL_EN 0x1A0 +#define MT6370_REG_BL_BSTCTRL 0x1A1 +#define MT6370_REG_BL_PWM 0x1A2 +#define MT6370_REG_BL_DIM2 0x1A4 + +#define MT6370_VENID_MASK GENMASK(7, 4) +#define MT6370_BL_EXT_EN_MASK BIT(7) +#define MT6370_BL_EN_MASK BIT(6) +#define MT6370_BL_CONFIG_MASK BIT(0) +#define MT6370_BL_CH_MASK GENMASK(5, 2) +#define MT6370_BL_DIM2_MASK GENMASK(2, 0) +#define MT6370_BL_DUMMY_6372_MASK GENMASK(2, 0) +#define MT6370_BL_DIM2_6372_SHIFT 3 +#define MT6370_BL_PWM_EN_MASK BIT(7) +#define MT6370_BL_PWM_HYS_EN_MASK BIT(2) +#define MT6370_BL_PWM_HYS_SEL_MASK GENMASK(1, 0) +#define MT6370_BL_OVP_EN_MASK BIT(7) +#define MT6370_BL_OVP_SEL_MASK GENMASK(6, 5) +#define MT6370_BL_OC_EN_MASK BIT(3) +#define MT6370_BL_OC_SEL_MASK GENMASK(2, 1) + +#define MT6370_BL_MAX_BRIGHTNESS 2048 + +enum { + MT6370_VID_COMMON = 0, + MT6370_VID_6372, + MT6370_VID_MAX, +}; + +enum mt6370_prop_type { + MT6370_PARSE_TYPE_BOOL = 0, + MT6370_PARSE_TYPE_U8, + MT6370_PARSE_TYPE_MAX, +}; + +struct mt6370_priv { + int vid_type; + struct backlight_device *bl; + struct device *dev; + struct gpio_desc *enable_gpio; + struct regmap *regmap; +}; + +static int mt6370_bl_update_status(struct backlight_device *bl_dev) +{ + struct mt6370_priv *priv = bl_get_data(bl_dev); + int brightness = backlight_get_brightness(bl_dev); + unsigned int enable_val; + u8 brightness_val[2]; + int ret; + + if (brightness) { + brightness_val[0] = (brightness - 1) & MT6370_BL_DIM2_MASK; + brightness_val[1] = (brightness - 1) + >> fls(MT6370_BL_DIM2_MASK); + + if (priv->vid_type == MT6370_VID_6372) { + brightness_val[0] <<= MT6370_BL_DIM2_6372_SHIFT; + brightness_val[0] |= MT6370_BL_DUMMY_6372_MASK; + } + + ret = regmap_raw_write(priv->regmap, MT6370_REG_BL_DIM2, + brightness_val, sizeof(brightness_val)); + if (ret) + return ret; + } + + if (priv->enable_gpio) + gpiod_set_value(priv->enable_gpio, brightness ? 1 : 0); + + enable_val = brightness ? MT6370_BL_EN_MASK : 0; + return regmap_update_bits(priv->regmap, MT6370_REG_BL_EN, + MT6370_BL_EN_MASK, enable_val); +} + +static int mt6370_bl_get_brightness(struct backlight_device *bl_dev) +{ + struct mt6370_priv *priv = bl_get_data(bl_dev); + unsigned int enable; + u8 brightness_val[2]; + int brightness, ret; + + ret = regmap_read(priv->regmap, MT6370_REG_BL_EN, &enable); + if (ret) + return ret; + + if (!(enable & MT6370_BL_EN_MASK)) + return 0; + + ret = regmap_raw_read(priv->regmap, MT6370_REG_BL_DIM2, + brightness_val, sizeof(brightness_val)); + if (ret) + return ret; + + if (priv->vid_type == MT6370_VID_6372) + brightness_val[0] >>= MT6370_BL_DIM2_6372_SHIFT; + + brightness = brightness_val[1] << fls(MT6370_BL_DIM2_MASK); + brightness += (brightness_val[0] & MT6370_BL_DIM2_MASK); + + return brightness + 1; +} + +static const struct backlight_ops mt6370_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = mt6370_bl_update_status, + .get_brightness = mt6370_bl_get_brightness, +}; + +#define MT6370_DT_PROP_DECL(_name, _type, _reg, _mask, _max, _inv) \ +{ \ + .name = "mediatek,bled-" #_name, \ + .type = MT6370_PARSE_TYPE_##_type, \ + .reg = _reg, \ + .mask = _mask, \ + .max_val = _max, \ + .invert = _inv, \ +} + +static int mt6370_init_backlight_properties(struct mt6370_priv *priv, + struct backlight_properties *props) +{ + struct device *dev = priv->dev; + u8 prop_val; + u32 brightness; + unsigned int mask, val; + static const struct { + char *name; + enum mt6370_prop_type type; + unsigned int reg; + unsigned int mask; + u8 max_val; + bool invert; + } vendor_opt_props[] = { + MT6370_DT_PROP_DECL(pwm-enable, BOOL, MT6370_REG_BL_PWM, + MT6370_BL_PWM_EN_MASK, 1, false), + MT6370_DT_PROP_DECL(pwm-hys-enable, BOOL, MT6370_REG_BL_PWM, + MT6370_BL_PWM_HYS_EN_MASK, 1, false), + MT6370_DT_PROP_DECL(pwm-hys-sel, U8, MT6370_REG_BL_PWM, + MT6370_BL_PWM_HYS_SEL_MASK, 3, false), + MT6370_DT_PROP_DECL(ovp-level-sel, U8, MT6370_REG_BL_BSTCTRL, + MT6370_BL_OVP_SEL_MASK, 3, false), + MT6370_DT_PROP_DECL(ovp-shutdown, BOOL, MT6370_REG_BL_BSTCTRL, + MT6370_BL_OVP_EN_MASK, 1, true), + MT6370_DT_PROP_DECL(ocp-level-sel, U8, MT6370_REG_BL_BSTCTRL, + MT6370_BL_OC_SEL_MASK, 3, false), + MT6370_DT_PROP_DECL(ocp-shutdown, BOOL, MT6370_REG_BL_BSTCTRL, + MT6370_BL_OC_EN_MASK, 1, true), + }, *prop_now; + int i, ret; + + /* vendor optional properties */ + for (i = 0; i < ARRAY_SIZE(vendor_opt_props); i++) { + prop_now = vendor_opt_props + i; + + switch (prop_now->type) { + case MT6370_PARSE_TYPE_BOOL: + if (device_property_read_bool(dev, prop_now->name)) + val = 1; + else + val = 0; + break; + case MT6370_PARSE_TYPE_U8: + ret = device_property_read_u8(dev, prop_now->name, + &prop_val); + /* Property not exist, keep value in default */ + if (ret) + continue; + + val = min_t(u8, prop_val, prop_now->max_val); + break; + default: + return -EINVAL; + } + + if (prop_now->invert) + val = prop_now->max_val - val; + + val <<= ffs(prop_now->mask) - 1; + + ret = regmap_update_bits(priv->regmap, prop_now->reg, + prop_now->mask, val); + if (ret) + return ret; + } + + /* common properties */ + ret = device_property_read_u32(dev, "max-brightness", &brightness); + if (ret) + brightness = MT6370_BL_MAX_BRIGHTNESS; + + props->max_brightness = min_t(u32, brightness, + MT6370_BL_MAX_BRIGHTNESS); + + ret = device_property_read_u32(dev, "default-brightness", &brightness); + if (ret) + brightness = props->max_brightness; + + props->brightness = min_t(u32, brightness, props->max_brightness); + + + ret = device_property_read_u8(dev, "mediatek,bled-channel-use", + &prop_val); + if (ret) { + dev_err(dev, "mediatek,bled-channel-use DT property missing\n"); + return ret; + } + + if (!prop_val) { + dev_err(dev, "No channel specified\n"); + return -EINVAL; + } + + mask = MT6370_BL_EXT_EN_MASK | MT6370_BL_CH_MASK; + val = prop_val << (ffs(MT6370_BL_CH_MASK) - 1); + + if (priv->enable_gpio) + val |= MT6370_BL_EXT_EN_MASK; + + return regmap_update_bits(priv->regmap, MT6370_REG_BL_EN, mask, val); +} + +static int mt6370_check_vendor_info(struct mt6370_priv *priv) +{ + unsigned int dev_info, vid; + int ret; + + ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &dev_info); + if (ret) + return ret; + + vid = FIELD_GET(MT6370_VENID_MASK, dev_info); + if (vid == 0x9 || vid == 0xb) + priv->vid_type = MT6370_VID_6372; + else + priv->vid_type = MT6370_VID_COMMON; + + return 0; +} + +static int mt6370_bl_probe(struct platform_device *pdev) +{ + struct mt6370_priv *priv; + struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .scale = BACKLIGHT_SCALE_LINEAR, + }; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) { + dev_err(&pdev->dev, "Failed to get regmap\n"); + return -ENODEV; + } + + ret = mt6370_check_vendor_info(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to check vendor info (%d)\n", ret); + return ret; + } + + priv->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->enable_gpio)) { + dev_err(&pdev->dev, "Failed to get 'enable' gpio\n"); + return PTR_ERR(priv->enable_gpio); + } + + ret = mt6370_init_backlight_properties(priv, &props); + if (ret) { + dev_err(&pdev->dev, "Failed to init backlight properties\n"); + return ret; + } + + priv->bl = devm_backlight_device_register(&pdev->dev, pdev->name, + &pdev->dev, priv, + &mt6370_bl_ops, &props); + if (IS_ERR(priv->bl)) { + dev_err(&pdev->dev, "Failed to register backlight\n"); + return PTR_ERR(priv->bl); + } + + backlight_update_status(priv->bl); + platform_set_drvdata(pdev, priv); + + return 0; +} + +static int mt6370_bl_remove(struct platform_device *pdev) +{ + struct mt6370_priv *priv = platform_get_drvdata(pdev); + struct backlight_device *bl_dev = priv->bl; + + bl_dev->props.brightness = 0; + backlight_update_status(priv->bl); + + return 0; +} + +static const struct of_device_id __maybe_unused mt6370_bl_of_match[] = { + { .compatible = "mediatek,mt6370-backlight", }, + {} +}; +MODULE_DEVICE_TABLE(of, mt6370_bl_of_match); + +static struct platform_driver mt6370_bl_driver = { + .driver = { + .name = "mt6370-backlight", + .of_match_table = mt6370_bl_of_match, + }, + .probe = mt6370_bl_probe, + .remove = mt6370_bl_remove, +}; +module_platform_driver(mt6370_bl_driver); + +MODULE_AUTHOR("ChiaEn Wu chiaen_wu@richtek.com"); +MODULE_DESCRIPTION("Mediatek MT6370 Backlight Driver"); +MODULE_LICENSE("GPL v2");
On Tue, May 31, 2022 at 07:19:00PM +0800, ChiaEn Wu wrote:
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 Backlight support.
Signed-off-by: ChiaEn Wu chiaen_wu@richtek.com
drivers/video/backlight/Kconfig | 8 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/mt6370-backlight.c | 338 +++++++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 drivers/video/backlight/mt6370-backlight.c
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index a003e02e13ce..d9868fbe7488 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -268,6 +268,14 @@ config BACKLIGHT_MAX8925 If you have a LCD backlight connected to the WLED output of MAX8925 WLED output, say Y here to enable this driver.
+config BACKLIGHT_MT6370
Is MT6370 really the best name for this driver? In other words, you don't expect there to be any family resemblance between this backlight and the backlight in the *next* Mediatak PMIC?
Moreover,
- tristate "Mediatek MT6370 Backlight Driver"
- depends on MFD_MT6370
- help
Say Y here to enable MT6370 Backlight support.
It's commonly used to drive the display WLED. There're 4 channels
inisde, and each channel can provide up to 30mA current.
Nitpicking but this doesn't align well with other help texts in this file.
config BACKLIGHT_APPLE tristate "Apple Backlight Driver" depends on X86 && ACPI diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index cae2c83422ae..e815f3f1deff 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o +obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o diff --git a/drivers/video/backlight/mt6370-backlight.c b/drivers/video/backlight/mt6370-backlight.c new file mode 100644 index 000000000000..f8a8d33203ed --- /dev/null +++ b/drivers/video/backlight/mt6370-backlight.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0
+#include <linux/backlight.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h>
+#define MT6370_REG_DEV_INFO 0x100 +#define MT6370_REG_BL_EN 0x1A0 +#define MT6370_REG_BL_BSTCTRL 0x1A1 +#define MT6370_REG_BL_PWM 0x1A2 +#define MT6370_REG_BL_DIM2 0x1A4
+#define MT6370_VENID_MASK GENMASK(7, 4) +#define MT6370_BL_EXT_EN_MASK BIT(7) +#define MT6370_BL_EN_MASK BIT(6) +#define MT6370_BL_CONFIG_MASK BIT(0) +#define MT6370_BL_CH_MASK GENMASK(5, 2) +#define MT6370_BL_DIM2_MASK GENMASK(2, 0) +#define MT6370_BL_DUMMY_6372_MASK GENMASK(2, 0) +#define MT6370_BL_DIM2_6372_SHIFT 3 +#define MT6370_BL_PWM_EN_MASK BIT(7) +#define MT6370_BL_PWM_HYS_EN_MASK BIT(2) +#define MT6370_BL_PWM_HYS_SEL_MASK GENMASK(1, 0) +#define MT6370_BL_OVP_EN_MASK BIT(7) +#define MT6370_BL_OVP_SEL_MASK GENMASK(6, 5) +#define MT6370_BL_OC_EN_MASK BIT(3) +#define MT6370_BL_OC_SEL_MASK GENMASK(2, 1)
+#define MT6370_BL_MAX_BRIGHTNESS 2048
+enum {
- MT6370_VID_COMMON = 0,
- MT6370_VID_6372,
- MT6370_VID_MAX,
Unused.
+};
+enum mt6370_prop_type {
- MT6370_PARSE_TYPE_BOOL = 0,
- MT6370_PARSE_TYPE_U8,
- MT6370_PARSE_TYPE_MAX,
Unused.
+};
+struct mt6370_priv {
- int vid_type;
- struct backlight_device *bl;
- struct device *dev;
- struct gpio_desc *enable_gpio;
- struct regmap *regmap;
+};
+static int mt6370_bl_update_status(struct backlight_device *bl_dev) +{
- struct mt6370_priv *priv = bl_get_data(bl_dev);
- int brightness = backlight_get_brightness(bl_dev);
- unsigned int enable_val;
- u8 brightness_val[2];
- int ret;
- if (brightness) {
brightness_val[0] = (brightness - 1) & MT6370_BL_DIM2_MASK;
brightness_val[1] = (brightness - 1)
>> fls(MT6370_BL_DIM2_MASK);
if (priv->vid_type == MT6370_VID_6372) {
brightness_val[0] <<= MT6370_BL_DIM2_6372_SHIFT;
brightness_val[0] |= MT6370_BL_DUMMY_6372_MASK;
Comment explaining why we have to set these bits would be useful.
}
ret = regmap_raw_write(priv->regmap, MT6370_REG_BL_DIM2,
brightness_val, sizeof(brightness_val));
if (ret)
return ret;
- }
- if (priv->enable_gpio)
gpiod_set_value(priv->enable_gpio, brightness ? 1 : 0);
- enable_val = brightness ? MT6370_BL_EN_MASK : 0;
- return regmap_update_bits(priv->regmap, MT6370_REG_BL_EN,
MT6370_BL_EN_MASK, enable_val);
+}
<snip>
+#define MT6370_DT_PROP_DECL(_name, _type, _reg, _mask, _max, _inv) \ +{ \
- .name = "mediatek,bled-" #_name, \
I'd rather have the whole DT property in the macro (because it helps with grepability).
- .type = MT6370_PARSE_TYPE_##_type, \
- .reg = _reg, \
- .mask = _mask, \
- .max_val = _max, \
- .invert = _inv, \
+}
+static int mt6370_init_backlight_properties(struct mt6370_priv *priv,
struct backlight_properties *props)
+{
- struct device *dev = priv->dev;
- u8 prop_val;
- u32 brightness;
- unsigned int mask, val;
- static const struct {
char *name;
enum mt6370_prop_type type;
unsigned int reg;
unsigned int mask;
u8 max_val;
bool invert;
- } vendor_opt_props[] = {
MT6370_DT_PROP_DECL(pwm-enable, BOOL, MT6370_REG_BL_PWM,
MT6370_BL_PWM_EN_MASK, 1, false),
MT6370_DT_PROP_DECL(pwm-hys-enable, BOOL, MT6370_REG_BL_PWM,
MT6370_BL_PWM_HYS_EN_MASK, 1, false),
MT6370_DT_PROP_DECL(pwm-hys-sel, U8, MT6370_REG_BL_PWM,
MT6370_BL_PWM_HYS_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ovp-level-sel, U8, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OVP_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ovp-shutdown, BOOL, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OVP_EN_MASK, 1, true),
MT6370_DT_PROP_DECL(ocp-level-sel, U8, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OC_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ocp-shutdown, BOOL, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OC_EN_MASK, 1, true),
- }, *prop_now;
- int i, ret;
- /* vendor optional properties */
- for (i = 0; i < ARRAY_SIZE(vendor_opt_props); i++) {
prop_now = vendor_opt_props + i;
switch (prop_now->type) {
case MT6370_PARSE_TYPE_BOOL:
if (device_property_read_bool(dev, prop_now->name))
val = 1;
else
val = 0;
break;
case MT6370_PARSE_TYPE_U8:
ret = device_property_read_u8(dev, prop_now->name,
&prop_val);
/* Property not exist, keep value in default */
if (ret)
continue;
val = min_t(u8, prop_val, prop_now->max_val);
break;
default:
return -EINVAL;
}
if (prop_now->invert)
val = prop_now->max_val - val;
val <<= ffs(prop_now->mask) - 1;
ret = regmap_update_bits(priv->regmap, prop_now->reg,
prop_now->mask, val);
if (ret)
return ret;
- }
Is it really worth all this tricky code for 7 properties?
The code would be much easier to read and maintain if it were coded directly. For example, the inverted boolean code is hard to read and can be written directly as:
val = device_property_read_bool(dev, "mediatek,bled-ovp_shutdown"); ret = regmap_update_bits(priv->regmap, MT6370_REG_BL_BST_CTRL, MT6370_BL_OVP_EN_MASK, MT6370_BL_OVP_EN_MASK * !val); if (ret) return ret;
The direct coded approach will probably also pay off if you switch the bindings over to microvolts/microamps since it becomes much more natural to call out to a lookup function to convert it into a register value.
- /* common properties */
- ret = device_property_read_u32(dev, "max-brightness", &brightness);
- if (ret)
brightness = MT6370_BL_MAX_BRIGHTNESS;
- props->max_brightness = min_t(u32, brightness,
MT6370_BL_MAX_BRIGHTNESS);
- ret = device_property_read_u32(dev, "default-brightness", &brightness);
- if (ret)
brightness = props->max_brightness;
- props->brightness = min_t(u32, brightness, props->max_brightness);
- ret = device_property_read_u8(dev, "mediatek,bled-channel-use",
&prop_val);
- if (ret) {
dev_err(dev, "mediatek,bled-channel-use DT property missing\n");
return ret;
- }
- if (!prop_val) {
dev_err(dev, "No channel specified\n");
return -EINVAL;
- }
If we are going to validity check this property then it needs an upper bounds check to (e.g. consider if property is set to 64).
- mask = MT6370_BL_EXT_EN_MASK | MT6370_BL_CH_MASK;
- val = prop_val << (ffs(MT6370_BL_CH_MASK) - 1);
- if (priv->enable_gpio)
val |= MT6370_BL_EXT_EN_MASK;
- return regmap_update_bits(priv->regmap, MT6370_REG_BL_EN, mask, val);
+}
+static int mt6370_check_vendor_info(struct mt6370_priv *priv)
A comment explaining what variants this function is intended to supported here would be good here.
+{
- unsigned int dev_info, vid;
- int ret;
- ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &dev_info);
- if (ret)
return ret;
- vid = FIELD_GET(MT6370_VENID_MASK, dev_info);
- if (vid == 0x9 || vid == 0xb)
priv->vid_type = MT6370_VID_6372;
- else
priv->vid_type = MT6370_VID_COMMON;
- return 0;
+}
No furthers comments so I trimmed the rest.
Daniel.
Hi Daniel,
Thanks for your valuable feedback!
Daniel Thompson daniel.thompson@linaro.org 於 2022年6月1日 週三 下午5:46寫道:
On Tue, May 31, 2022 at 07:19:00PM +0800, ChiaEn Wu wrote:
From: ChiaEn Wu chiaen_wu@richtek.com
Add Mediatek MT6370 Backlight support.
Signed-off-by: ChiaEn Wu chiaen_wu@richtek.com
drivers/video/backlight/Kconfig | 8 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/mt6370-backlight.c | 338 +++++++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 drivers/video/backlight/mt6370-backlight.c
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index a003e02e13ce..d9868fbe7488 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -268,6 +268,14 @@ config BACKLIGHT_MAX8925 If you have a LCD backlight connected to the WLED output of MAX8925 WLED output, say Y here to enable this driver.
+config BACKLIGHT_MT6370
Is MT6370 really the best name for this driver? In other words, you don't expect there to be any family resemblance between this backlight and the backlight in the *next* Mediatak PMIC?
Moreover,
MT6370, MT6371, MT6372 are very similar, their hardware design and features are almost the same and this driver can be compatible with them. But, MT6374 is not the same serials PMIC as MT6370, but the name is very similar. And It has no backlight feature, so I think MT6370 is the best name for this driver now.
tristate "Mediatek MT6370 Backlight Driver"
depends on MFD_MT6370
help
Say Y here to enable MT6370 Backlight support.
It's commonly used to drive the display WLED. There're 4 channels
inisde, and each channel can provide up to 30mA current.
Nitpicking but this doesn't align well with other help texts in this file.
OK! I will try to align well in the next patch.
config BACKLIGHT_APPLE tristate "Apple Backlight Driver" depends on X86 && ACPI diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index cae2c83422ae..e815f3f1deff 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o +obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o diff --git a/drivers/video/backlight/mt6370-backlight.c b/drivers/video/backlight/mt6370-backlight.c new file mode 100644 index 000000000000..f8a8d33203ed --- /dev/null +++ b/drivers/video/backlight/mt6370-backlight.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0
+#include <linux/backlight.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h>
+#define MT6370_REG_DEV_INFO 0x100 +#define MT6370_REG_BL_EN 0x1A0 +#define MT6370_REG_BL_BSTCTRL 0x1A1 +#define MT6370_REG_BL_PWM 0x1A2 +#define MT6370_REG_BL_DIM2 0x1A4
+#define MT6370_VENID_MASK GENMASK(7, 4) +#define MT6370_BL_EXT_EN_MASK BIT(7) +#define MT6370_BL_EN_MASK BIT(6) +#define MT6370_BL_CONFIG_MASK BIT(0) +#define MT6370_BL_CH_MASK GENMASK(5, 2) +#define MT6370_BL_DIM2_MASK GENMASK(2, 0) +#define MT6370_BL_DUMMY_6372_MASK GENMASK(2, 0) +#define MT6370_BL_DIM2_6372_SHIFT 3 +#define MT6370_BL_PWM_EN_MASK BIT(7) +#define MT6370_BL_PWM_HYS_EN_MASK BIT(2) +#define MT6370_BL_PWM_HYS_SEL_MASK GENMASK(1, 0) +#define MT6370_BL_OVP_EN_MASK BIT(7) +#define MT6370_BL_OVP_SEL_MASK GENMASK(6, 5) +#define MT6370_BL_OC_EN_MASK BIT(3) +#define MT6370_BL_OC_SEL_MASK GENMASK(2, 1)
+#define MT6370_BL_MAX_BRIGHTNESS 2048
+enum {
MT6370_VID_COMMON = 0,
MT6370_VID_6372,
MT6370_VID_MAX,
Unused.
+};
+enum mt6370_prop_type {
MT6370_PARSE_TYPE_BOOL = 0,
MT6370_PARSE_TYPE_U8,
MT6370_PARSE_TYPE_MAX,
Unused.
+};
+struct mt6370_priv {
int vid_type;
struct backlight_device *bl;
struct device *dev;
struct gpio_desc *enable_gpio;
struct regmap *regmap;
+};
+static int mt6370_bl_update_status(struct backlight_device *bl_dev) +{
struct mt6370_priv *priv = bl_get_data(bl_dev);
int brightness = backlight_get_brightness(bl_dev);
unsigned int enable_val;
u8 brightness_val[2];
int ret;
if (brightness) {
brightness_val[0] = (brightness - 1) & MT6370_BL_DIM2_MASK;
brightness_val[1] = (brightness - 1)
>> fls(MT6370_BL_DIM2_MASK);
if (priv->vid_type == MT6370_VID_6372) {
brightness_val[0] <<= MT6370_BL_DIM2_6372_SHIFT;
brightness_val[0] |= MT6370_BL_DUMMY_6372_MASK;
Comment explaining why we have to set these bits would be useful.
I got it, I will add the comment in the next patch.
}
ret = regmap_raw_write(priv->regmap, MT6370_REG_BL_DIM2,
brightness_val, sizeof(brightness_val));
if (ret)
return ret;
}
if (priv->enable_gpio)
gpiod_set_value(priv->enable_gpio, brightness ? 1 : 0);
enable_val = brightness ? MT6370_BL_EN_MASK : 0;
return regmap_update_bits(priv->regmap, MT6370_REG_BL_EN,
MT6370_BL_EN_MASK, enable_val);
+}
<snip>
+#define MT6370_DT_PROP_DECL(_name, _type, _reg, _mask, _max, _inv) \ +{ \
.name = "mediatek,bled-" #_name, \
I'd rather have the whole DT property in the macro (because it helps with grepability).
Do you mean the _name parameter must be the full name of the DT property and do not use "#" to concat like following example?
// in declare .name = _name, // in use MT6370_DT_PROP_DECL(mediatek,bled-pwm-enable, ......)
.type = MT6370_PARSE_TYPE_##_type, \
.reg = _reg, \
.mask = _mask, \
.max_val = _max, \
.invert = _inv, \
+}
+static int mt6370_init_backlight_properties(struct mt6370_priv *priv,
struct backlight_properties *props)
+{
struct device *dev = priv->dev;
u8 prop_val;
u32 brightness;
unsigned int mask, val;
static const struct {
char *name;
enum mt6370_prop_type type;
unsigned int reg;
unsigned int mask;
u8 max_val;
bool invert;
} vendor_opt_props[] = {
MT6370_DT_PROP_DECL(pwm-enable, BOOL, MT6370_REG_BL_PWM,
MT6370_BL_PWM_EN_MASK, 1, false),
MT6370_DT_PROP_DECL(pwm-hys-enable, BOOL, MT6370_REG_BL_PWM,
MT6370_BL_PWM_HYS_EN_MASK, 1, false),
MT6370_DT_PROP_DECL(pwm-hys-sel, U8, MT6370_REG_BL_PWM,
MT6370_BL_PWM_HYS_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ovp-level-sel, U8, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OVP_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ovp-shutdown, BOOL, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OVP_EN_MASK, 1, true),
MT6370_DT_PROP_DECL(ocp-level-sel, U8, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OC_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ocp-shutdown, BOOL, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OC_EN_MASK, 1, true),
}, *prop_now;
int i, ret;
/* vendor optional properties */
for (i = 0; i < ARRAY_SIZE(vendor_opt_props); i++) {
prop_now = vendor_opt_props + i;
switch (prop_now->type) {
case MT6370_PARSE_TYPE_BOOL:
if (device_property_read_bool(dev, prop_now->name))
val = 1;
else
val = 0;
break;
case MT6370_PARSE_TYPE_U8:
ret = device_property_read_u8(dev, prop_now->name,
&prop_val);
/* Property not exist, keep value in default */
if (ret)
continue;
val = min_t(u8, prop_val, prop_now->max_val);
break;
default:
return -EINVAL;
}
if (prop_now->invert)
val = prop_now->max_val - val;
val <<= ffs(prop_now->mask) - 1;
ret = regmap_update_bits(priv->regmap, prop_now->reg,
prop_now->mask, val);
if (ret)
return ret;
}
Is it really worth all this tricky code for 7 properties?
The code would be much easier to read and maintain if it were coded directly. For example, the inverted boolean code is hard to read and can be written directly as:
val = device_property_read_bool(dev, "mediatek,bled-ovp_shutdown"); ret = regmap_update_bits(priv->regmap, MT6370_REG_BL_BST_CTRL, MT6370_BL_OVP_EN_MASK, MT6370_BL_OVP_EN_MASK * !val); if (ret) return ret;
The direct coded approach will probably also pay off if you switch the bindings over to microvolts/microamps since it becomes much more natural to call out to a lookup function to convert it into a register value.
The purpose of my code is trying to avoid the repeat code in this function. And for loop can help to decrease the lines of code effectively, that's why I use these code to parse the DT properties.
/* common properties */
ret = device_property_read_u32(dev, "max-brightness", &brightness);
if (ret)
brightness = MT6370_BL_MAX_BRIGHTNESS;
props->max_brightness = min_t(u32, brightness,
MT6370_BL_MAX_BRIGHTNESS);
ret = device_property_read_u32(dev, "default-brightness", &brightness);
if (ret)
brightness = props->max_brightness;
props->brightness = min_t(u32, brightness, props->max_brightness);
ret = device_property_read_u8(dev, "mediatek,bled-channel-use",
&prop_val);
if (ret) {
dev_err(dev, "mediatek,bled-channel-use DT property missing\n");
return ret;
}
if (!prop_val) {
dev_err(dev, "No channel specified\n");
return -EINVAL;
}
If we are going to validity check this property then it needs an upper bounds check to (e.g. consider if property is set to 64).
OK, I will add the validation in the next patch.
mask = MT6370_BL_EXT_EN_MASK | MT6370_BL_CH_MASK;
val = prop_val << (ffs(MT6370_BL_CH_MASK) - 1);
if (priv->enable_gpio)
val |= MT6370_BL_EXT_EN_MASK;
return regmap_update_bits(priv->regmap, MT6370_REG_BL_EN, mask, val);
+}
+static int mt6370_check_vendor_info(struct mt6370_priv *priv)
A comment explaining what variants this function is intended to supported here would be good here.
Ok, I got it! I'll add the comment in the next patch.
+{
unsigned int dev_info, vid;
int ret;
ret = regmap_read(priv->regmap, MT6370_REG_DEV_INFO, &dev_info);
if (ret)
return ret;
vid = FIELD_GET(MT6370_VENID_MASK, dev_info);
if (vid == 0x9 || vid == 0xb)
priv->vid_type = MT6370_VID_6372;
else
priv->vid_type = MT6370_VID_COMMON;
return 0;
+}
No furthers comments so I trimmed the rest.
Daniel.
Best regards, ChiaEn Wu
On Fri, Jun 03, 2022 at 03:14:56AM +0800, ChiaEn Wu wrote:
Daniel Thompson daniel.thompson@linaro.org 於 2022年6月1日 週三 下午5:46寫道:
On Tue, May 31, 2022 at 07:19:00PM +0800, ChiaEn Wu wrote:
+#define MT6370_DT_PROP_DECL(_name, _type, _reg, _mask, _max, _inv) \ +{ \
.name = "mediatek,bled-" #_name, \
I'd rather have the whole DT property in the macro (because it helps with grepability).
Do you mean the _name parameter must be the full name of the DT property and do not use "#" to concat like following example?
// in declare .name = _name, // in use MT6370_DT_PROP_DECL(mediatek,bled-pwm-enable, ......)
Yes, I would prefer this form, although, as discussed below, I don't really like MT6370_DT_PROP_DECL().
.type = MT6370_PARSE_TYPE_##_type, \
.reg = _reg, \
.mask = _mask, \
.max_val = _max, \
.invert = _inv, \
+}
+static int mt6370_init_backlight_properties(struct mt6370_priv *priv,
struct backlight_properties *props)
+{
struct device *dev = priv->dev;
u8 prop_val;
u32 brightness;
unsigned int mask, val;
static const struct {
char *name;
enum mt6370_prop_type type;
unsigned int reg;
unsigned int mask;
u8 max_val;
bool invert;
} vendor_opt_props[] = {
MT6370_DT_PROP_DECL(pwm-enable, BOOL, MT6370_REG_BL_PWM,
MT6370_BL_PWM_EN_MASK, 1, false),
MT6370_DT_PROP_DECL(pwm-hys-enable, BOOL, MT6370_REG_BL_PWM,
MT6370_BL_PWM_HYS_EN_MASK, 1, false),
MT6370_DT_PROP_DECL(pwm-hys-sel, U8, MT6370_REG_BL_PWM,
MT6370_BL_PWM_HYS_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ovp-level-sel, U8, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OVP_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ovp-shutdown, BOOL, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OVP_EN_MASK, 1, true),
MT6370_DT_PROP_DECL(ocp-level-sel, U8, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OC_SEL_MASK, 3, false),
MT6370_DT_PROP_DECL(ocp-shutdown, BOOL, MT6370_REG_BL_BSTCTRL,
MT6370_BL_OC_EN_MASK, 1, true),
}, *prop_now;
int i, ret;
/* vendor optional properties */
for (i = 0; i < ARRAY_SIZE(vendor_opt_props); i++) {
prop_now = vendor_opt_props + i;
switch (prop_now->type) {
case MT6370_PARSE_TYPE_BOOL:
if (device_property_read_bool(dev, prop_now->name))
val = 1;
else
val = 0;
break;
case MT6370_PARSE_TYPE_U8:
ret = device_property_read_u8(dev, prop_now->name,
&prop_val);
/* Property not exist, keep value in default */
if (ret)
continue;
val = min_t(u8, prop_val, prop_now->max_val);
break;
default:
return -EINVAL;
}
if (prop_now->invert)
val = prop_now->max_val - val;
val <<= ffs(prop_now->mask) - 1;
ret = regmap_update_bits(priv->regmap, prop_now->reg,
prop_now->mask, val);
if (ret)
return ret;
}
Is it really worth all this tricky code for 7 properties?
The code would be much easier to read and maintain if it were coded directly. For example, the inverted boolean code is hard to read and can be written directly as:
val = device_property_read_bool(dev, "mediatek,bled-ovp_shutdown"); ret = regmap_update_bits(priv->regmap, MT6370_REG_BL_BST_CTRL, MT6370_BL_OVP_EN_MASK, MT6370_BL_OVP_EN_MASK * !val); if (ret) return ret;
The direct coded approach will probably also pay off if you switch the bindings over to microvolts/microamps since it becomes much more natural to call out to a lookup function to convert it into a register value.
The purpose of my code is trying to avoid the repeat code in this function. And for loop can help to decrease the lines of code effectively, that's why I use these code to parse the DT properties.
I'm not really convinced that is uses fewer lines of code. It certainly would if there were a very large number of properties but here there is only seven.
However I guess what I'm really complaining about is how hard it is to read the for loop. We have to study the macros, keep track six different arguments per property and review the complex logic of the for loop (which for example handles inverted u8's that don't actually exist).
To be clear, it's not that loops aren't useful for reducing boilerplate code. They can be. However trying to handle booleans and integers in the *same* loop ends up needlessly hard to read.
Also, I think that if/when you adopt microamps/microvolts then the hard-to-read problem will get even worse unless you get loops to do only one thing!
Daniel.
On 31/05/2022 13:18, ChiaEn Wu wrote:
From: ChiaEn Wu chiaen_wu@richtek.com
Sorry, for the patch dependency, we resend this patch series and we're sorry for any inconvenience that we may have caused.
What is the reason of resend? I just finished reviewing v1, so shall it is confusing to see a new one...
Best regards, Krzysztof
dri-devel@lists.freedesktop.org